#!/bin/bash # Function to log with timestamp log() { echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1" } # Function to handle errors handle_error() { local exit_code=$? local last_command=${BASH_COMMAND} log "ERROR: Command '$last_command' failed with exit code $exit_code." log "Exiting with error code: $exit_code" # Exit with the captured exit code exit $exit_code } # Set up error handling to call handle_error on ERR # and also exit immediately if a command exits with a non-zero status. set -e trap 'handle_error' ERR # Function to check if postgres is ready wait_for_postgres() { log "Waiting for PostgreSQL to be ready..." set +e # Temporarily disable exit on error for the until loop until PGPASSWORD=$(cat /run/secrets/postgres_password) psql -h ${DB_HOST:-postgres} -p ${DB_PORT:-5432} -U postgres -c '\q' 2>/dev/null; do log "PostgreSQL is unavailable - sleeping" sleep 1 done set -e # Re-enable exit on error log "PostgreSQL is up and running!" } # Verify Postgres is configured to use md5 authentication (and not trust/scram) for host connections check_postgres_md5_auth() { if [ "${SKIP_MD5_AUTH_CHECK}" = "true" ] || [ "${SKIP_MD5_AUTH_CHECK}" = "1" ]; then log "Skipping PostgreSQL md5 authentication check (SKIP_MD5_AUTH_CHECK=${SKIP_MD5_AUTH_CHECK})" return 0 fi log "Validating PostgreSQL authentication configuration (md5)..." local psql_base=(psql -h ${DB_HOST:-postgres} -p ${DB_PORT:-5432} -U postgres -d ${DB_NAME_SERVER:-server} -tAc) # 1) Ensure password_encryption is md5 so newly created passwords are compatible with md5 auth local password_encryption password_encryption=$(PGPASSWORD=$(cat /run/secrets/postgres_password) "${psql_base[@]}" "SHOW password_encryption;" | tr -d '[:space:]') if [ "${password_encryption}" != "md5" ]; then log "ERROR: password_encryption is '${password_encryption}', expected 'md5'." log " Update postgresql.conf to set password_encryption = 'md5' and restart Postgres," log " or set SKIP_MD5_AUTH_CHECK=true to bypass this enforcement." exit 1 fi # 2) Ensure pg_hba rules use md5 for host connections # Check if pg_hba_file_rules view exists (PG 10+) local has_hba_view has_hba_view=$(PGPASSWORD=$(cat /run/secrets/postgres_password) "${psql_base[@]}" "SELECT to_regclass('pg_catalog.pg_hba_file_rules') IS NOT NULL;" | tr -d '[:space:]') if [ "${has_hba_view}" != "t" ]; then log "WARNING: pg_hba_file_rules view is not available on this Postgres version; skipping pg_hba auth check." return 0 fi # Count any non-md5 host rules local non_md5_count non_md5_count=$(PGPASSWORD=$(cat /run/secrets/postgres_password) "${psql_base[@]}" \ "SELECT count(*) FROM pg_hba_file_rules WHERE type IN ('host','hostssl','hostnossl') AND auth_method <> 'md5';") non_md5_count=$(echo "$non_md5_count" | tr -d '[:space:]') if [ "${non_md5_count}" != "0" ]; then log "ERROR: Found ${non_md5_count} pg_hba host rules that are not using md5 authentication." PGPASSWORD=$(cat /run/secrets/postgres_password) "${psql_base[@]}" \ "SELECT line_number, type, database, user_name, address, netmask, auth_method FROM pg_hba_file_rules WHERE type IN ('host','hostssl','hostnossl') AND auth_method <> 'md5' ORDER BY line_number;" log " Update pg_hba.conf to use 'md5' for host entries (and reload Postgres)," log " or set SKIP_MD5_AUTH_CHECK=true to bypass this enforcement." exit 1 fi log "PostgreSQL authentication check passed (md5)." } # Function to merge CE and EE migrations merge_migrations() { log "Merging CE and EE migrations..." # Create the final migrations directory mkdir -p /app/server/migrations # Copy all CE migrations first if [ -d "/app/server/migrations-ce" ] && [ "$(ls -A /app/server/migrations-ce)" ]; then log "Copying CE migrations..." cp -r /app/server/migrations-ce/* /app/server/migrations/ log "CE migrations copied successfully" else log "No CE migrations found" fi # Overlay EE migrations (overwrites CE files with same name) if [ -d "/app/server/migrations-ee" ] && [ "$(ls -A /app/server/migrations-ee)" ]; then log "Overlaying EE migrations..." cp -r /app/server/migrations-ee/* /app/server/migrations/ log "EE migrations overlaid successfully" else log "No EE migrations found" fi # List final migrations for verification log "Final migration files:" ls -la /app/server/migrations/ | head -20 } # Function to merge CE and EE seeds merge_seeds() { log "Merging CE and EE seeds..." # Create the final seeds directory mkdir -p /app/server/seeds # Copy all CE seeds first if [ -d "/app/server/seeds-ce" ] && [ "$(ls -A /app/server/seeds-ce)" ]; then log "Copying CE seeds..." cp -r /app/server/seeds-ce/* /app/server/seeds/ log "CE seeds copied successfully" else log "No CE seeds found" fi # Overlay EE seeds (overwrites CE files with same name) if [ -d "/app/server/seeds-ee" ] && [ "$(ls -A /app/server/seeds-ee)" ]; then log "Overlaying EE seeds..." cp -r /app/server/seeds-ee/* /app/server/seeds/ log "EE seeds overlaid successfully" else log "No EE seeds found" fi } # Function to check if seeds have been run check_seeds_status() { local has_seeds has_seeds=$(PGPASSWORD=$(cat /run/secrets/postgres_password) psql -h ${DB_HOST:-postgres} -p ${DB_PORT:-5432} -U postgres -d ${DB_NAME_SERVER:-server} -tAc "SELECT EXISTS (SELECT 1 FROM users LIMIT 1);") if [ "$has_seeds" = "t" ]; then return 0 # Seeds have been run else return 1 # Seeds haven't been run fi } # Main setup process main() { log "Starting Enterprise Edition setup..." wait_for_postgres check_postgres_md5_auth # Merge CE and EE migrations/seeds merge_migrations merge_seeds log "Creating database..." node /app/server/setup/create_database.js log "Creating pgboss schema..." PGPASSWORD=$(cat /run/secrets/postgres_password) psql -h ${DB_HOST:-postgres} -p ${DB_PORT:-5432} -U postgres -d ${DB_NAME_SERVER:-server} -c 'CREATE SCHEMA IF NOT EXISTS pgboss;' log "Granting necessary permissions..." PGPASSWORD=$(cat /run/secrets/postgres_password) psql -h ${DB_HOST:-postgres} -p ${DB_PORT:-5432} -U postgres -d ${DB_NAME_SERVER:-server} -c 'GRANT ALL ON SCHEMA public TO postgres;' log "Running merged migrations (CE + EE)..." NODE_ENV=migration npx knex migrate:latest --knexfile /app/server/knexfile.cjs # Check if seeds need to be run if ! check_seeds_status; then log "Running seeds..." NODE_ENV=migration npx knex seed:run --knexfile /app/server/knexfile.cjs || { local exit_code=$? log "ERROR: Seeds failed with exit code $exit_code" exit $exit_code } log "Seeds completed!" else log "Seeds have already been run, skipping..." fi log "Enterprise Edition setup completed!" log "Exiting with success code: 0" exit 0 } # Execute main function main