File size: 6,640 Bytes
8059bf0 | 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 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 | #!/bin/sh
set -eu
PATH="/usr/lib/postgresql16/bin:$PATH"
log() {
printf '%s\n' "$*"
}
DATA_ROOT="${DATA_ROOT:-/data}"
APP_DATA_DIR="${APP_DATA_DIR:-$DATA_ROOT/data}"
POSTGRES_DATA_DIR="${POSTGRES_DATA_DIR:-$DATA_ROOT/postgres}"
REDIS_DATA_DIR="${REDIS_DATA_DIR:-$DATA_ROOT/redis}"
RUNTIME_DIR="${RUNTIME_DIR:-$DATA_ROOT/run}"
LOG_DIR="${LOG_DIR:-$DATA_ROOT/logs}"
POSTGRES_USER="${POSTGRES_USER:-postgres}"
POSTGRES_DB="${POSTGRES_DB:-sub2api}"
POSTGRES_PASSWORD="${POSTGRES_PASSWORD:-}"
REDIS_PASSWORD="${REDIS_PASSWORD:-}"
SERVER_PORT="${SERVER_PORT:-8080}"
BACKUP_INTERVAL_SECONDS="${BACKUP_INTERVAL_SECONDS:-1800}"
if [ -z "$POSTGRES_PASSWORD" ]; then
log "[startup] POSTGRES_PASSWORD is required"
exit 1
fi
if [ -z "${JWT_SECRET:-}" ]; then
log "[startup] JWT_SECRET is required"
exit 1
fi
if [ -z "${TOTP_ENCRYPTION_KEY:-}" ]; then
log "[startup] TOTP_ENCRYPTION_KEY is required"
exit 1
fi
mkdir -p "$APP_DATA_DIR" "$POSTGRES_DATA_DIR" "$REDIS_DATA_DIR" "$RUNTIME_DIR/postgresql" "$LOG_DIR"
chown -R sub2api:sub2api "$DATA_ROOT" /app
chmod 700 "$POSTGRES_DATA_DIR"
restore_if_needed() {
if [ -z "${HF_BACKUP_REPO:-}" ] || [ -z "${HF_TOKEN:-}" ]; then
log "[restore] skipped: backup repo is not configured"
return 0
fi
if su-exec sub2api python3 /app/deploy/huggingface/backup_manager.py restore --data-root "$DATA_ROOT"; then
return 0
fi
log "[restore] restore step failed; continuing with local state"
return 0
}
init_postgres() {
if [ -s "$POSTGRES_DATA_DIR/PG_VERSION" ]; then
return 0
fi
password_file="$RUNTIME_DIR/postgres-password.txt"
printf '%s' "$POSTGRES_PASSWORD" > "$password_file"
chown sub2api:sub2api "$password_file"
chmod 600 "$password_file"
su-exec sub2api initdb \
-D "$POSTGRES_DATA_DIR" \
--username="$POSTGRES_USER" \
--pwfile="$password_file" \
--auth-host=scram-sha-256 \
--auth-local=scram-sha-256
rm -f "$password_file"
}
start_postgres() {
su-exec sub2api pg_ctl \
-D "$POSTGRES_DATA_DIR" \
-l "$LOG_DIR/postgres.log" \
-o "-c listen_addresses=127.0.0.1 -c port=5432 -c unix_socket_directories=$RUNTIME_DIR/postgresql" \
-w start
}
ensure_database() {
if PGPASSWORD="$POSTGRES_PASSWORD" psql -h 127.0.0.1 -U "$POSTGRES_USER" -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname = '$POSTGRES_DB'" | grep -q 1; then
return 0
fi
PGPASSWORD="$POSTGRES_PASSWORD" createdb -h 127.0.0.1 -U "$POSTGRES_USER" "$POSTGRES_DB"
}
start_redis() {
if [ -n "$REDIS_PASSWORD" ]; then
su-exec sub2api redis-server \
--bind 127.0.0.1 \
--port 6379 \
--dir "$REDIS_DATA_DIR" \
--save 60 1 \
--appendonly yes \
--appendfsync everysec \
--requirepass "$REDIS_PASSWORD" \
--daemonize yes
else
su-exec sub2api redis-server \
--bind 127.0.0.1 \
--port 6379 \
--dir "$REDIS_DATA_DIR" \
--save 60 1 \
--appendonly yes \
--appendfsync everysec \
--daemonize yes
fi
}
wait_for_redis() {
attempts=0
while [ "$attempts" -lt 20 ]; do
if [ -n "$REDIS_PASSWORD" ]; then
if redis-cli -a "$REDIS_PASSWORD" -h 127.0.0.1 ping >/dev/null 2>&1; then
return 0
fi
else
if redis-cli -h 127.0.0.1 ping >/dev/null 2>&1; then
return 0
fi
fi
attempts=$((attempts + 1))
sleep 1
done
log "[startup] Redis did not become ready in time"
return 1
}
start_backup_loop() {
if [ -z "${HF_BACKUP_REPO:-}" ] || [ -z "${HF_TOKEN:-}" ]; then
log "[backup] periodic backups disabled"
return 0
fi
(
while true; do
sleep "$BACKUP_INTERVAL_SECONDS"
su-exec sub2api python3 /app/deploy/huggingface/backup_manager.py backup --data-root "$DATA_ROOT" || true
done
) &
BACKUP_LOOP_PID="$!"
export BACKUP_LOOP_PID
log "[backup] loop started (${BACKUP_INTERVAL_SECONDS}s)"
}
cleanup() {
if [ "${CLEANUP_DONE:-0}" = "1" ]; then
return 0
fi
CLEANUP_DONE=1
export CLEANUP_DONE
if [ -n "${BACKUP_LOOP_PID:-}" ]; then
kill "$BACKUP_LOOP_PID" >/dev/null 2>&1 || true
fi
if [ -n "${HF_BACKUP_REPO:-}" ] && [ -n "${HF_TOKEN:-}" ]; then
su-exec sub2api python3 /app/deploy/huggingface/backup_manager.py backup --data-root "$DATA_ROOT" || true
fi
if [ -n "$REDIS_PASSWORD" ]; then
redis-cli -a "$REDIS_PASSWORD" -h 127.0.0.1 shutdown >/dev/null 2>&1 || true
else
redis-cli -h 127.0.0.1 shutdown >/dev/null 2>&1 || true
fi
su-exec sub2api pg_ctl -D "$POSTGRES_DATA_DIR" -m fast stop >/dev/null 2>&1 || true
}
trap cleanup INT TERM EXIT
restore_if_needed
init_postgres
start_postgres
ensure_database
start_redis
wait_for_redis
start_backup_loop
export AUTO_SETUP="${AUTO_SETUP:-true}"
export DATA_DIR="$APP_DATA_DIR"
export SERVER_HOST="${SERVER_HOST:-0.0.0.0}"
export SERVER_PORT
export SERVER_MODE="${SERVER_MODE:-release}"
export RUN_MODE="${RUN_MODE:-standard}"
export DATABASE_HOST="${DATABASE_HOST:-127.0.0.1}"
export DATABASE_PORT="${DATABASE_PORT:-5432}"
export DATABASE_USER="${DATABASE_USER:-$POSTGRES_USER}"
export DATABASE_PASSWORD="${DATABASE_PASSWORD:-$POSTGRES_PASSWORD}"
export DATABASE_DBNAME="${DATABASE_DBNAME:-$POSTGRES_DB}"
export DATABASE_SSLMODE="${DATABASE_SSLMODE:-disable}"
export DATABASE_MAX_OPEN_CONNS="${DATABASE_MAX_OPEN_CONNS:-10}"
export DATABASE_MAX_IDLE_CONNS="${DATABASE_MAX_IDLE_CONNS:-5}"
export DATABASE_CONN_MAX_LIFETIME_MINUTES="${DATABASE_CONN_MAX_LIFETIME_MINUTES:-30}"
export DATABASE_CONN_MAX_IDLE_TIME_MINUTES="${DATABASE_CONN_MAX_IDLE_TIME_MINUTES:-5}"
export REDIS_HOST="${REDIS_HOST:-127.0.0.1}"
export REDIS_PORT="${REDIS_PORT:-6379}"
export REDIS_DB="${REDIS_DB:-0}"
export REDIS_POOL_SIZE="${REDIS_POOL_SIZE:-64}"
export REDIS_MIN_IDLE_CONNS="${REDIS_MIN_IDLE_CONNS:-2}"
export REDIS_ENABLE_TLS="${REDIS_ENABLE_TLS:-false}"
export ADMIN_EMAIL="${ADMIN_EMAIL:-admin@sub2api.local}"
export LOG_OUTPUT_TO_STDOUT="${LOG_OUTPUT_TO_STDOUT:-true}"
export LOG_OUTPUT_TO_FILE="${LOG_OUTPUT_TO_FILE:-false}"
export OPS_ENABLED="${OPS_ENABLED:-false}"
export DASHBOARD_AGGREGATION_ENABLED="${DASHBOARD_AGGREGATION_ENABLED:-false}"
export TZ="${TZ:-Asia/Shanghai}"
log "[startup] launching sub2api on port ${SERVER_PORT}"
su-exec sub2api /app/sub2api &
APP_PID="$!"
wait "$APP_PID"
|