import type { Einstellungen, Software } from 'bibliothek-types';
|
import { alleSoftware, softwareAnlegen, softwareLoeschen, umzugsAngabenSetzen } from './datenbank';
|
import { aktuelleEinstellungen, speichereEinstellungen } from './konfiguration';
|
import { ladeGeheimnisse, loescheGeheimnisse, speichereGeheimnisse, type Geheimniswert } from './geheimnisse';
|
|
/**
|
* Umzug auf einen anderen Rechner.
|
*
|
* Bewusst als lesbares JSON und nicht als Ordner-Kopie:
|
* - Die geholten Dateien (mehrere hundert MB) und die Git-Arbeitskopien (GB)
|
* sind jederzeit reproduzierbar — ein Abgleich holt sie neu. Nur Katalog,
|
* Einstellungen und Geheimnisse sind unersetzlich, und das sind wenige KB.
|
* - Die Datenbank im laufenden Betrieb zu kopieren wäre unsicher: die jüngsten
|
* Änderungen stehen im WAL und fehlten in der Kopie.
|
* - JSON übersteht Schema-Änderungen: beim Einlesen werden Felder zugeordnet,
|
* statt eine Binärdatei einer bestimmten Version vorauszusetzen.
|
*/
|
|
/** Aktuelle Fassung des Austauschformats — erlaubt später sauberes Nachziehen. */
|
const FORMAT_FASSUNG = 1;
|
|
export interface UmzugsPaket {
|
fassung: number;
|
erstelltAm: string;
|
einstellungen: Einstellungen;
|
software: Software[];
|
/** Nur enthalten, wenn ausdrücklich mit exportiert (enthält Klartext-Schlüssel!). */
|
geheimnisse?: Record<string, Geheimniswert[]>;
|
}
|
|
export function erstelleUmzugsPaket(mitGeheimnissen: boolean): UmzugsPaket {
|
const eintraege = alleSoftware();
|
const paket: UmzugsPaket = {
|
fassung: FORMAT_FASSUNG,
|
erstelltAm: new Date().toISOString(),
|
einstellungen: aktuelleEinstellungen(),
|
software: eintraege,
|
};
|
if (mitGeheimnissen) {
|
paket.geheimnisse = {};
|
for (const eintrag of eintraege) {
|
const werte = ladeGeheimnisse(eintrag.id);
|
if (werte.length > 0) {
|
paket.geheimnisse[String(eintrag.id)] = werte;
|
}
|
}
|
}
|
return paket;
|
}
|
|
export interface UmzugsErgebnis {
|
uebernommen: number;
|
mitGeheimnissen: number;
|
hinweis: string;
|
}
|
|
/**
|
* Liest ein Paket ein und ersetzt den bisherigen Katalog vollständig.
|
* Die eigentlichen Programm-Dateien kommen nicht mit — sie werden beim
|
* nächsten Abgleich neu geholt bzw. gebaut.
|
*/
|
export function uebernehmeUmzugsPaket(paket: UmzugsPaket): UmzugsErgebnis {
|
if (!paket || typeof paket !== 'object' || !Array.isArray(paket.software)) {
|
throw new Error('Die Datei ist kein gültiges Umzugs-Paket.');
|
}
|
if (paket.fassung > FORMAT_FASSUNG) {
|
throw new Error(`Das Paket stammt aus einer neueren Fassung (${paket.fassung}) — bitte die Anwendung aktualisieren.`);
|
}
|
|
// Bisherigen Bestand räumen, damit keine Mischung aus altem und neuem Stand entsteht.
|
for (const vorhandene of alleSoftware()) {
|
loescheGeheimnisse(vorhandene.id);
|
softwareLoeschen(vorhandene.id);
|
}
|
|
// Die Ids ändern sich beim Neuanlegen — für die Eltern-Zuordnung mitführen.
|
const idZuordnung = new Map<number, number>();
|
for (const eintrag of paket.software.filter((eintrag) => !eintrag.elternId)) {
|
idZuordnung.set(eintrag.id, uebernehmeEintrag(eintrag, 0).id);
|
}
|
for (const eintrag of paket.software.filter((eintrag) => eintrag.elternId)) {
|
idZuordnung.set(eintrag.id, uebernehmeEintrag(eintrag, idZuordnung.get(eintrag.elternId) ?? 0).id);
|
}
|
|
let mitGeheimnissen = 0;
|
for (const [alteId, werte] of Object.entries(paket.geheimnisse ?? {})) {
|
const neueId = idZuordnung.get(Number(alteId));
|
if (neueId) {
|
speichereGeheimnisse(neueId, werte);
|
mitGeheimnissen++;
|
}
|
}
|
|
if (paket.einstellungen) {
|
// Datenordner und Port gehören zum Zielrechner und werden bewusst nicht überschrieben.
|
speichereEinstellungen({
|
gitBenutzer: paket.einstellungen.gitBenutzer ?? '',
|
gitSchluessel: paket.einstellungen.gitSchluessel ?? '',
|
});
|
}
|
|
return {
|
uebernommen: idZuordnung.size,
|
mitGeheimnissen,
|
hinweis: 'Die Programm-Dateien wurden nicht mit übertragen — sie werden beim nächsten Abgleich neu geholt bzw. gebaut.',
|
};
|
}
|
|
/** Legt einen Eintrag neu an und stellt den bekannten Abgleich-Stand wieder her. */
|
function uebernehmeEintrag(eintrag: Software, elternId: number): Software {
|
const angelegt = softwareAnlegen({
|
name: eintrag.name ?? '',
|
beschreibung: eintrag.beschreibung ?? '',
|
elternId,
|
quelleTyp: eintrag.quelleTyp ?? '',
|
quelleWert: eintrag.quelleWert ?? '',
|
bauBefehl: eintrag.bauBefehl ?? '',
|
bauErgebnis: eintrag.bauErgebnis ?? '',
|
einstufung: eintrag.einstufung ?? 'lizenzfrei',
|
programmart: eintrag.programmart ?? 'unbestimmt',
|
hinweis: eintrag.hinweis ?? '',
|
anleitungUrl: eintrag.anleitungUrl ?? '',
|
erwarteterHerausgeber: eintrag.erwarteterHerausgeber ?? '',
|
});
|
// Version und Datei bleiben leer (Standard beim Anlegen): Die Datei fehlt auf dem
|
// neuen Rechner noch, der nächste Abgleich soll sie wirklich holen. Bewusst KEIN
|
// fehlgeschlagener Abgleich — „noch nicht geholt" ist kein Problemfall und soll
|
// nicht in der Warnleiste auftauchen.
|
umzugsAngabenSetzen(angelegt.id, eintrag.veroeffentlichtAm ?? '', eintrag.benoetigteGeheimnisse ?? []);
|
return angelegt;
|
}
|