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:
commit
6bd2ed52c1
52 files changed
+8103
No files matched your search
@@ -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>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user