revert code formating

This commit is contained in:
riri-314
2025-11-18 13:30:37 +01:00
parent f3b0784651
commit 9e72720bda

View File

@@ -1,16 +1,10 @@
"use client"; "use client"
import { useState, useEffect } from "react"; import { useState, useEffect } from "react"
import { Button } from "./ui/button"; import { Button } from "./ui/button"
import { Input } from "./ui/input"; import { Input } from "./ui/input"
import { Label } from "./ui/label"; import { Label } from "./ui/label"
import { import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "./ui/card"
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "./ui/card";
import { import {
Shield, Shield,
Lock, Lock,
@@ -26,66 +20,64 @@ import {
Eye, Eye,
EyeOff, EyeOff,
Ruler, Ruler,
} from "lucide-react"; } from "lucide-react"
import { APP_VERSION } from "./release-notes-modal"; import { APP_VERSION } from "./release-notes-modal"
import { getApiUrl, fetchApi } from "../lib/api-config"; import { getApiUrl, fetchApi } from "../lib/api-config"
import { TwoFactorSetup } from "./two-factor-setup"; import { TwoFactorSetup } from "./two-factor-setup"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select"
interface ProxMenuxTool { interface ProxMenuxTool {
key: string; key: string
name: string; name: string
enabled: boolean; enabled: boolean
} }
export function Settings() { export function Settings() {
const [authEnabled, setAuthEnabled] = useState(false); const [authEnabled, setAuthEnabled] = useState(false)
const [totpEnabled, setTotpEnabled] = useState(false); const [totpEnabled, setTotpEnabled] = useState(false)
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false)
const [error, setError] = useState(""); const [error, setError] = useState("")
const [success, setSuccess] = useState(""); const [success, setSuccess] = useState("")
// Setup form state // Setup form state
const [showSetupForm, setShowSetupForm] = useState(false); const [showSetupForm, setShowSetupForm] = useState(false)
const [username, setUsername] = useState(""); const [username, setUsername] = useState("")
const [password, setPassword] = useState(""); const [password, setPassword] = useState("")
const [confirmPassword, setConfirmPassword] = useState(""); const [confirmPassword, setConfirmPassword] = useState("")
// Change password form state // Change password form state
const [showChangePassword, setShowChangePassword] = useState(false); const [showChangePassword, setShowChangePassword] = useState(false)
const [currentPassword, setCurrentPassword] = useState(""); const [currentPassword, setCurrentPassword] = useState("")
const [newPassword, setNewPassword] = useState(""); const [newPassword, setNewPassword] = useState("")
const [confirmNewPassword, setConfirmNewPassword] = useState(""); const [confirmNewPassword, setConfirmNewPassword] = useState("")
const [show2FASetup, setShow2FASetup] = useState(false); const [show2FASetup, setShow2FASetup] = useState(false)
const [show2FADisable, setShow2FADisable] = useState(false); const [show2FADisable, setShow2FADisable] = useState(false)
const [disable2FAPassword, setDisable2FAPassword] = useState(""); const [disable2FAPassword, setDisable2FAPassword] = useState("")
const [proxmenuxTools, setProxmenuxTools] = useState<ProxMenuxTool[]>([]); const [proxmenuxTools, setProxmenuxTools] = useState<ProxMenuxTool[]>([])
const [loadingTools, setLoadingTools] = useState(true); const [loadingTools, setLoadingTools] = useState(true)
const [expandedVersions, setExpandedVersions] = useState< const [expandedVersions, setExpandedVersions] = useState<Record<string, boolean>>({
Record<string, boolean>
>({
[APP_VERSION]: true, // Current version expanded by default [APP_VERSION]: true, // Current version expanded by default
}); })
const [networkUnitSettings, setNetworkUnitSettings] = useState("Bytes"); const [networkUnitSettings, setNetworkUnitSettings] = useState("Bytes");
const [loadingUnitSettings, setLoadingUnitSettings] = useState(true); const [loadingUnitSettings, setLoadingUnitSettings] = useState(true);
// API Token state management // API Token state management
const [showApiTokenSection, setShowApiTokenSection] = useState(false); const [showApiTokenSection, setShowApiTokenSection] = useState(false)
const [apiToken, setApiToken] = useState(""); const [apiToken, setApiToken] = useState("")
const [apiTokenVisible, setApiTokenVisible] = useState(false); const [apiTokenVisible, setApiTokenVisible] = useState(false)
const [tokenPassword, setTokenPassword] = useState(""); const [tokenPassword, setTokenPassword] = useState("")
const [tokenTotpCode, setTokenTotpCode] = useState(""); const [tokenTotpCode, setTokenTotpCode] = useState("")
const [generatingToken, setGeneratingToken] = useState(false); const [generatingToken, setGeneratingToken] = useState(false)
const [tokenCopied, setTokenCopied] = useState(false); const [tokenCopied, setTokenCopied] = useState(false)
useEffect(() => { useEffect(() => {
checkAuthStatus(); checkAuthStatus()
loadProxmenuxTools(); loadProxmenuxTools()
getUnitsSettings(); getUnitsSettings();
}, []); }, [])
const changeNetworkUnit = (unit: string) => { const changeNetworkUnit = (unit: string) => {
localStorage.setItem("proxmenux-network-unit", unit); localStorage.setItem("proxmenux-network-unit", unit);
@@ -102,50 +94,50 @@ export function Settings() {
const checkAuthStatus = async () => { const checkAuthStatus = async () => {
try { try {
const response = await fetch(getApiUrl("/api/auth/status")); const response = await fetch(getApiUrl("/api/auth/status"))
const data = await response.json(); const data = await response.json()
setAuthEnabled(data.auth_enabled || false); setAuthEnabled(data.auth_enabled || false)
setTotpEnabled(data.totp_enabled || false); // Get 2FA status setTotpEnabled(data.totp_enabled || false) // Get 2FA status
} catch (err) { } catch (err) {
console.error("Failed to check auth status:", err); console.error("Failed to check auth status:", err)
} }
}; }
const loadProxmenuxTools = async () => { const loadProxmenuxTools = async () => {
try { try {
const response = await fetch(getApiUrl("/api/proxmenux/installed-tools")); const response = await fetch(getApiUrl("/api/proxmenux/installed-tools"))
const data = await response.json(); const data = await response.json()
if (data.success) { if (data.success) {
setProxmenuxTools(data.installed_tools || []); setProxmenuxTools(data.installed_tools || [])
} }
} catch (err) { } catch (err) {
console.error("Failed to load ProxMenux tools:", err); console.error("Failed to load ProxMenux tools:", err)
} finally { } finally {
setLoadingTools(false); setLoadingTools(false)
} }
}; }
const handleEnableAuth = async () => { const handleEnableAuth = async () => {
setError(""); setError("")
setSuccess(""); setSuccess("")
if (!username || !password) { if (!username || !password) {
setError("Please fill in all fields"); setError("Please fill in all fields")
return; return
} }
if (password !== confirmPassword) { if (password !== confirmPassword) {
setError("Passwords do not match"); setError("Passwords do not match")
return; return
} }
if (password.length < 6) { if (password.length < 6) {
setError("Password must be at least 6 characters"); setError("Password must be at least 6 characters")
return; return
} }
setLoading(true); setLoading(true)
try { try {
const response = await fetch(getApiUrl("/api/auth/setup"), { const response = await fetch(getApiUrl("/api/auth/setup"), {
@@ -156,155 +148,145 @@ export function Settings() {
password, password,
enable_auth: true, enable_auth: true,
}), }),
}); })
const data = await response.json(); const data = await response.json()
if (!response.ok) { if (!response.ok) {
throw new Error(data.error || "Failed to enable authentication"); throw new Error(data.error || "Failed to enable authentication")
} }
// Save token // Save token
localStorage.setItem("proxmenux-auth-token", data.token); localStorage.setItem("proxmenux-auth-token", data.token)
localStorage.setItem("proxmenux-auth-setup-complete", "true"); localStorage.setItem("proxmenux-auth-setup-complete", "true")
setSuccess("Authentication enabled successfully!"); setSuccess("Authentication enabled successfully!")
setAuthEnabled(true); setAuthEnabled(true)
setShowSetupForm(false); setShowSetupForm(false)
setUsername(""); setUsername("")
setPassword(""); setPassword("")
setConfirmPassword(""); setConfirmPassword("")
} catch (err) { } catch (err) {
setError( setError(err instanceof Error ? err.message : "Failed to enable authentication")
err instanceof Error ? err.message : "Failed to enable authentication"
);
} finally { } finally {
setLoading(false); setLoading(false)
} }
}; }
const handleDisableAuth = async () => { const handleDisableAuth = async () => {
if ( if (
!confirm( !confirm(
"Are you sure you want to disable authentication? This will remove password protection from your dashboard." "Are you sure you want to disable authentication? This will remove password protection from your dashboard.",
) )
) { ) {
return; return
} }
setLoading(true); setLoading(true)
setError(""); setError("")
setSuccess(""); setSuccess("")
try { try {
const token = localStorage.getItem("proxmenux-auth-token"); const token = localStorage.getItem("proxmenux-auth-token")
const response = await fetch(getApiUrl("/api/auth/disable"), { const response = await fetch(getApiUrl("/api/auth/disable"), {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
Authorization: `Bearer ${token}`, Authorization: `Bearer ${token}`,
}, },
}); })
const data = await response.json(); const data = await response.json()
if (!response.ok) { if (!response.ok) {
throw new Error(data.message || "Failed to disable authentication"); throw new Error(data.message || "Failed to disable authentication")
} }
localStorage.removeItem("proxmenux-auth-token"); localStorage.removeItem("proxmenux-auth-token")
localStorage.removeItem("proxmenux-auth-setup-complete"); localStorage.removeItem("proxmenux-auth-setup-complete")
setSuccess("Authentication disabled successfully! Reloading..."); setSuccess("Authentication disabled successfully! Reloading...")
setTimeout(() => { setTimeout(() => {
window.location.reload(); window.location.reload()
}, 1000); }, 1000)
} catch (err) { } catch (err) {
setError( setError(err instanceof Error ? err.message : "Failed to disable authentication. Please try again.")
err instanceof Error
? err.message
: "Failed to disable authentication. Please try again."
);
} finally { } finally {
setLoading(false); setLoading(false)
} }
}; }
const handleChangePassword = async () => { const handleChangePassword = async () => {
setError(""); setError("")
setSuccess(""); setSuccess("")
if (!currentPassword || !newPassword) { if (!currentPassword || !newPassword) {
setError("Please fill in all fields"); setError("Please fill in all fields")
return; return
} }
if (newPassword !== confirmNewPassword) { if (newPassword !== confirmNewPassword) {
setError("New passwords do not match"); setError("New passwords do not match")
return; return
} }
if (newPassword.length < 6) { if (newPassword.length < 6) {
setError("Password must be at least 6 characters"); setError("Password must be at least 6 characters")
return; return
} }
setLoading(true); setLoading(true)
try { try {
const response = await fetch(getApiUrl("/api/auth/change-password"), { const response = await fetch(getApiUrl("/api/auth/change-password"), {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
Authorization: `Bearer ${localStorage.getItem( Authorization: `Bearer ${localStorage.getItem("proxmenux-auth-token")}`,
"proxmenux-auth-token"
)}`,
}, },
body: JSON.stringify({ body: JSON.stringify({
current_password: currentPassword, current_password: currentPassword,
new_password: newPassword, new_password: newPassword,
}), }),
}); })
const data = await response.json(); const data = await response.json()
if (!response.ok) { if (!response.ok) {
throw new Error(data.error || "Failed to change password"); throw new Error(data.error || "Failed to change password")
} }
// Update token if provided // Update token if provided
if (data.token) { if (data.token) {
localStorage.setItem("proxmenux-auth-token", data.token); localStorage.setItem("proxmenux-auth-token", data.token)
} }
setSuccess("Password changed successfully!"); setSuccess("Password changed successfully!")
setShowChangePassword(false); setShowChangePassword(false)
setCurrentPassword(""); setCurrentPassword("")
setNewPassword(""); setNewPassword("")
setConfirmNewPassword(""); setConfirmNewPassword("")
} catch (err) { } catch (err) {
setError( setError(err instanceof Error ? err.message : "Failed to change password")
err instanceof Error ? err.message : "Failed to change password"
);
} finally { } finally {
setLoading(false); setLoading(false)
} }
}; }
const handleDisable2FA = async () => { const handleDisable2FA = async () => {
setError(""); setError("")
setSuccess(""); setSuccess("")
if (!disable2FAPassword) { if (!disable2FAPassword) {
setError("Please enter your password"); setError("Please enter your password")
return; return
} }
setLoading(true); setLoading(true)
try { try {
const token = localStorage.getItem("proxmenux-auth-token"); const token = localStorage.getItem("proxmenux-auth-token")
const response = await fetch(getApiUrl("/api/auth/totp/disable"), { const response = await fetch(getApiUrl("/api/auth/totp/disable"), {
method: "POST", method: "POST",
headers: { headers: {
@@ -312,47 +294,47 @@ export function Settings() {
Authorization: `Bearer ${token}`, Authorization: `Bearer ${token}`,
}, },
body: JSON.stringify({ password: disable2FAPassword }), body: JSON.stringify({ password: disable2FAPassword }),
}); })
const data = await response.json(); const data = await response.json()
if (!response.ok) { if (!response.ok) {
throw new Error(data.message || "Failed to disable 2FA"); throw new Error(data.message || "Failed to disable 2FA")
} }
setSuccess("2FA disabled successfully!"); setSuccess("2FA disabled successfully!")
setTotpEnabled(false); setTotpEnabled(false)
setShow2FADisable(false); setShow2FADisable(false)
setDisable2FAPassword(""); setDisable2FAPassword("")
checkAuthStatus(); checkAuthStatus()
} catch (err) { } catch (err) {
setError(err instanceof Error ? err.message : "Failed to disable 2FA"); setError(err instanceof Error ? err.message : "Failed to disable 2FA")
} finally { } finally {
setLoading(false); setLoading(false)
} }
}; }
const handleLogout = () => { const handleLogout = () => {
localStorage.removeItem("proxmenux-auth-token"); localStorage.removeItem("proxmenux-auth-token")
localStorage.removeItem("proxmenux-auth-setup-complete"); localStorage.removeItem("proxmenux-auth-setup-complete")
window.location.reload(); window.location.reload()
}; }
const handleGenerateApiToken = async () => { const handleGenerateApiToken = async () => {
setError(""); setError("")
setSuccess(""); setSuccess("")
if (!tokenPassword) { if (!tokenPassword) {
setError("Please enter your password"); setError("Please enter your password")
return; return
} }
if (totpEnabled && !tokenTotpCode) { if (totpEnabled && !tokenTotpCode) {
setError("Please enter your 2FA code"); setError("Please enter your 2FA code")
return; return
} }
setGeneratingToken(true); setGeneratingToken(true)
try { try {
const data = await fetchApi("/api/auth/generate-api-token", { const data = await fetchApi("/api/auth/generate-api-token", {
@@ -362,55 +344,47 @@ export function Settings() {
password: tokenPassword, password: tokenPassword,
totp_token: totpEnabled ? tokenTotpCode : undefined, totp_token: totpEnabled ? tokenTotpCode : undefined,
}), }),
}); })
if (!data.success) { if (!data.success) {
setError(data.message || data.error || "Failed to generate API token"); setError(data.message || data.error || "Failed to generate API token")
return; return
} }
if (!data.token) { if (!data.token) {
setError("No token received from server"); setError("No token received from server")
return; return
} }
setApiToken(data.token); setApiToken(data.token)
setSuccess( setSuccess("API token generated successfully! Make sure to copy it now as you won't be able to see it again.")
"API token generated successfully! Make sure to copy it now as you won't be able to see it again." setTokenPassword("")
); setTokenTotpCode("")
setTokenPassword("");
setTokenTotpCode("");
} catch (err) { } catch (err) {
setError( setError(err instanceof Error ? err.message : "Failed to generate API token. Please try again.")
err instanceof Error
? err.message
: "Failed to generate API token. Please try again."
);
} finally { } finally {
setGeneratingToken(false); setGeneratingToken(false)
} }
}; }
const copyApiToken = () => { const copyApiToken = () => {
navigator.clipboard.writeText(apiToken); navigator.clipboard.writeText(apiToken)
setTokenCopied(true); setTokenCopied(true)
setTimeout(() => setTokenCopied(false), 2000); setTimeout(() => setTokenCopied(false), 2000)
}; }
const toggleVersion = (version: string) => { const toggleVersion = (version: string) => {
setExpandedVersions((prev) => ({ setExpandedVersions((prev) => ({
...prev, ...prev,
[version]: !prev[version], [version]: !prev[version],
})); }))
}; }
return ( return (
<div className="space-y-6"> <div className="space-y-6">
<div> <div>
<h1 className="text-3xl font-bold">Settings</h1> <h1 className="text-3xl font-bold">Settings</h1>
<p className="text-muted-foreground mt-2"> <p className="text-muted-foreground mt-2">Manage your dashboard security and preferences</p>
Manage your dashboard security and preferences
</p>
</div> </div>
{/* Authentication Settings */} {/* Authentication Settings */}
@@ -420,9 +394,7 @@ export function Settings() {
<Shield className="h-5 w-5 text-blue-500" /> <Shield className="h-5 w-5 text-blue-500" />
<CardTitle>Authentication</CardTitle> <CardTitle>Authentication</CardTitle>
</div> </div>
<CardDescription> <CardDescription>Protect your dashboard with username and password authentication</CardDescription>
Protect your dashboard with username and password authentication
</CardDescription>
</CardHeader> </CardHeader>
<CardContent className="space-y-4"> <CardContent className="space-y-4">
{error && ( {error && (
@@ -442,31 +414,19 @@ export function Settings() {
<div className="flex items-center justify-between p-4 bg-muted/50 rounded-lg"> <div className="flex items-center justify-between p-4 bg-muted/50 rounded-lg">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<div <div
className={`w-10 h-10 rounded-full flex items-center justify-center ${ className={`w-10 h-10 rounded-full flex items-center justify-center ${authEnabled ? "bg-green-500/10" : "bg-gray-500/10"}`}
authEnabled ? "bg-green-500/10" : "bg-gray-500/10"
}`}
> >
<Lock <Lock className={`h-5 w-5 ${authEnabled ? "text-green-500" : "text-gray-500"}`} />
className={`h-5 w-5 ${
authEnabled ? "text-green-500" : "text-gray-500"
}`}
/>
</div> </div>
<div> <div>
<p className="font-medium">Authentication Status</p> <p className="font-medium">Authentication Status</p>
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">
{authEnabled {authEnabled ? "Password protection is enabled" : "No password protection"}
? "Password protection is enabled"
: "No password protection"}
</p> </p>
</div> </div>
</div> </div>
<div <div
className={`px-3 py-1 rounded-full text-sm font-medium ${ 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
? "bg-green-500/10 text-green-500"
: "bg-gray-500/10 text-gray-500"
}`}
> >
{authEnabled ? "Enabled" : "Disabled"} {authEnabled ? "Enabled" : "Disabled"}
</div> </div>
@@ -477,14 +437,10 @@ export function Settings() {
<div className="bg-blue-500/10 border border-blue-500/20 rounded-lg p-3 flex items-start gap-2"> <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" /> <Info className="h-5 w-5 text-blue-500 flex-shrink-0 mt-0.5" />
<p className="text-sm text-blue-500"> <p className="text-sm text-blue-500">
Enable authentication to protect your dashboard when accessing Enable authentication to protect your dashboard when accessing from non-private networks.
from non-private networks.
</p> </p>
</div> </div>
<Button <Button onClick={() => setShowSetupForm(true)} className="w-full bg-blue-500 hover:bg-blue-600">
onClick={() => setShowSetupForm(true)}
className="w-full bg-blue-500 hover:bg-blue-600"
>
<Shield className="h-4 w-4 mr-2" /> <Shield className="h-4 w-4 mr-2" />
Enable Authentication Enable Authentication
</Button> </Button>
@@ -544,19 +500,10 @@ export function Settings() {
</div> </div>
<div className="flex gap-2"> <div className="flex gap-2">
<Button <Button onClick={handleEnableAuth} className="flex-1 bg-blue-500 hover:bg-blue-600" disabled={loading}>
onClick={handleEnableAuth}
className="flex-1 bg-blue-500 hover:bg-blue-600"
disabled={loading}
>
{loading ? "Enabling..." : "Enable"} {loading ? "Enabling..." : "Enable"}
</Button> </Button>
<Button <Button onClick={() => setShowSetupForm(false)} variant="outline" className="flex-1" disabled={loading}>
onClick={() => setShowSetupForm(false)}
variant="outline"
className="flex-1"
disabled={loading}
>
Cancel Cancel
</Button> </Button>
</div> </div>
@@ -565,21 +512,13 @@ export function Settings() {
{authEnabled && ( {authEnabled && (
<div className="space-y-3"> <div className="space-y-3">
<Button <Button onClick={handleLogout} variant="outline" className="w-full bg-transparent">
onClick={handleLogout}
variant="outline"
className="w-full bg-transparent"
>
<LogOut className="h-4 w-4 mr-2" /> <LogOut className="h-4 w-4 mr-2" />
Logout Logout
</Button> </Button>
{!showChangePassword && ( {!showChangePassword && (
<Button <Button onClick={() => setShowChangePassword(true)} variant="outline" className="w-full">
onClick={() => setShowChangePassword(true)}
variant="outline"
className="w-full"
>
<Lock className="h-4 w-4 mr-2" /> <Lock className="h-4 w-4 mr-2" />
Change Password Change Password
</Button> </Button>
@@ -622,9 +561,7 @@ export function Settings() {
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="confirm-new-password"> <Label htmlFor="confirm-new-password">Confirm New Password</Label>
Confirm New Password
</Label>
<div className="relative"> <div className="relative">
<Lock className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" /> <Lock className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
<Input <Input
@@ -664,21 +601,15 @@ export function Settings() {
<div className="bg-blue-500/10 border border-blue-500/20 rounded-lg p-3 flex items-start gap-2"> <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" /> <Info className="h-5 w-5 text-blue-500 flex-shrink-0 mt-0.5" />
<div className="text-sm text-blue-400"> <div className="text-sm text-blue-400">
<p className="font-medium mb-1"> <p className="font-medium mb-1">Two-Factor Authentication (2FA)</p>
Two-Factor Authentication (2FA)
</p>
<p className="text-blue-300"> <p className="text-blue-300">
Add an extra layer of security by requiring a code from Add an extra layer of security by requiring a code from your authenticator app in addition to
your authenticator app in addition to your password. your password.
</p> </p>
</div> </div>
</div> </div>
<Button <Button onClick={() => setShow2FASetup(true)} variant="outline" className="w-full">
onClick={() => setShow2FASetup(true)}
variant="outline"
className="w-full"
>
<Shield className="h-4 w-4 mr-2" /> <Shield className="h-4 w-4 mr-2" />
Enable Two-Factor Authentication Enable Two-Factor Authentication
</Button> </Button>
@@ -689,17 +620,11 @@ export function Settings() {
<div className="space-y-3"> <div className="space-y-3">
<div className="bg-green-500/10 border border-green-500/20 rounded-lg p-3 flex items-center gap-2"> <div className="bg-green-500/10 border border-green-500/20 rounded-lg p-3 flex items-center gap-2">
<CheckCircle className="h-5 w-5 text-green-500" /> <CheckCircle className="h-5 w-5 text-green-500" />
<p className="text-sm text-green-500 font-medium"> <p className="text-sm text-green-500 font-medium">2FA is enabled</p>
2FA is enabled
</p>
</div> </div>
{!show2FADisable && ( {!show2FADisable && (
<Button <Button onClick={() => setShow2FADisable(true)} variant="outline" className="w-full">
onClick={() => setShow2FADisable(true)}
variant="outline"
className="w-full"
>
<Shield className="h-4 w-4 mr-2" /> <Shield className="h-4 w-4 mr-2" />
Disable 2FA Disable 2FA
</Button> </Button>
@@ -707,12 +632,8 @@ export function Settings() {
{show2FADisable && ( {show2FADisable && (
<div className="space-y-4 border border-border rounded-lg p-4"> <div className="space-y-4 border border-border rounded-lg p-4">
<h3 className="font-semibold"> <h3 className="font-semibold">Disable Two-Factor Authentication</h3>
Disable Two-Factor Authentication <p className="text-sm text-muted-foreground">Enter your password to confirm</p>
</h3>
<p className="text-sm text-muted-foreground">
Enter your password to confirm
</p>
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="disable-2fa-password">Password</Label> <Label htmlFor="disable-2fa-password">Password</Label>
@@ -723,9 +644,7 @@ export function Settings() {
type="password" type="password"
placeholder="Enter your password" placeholder="Enter your password"
value={disable2FAPassword} value={disable2FAPassword}
onChange={(e) => onChange={(e) => setDisable2FAPassword(e.target.value)}
setDisable2FAPassword(e.target.value)
}
className="pl-10" className="pl-10"
disabled={loading} disabled={loading}
/> />
@@ -733,19 +652,14 @@ export function Settings() {
</div> </div>
<div className="flex gap-2"> <div className="flex gap-2">
<Button <Button onClick={handleDisable2FA} variant="destructive" className="flex-1" disabled={loading}>
onClick={handleDisable2FA}
variant="destructive"
className="flex-1"
disabled={loading}
>
{loading ? "Disabling..." : "Disable 2FA"} {loading ? "Disabling..." : "Disable 2FA"}
</Button> </Button>
<Button <Button
onClick={() => { onClick={() => {
setShow2FADisable(false); setShow2FADisable(false)
setDisable2FAPassword(""); setDisable2FAPassword("")
setError(""); setError("")
}} }}
variant="outline" variant="outline"
className="flex-1" className="flex-1"
@@ -759,12 +673,7 @@ export function Settings() {
</div> </div>
)} )}
<Button <Button onClick={handleDisableAuth} variant="destructive" className="w-full" disabled={loading}>
onClick={handleDisableAuth}
variant="destructive"
className="w-full"
disabled={loading}
>
Disable Authentication Disable Authentication
</Button> </Button>
</div> </div>
@@ -781,8 +690,7 @@ export function Settings() {
<CardTitle>API Access Tokens</CardTitle> <CardTitle>API Access Tokens</CardTitle>
</div> </div>
<CardDescription> <CardDescription>
Generate long-lived API tokens for external integrations like Generate long-lived API tokens for external integrations like Homepage and Home Assistant
Homepage and Home Assistant
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent className="space-y-4"> <CardContent className="space-y-4">
@@ -816,10 +724,7 @@ export function Settings() {
</div> </div>
{!showApiTokenSection && !apiToken && ( {!showApiTokenSection && !apiToken && (
<Button <Button onClick={() => setShowApiTokenSection(true)} className="w-full bg-purple-500 hover:bg-purple-600">
onClick={() => setShowApiTokenSection(true)}
className="w-full bg-purple-500 hover:bg-purple-600"
>
<Key className="h-4 w-4 mr-2" /> <Key className="h-4 w-4 mr-2" />
Generate New API Token Generate New API Token
</Button> </Button>
@@ -877,10 +782,10 @@ export function Settings() {
</Button> </Button>
<Button <Button
onClick={() => { onClick={() => {
setShowApiTokenSection(false); setShowApiTokenSection(false)
setTokenPassword(""); setTokenPassword("")
setTokenTotpCode(""); setTokenTotpCode("")
setError(""); setError("")
}} }}
variant="outline" variant="outline"
className="flex-1" className="flex-1"
@@ -927,23 +832,10 @@ export function Settings() {
onClick={() => setApiTokenVisible(!apiTokenVisible)} onClick={() => setApiTokenVisible(!apiTokenVisible)}
className="h-7 w-7 p-0" className="h-7 w-7 p-0"
> >
{apiTokenVisible ? ( {apiTokenVisible ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
<EyeOff className="h-4 w-4" />
) : (
<Eye className="h-4 w-4" />
)}
</Button> </Button>
<Button <Button size="sm" variant="ghost" onClick={copyApiToken} className="h-7 w-7 p-0">
size="sm" <Copy className={`h-4 w-4 ${tokenCopied ? "text-green-500" : ""}`} />
variant="ghost"
onClick={copyApiToken}
className="h-7 w-7 p-0"
>
<Copy
className={`h-4 w-4 ${
tokenCopied ? "text-green-500" : ""
}`}
/>
</Button> </Button>
</div> </div>
</div> </div>
@@ -958,21 +850,18 @@ export function Settings() {
<div className="space-y-2"> <div className="space-y-2">
<p className="text-sm font-medium">How to use this token:</p> <p className="text-sm font-medium">How to use this token:</p>
<div className="bg-muted/50 rounded p-3 text-xs font-mono"> <div className="bg-muted/50 rounded p-3 text-xs font-mono">
<p className="text-muted-foreground mb-2"> <p className="text-muted-foreground mb-2"># Add to request headers:</p>
# Add to request headers:
</p>
<p>Authorization: Bearer YOUR_TOKEN_HERE</p> <p>Authorization: Bearer YOUR_TOKEN_HERE</p>
</div> </div>
<p className="text-xs text-muted-foreground"> <p className="text-xs text-muted-foreground">
See the README documentation for complete integration See the README documentation for complete integration examples with Homepage and Home Assistant.
examples with Homepage and Home Assistant.
</p> </p>
</div> </div>
<Button <Button
onClick={() => { onClick={() => {
setApiToken(""); setApiToken("")
setShowApiTokenSection(false); setShowApiTokenSection(false)
}} }}
variant="outline" variant="outline"
className="w-full" className="w-full"
@@ -1028,9 +917,7 @@ export function Settings() {
<Wrench className="h-5 w-5 text-orange-500" /> <Wrench className="h-5 w-5 text-orange-500" />
<CardTitle>ProxMenux Optimizations</CardTitle> <CardTitle>ProxMenux Optimizations</CardTitle>
</div> </div>
<CardDescription> <CardDescription>System optimizations and utilities installed via ProxMenux</CardDescription>
System optimizations and utilities installed via ProxMenux
</CardDescription>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
{loadingTools ? ( {loadingTools ? (
@@ -1040,22 +927,14 @@ export function Settings() {
) : proxmenuxTools.length === 0 ? ( ) : proxmenuxTools.length === 0 ? (
<div className="text-center py-8"> <div className="text-center py-8">
<Package className="h-12 w-12 text-muted-foreground mx-auto mb-3 opacity-50" /> <Package className="h-12 w-12 text-muted-foreground mx-auto mb-3 opacity-50" />
<p className="text-muted-foreground"> <p className="text-muted-foreground">No ProxMenux optimizations installed yet</p>
No ProxMenux optimizations installed yet <p className="text-sm text-muted-foreground mt-1">Run ProxMenux to configure system optimizations</p>
</p>
<p className="text-sm text-muted-foreground mt-1">
Run ProxMenux to configure system optimizations
</p>
</div> </div>
) : ( ) : (
<div className="space-y-2"> <div className="space-y-2">
<div className="flex items-center justify-between mb-4 pb-2 border-b border-border"> <div className="flex items-center justify-between mb-4 pb-2 border-b border-border">
<span className="text-sm font-medium text-muted-foreground"> <span className="text-sm font-medium text-muted-foreground">Installed Tools</span>
Installed Tools <span className="text-sm font-semibold text-orange-500">{proxmenuxTools.length} active</span>
</span>
<span className="text-sm font-semibold text-orange-500">
{proxmenuxTools.length} active
</span>
</div> </div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-2"> <div className="grid grid-cols-1 md:grid-cols-2 gap-2">
{proxmenuxTools.map((tool) => ( {proxmenuxTools.map((tool) => (
@@ -1077,10 +956,10 @@ export function Settings() {
open={show2FASetup} open={show2FASetup}
onClose={() => setShow2FASetup(false)} onClose={() => setShow2FASetup(false)}
onSuccess={() => { onSuccess={() => {
setSuccess("2FA enabled successfully!"); setSuccess("2FA enabled successfully!")
checkAuthStatus(); checkAuthStatus()
}} }}
/> />
</div> </div>
); )
} }