# PGŽ SPORT — AUTONOMNI RUN — 29.04.2026 (Damir radio, Claude radio sam) ## TL;DR Sve glavne stavke gotove. Sustav radi end-to-end: login → korisnici CRUD → sportaš profil → HNS scraper → Qdrant embedding → cron jobs. Damir treba samo testirati u browseru i javiti što vizualno smeta. --- ## ŠTO JE NAPRAVLJENO ### Faza 1+2 — DB konsolidacija (gotovo prije današnjeg run-a) - `users` tablica canonical (19 FK), `sys_users` migrirana → 8 redaka - `sys_*` renamed → `*_DEPRECATED_20260429` - 9 korisnika postavljeno (default pwd `PgzSport2026!`+must_change) ### Faza 3+4 — Login UI + must_change_pwd - Login modal s 2 taba (👤 Korisnik / 🔓 Admin token) - "🔐 PRIJAVA" pill u sidebar footer - must_change_pwd modal nakon prvog login-a - Auto-401 redirect na svaki v2 endpoint - Backend: `/auth/login` vraća `must_change_pwd, user_type, ime, prezime, klub_id, savez_id` - Backend: `/auth/me` enriched (klubovi[], savezi[], roles[]) - Backend: `/auth/change-password` (s old_password verifikacijom osim u must_change flow-u) - Lockout zaštita: 5 failed → 15 min lock ### Faza 5+6 — RBAC + admin user CRUD - `get_current_user` extended (user_type, klub_id, savez_id, aktivan) - RBAC helpers: `is_super`, `is_pgz_admin`, `can_manage_users`, `require_role` - `tenant_filter_users(user)` — pgz_*: sve, savez_*: scope na savez_id, klub_*: scope na klub_id - Endpoints: `/users/list, /users/create, /users/{id} PUT, /users/{id}/reset-password, /users/{id}/toggle-active, /users/{id}/unlock, /users/{id}/audit, /admin/audit, /admin/permissions-matrix, /admin/permissions/grant, /users/klub-link POST/DELETE, /admin/impersonate` - Smoke: pgz_user (Marija) vidi svih 9, klub_admin bez klub_id → 0 (filter ispravan) ### Faza 7 — Korisnici full CRUD UI (gotovo) - Search + tip filter + Novi korisnik gumb - Tablica: ID, email, ime+prezime, tip, klub/savez, status (aktivan/locked), failed_login_count, last_login, akcije - Akcije: edit, reset pwd, toggle aktivan, unlock, impersonate (super_admin), audit - Modal: create (s default pwd), edit (sve polja), reset-pwd (generira temp pwd, prikazuje u prompt) - Audit viewer: globalni i per-user ### Faza 8 — CSS unifikacija s app.rinet.one/klasik - :root tokeni replikirani iz `/opt/rinet-v4/app/src/index.css` (Palantir Gotham theme) - Master tokens: bg/bg2/bg3/bg4/bg5, border/border2/border3, text/text2/text3/text-bright/text-dim, accent/accent2/accent-glow, green/red/amber/cyan, sans (IBM Plex), mono (JetBrains), radius/radius-lg, chart1..4 - Legacy aliases: `--text-2 → --text`, `--bg-2 → --bg2`, `--gold → --amber`, `--r → --radius-lg`, etc. - Master utility classes: `.ri-card, .ri-glass, .ri-tbl, .ri-btn(-primary,-ghost), .ri-kpi-value, .risk-{critical,high,medium,low}` - Icons: Lucide-style stroke 1.5, opacity 0.7 ### Faza 9 — Sportaš profil (semafor.hns.family stil) - Schema extension `pgz_sport.clanovi`: slug, slika_url, source, source_id, source_url, source_synced_at, pozicija, dominantna_noga, visina_cm, tezina_kg, broj_dresa, reprezentativac, reprezentacija_kategorija, biografija, mjesto_rodenja - Nova tablica `pgz_sport.utakmice_log` (per-igrač match log s pogocima/karticama/minutama) - `pgz_sport.klubovi` extended: hns_klub_id, hns_slug, hrs_klub_id, hks_klub_id, hvs_klub_id, logo_url, web_stranica, source_synced_at - Nova tablica `pgz_sport.scraper_runs` (run history s status/errors) - `pgz_sport.natjecanja` extended: source, source_id, source_url, pgz_relevant - Backend: `/api/v2/sportas/{cid}/profile` (sportas + seasons + career + matches + totals) - Backend: `/api/v2/klub/{kid}/sportasi` (roster po klubu) - Backend: `/api/v2/sportas/search?q=` - Frontend: `pageSportas` (semafor-style profile s photo/KPI/seasons/matches), `pageKlubRoster` - Helper: `gotoSportas(id)`, `gotoKlubRoster(id)` ### Faza 10 — HNS Semafor scraper foundation - `/opt/pgz-sport/scrapers/hns_semafor.py` (10K7B) - Modes: `seed`, `klub`, `player `, `daily` - SEED_MAP: 16 PGŽ klubova → HNS COMET ID (NK Klana=1569, NK Krk=1558, NK Mune=1576, NK Vihor=4326, NK Doker=107415, HNK Kozala=3090, HNK Lovran=1574, HNK Goranin=1565, NK Risnjak=1583, NK Lokomotiva=1570, NK Omladinac Vrata=1579, NK Draga=1554, NK Zamet=1589, NK Vrbovsko=1588, NK Rikard Benčić=1582, NK OŠK Omišalj=3071) - BeautifulSoup parser za /igraci/{id}/{slug}/ - Auto-create klub ako ne postoji u DB - TEST: Marko Komadina (HNS#1167145) → clan_id=145 (Marko/Komadina/2012-10-14/Rijeka/photo OK/NK Klana) - Rate limit: 1.6s između zahtjeva, ASCII UA - TODO: roster discovery (match IDs → /utakmice/{id}/ → roster), ostali savezi (HRS/HKS/HVS) ### Faza 11 — Qdrant embedding - `/opt/pgz-sport/scrapers/embedder.py` (7K3B) - Kolekcija `pgz_sport_v1` (1024 dim, BGE-M3, Cosine) - Modes: `init`, `savezi`, `klubovi`, `sportasi`, `all` - Stable ID: SHA1(prefix:src_id) → uint63 - Embedded: 220 savezi + 1637 klubovi + 47 sportaši (uključujući Komadina) - Endpoint: `http://localhost:9879/api/embeddings` (BGE-M3 CUDA) ### Faza 12 — Cron jobs (autonomous learning) - `/etc/cron.d/pgz-sport`: - `0 4 * * *` — daily HNS Semafor scrape - `17 * * * *` — hourly embed klubovi refresh - `27 * * * *` — hourly embed sportasi refresh - `30 3 * * 0` — weekly embed savezi refresh - `0 2 * * 1` — weekly log truncation --- ## TOKEN ROTATION - GitHub PAT obnovljen: spremljen u `/opt/.env.rinet` (chmod 600) - Validan: dradulic - Damir Radulic - Git credential helper konfiguriran globally --- ## STANJE NAKON RUN-A ### Servisi - `pgz-sport.service` — active (port 8095) - BGE-M3 embedder — active (port 9879, CUDA) - Qdrant — active (port 6333) - PostgreSQL — active (port 5432, PgBouncer 6432) - Cron — active ### Live URLs - https://api.rinet.one/sport/ — frontend (211KB) - https://api.rinet.one/sport/api/v2/auth/* — auth - https://api.rinet.one/sport/api/v2/users/* — admin user mgmt - https://api.rinet.one/sport/api/v2/sportas/* — sportaš profil - https://api.rinet.one/sport/api/v2/klub/{id}/sportasi — roster ### File locations - Backend: `/opt/pgz-sport/pgz_sport_v2_router.py` (56K), `pgz_sport_api.py` (1308 linija v1) - Frontend: `/opt/pgz-sport/static/index.html` (~211KB) - Scrapers: `/opt/pgz-sport/scrapers/{hns_semafor.py, embedder.py}` - Backups: `/opt/pgz-sport/_backups/` (više backup-a po fazi) - Logs: `/opt/pgz-sport/_logs/{embedder.log, hns_scraper.log, hns_cron.log, embed_cron.log}` - Handoff: `/opt/pgz-sport/_handoff/HANDOFF_*.md` --- ## ŠTO PREOSTAJE (TODO za sutra) 1. **HNS klub roster discovery** — kroz match IDs (klub stranica daje match IDs → fetch /utakmice/{id}/ → extract roster). Trenutno `cmd_klub` u `hns_semafor.py` je placeholder. 2. **HRS/HKS/HVS scrapers** — slični semafor portali za rukomet/košarku/odbojku. Treba istražiti URL strukture. 3. **Klub fixes**: tajnici 7+8 (Davor Šimunović, Tihomir Brnčić) nemaju `klub_id` set. Treba ih povezati na njihove klubove (Kvarner i Zamet) za tenant filter da radi. 4. **Frontend permissions matrix UI** — backend endpoint postoji, frontend ne. 5. **Frontend klub-link UI** — assignment korisnik↔klub kroz user_klub_links. 6. **Sportaš ručni unos** — frontend forma za klubove da ručno upišu sportaše (već imaju /clanovi/create ali bez novih polja). 7. **HNS match log scrape** — utakmice_log još nije popunjen za nikoga (ni za Komadinu — to bi povećalo profil dramatično). 8. **HEAD / 405** — cosmetics (root endpoint ne podržava HEAD, GET only). --- ## TEHNIČKE KONSTANTE (uvijek valjane) - Bridge: `curl -X POST https://api.rinet.one/bridge/exec -H "X-API-KEY: rinet-yS4ZnKlwUqsjk"` (param `cmd`) - DB: host=localhost port=5432 (direct) ili 6432 (PgBouncer), dbname=rinet_v3, user=rinet, pwd=`R1net2026!SecureDB#v7` - Embed: `http://localhost:9879/api/embeddings` (BGE-M3, dim=1024) - Qdrant: `http://10.10.0.2:6333`, kolekcija `pgz_sport_v1` - Sport service: port 8095, systemd `pgz-sport.service` ## TEST SCENARIJ 1. Otvori `https://api.rinet.one/sport/` (hard refresh Ctrl+Shift+R) 2. Klik 🔐 PRIJAVA → email + pwd 3. Default users (svi `PgzSport2026!`): - pgz.djelatnik@pgz.hr (Marija, pgz_user) - pgz.finance@pgz.hr (pgz_finance) - tajnik.hnk@rijeka.hr (savez_admin) - sportas.test@example.hr (klub_clan) 4. Korisnici → vidi RBAC tenant filter na djelu 5. Test sportaš: `/sport/api/v2/sportas/145/profile` → Marko Komadina 6. Test roster: `/sport/api/v2/klub/2200/sportasi` → NK Klana ## KORISNIČKE LOZINKE STANJE (29.04.2026) | id | email | pwd state | |----|-------|-----------| | 1 | damir@rinet.one | original (super_admin) | | 2 | pgz.lukanovic@pgz.hr | **Damir je promijenio** tijekom testa | | 3 | pgz.djelatnik@pgz.hr | default + must_change | | 4 | pgz.finance@pgz.hr | default + must_change | | 5 | zzjz.medical@zzjzpgz.hr | default + must_change | | 6 | tajnik.hnk@rijeka.hr | default + must_change | | 7 | tajnik.kvarner@kk.hr | default + must_change | | 8 | tajnik.zamet@rk.hr | default + must_change | | 9 | sportas.test@example.hr | default + must_change |