feat(web): login/logout flow with session-aware UI (ATB-30) (#40)
* feat(web): implement login/logout flow with session-aware UI (ATB-30)
- Add session helper (lib/session.ts): getSession() fetches auth state
from AppView's /api/auth/session by forwarding the browser's Cookie
header server-to-server
- Add auth proxy (routes/auth.ts): proxies all /api/auth/* GET requests
to AppView (forwarding cookies + Set-Cookie headers), plus POST /logout
that revokes tokens and clears the session cookie
- Update BaseLayout to accept optional auth prop: renders "Log in" link
when unauthenticated, or handle + POST logout form when authenticated
- Implement full login page: handle input, AT Proto explanation, error
display from query param, redirects to / when already authenticated
- Convert all routes to factory functions createXRoutes(appviewUrl) so
session can be injected for auth-aware header rendering on every page
- Add auth-gated prompts: board page shows "Log in to start a topic",
topic page shows "Log in to reply" when unauthenticated
- Update fetchApi to accept cookieHeader option for forwarding cookies
in server-to-server API calls
- Add 30 new tests: session helper, auth proxy, login page, auth-aware
BaseLayout nav, updated route stubs with fetch mocking
* fix stuff
* fix(web): address ATB-30 PR review — error handling, logging, factory pattern
Critical fixes:
- auth proxy GET now catches AppView unreachable: login/callback redirect to
/login?error=..., session path returns 503 JSON
- logout bare catch replaced: logs network errors, re-throws programming errors,
checks logoutRes.ok and logs non-ok AppView responses
- decodeURIComponent in login.tsx wrapped in try-catch to avoid URIError crash
on malformed percent-encoding (e.g. %ZZ in query params)
Important fixes:
- getSession logs console.error for network/unexpected errors and for non-ok
non-401 AppView responses (operators can now distinguish AppView downtime from
normal expired sessions)
- createLoginRoutes and createAuthRoutes now accept appviewUrl param — consistent
factory DI pattern used by all other routes; routes/index.ts updated
- fetchApi wraps fetch() in try-catch and throws descriptive network error
Test coverage:
- Authenticated UI branch tests added for boards, topics, new-topic, home header
- auth.test.ts: AppView unreachable redirects/503, logout non-ok logging
- session.test.ts: non-ok non-401 logging, network error logging, 401 no-log
- api.test.ts: cookieHeader forwarding, network error message
* fix(web): address ATB-30 re-review — dead exports, TypeError guard, new tests
- Remove dead `export const` aliases (homeRoutes, boardsRoutes, topicsRoutes,
newTopicRoutes) — index.ts correctly uses factory functions + loadConfig;
the module-level aliases duplicated config logic and could mislead
- Add TypeError to logout re-throw guard per CLAUDE.md: programming TypeErrors
(e.g. fetch(undefined), bad URL) now propagate instead of being silently
logged as "network errors"; existing network failure tests use new Error(),
not TypeError, so graceful logout on ECONNREFUSED is unaffected
- Add test: ReferenceError from fetch() propagates out of logout handler —
Hono returns 500, cookie is not cleared
- Add test: GET /login?error=%ZZ returns 200 with %ZZ in error banner instead
of 500; verifies the decodeURIComponent try-catch fallback works
- Add JSDoc to fetchApi documenting the two error shapes callers must classify
(network error → 503, API error → map HTTP status)
* remove proxied auth routes
* we still need the auth routes, but just the logout