#!/usr/bin/env bash set -e log() { echo "[$(date -u +'%Y-%m-%dT%H:%M:%SZ')] $*" } log "======================================" log "PostgreSQL General HF Space starting" log "======================================" if [ -z "$POSTGRES_PASSWORD" ]; then log "ERROR: POSTGRES_PASSWORD is not set." log "Please set POSTGRES_PASSWORD in Hugging Face Space Secrets." exit 1 fi export POSTGRES_USER="${POSTGRES_USER:-admin}" export POSTGRES_DB="${POSTGRES_DB:-appdb}" # PostgreSQL runtime data directory: use Space local disk. export PGDATA="${PGDATA:-/home/user/pgdata}" # Mounted storage bucket directories. export DATA_DIR="${DATA_DIR:-/data}" export BACKUP_DIR="${BACKUP_DIR:-/data/backups}" export USER_FILE_DIR="${USER_FILE_DIR:-/data/files}" export EXPORT_DIR="${EXPORT_DIR:-/data/exports}" export GENERATED_DIR="${GENERATED_DIR:-/data/generated}" # Automatic backup interval, default: 1 hour. export BACKUP_INTERVAL_SECONDS="${BACKUP_INTERVAL_SECONDS:-3600}" log "Runtime configuration:" log "POSTGRES_USER=$POSTGRES_USER" log "POSTGRES_DB=$POSTGRES_DB" log "PGDATA=$PGDATA" log "DATA_DIR=$DATA_DIR" log "BACKUP_DIR=$BACKUP_DIR" log "USER_FILE_DIR=$USER_FILE_DIR" log "EXPORT_DIR=$EXPORT_DIR" log "GENERATED_DIR=$GENERATED_DIR" log "BACKUP_INTERVAL_SECONDS=$BACKUP_INTERVAL_SECONDS" log "Creating directories..." mkdir -p "$PGDATA" mkdir -p "$BACKUP_DIR" mkdir -p "$USER_FILE_DIR" mkdir -p "$EXPORT_DIR" mkdir -p "$GENERATED_DIR" # PostgreSQL needs a private data directory. Allow permission changes to fail on restricted runtimes. chown -R postgres:postgres "$PGDATA" || true chmod 700 "$PGDATA" || true chmod -R 755 "$DATA_DIR" || true log "Directory status:" ls -ld "$PGDATA" || true ls -ld "$DATA_DIR" || true ls -ld "$BACKUP_DIR" || true ls -ld "$USER_FILE_DIR" || true ls -ld "$EXPORT_DIR" || true ls -ld "$GENERATED_DIR" || true RESTORE_BACKUP=0 if [ ! -s "$PGDATA/PG_VERSION" ]; then log "No existing PostgreSQL data directory found." if [ -f "$BACKUP_DIR/latest.sql" ]; then log "Backup file found: $BACKUP_DIR/latest.sql" log "Database will be restored after PostgreSQL initialization." RESTORE_BACKUP=1 else log "No backup file found. A new database will be initialized." fi else log "Existing PostgreSQL data directory found." fi log "Starting PostgreSQL..." docker-entrypoint.sh postgres & POSTGRES_PID=$! log "Waiting for PostgreSQL to become ready..." until pg_isready -h 127.0.0.1 -p 5432 -U "$POSTGRES_USER"; do sleep 2 done log "PostgreSQL is ready." if [ "$RESTORE_BACKUP" = "1" ]; then log "Restoring database from $BACKUP_DIR/latest.sql ..." PGPASSWORD="$POSTGRES_PASSWORD" psql \ -h 127.0.0.1 \ -p 5432 \ -U "$POSTGRES_USER" \ -d "$POSTGRES_DB" \ < "$BACKUP_DIR/latest.sql" || { log "WARNING: Restore failed. Please check backup file." } log "Restore process finished." fi create_backup() { TIMESTAMP="$(date +%Y%m%d_%H%M%S)" BACKUP_FILE="$BACKUP_DIR/backup_${POSTGRES_DB}_${TIMESTAMP}.sql" LATEST_FILE="$BACKUP_DIR/latest.sql" log "Creating backup: $BACKUP_FILE" PGPASSWORD="$POSTGRES_PASSWORD" pg_dump \ -h 127.0.0.1 \ -p 5432 \ -U "$POSTGRES_USER" \ -d "$POSTGRES_DB" \ > "$BACKUP_FILE.tmp" mv "$BACKUP_FILE.tmp" "$BACKUP_FILE" cp "$BACKUP_FILE" "$LATEST_FILE" log "Backup saved: $BACKUP_FILE and $LATEST_FILE" } backup_loop() { log "Backup loop started. Interval: ${BACKUP_INTERVAL_SECONDS}s" while true; do sleep "$BACKUP_INTERVAL_SECONDS" if pg_isready -h 127.0.0.1 -p 5432 -U "$POSTGRES_USER"; then create_backup || log "WARNING: Backup failed." else log "WARNING: PostgreSQL is not ready. Backup skipped." fi done } if pg_isready -h 127.0.0.1 -p 5432 -U "$POSTGRES_USER"; then create_backup || log "WARNING: Initial backup failed." fi backup_loop & log "Checking PHP PostgreSQL extension for optional Adminer usage..." if php -m | grep -Ei "pgsql|pdo_pgsql" > /dev/null; then log "PHP PostgreSQL extension loaded." else log "WARNING: PHP PostgreSQL extension is not loaded. Adminer may not work." fi log "Starting FastAPI on port 7860..." log "API docs: /docs" log "Health: /api/health" uvicorn api:app --host 0.0.0.0 --port 7860 wait "$POSTGRES_PID"