Plan: CV Download Metrics Endpoint¶
Status: Planning Scope: Track CV download counts, expose via API
Goal¶
Track how many times the CV has been downloaded, broken down by locale (en/nl). Surfaced as a backend endpoint, visible in the admin area.
Approach Options¶
Option A — Database counter (recommended)¶
- Add a
cv_download_eventstable withtimestamp,locale,ip_hash - Increment on every
GET /api/download-cvcall - New endpoint
GET /api/metrics/cv-downloadsreturns{ total: N, byLocale: { en: X, nl: Y } } - Pros: already have Postgres + SQLAlchemy; no extra infra; persistent
- Cons: loses data if DB is reset; PII risk with IP (mitigated by hashing)
Option B — Application Insights custom event¶
- In
download_cvhandler:TelemetryClient.track_event("cv_download", {"locale": locale}) - Query endpoint calls KQL against Application Insights
- Pros: zero DB schema change; 90-day retention by default
- Cons: adds latency to metric reads; requires App Insights SDK wiring
Option C — Azure Monitor custom metric¶
- Emit via
azure-monitor-opentelemetry - Pros: integrates with existing dashboards
- Cons: overkill for a portfolio; cost at scale
Recommended: Option A¶
Schema¶
sql
New endpoints¶
GET /api/metrics/cv-downloads
→ { total: 42, byLocale: { english: 30, nederlands: 12 }, lastDownloadedAt: "..." }
Access: feature-flagged or require X-Admin-Token header (reuse API_ADMIN_TOKEN).
Changes required¶
- Alembic migration:
add_cv_download_events_table - SQLAlchemy model:
api/db/models/cv_download_event.py - Update
api/routers/cv.py→ insert row on each download - New router
api/routers/metrics.py→GET /api/metrics/cv-downloads - Pydantic response schema + OpenAPI type gen
- pytest tests: download increments counter; metrics returns correct total
Success Criteria¶
- [ ] Every
/api/download-cvcall creates a row incv_download_events - [ ]
GET /api/metrics/cv-downloadsreturns correct totals - [ ] IP is stored as SHA-256 hash, never plaintext
- [ ] Tests cover both download and metrics endpoints