Files
system_update/server/services/credentials.ts
T
gilles 08919752e3 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>
2026-06-05 19:50:25 +02:00

56 lines
2.2 KiB
TypeScript

// server/services/credentials.ts
import { eq } from "drizzle-orm";
import { db, schema } from "../db/client.js";
interface EncPair { encPassword: string | null; encSudoPassword: string | null; }
/** Résout la source des secrets : machine_credentials prioritaire, sinon legacy machines (fonction pure). */
export function resolveCreds(legacy: EncPair, creds: EncPair | null): EncPair {
if (creds && creds.encPassword) return { encPassword: creds.encPassword, encSudoPassword: creds.encSudoPassword };
return { encPassword: legacy.encPassword, encSudoPassword: legacy.encSudoPassword };
}
/** Écrit (insert/replace) la ligne machine_credentials pour une machine (secrets déjà chiffrés). */
export function writeCredentials(input: {
machineId: string;
encPassword: string | null;
encSudoPassword: string | null;
}): void {
const now = new Date().toISOString();
db.insert(schema.machineCredentials)
.values({
machineId: input.machineId,
authMethod: "password",
encPassword: input.encPassword,
encSudoPassword: input.encSudoPassword,
sudoMode: input.encSudoPassword ? "separate" : "same_as_ssh",
createdAt: now,
updatedAt: now,
status: "unknown",
})
.onConflictDoUpdate({
target: schema.machineCredentials.machineId,
set: { encPassword: input.encPassword, encSudoPassword: input.encSudoPassword, updatedAt: now },
})
.run();
}
/** Lit la ligne machine_credentials (ou null). */
export function readCredentials(machineId: string): EncPair | null {
const row = db.select().from(schema.machineCredentials)
.where(eq(schema.machineCredentials.machineId, machineId)).get();
return row ? { encPassword: row.encPassword, encSudoPassword: row.encSudoPassword } : null;
}
/** Backfill idempotent : crée une ligne machine_credentials pour chaque machine qui n'en a pas. */
export function backfillCredentials(): number {
const machines = db.select().from(schema.machines).all();
let created = 0;
for (const m of machines) {
if (readCredentials(m.id)) continue;
writeCredentials({ machineId: m.id, encPassword: m.encPassword, encSudoPassword: m.encSudoPassword });
created++;
}
return created;
}