fix: acknowledge pi maintenance barrier

This commit is contained in:
2026-08-04 19:32:05 +02:00
parent 0b9ad7f53f
commit 5b3ce93e31
11 changed files with 168 additions and 52 deletions
+20 -4
View File
@@ -15,7 +15,7 @@ import { settingsRoutes, effectiveSettings } from "./routes/settings.js";
import { createPiModelLister } from "./pi/list-models.js";
import { loadSettings, type Settings } from "./settings/settings-store.js";
import { ReadinessManager } from "./runtime/readiness-manager.js";
import { createMaintenanceGate } from "./runtime/maintenance-gate.js";
import { MaintenanceBarrier } from "./runtime/maintenance-gate.js";
import { WorkspaceRegistry } from "./workspaces/registry.js";
import { createProductionWorkspaceDiagnoser } from "./workspaces/diagnostics.js";
import { workspaceRoutes, type WorkspaceDiagnoser } from "./routes/workspaces.js";
@@ -33,8 +33,7 @@ export interface BuildAppDeps {
workspaceRegistry?: WorkspaceRegistry;
workspaceDiagnoser?: WorkspaceDiagnoser;
workspaceRuntimeSupport?: (workspace: WorkspaceDescriptor) => boolean;
/** Returns true while a host maintenance transaction is preventing new runtimes. */
maintenanceGate?: () => boolean;
maintenanceBarrier?: MaintenanceBarrier;
}
export function buildApp(config: AppConfig, deps?: BuildAppDeps): FastifyInstance {
@@ -97,12 +96,27 @@ export function buildApp(config: AppConfig, deps?: BuildAppDeps): FastifyInstanc
app.get("/health", async () => ({ status: "ok" }));
app.get("/health/dwh", async () => tht.dbPing());
app.get("/me", async (req) => getPrincipal(req));
const maintenanceBarrier = deps?.maintenanceBarrier ?? new MaintenanceBarrier();
sessionRoutes(app, {
mgr, tht: tht as ThtRunner, hub, getSettings, readiness, listModels, workspaceRegistry,
dwhPrecheck: config.dwhPrecheck,
legacyWorkspaceMode: config.legacyWorkspaceMode,
workspaceRuntimeSupport,
maintenanceGate: deps?.maintenanceGate ?? createMaintenanceGate(config.maintenanceFile),
maintenanceBarrier,
});
app.post("/internal/maintenance/activate", async (req, reply) => {
if (!isLoopback(req.ip) || req.principal?.subject !== "thothctl-maintenance") return reply.code(403).send({ error: "loopback maintenance control required" });
await maintenanceBarrier.activate();
return maintenanceBarrier.status();
});
app.post("/internal/maintenance/deactivate", async (req, reply) => {
if (!isLoopback(req.ip) || req.principal?.subject !== "thothctl-maintenance") return reply.code(403).send({ error: "loopback maintenance control required" });
maintenanceBarrier.deactivate();
return maintenanceBarrier.status();
});
app.get("/internal/maintenance/status", async (req, reply) => {
if (!isLoopback(req.ip) || req.principal?.subject !== "thothctl-maintenance") return reply.code(403).send({ error: "loopback maintenance control required" });
return maintenanceBarrier.status();
});
sqlRoutes(app, { tht: tht as ThtRunner, getSettings });
metaRoutes(app, { harnessDir: config.harnessDir, listModels });
@@ -111,3 +125,5 @@ export function buildApp(config: AppConfig, deps?: BuildAppDeps): FastifyInstanc
return app;
}
function isLoopback(ip: string): boolean { return ip === "127.0.0.1" || ip === "::1" || ip === "::ffff:127.0.0.1"; }