Scaffold homelab-monitor: Fastify API + React dashboard + CI
Vertical slice for Phase 1 (v1-dashboard milestone): Proxmox collector, SQLite storage, local auth, and a dashboard UI showing host/container status cards. Config-driven collector registry so future data sources (SSH-based hosts, Zabbix, network discovery) plug in without rewiring. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
import type { FastifyInstance } from "fastify";
|
||||
import type Database from "better-sqlite3";
|
||||
import { verifyCredentials } from "../auth/local.js";
|
||||
|
||||
declare module "@fastify/session" {
|
||||
interface FastifySessionObject {
|
||||
username?: string;
|
||||
}
|
||||
}
|
||||
|
||||
export function registerAuthRoutes(app: FastifyInstance, db: Database.Database): void {
|
||||
app.post<{ Body: { username: string; password: string } }>("/api/auth/login", async (req, reply) => {
|
||||
const { username, password } = req.body ?? {};
|
||||
if (!username || !password || !verifyCredentials(db, username, password)) {
|
||||
return reply.code(401).send({ error: "invalid credentials" });
|
||||
}
|
||||
req.session.username = username;
|
||||
return { username };
|
||||
});
|
||||
|
||||
app.post("/api/auth/logout", async (req, reply) => {
|
||||
await req.session.destroy();
|
||||
return reply.send({ ok: true });
|
||||
});
|
||||
|
||||
app.get("/api/auth/me", async (req, reply) => {
|
||||
if (!req.session.username) return reply.code(401).send({ error: "not authenticated" });
|
||||
return { username: req.session.username };
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import type { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
|
||||
import type Database from "better-sqlite3";
|
||||
import { getLatestSnapshots } from "../db/index.js";
|
||||
|
||||
async function requireAuth(req: FastifyRequest, reply: FastifyReply) {
|
||||
if (!req.session.username) {
|
||||
reply.code(401).send({ error: "not authenticated" });
|
||||
}
|
||||
}
|
||||
|
||||
export function registerHostRoutes(app: FastifyInstance, db: Database.Database): void {
|
||||
app.get("/api/hosts", { preHandler: requireAuth }, async () => {
|
||||
const rows = getLatestSnapshots(db);
|
||||
return {
|
||||
hosts: rows.map((r) => ({
|
||||
hostId: r.host_id,
|
||||
displayName: r.display_name,
|
||||
group: r.group_name,
|
||||
status: r.status,
|
||||
cpuPct: r.cpu_pct,
|
||||
memPct: r.mem_pct,
|
||||
diskPct: r.disk_pct,
|
||||
memPressurePct: r.mem_pressure_pct,
|
||||
cpuPressurePct: r.cpu_pressure_pct,
|
||||
uptimeSec: r.uptime_sec,
|
||||
lastSeen: r.ts,
|
||||
})),
|
||||
};
|
||||
});
|
||||
|
||||
app.get("/api/health", async () => ({ ok: true }));
|
||||
}
|
||||
Reference in New Issue
Block a user