import { Eye, EyeOff, Save } from 'lucide-react'; import { useEffect, useMemo, useState } from 'react'; import { api, errorMessage, setApiToken } from '../api.js'; export function Settings({ onChanged }) { const [fields, setFields] = useState([]); const [values, setValues] = useState({}); const [visibleSecrets, setVisibleSecrets] = useState({}); const [saving, setSaving] = useState(false); const [message, setMessage] = useState(''); const [error, setError] = useState(''); const requiredMissing = useMemo( () => fields.filter((field) => field.required && !String(values[field.key] || '').trim()), [fields, values], ); async function loadSettings() { setError(''); try { const result = await api.get('/settings'); setFields(result.data.fields || []); setValues(Object.fromEntries((result.data.fields || []).map((field) => [field.key, field.value || '']))); } catch (requestError) { setError(errorMessage(requestError)); } } useEffect(() => { loadSettings(); }, []); async function saveSettings(event) { event.preventDefault(); setSaving(true); setMessage(''); setError(''); try { setApiToken(values.API_TOKEN); const result = await api.put('/settings', { values }); setFields(result.data.fields || []); setValues(Object.fromEntries((result.data.fields || []).map((field) => [field.key, field.value || '']))); setMessage('Settings saved. Running jobs use the updated values; a changed PORT still needs a backend restart.'); await onChanged?.(); } catch (requestError) { setError(errorMessage(requestError)); } finally { setSaving(false); } } return (

Settings

Values are written to backend/.env. Keep access to this UI restricted because secrets are editable here.

{error ? : null} {message ? : null} {requiredMissing.length ? ( field.key).join(', ')}`} /> ) : null}
{fields.map((field) => { const secretVisible = visibleSecrets[field.key]; return ( ); })}
); } function inputType(field, secretVisible) { if (field.secret && !secretVisible) return 'password'; if (field.key.startsWith('RESTIC_KEEP_')) return 'number'; return 'text'; } function Notice({ text, tone }) { const classes = { bad: 'border-red-900 bg-red-950/50 text-red-200', good: 'border-emerald-900 bg-emerald-950/50 text-emerald-200', warn: 'border-amber-900 bg-amber-950/50 text-amber-200', }; return
{text}
; }