Spaces:
Paused
Paused
File size: 5,409 Bytes
8003382 8dec6cd 8003382 8dec6cd 8003382 8dec6cd 8003382 8dec6cd 8003382 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | #!/usr/bin/env bash
set -euo pipefail
# Postgres client/server binaries live in /usr/lib/postgresql/<ver>/bin on Debian
export PATH="$(ls -d /usr/lib/postgresql/*/bin 2>/dev/null | head -n1):$PATH"
: "${POSTGRES_USER:=demo}"
: "${POSTGRES_DB:=demo}"
: "${PGPORT:=5432}"
: "${APP_PORT:=7860}"
export PGHOST=/tmp # local clients connect over the unix socket in /tmp (trust auth)
# ---- pick data + backup locations ----
# HF persistent storage (if enabled) mounts a writable /data owned by uid 1000.
# When present we keep BOTH the live cluster AND backups there so they survive restarts.
if [ -d /data ] && [ -w /data ]; then
echo "[start] Persistent /data detected β using it for the cluster and backups."
export PGDATA="${PGDATA:-/data/pgdata}"
export BACKUP_DIR="${BACKUP_DIR:-/data/backups}"
else
echo "[start] No persistent /data β cluster is EPHEMERAL; relying on backup/restore."
export PGDATA="${PGDATA:-/home/appuser/data/pgdata}"
export BACKUP_DIR="${BACKUP_DIR:-/home/appuser/backups}"
fi
mkdir -p "$BACKUP_DIR"
# Generate a strong password if the user didn't supply one as a Space secret
if [ -z "${POSTGRES_PASSWORD:-}" ]; then
POSTGRES_PASSWORD="$(python3 -c 'import secrets;print(secrets.token_urlsafe(18))')"
echo "[start] No POSTGRES_PASSWORD secret set β generated one for this session."
fi
export POSTGRES_PASSWORD POSTGRES_USER POSTGRES_DB PGPORT BACKUP_DIR
FRESH_INIT=0
# ---- initialize the cluster (only when the data dir is empty) ----
if [ ! -s "$PGDATA/PG_VERSION" ]; then
FRESH_INIT=1
echo "[start] Initializing PostgreSQL cluster at $PGDATA"
mkdir -p "$PGDATA"
chmod 700 "$PGDATA"
PWFILE="$(mktemp)"
printf '%s' "$POSTGRES_PASSWORD" > "$PWFILE"
initdb -D "$PGDATA" \
--auth-host=scram-sha-256 \
--auth-local=trust \
-U "$POSTGRES_USER" \
--pwfile="$PWFILE" >/dev/null
rm -f "$PWFILE"
{
echo "listen_addresses = '*'"
echo "port = $PGPORT"
echo "unix_socket_directories = '/tmp'"
} >> "$PGDATA/postgresql.conf"
{
echo "host all all 0.0.0.0/0 scram-sha-256"
echo "host all all ::/0 scram-sha-256"
} >> "$PGDATA/pg_hba.conf"
fi
# ---- start postgres ----
echo "[start] Starting PostgreSQL on port $PGPORT"
pg_ctl -D "$PGDATA" -o "-p $PGPORT" -w -l "$PGDATA/server.log" start
# ensure the target database exists
if ! psql -p "$PGPORT" -U "$POSTGRES_USER" -d postgres -tAc \
"SELECT 1 FROM pg_database WHERE datname='$POSTGRES_DB'" | grep -q 1; then
echo "[start] Creating database '$POSTGRES_DB'"
createdb -p "$PGPORT" -U "$POSTGRES_USER" "$POSTGRES_DB"
fi
# ---- restore the latest backup onto a freshly-initialized cluster ----
# On a fresh boot (ephemeral disk, or first run) pull back the most recent dump so the
# data "survives" restarts. Skipped when the cluster already had data (persistent disk).
if [ "$FRESH_INIT" = "1" ]; then
echo "[start] Fresh cluster β checking for a backup to restore"
python /app/backup.py restore || echo "[start] no restore performed"
fi
# ---- expose Postgres publicly via a TCP tunnel ----
# Default: bore (no signup). If NGROK_AUTHTOKEN is set, use ngrok instead.
# Whichever runs writes the public endpoint to /tmp/tunnel.json for the web app.
rm -f /tmp/tunnel.json
if [ -n "${NGROK_AUTHTOKEN:-}" ]; then
echo "[start] Tunnel: ngrok (NGROK_AUTHTOKEN present)"
ngrok config add-authtoken "$NGROK_AUTHTOKEN" >/dev/null 2>&1 || true
ngrok tcp "$PGPORT" --log=stdout > "$HOME/ngrok.log" 2>&1 &
( set +e
for _ in $(seq 1 30); do
pub="$(curl -fsS localhost:4040/api/tunnels 2>/dev/null | grep -oE 'tcp://[^"]+' | head -1)"
if [ -n "$pub" ]; then
hp="${pub#tcp://}"
printf '{"host":"%s","port":"%s","provider":"ngrok"}' "${hp%%:*}" "${hp##*:}" > /tmp/tunnel.json
echo "[start] ngrok tunnel up at ${hp}"; break
fi
sleep 1
done ) &
else
BORE_HOST="${BORE_HOST:-bore.pub}"
echo "[start] Tunnel: bore -> ${BORE_HOST} (no token needed)"
bore local "$PGPORT" --to "$BORE_HOST" > "$HOME/bore.log" 2>&1 &
( set +e
for _ in $(seq 1 30); do
port="$(grep -aoE 'listening at [^[:space:]]+:[0-9]+' "$HOME/bore.log" 2>/dev/null \
| grep -oE '[0-9]+$' | tail -1)"
if [ -n "$port" ]; then
printf '{"host":"%s","port":"%s","provider":"bore"}' "$BORE_HOST" "$port" > /tmp/tunnel.json
echo "[start] bore tunnel up at ${BORE_HOST}:${port}"; break
fi
sleep 1
done ) &
fi
# ---- keep the Space awake (free Spaces sleep after 48h with NO http traffic) ----
# Postgres/ngrok traffic does NOT count β only requests to this web app do. So we hit
# the PUBLIC url (goes through HF's router => resets the idle timer). SPACE_HOST is
# injected by HF, e.g. user-space.hf.space. Each hit is logged to the Space logs.
if [ -n "${SPACE_HOST:-}" ]; then
echo "[start] Self keep-alive enabled β pinging https://${SPACE_HOST}/keepalive every 20m"
( while true; do
sleep 1200
ts="$(date '+%Y-%m-%d %H:%M:%S')"
code="$(curl -fsS -o /dev/null -w '%{http_code}' \
"https://${SPACE_HOST}/keepalive?src=self" 2>/dev/null || echo 000)"
echo "[keepalive] self ${ts} -> ${code}"
done ) &
fi
# ---- start the web UI (foreground; keeps the container alive) ----
echo "[start] Starting web UI on port $APP_PORT"
cd /app
exec uvicorn main:app --host 0.0.0.0 --port "$APP_PORT"
|