import { useEffect, useState } from 'hono/jsx' import { applyUpdate, checkForUpdate, getSystemInfo, restartServer } from '../api' import { setTheme } from '../index' import { navigate } from '../router' import { Button, DashboardInstallCmd, FormField, FormLabel, FormSelect, HeaderActions, Main, MainContent, MainHeader, MainTitle, Section, SectionTitle, } from '../styles' type UpdateInfo = { available: boolean, current: string, latest: string, commits: string[] } function formatUptime(ms: number): string { const seconds = Math.floor(ms / 1000) const days = Math.floor(seconds / 86400) const hours = Math.floor((seconds % 86400) / 3600) const minutes = Math.floor((seconds % 3600) / 60) const secs = seconds % 60 const parts: string[] = [] if (days > 0) parts.push(`${days}d`) if (hours > 0) parts.push(`${hours}h`) if (minutes > 0) parts.push(`${minutes}m`) parts.push(`${secs}s`) return parts.join(' ') } function pollUntilBack(onBack: () => void, onTimeout?: () => void) { let elapsed = 0 const poll = setInterval(async () => { elapsed += 2000 if (elapsed > 60000) { clearInterval(poll) onTimeout?.() return } try { const res = await fetch('/api/system/info') if (res.ok) { clearInterval(poll) onBack() } } catch {} }, 2000) } export function SettingsPage({ render }: { render: () => void }) { const [version, setVersion] = useState('') const [sha, setSha] = useState('') const [uptime, setUptime] = useState(0) const [themeChoice, setThemeChoice] = useState(localStorage.getItem('theme') || 'system') const [restarting, setRestarting] = useState(false) const [updateInfo, setUpdateInfo] = useState(null) const [checking, setChecking] = useState(false) const [updating, setUpdating] = useState(false) useEffect(() => { getSystemInfo().then(info => { setVersion(info.version) setSha(info.sha) setUptime(info.uptime) }) }, []) // Tick uptime every second useEffect(() => { const interval = setInterval(() => setUptime(u => u + 1000), 1000) return () => clearInterval(interval) }, []) const goBack = () => { navigate('/') } const handleThemeChange = (e: Event) => { const value = (e.target as HTMLSelectElement).value setThemeChoice(value) if (value === 'system') { localStorage.removeItem('theme') } else { localStorage.setItem('theme', value) } setTheme() } const refreshSystemInfo = () => { getSystemInfo().then(info => { setVersion(info.version) setSha(info.sha) setUptime(info.uptime) }) } const handleRestart = () => { if (!confirm('Are you sure you want to restart the server?')) return setRestarting(true) restartServer().catch(() => {}) pollUntilBack( () => { setRestarting(false); refreshSystemInfo() }, () => { setRestarting(false) }, ) } const handleCheckUpdate = async () => { setChecking(true) try { const info = await checkForUpdate() setUpdateInfo(info) } catch { setUpdateInfo(null) } setChecking(false) } const handleApplyUpdate = async () => { if (!confirm('This will update and restart the server. Continue?')) return setUpdating(true) try { await applyUpdate() pollUntilBack( () => { setUpdating(false); refreshSystemInfo(); setUpdateInfo(null) }, () => { setUpdating(false) }, ) } catch { setUpdating(false) } } return (
Settings
Theme Appearance
About
Version: {version} SHA: {sha}
Install CLI curl -fsSL {location.origin}/install | bash
Update {updating ? (
Updating... server will restart shortly.
) : updateInfo?.available ? (
{updateInfo.commits.length} update{updateInfo.commits.length !== 1 ? 's' : ''} available ({updateInfo.current} → {updateInfo.latest})
{updateInfo.commits.map(c =>
{c}
)}
) : updateInfo ? (
Up to date
) : ( )}
Server
Uptime: {formatUptime(uptime)}
) }