diff --git a/_audit/audit_ENDPOINTS_ADDED.md b/_audit/audit_ENDPOINTS_ADDED.md new file mode 100644 index 0000000..fab5d34 --- /dev/null +++ b/_audit/audit_ENDPOINTS_ADDED.md @@ -0,0 +1,71 @@ +# CC4 Sub1 — FastAPI Endpoint Coverage Extension +**Author:** cc4-sub1@rinet.one (Damir Radulić — dradulic@outlook.com / damir@rinet.one) +**Date:** 2026-05-05 +**Source audit:** `/opt/pgz-sport/_audit/audit_20260505_023639/errors.json` (57 errors / 80 pages) + +## Audit-driven scope reduction + +The CC1 audit listed 57 errors. Filtering for genuine API gaps (`http_4xx_5xx` + console 404/405 referencing `/api/`): + +| Class | Count | Verdict | +|---|---:|---| +| `THREE.WebGLRenderer` console errors on `an_mreza` | 21 | Headless Chromium GPU sandbox issue, NOT API. Skipped. | +| Three.js deprecation warnings | 16 | Frontend asset issue, NOT API. Skipped. | +| `/static/uploads/avatars/99-68860ddb.png` 404 | 4 | Missing file, not endpoint. Avatar mount works (`/uploads/avatars/`). Frontend has stale hash. Skipped. | +| `/static/erp.html` 502 | 1 | Tested live: returns 200. Transient 502 in audit — public path issue (`/sport/static/erp.html` returns 404 publicly because nginx maps `/static` differently). Not a Python-API gap. Skipped. | +| `/sport/api/v2/img-proxy?u=...` 404 | 3 | Tested live (anon + auth) → 200. Already deployed (`routers/img_proxy_router.py`, mounted line 1431). Skipped. | + +After eliminating non-API noise, an **extended frontend-fetch sweep** (grep all `fetch(...)` calls in `/opt/pgz-sport/static/*.html`, then probe each with anon + JWT) surfaced these real API gaps: + +| Status | Path | Method | Notes | +|---|---|---|---| +| 404 | `/api/v2/klubovi` | GET | v2 alias missing; only legacy `/api/klubovi` existed | +| 404 | `/api/v2/savezi` | GET | v2 alias missing; only legacy `/api/savezi` existed | +| 404 | `/api/v2/sport` and `/api/v2/sport/` | GET | namespace index missing | +| 500 | `/api/v2/kategorizirani/list` | GET | SQL bug: column alias used in WHERE clause | + +## Endpoints added / fixed + +All changes in `/opt/pgz-sport/pgz_sport_v2_router.py` (no new router file — domain already existed). + +| Method | Path | File | Lines added | Auth | Audit log | +|---|---|---|---:|---|---| +| GET | `/api/v2/klubovi` | pgz_sport_v2_router.py | ~25 | optional (read-only) | n/a (read) | +| GET | `/api/v2/savezi` | pgz_sport_v2_router.py | ~22 | optional (read-only) | n/a (read) | +| GET | `/api/v2/sport` | pgz_sport_v2_router.py | ~12 | optional (read-only) | n/a (discovery) | +| GET | `/api/v2/sport/` | pgz_sport_v2_router.py | (alias) | optional | n/a | +| FIX | `/api/v2/kategorizirani/list` | pgz_sport_v2_router.py | -1/+1 | optional | n/a (read) | + +State-changing endpoints: **none added** (all gaps were read-only listings/aliases). No audit_log entries needed. + +## Status matrix (smoke test, post-deploy) + +| Endpoint | anon | auth (JWT) | public via nginx | +|---|---:|---:|---:| +| `/api/v2/klubovi` | 200 | 200 | 200 | +| `/api/v2/klubovi?q=` | 200 | 200 | — | +| `/api/v2/savezi` | 200 | 200 | 200 | +| `/api/v2/sport` | 200 | 200 | — | +| `/api/v2/sport/` | 200 | 200 | — | +| `/api/v2/kategorizirani/list` | 200 | 200 | — | + +All read-only — middleware allows anonymous GETs on `/api/v2/*` listings. + +## Skipped (not API gaps) + +- `/static/uploads/avatars/99-68860ddb.png` — file missing on disk. Real avatar exists with hash `99-3a8466b0.png`. Frontend or DB has stale URL. Out of scope (data, not API). +- `/static/erp.html` 502 — public infrastructure (nginx upstream) hiccup; locally returns 200. +- `/sport/api/v2/img-proxy?u=...` — already implemented in `routers/img_proxy_router.py`, returns 200 with placeholder PNG when origin 404s. +- THREE.WebGLRenderer console errors — headless Chrome GPU issue, not solvable on the API. +- Three.js deprecation warnings — frontend asset upgrade, separate ticket. +- Google Analytics / external CDN URLs — none seen in this audit. + +## Per-domain commit + +| Domain | Commit | Files | +|---|---|---| +| v2 listings + sport namespace | _(pending git commit at end of run)_ | pgz_sport_v2_router.py | + +## Backups + +- `/opt/pgz-sport/_backups/r3_cc4/pgz_sport_v2_router.py.bak.1777962063` diff --git a/pgz_sport_v2_router.py b/pgz_sport_v2_router.py index 9498629..e764f13 100644 --- a/pgz_sport_v2_router.py +++ b/pgz_sport_v2_router.py @@ -3087,7 +3087,7 @@ import json # ═══ HOO KATEGORIZIRANI SPORTAŠI ═══ @router.get("/kategorizirani/list") def list_kategorizirani(kategorija: Optional[str] = None, sport: Optional[str] = None): - where = ["c.kategorija_hoo AS hoo_kategorija IS NOT NULL"]; params = [] + where = ["c.hoo_kategorija IS NOT NULL"]; params = [] if kategorija: where.append("c.hoo_kategorija=%s"); params.append(kategorija) if sport: where.append("LOWER(c.sport)=LOWER(%s)"); params.append(sport) sql = f"""SELECT c.id, c.ime, c.prezime, c.hoo_kategorija, c.sport, @@ -5348,3 +5348,77 @@ def graph_3d_iframe(min_orgs: int = 2, top_n: int = 100, sport: str = ""): return f.read() return "