"use client"; import { FormEvent, useEffect, useMemo, useState } from "react"; import { ConfigurationInitializer } from "@/app/ConfigurationInitializer"; import Home from "@/components/Home"; import { getApiUrl } from "@/utils/api"; import { formatFastApiDetail, UNAUTHORIZED_DETAIL } from "@/utils/authErrors"; import { toast } from "sonner"; type AuthStatus = { configured: boolean; authenticated: boolean; username: string | null; }; const initialStatus: AuthStatus = { configured: false, authenticated: false, username: null, }; export default function AuthGate() { const [status, setStatus] = useState(initialStatus); const [isLoading, setIsLoading] = useState(true); const [isSubmitting, setIsSubmitting] = useState(false); const [error, setError] = useState(null); const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); const [confirmPassword, setConfirmPassword] = useState(""); const isSetupMode = useMemo(() => !status.configured, [status.configured]); useEffect(() => { void refreshStatus(); }, []); useEffect(() => { if (typeof window === "undefined") { return; } const params = new URLSearchParams(window.location.search); if (params.get("reason") === "unauthorized") { toast.error("Unauthorized", { id: "auth-unauthorized-redirect", description: "Sign in to view this page.", duration: 5000, }); window.history.replaceState({}, "", window.location.pathname); } }, []); const refreshStatus = async () => { setIsLoading(true); setError(null); try { const response = await fetch(getApiUrl("/api/v1/auth/status"), { method: "GET", cache: "no-store", credentials: "include", }); if (!response.ok) { throw new Error("Could not load login state"); } const data = (await response.json()) as AuthStatus; setStatus({ configured: Boolean(data.configured), authenticated: Boolean(data.authenticated), username: data.username ?? null, }); } catch (fetchError) { console.error(fetchError); setError("Could not connect to the login service. Please refresh and try again."); } finally { setIsLoading(false); } }; const handleSubmit = async (event: FormEvent) => { event.preventDefault(); setError(null); const cleanedUsername = username.trim(); if (cleanedUsername.length < 3) { setError("Username must be at least 3 characters."); return; } if (password.length < 6) { setError("Password must be at least 6 characters."); return; } if (isSetupMode && password !== confirmPassword) { setError("Password confirmation does not match."); return; } setIsSubmitting(true); try { const response = await fetch( getApiUrl(isSetupMode ? "/api/v1/auth/setup" : "/api/v1/auth/login"), { method: "POST", credentials: "include", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ username: cleanedUsername, password, }), } ); const payload = await response.json(); if (!response.ok) { const detail = formatFastApiDetail(payload?.detail); if (response.status === 401) { setError(detail === UNAUTHORIZED_DETAIL ? UNAUTHORIZED_DETAIL : detail); } else { setError(detail || "Login failed. Please try again."); } return; } if (isSetupMode) { setStatus({ configured: true, authenticated: false, username: (payload as AuthStatus).username ?? cleanedUsername, }); setPassword(""); setConfirmPassword(""); toast.success("Account created", { description: "Sign in with your new username and password to continue.", duration: 6000, }); return; } setStatus({ configured: Boolean((payload as AuthStatus).configured), authenticated: Boolean((payload as AuthStatus).authenticated), username: (payload as AuthStatus).username ?? cleanedUsername, }); setPassword(""); setConfirmPassword(""); } catch (submitError) { console.error(submitError); setError("Login service is unavailable. Please try again in a moment."); } finally { setIsSubmitting(false); } }; if (isLoading) { return (
Presenton

Presenton

Preparing your workspace…

); } if (status.authenticated) { return ( ); } return (

Secure instance

{isSetupMode ? "Create your admin login" : "Sign in to continue"}

{isSetupMode ? "One-time setup for this deployment. You will use the same username and password on future visits." : "This deployment is protected. Enter your credentials to open the app."}

setUsername(event.target.value)} placeholder="your-admin-user" className="w-full rounded-[11px] border border-[#EDEEEF] bg-white px-4 py-3 font-syne text-sm text-black outline-none transition placeholder:text-[#999999] focus:border-[#a49cfc] focus:ring-2 focus:ring-[#5146E5]/20" disabled={isSubmitting} />
setPassword(event.target.value)} placeholder="At least 6 characters" className="w-full rounded-[11px] border border-[#EDEEEF] bg-white px-4 py-3 font-syne text-sm text-black outline-none transition placeholder:text-[#999999] focus:border-[#a49cfc] focus:ring-2 focus:ring-[#5146E5]/20" disabled={isSubmitting} />
{isSetupMode ? (
setConfirmPassword(event.target.value)} placeholder="Re-enter your password" className="w-full rounded-[11px] border border-[#EDEEEF] bg-white px-4 py-3 font-syne text-sm text-black outline-none transition placeholder:text-[#999999] focus:border-[#a49cfc] focus:ring-2 focus:ring-[#5146E5]/20" disabled={isSubmitting} />
) : null} {error ? (
{error}
) : null} {!isSetupMode && status.configured ? (

Setup is complete for this instance. Use the username and password you configured.

) : null}
); }