feat(docker): apply/prune/down + socle action_requests (tâche 2 SJ-6)
- migration 0005 : tables docker_image_events + action_requests - templates apply-compose (up -d --remove-orphans), prune-images (safe/agressif), down-compose (sans volumes/rmi) - dockerApply: parsers TDD (apply recreated/running/exited, prune images+bytes, down removed, parseHumanBytes) + orchestration applyStack/pruneImages/downStack réservée aux stacks enabled, insère docker_image_events - actionRequests: create/approve/reject/list — actions destructives validées explicitement (Hermes propose, opérateur approuve, run en arrière-plan) ; hors API directe (POST /:id/actions reste passif uniquement) - routes /machines/:id/action-requests + /action-requests/:id[/approve|/reject] - execute: RunActionOpts.aggressive, branches apply/prune/down, helper archiveExecution mutualisant le boilerplate d'archivage tsc 0 erreur · 91 tests · build OK · boot OK (migrations 0000→0005). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
// server/routes/actionRequests.ts
|
||||
import { Hono } from "hono";
|
||||
import {
|
||||
createActionRequest,
|
||||
getActionRequest,
|
||||
listActionRequests,
|
||||
approveActionRequest,
|
||||
rejectActionRequest,
|
||||
} from "../services/actionRequests.js";
|
||||
import type { ActionType } from "@shared/types.js";
|
||||
|
||||
export const actionRequestsRoutes = new Hono();
|
||||
|
||||
// Crée une demande d'action destructive (pending). Hermes/UI proposent ; aucune exécution ici.
|
||||
actionRequestsRoutes.post("/machines/:id/action-requests", async (c) => {
|
||||
const body = (await c.req.json()) as {
|
||||
action: ActionType;
|
||||
stackId?: string;
|
||||
aggressive?: boolean;
|
||||
summary?: string;
|
||||
requestedByType?: "user" | "hermes" | "schedule";
|
||||
};
|
||||
try {
|
||||
const req = createActionRequest({
|
||||
machineId: c.req.param("id"),
|
||||
action: body.action,
|
||||
requestedByType: body.requestedByType,
|
||||
summary: body.summary,
|
||||
payload: { stackId: body.stackId, aggressive: body.aggressive },
|
||||
});
|
||||
return c.json(req, 201);
|
||||
} catch (err) {
|
||||
return c.json({ error: (err as Error).message }, 400);
|
||||
}
|
||||
});
|
||||
|
||||
actionRequestsRoutes.get("/machines/:id/action-requests", (c) =>
|
||||
c.json(listActionRequests(c.req.param("id"))),
|
||||
);
|
||||
|
||||
actionRequestsRoutes.get("/action-requests/:reqId", (c) => {
|
||||
const req = getActionRequest(c.req.param("reqId"));
|
||||
return req ? c.json(req) : c.json({ error: "Demande introuvable" }, 404);
|
||||
});
|
||||
|
||||
// Validation opérateur → déclenche l'exécution en arrière-plan.
|
||||
actionRequestsRoutes.post("/action-requests/:reqId/approve", async (c) => {
|
||||
const body = (await c.req.json().catch(() => ({}))) as { approvedBy?: string };
|
||||
try {
|
||||
return c.json(approveActionRequest(c.req.param("reqId"), body.approvedBy), 202);
|
||||
} catch (err) {
|
||||
return c.json({ error: (err as Error).message }, 400);
|
||||
}
|
||||
});
|
||||
|
||||
actionRequestsRoutes.post("/action-requests/:reqId/reject", async (c) => {
|
||||
const body = (await c.req.json().catch(() => ({}))) as { by?: string };
|
||||
try {
|
||||
return c.json(rejectActionRequest(c.req.param("reqId"), body.by));
|
||||
} catch (err) {
|
||||
return c.json({ error: (err as Error).message }, 400);
|
||||
}
|
||||
});
|
||||
@@ -2,6 +2,7 @@
|
||||
import { Hono } from "hono";
|
||||
import { machinesRoutes } from "./machines.js";
|
||||
import { actionsRoutes } from "./actions.js";
|
||||
import { actionRequestsRoutes } from "./actionRequests.js";
|
||||
import { getServerCapabilities } from "../services/capabilities.js";
|
||||
import { getSystemMetrics, getSystemStatus } from "../services/system.js";
|
||||
|
||||
@@ -11,3 +12,4 @@ api.get("/system/status", (c) => c.json(getSystemStatus()));
|
||||
api.get("/system/metrics", (c) => c.json(getSystemMetrics()));
|
||||
api.route("/machines", machinesRoutes);
|
||||
api.route("/machines", actionsRoutes);
|
||||
api.route("/", actionRequestsRoutes);
|
||||
|
||||
Reference in New Issue
Block a user