This guide covers admin authentication, dashboard features, and admin API usage.
- Generate admin password hash (PBKDF2):
node generate-password-hash.js "your-password"- Set secret for your target environment:
npx wrangler secret put ADMIN_PASSWORD_HASH --env staging
# and/or
npx wrangler secret put ADMIN_PASSWORD_HASH --env production- Open
/admin/login, authenticate, then use/admin.
- Passwords are verified against
ADMIN_PASSWORD_HASH. - Hash format is PBKDF2:
pbkdf2:<iterations>:<salt_hex>:<hash_hex>
- Primary: KV namespace (
AUTH_TOKENS) when configured. - Fallback: D1 table
admin_sessions. - Cookie settings:
HttpOnlySecureSameSite=StrictMax-Age=86400(24 hours)
- Record CRUD.
- News story CRUD.
- Single-record AI generation (
Generate AIbutton). - Bulk AI backfill (
Backfill Missing AIbutton). - Single-record location enrichment (
Enrich Locationbutton). - Bulk location backfill (
Backfill Missing Locationbutton). - Sentry test event button (if enabled in environment).
Generate AIqueues one record for story summaries + synthesis.
Backfill Missing AIcalls bulk queueing API repeatedly until no more eligible records.
- Controlled per environment by
AI_SUMMARY_AUTO_ON_SAVE. true: record/story create/update automatically queues AI work.false: only manual buttons/API queue jobs.
All endpoints require admin authentication (session cookie).
GET /admin/loginPOST /admin/loginPOST /admin/logout
GET /admin/api/recordsGET /admin/api/records/:idPOST /admin/api/recordsPUT /admin/api/records/:idDELETE /admin/api/records/:id
GET /admin/api/storiesGET /admin/api/stories/:idPOST /admin/api/storiesPUT /admin/api/stories/:idDELETE /admin/api/stories/:id
POST /admin/api/records/:id/summarize- queues one record
POST /admin/api/records/summarize-all- bulk queueing with options:
limit(1-100, default25)offset(default0)only_missing(defaulttrue)include_fallback(defaulttrue)
- bulk queueing with options:
POST /admin/api/records/:id/enrich-location- enriches one record with AI-verified city + optional geocode
- options:
force(defaultfalse)geocode(defaulttrue)min_confidence(0..1, optional)
POST /admin/api/records/enrich-location-all- bulk enrichment with options:
limit(1-50, default12)offset(default0)only_missing(defaulttrue)force(defaultfalse)geocode(defaulttrue)min_confidence(0..1, optional)
- bulk enrichment with options:
POST /admin/api/sentry-test- sends test exception to Sentry
- returns
412ifSENTRY_DSNis not configured in that environment
dateshould be entered as a 4-digit year (YYYY) for new/edited records.- Existing legacy values like
YYYY-01-01 ...are still accepted and displayed. - Story URL validation enforces public
http/httpsURLs and blocks localhost/private ranges.
Example create payload:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"date": "2024",
"name": "Example Incident",
"city": "Toronto",
"province": "ON",
"victims": 5,
"deaths": 3,
"injuries": 2,
"newsStories": [
{
"id": "story-uuid-here",
"url": "https://example.com/article",
"body_text": "",
"ai_summary": ""
}
]
}- Confirm
ADMIN_PASSWORD_HASHsecret is set for the deployed environment. - For local dev, ensure
.dev.varscontains a valid PBKDF2 hash string.
- Confirm cookies are enabled.
- Re-login to refresh session.
- Confirm
AUTH_TOKENSbinding exists or D1 fallback is operational.
- Ensure
AI_SUMMARY_ENABLED=true. - Ensure
SUMMARY_QUEUEbinding exists in that environment.
- Confirm
SENTRY_DSNsecret in that environment. - Use Worker logs (
wrangler tail) to verify endpoint call and returnedeventId.