From 43a4c70053dd7c1cbd011abe1f0a19933d754495 Mon Sep 17 00:00:00 2001 From: Florian Wilde Date: Tue, 18 Aug 2026 08:10:32 +0200 Subject: [PATCH 1/2] docs(roadmap): Stand auf v1.10.0 gebracht Zuletzt angefasst bei v1.7.0. Seitdem sind drei Fassungen und vier Issues durchgelaufen, ohne dass die Roadmap es mitbekommen haette. Erledigt und deshalb aus den offenen Abschnitten entfernt: Wochenrueckblick, globale Zeitleiste und die Suche ueber Aufgaben - alle drei mit v1.10.0. Webhooks standen noch als Vorschlag drin, obwohl sie seit v1.7.0 laufen. In der Erledigt-Tabelle fehlten v1.8.0 bis v1.10.0. Die Nummerierung war ausserdem lueckenhaft: 1.0, dann 1.2 - der Punkt 1.1 war beim Umsetzen herausgeloescht worden. Jetzt wieder fortlaufend, und 1.0 (Rest aus v1.6.0) ist mit 1.3 zusammengefasst, weil beide dasselbe meinten. Neu ist ein Abschnitt "Aufgefallen beim Bauen": fuenf Beobachtungen aus der Arbeit an v1.6.0 bis v1.10.0, die sonst nur in Issue-Kommentaren und im Chat gestanden haetten - der abgeschnittene Anwendungsname auf schmalen Anzeigen, die fehlenden Indizes fuer die inzwischen vier projektuebergreifenden Seiten, der fehlende Projektfilter auf der Zeitleiste, der offene ASVS-Punkt zu geleakten Passwoertern und die zwei Fassungen ohne Release-Anker. Nichts davon ist dringend; festgehalten, damit es nicht verloren geht. --- ROADMAP.md | 108 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 74 insertions(+), 34 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index 98fb013..5f32ffb 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -7,25 +7,21 @@ Stück für Stück abarbeiten. Reihenfolge ist ein Vorschlag, keine Festlegung. Was bereits steht, listet [FEATURES.md](FEATURES.md). +**Stand:** v1.10.0. Alle Issues aus dem Dashboard sind abgearbeitet. + --- ## 1 · Als Nächstes empfohlen -### 1.0 Rest aus v1.6.0: persönliche API-Token `M` +### 1.1 Persönliche API-Token `M` -Schnellerfassung und die projektübergreifende Aufgabenseite sind mit v1.6.0 -ausgeliefert. Offen bleibt der dritte Teil — siehe 1.3. Er gehört sachlich -dazu: erfassen am Telefon, überblicken an einer Stelle, ansprechen per -Shortcut. +Der letzte offene Teil aus v1.6.0 („unterwegs erfassen"). Derzeit geht die +API nur mit Sitzungscookie. Ein Token je Konto würde `curl`-Aufrufe und +Handy-Shortcuts erlauben — eine Idee aus dem Terminal anlegen, ohne den +Browser zu öffnen. Nur der Hash in der Datenbank, wie bei den Sitzungen. -Bewusst nicht mitgeliefert wurde ein **Index für die übergreifende -Aufgabenabfrage**. `Task` hat kein `ownerId`, die Trennung läuft über -`project: { ownerId }`, und `[projectId, status, position]` greift dafür -nicht. Bei einigen hundert Aufgaben fällt das nicht auf; wird die Seite -träge, ist ein Index auf `dueDate` der erste Griff — und dann eine -Migration wert. - ---- +Bewusst noch nicht angefasst, weil sicherheitsrelevant: Geltungsbereich, +Widerruf und Ablauf wollen entschieden sein, nicht nebenbei gebaut. ### 1.2 Läuft es noch? `M` @@ -34,24 +30,17 @@ Nebula wüsste dann, ob ein Projekt nicht nur existiert, sondern auch läuft. Nutzt Zeitgeber und Benachrichtigungen, die es schon gibt. Der SSRF-Schutz greift dabei wie bei den Git-Aufrufen. -### 1.3 Persönliche API-Token `M` +### 1.3 Aufgaben ↔ Issues: der Rest `S` -Derzeit geht die API nur mit Sitzungscookie. Ein Token je Konto würde -`curl`-Aufrufe und Handy-Shortcuts erlauben — Idee aus dem Terminal anlegen, -ohne den Browser zu öffnen. Nur der Hash in der Datenbank, wie bei den -Sitzungen. - ---- - -### 1.4 Aufgaben ↔ Issues: was noch fehlt `S` - -Mit v1.7.0 stehen beide Richtungen: „als Issue anlegen“ und der Rückweg über -Webhooks. Offen geblieben ist Kleinkram: +Beide Richtungen stehen — „als Issue anlegen" seit v1.7.0, der Rückweg über +Webhook (v1.7.0) und zusätzlich über den Abgleich (v1.9.0). Offen ist +Kleinkram: - **Mehrere Repositories.** Der Knopf nimmt immer das führende. Bei einem Projekt aus Frontend und Backend fehlt die Wahl. -- **Beim Übernehmen aus Issues** wird der Rückweg nicht automatisch - eingerichtet — der Webhook ist getrennt zu setzen. +- **Webhook beim Verknüpfen anbieten.** Er ist getrennt einzurichten, und + wer das nicht tut, merkt nur an der Verzögerung, dass etwas fehlt. Ein + Hinweis an der richtigen Stelle würde reichen. Was **nicht** kommt, bleibt wie besprochen: eine echte beidseitige Synchronisation mit Konfliktauflösung. Die Zustände passen ohnehin nicht @@ -75,15 +64,60 @@ Voraussetzung, falls du das Projekt veröffentlichen willst. --- -## 3 · Kleinigkeiten +## 3 · Aufgefallen beim Bauen + +Beobachtungen aus der Arbeit an v1.6.0 bis v1.10.0 — festgehalten, damit sie +nicht verloren gehen. Nichts davon ist dringend. + +### 3.1 Anwendungsname bricht in der Kopfleiste ab `S` + +Zwischen 320 und 375 px wird aus „Nebula" ein „Neb…". Gemessen über fünf +Breiten und zwei Seiten: betrifft alle Seiten gleichermaßen. Seit die +Aufgaben als dritter Punkt danebenstehen, ist es enger geworden. + +### 3.2 Indizes für die projektübergreifenden Seiten `S` + +Weder `Task` noch `Activity` haben einen Besitzer — die Trennung läuft über +`project: { ownerId }`. Die vorhandenen Indizes +(`[projectId, status, position]` bzw. `[projectId, createdAt]`) greifen für +Abfragen über alle Projekte deshalb nicht. + +Betroffen sind inzwischen vier Seiten: Aufgabenübersicht, Wochenrückblick, +Zeitleiste und die Volltextsuche. Bei einigen hundert Datensätzen fällt das +nicht auf. Wird eine der Seiten träge, sind Indizes auf `Task.dueDate` und +`Activity.createdAt` der erste Griff — und dann eine Migration wert. + +### 3.3 Projektfilter auf der Zeitleiste `S` + +Über alle Projekte wird die Liste schnell lang. Der Wochenrückblick hat +Wochengrenzen als natürliche Bremse, die Zeitleiste nicht. Bewusst +weggelassen, weil das Issue nur „derselbe Verlauf, projektübergreifend" +verlangte. + +### 3.4 Abgleich gegen geleakte Passwörter `M` + +ASVS 2.1.7 verlangt es; umgesetzt ist bisher nur eine lokale Sperrliste plus +Mustererkennung (Wiederholungen, Zeichenfolgen, Benutzername im Passwort). +Have-I-Been-Pwned scheidet aus — Nebula ruft grundsätzlich nichts von außen, +und ein Passwort-Präfix nach draußen zu geben widerspräche dem erst recht. +Eine lokale Top-10000-Liste als Datei wäre der Kompromiss. + +### 3.5 v0.2.0 und v0.3.0 ohne Release `S` + +Beide stehen im Änderungsverlauf, existierten aber nie in der +`package.json` — dort springt die Historie von 0.1.0 auf 0.4.0. Nachgetragen +wurden sie in dem Commit, der den Verlauf einführte, und der liegt *nach* +0.4.0. Es gibt also keinen ehrlichen Commit, an dem ein Tag hängen könnte. +Die übrigen 18 Fassungen haben seit dem 18.08.2026 ein Release in Gitea. + +--- + +## 4 · Kleinigkeiten | Idee | Aufwand | Nutzen | | --- | --- | --- | -| Wochenrückblick | `S` | „Was ist diese Woche passiert" über alle Projekte, aus dem Aktivitätsverlauf | -| Globale Zeitleiste | `S` | Derselbe Verlauf, aber projektübergreifend als eigene Seite | -| Suche über Aufgaben | `S` | Die Volltextsuche deckt bisher nur Notizen ab | | Suchtreffer in Notizen hervorheben | `S` | In der Notiz selbst, nicht nur in der Trefferliste | -| Aufgabe ↔ Commit verknüpfen | `M` | Enthält ein Commit `#5`, wird die übernommene Aufgabe verlinkt — fällt weitgehend mit 1.4 ab | +| Aufgabe ↔ Commit verknüpfen | `M` | Enthält ein Commit `#5`, wird die übernommene Aufgabe verlinkt — fällt weitgehend mit 1.3 ab | | Tastaturkürzel-Übersicht mit `?` | `S` | Es gibt inzwischen genug davon | | Hell/Dunkel umschaltbar | `M` | Aktuell nur dunkel | | Eigenes Logo und Farbe | `S` | Ergänzt `APP_NAME` | @@ -91,7 +125,7 @@ Voraussetzung, falls du das Projekt veröffentlichen willst. --- -## 4 · Bewusst nicht +## 5 · Bewusst nicht Damit klar ist, was **nicht** kommt und warum: @@ -104,7 +138,10 @@ Damit klar ist, was **nicht** kommt und warum: - **Anhänge an Notizen.** Bräuchte Dateispeicher und damit eine zweite Sorge bei jeder Sicherung. Ein Link ins Git tut es auch. - **Eigene Git-Operationen** (klonen, committen, pushen). Nebula ist eine - Übersicht, keine Entwicklungsumgebung. + Übersicht, keine Entwicklungsumgebung. Issues über die HTTP-API der + Provider sind davon nicht berührt — die gibt es seit v1.7.0. +- **Beidseitige Synchronisation von Aufgaben und Issues** mit + Konfliktauflösung — siehe 1.3. - **Zeiterfassung, Rechnungen, Kundenverwaltung.** Andere Baustelle. - **Cloud-Synchronisation.** Die Daten bleiben in deiner Datenbank; das ist der Punkt. @@ -138,6 +175,9 @@ Damit klar ist, was **nicht** kommt und warum: | **v1.5.0** Sicherheitsdurchsicht, Server-Anleitung und Update-Skript | ✓ | | **v1.6.0** Mobile Ansicht, Listenansicht, Schnellerfassung, Aufgaben über alle Projekte | ✓ | | **v1.7.0** Aufgabe als Git-Issue anlegen, Webhooks für Push und Issues | ✓ | +| **v1.8.0** Anmeldung und Sitzungen nach OWASP: scrypt-Aufwand, Aufzählbarkeit, Leerlauffrist, Cookie-Härtung | ✓ | +| **v1.9.0** Geschlossene Issues haken ihre Aufgabe auch ohne Webhook ab | ✓ | +| **v1.10.0** Wochenrückblick, globale Zeitleiste, Suche über Aufgaben | ✓ | Zur Entscheidung beim Export: Git-Tokens gehen **nicht** mit. Die Verbindung wird nur beschrieben, das Token ist nach dem Import neu zu hinterlegen. Snapshots bleiben -- 2.54.0 From 3617222cc12fc9bb07de103804a7ca38fba967e7 Mon Sep 17 00:00:00 2001 From: Florian Wilde Date: Tue, 18 Aug 2026 08:20:57 +0200 Subject: [PATCH 2/2] docs: alle Markdown-Dokumente auf Stand v1.10.0 Die Dokumente waren unterschiedlich weit zurueck: FEATURES bei v1.9.0, README bei v1.8.0, DEPLOYMENT und CLAUDE.md noch bei v1.5.0. **README.** Die Einleitung nannte weder die projektuebergreifenden Aufgaben noch Rueckblick, Zeitleiste oder die Issue-Anbindung. In der API-Tabelle fehlten vier Routen (/api/activity, /api/tasks/:id/issue, /api/repositories/:id/webhook und der eingehende /api/webhooks/:id); die Suche war noch als "ueber die eigenen Notizen" beschrieben. Die Projektstruktur zeigte ein lib/sync.ts, das laengst ein Verzeichnis ist, und kannte weder tasks/ noch rueckblick/ oder verlauf/. Unter "Getestet mit" stand Node 26.5.0, tatsaechlich laeuft 24.19.0. Die Tabelle nennt jetzt den Werkzeugstand mit Datum und trennt ihn von der Aussage, wann zuletzt gegen echte Git-Systeme geprueft wurde - das war im Juli und ist seitdem nicht wiederholt worden. **FEATURES.** Neues Kapitel 2c zu Rueckblick und Zeitleiste, samt der beiden Entscheidungen, die sonst niemand nachvollziehen kann: Kalendertage in fester Zeitzone statt "7 x 24 Stunden", und "erledigt" aus dem meta-Feld statt aus dem Anzeigetext. Das Suchkapitel deckt jetzt Aufgaben mit ab. **DEPLOYMENT.** Der Abschnitt "Updates" sprang bisher direkt ins Skript, ohne zu erklaeren, was ein Update ueberhaupt ist - genau daran ist die Frage "was meinst du mit Deploy?" entstanden. Jetzt steht vorneweg, dass Git und laufende Instanz zwei Orte sind und woran man sieht, welche Fassung laeuft. Dazu ein Abschnitt zu Webhooks (setzt Erreichbarkeit voraus, ohne sie geht aber nichts verloren) und die Sitzungsfristen in der Haertungsliste. **CLAUDE.md.** Sechs Fallstricke aus den letzten Runden, die sonst beim naechsten Mal erneut Zeit kosten - allen voran "min-width: auto" bei Flex- und Grid-Kindern, das hier zweimal zugeschlagen hat. Ausserdem: dass die feste Zeitzone auch beim Rechnen gilt, dass Dialoge der Bildschirmtastatur ausweichen muessen, dass ein Ausdrucksindex nur bei Zeichengleichheit greift (mit EXPLAIN nachsehen), dass ein leeres Migrationsverzeichnis "migrate diff" abbrechen laesst - und dass eine Ueberlaufmessung gegen unvollstaendige Testdaten faelschlich gruen meldet. Geprueft: Querverweise zwischen den Dokumenten aufgeloest, alle in README und DEPLOYMENT genannten Umgebungsvariablen gegen Code und .env.example gehalten, API-Tabelle gegen die tatsaechlich vorhandenen Routen. --- CLAUDE.md | 58 ++++++++++++++++++++++++++++++++++++++++++--- DEPLOYMENT.md | 43 +++++++++++++++++++++++++++++++++- FEATURES.md | 65 ++++++++++++++++++++++++++++++++++++++++++++++----- README.md | 42 ++++++++++++++++++++++++++------- 4 files changed, 189 insertions(+), 19 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index de9da75..eea4abe 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -64,6 +64,31 @@ SSRF-Schutz (`lib/security/ssrf.ts`). - **Datumsangaben** über `lib/utils` formatieren (feste Zeitzone), sonst weichen Server-Render und Hydration voneinander ab. Relative Zeiten bekommen `suppressHydrationWarning`. +- **Die feste Zeitzone gilt auch beim *Rechnen*, nicht nur beim Formatieren.** + Im Container läuft die Anwendung nach UTC; „welcher Kalendertag" und + „welche Woche" kippen dort abends um einen Tag. `lib/tasks` (`dueBucket`) + und `lib/review` (`wocheUm`) rechnen deshalb über Berliner Kalendertage. + Und eine Woche ist **nicht** „7 × 24 Stunden" — bei einer Zeitumstellung + hat sie 167 oder 169. +- **`min-width: auto` bei Flex- und Grid-Kindern.** Hat hier zweimal + zugeschlagen: ein Kind weigert sich, unter die Inhaltsbreite seines + breitesten Kindes zu schrumpfen, und drückt stattdessen die Nachbarn aus + dem Bild. Einmal war es ein Sortierfeld mit `min-width`, das „Neues + Projekt" hinausschob; einmal die Heatmap, die die ganze Projektseite + doppelt so breit machte. Heilmittel ist beide Male `min-w-0` am Kind — + ohne das greift auch ein `overflow-x-auto` darin nicht. +- **`body { overflow-x: hidden }` versteckt genau diese Fehler.** Es steht in + `globals.css` als Notnagel; was es abschneidet, ist auf dem Telefon + schlicht nicht mehr erreichbar. Ursachen gehören in die Komponente. +- **Dialoge müssen der Bildschirmtastatur ausweichen.** Sie verkleinert nur + das *sichtbare* Fenster, nicht das Layoutfenster — `position: fixed` und + `100vh` beziehen sich weiter auf das volle. `components/ui/Modal.tsx` hängt + dafür an `window.visualViewport`; wer ein eigenes Overlay baut, braucht + dasselbe. +- **Blättern über Cursor, nicht über `skip`.** Der Aktivitätsverlauf schreibt + mehrere Zeilen im selben Augenblick; ein Offset über so eine Grenze hinweg + überspringt oder verdoppelt lautlos. Muster in `lib/timeline.ts`: + `(createdAt, id)` als Cursor, Sortierung identisch zur Bedingung. ## Migrationen @@ -83,9 +108,24 @@ vergleicht mit der *laufenden* Datenbank. Ist eine eben erzeugte Migration dort noch nicht eingespielt, steht ihr Inhalt in der nächsten gleich noch einmal. -**Die erzeugte SQL vor dem Anwenden lesen.** Der Volltextindex -`Note_fulltext_idx` ist ein Ausdrucksindex, den Prisma nicht kennt (er wird -bislang in Ruhe gelassen, aber das ist nicht garantiert). +**Das Zielverzeichnis vorher *nicht* anlegen.** Ein leerer Ordner unter +`prisma/migrations/` lässt `--from-migrations` mit `P3015` abbrechen +(„Could not find the migration file"). Erst die SQL woandershin erzeugen, +dann Ordner anlegen und hineinkopieren. + +**Die erzeugte SQL vor dem Anwenden lesen.** Die Volltextindizes +`Note_fulltext_idx` und `Task_fulltext_idx` sind Ausdrucksindizes, die Prisma +nicht kennt (sie werden bislang in Ruhe gelassen, aber das ist nicht +garantiert). + +**Ein Ausdrucksindex greift nur bei Zeichengleichheit.** Weicht der Ausdruck +in der Abfrage auch nur in einem `coalesce` vom Index ab, rechnet Postgres +`to_tsvector` je Zeile neu — ohne Fehlermeldung. Nachsehen statt annehmen: + +```bash +docker exec nebula-db psql -U nebula -d -c "SET enable_seqscan = off; +EXPLAIN (COSTS OFF) SELECT …" # muss einen Bitmap Index Scan zeigen +``` **Datenmigrationen von Hand schreiben.** `migrate diff` kennt nur den Zielzustand: Spalten, die woandershin wandern, würde es ersatzlos löschen. @@ -103,6 +143,18 @@ Bei Änderungen an der Oberfläche **hinsehen**. Chrome ist installiert, ein Skript mit `playwright-core` und `channel: "chrome"` genügt für Screenshots. Mehrere Anzeigefehler dieses Projekts sind ausschließlich so aufgefallen. +**Seitlichen Überlauf misst man.** `document.documentElement.scrollWidth` +gegen `clientWidth` auf 375 px — das findet in Sekunden, was beim Ansehen +durchrutscht. Ein Skript, das dabei nur die *obersten* Verursacher meldet +(Eltern, die selbst schon überlaufen, herausfiltern), ist deutlich brauchbarer +als eine Liste aller betroffenen Knoten. + +**Die Testdaten müssen die Bausteine enthalten, um die es geht.** Eine +Messung gegen ein Projekt ohne Repository, ohne Snapshot und ohne README +bescheinigte hier einmal „alles in Ordnung", während die echte Projektseite +doppelt so breit war wie der Bildschirm. Erst mit vollständigen Daten +nachgestellt trat der Fehler zutage. + **Barrierefreiheit misst man, statt sie zu behaupten.** `axe-core` liegt als Entwicklungsabhängigkeit bereit; ein Skript hängt es per `addScriptTag` in die laufende Seite und ruft `axe.run` mit den WCAG-Tags auf. Jeder diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index af1071a..65c9ec1 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -283,6 +283,23 @@ Zurückspielen: ## Updates +Es sind zwei getrennte Orte, und das ist der Punkt, an dem es leicht +durcheinandergeht: + +| | Was dort liegt | +| --- | --- | +| **Das Git-Repository** | Der Quellcode. Dorthin wird entwickelt. | +| **Diese Instanz** | Die laufende Anwendung, die du im Browser benutzt. | + +Ein Commit im Git ändert am laufenden Nebula **nichts**. Der Server holt sich +von allein nichts ab; er betreibt weiter die Fassung, die dort zuletzt gebaut +wurde. Ein Update ist der Schritt dazwischen: holen, bauen, umschalten. + +**Woran du siehst, was gerade läuft:** die Versionsnummer unten in der +Fußzeile. Ein Klick darauf öffnet den Änderungsverlauf. Steht dort eine +ältere Nummer als im Git, fehlen dir die neueren Funktionen — sie sind +geschrieben, aber noch nicht auf diesem Server. + Dafür gibt es `scripts/update.sh`. Es macht alles in der richtigen Reihenfolge und bricht ab, sobald ein Schritt scheitert: @@ -308,7 +325,7 @@ nennt den Weg zurück. ```bash ./scripts/update.sh --yes # ohne Rückfrage, für Cron -./scripts/update.sh --ref v1.4.0 # auf eine bestimmte Fassung +./scripts/update.sh --ref v1.9.0 # auf eine bestimmte Fassung ./scripts/update.sh --no-pull # nur neu bauen, ohne git pull ``` @@ -354,6 +371,23 @@ docker system df docker image prune -f # alte Abbilder nach mehreren Updates ``` +### Webhooks + +Der Abgleich holt den Stand in seinem Takt. Wer ihn **sofort** haben will — +ein Push ist durch, ein Issue wurde geschlossen —, trägt im Git-System einen +Webhook ein. Nebula erzeugt URL und Geheimnis am Repository (Funksymbol im +Git-Bereich der Projektseite), eingetragen wird der Haken von Hand. + +Voraussetzung ist, dass **dein Git-System diese Instanz erreicht**. Hinter +dem Reverse Proxy ist das gegeben; in einem abgeschotteten Netz nicht. Der +Endpunkt liegt unter `/api/webhooks/` und ist der einzige ohne +Anmeldung — ausgewiesen wird sich über eine Signatur, die gegen das +hinterlegte Geheimnis geprüft wird. + +**Ohne Webhook geht nichts verloren.** Der Abgleich prüft den Zustand +verknüpfter Issues ohnehin mit und hakt erledigte Aufgaben nach. Der Webhook +macht es nur sofort statt beim nächsten Durchlauf. + ### Automatischer Abgleich Standardmäßig **aus**. Einzuschalten unter *Admin-Einstellungen → @@ -383,6 +417,13 @@ Damit du weißt, worum du dich **nicht** kümmern musst: Zugriffe; Kontosperre nach acht Fehlversuchen. - **Git-Tokens** liegen AES-256-GCM-verschlüsselt in der Datenbank und verlassen den Server nie — auch nicht im Export. +- **Sitzungen** laufen nach 8 Stunden ohne Nutzung ab und spätestens nach + 30 Tagen (`SESSION_IDLE_HOURS`, `SESSION_TTL_DAYS`). In der Datenbank liegt + nur der Hash des Tokens; das Cookie ist `HttpOnly`, `SameSite=Strict` und + bei HTTPS zusätzlich mit dem Präfix `__Host-`. +- **Passwörter** werden mit scrypt nach der Empfehlung des OWASP Password + Storage Cheat Sheet gehasht (N = 2^17). Rechnet mit rund einer halben + Sekunde je Anmeldung — das ist gewollt. - Der **Bildoptimierer** von Next ist abgeschaltet: Nebula benutzt ihn nicht, und `/_next/image` wäre sonst eine Angriffsfläche ohne Gegenwert. diff --git a/FEATURES.md b/FEATURES.md index 2478792..b957c34 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -145,12 +145,17 @@ dort wäre er auf schmalen Anzeigen mit der Auswahlleiste kollidiert. Favoriten bleiben stets oben. - **Vier Ansichten**: freies Raster, **kompakte Liste**, nach Status gruppierte Abschnitte oder **Kanban**. -- **Volltextsuche über Notizen.** Die Suche im Board arbeitet auf den geladenen Projekten - (Name, Kurzbeschreibung, Tags, Repo-Pfad). Notizen liegen dort nicht vor — sie - mitzuladen würde die Seite aufblähen. Stattdessen sucht Postgres serverseitig mit - seiner deutschen Textsuche, entprellt ab zwei Zeichen. Treffer erscheinen als eigene - Liste mit hervorgehobener Fundstelle, die betroffenen Projekte werden zusätzlich - eingeblendet. Deutsche Stammformen greifen: „Webhook" findet auch „Webhooks". +- **Volltextsuche über Notizen und Aufgaben.** Die Suche im Board arbeitet auf den + geladenen Projekten (Name, Kurzbeschreibung, Tags, Repo-Pfad). Notizen und Aufgaben + liegen dort nicht vor — sie mitzuladen würde die Seite aufblähen. Stattdessen sucht + Postgres serverseitig mit seiner deutschen Textsuche, entprellt ab zwei Zeichen. + Treffer erscheinen als zwei eigene Listen mit hervorgehobener Fundstelle, die + betroffenen Projekte werden zusätzlich eingeblendet. Deutsche Stammformen greifen: + „Webhook" findet auch „Webhooks", „Rechnung" auch „Rechnungen". + + Beide Seiten haben je einen **Ausdrucksindex** (`Note_fulltext_idx`, + `Task_fulltext_idx`). Der Ausdruck darin muss Zeichen für Zeichen dem in der Abfrage + entsprechen, sonst greift er nicht und Postgres rechnet `to_tsvector` je Zeile neu. - **Archiv** ist standardmäßig ausgeblendet und per Schalter einblendbar. - **Leerzustände** mit direktem Einstieg statt einer leeren Fläche. @@ -341,6 +346,54 @@ Die Aufgabe kennt keinen Besitzer — die Trennung läuft über das Projekt --- +## 2c. Rückblick und Verlauf + +Zwei Sichten auf denselben Aktivitätsverlauf, über alle Projekte hinweg. Beide teilen +sich Zeilendarstellung und Tagesgruppierung (`components/ActivityRow`) — zwei Kopien +wären beim nächsten Feinschliff auseinandergelaufen. + +Der Verlauf kennt keinen Besitzer; die Trennung läuft über das Projekt +(`project: { ownerId }` als erste Bedingung). + +### Wochenrückblick + +`/rueckblick` beantwortet „was ist diese Woche passiert". Oben Kennzahlen — Vorgänge, +berührte Projekte, erledigte und neue Aufgaben —, darunter die Vorgänge nach +Kalendertagen gebündelt, neuester Tag zuerst. Über `?woche=-n` geht es bis zu 25 Wochen +zurück; weiter lohnt nicht, weil der Verlauf ohnehin nur 200 Einträge je Projekt hält. + +**Gerechnet wird auf Kalendertage in fester Zeitzone** (Europe/Berlin), nicht auf +„sieben mal 24 Stunden": liegt eine Zeitumstellung in der Woche, hat sie 167 oder 169 +Stunden. Und im Container läuft die Anwendung nach UTC — ein Eintrag von Sonntagabend +fiele sonst in die falsche Woche. Das Fach bestimmt der **Server**, damit Server-Render +und Hydration nicht auseinanderlaufen. + +**„Aufgaben erledigt" wird am `meta`-Feld abgelesen** (`to: "DONE"`), nicht am +Anzeigetext. Der Text ist zum Lesen da und ändert sich mit der Formulierung; eine +Zählung daran würde beim nächsten umformulierten Einzeiler unbemerkt kippen. Einträge +ohne `meta` zählen nicht mit — lieber untertreiben als raten. + +### Globale Zeitleiste + +`/verlauf` zeigt denselben Verlauf **fortlaufend**: ohne Wochengrenze, ohne Fazit, mit +„Ältere laden". Die erste Seite kommt serverseitig mit, alles Weitere über +`/api/activity`. + +**Geblättert wird per Cursor** über `(createdAt, id)`, nicht per `skip`. Der Verlauf +schreibt mehrere Einträge im selben Augenblick — beim Übernehmen von Issues entstehen +zehn Zeilen mit identischem Zeitstempel. Ein Offset über eine solche Grenze hinweg +überspringt Zeilen oder zeigt sie zweimal, und zwar lautlos. Der zweite Zweig der +Bedingung (gleicher Zeitpunkt, kleinere ID) fängt genau das ab. + +Nachgerechnet gegen 144 Einträge mit absichtlichen Blöcken gleicher Zeitstempel: 15 +Seiten, 144 Zeilen, keine Dublette, streng absteigend. + +Beide Seiten sind über die Schnellsuche erreichbar, der Rückblick zusätzlich über einen +Verweis vom Dashboard. Bewusst **kein** weiterer Punkt in der Kopfleiste — die ist auf +dem Telefon schon eng. + +--- + ## 3. Notizen - **Beliebig viele Notizen pro Projekt**, mit optionalem Titel und mehrzeiligem Text. diff --git a/README.md b/README.md index 7e56091..4e7b4ab 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,12 @@ Jedes Projekt bekommt einen **Status** (von der rohen Idee bis „Fertig"), beli Branches, Tags, Commits, Releases und offene Issues/PRs — egal ob **GitHub**, **GitLab** oder eine selbst gehostete **Gitea**-Instanz. +Neben dem Projektbrett gibt es **Aufgaben über alle Projekte hinweg** (nach Fälligkeit +geordnet), einen **Wochenrückblick** und eine **fortlaufende Zeitleiste** aus dem +Aktivitätsverlauf. Aufgaben lassen sich als **Git-Issue** anlegen; wird das Issue +geschlossen, hakt Nebula die Aufgabe ab — sofort per **Webhook** oder verzögert beim +nächsten Abgleich. + Die Anmeldung läuft über **Benutzername und Passwort oder Passkey** (WebAuthn). Nebula kennt zwei Betriebsarten: **Einzelbetrieb** für ein Konto oder **Mehrbenutzerbetrieb**, in dem jede Person ihren eigenen, streng getrennten Bereich hat. Umschalten geht jederzeit in @@ -275,15 +281,25 @@ nebula/ └── src/ ├── app/ │ ├── page.tsx Dashboard (Board, Filter, Kennzahlen) - │ ├── projects/[id]/ Projektdetail: Beschreibung, Notizen, Git + │ ├── projects/[id]/ Projektdetail: Beschreibung, Notizen, Aufgaben, Git + │ ├── tasks/ Aufgaben über alle Projekte, nach Fälligkeit + │ ├── rueckblick/ Wochenrückblick + │ ├── verlauf/ Fortlaufende Zeitleiste │ ├── connections/ Verwaltung der Git-Verbindungen - │ └── api/ REST-Endpunkte (Projekte, Notizen, Verbindungen, Sync) + │ ├── account/ admin/ Eigenes Konto · Adminbereich + │ └── api/ REST-Endpunkte (siehe Abschnitt „API") ├── components/ UI-Komponenten (Board, Karten, Panels, Dialoge) ├── lib/ + │ ├── auth/ Sitzungen, Passwörter, TOTP, WebAuthn, Zugriffsschutz │ ├── crypto.ts AES-256-GCM für Access Tokens │ ├── db.ts Prisma-Client │ ├── git/ Provider-Adapter: github.ts · gitlab.ts · gitea.ts - │ ├── sync.ts Repo-Snapshot holen und speichern + │ ├── sync/ Repo-Snapshot holen, speichern, Zeitgeber + │ ├── tasks/ Aufgabenlogik, Brücke zu Git-Issues + │ ├── webhooks/ Signaturprüfung eingehender Webhooks + │ ├── review.ts Wochengrenzen und Kennzahlen + │ ├── timeline.ts Cursor-Blättern für den Verlauf + │ ├── security/ SSRF-Schutz, Ratenbegrenzung, Body-Prüfung │ └── validation.ts Zod-Schemas für alle Eingaben └── types/ gemeinsame Typen für Server und Client ``` @@ -347,7 +363,8 @@ ausschließlich Daten des angemeldeten Kontos. | `PATCH` `DELETE` | `/api/templates/:id` | Vorlage ändern / löschen | | `POST` | `/api/templates/from-project` | Aus einem Projekt eine Vorlage machen | | `GET` | `/api/admin/backup` | Vollständige Sicherung als JSON (Admin) | -| `GET` | `/api/search?q=` | Volltextsuche über die eigenen Notizen | +| `GET` | `/api/search?q=` | Volltextsuche über eigene Notizen **und Aufgaben** | +| `GET` | `/api/activity` | Verlauf über alle Projekte, per Cursor (`?vor=&id=&limit=`) | | `GET` `POST` | `/api/notifications` | Benachrichtigungsziele | | `PATCH` `DELETE` | `/api/notifications/:id` | Ziel ändern / entfernen | | `POST` | `/api/notifications/:id/test` | Testnachricht senden | @@ -359,10 +376,13 @@ ausschließlich Daten des angemeldeten Kontos. | `GET` `POST` | `/api/projects/:id/repositories` | Repositories des Projekts | | `PATCH` `DELETE` | `/api/repositories/:id` | Repository ändern / vom Projekt lösen | | `POST` | `/api/repositories/:id/sync` | Ein einzelnes Repository abgleichen | +| `POST` `DELETE` | `/api/repositories/:id/webhook` | Webhook einrichten / zurücknehmen | +| `POST` | `/api/webhooks/:repositoryId` | **Eingehend** von GitHub/GitLab/Gitea, per Signatur geprüft | | `GET` `POST` | `/api/projects/:id/tasks` | Aufgaben des Projekts | | `PATCH` | `/api/projects/:id/tasks/reorder` | Neue Reihenfolge nach dem Ziehen | | `POST` | `/api/projects/:id/tasks/from-issues` | Offene Issues als Aufgaben übernehmen | | `PATCH` `DELETE` | `/api/tasks/:id` | Aufgabe ändern / löschen | +| `POST` | `/api/tasks/:id/issue` | Aufgabe als Issue im verknüpften Repository anlegen | | `GET` `POST` | `/api/projects/:id/notes` | Notizen des Projekts | | `PATCH` `DELETE` | `/api/notes/:id` | Notiz ändern / löschen | | `GET` `POST` | `/api/connections` | Git-Verbindungen | @@ -434,17 +454,21 @@ Grenzen, die du kennen solltest: ## Getestet mit -Stand 25.07.2026 gegen echte Systeme verifiziert — beide Betriebsarten, alle drei -Git-Adapter (GitHub, GitLab und Gitea jeweils gegen die öffentliche Instanz). +Am 25.07.2026 gegen echte Systeme verifiziert — beide Betriebsarten, alle drei +Git-Adapter (GitHub, GitLab und Gitea jeweils gegen die öffentliche Instanz). Seitdem +laufen die Ende-zu-Ende-Tests bei jeder Änderung mit; live nach draußen geht davon nur +ein Test gegen die GitHub-API. + +Werkzeugstand vom 18.08.2026: | | Version | | --- | --- | -| Node.js | 26.5.0 | +| Node.js | 24.19.0 | | npm | 11.17.0 | | Next.js | 15.5.21 | | Prisma | 6.19.3 | -| PostgreSQL | 17 (Alpine) | -| Docker / Compose | 29.6.2 / v5.3.1 | +| PostgreSQL | 17.11 (Alpine) | +| Docker | 29.7.2 | --- -- 2.54.0