import axios from "axios" import { NextResponse } from "next/server" import { validateToken } from "@/lib/utils" // This endpoint has two functions: // (1) Create a new LibreCloud user (Authentik, Email) // (2) Migrate a p0ntus mail account to a LibreCloud account (creates/migrates Authentik/Email) async function createEmail(email: string, password: string, migrate: boolean) { // Signup status check if (process.env.NEXT_PUBLIC_SIGNUP_ENABLED === "false") { return { success: false, message: "Signups are disabled" } } else if (process.env.NEXT_PUBLIC_SIGNUP_ENABLED === "true") { try { if (!process.env.MAIL_CONNECT_API_URL) { console.error("[!] Missing MAIL_CONNECT_API_URL environment variable") return { success: false, message: "Server configuration error" } } else { const response = await axios.post(`${process.env.MAIL_CONNECT_API_URL}/accounts/add`, { email, password, migrate, }) const responseData = response.data if (responseData.success) { return response.data } else if (responseData.error) { console.error("[!] Email creation failed:", responseData.error) return { success: false, message: responseData.error } } else { console.error("[!] Email creation failed with unknown error") return { success: false, message: "Failed to create email account" } } } } catch (error) { console.error("[!] Email creation error:", error) if (axios.isAxiosError(error)) { return { success: false, message: error.response?.data?.error || "Failed to connect to email service", } } return { success: false, message: "Failed to create email account" } } } else { return { success: false, message: "Account signup is not configured in your environment variables!" } } } async function deleteAuthentikUser(id: string) { const response = await axios.delete(`${process.env.AUTHENTIK_API_URL}/core/users/${id}/`, { headers: { "Content-Type": "application/json", Authorization: `Bearer ${process.env.AUTHENTIK_API_KEY}`, }, }) if (response.status === 204) { return true } else { return false } } export async function POST(request: Request) { if (process.env.NEXT_PUBLIC_SIGNUP_ENABLED === "true") { let atkCreated = false let userID = "" try { const body = await request.json() const { name, email, password, migrate, token } = body // Validate fields if (!name || !email || !password) { return NextResponse.json({ success: false, message: "The form you submitted is incomplete" }, { status: 400 }) } const tokenValidation = await validateToken(token) if (!tokenValidation.success) { console.error("Altcha validation failed:", tokenValidation.error) return NextResponse.json({ success: false, message: "Robot check failed, try refreshing" }, { status: 400 }) } if (!process.env.AUTHENTIK_API_URL || !process.env.AUTHENTIK_API_KEY) { console.error("Missing Authentik environment variables") return NextResponse.json({ success: false, message: "Server configuration error" }, { status: 500 }) } // Create Authentik user const genUser = email.split("@")[0] const userData = { username: genUser, name, is_active: true, groups: [ "b2c38bad-1d15-4ffd-b6d4-d95370a092ca" // this represents the "Users" group in Authentik ], email, type: "internal", } console.log("[i] Creating user in Authentik:", { username: genUser, email }) const response = await axios.request({ method: "post", maxBodyLength: Infinity, url: `${process.env.AUTHENTIK_API_URL}/core/users/`, headers: { "Content-Type": "application/json", Accept: "application/json", Authorization: `Bearer ${process.env.AUTHENTIK_API_KEY}`, }, data: JSON.stringify(userData), validateStatus: () => true, // capture response even for error status codes }) if (response.data?.detail) { console.error("[!] Authentik user creation issue:", response.data.detail) } if (response.status !== 201) { if (response.data.username && response.data.username[0] === "This field must be unique.") { return NextResponse.json({ success: false, message: "Username already exists" }, { status: 409 }) } console.error("Failed to create user in Authentik:", response.status, response.data) return NextResponse.json({ success: false, message: "Failed to create user account" }, { status: 500 }) } atkCreated = true // User created successfully, now set password userID = response.data.pk const updData = { password, } console.log("[i] Setting password for user:", userID) const updCfg = await axios.request({ method: "post", maxBodyLength: Number.POSITIVE_INFINITY, url: `${process.env.AUTHENTIK_API_URL}/core/users/${userID}/set_password/`, headers: { "Content-Type": "application/json", Authorization: `Bearer ${process.env.AUTHENTIK_API_KEY}`, }, data: updData, validateStatus: () => true, // capture response even for error status codes }) if (updCfg.data?.detail) { console.error("[!] Password setting issue:", updCfg.data.detail) return NextResponse.json({ success: false, message: "Invalid setting account password, contact support" }, { status: 400 }) } if (updCfg.status === 204) { // account created successfully, now create email console.log("[i] Creating email account for:", email) const emailRes = await createEmail(email, password, migrate) if (emailRes.success) { console.log("[S] Account creation successful for:", email) return NextResponse.json(emailRes, { status: 200 }) } else { console.error("[!] Email creation failed for:", email, emailRes.message) if (atkCreated) { const delRes = await deleteAuthentikUser(userID) if (delRes) { console.log("[i] Authentik user deleted successfully") } else { console.error("[!] Failed to delete Authentik user") } } return NextResponse.json({ success: false, message: "Failed to create email account" }, { status: 500 }) } } else if (updCfg.status === 400) { console.error("[!] Failed to set password:", updCfg.data) return NextResponse.json({ success: false, message: "Invalid password format" }, { status: 400 }) } else { console.error("[!] Unknown error setting password:", updCfg.status, updCfg.data) return NextResponse.json({ success: false, message: "Failed to complete account setup" }, { status: 500 }) } } catch (error: unknown) { if (axios.isAxiosError(error)) { if (error.response?.data?.detail) { console.error("[!] Request error with detail:", error.response.data.detail) if (atkCreated) { const delRes = await deleteAuthentikUser(userID) if (delRes) { console.log("[i] Authentik user deleted successfully") } else { console.error("[!] Failed to delete Authentik user") } } return NextResponse.json({ success: false, message: "Server error - Failed to create user" }, { status: 500 }) } else if (error.response?.data?.error) { console.error("[!] Request error (passed from Authentik):", error.response.data.error) if (atkCreated) { const delRes = await deleteAuthentikUser(userID) if (delRes) { console.log("[i] Authentik user deleted successfully") } else { console.error("[!] Failed to delete Authentik user") } } return NextResponse.json({ success: false, message: error.response.data.error }, { status: 500 }) } else if (error.code === "ECONNREFUSED" || error.code === "ENOTFOUND") { console.error("[!] Connection error:", error.message) if (atkCreated) { const delRes = await deleteAuthentikUser(userID) if (delRes) { console.log("[i] Authentik user deleted successfully") } else { console.error("[!] Failed to delete Authentik user") } } return NextResponse.json( { success: false, message: "Failed to connect to authentication service" }, { status: 503 }, ) } } console.error("[!] Unhandled error while creating user:", error) if (atkCreated) { const delRes = await deleteAuthentikUser(userID) if (delRes) { console.log("[i] Authentik user deleted successfully") } else { console.error("[!] Failed to delete Authentik user") } } return NextResponse.json({ success: false, message: "An unexpected error occurred" }, { status: 500 }) } } else if (process.env.NEXT_PUBLIC_SIGNUP_ENABLED === "false") { return NextResponse.json({ success: false, message: "Signups are disabled" }, { status: 403 }) } else { return NextResponse.json({ success: false, message: "Account signup is not configured in your environment variables!" }, { status: 500 }) } }