From 98bce1b0da675715b2e5482c0ad61ba21135f62f Mon Sep 17 00:00:00 2001
From: Server-Bibliothek <server-bibliothek@lokal>
Date: Di, 04 Aug 2026 07:00:04 +0200
Subject: [PATCH] Add a desktop client that knows what is installed locally
---
packages/bibliothek-client/src/api-client.ts | 130 +++++++++++++++++++++++++++++++++++++++++++
1 files changed, 130 insertions(+), 0 deletions(-)
diff --git a/packages/bibliothek-client/src/api-client.ts b/packages/bibliothek-client/src/api-client.ts
new file mode 100644
index 0000000..11f7bec
--- /dev/null
+++ b/packages/bibliothek-client/src/api-client.ts
@@ -0,0 +1,130 @@
+import type { Einstellungen, EinstellungenAnsicht, GeheimnisUebersicht, Software, SoftwareEingabe } from 'bibliothek-types';
+
+/** Wertet eine Antwort aus, wirft bei Fehler mit der Server-Meldung. */
+async function jsonOderFehler<T>(antwort: Response): Promise<T> {
+ if (!antwort.ok) {
+ const daten = (await antwort.json().catch(() => ({}))) as { fehler?: string };
+ throw new Error(daten.fehler ?? `Fehler ${antwort.status}`);
+ }
+ return antwort.json() as Promise<T>;
+}
+
+/** Lädt den kompletten Software-Katalog vom Server. */
+export async function ladeKatalog(): Promise<Software[]> {
+ return jsonOderFehler(await fetch('/api/software'));
+}
+
+/** Fragt, ob der Zugriff direkt am Server (lokal) erfolgt — steuert die Sichtbarkeit der Verwaltung. */
+export async function ladeUmgebung(): Promise<{ lokal: boolean }> {
+ return jsonOderFehler(await fetch('/api/umgebung'));
+}
+
+export async function softwareAnlegen(eingabe: SoftwareEingabe): Promise<Software> {
+ return jsonOderFehler(
+ await fetch('/api/software', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(eingabe),
+ })
+ );
+}
+
+export async function softwareAktualisieren(id: number, eingabe: SoftwareEingabe): Promise<Software> {
+ return jsonOderFehler(
+ await fetch(`/api/software/${id}`, {
+ method: 'PUT',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(eingabe),
+ })
+ );
+}
+
+export async function softwareLoeschen(id: number): Promise<void> {
+ const antwort = await fetch(`/api/software/${id}`, { method: 'DELETE' });
+ if (!antwort.ok) {
+ throw new Error(`Löschen fehlgeschlagen (Status ${antwort.status})`);
+ }
+}
+
+/** Hinterlegt eine Datei von Hand (Quellentyp „manuell"). */
+export async function dateiHochladen(id: number, datei: File): Promise<Software> {
+ return jsonOderFehler(
+ await fetch(`/api/software/${id}/datei?dateiname=${encodeURIComponent(datei.name)}`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/octet-stream' },
+ body: datei,
+ })
+ );
+}
+
+/** Löst einen sofortigen Abgleich der Bezugsquelle einer Software aus. */
+export async function abgleichen(id: number): Promise<Software> {
+ return jsonOderFehler(await fetch(`/api/software/${id}/abgleichen`, { method: 'POST' }));
+}
+
+/** Gleicht alle Einträge mit gesetzter Quelle ab. */
+export async function alleAbgleichen(): Promise<Software[]> {
+ return jsonOderFehler(await fetch('/api/abgleich/alle', { method: 'POST' }));
+}
+
+/** Stand der Bau-Geheimnisse einer Software (nur Namen, nie die Werte). */
+export async function ladeGeheimnisse(id: number): Promise<GeheimnisUebersicht[]> {
+ return jsonOderFehler(await fetch(`/api/software/${id}/geheimnisse`));
+}
+
+/** Speichert Bau-Geheimnisse. Ein leerer Wert lässt den bisherigen unverändert. */
+export async function speichereGeheimnisse(id: number, werte: Array<{ name: string; wert: string }>): Promise<GeheimnisUebersicht[]> {
+ return jsonOderFehler(
+ await fetch(`/api/software/${id}/geheimnisse`, {
+ method: 'PUT',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(werte),
+ })
+ );
+}
+
+/** URL für den Umzugs-Export (Katalog + Einstellungen, optional mit Geheimnissen). */
+export function exportUrl(mitGeheimnissen: boolean): string {
+ return `/api/export${mitGeheimnissen ? '?geheimnisse=ja' : ''}`;
+}
+
+/** Liest ein Umzugs-Paket ein und ersetzt damit den bisherigen Katalog. */
+export async function importiereUmzugsPaket(paket: unknown): Promise<{ uebernommen: number; mitGeheimnissen: number; hinweis: string }> {
+ return jsonOderFehler(
+ await fetch('/api/import', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(paket),
+ })
+ );
+}
+
+/** Lädt die Einstellungen samt der daraus abgeleiteten Pfade (nur lokal erlaubt). */
+export async function ladeEinstellungen(): Promise<EinstellungenAnsicht> {
+ return jsonOderFehler(await fetch('/api/einstellungen'));
+}
+
+export async function speichereEinstellungen(einstellungen: Einstellungen): Promise<EinstellungenAnsicht> {
+ return jsonOderFehler(
+ await fetch('/api/einstellungen', {
+ method: 'PUT',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(einstellungen),
+ })
+ );
+}
+
+/** URL zum Herunterladen mehrerer Programme als ZIP. */
+export function downloadMehrereUrl(ids: number[], angegebenerName?: string): string {
+ const parameter = new URLSearchParams({ ids: ids.join(',') });
+ if (angegebenerName) {
+ parameter.set('name', angegebenerName);
+ }
+ return `/api/download-mehrere?${parameter.toString()}`;
+}
+
+/** URL zum Herunterladen der Datei einer Software (optional mit angegebenem Namen fürs Protokoll). */
+export function downloadUrl(id: number, angegebenerName?: string): string {
+ const basis = `/api/software/${id}/download`;
+ return angegebenerName ? `${basis}?name=${encodeURIComponent(angegebenerName)}` : basis;
+}
--
Gitblit v1.9.3