#!/bin/sh set -eu fail() { echo "[entrypoint] $1" >&2 exit 1 } if [ "${1:-}" = "migrate" ]; then [ "$#" -eq 1 ] || fail "migrate accepts no additional arguments" [ -n "${DIRECT_URL:-}" ] || fail "DIRECT_URL is required for the one-shot migrator" [ -z "${DATABASE_URL+x}" ] || fail "DATABASE_URL must not be forwarded to the one-shot migrator" [ -z "${DATABASE_RUNTIME_ROLE+x}" ] || fail "DATABASE_RUNTIME_ROLE belongs to runtime services, not the migrator" [ -z "${AUTHENTIK_ADMIN_JIT_DATABASE_URL+x}" ] || fail "AUTHENTIK_ADMIN_JIT_DATABASE_URL belongs only to the web runtime" if env | grep -Eq '^MIGRATOR_[A-Z0-9_]*='; then fail "MIGRATOR_* aliases must not be forwarded to the one-shot migrator" fi if env | grep -Eq '^SEED_[A-Z0-9_]*='; then fail "seed credentials and flags must not be forwarded to the one-shot migrator" fi # Prisma CLI reads DATABASE_URL. Keep that alias inside this short-lived # process; the long-lived services never receive the migrator credential. node /myapp/scripts/verify-migrator-role.mjs node /myapp/scripts/adopt-prisma-baseline.mjs --adopt export DATABASE_URL="$DIRECT_URL" echo "[migrator] prisma migrate deploy" node /myapp/node_modules/prisma/build/index.js migrate deploy --config prisma.config.ts echo "[migrator] verify current Prisma schema" node /myapp/scripts/adopt-prisma-baseline.mjs --verify-current-schema echo "[migrator] sync permission catalog" node /myapp/packages/db/dist/deploy-permission-catalog-sync.server.js exit 0 fi [ "$#" -eq 0 ] || fail "unsupported entrypoint mode" [ "${RLS_RUNTIME_MODE:-}" = "required" ] || fail "RLS_RUNTIME_MODE=required is mandatory for the web runtime" [ -n "${DATABASE_URL:-}" ] || fail "DATABASE_URL is required for the web runtime" [ -z "${DIRECT_URL+x}" ] || fail "DIRECT_URL must not be forwarded to the web runtime" [ -z "${MIGRATOR_DATABASE_URL+x}" ] || fail "MIGRATOR_DATABASE_URL must not be forwarded to the web runtime" case "${WEB_RUNTIME_SURFACE:-}" in core) [ "${DATABASE_RUNTIME_ROLE:-}" = "hivegrid_app" ] || fail "DATABASE_RUNTIME_ROLE=hivegrid_app is mandatory for the web runtime" [ -n "${SESSION_SECRET:-}" ] || fail "SESSION_SECRET is required for the core web runtime" [ -z "${PORTAL_SESSION_SECRET+x}" ] || fail "PORTAL_SESSION_SECRET is forbidden in the core web runtime" if [ -n "${AUTHENTIK_ADMIN_JIT_DATABASE_URL:-}" ]; then node -e ' try { const url = new URL(process.env.AUTHENTIK_ADMIN_JIT_DATABASE_URL); const validProtocol = url.protocol === "postgres:" || url.protocol === "postgresql:"; if (!validProtocol || url.username !== "hivegrid_authentik_jit" || !url.password || url.pathname === "/") process.exit(1); } catch { process.exit(1); } ' || fail "AUTHENTIK_ADMIN_JIT_DATABASE_URL must use the dedicated hivegrid_authentik_jit login" fi ;; admin) [ "${DATABASE_RUNTIME_ROLE:-}" = "hivegrid_app" ] || fail "DATABASE_RUNTIME_ROLE=hivegrid_app is mandatory for the admin web runtime" [ -n "${SESSION_SECRET:-}" ] || fail "SESSION_SECRET is required for the admin web runtime" [ -z "${PORTAL_SESSION_SECRET+x}" ] || fail "PORTAL_SESSION_SECRET is forbidden in the admin web runtime" [ "${ADMIN_FALLBACK_LOGIN_ENABLED:-false}" = "false" ] || fail "ADMIN_FALLBACK_LOGIN_ENABLED=false is mandatory for the admin web runtime" for ADMIN_SSO_KEY in AUTHENTIK_ISSUER_URL AUTHENTIK_ADMIN_CLIENT_ID AUTHENTIK_ADMIN_CLIENT_SECRET AUTHENTIK_ADMIN_JIT_DATABASE_URL AUTHENTIK_ADMIN_REDIRECT_URI; do eval "ADMIN_SSO_VALUE=\${$ADMIN_SSO_KEY:-}" [ -n "$ADMIN_SSO_VALUE" ] || fail "$ADMIN_SSO_KEY is required for the admin web runtime" done node -e ' try { const issuer = new URL(process.env.AUTHENTIK_ISSUER_URL); const redirect = new URL(process.env.AUTHENTIK_ADMIN_REDIRECT_URI); const jit = new URL(process.env.AUTHENTIK_ADMIN_JIT_DATABASE_URL); const validIssuer = issuer.protocol === "https:" && !issuer.username && !issuer.password && !issuer.search && !issuer.hash; const validRedirect = redirect.protocol === "https:" && redirect.hostname === process.env.ADMIN_APP_HOST && redirect.pathname === "/auth/admin/callback" && !redirect.username && !redirect.password && !redirect.search && !redirect.hash; const validJit = (jit.protocol === "postgres:" || jit.protocol === "postgresql:") && jit.username === "hivegrid_authentik_jit" && Boolean(jit.password) && jit.pathname !== "/"; if (!validIssuer || !validRedirect || !validJit) process.exit(1); } catch { process.exit(1); } ' || fail "Admin Authentik or JIT configuration is invalid" ;; portal) [ "${DATABASE_RUNTIME_ROLE:-}" = "hivegrid_portal" ] || fail "DATABASE_RUNTIME_ROLE=hivegrid_portal is mandatory for the portal web runtime" [ -n "${PORTAL_SESSION_SECRET:-}" ] || fail "PORTAL_SESSION_SECRET is required for the portal web runtime" [ -z "${SESSION_SECRET+x}" ] || fail "SESSION_SECRET is forbidden in the portal web runtime" [ -z "${AUTHENTIK_ADMIN_JIT_DATABASE_URL+x}" ] || fail "AUTHENTIK_ADMIN_JIT_DATABASE_URL is forbidden in the portal web runtime" PORTAL_ENV_KEYS="$(env | sed 's/=.*//')" if printf '%s\n' "$PORTAL_ENV_KEYS" | grep -Eq '^(SESSION_SECRET|ADMIN_SESSION_SECRET|CORE_SESSION_SECRET|COOLIFY|AUTHENTIK_[A-Z0-9_]*|(OAUTH|GOOGLE|MICROSOFT|APPLE)(_[A-Z0-9_]*)?|[A-Z0-9_]*_OAUTH(_[A-Z0-9_]*)?|(BILLING|LAGO|MOLLIE|STRIPE)(_[A-Z0-9_]*)?|(SSH|TRAEFIK|INGRESS|CLOUDFLARE)(_[A-Z0-9_]*)?|PORTAL_SERVER_[A-Z0-9_]*|([A-Z0-9_]*_)?DOMAIN_CONTROLLER(_[A-Z0-9_]*)?)$' || printf '%s\n' "$PORTAL_ENV_KEYS" | grep '^COOLIFY_' | grep -Evq '^(COOLIFY_BRANCH|COOLIFY_CONTAINER_NAME|COOLIFY_FQDN|COOLIFY_RESOURCE_UUID|COOLIFY_URL)$'; then fail "forbidden core session, OAuth, billing, ingress, or domain-controller credential in the portal web runtime" fi unset PORTAL_ENV_KEYS ;; *) fail "WEB_RUNTIME_SURFACE must be explicitly set to core, admin, or portal" ;; esac [ -z "${HIVEGRID_RUNTIME_SURFACE+x}" ] || [ "$HIVEGRID_RUNTIME_SURFACE" = "$WEB_RUNTIME_SURFACE" ] || fail "HIVEGRID_RUNTIME_SURFACE must match WEB_RUNTIME_SURFACE" export HIVEGRID_RUNTIME_SURFACE="$WEB_RUNTIME_SURFACE" if env | grep -Eq '^MIGRATOR_[A-Z0-9_]*='; then fail "MIGRATOR_* credentials must not be forwarded to the web runtime" fi [ -z "${MIGRATE_ON_BOOT+x}" ] || fail "MIGRATE_ON_BOOT is forbidden; use the one-shot migrator" [ -z "${SEED_ON_BOOT+x}" ] || fail "SEED_ON_BOOT is forbidden; use the one-shot migrator" [ -z "${SEED_ONCE+x}" ] || fail "SEED_ONCE is forbidden in the web runtime" if env | grep -Eq '^SEED_[A-Z0-9_]*='; then fail "seed credentials and flags must not be forwarded to the web runtime" fi exec node build/server-runtime/server.js