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-server/src/konfiguration.ts |  107 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 107 insertions(+), 0 deletions(-)

diff --git a/packages/bibliothek-server/src/konfiguration.ts b/packages/bibliothek-server/src/konfiguration.ts
new file mode 100644
index 0000000..a13c673
--- /dev/null
+++ b/packages/bibliothek-server/src/konfiguration.ts
@@ -0,0 +1,107 @@
+import * as fs from 'fs';
+import * as path from 'path';
+import type { Einstellungen } from 'bibliothek-types';
+import { anwendungsDatenOrdner } from './pfade';
+
+/**
+ * Einstellungen liegen in `konfiguration.json` neben der Anwendung. Die Datei wird
+ * beim ersten Start mit Standardwerten angelegt und kann über die Oberfläche
+ * (nur lokal am Server) oder von Hand gepflegt werden.
+ *
+ * Umgebungsvariablen haben Vorrang — praktisch für abweichende Testläufe.
+ */
+
+const STANDARD_EINSTELLUNGEN: Einstellungen = {
+  datenOrdner: '', // leer = Unterordner „data" im Anwendungsdaten-Ordner
+  port: 3000,
+  gitBenutzer: '', // leer = SSH-Konfiguration des Dienstbenutzers entscheidet
+  gitSchluessel: '',
+};
+
+const konfigurationsPfad = path.join(anwendungsDatenOrdner(), 'konfiguration.json');
+
+/** Liest die Konfigurationsdatei (und legt sie beim ersten Start an). */
+function leseOderErzeuge(): Einstellungen {
+  try {
+    if (fs.existsSync(konfigurationsPfad)) {
+      return normalisiere(JSON.parse(fs.readFileSync(konfigurationsPfad, 'utf8')) as Partial<Einstellungen>);
+    }
+    const standard = { ...STANDARD_EINSTELLUNGEN };
+    schreibeDatei(standard);
+    return standard;
+  } catch (fehler) {
+    console.warn(`konfiguration.json konnte nicht gelesen werden, Standardwerte greifen: ${String(fehler)}`);
+    return { ...STANDARD_EINSTELLUNGEN };
+  }
+}
+
+// Beim Laden des Moduls einlesen, damit auch die Datenbank-Schicht schon den
+// konfigurierten Ordner sieht (sie wird beim Start direkt initialisiert).
+let gelesene: Einstellungen = leseOderErzeuge();
+
+/** Speichert geänderte Einstellungen. Der Datenordner wirkt erst nach einem Neustart. */
+export function speichereEinstellungen(neue: Partial<Einstellungen>): Einstellungen {
+  gelesene = normalisiere({ ...gelesene, ...neue });
+  schreibeDatei(gelesene);
+  return gelesene;
+}
+
+export function aktuelleEinstellungen(): Einstellungen {
+  return gelesene;
+}
+
+export function konfigurationsDatei(): string {
+  return konfigurationsPfad;
+}
+
+/** Tatsächlich genutzter Datenordner — Umgebungsvariable vor Einstellung vor Standard. */
+export function effektiverDatenOrdner(): string {
+  const ausUmgebung = process.env.DATEN_ORDNER?.trim();
+  if (ausUmgebung) {
+    return path.resolve(ausUmgebung);
+  }
+  if (gelesene.datenOrdner.trim()) {
+    return path.resolve(gelesene.datenOrdner.trim());
+  }
+  return path.join(anwendungsDatenOrdner(), 'data');
+}
+
+/** Tatsächlich genutzter Port — Umgebungsvariable vor Einstellung vor Standard. */
+export function effektiverPort(): number {
+  const ausUmgebung = Number(process.env.PORT);
+  if (Number.isInteger(ausUmgebung) && ausUmgebung > 0) {
+    return ausUmgebung;
+  }
+  return gelesene.port;
+}
+
+function normalisiere(eingabe: Partial<Einstellungen>): Einstellungen {
+  const port = Number(eingabe.port);
+  return {
+    datenOrdner: typeof eingabe.datenOrdner === 'string' ? eingabe.datenOrdner.trim() : '',
+    port: Number.isInteger(port) && port > 0 && port < 65536 ? port : STANDARD_EINSTELLUNGEN.port,
+    // Nur was als SSH-Benutzername taugt — schützt zugleich vor Einschleusen in die Befehlszeile.
+    gitBenutzer: /^[A-Za-z0-9._-]*$/.test(String(eingabe.gitBenutzer ?? '').trim()) ? String(eingabe.gitBenutzer ?? '').trim() : '',
+    gitSchluessel: String(eingabe.gitSchluessel ?? '').trim(),
+  };
+}
+
+/**
+ * Baut den SSH-Aufruf für Git aus den Einstellungen. Leer, wenn nichts gepflegt ist —
+ * dann greift die SSH-Konfiguration des Dienstbenutzers wie bisher.
+ */
+export function gitSshBefehl(): string {
+  const teile = ['ssh'];
+  if (gelesene.gitBenutzer) {
+    teile.push('-o', `User=${gelesene.gitBenutzer}`);
+  }
+  if (gelesene.gitSchluessel) {
+    teile.push('-i', `"${gelesene.gitSchluessel.replace(/\\/g, '/')}"`);
+  }
+  return teile.length > 1 ? teile.join(' ') : '';
+}
+
+function schreibeDatei(einstellungen: Einstellungen): void {
+  fs.mkdirSync(path.dirname(konfigurationsPfad), { recursive: true });
+  fs.writeFileSync(konfigurationsPfad, `${JSON.stringify(einstellungen, null, 2)}\n`);
+}

--
Gitblit v1.9.3