feat: add kimi a7m overlay workflow and remote43 validation
This commit is contained in:
@@ -106,6 +106,19 @@ if [[ -z "$upstream_base_url" ]]; then
|
||||
exit 2
|
||||
fi
|
||||
|
||||
pack_id="$(python3 - "$PACK_PATH" <<'PY'
|
||||
import json, pathlib, sys
|
||||
pack_path = pathlib.Path(sys.argv[1])
|
||||
pack_file = pack_path / "pack.json"
|
||||
pack = json.loads(pack_file.read_text(encoding='utf-8'))
|
||||
print(str(pack.get("pack_id", "")).strip())
|
||||
PY
|
||||
)"
|
||||
if [[ -z "$pack_id" ]]; then
|
||||
echo "missing pack_id in $PACK_PATH/pack.json" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
ssh_cmd() {
|
||||
local cmd="$1"
|
||||
ssh -i "$KEY" -o StrictHostKeyChecking=no "$REMOTE" "$cmd"
|
||||
@@ -340,9 +353,9 @@ if [[ -z "$host_bearer_token" ]]; then
|
||||
fi
|
||||
admin_uid="$(ssh_cmd "sudo -n docker exec $REMOTE_PG_CONTAINER_Q psql -U sub2api -d sub2api -Atc \"select id from users where role='admin' order by id asc limit 1;\"")"
|
||||
admin_uid="${admin_uid##*$'\n'}"
|
||||
sub_uid="$(remote_pg_query "select id from users where email like 'relay-sub-%@sub2api.local' and not exists (select 1 from user_subscriptions s where s.user_id=users.id and s.deleted_at is null) order by id desc limit 1;")"
|
||||
sub_uid="$(remote_pg_query "select u.id from users u where u.email like 'relay-sub-%@sub2api.local' and not exists (select 1 from user_subscriptions s where s.user_id=u.id and s.deleted_at is null) order by u.id desc limit 1;")"
|
||||
sub_uid="${sub_uid##*$'\n'}"
|
||||
sub_key="$(remote_pg_query "select k.key from users u join api_keys k on k.user_id=u.id where u.email like 'relay-sub-%@sub2api.local' and not exists (select 1 from user_subscriptions s where s.user_id=users.id and s.deleted_at is null) order by u.id desc limit 1;")"
|
||||
sub_key="$(remote_pg_query "select k.key from users u join api_keys k on k.user_id=u.id where u.email like 'relay-sub-%@sub2api.local' and not exists (select 1 from user_subscriptions s where s.user_id=u.id and s.deleted_at is null) order by u.id desc limit 1;")"
|
||||
sub_key="${sub_key##*$'\n'}"
|
||||
if [[ -z "$sub_uid" || -z "$sub_key" ]]; then
|
||||
fresh_seed="$(python3 - <<'PY'
|
||||
@@ -469,6 +482,17 @@ else
|
||||
crm_curl_json POST "/api/hosts" "$create_host_payload" > "$ART/01a-create-host.json"
|
||||
fi
|
||||
|
||||
host_id="$(python3 - "$ART/01a-create-host.json" <<'PY'
|
||||
import json, pathlib, sys
|
||||
obj = json.loads(pathlib.Path(sys.argv[1]).read_text(encoding='utf-8'))
|
||||
print(str(obj.get('host_id', '')).strip())
|
||||
PY
|
||||
)"
|
||||
if [[ -z "$host_id" ]]; then
|
||||
echo "missing host_id in $ART/01a-create-host.json" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
payload="$(python3 - "$CRM_HOST_BASE" "$host_bearer_token" "$PACK_PATH" "$provider_id" "$upstream_key" "$sub_key" "$sub_uid" "$SUBSCRIPTION_DAYS" <<'PY'
|
||||
import json, sys
|
||||
host_base, host_bearer_token, pack_path, provider_id, upstream_key, sub_key, sub_uid, subscription_days = sys.argv[1:9]
|
||||
@@ -614,10 +638,11 @@ ssh_cmd "cat /tmp/upstream_chat_headers.txt" > "$ART/19-upstream-chat.headers.tx
|
||||
ssh_cmd "cat /tmp/upstream_chat_body.txt" > "$ART/20-upstream-chat.body.txt"
|
||||
sanitize_headers_file "$ART/19-upstream-chat.headers.txt"
|
||||
|
||||
provider_query_suffix="?host_id=$(python3 - "$HOST_NAME" <<'PY'
|
||||
provider_query_suffix="$(python3 - "$pack_id" "$host_id" <<'PY'
|
||||
import sys
|
||||
from urllib.parse import quote
|
||||
print(quote(sys.argv[1], safe=''))
|
||||
pack_id, host_id = sys.argv[1:3]
|
||||
print(f"?pack_id={quote(pack_id, safe='')}&host_id={quote(host_id, safe='')}")
|
||||
PY
|
||||
)"
|
||||
|
||||
|
||||
246
scripts/remote43_patched_stack_lib.sh
Normal file
246
scripts/remote43_patched_stack_lib.sh
Normal file
@@ -0,0 +1,246 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
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 <<EOF
|
||||
AUTO_SETUP=true
|
||||
DATABASE_HOST=$pg_container
|
||||
DATABASE_PORT=5432
|
||||
DATABASE_USER=sub2api
|
||||
DATABASE_PASSWORD=$db_password
|
||||
DATABASE_DBNAME=$db_name
|
||||
REDIS_HOST=$redis_container
|
||||
REDIS_PORT=6379
|
||||
ADMIN_EMAIL=$admin_email
|
||||
ADMIN_PASSWORD=$admin_password
|
||||
JWT_SECRET=$jwt_secret
|
||||
TOTP_ENCRYPTION_KEY=$totp_key
|
||||
SECURITY_URL_ALLOWLIST_ENABLED=false
|
||||
SECURITY_URL_ALLOWLIST_MODE=disabled
|
||||
EOF
|
||||
}
|
||||
|
||||
render_remote43_crm_env() {
|
||||
local crm_port="$1"
|
||||
local sqlite_dsn="$2"
|
||||
local admin_token="$3"
|
||||
|
||||
cat <<EOF
|
||||
SUB2API_CRM_LISTEN_ADDR=127.0.0.1:$crm_port
|
||||
SUB2API_CRM_SQLITE_DSN=$sqlite_dsn
|
||||
SUB2API_CRM_ADMIN_TOKEN=$admin_token
|
||||
SUB2API_CRM_RECONCILE_WORKER_ENABLED=false
|
||||
EOF
|
||||
}
|
||||
|
||||
render_remote43_bootstrap_script() {
|
||||
local remote_root="$1"
|
||||
local host_env_file="$2"
|
||||
local crm_env_file="$3"
|
||||
local host_binary_name="$4"
|
||||
local crm_binary_name="$5"
|
||||
local data_dir="$6"
|
||||
local crm_db_file="$7"
|
||||
local crm_pid_file="$8"
|
||||
local crm_log_file="$9"
|
||||
local app_container="${10}"
|
||||
local pg_container="${11}"
|
||||
local redis_container="${12}"
|
||||
local network_name="${13}"
|
||||
local host_image="${14}"
|
||||
local pg_image="${15}"
|
||||
local redis_image="${16}"
|
||||
local db_password="${17}"
|
||||
local db_name="${18}"
|
||||
local host_port="${19}"
|
||||
local crm_port="${20}"
|
||||
local host_container_port="${21}"
|
||||
|
||||
local remote_root_q host_env_q crm_env_q host_binary_q crm_binary_q
|
||||
local data_dir_q crm_db_q crm_pid_q crm_log_q app_q pg_q redis_q
|
||||
local network_q host_image_q pg_image_q redis_image_q db_password_q db_name_q
|
||||
local host_port_q crm_port_q host_container_port_q
|
||||
remote_root_q="$(printf '%q' "$remote_root")"
|
||||
host_env_q="$(printf '%q' "$host_env_file")"
|
||||
crm_env_q="$(printf '%q' "$crm_env_file")"
|
||||
host_binary_q="$(printf '%q' "$host_binary_name")"
|
||||
crm_binary_q="$(printf '%q' "$crm_binary_name")"
|
||||
data_dir_q="$(printf '%q' "$data_dir")"
|
||||
crm_db_q="$(printf '%q' "$crm_db_file")"
|
||||
crm_pid_q="$(printf '%q' "$crm_pid_file")"
|
||||
crm_log_q="$(printf '%q' "$crm_log_file")"
|
||||
app_q="$(printf '%q' "$app_container")"
|
||||
pg_q="$(printf '%q' "$pg_container")"
|
||||
redis_q="$(printf '%q' "$redis_container")"
|
||||
network_q="$(printf '%q' "$network_name")"
|
||||
host_image_q="$(printf '%q' "$host_image")"
|
||||
pg_image_q="$(printf '%q' "$pg_image")"
|
||||
redis_image_q="$(printf '%q' "$redis_image")"
|
||||
db_password_q="$(printf '%q' "$db_password")"
|
||||
db_name_q="$(printf '%q' "$db_name")"
|
||||
host_port_q="$(printf '%q' "$host_port")"
|
||||
crm_port_q="$(printf '%q' "$crm_port")"
|
||||
host_container_port_q="$(printf '%q' "$host_container_port")"
|
||||
|
||||
cat <<EOF
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REMOTE_ROOT=$remote_root_q
|
||||
HOST_ENV_FILE=$host_env_q
|
||||
CRM_ENV_FILE=$crm_env_q
|
||||
HOST_BINARY="\$REMOTE_ROOT/$host_binary_q"
|
||||
CRM_BINARY="\$REMOTE_ROOT/$crm_binary_q"
|
||||
DATA_DIR=$data_dir_q
|
||||
CRM_DB_FILE=$crm_db_q
|
||||
CRM_PID_FILE=$crm_pid_q
|
||||
CRM_LOG_FILE=$crm_log_q
|
||||
APP_CONTAINER=$app_q
|
||||
PG_CONTAINER=$pg_q
|
||||
REDIS_CONTAINER=$redis_q
|
||||
NETWORK_NAME=$network_q
|
||||
HOST_IMAGE=$host_image_q
|
||||
PG_IMAGE=$pg_image_q
|
||||
REDIS_IMAGE=$redis_image_q
|
||||
DB_PASSWORD=$db_password_q
|
||||
DB_NAME=$db_name_q
|
||||
HOST_PORT=$host_port_q
|
||||
CRM_PORT=$crm_port_q
|
||||
HOST_CONTAINER_PORT=$host_container_port_q
|
||||
|
||||
mkdir -p "\$REMOTE_ROOT" "\$DATA_DIR"
|
||||
chmod 755 "\$HOST_BINARY" "\$CRM_BINARY"
|
||||
|
||||
rm -f "\$DATA_DIR/install.lock" "\$DATA_DIR/config.yaml" "\$DATA_DIR/.installed"
|
||||
rm -f "\$CRM_DB_FILE"
|
||||
|
||||
if [[ -f "\$CRM_PID_FILE" ]]; then
|
||||
kill "\$(cat "\$CRM_PID_FILE")" >/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
|
||||
}
|
||||
205
scripts/setup_remote43_patched_stack.sh
Executable file
205
scripts/setup_remote43_patched_stack.sh
Executable file
@@ -0,0 +1,205 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
# shellcheck disable=SC1091
|
||||
source "$ROOT_DIR/scripts/remote43_patched_stack_lib.sh"
|
||||
|
||||
KEY="${KEY:-/home/long/下载/zjsea.pem}"
|
||||
REMOTE="${REMOTE:-ubuntu@43.155.133.187}"
|
||||
STACK_NAME="${STACK_NAME:-sub2api-patched-$(date +%Y%m%d)}"
|
||||
HOST_PORT="${HOST_PORT:-18139}"
|
||||
CRM_PORT="${CRM_PORT:-18143}"
|
||||
LOCAL_HOST_TUNNEL_PORT="${LOCAL_HOST_TUNNEL_PORT:-$HOST_PORT}"
|
||||
LOCAL_CRM_TUNNEL_PORT="${LOCAL_CRM_TUNNEL_PORT:-$CRM_PORT}"
|
||||
HOST_IMAGE="${HOST_IMAGE:-weishaw/sub2api:0.1.129}"
|
||||
HOST_CONTAINER_PORT="${HOST_CONTAINER_PORT:-8080}"
|
||||
PG_IMAGE="${PG_IMAGE:-postgres:16-alpine}"
|
||||
REDIS_IMAGE="${REDIS_IMAGE:-redis:7-alpine}"
|
||||
DB_NAME="${DB_NAME:-sub2api}"
|
||||
DB_PASSWORD="${DB_PASSWORD:-$(remote43_random_hex 16)}"
|
||||
ADMIN_EMAIL="${ADMIN_EMAIL:-admin@sub2api.local}"
|
||||
ADMIN_PASSWORD="${ADMIN_PASSWORD:-Sub2API-Remote43-Temp-Admin-20260525}"
|
||||
JWT_SECRET="${JWT_SECRET:-$(remote43_random_hex 24)}"
|
||||
TOTP_ENCRYPTION_KEY="${TOTP_ENCRYPTION_KEY:-$(remote43_random_hex 32)}"
|
||||
CRM_ADMIN_TOKEN="${CRM_ADMIN_TOKEN:-$(remote43_random_hex 24)}"
|
||||
HOST_NAME="${HOST_NAME:-remote43-patched-${HOST_PORT}}"
|
||||
HOST_BINARY="${HOST_BINARY:-}"
|
||||
CRM_BINARY="${CRM_BINARY:-$ROOT_DIR/server}"
|
||||
PACK_DIR="${PACK_DIR:-$ROOT_DIR/packs/openai-cn-pack}"
|
||||
LOCAL_SHARED_PACK_DIR="${LOCAL_SHARED_PACK_DIR:-/tmp/openai-cn-pack-${STACK_NAME}}"
|
||||
LOCAL_OPERATOR_ENV_FILE="${LOCAL_OPERATOR_ENV_FILE:-/tmp/remote43-patched-stack-${HOST_PORT}.env}"
|
||||
LOCAL_TUNNEL_SCRIPT="${LOCAL_TUNNEL_SCRIPT:-/tmp/remote43-patched-stack-${HOST_PORT}.tunnel.sh}"
|
||||
REMOTE_ROOT="${REMOTE_ROOT:-/home/ubuntu/${STACK_NAME}_${HOST_PORT}}"
|
||||
REMOTE_PACK_PATH="${REMOTE_PACK_PATH:-$LOCAL_SHARED_PACK_DIR}"
|
||||
REMOTE_HOST_ENV_FILE="$REMOTE_ROOT/.env.host"
|
||||
REMOTE_CRM_ENV_FILE="$REMOTE_ROOT/.env.crm"
|
||||
REMOTE_BOOTSTRAP_FILE="$REMOTE_ROOT/bootstrap.sh"
|
||||
REMOTE_HOST_BINARY="$REMOTE_ROOT/sub2api-patched"
|
||||
REMOTE_CRM_BINARY="$REMOTE_ROOT/sub2api-cn-relay-manager-server"
|
||||
REMOTE_DATA_DIR="$REMOTE_ROOT/data"
|
||||
REMOTE_CRM_DB_FILE="$REMOTE_ROOT/sub2api-cn-relay-manager.db"
|
||||
REMOTE_CRM_PID_FILE="$REMOTE_ROOT/crm.pid"
|
||||
REMOTE_CRM_LOG_FILE="$REMOTE_ROOT/crm.log"
|
||||
REMOTE_APP_CONTAINER="${REMOTE_APP_CONTAINER:-${STACK_NAME}-app}"
|
||||
REMOTE_PG_CONTAINER="${REMOTE_PG_CONTAINER:-${STACK_NAME}-pg}"
|
||||
REMOTE_REDIS_CONTAINER="${REMOTE_REDIS_CONTAINER:-${STACK_NAME}-redis}"
|
||||
REMOTE_NETWORK="${REMOTE_NETWORK:-${STACK_NAME}-net}"
|
||||
DRY_RUN="${DRY_RUN:-0}"
|
||||
|
||||
die() {
|
||||
echo "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
require_cmd() {
|
||||
command -v "$1" >/dev/null 2>&1 || die "missing command: $1"
|
||||
}
|
||||
|
||||
run_cmd() {
|
||||
if [[ "$DRY_RUN" == "1" ]]; then
|
||||
printf 'DRY_RUN:'
|
||||
printf ' %q' "$@"
|
||||
printf '\n'
|
||||
return 0
|
||||
fi
|
||||
"$@"
|
||||
}
|
||||
|
||||
ssh_remote() {
|
||||
run_cmd ssh -i "$KEY" -o StrictHostKeyChecking=no "$REMOTE" "$@"
|
||||
}
|
||||
|
||||
scp_remote() {
|
||||
run_cmd scp -i "$KEY" -o StrictHostKeyChecking=no "$@"
|
||||
}
|
||||
|
||||
prepare_local_shared_pack() {
|
||||
case "$LOCAL_SHARED_PACK_DIR" in
|
||||
/tmp/*) ;;
|
||||
*)
|
||||
die "LOCAL_SHARED_PACK_DIR must stay under /tmp, got: $LOCAL_SHARED_PACK_DIR"
|
||||
;;
|
||||
esac
|
||||
|
||||
mkdir -p "$(dirname "$LOCAL_SHARED_PACK_DIR")"
|
||||
rm -rf "$LOCAL_SHARED_PACK_DIR"
|
||||
cp -R "$PACK_DIR" "$LOCAL_SHARED_PACK_DIR"
|
||||
}
|
||||
|
||||
write_local_tunnel_script() {
|
||||
cat > "$LOCAL_TUNNEL_SCRIPT" <<EOF
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
exec ssh -N \\
|
||||
-L ${LOCAL_CRM_TUNNEL_PORT}:127.0.0.1:${CRM_PORT} \\
|
||||
-L ${LOCAL_HOST_TUNNEL_PORT}:127.0.0.1:${HOST_PORT} \\
|
||||
-i $(printf '%q' "$KEY") \\
|
||||
-o StrictHostKeyChecking=no \\
|
||||
$(printf '%q' "$REMOTE")
|
||||
EOF
|
||||
chmod +x "$LOCAL_TUNNEL_SCRIPT"
|
||||
}
|
||||
|
||||
write_operator_env() {
|
||||
remote43_write_env_file "$LOCAL_OPERATOR_ENV_FILE" \
|
||||
CRM_BASE "http://127.0.0.1:${LOCAL_CRM_TUNNEL_PORT}" \
|
||||
HOST_BASE "http://127.0.0.1:${LOCAL_HOST_TUNNEL_PORT}" \
|
||||
CRM_HOST_BASE "http://127.0.0.1:${HOST_PORT}" \
|
||||
REMOTE_HOST_BASE "http://127.0.0.1:${HOST_PORT}" \
|
||||
HOST_NAME "$HOST_NAME" \
|
||||
PACK_PATH "$LOCAL_SHARED_PACK_DIR" \
|
||||
REMOTE_HOST_ENV_FILE "$REMOTE_HOST_ENV_FILE" \
|
||||
KEY "$KEY" \
|
||||
REMOTE "$REMOTE" \
|
||||
CRM_ADMIN_TOKEN "$CRM_ADMIN_TOKEN"
|
||||
chmod 600 "$LOCAL_OPERATOR_ENV_FILE"
|
||||
}
|
||||
|
||||
main() {
|
||||
require_cmd bash
|
||||
require_cmd curl
|
||||
require_cmd python3
|
||||
require_cmd ssh
|
||||
require_cmd scp
|
||||
remote43_require_file "$KEY" "ssh key"
|
||||
[[ -n "${HOST_BINARY// }" ]] || die "HOST_BINARY is required"
|
||||
remote43_require_file "$HOST_BINARY" "patched host binary"
|
||||
remote43_require_file "$CRM_BINARY" "crm server binary"
|
||||
[[ -d "$PACK_DIR" ]] || die "missing pack dir: $PACK_DIR"
|
||||
[[ "$REMOTE_PACK_PATH" == "$LOCAL_SHARED_PACK_DIR" ]] || die "REMOTE_PACK_PATH must equal LOCAL_SHARED_PACK_DIR so local/remote PACK_PATH stay identical"
|
||||
|
||||
prepare_local_shared_pack
|
||||
write_local_tunnel_script
|
||||
write_operator_env
|
||||
|
||||
local tmpdir host_env_file crm_env_file bootstrap_file
|
||||
tmpdir="$(mktemp -d)"
|
||||
trap "rm -rf $(printf '%q' "$tmpdir")" EXIT
|
||||
host_env_file="$tmpdir/.env.host"
|
||||
crm_env_file="$tmpdir/.env.crm"
|
||||
bootstrap_file="$tmpdir/bootstrap.sh"
|
||||
|
||||
render_remote43_host_env \
|
||||
"$REMOTE_PG_CONTAINER" \
|
||||
"$REMOTE_REDIS_CONTAINER" \
|
||||
"$DB_PASSWORD" \
|
||||
"$DB_NAME" \
|
||||
"$ADMIN_EMAIL" \
|
||||
"$ADMIN_PASSWORD" \
|
||||
"$JWT_SECRET" \
|
||||
"$TOTP_ENCRYPTION_KEY" > "$host_env_file"
|
||||
render_remote43_crm_env \
|
||||
"$CRM_PORT" \
|
||||
"file:${REMOTE_CRM_DB_FILE}?_foreign_keys=on&_busy_timeout=5000" \
|
||||
"$CRM_ADMIN_TOKEN" > "$crm_env_file"
|
||||
render_remote43_bootstrap_script \
|
||||
"$REMOTE_ROOT" \
|
||||
"$REMOTE_HOST_ENV_FILE" \
|
||||
"$REMOTE_CRM_ENV_FILE" \
|
||||
"$(basename "$REMOTE_HOST_BINARY")" \
|
||||
"$(basename "$REMOTE_CRM_BINARY")" \
|
||||
"$REMOTE_DATA_DIR" \
|
||||
"$REMOTE_CRM_DB_FILE" \
|
||||
"$REMOTE_CRM_PID_FILE" \
|
||||
"$REMOTE_CRM_LOG_FILE" \
|
||||
"$REMOTE_APP_CONTAINER" \
|
||||
"$REMOTE_PG_CONTAINER" \
|
||||
"$REMOTE_REDIS_CONTAINER" \
|
||||
"$REMOTE_NETWORK" \
|
||||
"$HOST_IMAGE" \
|
||||
"$PG_IMAGE" \
|
||||
"$REDIS_IMAGE" \
|
||||
"$DB_PASSWORD" \
|
||||
"$DB_NAME" \
|
||||
"$HOST_PORT" \
|
||||
"$CRM_PORT" \
|
||||
"$HOST_CONTAINER_PORT" > "$bootstrap_file"
|
||||
chmod +x "$bootstrap_file"
|
||||
|
||||
ssh_remote "mkdir -p $(printf '%q' "$REMOTE_ROOT") $(printf '%q' "$(dirname "$REMOTE_PACK_PATH")") && rm -rf $(printf '%q' "$REMOTE_PACK_PATH")"
|
||||
scp_remote "$HOST_BINARY" "$REMOTE:$REMOTE_HOST_BINARY"
|
||||
scp_remote "$CRM_BINARY" "$REMOTE:$REMOTE_CRM_BINARY"
|
||||
scp_remote "$host_env_file" "$REMOTE:$REMOTE_HOST_ENV_FILE"
|
||||
scp_remote "$crm_env_file" "$REMOTE:$REMOTE_CRM_ENV_FILE"
|
||||
scp_remote "$bootstrap_file" "$REMOTE:$REMOTE_BOOTSTRAP_FILE"
|
||||
scp_remote -r "$LOCAL_SHARED_PACK_DIR" "$REMOTE:$REMOTE_PACK_PATH"
|
||||
ssh_remote "bash $(printf '%q' "$REMOTE_BOOTSTRAP_FILE")"
|
||||
|
||||
cat <<EOF
|
||||
remote43 patched stack prepared
|
||||
remote host base: http://127.0.0.1:${HOST_PORT}
|
||||
remote crm base: http://127.0.0.1:${CRM_PORT}
|
||||
remote host env file: ${REMOTE_HOST_ENV_FILE}
|
||||
local operator env file: ${LOCAL_OPERATOR_ENV_FILE}
|
||||
local tunnel script: ${LOCAL_TUNNEL_SCRIPT}
|
||||
shared pack path: ${LOCAL_SHARED_PACK_DIR}
|
||||
|
||||
next:
|
||||
1. 在另一终端运行: ${LOCAL_TUNNEL_SCRIPT}
|
||||
2. 当前终端执行: set -a; source ${LOCAL_OPERATOR_ENV_FILE}; set +a
|
||||
3. 再运行: bash ${ROOT_DIR}/scripts/import_remote43_provider.sh kimi-a7m kimi-k2.6 A7M_KIMI_API_KEY /path/to/keyfile
|
||||
EOF
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@@ -273,6 +273,7 @@ run_test_import_remote43_provider_subscription_prep() {
|
||||
pack_dir="$tmpdir/pack"
|
||||
mkdir -p "$fakebin"
|
||||
mkdir -p "$pack_dir/providers"
|
||||
printf '%s\n' '{"pack_id":"openai-cn-pack","version":"1.1.3"}' > "$pack_dir/pack.json"
|
||||
printf '%s\n' '{"provider_id":"deepseek","base_url":"https://upstream.example.com/v1"}' > "$pack_dir/providers/deepseek.json"
|
||||
|
||||
cat > "$fakebin/curl" <<'EOF'
|
||||
@@ -334,13 +335,13 @@ case "$url" in
|
||||
*/api/import-batches/123)
|
||||
write_body '{"managed_resources":[{"ResourceType":"group","HostResourceID":"7","ResourceName":"DeepSeek 默认分组"}]}'
|
||||
;;
|
||||
*/api/providers/deepseek/status*)
|
||||
*/api/providers/deepseek/status\?pack_id=openai-cn-pack\&host_id=remote43-current-host)
|
||||
write_body '{"status":"ready"}'
|
||||
;;
|
||||
*/api/providers/deepseek/access/status*)
|
||||
*/api/providers/deepseek/access/status\?pack_id=openai-cn-pack\&host_id=remote43-current-host)
|
||||
write_body '{"latest_access_status":"subscription_ready"}'
|
||||
;;
|
||||
*/api/providers/deepseek/access/preview*)
|
||||
*/api/providers/deepseek/access/preview\?pack_id=openai-cn-pack\&host_id=remote43-current-host)
|
||||
write_body '{"available":true}'
|
||||
;;
|
||||
*)
|
||||
@@ -492,6 +493,7 @@ EOF
|
||||
HOST_BASE="http://127.0.0.1:18087" \
|
||||
CRM_HOST_BASE="http://127.0.0.1:18093" \
|
||||
REMOTE_HOST_BASE="http://127.0.0.1:18093" \
|
||||
HOST_NAME="human-friendly-host-name" \
|
||||
ROOT="$artifact_dir/root" \
|
||||
ART="$artifact_dir/run" \
|
||||
PACK_PATH="$pack_dir" \
|
||||
@@ -553,6 +555,14 @@ EOF
|
||||
assert_not_contains "$ssh_contents" "http://127.0.0.1:18087/v1/models"
|
||||
assert_not_contains "$ssh_contents" "http://127.0.0.1:18087/v1/chat/completions"
|
||||
assert_not_contains "$ssh_contents" "user-key"
|
||||
|
||||
local provider_status
|
||||
provider_status="$(cat "$artifact_dir/run/13-provider-status.json")"
|
||||
assert_contains "$provider_status" '"status":"ready"'
|
||||
|
||||
local access_status
|
||||
access_status="$(cat "$artifact_dir/run/14-access-status.json")"
|
||||
assert_contains "$access_status" '"latest_access_status":"subscription_ready"'
|
||||
}
|
||||
|
||||
run_test_migrate_historical_artifacts() {
|
||||
@@ -642,10 +652,111 @@ EOF
|
||||
[[ -f "$sensitive_root/20260522_foo/05-subscription-access-prep.sql" ]] || fail "sql file was not moved to sensitive mirror"
|
||||
}
|
||||
|
||||
run_test_remote43_patched_stack_renderers() {
|
||||
# shellcheck disable=SC1091
|
||||
source "$ROOT_DIR/scripts/remote43_patched_stack_lib.sh"
|
||||
|
||||
local host_env crm_env bootstrap
|
||||
host_env="$(render_remote43_host_env "stack-pg" "stack-redis" "db-pass" "sub2api" "admin@sub2api.local" "admin-pass" "jwt-secret" "totp-secret")"
|
||||
crm_env="$(render_remote43_crm_env "18143" "file:/tmp/sub2api.db?_foreign_keys=on" "crm-token")"
|
||||
bootstrap="$(render_remote43_bootstrap_script \
|
||||
"/home/ubuntu/test-stack" \
|
||||
"/home/ubuntu/test-stack/.env.host" \
|
||||
"/home/ubuntu/test-stack/.env.crm" \
|
||||
"sub2api-patched" \
|
||||
"sub2api-cn-relay-manager-server" \
|
||||
"/home/ubuntu/test-stack/data" \
|
||||
"/home/ubuntu/test-stack/sub2api-cn-relay-manager.db" \
|
||||
"/home/ubuntu/test-stack/crm.pid" \
|
||||
"/home/ubuntu/test-stack/crm.log" \
|
||||
"test-stack-app" \
|
||||
"test-stack-pg" \
|
||||
"test-stack-redis" \
|
||||
"test-stack-net" \
|
||||
"weishaw/sub2api:0.1.129" \
|
||||
"postgres:16-alpine" \
|
||||
"redis:7-alpine" \
|
||||
"db-pass" \
|
||||
"sub2api" \
|
||||
"18139" \
|
||||
"18143" \
|
||||
"8080")"
|
||||
|
||||
assert_contains "$host_env" "AUTO_SETUP=true"
|
||||
assert_contains "$host_env" "DATABASE_HOST=stack-pg"
|
||||
assert_contains "$host_env" "REDIS_HOST=stack-redis"
|
||||
assert_contains "$crm_env" "SUB2API_CRM_LISTEN_ADDR=127.0.0.1:18143"
|
||||
assert_contains "$crm_env" "SUB2API_CRM_ADMIN_TOKEN=crm-token"
|
||||
assert_contains "$bootstrap" 'rm -f "$DATA_DIR/install.lock" "$DATA_DIR/config.yaml" "$DATA_DIR/.installed"'
|
||||
assert_contains "$bootstrap" '-v "$HOST_BINARY:/app/sub2api:ro"'
|
||||
assert_contains "$bootstrap" '-p "127.0.0.1:$HOST_PORT:$HOST_CONTAINER_PORT"'
|
||||
assert_contains "$bootstrap" '/api/v1/auth/login'
|
||||
assert_contains "$bootstrap" '/healthz'
|
||||
assert_contains "$bootstrap" 'source "$1"; set +a; exec "$2"'
|
||||
}
|
||||
|
||||
run_test_setup_remote43_patched_stack_dry_run() {
|
||||
local tmpdir pack_dir shared_pack_dir host_bin crm_bin operator_env tunnel_script stdout_file ssh_key
|
||||
tmpdir="$(mktemp -d)"
|
||||
trap 'rm -rf "$tmpdir"' RETURN
|
||||
pack_dir="$tmpdir/pack"
|
||||
shared_pack_dir="$tmpdir/shared-pack"
|
||||
host_bin="$tmpdir/sub2api-patched"
|
||||
crm_bin="$tmpdir/server"
|
||||
operator_env="$tmpdir/operator.env"
|
||||
tunnel_script="$tmpdir/tunnel.sh"
|
||||
stdout_file="$tmpdir/setup.stdout.txt"
|
||||
ssh_key="$tmpdir/remote43.pem"
|
||||
|
||||
mkdir -p "$pack_dir/providers"
|
||||
printf '%s\n' '{"pack_id":"openai-cn-pack","version":"1.1.3"}' > "$pack_dir/pack.json"
|
||||
printf '%s\n' '{"provider_id":"kimi-a7m"}' > "$pack_dir/providers/kimi-a7m.json"
|
||||
printf '%s\n' '#!/usr/bin/env bash' > "$host_bin"
|
||||
printf '%s\n' '#!/usr/bin/env bash' > "$crm_bin"
|
||||
printf '%s\n' 'dummy-key' > "$ssh_key"
|
||||
chmod +x "$host_bin" "$crm_bin"
|
||||
|
||||
KEY="$ssh_key" \
|
||||
REMOTE="ubuntu@example.com" \
|
||||
STACK_NAME="test-stack" \
|
||||
HOST_PORT=18139 \
|
||||
CRM_PORT=18143 \
|
||||
HOST_BINARY="$host_bin" \
|
||||
CRM_BINARY="$crm_bin" \
|
||||
PACK_DIR="$pack_dir" \
|
||||
LOCAL_SHARED_PACK_DIR="$shared_pack_dir" \
|
||||
LOCAL_OPERATOR_ENV_FILE="$operator_env" \
|
||||
LOCAL_TUNNEL_SCRIPT="$tunnel_script" \
|
||||
REMOTE_ROOT="/home/ubuntu/test-stack" \
|
||||
DRY_RUN=1 \
|
||||
bash "$ROOT_DIR/scripts/setup_remote43_patched_stack.sh" >"$stdout_file"
|
||||
|
||||
[[ -f "$operator_env" ]] || fail "operator env file was not created"
|
||||
[[ -f "$tunnel_script" ]] || fail "tunnel script was not created"
|
||||
[[ -f "$shared_pack_dir/pack.json" ]] || fail "shared pack mirror was not created"
|
||||
|
||||
local stdout_text operator_env_text tunnel_text
|
||||
stdout_text="$(cat "$stdout_file")"
|
||||
operator_env_text="$(cat "$operator_env")"
|
||||
tunnel_text="$(cat "$tunnel_script")"
|
||||
|
||||
assert_contains "$stdout_text" "remote43 patched stack prepared"
|
||||
assert_contains "$stdout_text" "local operator env file: $operator_env"
|
||||
assert_contains "$stdout_text" "DRY_RUN: ssh -i $ssh_key"
|
||||
assert_contains "$operator_env_text" "CRM_BASE=http://127.0.0.1:18143"
|
||||
assert_contains "$operator_env_text" "HOST_BASE=http://127.0.0.1:18139"
|
||||
assert_contains "$operator_env_text" "PACK_PATH=$shared_pack_dir"
|
||||
assert_contains "$operator_env_text" "REMOTE_HOST_ENV_FILE=/home/ubuntu/test-stack/.env.host"
|
||||
assert_contains "$tunnel_text" "-L 18143:127.0.0.1:18143"
|
||||
assert_contains "$tunnel_text" "-L 18139:127.0.0.1:18139"
|
||||
}
|
||||
|
||||
run_test_build_subscription_access_prep_sql
|
||||
run_test_real_host_acceptance_after_import_hook
|
||||
run_test_check_deepseek_completion_split
|
||||
run_test_import_remote43_provider_subscription_prep
|
||||
run_test_migrate_historical_artifacts
|
||||
run_test_remote43_patched_stack_renderers
|
||||
run_test_setup_remote43_patched_stack_dry_run
|
||||
|
||||
echo "PASS: real host script regression checks"
|
||||
|
||||
Reference in New Issue
Block a user