Phase 1: auth, room CRUD, WebSocket chat, PWA frontend

Invite-only FastAPI + SQLAlchemy(async) + Postgres backend (session-cookie
auth via CLI-provisioned accounts, open-room CRUD, single-instance /ws/chat)
and a React + Vite PWA frontend (login, room list, chat view). Backend tests
pass against a local Postgres DB. See README.md and backend/README.md for
setup, and ARCHITECTURE.md for the full phased design.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-13 20:01:17 -06:00
co-authored by Claude Sonnet 5
parent 8ac35062dc
commit 99aa029c0d
77 changed files with 9042 additions and 0 deletions
+51
View File
@@ -0,0 +1,51 @@
import { createContext, useContext, useEffect, useState, type ReactNode } from 'react'
import * as authApi from '../api/auth'
import { ApiError } from '../api/client'
import type { User } from '../types'
interface AuthContextValue {
user: User | null
loading: boolean
login: (usernameOrEmail: string, password: string) => Promise<void>
logout: () => Promise<void>
}
const AuthContext = createContext<AuthContextValue | undefined>(undefined)
export function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<User | null>(null)
const [loading, setLoading] = useState(true)
useEffect(() => {
authApi
.me()
.then(setUser)
.catch((err) => {
if (!(err instanceof ApiError && err.status === 401)) {
console.error('Failed to load current user', err)
}
})
.finally(() => setLoading(false))
}, [])
async function login(usernameOrEmail: string, password: string) {
setUser(await authApi.login(usernameOrEmail, password))
}
async function logout() {
await authApi.logout()
setUser(null)
}
return (
<AuthContext.Provider value={{ user, loading, login, logout }}>
{children}
</AuthContext.Provider>
)
}
export function useAuth(): AuthContextValue {
const ctx = useContext(AuthContext)
if (!ctx) throw new Error('useAuth must be used within an AuthProvider')
return ctx
}