edb22a59c7
- 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>
64 lines
2.1 KiB
TypeScript
64 lines
2.1 KiB
TypeScript
// 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);
|
|
}
|
|
});
|