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,45 @@
|
||||
# Tâche 2 — SJ-6 : Docker apply / prune / down + socle action_requests
|
||||
|
||||
> Statut : **implémenté** (2026-06-06). tsc 0 erreur · 91 tests · build OK · boot OK (migrations 0000→0005).
|
||||
> Réf. design : `docs/design/tache2/20-docker.md §4.4-4.6`, `40-contrats-json.md §4`, `70-securite.md §2`, `80-sous-jalons.md` SJ-6.
|
||||
|
||||
## Périmètre livré
|
||||
|
||||
Actions Docker **destructives** (recrée/supprime) protégées par un socle de
|
||||
**validation explicite** (`action_requests`) : Hermes/UI proposent, l'opérateur approuve,
|
||||
l'exécution part en arrière-plan. Aucune de ces actions n'est accessible directement
|
||||
via `POST /:id/actions` (allowlist passive uniquement).
|
||||
|
||||
## Composants
|
||||
|
||||
- **Migration 0005** (`0005_silent_drax.sql`, timestamp monotone) : tables
|
||||
`docker_image_events` (historique pulled/recreated/pruned + bytes) et
|
||||
`action_requests` (pending|approved|rejected|executed|expired).
|
||||
- **Templates** `docker/apply-compose.sh.tpl` (`up -d --remove-orphans`),
|
||||
`docker/prune-images.sh.tpl` (safe par défaut / `<%#aggressive%>` = `-a --filter until=168h`),
|
||||
`docker/down-compose.sh.tpl` (down simple, **`--volumes`/`--rmi` interdits**).
|
||||
- **`server/services/dockerApply.ts`** :
|
||||
- parsers purs (TDD) : `parseDockerApply` (recreated/running/exited via ps json),
|
||||
`parseDockerPrune` (`imagesDeleted` + `Total reclaimed space` → octets),
|
||||
`parseDockerDown` (removed), `parseHumanBytes` (unités décimales Docker).
|
||||
- orchestration : `applyStack` / `pruneImages` / `downStack` — réservées aux stacks
|
||||
`enabled`, insèrent les `docker_image_events`. Erreurs nettoyées (réutilise `cleanDockerError`).
|
||||
- **`server/services/actionRequests.ts`** : `createActionRequest` (refuse une action non
|
||||
destructive, exige `stackId` pour apply/down), `approve` (→ `runAction` en tâche de fond,
|
||||
pose `executionId`/`executed`), `reject`, `get`, `list`.
|
||||
- **Routes** `server/routes/actionRequests.ts` (montées à la racine `/api`) :
|
||||
`POST /machines/:id/action-requests`, `GET …`, `GET/POST /action-requests/:id[/approve|/reject]`.
|
||||
- **`execute.ts`** : `RunActionOpts.aggressive`, branches `docker_compose_apply` /
|
||||
`docker_prune_images` / `docker_compose_down`, helper `archiveExecution` mutualisant
|
||||
le boilerplate (log/rapport/DB/état/event) + `ExecutionResult.docker.up|prune`.
|
||||
|
||||
## Sécurité
|
||||
|
||||
- Destructives **hors API directe** : passent obligatoirement par un `action_request` approuvé.
|
||||
- `down` sans volumes ni rmi (volumes préservés). Prune agressif = risque distinct (champ `aggressive`).
|
||||
- Erreurs Docker nettoyées (URL/token/password) avant UI/MCP.
|
||||
|
||||
## Reste tâche 2
|
||||
|
||||
SJ-7 (profils Proxmox/RPi + proxy persistent), SJ-8/9 (post-install). UI des boutons
|
||||
validés (Appliquer/Prune/Down) = tâche 3 (frontend, design system).
|
||||
Reference in New Issue
Block a user