- apps/api/scripts/migrate-and-start.sh: TESSERA_MIGRATE_DATABASE_URL fuer
den Migrationsschritt, DATABASE_URL bleibt unveraendert fuer den
Laufzeitschritt; leer/nicht gesetzt = bisheriges Verhalten
- Dockerfile kopiert scripts/ und ruft das Skript als CMD auf; exec statt
&&-Verkettung, damit Signale den Node-Prozess erreichen
- docker-compose.yml/.prod.yml reichen TESSERA_MIGRATE_DATABASE_URL durch
(leerer Vorgabewert); docker-compose.dev.yml/.ci.yml unveraendert
- .env.example erklaert beide Variablen mit Platzhaltern, Umstellung bleibt
auskommentiert
- 5 Tests in start-script.spec.ts (rot vor dem Skript, jetzt gruen)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FYZcd3SSmo14QTqWx2KKzU
- docker-compose.yml und docker-compose.prod.yml mounten /app/user-files
im Dienst api auf ein neues benanntes Volume user-files (Eigentuemerschaft
uid 1001 folgt aus dem Image, kein Bind-Mount)
- docker-compose.dev.yml bleibt unveraendert (Compose fuehrt Mount-Listen
ueber das Ziel zusammen)
- Betriebshandbuch Kapitel 6: Speicher als dauerhaft beschrieben, Mount-Zeile
woertlich zum Kopieren, Hinweis dass /opt/tessera/docker-compose.yml auf
dem Server separat gepflegt werden muss (Deploy erreicht sie nicht)
- Betriebshandbuch Kapitel 7: Fehlerzeile zu verschwundenen Avataren/Exporten
an den reparierten Repository-Stand angepasst
WINDOWS #17 bleibt offen, bis die Serverdatei manuell ergaenzt ist.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FYZcd3SSmo14QTqWx2KKzU
NEXT_PUBLIC_API_URL is read by the browser, not by the web container, so
http://api:3001 could never work outside Docker. The test server had been
corrected by hand long ago; the fix never came back here, so the file we
would ship to a customer was the broken one.
Also adopts the server's TESSERA_FORCE_CHANGE default of true, so a fresh
install requires the initial admin password to be changed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CALENDAR_ENCRYPTION_KEY was named after the calendar module because that
module needed encryption first, in Phase 5. Every feature since has shared the
same key -- SMTP, the DKV and tender mailboxes, and as of today the LDAP bind
password -- so the name has been describing one of five users rather than the
thing itself, and each new feature inherited the confusion.
TESSERA_ENCRYPTION_KEY is the name now. The old one is still read, because
renaming outright would stop every existing installation at the next start:
their .env carries the old name, and compose was just made to fail hard on a
missing key. When only the old name is present the API logs a deprecation
warning naming both, and when both are set the new one wins -- otherwise a
half-migrated .env would encrypt with one key and decrypt with the other.
CalendarCryptoService becomes CryptoService in its own global CryptoModule.
Four modules used to import CalendarModule purely to reach the provider, which
read as a dependency on calendars where there was none; that import is gone.
Compose keeps the hard failure: without either name the stack refuses to
start. Verified in both files for all three cases -- neither name set (abort),
only the old name (starts), only the new name (starts).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The base compose file carried a hardcoded fallback key, so a stack whose .env
never set the variable started anyway and encrypted every stored credential
(LDAP bind, calendar, SMTP, DKV and tender mailboxes) with a value that is
public in this repository. That is encryption which looks present and protects
nothing.
Both compose files now use the ${VAR:?message} form, so an unset or empty key
fails at compose level with a message naming the variable and how to generate
one, instead of starting with a known key or dying later inside the API with a
stack trace.
Verified both ways: without the variable `docker compose config` exits 1 and
prints the hint; with a key present it exits 0.
Consequence for a fresh clone: the local stack no longer comes up until
CALENDAR_ENCRYPTION_KEY is set in .env. That is the point.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Admin should not be forced to change password on every fresh deploy.
Opt-in via TESSERA_FORCE_CHANGE=true in .env if needed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
NEXT_PUBLIC_API_URL was undefined at build time, causing client bundles to
fall back to http://localhost:3001 — unreachable from the browser in prod.
- Add /api-proxy rewrite in next.config.ts (forwards to API_INTERNAL_URL at runtime)
- Bake NEXT_PUBLIC_API_URL=/api-proxy at build time in Dockerfile
- Fix api.ts to prefer API_INTERNAL_URL for server-side calls
- Fix docker-compose.prod.yml: set NEXT_PUBLIC_API_URL=http://api:3001 for runtime server-side code
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Move prisma to runtime dependencies so it's available in prod image
- API runs migrate deploy before starting (handles fresh installs + updates)
- Remove separate migrate service from prod compose
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- sidebar.test: expand category before asserting on module names
(categories are collapsed by default since UI-Umbau)
- ci.yml: replace build-deploy with publish job that pushes images
to git.vicolab.de container registry
- docker-compose.prod.yml: pull-only compose for server deployments
using registry images
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>