feat(schluessel): erzeugen, importieren, exportieren (#012)
Schluessel werden jetzt verwaltet statt nur im Prototyp gezeigt: Liste mit Filter, Anlegen (erzeugen oder importieren), Detail nach dem Prototyp aus #002, Bearbeiten der Metadaten und Download <slug>.pub. Warum so: - Erzeugen und Import eines Paars laufen als Vorgang im Worker, weil nur er den Master-Key hat. Passphrase und importierter Private Key reisen wie das Bootstrap-Passwort mit SSHKV_APP_SECRET verschluesselt im Job, nie in Vorgang.eingabe; der Worker loescht den Job danach. - Ein reiner Public Key ist kein Geheimnis und wird direkt im Web angelegt, mit verstaendlicher Meldung bei einem Duplikat. - Das Web liest nie das Chiffrat: ob ein Private Key gespeichert ist, verraet masterKeyVersion (per CHECK an das Chiffrat gekoppelt). Ein struktureller Test verlangt fuer jede zeilenliefernde Abfrage auf Schluessel ausserhalb des Workers ein festes select. - Migration: Vorgangsarten fuer Erzeugen, Import und (fuer #014) das Loeschen des Private Keys; CHECKs fuer den Slug (landet in Dateinamen) und "geloescht nur ohne Chiffrat". - Platzhalter PublicAusrollenAktion/PrivateAusrollenAktion fuer #016/#017. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
import { prisma } from "@/lib/db";
|
||||
import { WARTESCHLANGE, warteschlangeHolen, type VorgangsJob } from "@/server/vorgaenge/warteschlange";
|
||||
import { vorgangAusfuehren } from "@/worker/ausfuehren";
|
||||
import { HANDLER } from "@/worker/handler";
|
||||
|
||||
/**
|
||||
* Fuehrt einen eingereihten Vorgang so aus, wie es der Worker tut (#012): Job aus pg-boss
|
||||
* holen, mit seinem `geheim` ausfuehren, danach loeschen (src/worker/index.ts).
|
||||
* Liefert zusaetzlich die rohen Job-Daten, damit Tests pruefen koennen, was darin stand.
|
||||
*/
|
||||
export async function vorgangImWorker(vorgangId: string): Promise<{ jobDaten: VorgangsJob }> {
|
||||
const boss = await warteschlangeHolen();
|
||||
// Die Warteschlange ueberlebt das Leeren der Tabellen; deshalb gezielt den eigenen Job suchen.
|
||||
const jobs = await boss.fetch<VorgangsJob>(WARTESCHLANGE, { batchSize: 100 });
|
||||
const job = jobs.find((kandidat) => kandidat.data.vorgangId === vorgangId);
|
||||
if (!job) throw new Error(`Kein Job für Vorgang ${vorgangId}`);
|
||||
await vorgangAusfuehren(job.data.vorgangId, HANDLER, job.data.geheim);
|
||||
await boss.complete(WARTESCHLANGE, job.id);
|
||||
if (job.data.geheim) await boss.deleteJob(WARTESCHLANGE, job.id);
|
||||
return { jobDaten: job.data };
|
||||
}
|
||||
|
||||
/** Alle Zeilen aller Tabellen als ein Text – fuer „steht das Geheimnis irgendwo?“. */
|
||||
export async function datenbankAlsText(): Promise<string> {
|
||||
const teile = await Promise.all([
|
||||
prisma.vorgang.findMany(),
|
||||
prisma.protokoll.findMany(),
|
||||
prisma.$queryRaw<unknown[]>`SELECT data FROM pgboss.job`,
|
||||
]);
|
||||
return JSON.stringify(teile, (_k, wert: unknown) => (typeof wert === "bigint" ? wert.toString() : wert));
|
||||
}
|
||||
|
||||
export async function warteschlangeStoppen(): Promise<void> {
|
||||
const boss = await warteschlangeHolen();
|
||||
await boss.stop({ graceful: false });
|
||||
}
|
||||
Reference in New Issue
Block a user