Files
2026-09-16 16:14:06 +02:00

15 KiB

phase, plan, subsystem, tags, requires, provides, affects, actuals, plan_head_before, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, coverage, duration, completed, status
phase plan subsystem tags requires provides affects actuals plan_head_before tech-stack key-files key-decisions patterns-established requirements-completed coverage duration completed status
18-desktop-client-fertigstellen 01 infra
nestjs
tauri
gitea-actions
streamable-file
desktop-distribution
phase provides
06-desktop-client-ci-cd Tauri-Grundgeruest (apps/desktop, AppImage+NSIS-Bundle-Ziele, Tray, Setup-Seite)
.gitea/scripts/desktop-collect.sh (Pakete einsammeln, manifest.json schreiben)
.gitea/scripts/desktop-version.sh (Version aus dem Freigabe-Tag schreiben)
apps/api/src/desktop/ (GET /desktop/latest, GET /desktop/download/:platform, beide @Public())
packages/shared DesktopPlatform/DesktopManifest(File)/DesktopLatest(Response) Typen
apps/api/Dockerfile mit COPY desktop-dist
Basislinie 1.1.0 in tauri.conf.json/Cargo.toml/Cargo.lock/package.json
18-02-ci-pipeline-release-assets
18-03-web-oberflaeche
18-04-client-updateprüfung
tokens tasks commits
6718 2 2
0e4eb9b
added patterns
NestJS StreamableFile fuer grosse Downloads statt res.send(buffer) (Installer-Groessenordnung)
Manifest-getriebene Dateiauswahl: Dateiname kommt ausschliesslich aus manifest.json, nie aus dem Request-Pfad (Whitelist vor Dateisystemzugriff)
HTTP-Durchstich-Spec ueber NestFactory.create() + app.listen(0) statt fs-Mocks fuer datei-lesende Module
created modified
.gitea/scripts/desktop-collect.sh
.gitea/scripts/desktop-version.sh
apps/api/src/desktop/desktop.module.ts
apps/api/src/desktop/desktop.controller.ts
apps/api/src/desktop/desktop.service.ts
apps/api/src/desktop/desktop.service.spec.ts
desktop-dist/.gitkeep
.gitignore
apps/api/Dockerfile
apps/api/src/app.module.ts
packages/shared/src/index.ts
apps/desktop/package.json
apps/desktop/src-tauri/Cargo.toml
apps/desktop/src-tauri/Cargo.lock
apps/desktop/src-tauri/tauri.conf.json
DesktopController braucht @Inject(DesktopService) explizit auf dem Konstruktor-Parameter — Vitest transpiliert ueber esbuild, das emitDecoratorMetadata nicht abbildet; ohne den expliziten Token bleibt desktopService bei einem echten NestFactory-Bau (der HTTP-Durchstich-Test) undefined, obwohl derselbe Code unter tsc (nest build) korrekt aufgeloest wuerde.
Lokaler Stack am Ende beider Tasks zweimal neu gebaut (einmal je Task) statt nur einmal am Schluss, damit jede Verify-Stufe gegen den tatsaechlich damals gueltigen desktop-dist-Inhalt prueft und der Stack in einem konsistenten 1.1.0-Endzustand stehen bleibt.
PLATFORMS-Konstante (geschlossener Wertevorrat) vor jedem Dateisystemzugriff pruefen, danach erst das Manifest lesen — Reihenfolge ist die Sicherheitseigenschaft (T-18-01).
DESK-01
DESK-03
DESK-05
id description requirement verification human_judgment
D1 GET /desktop/latest liefert Version/Kanal/Dateiliste aus manifest.json (200) oder 404 ohne Manifest DESK-03
kind ref status
integration apps/api/src/desktop/desktop.service.spec.ts#Test 1 (latest, Manifest vorhanden) pass
kind ref status
integration apps/api/src/desktop/desktop.service.spec.ts#Test 2 (getLatest ohne Manifest) pass
kind ref status
other curl -sf http://localhost:3001/desktop/latest (lokaler Docker-Stack, neu gebautes Abbild) pass
false
id description requirement verification human_judgment
D2 GET /desktop/download/:platform streamt die Datei mit attachment-Header, Whitelist vor Dateisystemzugriff, Traversal/unbekannte Plattform enden mit 400, fehlende Pakete/Namen mit 404 DESK-03
kind ref status
integration apps/api/src/desktop/desktop.service.spec.ts#Test 3 (download/linux) pass
kind ref status
integration apps/api/src/desktop/desktop.service.spec.ts#Test 4 (Plattform-Whitelist + Traversal ueber HTTP) pass
kind ref status
integration apps/api/src/desktop/desktop.service.spec.ts#Test 5 (Whitelist vor Dateisystem) pass
kind ref status
integration apps/api/src/desktop/desktop.service.spec.ts#Test 6 (Manifest nur mit windows) pass
kind ref status
integration apps/api/src/desktop/desktop.service.spec.ts#Test 7 (manipulierter Name im Manifest) pass
kind ref status
other curl -sI http://localhost:3001/desktop/download/linux (lokaler Docker-Stack) pass
false
id description requirement verification human_judgment
D3 Beide Routen tragen @Public() (kein Anmelde-Zwang) DESK-03
kind ref status
unit apps/api/src/desktop/desktop.service.spec.ts#Test 8 (bewusst oeffentlich) pass
false
id description requirement verification human_judgment
D4 desktop-collect.sh sammelt das Tauri-AppImage ein, benennt es kanonisch um und schreibt manifest.json mit korrekter Groesse/SHA-256 DESK-01
kind ref status
other sh .gitea/scripts/desktop-collect.sh --require linux + sha256sum-Vergleich gegen manifest.json (zweimal ausgefuehrt: 0.0.1 und 1.1.0) pass
false
id description requirement verification human_judgment
D5 desktop-version.sh schreibt die reine X.Y.Z-Version des letzten Freigabe-Tags in tauri.conf.json/Cargo.toml, verweigert Vorab-/Metadatenformen DESK-05
kind ref status
other sh .gitea/scripts/desktop-version.sh --print + Negativproben (v1.2.3-beta abgelehnt, v2.0.0 akzeptiert-aber-ungeschrieben) pass
false
id description requirement verification human_judgment
D6 Basislinie 1.1.0 in allen vier Client-Dateien eingecheckt, cargo check bleibt gruen DESK-05
kind ref status
other cargo check (apps/desktop/src-tauri) -> Finished pass
false
13min 2026-09-16 complete

Phase 18 Plan 01: Desktop-Paket-Durchstich (Skript -> Abbild -> API) Summary

Linux-AppImage aus dem Tauri-Bau wird per neuem .gitea/scripts/desktop-collect.sh unter kanonischem Namen samt manifest.json eingesammelt, vom neu gebauten API-Abbild (apps/api/src/desktop/) ohne Anmeldung ausgeliefert (GET /desktop/latest, GET /desktop/download/linux), und die Client-Version stammt ab sofort aus dem Freigabe-Tag (desktop-version.sh, Basislinie 1.1.0).

Performance

  • Duration: 13 min
  • Started: 2026-09-16T13:58:00Z (geschaetzt)
  • Completed: 2026-09-16T14:11:25Z
  • Tasks: 2
  • Files modified: 15

Accomplishments

  • Neues API-Modul apps/api/src/desktop/ mit GET /desktop/latest (200 mit Version/Kanal/Dateiliste, 404 ohne Manifest) und GET /desktop/download/:platform (Stream mit Content-Disposition: attachment, Plattform-Whitelist vor jedem Dateisystemzugriff, Traversal/unbekannte Plattform -> 400, fehlende Pakete/manipulierte Namen -> 404) — 8 gruene Spec-Tests via echtem HTTP-Durchstich (NestFactory.create + app.listen(0), kein fs-Mock).
  • .gitea/scripts/desktop-collect.sh sammelt das gebaute AppImage ein, benennt es kanonisch (Tessera-{Version}{Suffix}.AppImage) und schreibt manifest.json (Version, Kanal, Commit, Groesse, SHA-256) — Kanalmodell identisch zu publish-images.sh (main=beta, Tag=live, sonst dev); Windows-Zweig bereits angelegt, aber in diesem Plan noch nicht gefordert (kommt in 18-05).
  • .gitea/scripts/desktop-version.sh schreibt die reine X.Y.Z-Version des letzten Freigabe-Tags in tauri.conf.json/Cargo.toml, verweigert jede Vorab-/Metadatenform (Pitfall 2 — NSIS-Ressourcen sind rein numerisch); Basislinie 1.1.0 (aktueller Tag v1.1.0) in allen vier Client-Dateien eingecheckt, cargo check bleibt gruen.
  • Lokaler Durchstich zweimal bewiesen: einmal mit dem Phase-6-AppImage (Version 0.0.1) fuer Task 1, einmal mit einem frisch gebauten AppImage (Version 1.1.0, Task 2) — beide Male liefert das neu gebaute API-Abbild die Datei ueber /desktop/download/linux und /api-proxy/desktop/latest (Web-Container) korrekt aus. Der lokale Stack steht am Ende auf der finalen 1.1.0-Baseline.

Task Commits

Each task was committed atomically:

  1. Task 1: Ein Linux-Paket aus dem Bau bis zum Download aus der API — eine Strecke - ae8fecb (feat)
  2. Task 2: Die Version kommt aus dem Freigabe-Tag — Skript und Basislinie 1.1.0 - 614289a (feat)

Plan metadata: commit pending (this SUMMARY + STATE.md/ROADMAP.md/REQUIREMENTS.md)

Files Created/Modified

  • .gitea/scripts/desktop-collect.sh - Pakete einsammeln, umbenennen, manifest.json schreiben (Kanalmodell, --require linux[,windows])
  • .gitea/scripts/desktop-version.sh - Version aus dem letzten Freigabe-Tag in tauri.conf.json/Cargo.toml schreiben, --print-Option
  • .gitignore - desktop-dist/* ignoriert, !desktop-dist/.gitkeep als versionierter Platzhalter
  • apps/api/Dockerfile - COPY desktop-dist ./desktop-dist vor USER nestjs
  • apps/api/src/app.module.ts - DesktopModule registriert (hinter HealthModule)
  • apps/api/src/desktop/desktop.module.ts - Modul-Verdrahtung (Vorbild health.module.ts)
  • apps/api/src/desktop/desktop.controller.ts - GET /desktop/latest, GET /desktop/download/:platform, beide @Public(), @Inject(DesktopService) explizit
  • apps/api/src/desktop/desktop.service.ts - Manifest lesen, PLATFORMS-Whitelist, Datei-Stream, 6-stufige Sicherheitspruefung in getPackage()
  • apps/api/src/desktop/desktop.service.spec.ts - HTTP-Durchstich-Spec (8 Tests, echtes Temp-Verzeichnis, unabhaengig berechneter SHA-256)
  • packages/shared/src/index.ts - DesktopPlatform, DesktopManifestFile, DesktopManifest, DesktopLatestFile, DesktopLatestResponse
  • desktop-dist/.gitkeep - Platzhalter, damit docker build auch ohne CI-Pakete funktioniert
  • apps/desktop/package.json, apps/desktop/src-tauri/tauri.conf.json, apps/desktop/src-tauri/Cargo.toml, apps/desktop/src-tauri/Cargo.lock - Basislinie 1.1.0

Decisions Made

  • @Inject(DesktopService) explizit auf dem Controller-Konstruktor gesetzt, weil Vitest ueber esbuild transpiliert (kein emitDecoratorMetadata) — ohne den expliziten Token bleibt die Abhaengigkeit im echten NestFactory.create()-Durchstich undefined, obwohl nest build (tsc) denselben Code ohne @Inject() korrekt aufloest. Kein Verhaltensunterschied im Produktionsbau, nur eine Testinfrastruktur-Notwendigkeit fuer den in RESEARCH/PATTERNS vorgeschlagenen echten HTTP-Durchstich ohne fs-Mocks.
  • Lokaler Docker-Stack (API-Container) wurde zweimal neu gebaut — einmal je Task — statt nur am Ende, damit jede der drei automatisierten <verify>-Stufen tatsaechlich gegen den zu diesem Zeitpunkt gueltigen desktop-dist-Inhalt prueft, und der Stack am Ende in einem konsistenten 1.1.0-Zustand stehen bleibt (nicht mit der Task-1-Zwischenversion 0.0.1).
  • Testobjekt fuer Task 1: das bereits vorhandene Phase-6-AppImage (Tessera_0.0.1_amd64.AppImage, 106.461.688 Bytes, SHA-256 ea5e1ef5...) wurde direkt verwendet, wie im Plan als zulaessige Abkuerzung vorgesehen ("Liegt ... noch das AppImage aus Phase 6, reicht es fuer diesen Durchstich"). Fuer Task 2 war ein frischer Bau mit der neuen Version 1.1.0 zwingend (Basislinie-Nachweis).

AppImage-Baudaten (Auftrag des Output-Abschnitts)

  • Task 1 (Testobjekt Phase-6-AppImage, kein frischer Bau): Tessera_0.0.1_amd64.AppImage, 106.461.688 Bytes, SHA-256 ea5e1ef56c282009ab8c20adbf84dbdb8b3fc29e777884817d50c7ad44bfb0ec (Build-Datum 25. Juni, aus einer fruaheren Sitzung — nicht in dieser Sitzung neu gebaut).
  • Task 2 (frischer Bau mit Basislinie 1.1.0): Tessera_1.1.0_amd64.AppImage, 106.928.632 Bytes, SHA-256 da38fd89ced60c91e4a32929f43dfdc1435efdc8b668cddb2f47c9348010fcb4. pnpm --filter @tessera/desktop exec tauri build --bundles appimage lief bei warmem target/-Verzeichnis (nach Entfernen des alten bundle/-Ordners) — Rust-Kompilierung 40,99 s laut cargo-Ausgabe, Gesamtlauf (inkl. Bundling) rund 2,5 Minuten Wanduhrzeit (14:06:56Z Start bis 14:09:39Z Manifest-Buildzeit).

Deviations from Plan

Auto-fixed Issues

1. [Rule 3 - Blocking] @Inject(DesktopService) noetig fuer den HTTP-Durchstich-Test unter Vitest

  • Found during: Task 1 (erster Testlauf von desktop.service.spec.ts)
  • Issue: Alle 5 HTTP-abhaengigen Tests scheiterten mit 500 ("Cannot read properties of undefined (reading 'getLatest')"). Ursache: Vitest transpiliert .ts-Dateien ueber esbuild, das emitDecoratorMetadata (TypeScript-Compiler-Feature) nicht abbildet — NestJS' automatische Konstruktor-Injection stuetzt sich normalerweise auf die von tsc erzeugten design:paramtypes-Metadaten, die unter esbuild fehlen. Ein echter NestFactory.create()-Bau (wie ihn RESEARCH/PATTERNS fuer den fs-mock-freien Test vorschlagen) konnte DesktopService deshalb nicht automatisch in DesktopController injizieren.
  • Fix: Expliziten Injection-Token per @Inject(DesktopService) auf dem Konstruktor-Parameter ergaenzt — das macht die Abhaengigkeit unabhaengig von design:paramtypes explizit und funktioniert sowohl unter Vitest/esbuild als auch im echten nest build (tsc) unveraendert.
  • Files modified: apps/api/src/desktop/desktop.controller.ts
  • Verification: Alle 8 Spec-Tests gruen nach der Aenderung (pnpm --filter @tessera/api exec vitest run src/desktop).
  • Committed in: ae8fecb (Task 1 commit)

Total deviations: 1 auto-fixed (1 blocking) Impact on plan: Notwendig, um den vom Plan geforderten fs-mock-freien HTTP-Durchstich-Test ueberhaupt lauffaehig zu machen. Keine Verhaltensaenderung im Produktionscode, keine Ausweitung des Umfangs.

Issues Encountered

None.

User Setup Required

None - no external service configuration required.

Next Phase Readiness

  • Die duenne Strecke Skript -> Abbild -> API ist bewiesen; 18-02 (CI-Pipeline, desktop-Job, publish-release.sh-Erweiterung) kann direkt auf desktop-collect.sh/desktop-version.sh und dem API-Modul aufbauen.
  • packages/shared-Typen (DesktopLatestResponse etc.) stehen fuer 18-03 (Web-Oberflaeche) und 18-04 (Client-Versionspruefung) bereit.
  • Kein Blocker. Der Windows-Cross-Bau (cargo-xwin, NSIS) ist NICHT Teil dieses Plans — desktop-collect.sh hat den Windows-Zweig bereits vorbereitet (ungetestet), 18-05 baut ihn aus und beweist ihn in der Pipeline.

Phase: 18-desktop-client-fertigstellen Completed: 2026-09-16

Self-Check: PASSED

All created files verified on disk (.gitea/scripts/desktop-collect.sh, .gitea/scripts/desktop-version.sh, apps/api/src/desktop/{desktop.module.ts,desktop.controller.ts,desktop.service.ts,desktop.service.spec.ts}, desktop-dist/.gitkeep). All three task/plan commits found in git log (ae8fecb, 614289a, plus this SUMMARY's own commit). All plan-level <verification> items re-run and passing: pnpm --filter @tessera/api exec vitest run src/desktop (8/8 green), pnpm --filter @tessera/api type-check (clean), desktop-dist/manifest.json at 1.1.0/Tessera-1.1.0.AppImage with matching SHA-256, local Docker stack serving /desktop/latest and /desktop/download/linux (also via /api-proxy/), both new scripts pass sh -n, desktop-version.sh rejects a pre-release tag.