246 lines
12 KiB
Markdown
246 lines
12 KiB
Markdown
# Änderungen
|
||
|
||
Alle nennenswerten Änderungen an diesem Projekt stehen hier.
|
||
Das Format folgt [Keep a Changelog](https://keepachangelog.com/de/1.1.0/),
|
||
die Versionierung [Semantic Versioning](https://semver.org/lang/de/).
|
||
|
||
## [Unveröffentlicht]
|
||
|
||
### Hinzugefügt
|
||
|
||
- **Schnellerfassung vom Handy** unter _Erfassen_: ein Feld, ein Knopf. Was
|
||
ohne Netz erfasst wird, bleibt im Gerät und geht hinaus, sobald wieder eine
|
||
Verbindung da ist — mit einer Kennung je Erfassung, damit eine doppelt
|
||
zugestellte Nachricht keine zweite Aufgabe ergibt.
|
||
- Der Planer lässt sich als **App auf dem Startbildschirm ablegen**
|
||
(Manifest, eigenes Symbol, eigener Startpunkt).
|
||
- **Öffentliche Schnittstelle** unter `/api/v1`: Aufgaben lesen, anlegen,
|
||
ändern und abhaken. Beschreibung in [docs/API.md](docs/API.md).
|
||
- **Tokens je Anwendung** mit Rechten je Token, wahlweise mit Ablauf,
|
||
verwaltbar unter _API_.
|
||
- Handbuchseite zur Schnellerfassung.
|
||
|
||
### Sicherheit
|
||
|
||
- Von einem Token liegt nur der Fingerabdruck in der Datenbank. Der Wert
|
||
erscheint genau einmal — beim Anlegen.
|
||
- Die drei Rechte schließen einander nicht ein: Ein Dienst, der nur erfasst,
|
||
kann den Bestand nicht lesen.
|
||
- Jeder Fehlschlag beim Ausweisen sieht gleich aus. Ob ein Token unbekannt,
|
||
abgelaufen oder zurückgezogen ist, verrät die Schnittstelle nicht.
|
||
- Jede schreibende Nutzung steht im Protokoll, mitsamt dem Token — auch jeder
|
||
abgewiesene Zugriff.
|
||
- Die Schnittstelle kann weder löschen noch Bereiche, Projekte, Zeitblöcke
|
||
oder Kalender anfassen.
|
||
|
||
## [0.6.0] – 2026-09-12
|
||
|
||
### Hinzugefügt
|
||
|
||
- **Zammad** (nur `firma`): Tickets, auf die der Suchausdruck passt, werden zu
|
||
Aufgaben — mit Verweis ins Ticket. Wird ein Ticket dort geschlossen, hakt
|
||
der Planer die Aufgabe ab.
|
||
- **Telegram** (nur `daheim`): Eine Nachricht an den Bot wird zur Aufgabe.
|
||
Meldungen gehen denselben Weg zurück, mit einem Knopf zum Abhaken. Ein Chat
|
||
meldet sich über einen Einmalcode an, der zehn Minuten gilt.
|
||
- **Telegram als dritter Meldeweg** neben Push und E-Mail.
|
||
- **Spiegel** zwischen den Instanzen: `firma` schickt Belegungen ohne Titel
|
||
und die einzeln dafür markierten Aufgaben, `daheim` empfängt. Gespiegelte
|
||
Belegung erscheint in der Wochenansicht als „Dienstlich belegt" und zählt
|
||
gegen die Tageskapazität.
|
||
- Seite **Integrationen**: je Integration Zustand, letzter Erfolg, letzter
|
||
Fehler und ein Knopf, der einen Lauf sofort anstößt.
|
||
- Handbuchseite zu den Integrationen.
|
||
|
||
### Geändert
|
||
|
||
- Was aus einem Fund wird, entscheidet für alle Integrationen dieselbe
|
||
Stelle. Geführt werden nur Titel, Notiz, Verweis und Fälligkeit — Bereich,
|
||
Projekt, Priorität und geschätzter Aufwand gehören der Planung und werden
|
||
nie von außen überschrieben.
|
||
- Eine klemmende Integration meldet sich wie ein klemmender Kalender: erst
|
||
nach dem dritten Fehlversuch in Folge.
|
||
- Die Demo-Instanz für die Bilder spricht nicht mehr nach außen.
|
||
|
||
### Sicherheit
|
||
|
||
- Wer dem Telegram-Bot schreibt, ohne verbunden zu sein, bekommt **keine
|
||
Antwort** — auch keine Fehlermeldung. Sonst ließe sich herausfinden, dass
|
||
es diesen Planer gibt.
|
||
- Jede Sendung des Spiegels trägt eine Signatur über den Rumpf und ist nach
|
||
fünf Minuten wertlos. Der Empfänger antwortet auf jeden Fehlschlag gleich:
|
||
falsches Geheimnis, verstellte Uhr und Unsinn im Inhalt sind von außen
|
||
nicht zu unterscheiden.
|
||
- Der Signaturvergleich läuft in gleichbleibender Zeit.
|
||
- Aufgaben verlassen die Instanz nur mit ausdrücklicher Freigabe je Aufgabe.
|
||
Ein Schalter für einen ganzen Bereich würde zu leicht Kundennamen nach
|
||
Hause schicken.
|
||
|
||
## [0.5.0] – 2026-09-11
|
||
|
||
### Hinzugefügt
|
||
|
||
- **Benachrichtigungen** per Web-Push und E-Mail, je Benutzer einstellbar.
|
||
Vier Anlässe: Tagesüberblick am Morgen, fällige Aufgaben, Vorlauf vor einem
|
||
Zeitblock und ein Kalender, der sich nicht abgleichen lässt.
|
||
- **Ruhezeit** von 20:00 bis 07:00 ab Werk, über Mitternacht hinweg; wahlweise
|
||
das ganze Wochenende. Der Tagesüberblick kommt trotzdem zu seiner Zeit — er
|
||
käme sonst nie, wenn jemand früh aufsteht.
|
||
- Anmeldung eines Browsers für Push, Liste der angemeldeten Geräte und ein
|
||
Knopf, der eine Probemeldung über alle eingeschalteten Wege schickt.
|
||
- Ein Lauf alle zehn Minuten prüft, ob etwas zu melden ist; die nächtliche
|
||
Aufräumrunde entfernt Nachweise, die älter als 30 Tage sind.
|
||
- Handbuchseite zu Benachrichtigungen.
|
||
|
||
### Geändert
|
||
|
||
- Der Kalenderabgleich zählt Fehlversuche in Folge mit und meldet erst ab dem
|
||
dritten. Ein Netz hustet, ein Anbieter hat Wartung — das ist noch keine
|
||
Nachricht wert.
|
||
- Ab Werk meldet der Planer gar nichts: kein Weg ist eingeschaltet. Wer beim
|
||
ersten Start mit Meldungen überschüttet wird, schaltet alles ab — und dann
|
||
auch das Nützliche.
|
||
|
||
### Sicherheit
|
||
|
||
- Die Push-Schlüssel eines Geräts liegen verschlüsselt in der Datenbank.
|
||
Weder Endpunkt noch Schlüssel noch Mailinhalte landen im Protokoll.
|
||
- Ein Gerät, das der Push-Dienst mit 404 oder 410 ablehnt, wird sofort
|
||
entfernt; andere Fehler führen nach fünf Versuchen zum selben Ergebnis.
|
||
- Der Nachweis einer Meldung wird **vor** dem Senden geschrieben: lieber eine
|
||
verpasste Nachricht als eine, die im Minutentakt erneut versucht wird.
|
||
|
||
## [0.4.0] – 2026-09-11
|
||
|
||
### Hinzugefügt
|
||
|
||
- **Wochenansicht** mit Ziehen und Ablegen: Aufgaben aus der linken Spalte in
|
||
ein Zeitfenster ziehen legt einen Zeitblock an und schreibt ihn in den
|
||
Kalender. Die Dauer kommt aus dem geschätzten Aufwand der Aufgabe.
|
||
- Zeitblöcke verschieben und ausplanen; der Kalendertermin zieht mit.
|
||
- **Rückweg**: Wird ein Zeitblock im Kalender verschoben, übernimmt der Planer
|
||
das beim nächsten Abgleich. Wird er dort gelöscht, verschwindet auch der
|
||
Zeitblock — die Aufgabe bleibt und fällt zurück auf offen.
|
||
- Kapazität und Belegung je Wochentag über jeder Spalte; überbuchte Tage
|
||
werden rot, sperren aber nichts.
|
||
- Eine eingeplante Aufgabe wechselt selbsttätig von _Offen_ auf _Geplant_ und
|
||
wieder zurück, wenn nichts mehr für sie geplant ist.
|
||
- Handbuchseite zur Wochenplanung.
|
||
|
||
### Sicherheit
|
||
|
||
- **Schleifenschutz** über `hash = sha256(startISO|endeISO)`: Der Planer
|
||
schreibt nur bei eigener Änderung und übernimmt aus dem Kalender nur, wenn
|
||
der Fingerabdruck abweicht. Ohne das würden App und Kalender einander
|
||
endlos hinterherschreiben.
|
||
- Jeder Schreibzugriff auf einen bestehenden Termin geht mit `If-Match`
|
||
heraus. Bei HTTP 412 **gewinnt der Kalender**: der Planer schreibt nicht,
|
||
verwirft seinen Fingerabdruck und überlässt dem nächsten Abgleich, den
|
||
fremden Stand zu holen.
|
||
- Zeitblöcke gehören dem Benutzer über sein Kalenderkonto; die Kennung geht
|
||
in jede where-Bedingung, nicht in einen nachträglichen Vergleich.
|
||
|
||
## [0.3.0] – 2026-09-10
|
||
|
||
### Hinzugefügt
|
||
|
||
- Kalenderkonten für Microsoft 365 (Graph) und Google verbinden, umbenennen,
|
||
von Hand abgleichen und trennen. Google gibt es nur in der Instanz
|
||
`daheim`; in `firma` liefert die Route 404.
|
||
- Termine werden örtlich gespiegelt: 7 Tage zurück bis 90 Tage voraus.
|
||
Gelesen werden nur Beginn, Ende, Betreff, ganztägig und "zeigt an als" —
|
||
keine Teilnehmer, Beschreibungen, Anhänge oder Orte.
|
||
- Die Startseite zeigt je Bereich, wie voll der heutige Tag ist: Termine aus
|
||
allen verbundenen Kalendern gegen die Tageskapazität.
|
||
- Hintergrundjobs: alle 15 Minuten abgleichen, nachts alte Termine und
|
||
abgelaufene WebAuthn-Herausforderungen entfernen.
|
||
- Handbuchseite zu den Kalenderkonten, Einrichtungsanleitung für Entra ID
|
||
und die Google Cloud Console in der README.
|
||
|
||
### Sicherheit
|
||
|
||
- Zugangs- und Aktualisierungstoken liegen verschlüsselt in der Datenbank
|
||
(AES-256-GCM, Schlüssel aus AUTH_SECRET abgeleitet). Wer eine Sicherung in
|
||
die Hände bekommt, kann damit ohne AUTH_SECRET nichts anfangen.
|
||
- Der Zustandswert im OAuth-Ablauf ist signiert und trägt die
|
||
Benutzerkennung; ohne das ließe sich ein fremdes Kalenderkonto an ein
|
||
beliebiges Konto hängen. Er verfällt nach 30 Minuten.
|
||
- Fehlermeldungen nennen nie den Antwortkörper des Token-Endpunkts — der
|
||
enthält das Client-Secret oft im Echo.
|
||
- Protokolliert werden beim Abgleich nur Zahlen, nie Kontonamen oder
|
||
Termininhalte.
|
||
|
||
## [0.2.0] – 2026-09-10
|
||
|
||
### Hinzugefügt
|
||
|
||
- Aufgaben mit Typ, Status, Priorität, Aufwand, Fälligkeit und Herkunft;
|
||
Projekte als Klammer darum.
|
||
- Schnellerfassung mit einem einzigen Feld auf der Startseite und in der Inbox.
|
||
- Inbox: alles ohne Bereich sammelt sich dort und wird später einsortiert.
|
||
Die Zahl im Menü zeigt, wie viel noch liegt.
|
||
- Aufgabenliste mit Filtern nach Suche, Bereich, Status, Art und Projekt.
|
||
Die Filter stehen in der Adresse und lassen sich als Lesezeichen ablegen.
|
||
- Ideen als eigene Ansicht, ohne Termin und ohne Aufwand; per Klick wird
|
||
daraus eine Aufgabe.
|
||
- Projekte mit Arbeitspaketen, Meilensteinen und Fortschritt.
|
||
- Die Startseite zeigt, was überfällig, heute oder morgen fällig ist.
|
||
- Handbuchseiten zu Aufgaben, Ideen und Projekten.
|
||
|
||
### Geändert
|
||
|
||
- Die Startseite zählt nicht mehr nur Bereiche auf, sondern stellt voran,
|
||
was jetzt dran ist.
|
||
|
||
### Sicherheit
|
||
|
||
- Statuswechsel folgen festen Übergängen: Abgeschlossenes wird erst wieder
|
||
geöffnet. Ein abweichender Wert im Formular führt zu einer Meldung, nicht
|
||
zu einem stillen Zustandssprung.
|
||
- Angegebene Bereiche und Projekte werden vor dem Speichern geprüft; ein
|
||
erfundener Verweis gibt eine Meldung statt eines Datenbankfehlers.
|
||
|
||
## [0.1.0] – 2026-09-10
|
||
|
||
### Hinzugefügt
|
||
|
||
- Projektgerüst mit Next 16 (App Router, `output: "standalone"`), React 19,
|
||
TypeScript strict, Tailwind 4 und shadcn-Primitiven.
|
||
- `npm run pruefen` bündelt Prettier, ESLint, `tsc --noEmit`, Vitest und
|
||
ShellCheck. ShellCheck läuft über das Docker-Abbild, wenn es lokal fehlt.
|
||
- Datenmodell für Benutzer mit Rolle, OIDC-Konten, Passkeys, Bereiche und
|
||
Audit-Einträge; PostgreSQL 16 über Prisma 7 mit pg-Treiberadapter.
|
||
- Anmeldung über einen generischen OIDC-Anbieter, über Passkeys
|
||
(SimpleWebAuthn) und wahlweise über E-Mail und Passwort.
|
||
- Rollen `BENUTZER` und `ADMIN`. Jede schreibende Aktion prüft die Rolle
|
||
serverseitig in der Seite **und** noch einmal in der Server Action.
|
||
- Audit-Protokoll für jede schreibende Aktion, mit Protokollseite für die
|
||
Verwaltung. Details werden vor dem Speichern gesäubert.
|
||
- Instanzsteuerung über `INSTANZ=firma|daheim`. Was eine Instanz nicht darf,
|
||
liefert dort 404 und lädt seine Konfiguration gar nicht erst.
|
||
- Bereiche mit Farbe, Arbeitsfenster und Tageskapazität je Wochentag.
|
||
- Zeitrechnung durchgehend in Europe/Berlin, mit Tagesgrenzen, Wochenbeginn
|
||
am Montag und deutschen Anzeigeformaten.
|
||
- `/api/health` mit Status, Version, Instanz und Erreichbarkeit der Datenbank.
|
||
- Hell und Dunkel, Navigation mit Rollenfilter, Oberfläche auf Deutsch.
|
||
- Dockerfile in drei Stufen und Compose-Stapel mit eigenem Migrationsdienst.
|
||
- `backup.sh` (pg_dump im Custom-Format, `.env` und Uploads),
|
||
`update.sh` (mit Rückrollpfad), `scripts/release.sh` und `scripts/zeit.sh`.
|
||
- Tests: Unit mit Vitest, Integrationstest und Migrationstest gegen eine
|
||
echte Datenbank, E2E-Rauchtest mit Playwright.
|
||
- `npm run screenshots` nimmt die Handbuchbilder in einer Wegwerf-Instanz mit
|
||
ausschließlich erfundenen Daten auf.
|
||
- README, Schnellstart, Handbuch, ROADMAP und DEVLOG.
|
||
|
||
### Sicherheit
|
||
|
||
- Anmeldung, Passwort und Passkey antworten bei Fehlschlag immer gleich,
|
||
damit sich nicht abfragen lässt, wer ein Konto hat.
|
||
- Sitzungen als JWT, aber Rolle und Sperrstatus werden bei jeder Anfrage
|
||
frisch aus der Datenbank gelesen — eine Sperre wirkt sofort.
|
||
- Die WebAuthn-Herausforderung wird nach einem Versuch verworfen, gleich ob
|
||
er geglückt ist.
|
||
- Der Demo-Seed verweigert den Dienst ohne `DEMO_MODUS=true`.
|
||
- `allowDangerousEmailAccountLinking` ist bewusst aktiv: je Instanz gibt es
|
||
genau einen vertrauenswürdigen OIDC-Anbieter, der die Adresse selbst prüft.
|