| | |
| | | # 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. |