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,73 @@
|
||||
import express from "express";
|
||||
import session from "express-session";
|
||||
import FileStoreFactory from "session-file-store";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { dirname, join } from "node:path";
|
||||
import { mkdirSync, existsSync } from "node:fs";
|
||||
import { env, warnIfAuthNotConfigured } from "./env.js";
|
||||
import { resolveDataPath } from "./paths.js";
|
||||
import { runMigrations } from "./db/migrate.js";
|
||||
import { authRouter } from "./auth/router.js";
|
||||
import { meRouter } from "./routes/me.js";
|
||||
import { usersRouter } from "./routes/users.js";
|
||||
import { auditLogRouter } from "./routes/auditLog.js";
|
||||
import { secretsRouter } from "./routes/secrets.js";
|
||||
import { ipamRouter } from "./routes/ipam.js";
|
||||
|
||||
warnIfAuthNotConfigured();
|
||||
await runMigrations();
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const webDist = join(__dirname, "..", "..", "web", "dist");
|
||||
const agentDir = join(__dirname, "..", "..", "agent");
|
||||
|
||||
const FileStore = FileStoreFactory(session);
|
||||
const sessionDir = resolveDataPath(env.sessionDir);
|
||||
mkdirSync(sessionDir, { recursive: true });
|
||||
|
||||
const app = express();
|
||||
app.set("trust proxy", 1);
|
||||
app.use(express.json());
|
||||
app.use(
|
||||
session({
|
||||
store: new FileStore({ path: sessionDir, logFn: () => {} }),
|
||||
secret: env.sessionSecret,
|
||||
resave: false,
|
||||
saveUninitialized: false,
|
||||
cookie: {
|
||||
httpOnly: true,
|
||||
sameSite: "lax",
|
||||
secure: env.nodeEnv === "production",
|
||||
maxAge: 7 * 24 * 60 * 60 * 1000,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
app.get("/health", (_req, res) => res.json({ ok: true }));
|
||||
|
||||
// Publicly readable so `curl .../agent/linux/install.sh | bash` works from a
|
||||
// freshly provisioned server with no prior session. Contains no secrets.
|
||||
if (existsSync(agentDir)) {
|
||||
app.use("/agent", express.static(agentDir));
|
||||
}
|
||||
|
||||
app.use("/auth", authRouter);
|
||||
app.use("/api/me", meRouter);
|
||||
app.use("/api/users", usersRouter);
|
||||
app.use("/api/audit-log", auditLogRouter);
|
||||
app.use("/api/secrets", secretsRouter);
|
||||
app.use("/api/ipam", ipamRouter);
|
||||
|
||||
if (existsSync(webDist)) {
|
||||
app.use(express.static(webDist));
|
||||
app.get("*", (_req, res) => res.sendFile(join(webDist, "index.html")));
|
||||
}
|
||||
|
||||
app.use((err: unknown, _req: express.Request, res: express.Response, _next: express.NextFunction) => {
|
||||
console.error(err);
|
||||
res.status(500).json({ error: "internal_error" });
|
||||
});
|
||||
|
||||
app.listen(env.port, () => {
|
||||
console.log(`homelab-manager listening on port ${env.port}`);
|
||||
});
|
||||
Reference in new issue
Block a user