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:
@@ -0,0 +1,78 @@
|
||||
// server/cli/createApiClient.ts
|
||||
import { pathToFileURL } from "node:url";
|
||||
import type { ApiClientScope } from "@shared/types.js";
|
||||
import { runMigrations } from "../db/migrate.js";
|
||||
import { createApiClient } from "../services/apiClients.js";
|
||||
|
||||
export interface CreateApiClientCliOptions {
|
||||
name: string;
|
||||
scopes: ApiClientScope[];
|
||||
}
|
||||
|
||||
const ALLOWED_SCOPES: ApiClientScope[] = ["read", "operate", "admin", "debug"];
|
||||
|
||||
export function parseCreateApiClientArgs(args: string[]): CreateApiClientCliOptions {
|
||||
let name = "";
|
||||
let scopes: ApiClientScope[] = ["read"];
|
||||
|
||||
for (let i = 0; i < args.length; i += 1) {
|
||||
const arg = args[i];
|
||||
if (arg === "--name") {
|
||||
i += 1;
|
||||
name = args[i] ?? "";
|
||||
} else if (arg === "--scopes") {
|
||||
i += 1;
|
||||
scopes = parseScopes(args[i] ?? "");
|
||||
} else if (arg === "--help" || arg === "-h") {
|
||||
throw new Error(helpText());
|
||||
} else {
|
||||
throw new Error(`Argument inconnu: ${arg}\n\n${helpText()}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (!name.trim()) throw new Error(`--name est obligatoire\n\n${helpText()}`);
|
||||
return { name: name.trim(), scopes };
|
||||
}
|
||||
|
||||
function parseScopes(raw: string): ApiClientScope[] {
|
||||
const scopes = raw
|
||||
.split(",")
|
||||
.map((scope) => scope.trim())
|
||||
.filter(Boolean) as ApiClientScope[];
|
||||
|
||||
if (scopes.length === 0) return ["read"];
|
||||
for (const scope of scopes) {
|
||||
if (!ALLOWED_SCOPES.includes(scope)) {
|
||||
throw new Error(`Scope invalide: ${scope}. Scopes valides: ${ALLOWED_SCOPES.join(", ")}`);
|
||||
}
|
||||
}
|
||||
return [...new Set(scopes)];
|
||||
}
|
||||
|
||||
function helpText(): string {
|
||||
return [
|
||||
"Usage:",
|
||||
" pnpm api-client:create -- --name \"App Rust\" --scopes read,operate",
|
||||
"",
|
||||
"Variables requises:",
|
||||
" SU_MASTER_KEY clé hex 64 caractères",
|
||||
" SU_DB_PATH chemin SQLite optionnel",
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const options = parseCreateApiClientArgs(process.argv.slice(2));
|
||||
runMigrations();
|
||||
const created = createApiClient(options);
|
||||
console.log(JSON.stringify(created, null, 2));
|
||||
}
|
||||
|
||||
const entrypoint = process.argv[1] ? pathToFileURL(process.argv[1]).href : "";
|
||||
if (import.meta.url === entrypoint) {
|
||||
main().catch((err) => {
|
||||
console.error((err as Error).message);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
}
|
||||
|
||||
export const createApiClientCliInternals = { parseScopes, helpText };
|
||||
Reference in New Issue
Block a user