#!/bin/sh # Container-Start: # 1) Auf DB warten # 2) Auto-Baseline für bestehende DBs (db-push-Ära ohne _prisma_migrations) # 3) `prisma migrate deploy` (idempotent, datenerhaltend) # 4) Auto-Seed bei leerer User-Tabelle (oder RUN_SEED=true) # Neue Schema-Änderung anlegen (lokal, im Dev): npm run schema:sync set -e # DATABASE_URL aus DB_*-Komponenten bauen, falls nicht explizit gesetzt. # Wichtig: encodeURIComponent für DB_USER + DB_PASSWORD, damit Sonderzeichen # wie $, !, #, @, :, / etc. nicht die URL-Authority-Syntax brechen. # Wir nutzen node-eval (ist eh installiert), kein extra-Tool wie jq nötig. if [ -z "$DATABASE_URL" ] && [ -n "$DB_USER" ] && [ -n "$DB_PASSWORD" ] && [ -n "$DB_NAME" ]; then DATABASE_URL=$(node -e " const u = encodeURIComponent(process.env.DB_USER); const p = encodeURIComponent(process.env.DB_PASSWORD); const h = process.env.DB_HOST || 'db'; const port = process.env.DB_PORT || '3306'; const n = process.env.DB_NAME; process.stdout.write(\`mysql://\${u}:\${p}@\${h}:\${port}/\${n}\`); ") export DATABASE_URL echo "[entrypoint] DATABASE_URL aus DB_*-Komponenten gebaut (host=${DB_HOST:-db})" fi echo "[entrypoint] Warte auf Datenbank…" # Erst auf DB-Verfügbarkeit warten via einfachem Connect-Check. # Wir nutzen Prisma's interne Engine, kein extra mysql-client nötig. TRIES=30 until node -e " const { PrismaClient } = require('@prisma/client'); const p = new PrismaClient(); p.\$queryRaw\`SELECT 1\` .then(() => p.\$disconnect().then(() => process.exit(0))) .catch(() => process.exit(1)); " 2>/dev/null; do TRIES=$((TRIES - 1)) if [ "$TRIES" -le 0 ]; then echo "[entrypoint] DB nicht erreichbar – Abbruch" exit 1 fi echo "[entrypoint] DB noch nicht bereit – retry in 2s ($TRIES Versuche übrig)" sleep 2 done echo "[entrypoint] DB erreichbar" # Auto-Baseline: Wenn die DB Anwendungs-Tabellen enthält (z.B. User), aber noch # keine _prisma_migrations-Tabelle, dann ist es eine "alte" DB, die früher mit # `prisma db push` synced wurde. Wir markieren 0_init als bereits angewendet, # damit `migrate deploy` nicht versucht, alle Tabellen nochmal anzulegen. NEEDS_BASELINE=$(node -e " const { PrismaClient } = require('@prisma/client'); const p = new PrismaClient(); (async () => { try { const dbName = process.env.DB_NAME; const tables = await p.\$queryRawUnsafe( \`SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = ?\`, dbName ); const names = tables.map(t => t.TABLE_NAME); const hasMigrations = names.includes('_prisma_migrations'); const hasUserTable = names.includes('User'); // Existing DB (User da) ohne Migrations-Tracking => Baseline nötig if (hasUserTable && !hasMigrations) process.stdout.write('yes'); else process.stdout.write('no'); } catch (e) { process.stdout.write('no'); } finally { await p.\$disconnect(); } })(); " 2>/dev/null) if [ "$NEEDS_BASELINE" = "yes" ]; then echo "[entrypoint] Bestehende DB ohne Migrations-Tracking erkannt – markiere 0_init als angewendet (Baseline)" npx prisma migrate resolve --applied 0_init || echo "[entrypoint] Baseline fehlgeschlagen – fahre trotzdem fort" fi # Migrations anwenden (idempotent: bereits angewendete werden übersprungen). # Im Gegensatz zu `db push` löscht `migrate deploy` keine Daten — Schema- # Änderungen werden über versionierte Migrations-Files unter prisma/migrations/ # eingespielt. Neue Migration anlegen mit: npm run schema:sync (lokal, dev). echo "[entrypoint] Wende Migrations an…" if ! npx prisma migrate deploy; then echo "[entrypoint] migrate deploy fehlgeschlagen – Abbruch" exit 1 fi echo "[entrypoint] DB-Schema aktuell" # Auto-Seed: wenn die User-Tabelle leer ist (= Erstinstallation), automatisch seeden. # RUN_SEED=true erzwingt Seed auch bei nicht-leerer DB (z.B. nach Reset). USER_COUNT=$(node -e " const { PrismaClient } = require('@prisma/client'); const p = new PrismaClient(); p.user.count() .then((n) => { process.stdout.write(String(n)); process.exit(0); }) .catch(() => { process.stdout.write('-1'); process.exit(0); }); " 2>/dev/null) if [ "${RUN_SEED:-false}" = "true" ]; then echo "[entrypoint] RUN_SEED=true – seede DB (Force)" npx prisma db seed || echo "[entrypoint] Seed fehlgeschlagen oder schon gelaufen – ignoriert" elif [ "$USER_COUNT" = "0" ]; then echo "[entrypoint] DB ist leer (User-Count=0) – Auto-Seed wird ausgeführt" npx prisma db seed || echo "[entrypoint] Auto-Seed fehlgeschlagen – ignoriert" else echo "[entrypoint] DB enthält $USER_COUNT User – kein Seed nötig" fi echo "[entrypoint] Starte Backend…" exec "$@"