damir
ae9c4e2bfd
Sportski objekti: API + Leaflet map page + address enrichment
...
DB: pgz_sport.sportski_objekti (103 objekti, 103 s geo, 60 s adresom, 31 tip)
API:
- /api/v2/sportski-objekti (filter: tip, grad, sport, q)
- /api/v2/sportski-objekti/meta (tipovi, gradovi, sportovi, ukupno)
Frontend:
- /static/objekti.html — Leaflet (OpenStreetMap) interactive map
- 3 dropdown filter (tip, grad, sport) + search
- Side panel s listom + map markers s ikonama (🏟️ ⚽ 🏊 ⛵ 🎿 🎳 ⛸️ 🎯 🥌 🏃 )
- Popup: naziv, tip, kapacitet, adresa, upravitelj, izgradeno, sportovi, web link, Google Maps link
- /objekti, /sport/objekti, /sport/api/v2/sportski-objekti routes
Sidebar app.html: +Sportski objekti link
Background: scripts/objekti_enrich_address.py (Nominatim reverse-geocode 60 objekata bez adrese)
2026-05-05 18:35:04 +02:00
Damir Radulić
9b0ed43b92
RUSH 4-sub: filteri Klubovi/Sportaši + manifestacije card view + CRM v2 redesign
...
RUSH-1 Klubovi: list_klubovi() LEFT JOIN v_klubovi_financiranje (prima_pgz/rss/grad_rijeka, u_godisnjaku, ukupno_potpora). financiran=true sad OR od 3 davatelja (drop legacy klubovi.pgz_sufinanciran s 1312 false-positive). Sort sort=potpora&order=desc. UI: gold ukupno_potpora + tooltip + sortable kolona. Defaults priority view (financirani+godišnjak ON, hns_roster OFF). Test: priority=604, +hns=36, all=1641, financiran=15 sorted ZAMET 80208€.
RUSH-2 Sportaši: SELECT widened (slika_url, reprezentativac, kategoriziran, broj_dresa). avatarUrl() helper s 3 forme (apsolutni / lokalni /sport/uploads/avatars / initials fallback) + 32px circular avatar lijevo od imena. Test: priority=3712, no-priority=6086, +hns=1439, 1990-2000=645.
RUSH-3 Manifestacije: bugfix razina filter HTTP 500 (ambiguous column nakon LEFT JOIN savezi → m.razina/mjesto/organizator). 3 dropdowna iz meta (26 mjesta / 8 razina / 50 organizatora), view toggle 🃏 Kartice / 📋 Tablica (localStorage), 🔗 link ikona u card+table, source_url → Google fallback. Test: default=3, mjesto=Lošinj=2, razina=Tradicionalna=3, organizator=AK Kvarner=1.
RUSH-4 CRM v2: tab strip rewrite (10 taba u spec redu Članarine|Liječnički|Obrasci|E-mail|Accounts|Contacts|Leads|Opps|Activities|Cases, sticky+scrollable+gold underline). Pipeline → Opps tab. Novi e-mail templates tab (5 endpointa, 3 seed templates, +Novi modal). Card layout (.cgrid/.ccard) za Accounts/Contacts/Leads/Opps. Export dropdown 📥 ▾ CSV/XLSX(SheetJS CDN)/PDF na svaki tab. Test: /crm_v2 200, 10/10 tab labela, 10 Export dropdowna + 31 exportTab() handlera.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-05 18:33:20 +02:00
damir
007825acee
Bug hunt V7:
...
DB:
- Aggressive je_klub=false flag for programs/treninzi/totals (>100K€ no klub_id)
- 53 ne-klubovi flagged false (RSS Rijeka ukupni, Stručni rad, Potpora loptačkim, etc)
Frontend (sport2.html):
- Panel back button (← Natrag) + history stack
- window._panelHistory + pushPanelState + panelBack functions
- closePanel resets history
2026-05-05 14:56:53 +02:00
Damir Radulić
1e611d59f1
HNS sprint: 3-tab drill-down + parallel deep scraper dispatch
...
HNS-1 verify: smoke test 93409 OK, gap 854 uncovered, throughput ~60/min
HNS-2 dispatch: scripts/hns_dispatch.sh + 5 parallel workers shard'd po roster ID; coverage 265→1098 distinct_seasons (93.7% of 1172 roster), 125→971 distinct_matches; total seasons 3170→13371, matches 23515→150071
HNS-3 UI: 6-tab panel collapsed na 3 (🏆 Karijera / 📅 Utakmice / 👤 Profil); novi /api/v2/clan/{id}/hns-matches?limit=30 + /clan/{id}/hns-profile (bio + summary + HNS deep link); prof-grid 3-col card s gold jersey badge; OIB RBAC-mask. Test Josip Zec 449: 72 sez/30 utak.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-05 14:24:05 +02:00
damir
448273945c
/sport/* aliases u app: admin, dokumenti, crm/v2, erp/full
2026-05-05 14:13:32 +02:00
damir
360b8008ba
Crisis V6: panel expand + klub matching + ne-klub filter + samo_klubovi default
...
DB:
- pgz_sport.potpore_nositelji.je_klub flag (false za RSS programs/savezi)
- Re-match klub_id case-insensitive trim normalize
Endpoint:
- /api/dashboard/top-primatelji: samo_klubovi=True default
Frontend:
- sport2.html #panel/#dpanel: 70vw / 1100px max-width za HNS karijera
- mobile responsive za panel
2026-05-05 14:09:47 +02:00
damir
f7b5114f58
PDF link target=_blank + nginx timeouts + priority filteri (samo s podacima)
...
nginx (sport.rinet.one):
- proxy_read_timeout 60s → 300s
- proxy_send_timeout 300s
- proxy_buffering off (PDF stream)
- client_max_body_size 50M → 100M
Endpoints:
- /api/v2/klubovi/financirani: +with_data filter (samo s potporama/godišnjakom/HNS)
- /api/v2/sportasi/filtered: +samo_priority +samo_s_hns
Frontend:
- PDF link target=_blank rel=noopener
- window._klub_only_priority = true (default)
- window._sportas_only_priority = true (default)
DB View:
- pgz_sport.v_nogomet_priority (prima_potpore, u_godisnjaku, ima_hns_roster)
2026-05-05 13:51:07 +02:00
damir
1d02c0897d
Sidebar: +ERP +CRM +Dokumenti, godišnjaci import (18 PDFs), filter helpers
...
- pgz nav now includes /erp/full, /crm/v2, /admin/users, /dokumenti
- 4 dokumenti endpoints: list, godišnjaci/list, godišnjak/{godina} PDF, detail
- 18 godišnjaka u pgz_sport.dokumenti (2006-2024) with savez_id=333
- PGŽ filter helpers (window._pgz_filter_priority, togglePGZFilter)
- navItemClick handler for nav items with href
2026-05-05 13:08:11 +02:00
damir
9fb512932a
HNS+UI: 4 nova endpointa + multi-sport schema (M2M kategorije + player_stats)
...
Endpoints:
- GET /api/v2/enrich-sources — sport→source mapping
- GET /api/v2/klubovi/priority-sort — financirani/godišnjak prvi
- GET /api/v2/clan/{id}/kategorije — many-to-many kategorije
- GET /api/v2/clan/{id}/full — kompletna slika (profil+kategorije+sezone+utakmice+stats)
- POST /api/v2/export/klubovi — XLSX export selektiranih
Schema:
- pgz_sport.clan_kategorije (M2M: igrač u juniorskoj+seniorskoj)
- pgz_sport.player_stats (multi-sport: nogomet/košarka/rukomet/odbojka/vaterpolo)
- pgz_sport.klub_roster (multi-source)
- pgz_sport.enrichment_sources (sport→izvor)
- View: v_pgz_priority_klubovi (financiran || u_godisnjaku)
- View: v_klubovi_priority_sort (priority sort)
Sport harvesters scaffold:
- scripts/sport_harvesters/__base.py (SportHarvester class)
- hks_basketball.py, hrs_handball.py, hos_volleyball.py, hvs_waterpolo.py
2026-05-05 10:42:49 +02:00
damir
c68fd4471e
HNS endpoints: /clan/{id}/hns-career + /klubovi/pgz-financirani + /dashboard/hns-coverage
...
Backed by: pgz_sport.hns_player_seasons, hns_klub_roster, v_pgz_financirani_klubovi
Used by: cc-hns subagents for UI integration
2026-05-05 10:22:36 +02:00
damir
a20230187f
Playwright logout test: dialog handler + wait_for_url for async flow
2026-05-05 09:24:59 +02:00
damir
a0fb328029
Playwright E2E: better logout selector chain + JS fallback
...
Test now tries (in order):
1. .sb-foot .lo (topbar logout in sidebar foot)
2. .lo (any logout class)
3. #pgz-menu-logout (sidebar.js menu link)
4. a/button :has-text('Odjava')
5. JS fallback: window.logout() or PGZSidebar.logout()
Also: dialog handler accepts confirm() automatically.
2026-05-05 09:23:13 +02:00
damir
dd2f7daaf8
CRISIS V3: definitive apiAuth + mobile hamburger + Playwright E2E test
...
apiAuth in app.html:
- Pre-checks JWT exp client-side BEFORE making request
- On expired: clears localStorage + redirects /login?reason=expired
- On 401 from server: clears + redirects /login?reason=unauthorized
- Single-flight redirect via window.__pgz_redirecting flag
login.html:
- Toast for ?reason=expired (red) / ?reason=unauthorized (orange)
app.html mobile:
- Hamburger button injected into topbar (.tb)
- Mobile CSS: sidebar slide-in -280→0, backdrop overlay, full-width drill-down
- toggleMobileSidebar() global function
- @media (max-width:768px) display:inline-flex, sidebar fixed pos
scripts/playwright_e2e.py:
- Desktop test (1280x800): login, JWT persist, profile, logo, logout
- Mobile test (375x812 iPhone X): viewport, login flow, hamburger, no h-scroll
- Output: _audit/playwright_<TS>/results.json + screenshots/*.png
Reproducible: TS=YYYYmmdd_HHMM python3 scripts/playwright_e2e.py
2026-05-05 09:21:39 +02:00
CC1
3e60e5095a
CC1 audit fixes #6/#8/#9 from CONSOLIDATED.md
...
#9 sportas trailing-slash (verified clean):
Frontend constructs /sportas/{id}/profil cleanly (sport2.html:L1582).
Live test: 200 on /sportas/449/profil; 307 on trailing-slash variant
(FastAPI auto-redirect, harmless). No fix needed.
#8 [VERIFY]/[UNRESOLVED] klubovi surfaced to audit log:
3 manual_review klubovi (2619 Čavle, 2630 Opatija, 4426 empty) inserted
into pgz_sport.sys_audit with action='klub.manual_review_pending',
visible in /audit page for human triage. Total audit rows: 633.
#6 backup-table archival:
Moved 26 *_backup_*/*_premerge_*/*_pre_*/*_dedup_*/*_deprecated_*/*_garbage_*
tables (~97k rows) from pgz_sport → pgz_sport_archive schema.
- Snapshot dump: _audit/db_snapshots/backup_tables_20260505_085057.sql.gz (56 MB)
- Script: scripts/archive_backup_tables.sql (idempotent, with rollback)
- pgz_sport canonical tables: 112 → 86
- All live API endpoints still 200, db=ok, errors_logged stable at 23
- Josip Zec test 257/182/15 still PASS
.gitignore: exclude _audit/db_snapshots/ (large pg_dump archives)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-05 08:52:07 +02:00
damir
aad034a59d
PHASE 5: sidebar Debug link + swarm_monitor.py daemon
...
- static/shared/sidebar.js: '🩺 Debug' link in pgz_admin sidebar
- scripts/swarm_monitor.py: detects stuck/idle CC agents,
Telegram alerts on session expired or limit prompts
- pgz-swarm-monitor.service running 60s checks
Full debug stack now active:
- pgz-debug-tail: error stream
- pgz-auto-triage: pattern → CC dispatch
- pgz-swarm-monitor: agent health
- /api/debug/* dashboard
2026-05-05 08:48:02 +02:00
damir
63ca005b6e
DEBUG OBSERVABILITY: live error feed + auto-triage bot + dashboard
...
PHASE 1 — DEBUG mode:
- /etc/systemd/system/pgz-sport.service.d/debug.conf: DEBUG=1, LOG_LEVEL=DEBUG, PYTHONUNBUFFERED=1, UVICORN_LOG_LEVEL=debug
PHASE 2 — Error stream:
- /opt/pgz-sport/scripts/debug_tail.sh: tail journalctl + nginx → /var/log/pgz-sport-debug/{stream,errors}.jsonl
- pgz-debug-tail.service (always restart, multiplexes 4 sources)
PHASE 3 — Auto-triage bot:
- /opt/pgz-sport/scripts/auto_triage.py: classifies errors, dispatches CC agents
- Patterns: 5xx spike → CC4, 401/403 spike → CC2, 4xx API → CC3, ImportError/DB → CC4
- Rate limit: 6 telegram/5min
- Records decisions in triage_decisions.jsonl
- pgz-auto-triage.service
PHASE 4 — Live dashboard:
- routers/debug_router.py mounted in pgz_sport_api
- GET /api/debug/health — services + DB + error count
- GET /api/debug/errors?limit=N — last N errors (JSON)
- GET /api/debug/decisions — auto-fix decisions
- GET /api/debug/stream — full log tail
- GET /api/debug/dashboard — live HTML refresh 5s
Damir admin tier dashboard: https://sport.rinet.one/sport/api/debug/dashboard
2026-05-05 08:46:09 +02:00
claude-cc1
7251d27c21
CC1 R6 — coverage report + 2 more klubovi fixed
...
Coverage report (`/opt/pgz-sport/data_quality_report.md`):
- 5952 entities measured (savezi 246, klubovi 2244, sportasi 3243, objekti 106, manifestacije 113)
- Weighted mean coverage 52.1%
- Per-type stats: objekt 79.7% > manif 81.9% > savez 59.8% > klub 57.1% > sportas 46.2%
- Distribution histogram per type
- TOP 50 entities for manual review (lowest coverage with non-empty name) with portal links
Mreža verification (Playwright headless):
- pgz-savez-nogometni anchor injected, label "Nogometni savez PGŽ", color #F4C430, size 40
- 6 anchor edges to top-3 persons + top-3 entities
- 90 nodes / 186 edges total after augmentation
- "🎯 Centar (PGŽ)" button visible
- centerMrezaOnAnchor() fires 1.5s after render
Cleanup v2 (`scripts/r6_cleanup_v2.sql`):
- 2636 [VERIFY] → Odbojkaški Klub "Odbojkaška Akademija Petica" (civic#114850)
- 2641 [VERIFY] → Ženski Odbojkaški Klub "Crikvenica" (civic#78781)
- 12 of 14 originals now confirmed; 2 still need manual ([VERIFY] 2619 Vrh Čavje 31, 2630 1. Istarske čete 3 — no civic.entities row at those addresses)
sport-pgz.hr scrape: site is a Vite SPA with no public JSON club listing endpoint;
individual club slugs return 404. Best authoritative source remains civic.entities.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-05 01:46:39 +02:00
Damir Radulić
f9ebcddf28
CC2 R6: middleware-wide JWT, avatar demo mode, mock mailer, login rate limit
...
#1 JWT middleware extended:
- Was: /api/admin/* only
- Now: any POST/PUT/PATCH/DELETE under /api/* requires Bearer JWT
- Whitelist (still anonymous): /api/auth/login, /refresh, /forgot-password,
/password/reset, /reset-password, /setup-password, /google;
/api/gdpr/consent; any path ending /avatar
- 14 mutating endpoints verified to return 401 without token
#2 Avatar upload demo mode (routers/clan_panel_router.py):
- Anonymous → returns {demo_mode:true, slika_url:null,
message:'Demo mode — slika nije spremljena. Prijavite se za pravu pohranu.'},
no FS write, no DB write
- Authenticated (valid JWT, allowed role) → real save as before
- Auth check now uses auth.auth_v2.decode_token (proper secret + revocation)
instead of the broken local _resolve_role
#3 Mock mailer (auth/mailer.py):
- send_email writes RFC 822 .eml to /tmp/pgz_mailbox + appends to INDEX.jsonl
- send_password_reset, send_invite helpers with HR text + HTML alt
- Real SMTP active when PGZ_SMTP_HOST is set (env-driven, off by default)
- forgot-password and admin invite both call mailer; audit logs mail status
#5 Rate limiting on /api/auth/login:
- Per-user: 5 wrong attempts → 5-minute DB-backed lockout
(was 5 → 15 min). Configurable via PGZ_LOGIN_LOCK_THRESHOLD/MINUTES.
- Per-IP: 10 fails / 5-min sliding window in-memory → HTTP 429
Configurable via PGZ_LOGIN_IP_THRESHOLD/WINDOW_SEC. Successful
login clears the IP counter.
- Failed attempts respond '(N/5) — račun je zaključan na 5 minuta'
- New audit actions: login.ratelimit.ip; login.fail meta now
includes fails count, locked, lock_minutes
#4 Live test report: 46/46 across 6 demo users — login, JWT gate on 14
mutating endpoints, public path whitelist, demo-mode avatar +
real save, forgot-password e-mail to mailbox, no-leak unknown email,
5-fail lockout, 423 during lockout, audit coverage.
2026-05-05 01:42:53 +02:00
Damir Radulić
0046b8d695
CC2 R5: defense-in-depth JWT + invite/reset token flows + audit
...
#1 JWT middleware:
- pgz_sport_api.py: starlette middleware require_jwt_on_admin runs before
every /api/admin/* route. Even routes that lack Depends(require_user)
cannot be reached without a valid Bearer token (verifies signature,
exp, typ='access', revocation via user_sessions). OPTIONS passes for CORS.
#2 Invitation flow:
- pgz_sport.user_action_tokens table (token_hash, user_id, kind, expires_at,
used_at, created_by, ip, meta). Single-use, raw token never persisted.
- POST /api/admin/users/{id}/invite — issues 'invite' token (TTL 7d),
marks must_change_pwd, revokes existing sessions, returns invite_link.
- GET /api/auth/setup-password?token=X — preflight (no consume).
- POST /api/auth/setup-password — consumes token, sets password, sets
email_verified=true.
#3 Password reset flow:
- POST /api/auth/forgot-password — generic 'ako račun postoji' response;
issues 'reset' token (TTL 2h) only for active users. Token returned in
response only on localhost or if PGZ_REVEAL_RESET_TOKEN=1.
- GET /api/auth/reset-password?token=X — preflight.
- POST /api/auth/reset-password — consumes token, sets new password,
revokes all active sessions.
#4 Audit coverage (auth events):
- login.ok, login.fail (with reason), login.locked, login.2fa_required,
login.2fa_fail, logout, auth.refresh, password.change, password.reset.ok,
password.reset.fail, password.forgot.issue, password.forgot.miss,
invite.consume.ok, invite.consume.fail, user.invite, user.create,
user.update, user.delete, user.role.change, user.suspend, user.unsuspend,
user.password.reset, 2fa.verify.ok, 2fa.verify.fail, 2fa.disable.
#5 Live tests: 41/41 across 6 demo users (incl. fresh invited+deleted user).
Phase 2 verifies 14 endpoints reject no-auth and accept valid Bearer.
2026-05-05 01:28:29 +02:00
claude-cc1
64082d0642
CC1 R3B-P3 — geocoding precision (Crikvenica + OSM cross-check)
...
- New scripts/geocode_v3_osm.py: matches DB objekti against OSM Overpass sports facilities
- Applied 53 OSM updates, then reverted bad cross-city matches to hand-curated coords
- Crikvenica venues now precise (Gradska dvorana, SS Antun Barac, Stadion, Sport+ Centar)
- Atletska dvorana Luciano Sušanj fixed to Kantrida
- Skate park Delta, Boulder dvorana, Boćarski Podvežica reverted from wrong matches
- Google Places API not available (project disabled), Overpass + curated fallback used
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-05 00:04:50 +02:00
Damir Radulić
a7ec0a86be
PGŽ Sport Platform — Round 1+2 baseline (sport2.html + API)
2026-05-04 23:39:08 +02:00