#!/bin/sh set -e PGDATA="/tmp/pgdata" PGUSER="postgres" PGPASS="postgres" PGDB="su2adb" REDIS_DIR="/tmp/redis" BUCKET_DUMP="hf://buckets/cjovs/su2a-data/su2adb_dump.sql" if [ "$(id -u)" = "0" ]; then mkdir -p /tmp/redis 2>/dev/null || true chown -R su2a:su2a /app/data 2>/dev/null & chown su2a:su2a /app 2>/dev/null || true sleep 2 kill %1 2>/dev/null || true exec su-exec su2a "$0" "$@" fi echo "=== Starting PostgreSQL ===" rm -f "$PGDATA/postmaster.pid" 2>/dev/null || true if [ ! -f "$PGDATA/PG_VERSION" ]; then echo "[pg] Initializing $PGDATA ..." initdb -D "$PGDATA" --username="$PGUSER" --auth=trust 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" fi pg_ctl -D "$PGDATA" -l "$PGDATA/logfile" -o "-k /tmp -p 5432" start until pg_isready -h localhost -p 5432 -U "$PGUSER" -q 2>/dev/null; do sleep 1; done echo "[pg] PostgreSQL ready." psql -h localhost -p 5432 -U "$PGUSER" -d postgres -c "ALTER USER $PGUSER WITH PASSWORD '$PGPASS';" 2>/dev/null || true if ! psql -h localhost -p 5432 -U "$PGUSER" -lqt 2>/dev/null | cut -d \| -f 1 | grep -qw "$PGDB"; then echo "[pg] Creating database $PGDB ..." createdb -h localhost -p 5432 -U "$PGUSER" "$PGDB" # Restore from bucket if dump exists if [ -n "$HF_TOKEN" ]; then echo "[pg] Checking bucket for backup..." if hf buckets list cjovs/su2a-data --quiet 2>/dev/null | grep -q "su2adb_dump.sql"; then echo "[pg] Restoring from bucket backup..." hf buckets cp "$BUCKET_DUMP" /tmp/restore.sql --quiet 2>/dev/null || true if [ -f /tmp/restore.sql ] && [ -s /tmp/restore.sql ]; then psql -h localhost -p 5432 -U "$PGUSER" -d "$PGDB" -f /tmp/restore.sql 2>/dev/null || true rm -f /tmp/restore.sql echo "[pg] Restore complete." fi fi fi fi echo "[pg] Database $PGDB ready." echo "=== Starting Redis ===" mkdir -p "$REDIS_DIR" 2>/dev/null || true redis-server --daemonize yes --dir "$REDIS_DIR" --save "" --port 6379 --bind 127.0.0.1 echo "[redis] Redis started." # === Auto-update: download latest binary from GitHub === echo "[update] Checking for latest version..." set +e python3 <<'PYEOF' import os, json, tarfile, io from urllib.request import Request, urlopen try: req = Request('https://api.github.com/repos/Wei-Shaw/sub2api/releases/latest') req.add_header('Accept', 'application/vnd.github.v3+json') req.add_header('User-Agent', 'Sub2API-AutoUpdater') with urlopen(req, timeout=30) as resp: release = json.loads(resp.read()) tag = release['tag_name'].lstrip('v') for asset in release.get('assets', []): name = asset.get('name', '') if 'linux_amd64' in name and name.endswith('.tar.gz'): url = asset['browser_download_url'] print(f'[update] v{tag} found, downloading {name} ...') req2 = Request(url) req2.add_header('User-Agent', 'Sub2API-AutoUpdater') with urlopen(req2, timeout=600) as resp: data = resp.read() with tarfile.open(fileobj=io.BytesIO(data), mode='r:gz') as tar: for member in tar: mname = member.name.rstrip('/') if mname == 'sub2api' or mname == './sub2api': f = tar.extractfile(member) if f: tmp = '/tmp/.su2a.new' with open(tmp, 'wb') as out: out.write(f.read()) os.chmod(tmp, 0o755) import shutil shutil.move(tmp, '/app/su2a') print(f'[update] Updated to v{tag}') exit(0) break print('[update] No matching linux_amd64 asset found') exit(1) except Exception as e: print(f'[update] Skipped: {e}') exit(1) PYEOF UPDATE_EXIT=$? set -e if [ $UPDATE_EXIT -eq 0 ]; then echo "[update] Running latest version" else echo "[update] Running built-in version (will retry next restart)" fi cleanup() { echo "=== Shutting down ===" redis-cli -h 127.0.0.1 -p 6379 shutdown 2>/dev/null || true # Backup database to bucket if [ -n "$HF_TOKEN" ]; then echo "[pg] Backing up database to bucket..." pg_dump -h localhost -p 5432 -U "$PGUSER" -d "$PGDB" --no-owner --no-acl -f /tmp/su2adb_dump.sql 2>/dev/null || true if [ -f /tmp/su2adb_dump.sql ] && [ -s /tmp/su2adb_dump.sql ]; then hf buckets cp /tmp/su2adb_dump.sql "$BUCKET_DUMP" --quiet 2>/dev/null || true echo "[pg] Backup uploaded." fi fi pg_ctl -D "$PGDATA" stop -m fast 2>/dev/null || true } trap cleanup EXIT INT TERM export DATABASE_DBNAME="$PGDB" # Override config via env vars to bypass FUSE read corruption export DATABASE_HOST=localhost export DATABASE_PORT=5432 export DATABASE_USER="$PGUSER" export DATABASE_PASSWORD="$PGPASS" export DATABASE_SSLMODE=disable export REDIS_HOST=localhost export REDIS_PORT=6379 export REDIS_DB=0 export REDIS_ENABLE_TLS=false export SERVER_HOST=0.0.0.0 export SERVER_PORT=8080 export SERVER_MODE=release export JWT_SECRET=e32467771e531062d2b2d68919e7430e60c32f0f0381320ed068cd6d61435e9c export JWT_EXPIRE_HOUR=24 echo "=== Starting su2a ===" while true; do echo "[su2a] Starting..." /app/su2a "$@" & PID=$! # On fresh DB: wait for tables then create admin user if [ ! -f /tmp/.init_done ] && [ -n "$ADMIN_EMAIL" ] && [ -n "$ADMIN_PASSWORD" ]; then for i in $(seq 1 30); do if psql -h localhost -p 5432 -U "$PGUSER" -d "$PGDB" -c "SELECT 1 FROM users LIMIT 1" >/dev/null 2>&1; then echo "[init] Tables ready, creating admin user..." pip3 install --no-cache-dir --break-system-packages -q bcrypt 2>/dev/null python3 -c " import os, bcrypt, subprocess email = os.environ['ADMIN_EMAIL'] password = os.environ['ADMIN_PASSWORD'] hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode() sql = f\"\"\"INSERT INTO users (email, password_hash, role, status, balance, concurrency, signup_source) VALUES ('{email}', '{hashed}', 'admin', 'active', 0, 5, 'email') ON CONFLICT (email) WHERE deleted_at IS NULL DO UPDATE SET password_hash = EXCLUDED.password_hash, role = 'admin'\"\"\" r = subprocess.run(['psql', '-h', 'localhost', '-p', '5432', '-U', 'postgres', '-d', 'su2adb', '-c', sql], capture_output=True, text=True) if r.returncode == 0: print('[init] Admin user created: ' + email) else: print('[init] ERROR creating admin: ' + r.stderr.strip()) " touch /tmp/.init_done break fi sleep 2 done fi wait $PID echo "[su2a] Exited, restarting in 2s..." sleep 2 done