Dev (Vite dev server / e2e mock mode): falls back to 'http://localhost:5179' so
Playwright route interception on http://localhost:5179/** keeps working.
Production (npm run build -> PROD=true): falls back to '/api' so nginx proxy
strips the prefix without any hostname baked in at build time.
VITE_API_BASE_URL (set by Aspire in dev, injected LAN IP) always wins over both.
Evidence: npm run e2e 48/48 PASS (mock mode, both viewports).
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
deploy/truenas/compose.yaml:
- 4 services: db (postgres:17), api (.NET), frontend (nginx), backup sidecar
- Named volumes as NAS bind-mounts (paths configured in .env)
- Healthchecks: db pg_isready, api /health, frontend waits on api
- ANTHROPIC_API_KEY placeholder for FEAT-12a AI stub
deploy/truenas/.env.example: POSTGRES_PASSWORD, PORT, REGISTRY, dataset paths
deploy/truenas/scripts/:
- entrypoint.sh: installs daily 03:00 cron, starts crond in foreground
- backup.sh: pg_dump -> .sql + tar czf photos -> .tar.gz + rotation
- restore.sh: psql < dump + tar xzf photos; confirmation prompt (-f to skip)
Predecessor logic (commit 6a37f9e) translated from PowerShell to POSIX sh
for Alpine containers. Same pg_dump/restore semantics, same KEEP_DAYS rotation.
.gitea/workflows/ci.yml (DRAFT -- inactive until Julian confirms Actions):
dotnet test + npm test/build on push/PR to main;
docker build+push to Gitea registry on main push only
docs/ops.md:
German ops guide -- architecture diagram, first-install checklist,
start/stop/update commands, backup runbook (manual + auto), ZFS-snapshot
layering strategy, CI activation steps, open questions, troubleshooting table
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- GerbilManagerWebAPI/Dockerfile: net7->net10 multi-stage; build context=repo
root (includes ServiceDefaults); port 8080 (.NET 10 container default)
- gerbil-manager-web/Dockerfile: drop VITE_API_BASE_URL build-arg; nginx proxies
/api/* to api:8080 (strip prefix), /scalar, /openapi/ -- one published port 80
- gerbil-manager-web/src/api/client.ts: default API_BASE_URL /api (relative),
no hostname coupling at build time; Aspire still injects absolute LAN URL in dev
- GerbilManagerWebAPI/Program.cs: run EF Migrate() at startup always (not Dev-only)
- GerbilManager.ServiceDefaults/Extensions.cs: expose /health + /alive in all
environments (compose healthcheck requires them in Production)
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Replace em dashes (UTF-8 multi-byte) with ASCII hyphens; PowerShell 5.1
reads ps1 files as CP1252 by default so any non-ASCII in source causes
cascading parse errors
- Extract POSTGRES_PASSWORD from container env (Aspire uses scram-sha-256;
peer/trust auth does not work) and pass as PGPASSWORD env var to pg_dump
and psql in both Backup and Restore scripts
- @() wrap on Get-ChildItem result to force array before .Count under
Set-StrictMode -Version Latest (single-item returns a bare object in PS5)
- Restore-test evidence: 73 ColorVarieties -> DELETE 12 -> 61 -> pg_restore
-> 73 confirmed (run 2026-06-06 07:09 against postgres-gatkzrgq)
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- GridifyParams wrapper: nullable page/pageSize/filter/orderBy so list endpoints
work with no query params (Gridify's non-nullable int Page made [AsParameters]
treat them as required -> 400). Defaults page=1,pageSize=20.
- Normalize incoming filter '==' -> '=' : the frontend gridify.ts emits '==' for
equals (its convention) but Gridify's equals is '='. Safe (values are escaped;
!= >= <= =* contain no '=='). Fixes status==Active (frontend default that 500'd).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- survivedToGoHome: unlabeled Tabelle1 col E detected positionally; value-
adaptive row parsing recovers it from schema-shifted Tabelle2 rows too
(118 recovered, 79 confirmed by the E=F-TG-s identity)
- breakdown G -> males/females/stillborn/diedLater ('s' = died before Abgabe)
- validation: E = F - TG - s; 113 mismatches as German review-report warnings
- (name, Zucht) canonicalisation: [brackets] == of/von suffix ([ZdkC] ==
von den Kleinen Chaoten); Zucht = dedup discriminator (0 splits in data)
- animal->litter matching via DOB+(Vater,Mutter): 95 high-confidence,
40 date-only, 9 ambiguous; litterRef in animals.json
- regenerated report: 889 raw -> 574 unique (279 dated), 32 conflicts
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
8 tables (Gerbils, Litters, Contacts, Enclosures, ColorVarieties, GerbilPhotos,
HealthRecords, WeightRecords); enums as text columns; ColorVariety HasData seed
of the 18 GEN-1 base varieties. Replaces the prior pre-DATA-2 InitialCreate.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>