mirror of
https://github.com/kikootwo/ReadMeABook.git
synced 2026-06-02 20:30:10 +00:00
dfc34df3d1
Introduce file and directory permission settings (fileChmod, dirChmod) end-to-end. UI: new controls in Paths settings with octal validation and defaults (664/775). API: GET exposes defaults; PUT validates octal strings and upserts configuration keys (file_chmod, dir_chmod) and clears related cache keys. Runtime: read config values in file utilities and services (FileOrganizer, direct-download, chapter-merger, epub-fixer) to apply mkdir modes and chmod files/dirs; FileOrganizer now accepts fileMode/dirMode and getFileOrganizer reads/parses DB settings. Docker: add UMASK option to docker-compose and propagate/apply UMASK in entrypoint/app-start scripts. Tests: update mocks to account for config service usage.
476 lines
18 KiB
Bash
476 lines
18 KiB
Bash
#!/bin/bash
|
||
set -e
|
||
|
||
echo "🚀 ReadMeABook Unified Container Starting..."
|
||
|
||
# ============================================================================
|
||
# PUID/PGID USER REMAPPING (Hybrid approach with gosu)
|
||
# ============================================================================
|
||
# Hybrid approach to support user file ownership while maintaining PostgreSQL compatibility:
|
||
# - postgres user: Keep UID 103 (required by PostgreSQL), remap GID → PGID
|
||
# - redis user: Remap UID → PUID, GID → PGID (also uses gosu at runtime)
|
||
# - node user: Remap UID → PUID, GID → PGID (also uses gosu at runtime)
|
||
#
|
||
# NOTE: We use gosu in app-start.sh and redis-start.sh to ensure the process
|
||
# actually runs with the correct UID:GID. This fixes issues where PUID collides
|
||
# with existing system users (e.g., PUID=65534 collides with 'nobody').
|
||
#
|
||
# Result:
|
||
# - PostgreSQL data (103:PGID) - postgres user with shared group
|
||
# - Redis/App/Downloads/Media (PUID:PGID) - your user owns everything else
|
||
# - All files accessible via PGID group permissions
|
||
|
||
PUID=${PUID:-}
|
||
PGID=${PGID:-}
|
||
|
||
if [ -n "$PUID" ] && [ -n "$PGID" ]; then
|
||
echo "🔧 PUID/PGID detected - Configuring hybrid user mapping for $PUID:$PGID"
|
||
echo ""
|
||
|
||
# Get current UIDs/GIDs before remapping
|
||
POSTGRES_UID=$(id -u postgres)
|
||
POSTGRES_GID=$(id -g postgres)
|
||
REDIS_UID=$(id -u redis)
|
||
NODE_UID=$(id -u node)
|
||
|
||
echo " Current UIDs: postgres=$POSTGRES_UID redis=$REDIS_UID node=$NODE_UID"
|
||
echo ""
|
||
echo " Applying hybrid mapping strategy:"
|
||
echo " - postgres: Keep UID $POSTGRES_UID, remap GID → $PGID (PostgreSQL compatibility)"
|
||
echo " - redis: Remap to $PUID:$PGID (full user ownership)"
|
||
echo " - node: Remap to $PUID:$PGID (full user ownership)"
|
||
echo ""
|
||
|
||
# Step 1: Remap postgres group to PGID (keep UID 103 for PostgreSQL compatibility)
|
||
echo " [1/3] Remapping postgres group to $PGID..."
|
||
groupmod -o -g "$PGID" postgres 2>/dev/null || true
|
||
# Postgres user keeps its UID but gets the new GID
|
||
usermod -g "$PGID" postgres
|
||
|
||
# Step 2: Remap redis user to PUID:PGID
|
||
echo " [2/3] Remapping redis user to $PUID:$PGID..."
|
||
groupmod -o -g "$PGID" redis 2>/dev/null || true
|
||
usermod -o -u "$PUID" -g "$PGID" redis
|
||
|
||
# Step 3: Remap node user to PUID:PGID
|
||
echo " [3/3] Remapping node user to $PUID:$PGID..."
|
||
groupmod -o -g "$PGID" node 2>/dev/null || true
|
||
usermod -o -u "$PUID" -g "$PGID" node
|
||
|
||
echo ""
|
||
echo "✅ User mapping complete!"
|
||
echo ""
|
||
echo " File ownership will be:"
|
||
echo " - PostgreSQL data (/var/lib/postgresql/data): $POSTGRES_UID:$PGID"
|
||
echo " - Redis data (/var/lib/redis): $PUID:$PGID"
|
||
echo " - App config (/app/config): $PUID:$PGID"
|
||
echo " - Downloads (/downloads): $PUID:$PGID"
|
||
echo " - Media (/media): $PUID:$PGID"
|
||
echo ""
|
||
echo " On your host, these will appear as:"
|
||
echo " - PostgreSQL: UID $POSTGRES_UID, GID $PGID (readable via group)"
|
||
echo " - Everything else: Your user ($PUID:$PGID)"
|
||
echo ""
|
||
|
||
# For LXC users, provide helpful mapping info
|
||
if [ "$POSTGRES_UID" != "$PUID" ]; then
|
||
echo " 📝 LXC Note: You need to map container UID $POSTGRES_UID to an accessible host UID"
|
||
echo " Example lxc.idmap configuration:"
|
||
echo " lxc.idmap: u 0 100000 $POSTGRES_UID"
|
||
echo " lxc.idmap: g 0 100000 $POSTGRES_UID"
|
||
echo " lxc.idmap: u $POSTGRES_UID $POSTGRES_UID 1 # Passthrough postgres UID"
|
||
echo " lxc.idmap: g $POSTGRES_UID 100$POSTGRES_UID 1"
|
||
echo " lxc.idmap: u $((POSTGRES_UID + 1)) 100$((POSTGRES_UID + 1)) 65432"
|
||
echo " lxc.idmap: g $((POSTGRES_UID + 1)) 100$((POSTGRES_UID + 1)) 65432"
|
||
echo ""
|
||
fi
|
||
else
|
||
echo "ℹ️ PUID/PGID not set - using default system user IDs"
|
||
echo " Default ownership:"
|
||
echo " - PostgreSQL data: postgres (UID 103)"
|
||
echo " - Redis data: redis (UID 102)"
|
||
echo " - App/Downloads: node (UID 1000)"
|
||
echo ""
|
||
echo " To customize ownership, set PUID and PGID environment variables"
|
||
echo " Example: PUID=1000 PGID=1000"
|
||
echo ""
|
||
fi
|
||
|
||
# ============================================================================
|
||
# GENERATE DEFAULT SECRETS IF NOT PROVIDED
|
||
# ============================================================================
|
||
generate_secret() {
|
||
openssl rand -base64 32
|
||
}
|
||
|
||
# URL encode function for database password
|
||
urlencode() {
|
||
local string="$1"
|
||
local strlen=${#string}
|
||
local encoded=""
|
||
local pos c o
|
||
|
||
for (( pos=0 ; pos<strlen ; pos++ )); do
|
||
c=${string:$pos:1}
|
||
case "$c" in
|
||
[-_.~a-zA-Z0-9] ) o="${c}" ;;
|
||
* ) printf -v o '%%%02x' "'$c"
|
||
esac
|
||
encoded+="${o}"
|
||
done
|
||
echo "${encoded}"
|
||
}
|
||
|
||
# Secrets file location (persisted on volume)
|
||
SECRETS_FILE="/app/config/.secrets"
|
||
|
||
# Load existing secrets from file if it exists
|
||
if [ -f "$SECRETS_FILE" ]; then
|
||
echo "🔑 Loading persisted secrets from $SECRETS_FILE"
|
||
source "$SECRETS_FILE"
|
||
fi
|
||
|
||
# Generate secrets only if not already set (from env, file, or generate new)
|
||
export POSTGRES_PASSWORD="${POSTGRES_PASSWORD:-$(generate_secret)}"
|
||
export JWT_SECRET="${JWT_SECRET:-$(generate_secret)}"
|
||
export JWT_REFRESH_SECRET="${JWT_REFRESH_SECRET:-$(generate_secret)}"
|
||
export CONFIG_ENCRYPTION_KEY="${CONFIG_ENCRYPTION_KEY:-$(generate_secret)}"
|
||
|
||
# Persist secrets to file for future container restarts
|
||
cat > "$SECRETS_FILE" <<EOF
|
||
# Auto-generated secrets - DO NOT DELETE THIS FILE
|
||
# Generated on: $(date)
|
||
POSTGRES_PASSWORD="$POSTGRES_PASSWORD"
|
||
JWT_SECRET="$JWT_SECRET"
|
||
JWT_REFRESH_SECRET="$JWT_REFRESH_SECRET"
|
||
CONFIG_ENCRYPTION_KEY="$CONFIG_ENCRYPTION_KEY"
|
||
EOF
|
||
|
||
chmod 600 "$SECRETS_FILE"
|
||
echo "✅ Secrets persisted to $SECRETS_FILE"
|
||
|
||
# Set other defaults
|
||
export POSTGRES_USER="${POSTGRES_USER:-readmeabook}"
|
||
export POSTGRES_DB="${POSTGRES_DB:-readmeabook}"
|
||
export PLEX_CLIENT_IDENTIFIER="${PLEX_CLIENT_IDENTIFIER:-readmeabook-$(openssl rand -hex 8)}"
|
||
export PLEX_PRODUCT_NAME="${PLEX_PRODUCT_NAME:-ReadMeABook}"
|
||
export LOG_LEVEL="${LOG_LEVEL:-info}"
|
||
|
||
# ============================================================================
|
||
# DETECT EXTERNAL SERVICES
|
||
# ============================================================================
|
||
# Check if user provided external DATABASE_URL or REDIS_URL
|
||
USE_EXTERNAL_POSTGRES=false
|
||
USE_EXTERNAL_REDIS=false
|
||
|
||
if [ -n "$DATABASE_URL" ]; then
|
||
DB_HOST=$(echo "$DATABASE_URL" | sed -n 's|.*@\([^:/]*\).*|\1|p')
|
||
if [ "$DB_HOST" != "127.0.0.1" ] && [ "$DB_HOST" != "localhost" ]; then
|
||
USE_EXTERNAL_POSTGRES=true
|
||
echo "ℹ️ External PostgreSQL detected at $DB_HOST"
|
||
fi
|
||
fi
|
||
|
||
if [ -n "$REDIS_URL" ]; then
|
||
# Extract host from REDIS_URL - handles both redis://host:port and redis://:password@host:port
|
||
if echo "$REDIS_URL" | grep -q '@'; then
|
||
REDIS_HOST=$(echo "$REDIS_URL" | sed -n 's|.*@\([^:/]*\).*|\1|p')
|
||
else
|
||
REDIS_HOST=$(echo "$REDIS_URL" | sed -n 's|redis://\([^:/]*\).*|\1|p')
|
||
fi
|
||
if [ -n "$REDIS_HOST" ] && [ "$REDIS_HOST" != "127.0.0.1" ] && [ "$REDIS_HOST" != "localhost" ]; then
|
||
USE_EXTERNAL_REDIS=true
|
||
echo "ℹ️ External Redis detected at $REDIS_HOST"
|
||
fi
|
||
fi
|
||
|
||
# ============================================================================
|
||
# INITIALIZE POSTGRESQL (only if using internal PostgreSQL)
|
||
# ============================================================================
|
||
if [ "$USE_EXTERNAL_POSTGRES" = "false" ]; then
|
||
echo "📦 Configuring internal PostgreSQL..."
|
||
PGDATA="/var/lib/postgresql/data"
|
||
PG_WAS_EMPTY=0
|
||
|
||
# Ensure correct ownership of data directories (critical for bind mounts)
|
||
echo "🔧 Setting up directory permissions..."
|
||
|
||
# PostgreSQL directories - owned by postgres user, group accessible
|
||
if ! chown -R postgres:postgres "$PGDATA" /var/run/postgresql 2>/dev/null; then
|
||
echo ""
|
||
echo "❌ ERROR: Failed to set ownership on PostgreSQL directories"
|
||
echo ""
|
||
echo " This usually happens when using bind mounts on incompatible filesystems."
|
||
echo ""
|
||
echo " Common causes:"
|
||
echo " - WSL2: Project on Windows filesystem (/mnt/c/...)"
|
||
echo " - NFS/CIFS: Mount without proper permission support"
|
||
echo ""
|
||
echo " Solutions:"
|
||
echo ""
|
||
echo " 1. Use Docker named volumes (recommended for WSL2):"
|
||
echo " In docker-compose.yml, change:"
|
||
echo " - ./pgdata:/var/lib/postgresql/data"
|
||
echo " To:"
|
||
echo " - pgdata:/var/lib/postgresql/data"
|
||
echo " Then add at bottom:"
|
||
echo " volumes:"
|
||
echo " pgdata:"
|
||
echo ""
|
||
echo " 2. Move project to Linux filesystem (WSL2):"
|
||
echo " mkdir -p ~/readmeabook && cd ~/readmeabook"
|
||
echo " # Copy docker-compose.yml and restart"
|
||
echo ""
|
||
echo " 3. Pre-create directories with correct ownership:"
|
||
echo " mkdir -p pgdata redis config cache"
|
||
echo " # Let Docker create them on first run"
|
||
echo ""
|
||
exit 1
|
||
fi
|
||
|
||
if [ -n "$PGID" ]; then
|
||
# With PUID/PGID: Use 750 (owner rwx, group rx) for PostgreSQL data
|
||
# This allows the PGID group to read PostgreSQL files if needed
|
||
chmod 750 "$PGDATA"
|
||
chmod 775 /var/run/postgresql
|
||
else
|
||
# Without PUID/PGID: Use strict 700 permissions (owner only)
|
||
chmod 700 "$PGDATA"
|
||
chmod 775 /var/run/postgresql
|
||
fi
|
||
else
|
||
echo "⏭️ Skipping internal PostgreSQL setup (using external database)"
|
||
fi
|
||
|
||
# Redis directory - owned by redis user (remapped to PUID:PGID if set)
|
||
if [ "$USE_EXTERNAL_REDIS" = "false" ]; then
|
||
if ! chown -R redis:redis /var/lib/redis 2>/dev/null; then
|
||
echo ""
|
||
echo "❌ ERROR: Failed to set ownership on Redis directory"
|
||
echo " See solutions above for PostgreSQL directories"
|
||
echo ""
|
||
exit 1
|
||
fi
|
||
chmod 770 /var/lib/redis
|
||
else
|
||
echo "⏭️ Skipping internal Redis setup (using external Redis)"
|
||
fi
|
||
|
||
# App directories - owned by node user (remapped to PUID:PGID if set)
|
||
# These need group write permissions for shared access
|
||
if ! chown -R node:node /app/config /app/cache 2>/dev/null; then
|
||
echo ""
|
||
echo "❌ ERROR: Failed to set ownership on app directories"
|
||
echo " See solutions above for PostgreSQL directories"
|
||
echo ""
|
||
exit 1
|
||
fi
|
||
chmod 775 /app/config /app/cache
|
||
|
||
echo "✅ Directory permissions configured"
|
||
|
||
if [ "$USE_EXTERNAL_POSTGRES" = "false" ]; then
|
||
# Only initialize/setup PostgreSQL if using internal instance
|
||
if [ ! -f "$PGDATA/PG_VERSION" ]; then
|
||
PG_WAS_EMPTY=1
|
||
echo "📦 Initializing PostgreSQL database..."
|
||
su - postgres -c "/usr/lib/postgresql/16/bin/initdb -D $PGDATA"
|
||
|
||
# Configure PostgreSQL for local access
|
||
echo "host all all 127.0.0.1/32 trust" >> "$PGDATA/pg_hba.conf"
|
||
echo "host all all ::1/128 trust" >> "$PGDATA/pg_hba.conf"
|
||
echo "local all all trust" >> "$PGDATA/pg_hba.conf"
|
||
|
||
# Update postgresql.conf for performance
|
||
cat >> "$PGDATA/postgresql.conf" <<EOF
|
||
listen_addresses = '127.0.0.1'
|
||
max_connections = 100
|
||
shared_buffers = 128MB
|
||
work_mem = 4MB
|
||
maintenance_work_mem = 64MB
|
||
effective_cache_size = 512MB
|
||
log_destination = 'stderr'
|
||
logging_collector = off
|
||
EOF
|
||
|
||
echo "✅ PostgreSQL initialized"
|
||
else
|
||
echo "✅ PostgreSQL data directory already exists"
|
||
fi
|
||
|
||
# ========================================================================
|
||
# START POSTGRESQL TEMPORARILY TO CREATE USER/DATABASE
|
||
# ========================================================================
|
||
echo "🔧 Starting PostgreSQL for setup..."
|
||
su - postgres -c "/usr/lib/postgresql/16/bin/pg_ctl -D $PGDATA -w start -o '-c listen_addresses=127.0.0.1'"
|
||
|
||
# Wait for PostgreSQL to be ready
|
||
for i in {1..30}; do
|
||
if su - postgres -c "/usr/lib/postgresql/16/bin/pg_isready -h 127.0.0.1 -p 5432" > /dev/null 2>&1; then
|
||
echo "✅ PostgreSQL is ready"
|
||
break
|
||
fi
|
||
echo "⏳ Waiting for PostgreSQL to be ready... ($i/30)"
|
||
sleep 1
|
||
done
|
||
|
||
# Always ensure user and database exist (safe due to IF NOT EXISTS checks)
|
||
# This handles cases where data directory exists but user/database don't
|
||
echo "👤 Ensuring database user and database exist..."
|
||
su - postgres -c "psql -h 127.0.0.1 -U postgres" <<EOF
|
||
DO \$\$
|
||
BEGIN
|
||
IF NOT EXISTS (SELECT FROM pg_user WHERE usename = '$POSTGRES_USER') THEN
|
||
CREATE USER $POSTGRES_USER WITH PASSWORD '$POSTGRES_PASSWORD';
|
||
RAISE NOTICE 'Created user $POSTGRES_USER';
|
||
ELSE
|
||
RAISE NOTICE 'User $POSTGRES_USER already exists';
|
||
END IF;
|
||
END
|
||
\$\$;
|
||
|
||
SELECT 'CREATE DATABASE $POSTGRES_DB' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = '$POSTGRES_DB')\\gexec
|
||
|
||
GRANT ALL PRIVILEGES ON DATABASE $POSTGRES_DB TO $POSTGRES_USER;
|
||
ALTER DATABASE $POSTGRES_DB OWNER TO $POSTGRES_USER;
|
||
EOF
|
||
|
||
if [ "$PG_WAS_EMPTY" -eq 1 ]; then
|
||
echo "✅ Database initialized and setup complete"
|
||
else
|
||
echo "✅ Database user and permissions verified"
|
||
fi
|
||
|
||
fi
|
||
|
||
# ============================================================================
|
||
# SET ENVIRONMENT VARIABLES FOR APP
|
||
# ============================================================================
|
||
# Set DATABASE_URL and REDIS_URL based on whether we're using internal or external services
|
||
if [ "$USE_EXTERNAL_POSTGRES" = "false" ]; then
|
||
# URL-encode the password to handle special characters
|
||
ENCODED_PASSWORD=$(urlencode "$POSTGRES_PASSWORD")
|
||
export DATABASE_URL="postgresql://$POSTGRES_USER:$ENCODED_PASSWORD@127.0.0.1:5432/$POSTGRES_DB"
|
||
echo "✅ Using internal PostgreSQL (127.0.0.1:5432)"
|
||
else
|
||
# DATABASE_URL already set by user - do not modify
|
||
echo "✅ Using external DATABASE_URL: $(echo "$DATABASE_URL" | sed 's|//.*@|//***@|')"
|
||
fi
|
||
|
||
if [ "$USE_EXTERNAL_REDIS" = "false" ]; then
|
||
export REDIS_URL="redis://127.0.0.1:6379"
|
||
echo "✅ Using internal Redis (127.0.0.1:6379)"
|
||
else
|
||
# REDIS_URL already set by user - do not modify
|
||
echo "✅ Using external REDIS_URL: $(echo "$REDIS_URL" | sed 's|//.*@|//***@|')"
|
||
fi
|
||
|
||
export NODE_ENV="production"
|
||
export PORT="3030"
|
||
export HOSTNAME="0.0.0.0"
|
||
|
||
# Persist environment variables for supervisord and child processes
|
||
# PUID/PGID are critical for gosu-based user switching in app-start.sh and redis-start.sh
|
||
cat > /etc/environment <<EOF
|
||
DATABASE_URL=$DATABASE_URL
|
||
REDIS_URL=$REDIS_URL
|
||
USE_EXTERNAL_POSTGRES=$USE_EXTERNAL_POSTGRES
|
||
USE_EXTERNAL_REDIS=$USE_EXTERNAL_REDIS
|
||
JWT_SECRET=$JWT_SECRET
|
||
JWT_REFRESH_SECRET=$JWT_REFRESH_SECRET
|
||
CONFIG_ENCRYPTION_KEY=$CONFIG_ENCRYPTION_KEY
|
||
PLEX_CLIENT_IDENTIFIER=$PLEX_CLIENT_IDENTIFIER
|
||
PLEX_PRODUCT_NAME=$PLEX_PRODUCT_NAME
|
||
LOG_LEVEL=$LOG_LEVEL
|
||
NODE_ENV=$NODE_ENV
|
||
PORT=$PORT
|
||
HOSTNAME=$HOSTNAME
|
||
PUID=${PUID:-}
|
||
PGID=${PGID:-}
|
||
UMASK=${UMASK:-}
|
||
ROOTLESS_CONTAINER=${ROOTLESS_CONTAINER:-}
|
||
EOF
|
||
|
||
echo "✅ Environment configured"
|
||
|
||
# ============================================================================
|
||
# RUN PRISMA MIGRATIONS
|
||
# ============================================================================
|
||
if [ "$USE_EXTERNAL_POSTGRES" = "true" ]; then
|
||
echo "⚠️ Running schema sync against EXTERNAL database - prisma db push --accept-data-loss"
|
||
echo " This runs on every container start. Ensure your external database is backed up."
|
||
fi
|
||
echo "🔄 Running Prisma migrations..."
|
||
cd /app
|
||
su - node -c "cd /app && DATABASE_URL='$DATABASE_URL' npx prisma db push --skip-generate --accept-data-loss" || echo "⚠️ Migrations may have failed, continuing..."
|
||
|
||
# Run data migrations (run-once SQL scripts tracked in _data_migrations table)
|
||
echo "🔄 Running data migrations..."
|
||
|
||
for sql_file in /app/prisma/data-migrations/*.sql; do
|
||
if [ -f "$sql_file" ]; then
|
||
migration_name=$(basename "$sql_file")
|
||
|
||
already_run=$(psql "$DATABASE_URL" -tA -c "SELECT 1 FROM _data_migrations WHERE name = '$migration_name' LIMIT 1;")
|
||
if [ "$already_run" = "1" ]; then
|
||
echo " Skipping $migration_name (already executed)"
|
||
continue
|
||
fi
|
||
|
||
echo " Running $migration_name..."
|
||
if su - node -c "cd /app && DATABASE_URL='$DATABASE_URL' npx prisma db execute --schema prisma/schema.prisma --file '$sql_file'"; then
|
||
psql "$DATABASE_URL" -c "INSERT INTO _data_migrations (name) VALUES ('$migration_name');"
|
||
echo " ✅ $migration_name completed"
|
||
else
|
||
echo "⚠️ Data migration $migration_name failed, will retry on next start"
|
||
fi
|
||
fi
|
||
done
|
||
|
||
# Stop internal PostgreSQL (supervisord will restart it via wrapper)
|
||
if [ "$USE_EXTERNAL_POSTGRES" = "false" ]; then
|
||
echo "🔧 Stopping temporary PostgreSQL instance..."
|
||
su - postgres -c "/usr/lib/postgresql/16/bin/pg_ctl -D $PGDATA stop -m fast"
|
||
fi
|
||
|
||
# ============================================================================
|
||
# DISPLAY STARTUP INFO
|
||
# ============================================================================
|
||
echo ""
|
||
echo "============================================"
|
||
echo "🎉 ReadMeABook is starting!"
|
||
echo "============================================"
|
||
echo "📍 Access your application at: http://localhost:3030"
|
||
echo ""
|
||
if [ "$POSTGRES_PASSWORD" = "$(generate_secret)" ]; then
|
||
echo "🔐 Auto-generated secrets (first run):"
|
||
echo " - Database password: $POSTGRES_PASSWORD"
|
||
echo " - Store these securely if you need to access the database directly"
|
||
fi
|
||
echo ""
|
||
echo "📊 Services starting:"
|
||
if [ "$USE_EXTERNAL_POSTGRES" = "false" ]; then
|
||
echo " - PostgreSQL (internal, 127.0.0.1:5432)"
|
||
else
|
||
echo " - PostgreSQL (external - local instance disabled)"
|
||
fi
|
||
if [ "$USE_EXTERNAL_REDIS" = "false" ]; then
|
||
echo " - Redis (internal, 127.0.0.1:6379, UID:GID=${PUID:-102}:${PGID:-102})"
|
||
else
|
||
echo " - Redis (external - local instance disabled)"
|
||
fi
|
||
echo " - Next.js App (port 3030, UID:GID=${PUID:-1000}:${PGID:-1000})"
|
||
if [ "${ROOTLESS_CONTAINER}" = "true" ]; then
|
||
echo ""
|
||
echo "🔐 ROOTLESS_CONTAINER=true - gosu will be skipped (user namespace handles UID mapping)"
|
||
elif [ -n "$PUID" ] && [ -n "$PGID" ]; then
|
||
echo ""
|
||
echo "🔐 Using gosu for reliable UID:GID switching"
|
||
echo " App and Redis will run as $PUID:$PGID"
|
||
fi
|
||
echo "============================================"
|
||
echo ""
|
||
|
||
# Start supervisord with all services
|
||
exec "$@"
|