import { createAppToken, createInvite, listAppTokens, login, logout, registerUser, revokeAppToken } from "@sttracker/core/auth"; import { z } from "zod"; import { clearSessionCookie, json, noContent, route, sessionCookie } from "../http.ts"; const credentials = z.object({ username: z.string().min(1).max(64), password: z.string().min(8).max(1024), }); export const authRoutes = [ route({ method: "POST", path: "auth/register", summary: "Create an account. The first user becomes admin; later ones need an invite code.", auth: "none", rateLimited: true, body: credentials.extend({ displayName: z.string().min(1).max(100).optional(), inviteCode: z.string().max(200).optional() }).strict(), handler: async ({ deps, body }) => json(await registerUser(deps.db, body), 201), }), route({ method: "POST", path: "auth/login", summary: "Sign in; sets the stt_session cookie.", auth: "none", rateLimited: true, body: z.object({ username: z.string().max(64), password: z.string().max(1024) }).strict(), handler: async ({ deps, body }) => { const { user, sessionToken } = await login(deps.db, body.username, body.password); return json(user, 200, { "set-cookie": sessionCookie(sessionToken, deps.publicUrl) }); }, }), route({ method: "POST", path: "auth/logout", summary: "End the current session and clear the cookie.", auth: "session", handler: async ({ deps, sessionToken }) => { if (sessionToken) await logout(deps.db, sessionToken); return new Response(null, { status: 204, headers: { "set-cookie": clearSessionCookie(deps.publicUrl) } }); }, }), route({ method: "GET", path: "me", summary: "The signed-in user.", auth: "any:read", handler: async ({ user }) => user }), route({ method: "GET", path: "me/tokens", summary: "List your app tokens (metadata only).", auth: "session", handler: async ({ deps, user }) => listAppTokens(deps.db, user.id), }), route({ method: "POST", path: "me/tokens", summary: "Create an app token. The token is returned once.", auth: "session", body: z.object({ name: z.string().min(1).max(100), scopes: z.array(z.enum(["read", "write", "ingest"])).min(1) }).strict(), handler: async ({ deps, user, body }) => json(await createAppToken(deps.db, user.id, body.name, [...new Set(body.scopes)]), 201), }), route({ method: "DELETE", path: "me/tokens/:id", summary: "Revoke an app token.", auth: "session", handler: async ({ deps, user, params }) => { await revokeAppToken(deps.db, user.id, params.id!); return noContent(); }, }), route({ method: "POST", path: "admin/invites", summary: "Create an invite code (admin). The code is returned once.", auth: "session", body: z.object({ ttlDays: z.number().int().min(1).max(365).optional() }).strict().optional(), handler: async ({ deps, user, body }) => json({ code: await createInvite(deps.db, user.id, body?.ttlDays) }, 201), }), ];