2025-11-04 18:07:13 +01:00
"use client"
import { useState , useEffect } from "react"
import { Button } from "./ui/button"
import { Input } from "./ui/input"
import { Label } from "./ui/label"
import { Card , CardContent , CardDescription , CardHeader , CardTitle } from "./ui/card"
import { Shield , Lock , User , AlertCircle , CheckCircle , Info } from "lucide-react"
import { getApiUrl } from "../lib/api-config"
export function Settings() {
const [ authEnabled , setAuthEnabled ] = useState ( false )
const [ loading , setLoading ] = useState ( false )
const [ error , setError ] = useState ( "" )
const [ success , setSuccess ] = useState ( "" )
// Setup form state
const [ showSetupForm , setShowSetupForm ] = useState ( false )
const [ username , setUsername ] = useState ( "" )
const [ password , setPassword ] = useState ( "" )
const [ confirmPassword , setConfirmPassword ] = useState ( "" )
// Change password form state
const [ showChangePassword , setShowChangePassword ] = useState ( false )
const [ currentPassword , setCurrentPassword ] = useState ( "" )
const [ newPassword , setNewPassword ] = useState ( "" )
const [ confirmNewPassword , setConfirmNewPassword ] = useState ( "" )
useEffect ( ( ) = > {
checkAuthStatus ( )
} , [ ] )
const checkAuthStatus = async ( ) = > {
try {
const response = await fetch ( getApiUrl ( "/api/auth/status" ) )
const data = await response . json ( )
setAuthEnabled ( data . auth_enabled || false )
} catch ( err ) {
console . error ( "Failed to check auth status:" , err )
}
}
const handleEnableAuth = async ( ) = > {
setError ( "" )
setSuccess ( "" )
if ( ! username || ! password ) {
setError ( "Please fill in all fields" )
return
}
if ( password !== confirmPassword ) {
setError ( "Passwords do not match" )
return
}
if ( password . length < 6 ) {
setError ( "Password must be at least 6 characters" )
return
}
setLoading ( true )
try {
const response = await fetch ( getApiUrl ( "/api/auth/setup" ) , {
method : "POST" ,
headers : { "Content-Type" : "application/json" } ,
body : JSON.stringify ( {
username ,
password ,
enable_auth : true ,
} ) ,
} )
const data = await response . json ( )
if ( ! response . ok ) {
throw new Error ( data . error || "Failed to enable authentication" )
}
// Save token
localStorage . setItem ( "proxmenux-auth-token" , data . token )
localStorage . setItem ( "proxmenux-auth-setup-complete" , "true" )
setSuccess ( "Authentication enabled successfully!" )
setAuthEnabled ( true )
setShowSetupForm ( false )
setUsername ( "" )
setPassword ( "" )
setConfirmPassword ( "" )
} catch ( err ) {
setError ( err instanceof Error ? err . message : "Failed to enable authentication" )
} finally {
setLoading ( false )
}
}
const handleDisableAuth = async ( ) = > {
if (
! confirm (
2025-11-07 19:25:36 +01:00
"¿Estás seguro de que quieres deshabilitar la autenticación? Esto eliminará la protección por contraseña de tu dashboard y tendrás que configurarla de nuevo si quieres reactivarla." ,
2025-11-04 18:07:13 +01:00
)
) {
return
}
setLoading ( true )
setError ( "" )
setSuccess ( "" )
try {
2025-11-07 19:25:36 +01:00
const response = await fetch ( getApiUrl ( "/api/auth/disable" ) , {
2025-11-04 18:07:13 +01:00
method : "POST" ,
2025-11-07 19:25:36 +01:00
headers : {
"Content-Type" : "application/json" ,
Authorization : ` Bearer ${ localStorage . getItem ( "proxmenux-auth-token" ) } ` ,
} ,
2025-11-04 18:07:13 +01:00
} )
2025-11-07 19:25:36 +01:00
const data = await response . json ( )
if ( ! response . ok ) {
throw new Error ( data . error || "Failed to disable authentication" )
}
2025-11-04 18:07:13 +01:00
localStorage . removeItem ( "proxmenux-auth-token" )
2025-11-07 19:25:36 +01:00
localStorage . removeItem ( "proxmenux-saved-username" )
localStorage . removeItem ( "proxmenux-saved-password" )
localStorage . removeItem ( "proxmenux-auth-setup-complete" )
setSuccess ( "¡Autenticación deshabilitada correctamente! La página se recargará..." )
// Recargar la página después de 1.5 segundos
setTimeout ( ( ) = > {
window . location . reload ( )
} , 1500 )
2025-11-04 18:07:13 +01:00
} catch ( err ) {
2025-11-07 19:25:36 +01:00
setError (
err instanceof Error ? err . message : "No se pudo deshabilitar la autenticación. Por favor, inténtalo de nuevo." ,
)
2025-11-04 18:07:13 +01:00
} finally {
setLoading ( false )
}
}
const handleChangePassword = async ( ) = > {
setError ( "" )
setSuccess ( "" )
if ( ! currentPassword || ! newPassword ) {
setError ( "Please fill in all fields" )
return
}
if ( newPassword !== confirmNewPassword ) {
setError ( "New passwords do not match" )
return
}
if ( newPassword . length < 6 ) {
setError ( "Password must be at least 6 characters" )
return
}
setLoading ( true )
try {
const response = await fetch ( getApiUrl ( "/api/auth/change-password" ) , {
method : "POST" ,
headers : {
"Content-Type" : "application/json" ,
Authorization : ` Bearer ${ localStorage . getItem ( "proxmenux-auth-token" ) } ` ,
} ,
body : JSON.stringify ( {
current_password : currentPassword ,
new_password : newPassword ,
} ) ,
} )
const data = await response . json ( )
if ( ! response . ok ) {
throw new Error ( data . error || "Failed to change password" )
}
// Update token if provided
if ( data . token ) {
localStorage . setItem ( "proxmenux-auth-token" , data . token )
}
setSuccess ( "Password changed successfully!" )
setShowChangePassword ( false )
setCurrentPassword ( "" )
setNewPassword ( "" )
setConfirmNewPassword ( "" )
} catch ( err ) {
setError ( err instanceof Error ? err . message : "Failed to change password" )
} finally {
setLoading ( false )
}
}
2025-11-04 19:13:47 +01:00
const handleLogout = ( ) = > {
localStorage . removeItem ( "proxmenux-auth-token" )
window . location . reload ( )
}
2025-11-04 18:07:13 +01:00
return (
< div className = "space-y-6" >
< div >
< h1 className = "text-3xl font-bold" > Settings < / h1 >
< p className = "text-muted-foreground mt-2" > Manage your dashboard security and preferences < / p >
< / div >
{ /* Authentication Settings */ }
< Card >
< CardHeader >
< div className = "flex items-center gap-2" >
< Shield className = "h-5 w-5 text-blue-500" / >
< CardTitle > Authentication < / CardTitle >
< / div >
< CardDescription > Protect your dashboard with username and password authentication < / CardDescription >
< / CardHeader >
< CardContent className = "space-y-4" >
{ error && (
< div className = "bg-red-500/10 border border-red-500/20 rounded-lg p-3 flex items-start gap-2" >
< AlertCircle className = "h-5 w-5 text-red-500 flex-shrink-0 mt-0.5" / >
< p className = "text-sm text-red-500" > { error } < / p >
< / div >
) }
{ success && (
< div className = "bg-green-500/10 border border-green-500/20 rounded-lg p-3 flex items-start gap-2" >
< CheckCircle className = "h-5 w-5 text-green-500 flex-shrink-0 mt-0.5" / >
< p className = "text-sm text-green-500" > { success } < / p >
< / div >
) }
< div className = "flex items-center justify-between p-4 bg-muted/50 rounded-lg" >
< div className = "flex items-center gap-3" >
< div
className = { ` w-10 h-10 rounded-full flex items-center justify-center ${ authEnabled ? "bg-green-500/10" : "bg-gray-500/10" } ` }
>
< Lock className = { ` h-5 w-5 ${ authEnabled ? "text-green-500" : "text-gray-500" } ` } / >
< / div >
< div >
< p className = "font-medium" > Authentication Status < / p >
< p className = "text-sm text-muted-foreground" >
{ authEnabled ? "Password protection is enabled" : "No password protection" }
< / p >
< / div >
< / div >
< div
className = { ` px-3 py-1 rounded-full text-sm font-medium ${ authEnabled ? "bg-green-500/10 text-green-500" : "bg-gray-500/10 text-gray-500" } ` }
>
{ authEnabled ? "Enabled" : "Disabled" }
< / div >
< / div >
{ ! authEnabled && ! showSetupForm && (
< div className = "space-y-3" >
< div className = "bg-blue-500/10 border border-blue-500/20 rounded-lg p-3 flex items-start gap-2" >
< Info className = "h-5 w-5 text-blue-500 flex-shrink-0 mt-0.5" / >
< p className = "text-sm text-blue-500" >
Enable authentication to protect your dashboard when accessing from non - private networks .
< / p >
< / div >
< Button onClick = { ( ) = > setShowSetupForm ( true ) } className = "w-full bg-blue-500 hover:bg-blue-600" >
< Shield className = "h-4 w-4 mr-2" / >
Enable Authentication
< / Button >
< / div >
) }
{ ! authEnabled && showSetupForm && (
< div className = "space-y-4 border border-border rounded-lg p-4" >
< h3 className = "font-semibold" > Setup Authentication < / h3 >
< div className = "space-y-2" >
< Label htmlFor = "setup-username" > Username < / Label >
< div className = "relative" >
< User className = "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" / >
< Input
id = "setup-username"
type = "text"
placeholder = "Enter username"
value = { username }
onChange = { ( e ) = > setUsername ( e . target . value ) }
className = "pl-10"
disabled = { loading }
/ >
< / div >
< / div >
< div className = "space-y-2" >
< Label htmlFor = "setup-password" > Password < / Label >
< div className = "relative" >
< Lock className = "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" / >
< Input
id = "setup-password"
type = "password"
placeholder = "Enter password (min 6 characters)"
value = { password }
onChange = { ( e ) = > setPassword ( e . target . value ) }
className = "pl-10"
disabled = { loading }
/ >
< / div >
< / div >
< div className = "space-y-2" >
< Label htmlFor = "setup-confirm-password" > Confirm Password < / Label >
< div className = "relative" >
< Lock className = "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" / >
< Input
id = "setup-confirm-password"
type = "password"
placeholder = "Confirm password"
value = { confirmPassword }
onChange = { ( e ) = > setConfirmPassword ( e . target . value ) }
className = "pl-10"
disabled = { loading }
/ >
< / div >
< / div >
< div className = "flex gap-2" >
< Button onClick = { handleEnableAuth } className = "flex-1 bg-blue-500 hover:bg-blue-600" disabled = { loading } >
{ loading ? "Enabling..." : "Enable" }
< / Button >
< Button onClick = { ( ) = > setShowSetupForm ( false ) } variant = "outline" className = "flex-1" disabled = { loading } >
Cancel
< / Button >
< / div >
< / div >
) }
{ authEnabled && (
< div className = "space-y-3" >
2025-11-04 19:13:47 +01:00
< Button onClick = { handleLogout } variant = "outline" className = "w-full bg-transparent" >
Logout
< / Button >
2025-11-04 18:07:13 +01:00
{ ! showChangePassword && (
< Button onClick = { ( ) = > setShowChangePassword ( true ) } variant = "outline" className = "w-full" >
< Lock className = "h-4 w-4 mr-2" / >
Change Password
< / Button >
) }
{ showChangePassword && (
< div className = "space-y-4 border border-border rounded-lg p-4" >
< h3 className = "font-semibold" > Change Password < / h3 >
< div className = "space-y-2" >
< Label htmlFor = "current-password" > Current Password < / Label >
< div className = "relative" >
< Lock className = "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" / >
< Input
id = "current-password"
type = "password"
placeholder = "Enter current password"
value = { currentPassword }
onChange = { ( e ) = > setCurrentPassword ( e . target . value ) }
className = "pl-10"
disabled = { loading }
/ >
< / div >
< / div >
< div className = "space-y-2" >
< Label htmlFor = "new-password" > New Password < / Label >
< div className = "relative" >
< Lock className = "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" / >
< Input
id = "new-password"
type = "password"
placeholder = "Enter new password (min 6 characters)"
value = { newPassword }
onChange = { ( e ) = > setNewPassword ( e . target . value ) }
className = "pl-10"
disabled = { loading }
/ >
< / div >
< / div >
< div className = "space-y-2" >
< Label htmlFor = "confirm-new-password" > Confirm New Password < / Label >
< div className = "relative" >
< Lock className = "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" / >
< Input
id = "confirm-new-password"
type = "password"
placeholder = "Confirm new password"
value = { confirmNewPassword }
onChange = { ( e ) = > setConfirmNewPassword ( e . target . value ) }
className = "pl-10"
disabled = { loading }
/ >
< / div >
< / div >
< div className = "flex gap-2" >
< Button
onClick = { handleChangePassword }
className = "flex-1 bg-blue-500 hover:bg-blue-600"
disabled = { loading }
>
{ loading ? "Changing..." : "Change Password" }
< / Button >
< Button
onClick = { ( ) = > setShowChangePassword ( false ) }
variant = "outline"
className = "flex-1"
disabled = { loading }
>
Cancel
< / Button >
< / div >
< / div >
) }
< Button onClick = { handleDisableAuth } variant = "destructive" className = "w-full" disabled = { loading } >
Disable Authentication
< / Button >
< / div >
) }
< / CardContent >
< / Card >
{ /* About Section */ }
< Card >
< CardHeader >
< CardTitle > About < / CardTitle >
< CardDescription > ProxMenux Monitor information < / CardDescription >
< / CardHeader >
< CardContent className = "space-y-2" >
< div className = "flex justify-between" >
< span className = "text-muted-foreground" > Version < / span >
< span className = "font-medium" > 1.0 . 1 < / span >
< / div >
< div className = "flex justify-between" >
< span className = "text-muted-foreground" > Build < / span >
< span className = "font-medium" > Debian Package < / span >
< / div >
< / CardContent >
< / Card >
< / div >
)
}