feat(web): monitor config API route

Implement GET and PATCH endpoints for monitor configuration, matching the existing discover config pattern. GET returns stored or default values; PATCH upserts known keys with allowlist filtering.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jonathan
2026-07-13 00:16:43 +02:00
parent 34ebe39dd7
commit 2a50c1f518
2 changed files with 67 additions and 0 deletions
@@ -0,0 +1,29 @@
import { describe, it, expect } from "vitest";
import { prisma } from "@/lib/db";
import { GET, PATCH } from "./route";
function patch(body: unknown) {
return PATCH(new Request("http://localhost/api/monitor/config", {
method: "PATCH", headers: { "content-type": "application/json" }, body: JSON.stringify(body),
}));
}
describe("monitor config API", () => {
it("GET returns defaults when nothing is stored", async () => {
const body = await (await GET()).json();
expect(body).toMatchObject({
"monitor.enabled": "false", "monitor.autoMonitorFuture": "false",
"monitor.pollIntervalHours": "24", "monitor.retryIntervalHours": "6",
"monitor.qualityCutoff": "2", "monitor.upgradeWindowDays": "14",
});
});
it("PATCH upserts known keys and ignores unknown ones", async () => {
const res = await patch({ "monitor.enabled": "true", "monitor.pollIntervalHours": 12, "nope": "x" });
expect((await res.json()).updated).toBe(2);
const body = await (await GET()).json();
expect(body["monitor.enabled"]).toBe("true");
expect(body["monitor.pollIntervalHours"]).toBe("12");
expect(await prisma.config.findUnique({ where: { key: "nope" } })).toBeNull();
});
});
+38
View File
@@ -0,0 +1,38 @@
import { prisma } from "@/lib/db";
const DEFAULTS: Record<string, string> = {
"monitor.enabled": "false",
"monitor.autoMonitorFuture": "false",
"monitor.pollIntervalHours": "24",
"monitor.retryIntervalHours": "6",
"monitor.qualityCutoff": "2",
"monitor.upgradeWindowDays": "14",
};
export async function GET() {
const rows = await prisma.config.findMany({ where: { key: { in: Object.keys(DEFAULTS) } } });
const stored = Object.fromEntries(rows.map((r) => [r.key, r.value]));
return Response.json(
Object.fromEntries(Object.entries(DEFAULTS).map(([k, d]) => [k, stored[k] ?? d])),
);
}
export async function PATCH(request: Request) {
let body: unknown;
try {
body = await request.json();
} catch {
return Response.json({ error: "invalid JSON" }, { status: 400 });
}
const entries = Object.entries((body ?? {}) as Record<string, unknown>).filter(
([k]) => k in DEFAULTS,
);
for (const [key, value] of entries) {
await prisma.config.upsert({
where: { key },
create: { key, value: String(value), secret: false },
update: { value: String(value) },
});
}
return Response.json({ updated: entries.length });
}