Files
opencrm/backend/docker-entrypoint.sh
T
duffyduck 8ee5c9b07a Rollen+Permissions-Sync beim Container-Start
Folge-Fix für die DSGVO-Menü-Sache. Settings.tsx hatte ich auf
audit:read || gdpr:admin erweitert, aber auf bestehenden
Installationen läuft der prisma-Seed nicht (nur auf leeren DBs).
Wer das System früher installiert hat, hat die DSGVO-Rolle ohne
audit:read in der DB – das JWT enthielt die Perm dann nie, und der
neue Settings.tsx-Check blieb wirkungslos.

Neues Skript prisma/sync-roles.ts läuft idempotent bei jedem
Container-Start: upserts Permissions-Katalog + syncRolePermissions
für Admin, Developer, DSGVO, Mitarbeiter (R/W + R/O), Kunde.
Stammdaten, User und Verträge werden NICHT angefasst – sicher auf
prod.

Live-verifiziert: nach `DELETE audit:read FROM RolePermission`
liefert der nächste Lauf "+1 Permissions an Rolle #27", DSGVO ist
wieder komplett.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 12:41:39 +02:00

144 lines
6.4 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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)
RAN_SEED=false
if [ "${RUN_SEED:-false}" = "true" ]; then
echo "[entrypoint] RUN_SEED=true seede DB (Force)"
if npx prisma db seed; then RAN_SEED=true; else echo "[entrypoint] Seed fehlgeschlagen oder schon gelaufen ignoriert"; fi
elif [ "$USER_COUNT" = "0" ]; then
echo "[entrypoint] DB ist leer (User-Count=0) Auto-Seed wird ausgeführt"
if npx prisma db seed; then RAN_SEED=true; else echo "[entrypoint] Auto-Seed fehlgeschlagen ignoriert"; fi
else
echo "[entrypoint] DB enthält $USER_COUNT User kein Seed nötig"
fi
# Eingebaute Factory-Defaults nach Erstinstallation einspielen.
# Das ist die Werkseinstellung für neue VMs: PDF-Vorlagen, Anbieter, Tarife,
# HTML-Templates alles aus /app/factory-defaults-builtin/. Erfolgt nur wenn
# der Auto-Seed gerade lief (= frische DB), sonst werden Updates auf
# bestehenden Installationen nicht ungewollt überschrieben.
if [ "$RAN_SEED" = "true" ] && [ -d /app/factory-defaults-builtin ] \
&& [ -n "$(ls -A /app/factory-defaults-builtin 2>/dev/null | grep -v -E '^(README\.md|\.gitkeep)$')" ]; then
echo "[entrypoint] Spiele eingebaute Factory-Defaults ein…"
FACTORY_DEFAULTS_DIR=/app/factory-defaults-builtin npx tsx scripts/seed-factory-defaults.ts \
|| echo "[entrypoint] Factory-Defaults-Seed fehlgeschlagen ignoriert"
fi
# Permissions + Rollen-Sync: Stellt sicher, dass nachträglich hinzugefügte
# Permissions (z.B. audit:read auf der DSGVO-Rolle) auch auf bestehenden
# DBs ankommen. Seed läuft NICHT auf nicht-leeren DBs, daher würden alte
# Installationen sonst mit unvollständigen Role-Perms laufen. Idempotent,
# fasst keine Stammdaten / User / Verträge an.
echo "[entrypoint] Rollen + Permissions synchronisieren…"
npx tsx prisma/sync-roles.ts \
|| echo "[entrypoint] Role-Sync fehlgeschlagen nicht kritisch"
# Datenbereinigung: XSS-Strings aus Customer/User-Stringfeldern strippen,
# nicht-whitelisted AppSettings entfernen, Pentest-Marker melden (Default
# nur warnen; CLEANUP_PURGE_PENTEST=true löscht markierte Records).
# Idempotent läuft bei jedem Container-Start ohne Risiko.
echo "[entrypoint] Datenbereinigung läuft…"
npx tsx prisma/cleanup-xss-and-mass-assignment.ts \
|| echo "[entrypoint] Cleanup übersprungen / fehlgeschlagen nicht kritisch"
echo "[entrypoint] Starte Backend…"
exec "$@"