"use client"; import { useState, useEffect } from "react"; import { Card, Button, Input } from "@/shared/components"; import { useRouter } from "next/navigation"; export default function LoginPage() { const [password, setPassword] = useState(""); const [error, setError] = useState(""); const [resetHint, setResetHint] = useState(""); const [retryAfter, setRetryAfter] = useState(0); const [loading, setLoading] = useState(false); const [hasPassword, setHasPassword] = useState(null); const [authMode, setAuthMode] = useState("password"); const [oidcConfigured, setOidcConfigured] = useState(false); const [oidcLoginLabel, setOidcLoginLabel] = useState("Sign in with OIDC"); const [mustChange, setMustChange] = useState(false); const [newPassword, setNewPassword] = useState(""); const router = useRouter(); // Countdown for rate-limit useEffect(() => { if (retryAfter <= 0) return; const id = setInterval(() => setRetryAfter((s) => (s > 0 ? s - 1 : 0)), 1000); return () => clearInterval(id); }, [retryAfter]); useEffect(() => { async function checkAuth() { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 5000); const baseUrl = typeof window !== "undefined" ? window.location.origin : ""; try { const res = await fetch(`${baseUrl}/api/auth/status`, { signal: controller.signal, }); clearTimeout(timeoutId); if (res.ok) { const data = await res.json(); if (data.requireLogin === false) { router.push("/dashboard"); router.refresh(); return; } setHasPassword(!!data.hasPassword); setAuthMode(data.authMode || "password"); setOidcConfigured(data.oidcConfigured === true); setOidcLoginLabel(data.oidcLoginLabel || "Sign in with OIDC"); } else { // Safe fallback on non-OK response to avoid infinite loading state. setHasPassword(true); } } catch (err) { clearTimeout(timeoutId); setHasPassword(true); } } checkAuth(); }, [router]); const handleLogin = async (e) => { e.preventDefault(); setLoading(true); setError(""); setResetHint(""); try { const res = await fetch("/api/auth/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ password }), }); if (res.ok) { const data = await res.json(); if (data.mustChangePassword) { setMustChange(true); return; } router.push("/dashboard"); router.refresh(); } else { const data = await res.json(); setError(data.error || "Invalid password"); if (data.resetHint) setResetHint(data.resetHint); if (data.retryAfter) setRetryAfter(Number(data.retryAfter)); } } catch (err) { setError("An error occurred. Please try again."); } finally { setLoading(false); } }; // Force a new password before entering the dashboard (default + remote). const handleSetNewPassword = async (e) => { e.preventDefault(); setLoading(true); setError(""); try { const res = await fetch("/api/settings", { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ currentPassword: password, newPassword }), }); if (res.ok) { router.push("/dashboard"); router.refresh(); } else { const data = await res.json(); setError(data.error || "Failed to set password"); } } catch (err) { setError("An error occurred. Please try again."); } finally { setLoading(false); } }; const handleOidcLogin = () => { window.location.href = "/api/auth/oidc/start"; }; const oidcAvailable = oidcConfigured && ["oidc", "both"].includes(authMode); const passwordAvailable = authMode !== "oidc" || !oidcConfigured; // Show loading state while checking password if (hasPassword === null) { return (
Loading...
{authMode === "oidc" && oidcConfigured ? "Sign in with your OIDC provider to access the dashboard" : "Enter your password to access the dashboard"}
{error}
)}