Commit Graph

3 Commits

Author SHA1 Message Date
Matthias Hochmeister
9927711192 fix(deploy): vollständige pg-Closure + funktionierender RUN_SEED im Runner-Image
Zwei BLOCKING-Befunde für "Deployment (Docker + externes Traefik)":

1) pg-Laufzeit-Closure unvollständig: pg-types/lib/binaryParsers.js macht
   eager require('pg-int8'), postgres-interval/index.js require('xtend/mutable').
   Beide lagen NICHT im Runner-Image -> node docker/migrate.mjs crasht beim
   Container-Start mit ERR_MODULE_NOT_FOUND, Deploy kaputt. Fix: COPY für
   pg-int8 + xtend ergänzt. Neuer Test berechnet die reale Closure aus
   node_modules und schlägt fehl, sobald ein Paket nicht ins Image kopiert
   wird (schützt vor erneutem Brechen bei pg/drizzle-Updates).

2) RUN_SEED toter Pfad: entrypoint.sh rief docker/seed.mjs auf, das nie
   existierte -> RUN_SEED=true no-opte still zu leerem Katalog. Fix:
   scripts/build-seed-bundle.mjs bündelt src/db/seed (inkl. Schema + Daten,
   pg/drizzle-orm extern) per esbuild zu selbstständigem docker/seed.mjs;
   im builder erzeugt und ins Runner-Image kopiert. entrypoint.sh bricht
   jetzt laut ab, wenn RUN_SEED=true und das Bundle fehlt, statt still zu
   überspringen. docker/seed.mjs ist generiert -> gitignored.

Verifiziert offline: tsc --noEmit, vitest (deployment + seed-Daten, 36 grün),
Bundle baut + lädt sauber (externe Imports nur pg/drizzle-orm, exit 1 ohne
DATABASE_URL). docker build/run sind im Sandbox deferred (kein Docker/Postgres).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 12:54:59 +02:00
Matthias Hochmeister
d50ec765ab Workstream 10: Deployment (Docker + externes Traefik) (Phase 7)
Liefert das reproduzierbare Compose-Setup hinter EXTERNEM Traefik:

- Dockerfile (multi-stage deps/builder/runner, Next.js standalone, non-root
  UID/GID 1001, HEALTHCHECK gegen /api/health).
- docker/entrypoint.sh: wartet via pg_isready auf Postgres, wendet Migrationen
  idempotent an (docker/migrate.mjs, plain ESM ohne tsx/drizzle-kit), optionaler
  Seed (RUN_SEED), dann exec node server.js.
- docker-compose.yml: genau vier Services (app, postgres, osrm, nominatim),
  KEIN Proxy-Service; externes traefik-Netz + internes Netz; Traefik-Labels
  (Host, websecure, tls.certresolver, Security-Header-Middleware);
  Postgres-/App-Healthchecks; AUTH_URL/AUTH_TRUST_HOST/Forwarded-Header.
- docker-compose.override.yml.example: lokal :3000 ohne TLS (http AUTH_URL).
- .dockerignore, Makefile (build/up/down/logs/deploy/data/config).
- .env.example: voller Vertrag inkl. APP_HOST, TRAEFIK_*, POSTGRES_*, RUN_SEED.
- docs/reference/deployment-traefik.md: externes Netz, Authentik-Redirect-URI
  https://${APP_HOST}/api/auth/callback/authentik, Forwarded-Header/Cookies,
  /api/health-Allowlist.
- tests/unit/deployment.test.ts (TDD): statische Offline-Verifikation der
  Artefakte; vitest.config.ts nimmt tests/unit/** auf.

Offline verifiziert: tsc --noEmit sauber; vitest run grün (200 passed,
7 db-roundtrip skipped); next build erzeugt .next/standalone/server.js;
sh -n docker/entrypoint.sh ok; make -n deploy zeigt build->up.
Deferred (kein Docker/Postgres in der Sandbox): docker build/run id -u=1001,
docker compose config --services, /api/health anonym 200, End-to-End Traefik.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 12:35:45 +02:00
Matthias Hochmeister
e8bb75412b Workstream 4: Geo & Eintreffzeit-Sortierung (Phase 3)
Selbstgehostete Geo-Dienste (OSRM + Nominatim auf Österreich-Extrakt),
Geokodierung beim Speichern und ETA-Sortierung der Suchtreffer mit
vollständigem Haversine-Luftlinie-Fallback.

- src/lib/geo/types.ts, config.ts: reine Typen + zentrale Konfiguration
  (aus kanonischem env.ts; Defaults, kaputte URL wird weiterhin abgelehnt).
- haversine.ts: Luftlinie in Metern (rein). St. Pölten->Wien ~55 km verifiziert.
- nominatim.ts: kanonische, reine geocodeAddress(address) (countrycodes=at,
  Timeout/Abort, status ok/not_found/error; KEIN geocodeBrigade-Zweitpfad).
- osrm.ts: etaTable via /table (sources=0, lng,lat), wirft bei Fehler.
- eintreffzeit.ts: orderByEintreffzeit (OSRM-first, kompletter Haversine-
  Fallback bei Wurf, Kandidaten ohne Koordinaten ans Ende, stabile Sortierung;
  OSRM-Funktion injizierbar fuer Tests).
- candidates.ts: searchHitsToGeoCandidates (Adapter, laedt brigades.lat/lng)
  + reine filterAndCapCandidates (Bounding-Box-Vorfilter 60 km, max 100).
- API: /api/geo/geocode (POST, auth-gated 401, Zod-Body, 404 bei not_found)
  und /api/geo/health (GET, auth-gated; OSRM/Nominatim up/down) — beide
  default-deny ueber apiAuth.
- Komponenten: standort-input.tsx (Client, Geolocation + Geocode-Fetch),
  eta-badge.tsx (kennzeichnet Luftlinie-Fallback), optionale karte.tsx
  via next/dynamic (ssr:false).
- Infra: docker-compose.geo.yml (internes Netz, Healthchecks), docker/osrm/
  Dockerfile, scripts/prepare-osm-data.sh, infra/geo/{Makefile,README.md}.

WS4 legt KEINE Migration an (brigades-Geo-Spalten + brigades_latlng_idx
stammen aus WS2); drizzle-kit check bleibt sauber.

Offline verifiziert: tsc --noEmit (exit 0), next lint (0 Warnungen),
vitest run (54 passed / 7 skipped DB-roundtrip), next build (exit 0 mit
gesetzten env-Vars), drizzle-kit check ("Everything's fine").
Deferred (kein Postgres/Server im Sandbox): db:migrate, Live-OSRM/Nominatim,
Playwright-E2E.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 09:37:39 +02:00