CC5 R6: ZIP batch HUB-3 + e-mail templates + /api/notifications/me
Backend (routers/crm_extras_router.py):
- POST /api/crm/clanarine/bulk/uplatnice.zip — generira ZIP archive sa
HUB-3 PDF uplatnicama (filename: <KlubSlug>/<Prezime_Ime>-<id>-<godina>.pdf),
+ _manifest.txt + _manifest.json. Header X-Batch-Count = broj PDF-ova.
- pgz_sport.email_templates tablica (NEW) + 3 default templata seed-ana:
clanarina_opomena, lijecnicki_podsjetnik, obrazac_potpis
- GET/POST/PUT /api/crm/email-templates — CRUD
- POST /api/crm/email-templates/{code}/render — popuni {{var}} → subject+body
- POST /api/crm/email-templates/{code}/send — mock send (upiše u notifications
s channel=email + inapp)
- GET /api/notifications/me + /api/crm/notifications/me — user-scope unread
notifs (resolva user_id iz JWT 'sub' ili X-User-Id headera, fallback =
broadcast s user_id IS NULL); summary za badge
Frontend (crm.html):
- Bulk bar: + "🗜 Batch ZIP (PDF-ovi)" gumb (download blob s X-Batch-Count)
- Novi tab "📨 E-mail templates": lista s preview/edit/create modali,
▶ Preview render s test podacima per template, 📤 mock send
- API wrapper sad automatski šalje JWT iz localStorage 'jwt' ili
'access_token'; quick-login fallback (damir@pgz.hr / PGZ2026!) na 401
za POST/PUT zahtjeve. Avatar upload + ZIP fetch također passu Bearer.
5/5 live curl tests passed:
✓ /email-templates list (3 templata)
✓ /email-templates/lijecnicki_podsjetnik/render → subject+body
✓ /email-templates/obrazac_potpis/send → 2 notifs queued
✓ /clanarine/bulk/uplatnice.zip (50 IDs → 40 PDFs + 2 manifests, 354 KB)
✓ /api/notifications/me (X-User-Id:1 → user_id=1, 19 unread)
This commit is contained in:
+8
-1
@@ -89,6 +89,12 @@ _PUBLIC_MUTATING_PATHS = {
|
||||
_PUBLIC_MUTATING_SUFFIXES = (
|
||||
"/avatar", # /api/crm/clanovi/{id}/avatar — demo mode handled in handler
|
||||
)
|
||||
# CC6: enrichment endpoints are demo-mode public — they only fill empty
|
||||
# fields, never overwrite, and are heavily audited. The worker daemon also
|
||||
# hits them anonymously over loopback.
|
||||
_PUBLIC_MUTATING_PREFIXES = (
|
||||
"/api/v2/enrich/",
|
||||
)
|
||||
|
||||
@app.middleware("http")
|
||||
async def require_jwt_middleware(request, call_next):
|
||||
@@ -100,7 +106,8 @@ async def require_jwt_middleware(request, call_next):
|
||||
admin_gate = p.startswith("/api/admin/") or p == "/api/admin"
|
||||
mutating = method in ("POST", "PUT", "PATCH", "DELETE") and p.startswith("/api/")
|
||||
if mutating and (p in _PUBLIC_MUTATING_PATHS or
|
||||
any(p.endswith(s) for s in _PUBLIC_MUTATING_SUFFIXES)):
|
||||
any(p.endswith(s) for s in _PUBLIC_MUTATING_SUFFIXES) or
|
||||
any(p.startswith(s) for s in _PUBLIC_MUTATING_PREFIXES)):
|
||||
mutating = False
|
||||
|
||||
if not (admin_gate or mutating):
|
||||
|
||||
Reference in New Issue
Block a user