import { auth, authRouteHandlers, canDeleteUserAccount, canUserSelfRegister, ensureSupportUserBootstrap, ensureUserUsername, hasOwnerUser, promoteFirstRegisteredUserToOwner, resolveEmailFromLoginIdentifier, } from "@/lib/auth/server" export const runtime = "nodejs" type AuthPostResponse = { user?: { id?: string role?: string email?: string name?: string username?: string } message?: string } function jsonResponse(payload: unknown, status: number): Response { return Response.json(payload, { status }) } async function parseJsonBody(request: Request): Promise | null> { return (await request.json().catch(() => null)) as Record | null } function buildJsonRequest(request: Request, body: Record): Request { const headers = new Headers(request.headers) headers.set("content-type", "application/json") return new Request(request.url, { method: request.method, headers, body: JSON.stringify(body), }) } function isDeleteUserAuthPath(pathname: string): boolean { const actionPrefix = "/api/auth/" const actionIndex = pathname.indexOf(actionPrefix) if (actionIndex === -1) { return false } const actionPath = pathname.slice(actionIndex + actionPrefix.length) return actionPath === "delete-user" || actionPath.startsWith("delete-user/") } async function guardProtectedAccountDeletion(request: Request): Promise { const pathname = new URL(request.url).pathname if (!isDeleteUserAuthPath(pathname)) { return null } const session = await auth.api .getSession({ headers: request.headers, }) .catch(() => null) const userId = session?.user?.id if (!userId) { return null } const allowed = await canDeleteUserAccount(userId) if (allowed) { return null } return jsonResponse( { message: "This account is protected and cannot be deleted.", }, 403, ) } async function handleSignInPost(request: Request): Promise { await ensureSupportUserBootstrap() const body = await parseJsonBody(request) const identifier = typeof body?.identifier === "string" ? body.identifier : null const rawEmail = typeof body?.email === "string" ? body.email : null const resolvedEmail = await resolveEmailFromLoginIdentifier(identifier ?? rawEmail) if (!resolvedEmail) { return jsonResponse( { message: "Invalid email or username.", }, 401, ) } const rewrittenBody = { ...(body ?? {}), email: resolvedEmail, } return authRouteHandlers.POST(buildJsonRequest(request, rewrittenBody)) } async function handleSignUpPost(request: Request): Promise { await ensureSupportUserBootstrap() const signUpBody = await parseJsonBody(request) const preferredUsername = typeof signUpBody?.username === "string" ? signUpBody.username : undefined const { username: _ignoredUsername, ...signUpBodyWithoutUsername } = signUpBody ?? {} const hadOwnerBeforeSignUp = await hasOwnerUser() const registrationEnabled = await canUserSelfRegister() if (!registrationEnabled) { return jsonResponse( { message: "Registration is currently disabled.", }, 403, ) } const response = await authRouteHandlers.POST( buildJsonRequest(request, { ...signUpBodyWithoutUsername, }), ) if (!response.ok) { return response } const payload = (await response .clone() .json() .catch(() => null)) as AuthPostResponse | null const userId = payload?.user?.id if (!userId) { return response } await ensureUserUsername(userId, { preferred: preferredUsername, fallbackEmail: payload?.user?.email, fallbackName: payload?.user?.name, }) if (hadOwnerBeforeSignUp || !payload?.user) { return response } const promoted = await promoteFirstRegisteredUserToOwner(userId) if (!promoted) { return jsonResponse( { message: "Initial owner registration window has just closed. Please sign in instead.", }, 409, ) } payload.user.role = "owner" return new Response(JSON.stringify(payload), { status: response.status, headers: response.headers, }) } export async function GET(request: Request): Promise { await ensureSupportUserBootstrap() const deletionGuardResponse = await guardProtectedAccountDeletion(request) if (deletionGuardResponse) { return deletionGuardResponse } return authRouteHandlers.GET(request) } export async function POST(request: Request): Promise { const pathname = new URL(request.url).pathname if (pathname.endsWith("/sign-in/email")) { return handleSignInPost(request) } if (pathname.endsWith("/sign-up/email")) { return handleSignUpPost(request) } await ensureSupportUserBootstrap() const deletionGuardResponse = await guardProtectedAccountDeletion(request) if (deletionGuardResponse) { return deletionGuardResponse } return authRouteHandlers.POST(request) } export async function PATCH(request: Request): Promise { await ensureSupportUserBootstrap() const deletionGuardResponse = await guardProtectedAccountDeletion(request) if (deletionGuardResponse) { return deletionGuardResponse } return authRouteHandlers.PATCH(request) } export async function PUT(request: Request): Promise { await ensureSupportUserBootstrap() const deletionGuardResponse = await guardProtectedAccountDeletion(request) if (deletionGuardResponse) { return deletionGuardResponse } return authRouteHandlers.PUT(request) } export async function DELETE(request: Request): Promise { await ensureSupportUserBootstrap() const deletionGuardResponse = await guardProtectedAccountDeletion(request) if (deletionGuardResponse) { return deletionGuardResponse } return authRouteHandlers.DELETE(request) }