protokoll-ki-updates-2026-10-01
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Projektübersicht — README
Übersicht
Dieses Projekt ist eine lokale Weboberfläche zur Verwaltung von TGA-Projekten (Elektro, HLS, Architektur) über wöchentliche Sitzungen. Die Anwendung besteht aus:
- einem Frontend mit
Test.htmlals der einen Anwendungsseite — sie enthält alle Bereiche einschließlich des Projektbereichs eines einzelnen Projekts (Terminplan/GANTT, Aufgaben, Dateien, Einstellungen) — dazustyles.css,plan-styles.cssund den JS-Modulen unterpublic/js/ - einem einfachen Node.js-/Express-Server in
server.js - einem Datenspeicher in einer SQLite-Datenbank (
node:sqlite, benötigt Node ≥ 22.13) untersessions/projektuebersicht.db - einem Docker-Setup (
Docker/Dockerfile+docker-compose.ymlim Projektstamm)
Datei- und Komponentenübersicht
Wichtige Laufzeit-Dateien
-
server.js- Node.js/Express-Server
- dient als API für das Frontend
- stellt statische Dateien (
Test.html, die Module unterjs/,styles.css,plan-styles.css, etc.) bereit; statische Assets werden mitCache-Control: no-storeausgeliefert - implementiert Endpunkte:
GET /→ liefertTest.htmlGET /load-all?department=…→ lädt alle Sitzungen plus globale Personenliste einer AbteilungPOST /save-week {department, week, baseUpdatedAt}→ speichert/aktualisiert eine Sitzung; mitbaseUpdatedAt(die vom Client gehaltene Revision =updatedAtausload-all) erkennt der Server veraltete Speicherungen und antwortet dann mit HTTP 409{ok:false, conflict:true, …}; auch gelöschte Sitzungen werden mit nicht-leerer Basis nicht wiederbelebt (409). Erfolgreiche Saves antworten{ok:true, updatedAt}(neue Revision)POST /save-people {department, people}→ ersetzt die globale Personenliste einer AbteilungPOST /delete-week {department, label}→ löscht eine SitzungGET /health→ Gesundheitscheck ({ok:true})- Deep Links je Abteilung + Tab, z. B.
GET /HLS/AuslastungoderGET /ELT/Projektübersicht→ liefertTest.html; gültige Tab-Slugs sindProjektübersicht | Auslastung | Projekte | Projektbeteiligte, alles andere ergibt 404 GET /Projektplan(/<ProjName>)?→ liefert ebenfallsTest.html; der Projektbereich ist seit dem Umbau kein eigenes Dokument mehr, sondern ein Bereich dieser Seite, denapplyUrlRoute()(public/js/ui.js) aus der Adresse auswählt. Die Identifikation erfolgt über die Query-ParameterweekLabelundprojNr, der Projektname ist nur ein lesbares Pfad-Segment. Alte Links auf/project-plan.htmlwerden per 301 auf dieses Schema umgeleitet
- validiert Abteilungen gegen
ELT | HLS | ARCH(unbekannte fallen stillschweigend aufELTzurück) - legt das
sessions-Verzeichnis beim Start an, falls es nicht existiert - JSON-Body-Limit von 2 MB
- beendet sich mit aussagekräftiger Fehlermeldung, wenn der Port belegt ist
-
package.json- definiert den Node.js-Startbefehl
npm start - enthält Produktionsabhängigkeiten
expressundcors
- definiert den Node.js-Startbefehl
-
package-lock.json- wird für reproduzierbare Docker-Builds verwendet (
npm ci)
- wird für reproduzierbare Docker-Builds verwendet (
-
public/js/(Module)- Die Client-Logik ist in kleine klassische Script-Dateien aufgeteilt (alle Funktionen global auf
window, kein Bundler):- gemeinsame Helfer:
globals.js,utils.js,ui.js,date-picker.js,server.js,store.js,weeks.js,gantt.js,showmode.js,tabs-auth.js,pdf.js,init.js - tab-spezifisch in eigenen Ordnern:
board/board.js(Projektboard),people/people.js(Projektbeteiligte),projects/projects.js(Projekte),load/load.js(Auslastung) - Planungs-Module unter
js/plan/(plan-state,plan-build-gantt,plan-gantt,plan-drag,plan-modals,plan-schedule)
- gemeinsame Helfer:
- rendert das Projektboard, die Projekt- und Personenverwaltung sowie die Auslastungsansicht
- lädt Daten über den Server und speichert Änderungen als JSON-Objekte in die SQLite-Datenbank zurück
- verwendet
currentDepartmentzur Trennung von Abteilungen (ELT,HLS,ARCH) - speichert:
- alle Sitzungen und die globale Personenliste pro Abteilung in
sessions/projektuebersicht.db
- alle Sitzungen und die globale Personenliste pro Abteilung in
- sortiert Sitzungen nach Kalenderwoche, nicht alphabetisch
- sperrt historische Sitzungen automatisch (bis auf die chronologisch letzte)
- Personen sind global pro Abteilung verwaltet; historische Sitzungen behalten ihre lokalen Personenkopien
- speichert keine Daten in
localStorage(dort liegt nur UI-Zustand wie Abteilung, Theme und Header-Collapse)
- Die Client-Logik ist in kleine klassische Script-Dateien aufgeteilt (alle Funktionen global auf
-
Test.html- die Haupt-Benutzeroberfläche der Anwendung (Tabs: Projektboard, Auslastung, Projekte, Projektbeteiligte)
- lädt
styles.cssund die JS-Module ausjs/in fester Reihenfolge (sieheAGENTS.md) - enthält die Abteilungsauswahl, Sitzungsnavigation, Tab-Navigation, das Passwort-Modal sowie PDF-Export
- lädt CryptoJS (Passwortprüfung) und pdfmake (PDF-Export) von
cdnjs.cloudflare.com— diese Funktionen benötigen daher Internetzugang
-
Projektbereich (Bereich
#planinTest.html, Module unterjs/plan/)- der eigene Bereich EINES Projekts mit Dashboard, Terminplan (GANTT/TODOs/Meilensteine), Aufgaben, Dateien und Einstellungen
- wird über
openProjectPlan()(js/gantt.js) geöffnet — ein gewöhnlicher Bereichswechsel übershowTab('plan'), genau wie vom Dashboard ins Projektboard; Menüband und Seitenleiste bleiben stehen - war bis zum Umbau ein eigenes Dokument
project-plan.htmlmit nachgebauter, abgespeckter Seitenleiste; die Datei ist entfallen, ihre Stile liegen jetzt inplan-styles.css
-
plan-styles.css- die Stile des Projektbereichs (früher ein
<style>-Block inproject-plan.html)
- die Stile des Projektbereichs (früher ein
-
Bereich „Einstellungen" (
js/settings/settings.js)- gehört dem angemeldeten Konto selbst: Stammdaten zur Ansicht und der Passwortwechsel (
POST /change-password). Der Dialog erscheint auch erzwungen direkt nach der Anmeldung, solange das Kontomust_change_passwordträgt
- gehört dem angemeldeten Konto selbst: Stammdaten zur Ansicht und der Passwortwechsel (
-
Bereich „Admin" (
js/admin/admin.js, nur für Administratoren)- gegliedert in einzelne Abschnitte, zwischen denen die aufklappbare Gruppe „Admin" in der Seitenleiste umschaltet; sichtbar ist immer genau einer, der gewählte steht im URL-Fragment (z. B.
/Admin#auslastung) und ist damit verlinkbar - Abschnitte: „Benutzerverwaltung" (Konten, Rollen, Wochenstunden, Abwesenheiten — bis zum Umbau die eigene Seite
admin-users.html, alte Links werden per 301 umgeleitet), „Honorar" (Stundensatz, Anteile je Leistungsphase), „Auslastung" (Wochenstunden einer vollen Stelle, Ampel-Schwellen, Balkenskala, Aufwand je Leistungsphase und Rolle), „Termine & Sitzungen" (Arbeitsstunden je Werktag, automatische Sperre), „Feiertage" (die gesetzlichen Feiertage eines Jahres werden berechnet — feste Termine plus alles, was am Osterdatum hängt — und auf Knopfdruck übernommen; kein Netzdienst, kein Internetzugang nötig), „Aufgaben-Vorlagen", „Betrieb & Sicherheit" (aufbewahrte Backups, Gültigkeit einer Anmeldung, Anmeldebremse) und „Datenbank & Sitzungen" (DB Backup, DB Import, Sitzung löschen) - all diese Werte waren vorher Konstanten im Quelltext; gelesen werden sie über
GET /app-settings(jedes Konto — das Frontend rechnet damit), geschrieben überPUT /app-settings(nur Administratoren, serverseitig überrequireRole('admin')abgesichert) - die vertraglichen Wochenstunden je Person stehen am Benutzerkonto (Benutzerverwaltung) und sind die Bezugsgröße der Auslastungs-Ampel: Eine halbe Stelle gilt schon bei halber Last als voll ausgelastet
- gegliedert in einzelne Abschnitte, zwischen denen die aufklappbare Gruppe „Admin" in der Seitenleiste umschaltet; sichtbar ist immer genau einer, der gewählte steht im URL-Fragment (z. B.
-
styles.css- Layout und Styling der Oberfläche
Docker-bezogene Dateien
-
Docker/Dockerfile- Basis-Image:
node:22-alpine(nötig, danode:sqliteerst ab Node 22.13 ohne Flag stabil verfügbar ist) - kopiert
package.jsonundpackage-lock.json, installiert Produktionsabhängigkeiten mitnpm ci - kopiert
server.jsundpublic/nach/app - legt sicherheitshalber
/app/sessionsan - läuft als nicht-root-User
node - enthält einen
HEALTHCHECKauf/health - startet
node server.js
- Basis-Image:
-
docker-compose.yml(im Projektstamm)- definiert den Service
projektuebersicht - baut das Image mit Build-Kontext
.undDocker/Dockerfile - mappt Port
3003:3003 - bindet das Host-Verzeichnis
./sessionsin den Container nach/app/sessions - setzt
restart: unless-stopped - definiert einen
healthcheck
- definiert den Service
-
.dockerignore- schließt unnötige Dateien aus dem Docker-Build aus:
node_modules/,npm-debug.log,.git,.gitignore,Docker/*(außerDocker/Dockerfile)
- schließt unnötige Dateien aus dem Docker-Build aus:
Sonstige Dateien
-
dein_bild.ico- optionales Icon, wird vom Server statisch ausgeliefert, wenn angefragt
-
AGENTS.md- Projektnotizen / Dokumentation, aber nicht Teil des Anwendungslaufs
Persistenz und Datenfluss
Speicherung
- Alle persistierten Daten werden in einer SQLite-Datenbank (
node:sqlite,DatabaseSync, WAL-Modus,PRAGMA foreign_keys = ON) untersessions/projektuebersicht.dbgespeichert. - Das Schema ist vollständig relational (19 Tabellen):
weeks,week_people(+week_people_roles),attendance,projects,project_lph,project_assign,project_aufwand,project_aktuelle_lp,project_plan,plan_people,plan_todos(+plan_todo_assignees,plan_todo_deps),plan_milestones(+plan_milestone_deps),people(+person_roles,person_absences). Projekt-/Personen-IDs bleiben dadurch über Neustarts stabil. - Der Datenbankpfad ist über die Umgebungsvariable
DB_PATHübersteuerbar (für Tests/Backups), Standard istsessions/projektuebersicht.db.
Server-API
- Der Server wird in
server.jserstellt und startet auf Port3003oder dem Wert vonprocess.env.PORT. - Das Frontend ruft den Server über
window.location.originan (Fallbackhttp://localhost:3003beifile://-Öffnung).
Wo werden die Daten gespeichert?
- Das Frontend schreibt über den Server in die SQLite-Datenbank.
- In
js/store.jsspeichert die Anwendung nur geänderte Sitzungen (Dirty-Tracking übermarkWeekDirty()/markPeopleDirty()) via/save-week(Sitzungen) bzw./save-people(globale Personen); ein In-Flight-Guard verhindert Doppel-Speicherungen. Jede Sitzung führt einen Revisions-Zeitstempel (updated_at, übertragen alsupdatedAt); speichert ein Client mit veralteter Revision (baseUpdatedAt), antwortet der Server mit HTTP 409 und der Client lädt nur diese eine Sitzung neu (andere ungespeicherte Änderungen bleiben erhalten). - Der Server zerlegt die JSON-Objekte in die relationalen Tabellen (
serializeWeek()/loadWeeks()) in einer Transaktion pro Sitzung. Beim Start migriertmigrateLegacySchema()idempotent ein altes Single-JSON-Dokument-Schema in das relationale Schema; anschließend ergänztmigrateWeeksUpdatedAt()dieupdated_at-Spalte derweeks-Tabelle (idempotent).PRAGMA busy_timeout = 5000verhindert Fehler bei parallelen Schreibzugriffen.
Betrieb
Lokal mit Node.js (Node ≥ 22.13)
npm install
npm start
Danach ist die Anwendung erreichbar unter http://localhost:3003.
Falls der Port belegt ist:
PORT=3004 npm start
Mit Docker
Im Projektstamm ausführen:
docker compose up --build
Danach ist die Anwendung erreichbar unter:
http://localhost:3003
Wichtige Hinweise
- Der Server benötigt Lese-/Schreibzugriff auf
sessions/(für die SQLite-Datenbank und das Docker-Bind-Mount). - Die Docker-Compose-Konfiguration liegt im Projektstamm und mountet
./sessionsin den Container. Achte darauf, dass der relative Pfad im Kontext korrekt ist, wenn dudocker composeaus einem anderen Verzeichnis startest. js/store.jsundjs/server.jsarbeiten mit Abteilungsparametern und speichern Daten abteilungsweise getrennt.- Die Passwortfunktion (Tabs „Projekte“/„Projektbeteiligte“, Sitzungslöschung, Entsperren) ist nur ein einfacher Client-Mechanismus im Frontend (SHA256-Vergleich) und kein echter Sicherheitsmechanismus.
- Globale Personen werden pro Abteilung in der Datenbank verwaltet. Wird eine Person global gelöscht, bleibt sie in bereits existierenden Sitzungen sichtbar (Wochen-Personenkopien).
- CryptoJS und pdfmake werden von
cdnjs.cloudflare.comgeladen — PDF-Export und Passwortprüfung funktionieren ohne Internet nicht.
Languages
JavaScript
76.5%
CSS
17.1%
HTML
5.5%
Shell
0.8%