Files
Homelab-manager/web/src/App.tsx
T
bobbanandClaude Sonnet 5.5 3035d7fc08 Make the Generator's server-name lists editable, add themes, import names from Skatteverket
Name lists now live in settings instead of the web bundle. Admins edit them under
Settings -> Names (add/remove names, create/rename/delete lists, reset a built-in
list). Names are folded to hostname-safe form (a-z, 0-9, hyphen) and validated on
the server; saves are audited.

Adds Swedish boy names, Pixar, Norse mythology and Astrid Lindgren lists, and
lengthens the Swedish girl and Disney lists. "Mixed" is now every list with each
name counted once.

Admins can preview and import the most common Swedish names from Skatteverket's
open "Namn pa nyfodda" data (girls or boys, latest 1-5 full years); nothing is
stored until the editor is saved. The old comment that credited SCB statistics is
gone: SCB stopped publishing name statistics after 2023.

Privacy page, README and ROLES updated for the new outbound call and page.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-03 01:53:31 +02:00

175 lines
6.3 KiB
TypeScript

import { useEffect, useState, type ReactNode } from "react";
import { Routes, Route, Navigate } from "react-router-dom";
import { api, type CurrentUser, type UserRole, UnauthorizedError } from "./api/client";
import Login from "./pages/Login";
import Dashboard from "./pages/Dashboard";
import Users from "./pages/Users";
import Sessions from "./pages/Sessions";
import AuditLog from "./pages/AuditLog";
import DiagLog from "./pages/DiagLog";
import Secrets from "./pages/Secrets";
import Ipam from "./pages/Ipam";
import Ports from "./pages/Ports";
import Dns from "./pages/Dns";
import Servers from "./pages/Servers";
import ServerDetail from "./pages/ServerDetail";
import Integrations from "./pages/Integrations";
import Docker from "./pages/Docker";
import Tailscale from "./pages/Tailscale";
import Semaphore from "./pages/Semaphore";
import Gitea from "./pages/Gitea";
import Proxmox from "./pages/Proxmox";
import Synology from "./pages/Synology";
import UptimeKuma from "./pages/UptimeKuma";
import PbsBackup from "./pages/PbsBackup";
import OsTicket from "./pages/OsTicket";
import AdminLinks from "./pages/AdminLinks";
import Alerts from "./pages/Alerts";
import Generator from "./pages/Generator";
import Maintenance from "./pages/Maintenance";
import Domains from "./pages/Domains";
import Consistency from "./pages/Consistency";
import Privacy from "./pages/Privacy";
import Settings from "./pages/Settings";
import NotificationSettings from "./pages/settings/NotificationSettings";
import BadgeSettings from "./pages/settings/BadgeSettings";
import DisplaySettings from "./pages/settings/DisplaySettings";
import TagSettings from "./pages/settings/TagSettings";
import CacheSettings from "./pages/settings/CacheSettings";
import LogSettings from "./pages/settings/LogSettings";
import BackupSettings from "./pages/settings/BackupSettings";
import NameSettings from "./pages/settings/NameSettings";
import AppShell from "./layout/AppShell";
import DialogHost from "./components/DialogHost";
import { setDateTimeSettings } from "./utils/date";
import { setPageSize } from "./utils/pageSize";
const roleRank: Record<UserRole, number> = { viewer: 0, operator: 1, admin: 2 };
function RequireRole({
user,
minRole,
children,
}: {
user: CurrentUser;
minRole: UserRole;
children: ReactNode;
}) {
if (roleRank[user.role] < roleRank[minRole]) {
return (
<div className="alert alert-danger">You don't have permission to view this page.</div>
);
}
return <>{children}</>;
}
export default function App() {
const [user, setUser] = useState<CurrentUser | null | undefined>(undefined);
useEffect(() => {
api.me().then(
(res) => setUser(res.user),
(err) => {
if (!(err instanceof UnauthorizedError)) {
console.error(err);
}
setUser(null);
},
);
api.settings.display().then((res) => {
setDateTimeSettings(res.display);
setPageSize(res.display.pageSize);
}).catch(() => {});
}, []);
if (user === undefined) {
return <div className="loading-screen">Loading…</div>;
}
if (user === null) {
return <Login />;
}
return (
<AppShell user={user}>
<Routes>
<Route path="/" element={<Dashboard user={user} />} />
<Route path="/servers" element={<Servers user={user} />} />
<Route path="/servers/:id" element={<ServerDetail user={user} />} />
<Route path="/dns" element={<Dns user={user} />} />
<Route path="/ipam" element={<Ipam user={user} />} />
<Route path="/ports" element={<Ports user={user} />} />
<Route path="/secrets" element={<Secrets user={user} />} />
<Route path="/integrations" element={<Integrations user={user} />} />
<Route path="/generator" element={<Generator user={user} />} />
<Route path="/privacy" element={<Privacy />} />
<Route path="/consistency" element={<Consistency user={user} />} />
<Route path="/domains" element={<Domains user={user} />} />
<Route path="/maintenance" element={<Maintenance user={user} />} />
<Route path="/docker" element={<Docker user={user} />} />
<Route path="/tailscale" element={<Tailscale user={user} />} />
<Route path="/semaphore" element={<Semaphore user={user} />} />
<Route path="/gitea" element={<Gitea user={user} />} />
<Route path="/proxmox" element={<Proxmox user={user} />} />
<Route path="/synology" element={<Synology user={user} />} />
<Route path="/uptime-kuma" element={<UptimeKuma user={user} />} />
<Route path="/pbs" element={<PbsBackup user={user} />} />
<Route path="/osticket" element={<OsTicket user={user} />} />
<Route path="/admin-links" element={<AdminLinks user={user} />} />
<Route path="/alerts" element={<Alerts user={user} />} />
<Route
path="/users"
element={
<RequireRole user={user} minRole="admin">
<Users />
</RequireRole>
}
/>
<Route
path="/sessions"
element={
<RequireRole user={user} minRole="admin">
<Sessions />
</RequireRole>
}
/>
<Route
path="/audit-log"
element={
<RequireRole user={user} minRole="operator">
<AuditLog />
</RequireRole>
}
/>
<Route
path="/diag-log"
element={
<RequireRole user={user} minRole="admin">
<DiagLog />
</RequireRole>
}
/>
<Route
path="/settings"
element={
<RequireRole user={user} minRole="admin">
<Settings />
</RequireRole>
}
>
<Route index element={<Navigate to="notifications" replace />} />
<Route path="notifications" element={<NotificationSettings />} />
<Route path="badges" element={<BadgeSettings />} />
<Route path="display" element={<DisplaySettings />} />
<Route path="tags" element={<TagSettings />} />
<Route path="names" element={<NameSettings />} />
<Route path="cache" element={<CacheSettings />} />
<Route path="logs" element={<LogSettings />} />
<Route path="backup" element={<BackupSettings />} />
</Route>
</Routes>
<DialogHost />
</AppShell>
);
}