- useAuth: session token from URL/localStorage, /api/auth/me validation, logout
- LoginPage: email form with POST /api/auth/request, error handling
- App.tsx: use useAuth hook, LoginPage, 'sent' confirmation route
- Fix: auto-route to game covers both 'login' and 'sent' routes when authenticated
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- server.js: Node HTTP server with all API routes, static file serving, SPA fallback
- Path traversal protection on static file serving
- Async handler wrapped with .catch() to prevent unhandled rejections
- readBody: size limit (1MB) + error handler
- letter validation: single [A-Z] char check on progress/mnemonics routes
- phrase length limit (500 chars) on POST /api/mnemonics
- requireAdmin reads ADMIN_PASSWORD at request time for testability
- logout uses session.token from requireAuth (not re-read from header)
- 51 server tests passing (auth, progress, admin routes + edge cases)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove module-level `let db` singleton; all query functions now close
over the local `instance` from their `initDb` call, preventing
cross-contamination when multiple callers each invoke `initDb`
- Add REFERENCES … ON DELETE CASCADE to profile_id in magic_tokens,
sessions, user_progress, user_mnemonics, and letter_stats so that
the already-enabled foreign_keys pragma is actually enforced by DDL
- recordAnswer: normalise letter to uppercase before storing stats so
lowercase 'a' and uppercase 'A' are never tracked separately
- updateLevel: call getOrCreateProgress first to avoid a silent no-op
UPDATE when no progress row exists yet
- saveMnemonic: replace SELECT-then-INSERT with an atomic UPSERT
(INSERT … ON CONFLICT DO UPDATE) and detect first-save by checking
whether created_at equals the current timestamp
- getSession: re-fetch the row after updating last_seen so the returned
object reflects the freshly written timestamp
- db.test.js: add tests for findProfileById, findProfileById (null),
updateProfileSeen, updateLevel (normal + no-row-yet), getAdminUsers,
and getAdminStats (21 tests total, all pass)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements Task 4 (TDD): wrote failing tests first, then the full SQLite
database layer covering profiles, magic tokens, sessions, user progress,
mnemonics, and letter stats. All 14 tests pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Use pendingCancel flag (consumed once) to suppress stop-before-play while
allowing subsequent play() calls to proceed normally. Add afterEach timer
cleanup, re-play test, and behavioral assertion for off().
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Complete Product Requirements Document with full technical specs
- MorseQuest adventure-based branding and visual identity
- All logos, icons, and brand assets ready for development
- Freemium business model ($9.95/year, first year free launch strategy)
- Ready for Keylink IT Forge platform deployment
Phase 1 MVP ready to begin (10-12 week timeline)