Datasourceforcryptocurrency-6 / sync-entrypoint.sh
Really-amin's picture
Fix persistence: restore/snapshot state via HF dataset; pin base image to 3.8.50
75478bc verified
Raw History Blame Contribute Delete
5.21 kB
#!/bin/bash
set -e
# ── Secrets ───────────────────────────────────────────────────────────────────
# Set these as HF Secrets in Space Settings β†’ Variables and secrets.
# If unset, random values are generated β€” auth breaks on every restart!
if [ -z "$JWT_SECRET" ]; then
echo "[WARN] JWT_SECRET not set β€” generating ephemeral value. Set it as a HF Secret."
export JWT_SECRET="$(openssl rand -base64 48)"
fi
if [ -z "$API_KEY_SECRET" ]; then
echo "[WARN] API_KEY_SECRET not set β€” generating ephemeral value. Set it as a HF Secret."
export API_KEY_SECRET="$(openssl rand -hex 32)"
fi
if [ -z "$OMNIROUTE_WS_BRIDGE_SECRET" ]; then
echo "[WARN] OMNIROUTE_WS_BRIDGE_SECRET not set β€” generating ephemeral value."
export OMNIROUTE_WS_BRIDGE_SECRET="$(openssl rand -hex 32)"
fi
# ── Core config ───────────────────────────────────────────────────────────────
export PORT="${PORT:-7860}"
export HOSTNAME="0.0.0.0"
export INITIAL_PASSWORD="${INITIAL_PASSWORD:-omniroute}"
export DATA_DIR="${DATA_DIR:-/app/data}"
export AUTH_COOKIE_SECURE="${AUTH_COOKIE_SECURE:-false}"
export NODE_ENV="${NODE_ENV:-production}"
export STORAGE_DRIVER="${STORAGE_DRIVER:-sqlite}"
# ── Public URL ────────────────────────────────────────────────────────────────
if [ -n "$SPACE_HOST" ]; then
export NEXT_PUBLIC_BASE_URL="https://${SPACE_HOST}"
export BASE_URL="https://${SPACE_HOST}"
else
export NEXT_PUBLIC_BASE_URL="${NEXT_PUBLIC_BASE_URL:-http://0.0.0.0:${PORT}}"
export BASE_URL="${BASE_URL:-${NEXT_PUBLIC_BASE_URL}}"
fi
# ── Storage dir must exist before restore ─────────────────────────────────────
mkdir -p "$DATA_DIR"
# ── HF Dataset persistence (Node, zero-dependency) ────────────────────────────
# Set HF_TOKEN + HF_STATE_DATASET as HF Secrets to persist the DB across
# restarts and rebuilds. Unlike the old python3/huggingface_hub block, hf-sync
# needs nothing beyond the Node runtime that already ships in this image, it
# pulls a WAL-consistent snapshot via OmniRoute's own export API, and it uploads
# on an interval AND on shutdown (SIGTERM) so a restart can't drop config.
if [ -n "$HF_TOKEN" ] && [ -n "$HF_STATE_DATASET" ]; then
echo "[sync] Restoring state from HF dataset: $HF_STATE_DATASET"
node /app/hf-sync.mjs restore || echo "[sync] restore reported an error (continuing with local/fresh DB)"
else
echo "[sync] HF persistence disabled"
echo "[sync] Set HF_TOKEN and HF_STATE_DATASET to enable persistence"
fi
echo "================================================"
echo " OmniRoute starting on port $PORT"
echo " Dashboard: ${NEXT_PUBLIC_BASE_URL}"
echo " Data dir: $DATA_DIR"
echo "================================================"
# ── Run the app as a child so we can snapshot on shutdown ─────────────────────
# HF Spaces delivers SIGTERM on restart/rebuild AND when a free Space is put to
# sleep. `exec node …` would replace this shell and make a shutdown snapshot
# impossible, so we background the app, trap the signal, take one final upload,
# then forward the signal on.
#
# Ordering matters and is deliberate: we snapshot the DB via OmniRoute's own
# export API *while the app is still alive*, and only THEN stop the app. The
# final snapshot is bounded by `timeout` (and a low per-request timeout) so on
# slow cpu-basic hardware it aborts cleanly β€” leaving the dataset on the last
# good commit β€” rather than being frozen mid-upload when SIGKILL lands. The
# periodic daemon is the real safety net; this is best-effort on top.
APP_PID=""
SHUTDOWN_BUDGET="${HF_SYNC_SHUTDOWN_SECONDS:-8}"
shutdown() {
echo "[sync] shutdown signal received"
if [ -n "$HF_TOKEN" ] && [ -n "$HF_STATE_DATASET" ]; then
echo "[sync] taking final snapshot before exit (budget ${SHUTDOWN_BUDGET}s)"
HF_SYNC_HTTP_TIMEOUT_MS=$(( (SHUTDOWN_BUDGET - 1) * 1000 )) \
timeout "${SHUTDOWN_BUDGET}s" node /app/hf-sync.mjs snapshot \
|| echo "[sync] final snapshot skipped/failed (dataset stays on last good commit)"
fi
if [ -n "$APP_PID" ]; then kill -TERM "$APP_PID" 2>/dev/null || true; wait "$APP_PID" 2>/dev/null || true; fi
exit 0
}
trap shutdown TERM INT
# Periodic snapshotter (no-ops immediately if persistence is disabled).
node /app/hf-sync.mjs daemon &
SYNC_PID=$!
node dev/run-standalone.mjs &
APP_PID=$!
# `wait` returns when the trap fires; loop so we keep waiting until the app
# actually exits rather than returning on the first delivered signal.
while kill -0 "$APP_PID" 2>/dev/null; do
wait "$APP_PID" && break
done
EXIT_CODE=$?
kill -TERM "$SYNC_PID" 2>/dev/null || true
echo "[sync] app exited with code $EXIT_CODE"
exit $EXIT_CODE