Scaffold Homelab Manager foundation

Monorepo (Express+TS+Drizzle/libSQL server, React+Vite+Tabler web) matching
the stack used by ScheduleTaskManager and Sloth Manager. Includes Authentik
OIDC login with local admin/operator/viewer roles (first user becomes admin),
a generalized audit log, encrypted-at-rest storage for future integration API
tokens, the DB schema for all planned modules, and the Tabler-styled app
shell/nav. Also ports the Secrets (expiry tracker) and IP Addresses (IPAM)
modules from Sloth Manager onto the new stack.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
bobbanandClaude Sonnet 5 committed 2026-09-14 21:57:13 +02:00
commit 6bd2ed52c1
52 files changed
+8103

No files matched your search

+91
View File
@@ -0,0 +1,91 @@
import { useEffect, useState, type ReactNode } from "react";
import { Routes, Route } 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 AuditLog from "./pages/AuditLog";
import Secrets from "./pages/Secrets";
import Ipam from "./pages/Ipam";
import ComingSoon from "./pages/ComingSoon";
import AppShell from "./layout/AppShell";
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);
},
);
}, []);
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={<ComingSoon title="Servers & Tasks" />} />
<Route path="/dns" element={<ComingSoon title="DNS" />} />
<Route path="/ipam" element={<Ipam user={user} />} />
<Route path="/secrets" element={<Secrets user={user} />} />
<Route path="/integrations" element={<ComingSoon title="Integrations" />} />
<Route
path="/users"
element={
<RequireRole user={user} minRole="admin">
<Users />
</RequireRole>
}
/>
<Route
path="/audit-log"
element={
<RequireRole user={user} minRole="operator">
<AuditLog />
</RequireRole>
}
/>
<Route
path="/settings"
element={
<RequireRole user={user} minRole="admin">
<ComingSoon title="Settings" />
</RequireRole>
}
/>
</Routes>
</AppShell>
);
}