Schicht Tool Runtime Node.js 22 HTTP Express 5 Browser Playwright 1.59 (Chromium) DB SQLite — nativ via node:sqlite Push web-pushV2-Frontend SvelteKit 2 + Svelte 5 (adapter-static) V1-Frontend Vanilla-JS-PWA unter /mobile/ Tests node:test
│ ├── server.js Express-Composition + Boot
│ ├── auth.js Bearer-Token + Anti-Brute-Force
│ ├── ratelimits.js express-rate-limit-Instanzen
│ ├── scheduler.js Intervall- / Wochenplan-Logik
│ ├── runScrape.js Scrape-Cycle-Orchestrierung
│ ├── sse.js Server-Sent-Events Broadcast
│ ├── pushValidate.js SSRF-Allowlist für Push-Endpoints
│ ├── secretCrypto.js AES-256-GCM für settings.json-Secrets
│ ├── state.js Geteilter Mutable-State
│ ├── settings.js Settings-Persistenz (mit Encryption)
│ ├── scraper.js Playwright Login + Scraping
│ ├── push.js Web-Push (VAPID, FCM/Mozilla/Apple)
│ ├── routes/ 11 Express-Route-Module
│ ├── db/ SQLite-Layer (9 Module)
│ ├── bot/ Telegram-Bot (8 Module)
│ └── shared/ envLoader, escapeHtml, apiError
├── test/unit/ Unit-Tests
├── web-svelte/ V2-Frontend
├── dist/ Build-Output (gitignored)
│ ├── floorplans/ Geteilte RaumView-Helper
│ └── assets/ Logo, Icons
├── data/ Runtime (Docker-Volume)
Modul Aufgabe index.jsRouter-Aggregation status.jsGET /api/statussettings.jsGET/PATCH /api/settingsnoten.jsGET /api/noten, /:id/pruefungen, /api/history/:idstundenplan.jsGET /api/stundenplan, POST .../clearstats.jsGET /api/statsscrape.jsPOST /api/scrapepush.jsVAPID, subscribe, test logs.jsGET /api/logsevents.jsSSE GET /api/events static.jsAsset-Serving (/, /mobile/, Floorplans)
Modul Aufgabe index.jsConnection-Singleton, Migrations, Bootstrap schema.jsCREATE TABLE-Statementsqueries.jsGenerische Helper parsers.jsDOM → Domain-Objekt noten.jsNoten-CRUD + History-Append stundenplan.jsTermin-CRUD + Raumwechsel-Detection pruefungen.jsLB/ZP/OTHER-CRUD + History stats.jsAggregat-Queries push.jsSubscriptions
Modul Aufgabe index.jsLong-Polling, Routing state.jsPer-User-State telegram.jsTelegram-API-Wrapper format.jsNoten- / Termin-Formatter keyboards.jsInline-Keyboards screens.jsAntwort-Templates handlers.jsCommand-Handler notify.jsPush-Bridge (Web-Push → Telegram)
Tabelle Inhalt notenModul-Stammdaten + aktuelle Note + Frisch-Marker noten_historyAppend-only Verlauf jeder Modulnoten-Änderung noten_pruefungenLB / ZP / OTHER pro Modul mit Gewicht pruefungen_historyAppend-only Verlauf jeder ZP/LB-Bewertungs-Änderung stundenplanTermine mit Datum, Zeit, Raum, Dozent + Raumwechsel-Marker push_subscriptionsPWA-Push-Subscriptions (endpoint + Krypto-Keys)
DB-Connection ist seit v1.0.0 ein Boot-Singleton — Migrationen + reclassifyOtherPruefungen laufen einmal beim Start, alle Routen / Bot-Screens nutzen den geteilten Handle.
1. envLoader → .env + ENV merge
2. settings.load() → data/settings.json (mit Decryption)
3. db.init() → Connection + Migrations + Reclassify
4. push.init() → VAPID-Keys laden / generieren
5. server start → Express + Static
6. scheduler.start() → Intervall/Wochenplan
7. bot.start() (opt) → Telegram-Long-Polling
Vanilla-PWA (/mobile/) ist langlebig — kein Build, läuft unverändert über Jahre, schlanker Service-Worker
SvelteKit-SPA (/) für die Desktop-Erfahrung mit komplexeren Interaktionen (Filter, Tabellen, Charts)
Beide werden vom selben Express-Server gehostet — dist/ für SvelteKit, web/mobile/ für Vanilla.
Native Node-API seit v22.5
Keine native Build-Dependency mehr → schlankerer Container
Performance ausreichend für unsere Größenordnung
Vor v1.0.0 öffnete jede Route eine neue Connection → Migrations liefen mehrfach
Singleton + reclassifyOtherPruefungen einmal beim Start → robust, performant
Pragmatik: das At-Rest-Encryption schützt gegen versehentliche Leaks (Backup, Snapshot, Sharing). Gegen Shell-Access auf den laufenden Host hilft es nicht — dafür gibt’s Backup-Encryption.