feat: add auth-state awareness and graceful degradation

- Add /api/auth/whoami endpoint check on app load
- Store auth state in Zustand (authenticated, username, loaded)
- Hide Contacts tab when unauthenticated
- Gate fetchNearbyContacts calls on auth.authenticated
- Replace Save button with Log in affordance when unauthenticated
- Add Login/Logout buttons to panel header
- Prevent any /api/contacts/* requests from firing when unauthenticated

Public functionality (search, routing, place details) remains
fully functional for unauthenticated users.
This commit is contained in:
Matt 2026-04-27 01:26:05 +00:00
commit 0d4a807a05
29 changed files with 13091 additions and 317 deletions

View file

@ -286,3 +286,27 @@ export async function fetchLandclass(lat, lon, signal) {
return null
}
}
// ── Auth API ──
/**
* Check authentication state via whoami endpoint.
* Uses redirect: manual to detect auth without triggering navigation.
* @returns {Promise<{authenticated: boolean, username: string|null}>}
*/
export async function fetchAuthState() {
try {
const resp = await fetch('/api/auth/whoami', { redirect: 'manual' })
// Redirect response means unauthenticated (Authentik SSO flow)
if (resp.type === 'opaqueredirect' || resp.status === 302) {
return { authenticated: false, username: null }
}
if (!resp.ok) {
return { authenticated: false, username: null }
}
return resp.json()
} catch {
return { authenticated: false, username: null }
}
}