/** * GET /_emdash/api/auth/invite/accept * * Validate an invite token and return invite data for the UI. * This is called when the invitee clicks the email link. */ import type { APIRoute } from "astro"; export const prerender = false; import { validateInvite, InviteError, roleFromLevel } from "@premium-cms/auth"; import { createKyselyAdapter } from "@premium-cms/auth/adapters/kysely"; import { apiError, apiSuccess, handleError } from "#api/error.js"; import { AuthzRepository } from "#db/repositories/authz.js"; export const GET: APIRoute = async ({ url, locals }) => { const { emdash } = locals; if (!emdash?.db) { return apiError("NOT_CONFIGURED", "EmDash is not initialized", 500); } const token = url.searchParams.get("token"); if (!token) { return apiError("MISSING_PARAM", "Token is required", 400); } try { const adapter = createKyselyAdapter(emdash.db); const invite = await validateInvite(adapter, token); return apiSuccess({ success: true, email: invite.email, role: invite.role, roleId: invite.roleId, roleName: await roleDisplayName(emdash.db, invite.roleId, invite.role), }); } catch (error) { if (error instanceof InviteError) { const statusMap: Record = { invalid_token: 404, token_expired: 410, user_exists: 409, }; return apiError(error.code.toUpperCase(), error.message, statusMap[error.code] ?? 400); } return handleError(error, "Failed to validate invite", "INVITE_VALIDATE_ERROR"); } }; /** Human name for a role, preferring the roles table over the legacy level. */ async function roleDisplayName( db: import("kysely").Kysely, roleId: string | null, level: number, ): Promise { if (roleId) { const summary = await new AuthzRepository(db).roleSummary(roleId); if (summary) return summary.name; } return roleFromLevel(level as 10 | 20 | 30 | 40 | 50) ?? "Unknown"; }