Files
system_update/server/services/refresh.ts
T
2026-06-05 04:10:56 +02:00

82 lines
3.0 KiB
TypeScript

// server/services/refresh.ts
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 { reduceAptLines } from "../templates/aptReduce.js";
import { runScriptSudo } from "../ssh/client.js";
import { parseAptSimulate, parseRebootRequired } from "./aptParse.js";
import { outputHub } from "../ws/outputHub.js";
import type { UpdateSnapshot, MachineStatus } 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 {
const s = raw.indexOf(start);
if (s === -1) return "";
const from = s + start.length;
const e = raw.indexOf(end, from);
return raw.slice(from, e === -1 ? undefined : e).trim();
}
export async function refreshMachine(machineId: string): Promise<UpdateSnapshot> {
const m = getMachineRow(machineId);
if (!m) throw new Error("Machine introuvable");
db.update(schema.machines).set({ status: "running" }).where(eq(schema.machines.id, machineId)).run();
outputHub.clear(machineId);
const proxy = m.aptProxyMode === "runtime" ? m.aptProxyUrl : null;
const script = renderTemplate("apt/check.sh.tpl", { aptProxy: proxy });
let raw = "";
try {
const res = await runScriptSudo(getCreds(m), script, (c) => {
raw += c;
outputHub.publish(machineId, c);
});
raw = res.stdout;
} catch (err) {
db.update(schema.machines).set({ status: "error" }).where(eq(schema.machines.id, machineId)).run();
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 checkedAt = new Date().toISOString();
const snapshot: UpdateSnapshot = {
machineId,
hostname: m.hostname,
os: { family: m.osFamily as UpdateSnapshot["os"]["family"], version: "" },
checkedAt,
status,
apt: { enabled: true, count: packages.length, rebootRequired, packages },
rawHints: { logImportantLines: reduceAptLines(raw) },
};
db.insert(schema.snapshots).values({
id: randomUUID(),
machineId,
checkedAt,
status,
payloadJson: JSON.stringify(snapshot),
}).run();
db.update(schema.machines).set({ status, lastCheckedAt: checkedAt }).where(eq(schema.machines.id, machineId)).run();
return snapshot;
}
export function getLatestSnapshot(machineId: string): UpdateSnapshot | null {
const row = db
.select()
.from(schema.snapshots)
.where(eq(schema.snapshots.machineId, machineId))
.orderBy(desc(schema.snapshots.checkedAt))
.get();
return row ? (JSON.parse(row.payloadJson) as UpdateSnapshot) : null;
}