feat: socle BDD (tâche 1.9 Phase 1-2) + moteur APT (tâche 2 SJ-0→3) + WIP capabilities/auth/Rust
Checkpoint multi-chantiers (arbre vert : tsc 0 erreur, 70 tests, build OK). - tâche 1.9 Phase 1 : schéma socle (machine_state/events/reports/raw_artifacts/ hardware/metrics + colonnes étendues) + wiring refresh/execute. Migration 0002. - tâche 1.9 Phase 2 : machine_credentials + machine_host_keys (non destructif, dual-read + backfill). Migration 0003. Fix séquence journal de migration. - tâche 2 : SJ-0 (types étendus rétro-compatibles, réducteur Docker, resolveTemplate), SJ-1 (update-analyze enrichi), SJ-2 (apply + diff dpkg + timeout inactivité SSH), SJ-3 (reboot vérifié boot_id). - WIP parallèle inclus : /api/capabilities, auth/apiTokens/apiClients, system metrics, scaffold app_rust, ajustements frontend. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+35
-12
@@ -3,12 +3,13 @@ import { randomUUID } from "node:crypto";
|
||||
import { eq, desc } from "drizzle-orm";
|
||||
import { db, schema } from "../db/client.js";
|
||||
import { getMachineRow, getCreds } from "./machines.js";
|
||||
import { renderTemplate } from "../templates/render.js";
|
||||
import { renderTemplate, resolveTemplate } from "../templates/render.js";
|
||||
import { reduceAptLines } from "../templates/aptReduce.js";
|
||||
import { runScriptSudo } from "../ssh/client.js";
|
||||
import { parseAptSimulate, parseRebootRequired } from "./aptParse.js";
|
||||
import { buildAptSnapshotDetail } from "./aptParse.js";
|
||||
import { outputHub } from "../ws/outputHub.js";
|
||||
import type { UpdateSnapshot, MachineStatus } from "@shared/types.js";
|
||||
import { deriveAptState, upsertMachineState, recordEvent } from "./machineState.js";
|
||||
import type { UpdateSnapshot, MachineStatus, AptSnapshotDetail } from "@shared/types.js";
|
||||
|
||||
/** Extrait la section entre deux marqueurs ===SU:X=== d'une sortie de script. */
|
||||
export function extractSection(raw: string, start: string, end: string): string {
|
||||
@@ -27,7 +28,7 @@ export async function refreshMachine(machineId: string): Promise<UpdateSnapshot>
|
||||
outputHub.clear(machineId);
|
||||
|
||||
const proxy = m.aptProxyMode === "runtime" ? m.aptProxyUrl : null;
|
||||
const script = renderTemplate("apt/check.sh.tpl", { aptProxy: proxy });
|
||||
const script = renderTemplate(resolveTemplate("update-analyze", m.osFamily), { aptProxy: proxy });
|
||||
|
||||
let raw = "";
|
||||
try {
|
||||
@@ -41,32 +42,54 @@ export async function refreshMachine(machineId: string): Promise<UpdateSnapshot>
|
||||
throw err;
|
||||
}
|
||||
|
||||
const simulate = extractSection(raw, "===SU:SIMULATE===", "===SU:REBOOT===");
|
||||
const rebootSection = extractSection(raw, "===SU:REBOOT===", "===SU:END===");
|
||||
const packages = parseAptSimulate(simulate);
|
||||
const rebootRequired = parseRebootRequired(rebootSection);
|
||||
const status: MachineStatus = packages.length > 0 ? "updates_available" : "ok";
|
||||
const updateExit = /===SU:EXIT=(\d+)===/.exec(raw);
|
||||
const detail: AptSnapshotDetail = buildAptSnapshotDetail({
|
||||
upgradeSim: extractSection(raw, "===SU:APT_SIM_UPGRADE===", "===SU:APT_SIM_DISTUPGRADE==="),
|
||||
distUpgradeSim: extractSection(raw, "===SU:APT_SIM_DISTUPGRADE===", "===SU:APT_HELD==="),
|
||||
heldRaw: extractSection(raw, "===SU:APT_HELD===", "===SU:REBOOT==="),
|
||||
rebootRaw: extractSection(raw, "===SU:REBOOT===", "===SU:EXIT"),
|
||||
updateFailed: updateExit ? Number(updateExit[1]) !== 0 : false,
|
||||
});
|
||||
|
||||
// MachineStatus n'a pas "warning" : warning => updates_available côté machine.
|
||||
const status: MachineStatus =
|
||||
detail.status === "error" ? "error" : detail.count > 0 || detail.status === "warning" ? "updates_available" : "ok";
|
||||
const checkedAt = new Date().toISOString();
|
||||
|
||||
const snapshot: UpdateSnapshot = {
|
||||
machineId,
|
||||
hostname: m.hostname,
|
||||
os: { family: m.osFamily as UpdateSnapshot["os"]["family"], version: "" },
|
||||
os: { family: m.osFamily as UpdateSnapshot["os"]["family"], version: m.osVersion ?? "" },
|
||||
checkedAt,
|
||||
status,
|
||||
apt: { enabled: true, count: packages.length, rebootRequired, packages },
|
||||
apt: detail,
|
||||
schemaVersion: 1,
|
||||
kind: "apt_update_analyze",
|
||||
rawHints: { logImportantLines: reduceAptLines(raw) },
|
||||
};
|
||||
|
||||
const snapshotId = randomUUID();
|
||||
db.insert(schema.snapshots).values({
|
||||
id: randomUUID(),
|
||||
id: snapshotId,
|
||||
machineId,
|
||||
kind: "apt_update_analyze",
|
||||
schemaVersion: 1,
|
||||
checkedAt,
|
||||
status,
|
||||
payloadJson: JSON.stringify(snapshot),
|
||||
importantJson: JSON.stringify(snapshot.rawHints?.logImportantLines ?? []),
|
||||
}).run();
|
||||
db.update(schema.machines).set({ status, lastCheckedAt: checkedAt }).where(eq(schema.machines.id, machineId)).run();
|
||||
|
||||
upsertMachineState(machineId, deriveAptState(snapshot));
|
||||
recordEvent({
|
||||
machineId,
|
||||
eventType: "apt_refresh",
|
||||
severity: "info",
|
||||
snapshotId,
|
||||
message: `Refresh APT : ${snapshot.apt.count} mise(s) à jour`,
|
||||
});
|
||||
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user