// app/api/auth/register/route.ts import { NextResponse } from "next/server"; const DIRECTUS = (process.env.DIRECTUS_URL || process.env.NEXT_PUBLIC_API_BASE_URL || "").replace(/\/$/, ""); const SERVICE_TOKEN = process.env.DIRECTUS_TOKEN_ADMIN_REGISTER || ""; const DEFAULT_ROLE = process.env.DIRECTUS_DEFAULT_ROLE || undefined; const SECURE = process.env.NODE_ENV === "production"; function bad(message: string, status = 400) { return NextResponse.json({ error: message }, { status }); } const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; async function directusLogin(email: string, password: string) { const r = await fetch(`${DIRECTUS}/auth/login`, { method: "POST", headers: { "Content-Type": "application/json", Accept: "application/json" }, body: JSON.stringify({ email, password }), cache: "no-store", }); const j = await r.json().catch(() => ({})); if (!r.ok) throw new Error(j?.errors?.[0]?.message || j?.message || `Login failed (${r.status})`); return j?.data || j; } export async function POST(req: Request) { try { if (!DIRECTUS) return bad("Missing DIRECTUS_URL/NEXT_PUBLIC_API_BASE_URL", 500); if (!SERVICE_TOKEN) return bad("Missing DIRECTUS_SERVICE_TOKEN / admin token", 500); const body = await req.json().catch(() => ({} as any)); const email = String(body?.email ?? "").trim().toLowerCase(); const username = String(body?.username ?? "").trim(); const password = String(body?.password ?? "").trim(); const confirm = String(body?.confirmPassword ?? body?.confirm ?? "").trim(); if (!email || !username || !password || !confirm) return bad("All fields are required"); if (!EMAIL_RE.test(email)) return bad("Enter a valid email address"); if (password.length < 8) return bad("Password must be at least 8 characters"); if (password !== confirm) return bad("Passwords do not match"); // Optional pre-check to return a friendly 409 instead of a generic Directus error const existsRes = await fetch( `${DIRECTUS}/users?filter[_or][0][email][_eq]=${encodeURIComponent(email)}` + `&filter[_or][1][username][_eq]=${encodeURIComponent(username)}` + `&fields=id,email,username&limit=1`, { headers: { Authorization: `Bearer ${SERVICE_TOKEN}`, Accept: "application/json", }, cache: "no-store", } ); const existsJson = await existsRes.json().catch(() => ({})); if (Array.isArray(existsJson?.data) && existsJson.data.length > 0) { return bad("Email or username already in use", 409); } // Create user with sane defaults const createPayload: any = { email, username, password, status: "active",, }; if (DEFAULT_ROLE) createPayload.role = DEFAULT_ROLE; const createRes = await fetch(`${DIRECTUS}/users`, { method: "POST", headers: { Authorization: `Bearer ${SERVICE_TOKEN}`, "Content-Type": "application/json", Accept: "application/json", }, body: JSON.stringify(createPayload), cache: "no-store", }); const cj = await createRes.json().catch(() => ({})); if (!createRes.ok) { const msg = cj?.errors?.[0]?.message || cj?.message || `User create failed (${createRes.status})`; return bad(msg, createRes.status || 500); } // Auto-login (email-based; directus expects "email" even though it's an identifier) const tokens = await directusLogin(email, password); const res = NextResponse.json({ ok: true, id: cj?.data?.id || null }, { status: 201 }); if (tokens?.access_token) { res.cookies.set("ma_at", tokens.access_token, { path: "/", httpOnly: true, sameSite: "lax", secure: SECURE, maxAge: 60 * 60, // 1h }); } if (tokens?.refresh_token) { res.cookies.set("ma_rt", tokens.refresh_token, { path: "/", httpOnly: true, sameSite: "lax", secure: SECURE, maxAge: 60 * 60 * 24 * 30, // 30d }); } return res; } catch (e: any) { return bad(e?.message || "Registration error", e?.status || 500); } }