From 9b3bf078061d2d02e7ebe7fb3c31869e35272be1 Mon Sep 17 00:00:00 2001
From: Server-Bibliothek <server-bibliothek@lokal>
Date: Di, 04 Aug 2026 06:24:47 +0200
Subject: [PATCH] Rewrite README and architecture docs for the new implementation
---
ARCHITEKTUR.md | 397 +++++++++++++++++++++++---------------------------------
1 files changed, 166 insertions(+), 231 deletions(-)
diff --git a/ARCHITEKTUR.md b/ARCHITEKTUR.md
index 9af377f..a7e9ff7 100644
--- a/ARCHITEKTUR.md
+++ b/ARCHITEKTUR.md
@@ -1,272 +1,207 @@
-# Architektur-Dokumentation – Server-Bibliothek
+# Architektur
-Diese Datei ist für die technische Übergabe gedacht: sie erklärt, wie das
-Projekt aufgebaut ist, welche Entscheidungen warum getroffen wurden, und was
-bei einer Anpassung an eine andere Infrastruktur zu beachten ist.
-
-Für die Bedienungsanleitung (Endnutzer-Sicht) siehe `README.md`.
-Für die Entwicklungshistorie (was wurde wann gebaut) siehe `CHANGELOG.md`.
+Die technische Innensicht: wie das Projekt aufgebaut ist, welche Entscheidungen warum so
+getroffen wurden und wo die Grenzen liegen. Für Betrieb und Bedienung siehe
+[README.md](README.md).
---
-## 1. Tech-Stack
+## 1. Grundriss
-- **Python 3.8+**, **Flask** (Web-Framework) – einzige externe Abhängigkeit
- laut `requirements.txt`
-- **SQLite** (`data/bibliothek.db`) – eine einzelne Datei, kein Datenbankserver
-- **Server-seitig gerenderte HTML-Templates** (Jinja2), kein JavaScript-Framework,
- kein Build-Prozess – nur Vanilla-JS für Interaktivität (Checklisten,
- Formular-Verhalten)
-- Läuft als einfacher Flask-Entwicklungsserver (`app.run(host="0.0.0.0", ...)`)
- – **nicht produktionsgehärtet**, siehe Abschnitt 7 "Bekannte Grenzen"
+npm-Workspaces mit drei Paketen. Die Typen liegen bewusst dazwischen: Server und
+Oberfläche teilen sich einen Vertrag, den der Übersetzer prüft — ändert sich ein Feld,
+bricht die Übersetzung auf beiden Seiten.
-Bewusste Design-Entscheidung: keine Frameworks/Build-Tools, damit eine Person
-ohne Programmiererfahrung (Zielgruppe dieses Projekts) das System mit einem
-Doppelklick starten kann, ohne `npm install` o. Ä.
+| Paket | Rolle |
+|---|---|
+| `bibliothek-types` | Gemeinsame Typen. Kennt weder Server noch Browser |
+| `bibliothek-server` | Express, SQLite, Bezugsquellen, Zugriffsgrenze |
+| `bibliothek-client` | Oberfläche aus `@dh-software/dh-components` (Lit) |
-## 2. Projektstruktur
+TypeScript durchgehend im `strict`-Modus.
-```
-server-bibliothek/
-├── app.py Haupt-Flask-Anwendung, alle Routen
-├── updater.py Update-Check-Logik (winget/GitHub/Direkt-Link)
-├── automatische_pruefung.py Eigenständiges Skript für geplante Update-Checks
-├── requirements.txt Python-Abhängigkeiten (nur Flask)
-├── data/
-│ ├── bibliothek.db SQLite-Datenbank (wird beim ersten Start angelegt)
-│ └── doku_bilder/<software_id>/ Hochgeladene Doku-Screenshots
-├── installers/ Standard-Ablageort für Installer-Dateien
-│ (Nutzer koennen aber JEDEN Pfad eintragen,
-│ dieser Ordner ist nur ein Vorschlag)
-├── static/style.css Gesamtes Styling (ein File, keine Build-Pipeline)
-├── templates/ Jinja2-HTML-Templates
-├── install.bat Einmalige Einrichtung (Python-Abhaengigkeiten)
-├── start.bat Startet den Webserver
-├── pruefe_updates.bat Manueller Update-Check-Lauf
-├── automatisierung_einrichten.bat Richtet taeglichen Update-Check per Windows-Aufgabenplanung ein
-├── firewall-freigabe-einrichten.bat Oeffnet Port 5000 in der Windows-Firewall
-├── Update-Quellen-Uebersicht.md Nachschlagehilfe fuer winget-IDs/GitHub-Pfade
-├── README.md Bedienungsanleitung
-├── ARCHITEKTUR.md Diese Datei
-└── CHANGELOG.md Entwicklungshistorie
+### Warum Node und nicht Flask
+
+Der übernommene Prototyp war Flask. Der Umbau auf Node hatte zwei Gründe: Die Oberfläche
+sollte auf dem hauseigenen Komponenten-Satz aufsetzen, und der Auslieferungsstand sollte
+eine einzelne Programmdatei ohne installierte Laufzeitumgebung sein. Beides zieht Node
+nach sich.
+
+### Warum keine Datenbankbibliothek
+
+Ab Node 22 bringt die Laufzeitumgebung SQLite mit (`node:sqlite`). Damit entfällt eine
+native Abhängigkeit, die für jede Node-Version neu übersetzt werden müsste — genau daran
+war ein erster Versuch mit `better-sqlite3` gescheitert. Journalmodus WAL, damit Lesen
+während eines laufenden Abgleichs nicht blockiert.
+
+---
+
+## 2. Bezugsquellen
+
+Das Herzstück. Jede Art, an eine Datei zu kommen, ist eine Klasse hinter derselben
+Schnittstelle:
+
+```ts
+interface Bezugsquelle {
+ gleicheAb(auftrag: Abgleichauftrag): Promise<QuellenAbgleich>;
+}
```
-## 3. Datenbankschema
-
-Alle Tabellen werden in `init_db()` (app.py) angelegt. Migrationen (neue
-Spalten) laufen automatisch beim Start via `ALTER TABLE ... ADD COLUMN`,
-bestehende Daten bleiben dabei erhalten.
-
-### `software` – der Tool-Katalog
-| Spalte | Typ | Bedeutung |
-|---|---|---|
-| id | INTEGER PK | |
-| name | TEXT | Anzeigename, wird auch für Dateinamen-Ableitung sanitisiert |
-| kategorie | TEXT | Freitext, für Gruppierung in Checklisten |
-| version | TEXT | Bei "direkt"-Update-Quelle: technische Kennung (ETag), keine echte Versionsnummer |
-| installer_pfad | TEXT | Ordner **oder** Datei-Pfad, siehe Abschnitt 4 "Ordner-Konvention" |
-| install_parameter | TEXT | Silent-Install-Flags, z. B. `/S` oder `/quiet /norestart` |
-| dokumentation | TEXT | Freitext-Anleitung |
-| letzte_pruefung | TEXT (Datum) | Für die Status-Anzeige (Aktuell/Bitte prüfen/Nie geprüft) |
-| erstellt_am | TEXT (Zeitstempel) | |
-| auto_update | INTEGER (0/1) | Automatische Aktualisierung aktiv? |
-| update_quelle | TEXT | `winget` \| `github` \| `direkt` \| leer |
-| update_wert | TEXT | Paket-ID / `besitzer/projekt` / URL, je nach Quelle |
-
-### `zuweisungen` – einzelne Tool-Zuweisungen (Zeilen eines Protokolls)
-| Spalte | Bedeutung |
+| Typ | Vorgehen |
|---|---|
-| id, software_id | |
-| protokoll_id | FK auf `protokolle` (siehe unten) |
-| mitarbeiter, rechnername | Denormalisiert hier UND im Protokoll gespeichert (Absicht: die Installationsverlauf-Ansicht pro Software bleibt dadurch einfach) |
-| installierte_version, installiert_von, installiert_am, notiz | |
+| `direkt` | HTTP-Abruf eines festen Links |
+| `github` | Neuestes Release über die GitHub-Schnittstelle, passendes Asset wählen |
+| `winget` | `winget download`, danach `winget show` für das Veröffentlichungsdatum |
+| `svn` | `svn export` einer Datei |
+| `git` | Repository klonen bzw. aktualisieren, bauen, Ergebnis übernehmen |
+| `manuell` | Kein Abruf — die Datei wird einmalig hinterlegt und bleibt liegen |
-### `protokolle` – ein Eintrag pro Rechner-Einrichtung
-| Spalte | Bedeutung |
-|---|---|
-| id, mitarbeiter, rechnername, datum, erstellt_von, notiz, erstellt_zeitpunkt | |
+Eine neue Quelle anzubinden heißt: eine Klasse schreiben und in der Übersicht eintragen.
+Der Rest — Abgleich, Prüfung, Speicherung, Anzeige — bleibt unberührt.
-Ein Protokoll fasst mehrere `zuweisungen`-Zeilen zusammen (1 Protokoll : n
-Zuweisungen), damit bei z. B. 40 Mitarbeitern × 40 Tools keine unübersichtliche
-flache Liste entsteht, sondern eine Übersicht pro Person/Rechner.
+### Nichts wird ersetzt, bevor es geprüft ist
-### `update_log` – Protokoll jedes automatischen Update-Checks
-| Spalte | Bedeutung |
-|---|---|
-| id, software_id, zeitpunkt, alte_version, neue_version, erfolg (0/1), meldung | |
+Der teuerste Fehler wäre, eine funktionierende Datei gegen Müll zu tauschen, weil eine
+Quelle stillschweigend etwas anderes liefert als erwartet. Deshalb schreibt keine Quelle
+direkt in den Zielordner:
-### `doku_bilder` – hochgeladene Screenshots zur Dokumentation
-| Spalte | Bedeutung |
-|---|---|
-| id, software_id, dateiname, hochgeladen_am | Datei liegt unter `data/doku_bilder/<software_id>/<dateiname>` |
+1. Herunterladen bzw. bauen in `<zielordner>.neu`
+2. `inhaltBeanstanden()` prüft: leer? HTML statt Programmdatei (Signatur und
+ Inhaltstyp)? plötzlich unter der Hälfte der bisherigen Größe und kleiner als 100 KB?
+3. Erst wenn nichts zu beanstanden ist, wird getauscht — sonst verworfen
-## 4. Kernkonzept: Ordner-Konvention (`updater.py`)
+Der Eintrag meldet dann das Problem, behält aber seine bisherige Datei. Eine tote Quelle
+kostet Aktualität, nie den Download.
-Das Feld `installer_pfad` kann zwei Formen haben:
-- **Datei-Pfad** (endet auf `.exe`/`.msi`/etc.): ältere/explizite Schreibweise
-- **Ordner-Pfad** (kein Dateiname): empfohlene, aktuelle Schreibweise
+### Selbstgebaute Werkzeuge
-Die Funktion `updater.effektiver_ablageordner(installer_pfad, name)` ist die
-zentrale Logik: sie sorgt **immer** dafür, dass jedes Tool in einem eigenen
-Unterordner landet (benannt nach dem sanitisierten Tool-Namen), selbst wenn
-im Feld nur ein geteilter Basisordner steht oder ein alter kompletter
-Dateipfad. Das verhindert, dass mehrere Tools sich einen Ordner teilen und
-Dateien "nackt" nebeneinander landen (siehe CHANGELOG, Runden r6–r8, dort
-wurde das mehrfach nachgeschärft, nachdem reale Testfälle Lücken zeigten).
+Bei `git` beschreibt das Repository seinen eigenen Bau in `software-bibliothek.json`:
-`updater.verwaltete_datei_pfad(installer_pfad, name)` findet die aktuell
-abgelegte Datei (falls vorhanden), inkl. Rückwärtskompatibilität zu Dateien,
-die noch am alten, nicht verschachtelten Ort liegen (Übergangslogik).
+```json
+{
+ "buildCommand": "…",
+ "artifact": "dist/MeinWerkzeug.exe",
+ "secrets": [{ "name": "…", "description": "…" }]
+}
+```
-**Wichtig für Weiterentwicklung:** Jede neue Stelle im Code, die mit
-Installer-Dateien arbeitet, MUSS über diese beiden Funktionen gehen, statt
-`installer_pfad` direkt zu verwenden – sonst wird die
-Unterordner-Garantie umgangen.
+Damit muss die Bibliothek nichts über einzelne Werkzeuge wissen — ein neues anzubinden
+erfordert keine Änderung an ihr. Gebaut wird nur, wenn sich der Commit geändert hat.
+Zeitgrenze 30 Minuten, weil Bauvorgänge mit vielen Abhängigkeiten lange dauern.
-## 5. Automatische Aktualisierung (drei Quellen)
+Die Kennung für den Zugriff kommt aus den Einstellungen (`GIT_SSH_COMMAND`), nicht aus der
+URL. So bleibt der Katalogeintrag frei von Kontoangaben, und ein Kontowechsel ist eine
+einzige Änderung statt einer pro Eintrag.
-`updater.pruefe_software(name, quelle, update_wert, aktuelle_version, installer_pfad)`
-ist der Einstiegspunkt, delegiert an:
+### Bau-Geheimnisse
-- **`pruefe_winget`** – ruft `winget show`/`winget download` auf (nur unter
- Windows verfügbar; auf Nicht-Windows-Systemen liefert es einen sauberen
- Fehler statt eines Absturzes)
-- **`pruefe_github`** – nutzt die öffentliche GitHub-API
- (`api.github.com/repos/.../releases/latest`), sucht ein `.exe`/`.msi`-Asset
-- **`pruefe_direkt`** – fester Download-Link, Versionsvergleich über
- HTTP-`ETag`/`Last-Modified`-Header statt echter Versionsnummer
+Werte liegen getrennt vom Katalog in `secrets/<id>.json` und werden beim Bau als
+Umgebungsvariablen gesetzt — sie berühren die Platte der Arbeitskopie nie. Das Verfahren
+im Ganzen beschreibt der Skill `build-secrets` im Repository `dh-claude-rules`.
-Alle drei prüfen zusätzlich, ob die Datei **physisch noch existiert** – auch
-bei gleicher Versionsnummer wird neu heruntergeladen, falls die Datei manuell
-gelöscht wurde (Bug wurde in Runde r5 gefunden und behoben).
+Geschützt sind sie durch Dateiberechtigungen (`icacls`, beschränkt auf SYSTEM,
+Administratoren und das Dienstkonto, bei jedem Start neu gesetzt), durch die
+Zugriffsgrenze und dadurch, dass sie in keiner aus dem Netzwerk lesbaren Schnittstelle
+auftauchen. Eine Verschlüsselung wäre unter Windows nur über die Datenschutz-Schnittstelle
+des Betriebssystems sinnvoll; die ist aus der Laufzeitumgebung nicht ohne Zusatzpaket
+erreichbar.
-Vor jedem Ersetzen einer Datei wird über `sichern_und_ersetzen()` eine
-Sicherheitskopie in einen `_sicherungen`-Unterordner gelegt.
+---
-## 6. "Rechner einrichten" – Verteilungs-Mechanismus
+## 3. Zugriffsgrenze
-Route `/einrichten` (app.py) erzeugt bei Bedarf ein individuelles
-Installations-Paket (ZIP mit `einrichten.ps1` + `Rechner-einrichten-starten.bat`
-**+ den echten Installer-Dateien/ZIPs selbst** unter `tools/<id>.<endung>`),
-statt vorab für jede Mitarbeiter/Tool-Kombination ein festes Skript zu pflegen.
+Kein Anmeldeverfahren, sondern eine Grenze an der Netzwerkschnittstelle: Eine Middleware
+prüft, ob die Anfrage von `localhost` kommt. Verwaltende Routen tragen sie, lesende nicht.
-**Wichtige Design-Entscheidung (Runde r16):** Die Installer-Dateien werden
-bereits beim Erzeugen des Pakets (auf dem PC, der die Checkliste ausfüllt)
-eingebettet, nicht erst beim Ausführen auf dem Zielrechner nachgeladen. Ein
-früherer Ansatz lud die Dateien per `Invoke-WebRequest` zur Laufzeit vom
-Server nach – das erforderte Netzwerkzugriff des Zielrechners auf den Server
-in genau dem Moment der Einrichtung, was in der Praxis nicht immer gegeben
-ist (z. B. Rechner noch nicht ans Firmennetz angebunden). Jetzt braucht der
-Zielrechner für die eigentliche Installation **kein Netzwerk mehr** – nur die
-abschließende, optionale Meldung ans zentrale Protokoll (`/zuweisungen/neu`)
-versucht noch einen Netzwerkzugriff, scheitert dieser, bleibt trotzdem das
-lokale Protokoll (`protokoll.log`) vollständig.
+- Frei erreichbar: Katalog lesen, einzeln oder als ZIP herunterladen, Umgebung abfragen
+- Nur lokal: Anlegen, Ändern, Löschen, Abgleich anstoßen, Datei hinterlegen, Geheimnisse,
+ Export/Import, Einstellungen, Zugriffsprotokoll
-Ablauf beim Ausführen auf dem Zielrechner:
-1. `.bat` kopiert den **kompletten entpackten Ordner** (inkl. `tools/`-
- Unterordner mit den echten Installer-Dateien) nach `C:\ClientBibliothek`
- (per `xcopy /E /I /Y`)
-2. Self-Elevation (Admin-Rechte-Anfrage) über `Start-Process -Verb RunAs`
-3. Automatische Windows-Defender-Ausnahme für `C:\ClientBibliothek`
- (`Add-MpPreference -ExclusionPath`) – ein einziger Pfad statt vieler
- Einzelausnahmen
-4. Pro ausgewähltem Tool: entweder stille Installation aus der **lokal
- mitgelieferten** Datei unter `tools/<id>.<endung>` (mit hinterlegtem
- Silent-Parameter, `.msi` läuft über `msiexec`) oder – bei Tools ohne
- einzelne Installer-Datei ("Ordner-Typ", z. B. portable Programme wie
- Blender) – die mitgelieferte ZIP-Datei wird nach `C:\ClientBibliothek\pakete\`
- kopiert, **bewusst ohne automatisches Entpacken** (Entscheidung in Runde
- r14: der Zielort für portable Tools variiert, das soll eine Person bewusst
- entscheiden)
-5. Lokales Protokoll unter `C:\ClientBibliothek\protokoll.log`
-6. Optionaler POST an `/zuweisungen/neu` auf dem Server, um das zentrale
- Protokoll zu befüllen (Rechnername wird dabei über `$env:COMPUTERNAME`
- automatisch erkannt) – best effort, kein Abbruch bei Fehlschlag
+Die Oberfläche fragt beim Start `/api/umgebung` und blendet aus, was ohnehin abgewiesen
+würde. Das ist Bequemlichkeit, keine Absicherung — die liegt beim Server.
-Die Ordner-Struktur unter `C:\ClientBibliothek`:
-- `\tools` – die mitgelieferten Installer-Dateien/ZIPs (Quelle für die
- Installation, kommen direkt aus dem heruntergeladenen Paket)
-- `\pakete` – ZIPs von portablen Tools, bleiben bewusst liegen
-- `\protokoll.log` – lokale Mitschrift
+Das passt zum Auftrag („Vollzugriff nur am Server") und kommt ohne Benutzerverwaltung aus.
+Der Preis: Wer am Server sitzt, darf alles. Rollen und Rechte sind im Ticket bewusst
+zurückgestellt.
-Serverseitig teilen sich `/einrichten` (POST) und `/software/<id>/download`
-die Hilfsfunktion `_zippe_ordner_in_bytes()` für das ZIP-Packen eines
-Ordners, um Code-Duplikation zu vermeiden.
+---
-**Der Client-Ordner-Pfad ist eine Konstante** (`CLIENT_ORDNER` in `app.py`,
-aktuell `C:\ClientBibliothek`) – zentral anpassbar, falls die Infrastruktur
-einen anderen Standardpfad vorgibt.
+## 4. Oberfläche
-## 6a. Zugriffsbeschränkung: lokal (Server) vs. Netzwerk
+Aufgebaut aus `@dh-software/dh-components`, damit das Werkzeug aussieht wie der Rest des
+Hauses. Zwei Besonderheiten:
-Seit Runde r17 unterscheidet die Anwendung, ob eine Anfrage direkt vom Server
-selbst kommt (z. B. per RDP-Sitzung, im Browser dort `localhost`/`127.0.0.1`
-aufgerufen) oder über das Netzwerk von einem anderen Rechner.
+**Light DOM statt Shadow DOM.** Die Komponenten werden über `createRenderRoot()` ins Light
+DOM gerendert, sonst greifen die globalen `::part()`-Regeln des Themes nicht.
-- `ist_lokaler_zugriff()` prüft `request.remote_addr` gegen `127.0.0.1`/`::1`
-- `@app.before_request`-Hook `_zugriff_beschraenken()` blockiert für
- Netzwerk-Zugriffe alle Routen außer denen in `OEFFENTLICHE_ENDPUNKTE`
- (aktuell nur `einrichten` und `static`) mit einer 403-Seite
-- Die Seite `/einrichten` zeigt sich bei Netzwerkzugriff unter anderem Namen
- ("Software anfordern" statt "Rechner einrichten") und mit Hinweis auf die
- Protokollierung
-- Jeder Aufruf/Download über `/einrichten` wird in der Tabelle `zugriffslog`
- festgehalten (Zeitpunkt, IP-Adresse, per Reverse-DNS ermittelter
- Rechnername falls auflösbar, selbst angegebener Name, Aktion) – einsehbar
- unter `/zugriffslog` (selbst wieder nur lokal erreichbar)
+**Themewechsel zur Laufzeit.** Zwei `<style>`-Elemente mit den Farbdefinitionen für hell
+und dunkel, umgeschaltet über `.disabled`. Drei Modi: System, hell, dunkel. Nach dem
+Vorbild des Meeting-Transkriptors, damit sich die Werkzeuge gleich anfühlen.
-**Wichtige Einschränkung:** Ein echter Windows-Benutzername lässt sich über
-reines HTTP **nicht** zuverlässig ermitteln – dafür wäre eine
-NTLM/Kerberos-Integration nötig (z. B. über einen IIS-Reverse-Proxy vor
-Flask, der die Windows-Anmeldung übernimmt und den Benutzernamen per Header
-an die Flask-App durchreicht). Das ist in diesem Prototyp **nicht**
-umgesetzt. Aktuell basiert die Identifizierung auf der IP-Adresse, einem per
-Reverse-DNS aufgelösten Rechnernamen (funktioniert nur, wenn im
-Firmennetzwerk DNS-Einträge für Client-Rechner existieren) und dem selbst im
-Formular angegebenen Namen (nicht verifiziert). Für eine belastbarere
-Identifizierung müsste diese IIS/NTLM-Integration nachgerüstet werden.
+Der Katalog ist eine kompakte Liste statt Karten — er soll dreistellige Zahlen an
+Einträgen aushalten. Reiter trennen lizenzfrei, lizenzpflichtig und intern; die
+Mehrfachauswahl wirkt über Reitergrenzen hinweg und lädt als ZIP.
-**Anpassung an andere Infrastruktur:** Sollte der Server z. B. hinter einem
-Reverse-Proxy laufen, liefert `request.remote_addr` ggf. die Proxy-IP statt
-der echten Client-IP – in dem Fall müssten `X-Forwarded-For`-Header
-ausgewertet werden (aktuell nicht implementiert, da kein Proxy im Einsatz).
+Zusatzinhalte (Sprachpakete, Erweiterungen) hängen über `elternId` an ihrem Werkzeug und
+stehen eingerückt darunter, ohne eigene Trennlinie — Werkzeug und Zubehör lesen sich als
+ein Block.
-## 7. Bekannte Grenzen / nicht vollständig getestet
+---
-- **Keine echte Authentifizierung, nur Herkunfts-Beschränkung.** Seit r17
- ist die Verwaltung (Katalog, Protokolle, Zugriffsprotokoll) nur noch vom
- Server selbst aus erreichbar (siehe Abschnitt 6a) – das ist aber eine
- Netzwerk-/IP-basierte Einschränkung, **kein Login mit Benutzername/Passwort**.
- Wer direkt am Server sitzt (oder sich als diesen ausgibt, z. B. über
- IP-Spoofing im selben Netzwerksegment), hat vollen Zugriff. Für ein
- belastbares Sicherheitskonzept mit echten Benutzerkonten wäre ein
- richtiges Login-System nötig.
-- **Kein HTTPS/TLS.** Die Kommunikation läuft unverschlüsselt über HTTP. Für
- ein internes, abgeschottetes Netzwerk meist unkritisch, sollte aber vor
- einem größeren Rollout bewertet werden.
-- **Flask-Entwicklungsserver**, kein WSGI-Produktivserver (z. B. Waitress,
- gunicorn). Für kleine interne Nutzung ausreichend, bei mehr gleichzeitigen
- Nutzern ggf. wechseln.
-- **PowerShell-Teile des Verteilungs-Skripts** (Self-Elevation, `msiexec`,
- `Add-MpPreference`) konnten in der Entwicklungsumgebung (Linux-Sandbox)
- nicht Ende-zu-Ende ausgeführt werden, nur mit dem echten PowerShell-Parser
- auf Syntaxfehler geprüft, plus die reinen Netzwerk-Teile
- (Invoke-WebRequest/Invoke-RestMethod, Expand-Archive) wurden live gegen den
- echten Server getestet. Vor größerem Rollout: an 1–2 unkritischen Rechnern
- gegentesten.
-- **winget-Pfad der automatischen Aktualisierung** konnte aus demselben
- Grund nicht direkt ausgeführt werden (winget existiert nur unter Windows).
- Der GitHub- und Direkt-Link-Pfad wurden dagegen live gegen echte
- Schnittstellen getestet.
-- **Keine mehrsprachige Oberfläche** – komplett auf Deutsch hartkodiert.
+## 5. Auslieferung als eine Datei
-## 8. Konfigurationspunkte für die Anpassung
+`npm run build:exe` in vier Schritten:
-| Was | Wo |
-|---|---|
-| Client-Ordner auf Zielrechnern | `CLIENT_ORDNER` in `app.py` |
-| Port des Webservers | `app.run(..., port=5000)` am Ende von `app.py`, sowie in den `.bat`-Dateien/Firewall-Skript |
-| Schwelle "Bitte prüfen"-Warnung | `TAGE_BIS_WARNUNG` in `app.py` (aktuell 30 Tage) |
-| Erlaubte Bildformate für Doku-Upload | `ERLAUBTE_BILD_ENDUNGEN` in `app.py` |
-| Max. Uploadgröße | `app.config["MAX_CONTENT_LENGTH"]` in `app.py` |
-| Versionsmarker (Diagnose, ob neue Version läuft) | `APP_VERSION` in `app.py` |
+1. TypeScript übersetzen
+2. Server mit esbuild zu einer CommonJS-Datei bündeln
+3. Die Oberfläche als Einzelressourcen in die SEA-Konfiguration eintragen
+4. Node-Programmdatei kopieren und den Blob per `postject` hineinschreiben
+
+**Warum Einzelressourcen und nicht ein eingebettetes Bündel:** Ein früher Versuch hat das
+JavaScript-Bündel als Zeichenkette in die Server-Datei eingebettet. Die eingebettete
+Schriftart enthält Bytefolgen, die als UTF-8 ungültig sind — sie wurden zu U+FFFD und das
+Bündel damit unbrauchbar. Die Seite blieb weiß, ohne Fehlermeldung. Aufgefallen ist es
+erst beim Vergleich der ausgelieferten Bytes mit der Datei auf der Platte. Über
+`sea.getRawAsset()` kommen die Ressourcen unverändert an.
+
+**Warum esbuild und nicht webpack:** webpack scheitert an einer Abhängigkeit in der
+Server-Kette, deren `tsconfig.json` per `extends` auf ein nicht aufgelöstes Paket zeigt.
+esbuild kümmert das nicht.
+
+Der ZIP-Erzeuger für Mehrfach-Downloads ist von Hand geschrieben (nur speichernd, ohne
+Kompression — Installer sind ohnehin komprimiert). Das spart eine Abhängigkeit in einem
+Bündel, das ohne Fremdcode auskommen soll.
+
+---
+
+## 6. Datenmodell
+
+Eine Tabelle `software` und eine Tabelle für das Zugriffsprotokoll. Neue Spalten kommen
+über eine Liste hinzu, die beim Start mit dem tatsächlichen Schema abgeglichen wird —
+fehlende werden ergänzt. Damit übersteht eine bestehende Installation ein Update ohne
+Handarbeit.
+
+Ordnernamen im Datenverzeichnis haben denselben Weg: Eine Umbenennungsliste beim Start
+zieht ältere Installationen nach.
+
+Zur Version: Angezeigt wird das **Veröffentlichungsdatum des Anbieters**, nicht der
+Abrufzeitpunkt. Nur wenn eine Quelle keins liefert, wird ersatzweise der Abrufzeitpunkt
+genannt und als solcher benannt — die Frage „ist das aktuell?" beantwortet das Datum des
+Anbieters, unser Abrufzeitpunkt sagt darüber nichts.
+
+---
+
+## 7. Grenzen
+
+- **Kein HTTPS.** Damit fehlt dem Browser der sichere Kontext, und die Schnittstellen für
+ Dateizugriff (`showDirectoryPicker`, `crypto.subtle`) stehen an der Netzwerkadresse nicht
+ zur Verfügung. Nachgemessen: an `localhost` vorhanden, an der IP nicht.
+- **Kein Dienstbetrieb.** Die Programmdatei muss von Hand gestartet werden.
+- **Keine automatisierten Tests.** Geprüft wurde bisher von Hand gegen die echten Quellen.
+- **Keine Rollen.** Wer am Server sitzt, darf alles.
+- **Ein hinterlegtes Geheimnis lässt sich über die Oberfläche nicht einzeln löschen** —
+ ein leeres Feld bedeutet „unverändert lassen". Nur über die Schnittstelle mit einer
+ Liste, die den Namen weglässt.
+- **Bauvorgänge laufen mit den Rechten des Dienstkontos.** Wer einen Bau-Befehl hinterlegen
+ kann, kann Code auf dem Server ausführen. Das setzt Vertrauen in die angebundenen
+ Repositories voraus.
--
Gitblit v1.9.3