#!/usr/bin/env bash set -euo pipefail # remote43 patched stack 渲染 helper,供部署脚本和测试脚本共享。 remote43_require_file() { local path="$1" local label="$2" [[ -f "$path" ]] || { echo "missing $label: $path" >&2 return 1 } } remote43_random_hex() { local bytes="${1:?bytes required}" python3 - "$bytes" <<'PY' import secrets import sys print(secrets.token_hex(int(sys.argv[1]))) PY } remote43_write_env_file() { local path="$1" shift : > "$path" while [[ $# -gt 0 ]]; do local key="$1" local value="$2" shift 2 case "$value" in *$'\n'*) echo "env value for $key must not contain newlines" >&2 return 1 ;; esac printf '%s=%s\n' "$key" "$value" >> "$path" done } render_remote43_host_env() { local pg_container="$1" local redis_container="$2" local db_password="$3" local db_name="$4" local admin_email="$5" local admin_password="$6" local jwt_secret="$7" local totp_key="$8" cat </dev/null 2>&1 || true rm -f "\$CRM_PID_FILE" fi rm -f "\$CRM_LOG_FILE" sudo -n docker rm -f "\$APP_CONTAINER" "\$PG_CONTAINER" "\$REDIS_CONTAINER" >/dev/null 2>&1 || true sudo -n docker network inspect "\$NETWORK_NAME" >/dev/null 2>&1 || sudo -n docker network create "\$NETWORK_NAME" >/dev/null sudo -n docker run -d --name "\$PG_CONTAINER" --network "\$NETWORK_NAME" \\ -e POSTGRES_USER=sub2api \\ -e POSTGRES_PASSWORD="\$DB_PASSWORD" \\ -e POSTGRES_DB="\$DB_NAME" \\ "\$PG_IMAGE" >/dev/null sudo -n docker run -d --name "\$REDIS_CONTAINER" --network "\$NETWORK_NAME" \\ "\$REDIS_IMAGE" >/dev/null sleep 10 sudo -n docker run -d --name "\$APP_CONTAINER" --network "\$NETWORK_NAME" \\ -p "127.0.0.1:\$HOST_PORT:\$HOST_CONTAINER_PORT" \\ --env-file "\$HOST_ENV_FILE" \\ -v "\$DATA_DIR:/app/data" \\ -v "\$HOST_BINARY:/app/sub2api:ro" \\ "\$HOST_IMAGE" /app/sub2api >/dev/null python3 - "\$HOST_ENV_FILE" "\$HOST_PORT" <<'PY' import json import pathlib import subprocess import sys import time env_path = pathlib.Path(sys.argv[1]) host_port = sys.argv[2] values = {} for line in env_path.read_text(encoding='utf-8').splitlines(): if '=' not in line: continue key, value = line.split('=', 1) values[key] = value payload = json.dumps({ 'email': values['ADMIN_EMAIL'], 'password': values['ADMIN_PASSWORD'], 'turnstile_token': '', }, ensure_ascii=False) url = f"http://127.0.0.1:{host_port}/api/v1/auth/login" for _ in range(60): result = subprocess.run( ['curl', '-fsS', '-H', 'Content-Type: application/json', '-X', 'POST', url, '-d', payload], text=True, capture_output=True, ) if result.returncode == 0 and 'access_token' in result.stdout: raise SystemExit(0) time.sleep(2) raise SystemExit(f'host login did not become ready on {url}') PY nohup bash -lc 'set -a; source "\$1"; set +a; exec "\$2"' _ "\$CRM_ENV_FILE" "\$CRM_BINARY" >"\$CRM_LOG_FILE" 2>&1 & echo \$! > "\$CRM_PID_FILE" python3 - "\$CRM_PORT" <<'PY' import subprocess import sys import time url = f"http://127.0.0.1:{sys.argv[1]}/healthz" for _ in range(30): result = subprocess.run(['curl', '-fsS', url], text=True, capture_output=True) if result.returncode == 0 and result.stdout.strip() == 'ok': raise SystemExit(0) time.sleep(1) raise SystemExit(f'crm healthz did not become ready on {url}') PY printf 'host_base=http://127.0.0.1:%s\n' "\$HOST_PORT" printf 'crm_base=http://127.0.0.1:%s\n' "\$CRM_PORT" printf 'remote_host_env=%s\n' "\$HOST_ENV_FILE" printf 'crm_log=%s\n' "\$CRM_LOG_FILE" EOF }