import { Router } from 'express'; import { db } from '../db/client'; import { users } from '../db/schema'; import { anyUsersExist, createSession, destroySession, failedLoginDelayMs, findUserByUsername, getSessionUser, hashPassword, sleep, validateNewCredentials, verifyPassword, } from '../auth'; const router = Router(); // Drives the client's auth gate: `configured: false` → show the first-run // setup screen; unauthenticated → login screen; otherwise `user` identifies // the session for the UI. router.get('/status', (req, res) => { const user = getSessionUser(req); res.json({ configured: anyUsersExist(), authenticated: user !== null, user: user ?? null, }); }); // First-run onboarding: creates the first account and signs it in. Locked to // the empty-users state so it can never be used to add accounts later (that's // POST /api/users, which requires a session). router.post('/setup', (req, res) => { if (anyUsersExist()) { res.status(409).json({ error: 'Setup has already been completed' }); return; } const { username, password } = req.body ?? {}; const invalid = validateNewCredentials(username, password); if (invalid) { res.status(400).json({ error: invalid }); return; } const created = db .insert(users) .values({ username: (username as string).trim(), passwordHash: hashPassword(password as string) }) .returning({ id: users.id, username: users.username }) .get(); createSession(res, created.id); res.status(201).json({ authenticated: true, user: created }); }); router.post('/login', async (req, res) => { const { username, password } = req.body ?? {}; const user = typeof username === 'string' ? findUserByUsername(username) : undefined; if (!user || typeof password !== 'string' || !verifyPassword(password, user.passwordHash)) { await sleep(failedLoginDelayMs); // flat delay = cheap brute-force damping res.status(401).json({ error: 'Incorrect username or password' }); return; } createSession(res, user.id); res.json({ authenticated: true, user: { id: user.id, username: user.username } }); }); router.post('/logout', (req, res) => { destroySession(req, res); res.json({ authenticated: false }); }); export default router;