feat(scripts): automate real-host access prep closure
This commit is contained in:
99
scripts/host_access_prep_lib.sh
Normal file
99
scripts/host_access_prep_lib.sh
Normal file
@@ -0,0 +1,99 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
sql_escape_literal() {
|
||||
local value="$1"
|
||||
local squote="'"
|
||||
value=${value//$squote/$squote$squote}
|
||||
printf '%s' "$value"
|
||||
}
|
||||
|
||||
sql_literal() {
|
||||
printf "'%s'" "$(sql_escape_literal "$1")"
|
||||
}
|
||||
|
||||
build_top_up_user_balance_sql() {
|
||||
local user_id="$1"
|
||||
local min_balance="$2"
|
||||
cat <<SQL
|
||||
UPDATE users
|
||||
SET balance = CASE WHEN balance < ${min_balance} THEN ${min_balance} ELSE balance END,
|
||||
updated_at = now()
|
||||
WHERE id = ${user_id};
|
||||
SQL
|
||||
}
|
||||
|
||||
build_bind_api_key_group_sql() {
|
||||
local api_key="$1"
|
||||
local group_id="$2"
|
||||
cat <<SQL
|
||||
UPDATE api_keys
|
||||
SET group_id = ${group_id},
|
||||
updated_at = now()
|
||||
WHERE key = $(sql_literal "$api_key");
|
||||
SQL
|
||||
}
|
||||
|
||||
build_upsert_subscription_sql() {
|
||||
local user_id="$1"
|
||||
local group_id="$2"
|
||||
local duration_days="$3"
|
||||
local assigned_by="$4"
|
||||
local notes="$5"
|
||||
cat <<SQL
|
||||
INSERT INTO user_subscriptions (
|
||||
user_id,
|
||||
group_id,
|
||||
starts_at,
|
||||
expires_at,
|
||||
status,
|
||||
assigned_by,
|
||||
assigned_at,
|
||||
notes,
|
||||
created_at,
|
||||
updated_at,
|
||||
deleted_at
|
||||
)
|
||||
VALUES (
|
||||
${user_id},
|
||||
${group_id},
|
||||
now(),
|
||||
now() + interval '$(sql_escape_literal "$duration_days") days',
|
||||
'active',
|
||||
${assigned_by},
|
||||
now(),
|
||||
$(sql_literal "$notes"),
|
||||
now(),
|
||||
now(),
|
||||
NULL
|
||||
)
|
||||
ON CONFLICT (user_id, group_id) WHERE deleted_at IS NULL
|
||||
DO UPDATE SET
|
||||
starts_at = EXCLUDED.starts_at,
|
||||
expires_at = EXCLUDED.expires_at,
|
||||
status = 'active',
|
||||
assigned_by = EXCLUDED.assigned_by,
|
||||
assigned_at = EXCLUDED.assigned_at,
|
||||
notes = EXCLUDED.notes,
|
||||
updated_at = now(),
|
||||
deleted_at = NULL;
|
||||
SQL
|
||||
}
|
||||
|
||||
build_subscription_access_prep_sql() {
|
||||
local user_id="$1"
|
||||
local api_key="$2"
|
||||
local group_id="$3"
|
||||
local min_balance="$4"
|
||||
local duration_days="$5"
|
||||
local assigned_by="$6"
|
||||
local notes="$7"
|
||||
|
||||
cat <<SQL
|
||||
BEGIN;
|
||||
$(build_top_up_user_balance_sql "$user_id" "$min_balance")
|
||||
$(build_bind_api_key_group_sql "$api_key" "$group_id")
|
||||
$(build_upsert_subscription_sql "$user_id" "$group_id" "$duration_days" "$assigned_by" "$notes")
|
||||
COMMIT;
|
||||
SQL
|
||||
}
|
||||
233
scripts/import_remote43_provider.sh
Executable file
233
scripts/import_remote43_provider.sh
Executable file
@@ -0,0 +1,233 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
provider_id="${1:?provider_id required}"
|
||||
model_name="${2:?model_name required}"
|
||||
env_var="${3:?env var required}"
|
||||
key_file="${4:-}"
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
# shellcheck disable=SC1091
|
||||
source "$ROOT_DIR/scripts/host_access_prep_lib.sh"
|
||||
|
||||
KEY="${KEY:-/home/long/下载/zjsea.pem}"
|
||||
REMOTE="${REMOTE:-ubuntu@43.155.133.187}"
|
||||
CRM_BASE="${CRM_BASE:-http://127.0.0.1:18088}"
|
||||
HOST_BASE="${HOST_BASE:-http://127.0.0.1:18087}"
|
||||
PACK_PATH="${PACK_PATH:-/home/ubuntu/sub2api-cn-relay-manager/packs/openai-cn-pack}"
|
||||
ROOT="${ROOT:-$ROOT_DIR/artifacts/real-host-acceptance}"
|
||||
ART="${ART:-$ROOT/$(date +%Y%m%d_%H%M%S)_remote43_${provider_id}_key_import}"
|
||||
MIN_BALANCE="${MIN_BALANCE:-10}"
|
||||
SUBSCRIPTION_DAYS="${SUBSCRIPTION_DAYS:-30}"
|
||||
SUBSCRIPTION_NOTES="${SUBSCRIPTION_NOTES:-hermes remote subscription validation}"
|
||||
mkdir -p "$ART"
|
||||
|
||||
if [[ -n "$key_file" ]]; then
|
||||
upstream_key="$(tr -d '\r\n' < "$key_file")"
|
||||
key_source="file:$key_file"
|
||||
else
|
||||
upstream_key="${!env_var:-}"
|
||||
key_source="env:$env_var"
|
||||
fi
|
||||
if [[ -z "$upstream_key" ]]; then
|
||||
echo "missing key from $key_source" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
ssh_cmd() {
|
||||
local cmd="$1"
|
||||
ssh -i "$KEY" -o StrictHostKeyChecking=no "$REMOTE" "$cmd"
|
||||
}
|
||||
|
||||
remote_pg_exec() {
|
||||
local sql="$1"
|
||||
local encoded
|
||||
encoded="$(printf '%s' "$sql" | base64 -w0)"
|
||||
ssh_cmd "printf '%s' '$encoded' | base64 -d | sudo -n docker exec -i sub2api-relaymgr-pg psql -U sub2api -d sub2api"
|
||||
}
|
||||
|
||||
remote_fetch_group_state() {
|
||||
local group_id="$1"
|
||||
local user_id="$2"
|
||||
local api_key="$3"
|
||||
local output_path="$4"
|
||||
local encoded
|
||||
encoded="$(python3 - "$group_id" "$user_id" "$api_key" <<'PY'
|
||||
import json, sys
|
||||
|
||||
group_id, user_id, api_key = sys.argv[1:4]
|
||||
query = f"""
|
||||
WITH group_row AS (
|
||||
SELECT row_to_json(g) AS data FROM groups g WHERE g.id = {group_id}
|
||||
),
|
||||
subscription_row AS (
|
||||
SELECT row_to_json(s) AS data FROM user_subscriptions s
|
||||
WHERE s.user_id = {user_id} AND s.group_id = {group_id} AND s.deleted_at IS NULL
|
||||
ORDER BY s.id DESC LIMIT 1
|
||||
),
|
||||
key_row AS (
|
||||
SELECT row_to_json(k) AS data FROM api_keys k WHERE k.key = {json.dumps(api_key)}
|
||||
)
|
||||
SELECT json_build_object(
|
||||
'group_id', {group_id},
|
||||
'group', (SELECT data FROM group_row),
|
||||
'subscription', (SELECT data FROM subscription_row),
|
||||
'key', (SELECT data FROM key_row)
|
||||
);
|
||||
"""
|
||||
print(query)
|
||||
PY
|
||||
)"
|
||||
ssh_cmd "printf '%s' '$encoded' | base64 -d | sudo -n docker exec -i sub2api-relaymgr-pg psql -U sub2api -d sub2api -At -F ''" > "$output_path"
|
||||
}
|
||||
|
||||
python3 - "$ART/00-local-key-source.json" "$key_source" "$provider_id" "$upstream_key" <<'PY'
|
||||
import json, sys, pathlib
|
||||
path, source, provider_id, key = sys.argv[1:5]
|
||||
pathlib.Path(path).write_text(json.dumps({
|
||||
'source': source,
|
||||
'provider_id': provider_id,
|
||||
'upstream_key_prefix': key[:12],
|
||||
'upstream_key_suffix': key[-6:],
|
||||
}, ensure_ascii=False, indent=2), encoding='utf-8')
|
||||
PY
|
||||
|
||||
crm_token="$(ssh_cmd "grep ^SUB2API_CRM_ADMIN_TOKEN= /home/ubuntu/sub2api-cn-relay-manager/.env.remote | cut -d= -f2-")"
|
||||
crm_token="${crm_token##*$'\n'}"
|
||||
admin_key="$(ssh_cmd "sudo -n docker exec sub2api-relaymgr-pg psql -U sub2api -d sub2api -Atc \"select value from settings where key='admin_api_key';\"")"
|
||||
admin_key="${admin_key##*$'\n'}"
|
||||
admin_uid="$(ssh_cmd "sudo -n docker exec sub2api-relaymgr-pg 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="$(ssh_cmd "sudo -n docker exec sub2api-relaymgr-pg psql -U sub2api -d sub2api -Atc \"select id from users where email like 'relay-sub-%@sub2api.local' order by id desc limit 1;\"")"
|
||||
sub_uid="${sub_uid##*$'\n'}"
|
||||
sub_key="$(ssh_cmd "sudo -n docker exec sub2api-relaymgr-pg psql -U sub2api -d sub2api -Atc \"select k.key from users u join api_keys k on k.user_id=u.id where u.email like 'relay-sub-%@sub2api.local' order by u.id desc limit 1;\"")"
|
||||
sub_key="${sub_key##*$'\n'}"
|
||||
|
||||
python3 - "$ART/01-runtime-context.json" "$CRM_BASE" "$HOST_BASE" "$provider_id" "$sub_uid" "$sub_key" <<'PY'
|
||||
import json, sys, pathlib
|
||||
path, crm, host, provider_id, sub_uid, sub_key = sys.argv[1:7]
|
||||
pathlib.Path(path).write_text(json.dumps({
|
||||
'crm_base': crm,
|
||||
'host_base': host,
|
||||
'provider_id': provider_id,
|
||||
'subscription_user_id': sub_uid,
|
||||
'subscription_user_key_prefix': sub_key[:12],
|
||||
}, ensure_ascii=False, indent=2), encoding='utf-8')
|
||||
PY
|
||||
|
||||
payload="$(python3 - "$HOST_BASE" "$admin_key" "$PACK_PATH" "$provider_id" "$upstream_key" "$sub_key" "$sub_uid" "$SUBSCRIPTION_DAYS" <<'PY'
|
||||
import json, sys
|
||||
host_base, admin_key, pack_path, provider_id, upstream_key, sub_key, sub_uid, subscription_days = sys.argv[1:9]
|
||||
print(json.dumps({
|
||||
'host_base_url': host_base,
|
||||
'host_api_key': admin_key,
|
||||
'pack_path': pack_path,
|
||||
'provider_id': provider_id,
|
||||
'keys': [upstream_key],
|
||||
'mode': 'partial',
|
||||
'access_mode': 'subscription',
|
||||
'access_api_key': sub_key,
|
||||
'subscription_days': int(subscription_days),
|
||||
'subscription_users': [sub_uid],
|
||||
}, ensure_ascii=False))
|
||||
PY
|
||||
)"
|
||||
|
||||
ssh_cmd "curl -sS -D /tmp/import_headers.txt -o /tmp/import_body.json -X POST -H 'Authorization: Bearer $crm_token' -H 'Content-Type: application/json' $CRM_BASE/api/providers/$provider_id/import -d $(printf %q "$payload")"
|
||||
ssh_cmd "cat /tmp/import_headers.txt" > "$ART/02-import.headers.txt"
|
||||
ssh_cmd "cat /tmp/import_body.json" > "$ART/03-import.body.json"
|
||||
|
||||
batch_id="$(python3 - "$ART/03-import.body.json" <<'PY'
|
||||
import json, sys, pathlib
|
||||
obj=json.loads(pathlib.Path(sys.argv[1]).read_text())
|
||||
print(obj['batch_id'])
|
||||
PY
|
||||
)"
|
||||
|
||||
ssh_cmd "curl -sS -H 'Authorization: Bearer *** $CRM_BASE/api/import-batches/$batch_id" > "$ART/04-batch-detail-initial.json"
|
||||
subscription_group_id="$(python3 - "$ART/04-batch-detail-initial.json" <<'PY'
|
||||
import json, pathlib, sys
|
||||
obj = json.loads(pathlib.Path(sys.argv[1]).read_text())
|
||||
for item in obj.get('managed_resources', []):
|
||||
if item.get('ResourceType') == 'group':
|
||||
print(item.get('HostResourceID', ''))
|
||||
break
|
||||
else:
|
||||
raise SystemExit('missing managed group in batch detail')
|
||||
PY
|
||||
)"
|
||||
|
||||
prep_sql="$(build_subscription_access_prep_sql "$sub_uid" "$sub_key" "$subscription_group_id" "$MIN_BALANCE" "$SUBSCRIPTION_DAYS" "$admin_uid" "$SUBSCRIPTION_NOTES")"
|
||||
python3 - "$ART/05-subscription-access-prep.sql" "$prep_sql" <<'PY'
|
||||
import pathlib, sys
|
||||
pathlib.Path(sys.argv[1]).write_text(sys.argv[2], encoding='utf-8')
|
||||
PY
|
||||
remote_pg_exec "$prep_sql" > "$ART/06-subscription-access-prep.psql.txt"
|
||||
ssh_cmd "sudo -n docker exec sub2api-relaymgr-redis redis-cli FLUSHDB" > "$ART/07-redis-flush.txt"
|
||||
remote_fetch_group_state "$subscription_group_id" "$sub_uid" "$sub_key" "$ART/08-subscription-group-state.json"
|
||||
|
||||
python3 - "$ART/01-runtime-context.json" "$CRM_BASE" "$HOST_BASE" "$provider_id" "$sub_uid" "$sub_key" "$subscription_group_id" "$admin_uid" <<'PY'
|
||||
import json, sys, pathlib
|
||||
path, crm, host, provider_id, sub_uid, sub_key, group_id, admin_uid = sys.argv[1:9]
|
||||
pathlib.Path(path).write_text(json.dumps({
|
||||
'crm_base': crm,
|
||||
'host_base': host,
|
||||
'provider_id': provider_id,
|
||||
'subscription_user_id': sub_uid,
|
||||
'subscription_user_key_prefix': sub_key[:12],
|
||||
'subscription_group_id': group_id,
|
||||
'admin_user_id': admin_uid,
|
||||
}, ensure_ascii=False, indent=2), encoding='utf-8')
|
||||
PY
|
||||
|
||||
probe_payload="$(python3 - "$model_name" <<'PY'
|
||||
import json, sys
|
||||
print(json.dumps({
|
||||
'model': sys.argv[1],
|
||||
'messages': [{'role':'user','content':'ping'}],
|
||||
'max_tokens': 8,
|
||||
'temperature': 0,
|
||||
}, ensure_ascii=False))
|
||||
PY
|
||||
)"
|
||||
ssh_cmd "curl -sS -D /tmp/chat_headers.txt -o /tmp/chat_body.json -H 'Authorization: Bearer *** -H 'Content-Type: application/json' $HOST_BASE/v1/chat/completions -d $(printf %q "$probe_payload")"
|
||||
ssh_cmd "cat /tmp/chat_headers.txt" > "$ART/09-chat.headers.txt"
|
||||
ssh_cmd "cat /tmp/chat_body.json" > "$ART/10-chat.body.json"
|
||||
|
||||
ssh_cmd "curl -sS -H 'Authorization: Bearer *** $CRM_BASE/api/providers/$provider_id/status" > "$ART/11-provider-status.json"
|
||||
ssh_cmd "curl -sS -H 'Authorization: Bearer *** $CRM_BASE/api/providers/$provider_id/access/status" > "$ART/12-access-status.json"
|
||||
preview_payload="$(python3 - "$provider_id" <<'PY'
|
||||
import json, sys
|
||||
print(json.dumps({'provider_id': sys.argv[1], 'mode': 'subscription'}, ensure_ascii=False))
|
||||
PY
|
||||
)"
|
||||
ssh_cmd "curl -sS -X POST -H 'Authorization: Bearer *** -H 'Content-Type: application/json' $CRM_BASE/api/providers/$provider_id/access/preview -d $(printf %q "$preview_payload")" > "$ART/13-access-preview.json"
|
||||
ssh_cmd "curl -sS -H 'Authorization: Bearer *** $CRM_BASE/api/import-batches/$batch_id" > "$ART/14-batch-detail-final.json"
|
||||
|
||||
python3 - "$ART" "$provider_id" "$batch_id" <<'PY'
|
||||
import json, pathlib, sys
|
||||
art=pathlib.Path(sys.argv[1])
|
||||
provider_id=sys.argv[2]
|
||||
batch_id=int(sys.argv[3])
|
||||
import_obj=json.loads((art/'03-import.body.json').read_text())
|
||||
access_status=json.loads((art/'12-access-status.json').read_text())
|
||||
preview=json.loads((art/'13-access-preview.json').read_text())
|
||||
chat_headers=(art/'09-chat.headers.txt').read_text()
|
||||
group_state=json.loads((art/'08-subscription-group-state.json').read_text())
|
||||
summary={
|
||||
'artifact_dir': str(art),
|
||||
'provider_id': provider_id,
|
||||
'batch_id': batch_id,
|
||||
'batch_status': import_obj.get('batch_status'),
|
||||
'access_status_from_import': import_obj.get('access_status'),
|
||||
'provider_status_from_import': import_obj.get('provider_status'),
|
||||
'direct_chat_http200': '200 OK' in chat_headers,
|
||||
'latest_access_status': access_status.get('latest_access_status') or access_status.get('batch_access_status'),
|
||||
'preview_available': preview.get('available'),
|
||||
'accepted_keys_count': import_obj.get('accepted_keys_count'),
|
||||
'subscription_group_id': group_state.get('group_id'),
|
||||
'key_group_id': (group_state.get('key') or {}).get('group_id'),
|
||||
'subscription_status': (group_state.get('subscription') or {}).get('status'),
|
||||
}
|
||||
print(json.dumps(summary, ensure_ascii=False))
|
||||
PY
|
||||
@@ -116,6 +116,7 @@ require_var PROVIDER_ID
|
||||
MODE="${MODE:-partial}"
|
||||
ACCESS_MODE="${ACCESS_MODE:-self_service}"
|
||||
SUBSCRIPTION_DAYS="${SUBSCRIPTION_DAYS:-30}"
|
||||
AFTER_IMPORT_HOOK_COMMAND="${AFTER_IMPORT_HOOK_COMMAND:-}"
|
||||
|
||||
if [[ -n "${HOST_BEARER_TOKEN:-}" ]]; then
|
||||
HOST_AUTH_TYPE="${HOST_AUTH_TYPE:-bearer}"
|
||||
@@ -208,6 +209,21 @@ RESP_IMPORT="$(curl_json POST "/api/providers/$PROVIDER_ID/import" "$IMPORT_PAYL
|
||||
save_json 05-import "$RESP_IMPORT"
|
||||
BATCH_ID="$(printf '%s' "$RESP_IMPORT" | json_get batch_id || true)"
|
||||
|
||||
if [[ -n "$BATCH_ID" && "$DRY_RUN" != "1" ]]; then
|
||||
RESP_BATCH_DETAIL="$(curl_json GET "/api/import-batches/$BATCH_ID")"
|
||||
save_json 05a-batch-detail-pre-access "$RESP_BATCH_DETAIL"
|
||||
export BATCH_DETAIL_FILE="$ARTIFACT_DIR/05a-batch-detail-pre-access.json"
|
||||
else
|
||||
unset BATCH_DETAIL_FILE || true
|
||||
fi
|
||||
|
||||
if [[ -n "$AFTER_IMPORT_HOOK_COMMAND" ]]; then
|
||||
export BATCH_ID PROVIDER_ID HOST_BASE_URL CRM_BASE_URL ACCESS_MODE MODE ARTIFACT_DIR
|
||||
bash -lc "$AFTER_IMPORT_HOOK_COMMAND" \
|
||||
>"$ARTIFACT_DIR/05b-after-import-hook.stdout.txt" \
|
||||
2>"$ARTIFACT_DIR/05b-after-import-hook.stderr.txt"
|
||||
fi
|
||||
|
||||
echo "batch_id=${BATCH_ID:-unknown}"
|
||||
|
||||
ACCESS_PREVIEW_PAYLOAD="$(python3 - <<'PY'
|
||||
|
||||
249
scripts/test_real_host_scripts.sh
Normal file
249
scripts/test_real_host_scripts.sh
Normal file
@@ -0,0 +1,249 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
|
||||
fail() {
|
||||
echo "FAIL: $*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
assert_contains() {
|
||||
local haystack="$1"
|
||||
local needle="$2"
|
||||
if [[ "$haystack" != *"$needle"* ]]; then
|
||||
fail "expected to find [$needle] in [$haystack]"
|
||||
fi
|
||||
}
|
||||
|
||||
run_test_build_subscription_access_prep_sql() {
|
||||
# shellcheck disable=SC1091
|
||||
source "$ROOT_DIR/scripts/host_access_prep_lib.sh"
|
||||
|
||||
local sql
|
||||
sql="$(build_subscription_access_prep_sql 42 'sk-test-123' 7 10 30 1 'hermes remote subscription validation')"
|
||||
|
||||
assert_contains "$sql" "UPDATE users"
|
||||
assert_contains "$sql" "balance < 10"
|
||||
assert_contains "$sql" "UPDATE api_keys"
|
||||
assert_contains "$sql" "group_id = 7"
|
||||
assert_contains "$sql" "key = 'sk-test-123'"
|
||||
assert_contains "$sql" "INSERT INTO user_subscriptions"
|
||||
assert_contains "$sql" "ON CONFLICT (user_id, group_id) WHERE deleted_at IS NULL"
|
||||
assert_contains "$sql" "now() + interval '30 days'"
|
||||
|
||||
local quoted_sql
|
||||
quoted_sql="$(build_bind_api_key_group_sql "sk-o'reilly" 7)"
|
||||
assert_contains "$quoted_sql" "WHERE key = 'sk-o''reilly'"
|
||||
}
|
||||
|
||||
run_test_real_host_acceptance_after_import_hook() {
|
||||
local tmpdir fakebin artifact_dir hook_file
|
||||
tmpdir="$(mktemp -d)"
|
||||
trap 'rm -rf "$tmpdir"' RETURN
|
||||
fakebin="$tmpdir/bin"
|
||||
artifact_dir="$tmpdir/artifacts"
|
||||
hook_file="$artifact_dir/hook.txt"
|
||||
mkdir -p "$fakebin"
|
||||
|
||||
cat > "$fakebin/curl" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
url=""
|
||||
for arg in "$@"; do
|
||||
if [[ "$arg" == http://* || "$arg" == https://* ]]; then
|
||||
url="$arg"
|
||||
fi
|
||||
done
|
||||
[[ -n "$url" ]] || {
|
||||
echo "missing url in curl args: $*" >&2
|
||||
exit 1
|
||||
}
|
||||
case "$url" in
|
||||
*/api/hosts/test-host)
|
||||
printf '%s\n' '{"host_id":"test-host"}'
|
||||
;;
|
||||
*/api/hosts/test-host/probe)
|
||||
printf '%s\n' '{"ok":true}'
|
||||
;;
|
||||
*/api/packs/install)
|
||||
printf '%s\n' '{"pack_id":1}'
|
||||
;;
|
||||
*/api/providers/deepseek/preview-import)
|
||||
printf '%s\n' '{"available":true}'
|
||||
;;
|
||||
*/api/providers/deepseek/import)
|
||||
printf '%s\n' '{"batch_id":123,"batch_status":"partially_succeeded","access_status":"broken"}'
|
||||
;;
|
||||
*/api/import-batches/123)
|
||||
printf '%s\n' '{"managed_resources":[{"ResourceType":"group","HostResourceID":"7","ResourceName":"DeepSeek 默认分组"}]}'
|
||||
;;
|
||||
*/api/providers/deepseek/access/preview)
|
||||
printf '%s\n' '{"available":true}'
|
||||
;;
|
||||
*/api/providers/deepseek/access/status)
|
||||
printf '%s\n' '{"latest_access_status":"subscription_ready"}'
|
||||
;;
|
||||
*/api/providers/deepseek/status)
|
||||
printf '%s\n' '{"status":"ready"}'
|
||||
;;
|
||||
*/api/providers/deepseek/reconcile)
|
||||
printf '%s\n' '{"status":"in_sync"}'
|
||||
;;
|
||||
*/api/import-batches/123/rollback)
|
||||
printf '%s\n' '{"status":"rolled_back"}'
|
||||
;;
|
||||
*)
|
||||
echo "unexpected curl url: $url" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
EOF
|
||||
chmod +x "$fakebin/curl"
|
||||
|
||||
PATH="$fakebin:$PATH" \
|
||||
ARTIFACT_DIR="$artifact_dir" \
|
||||
CRM_BASE_URL="http://crm.example.com" \
|
||||
CRM_ADMIN_TOKEN="token" \
|
||||
HOST_NAME="test-host" \
|
||||
HOST_BASE_URL="http://host.example.com" \
|
||||
PACK_PATH="/tmp/openai-pack" \
|
||||
PROVIDER_ID="deepseek" \
|
||||
HOST_API_KEY="host-key" \
|
||||
MODE="partial" \
|
||||
ACCESS_MODE="subscription" \
|
||||
ACCESS_API_KEY="user-key" \
|
||||
SUBSCRIPTION_USERS="42" \
|
||||
SKIP_ROLLBACK="1" \
|
||||
AFTER_IMPORT_HOOK_COMMAND='printf "%s\n" "$BATCH_ID:$BATCH_DETAIL_FILE:$ACCESS_MODE" > "$ARTIFACT_DIR/hook.txt"' \
|
||||
"$ROOT_DIR/scripts/real_host_acceptance.sh" >/dev/null
|
||||
|
||||
[[ -f "$hook_file" ]] || fail "after-import hook did not create $hook_file"
|
||||
local hook_contents
|
||||
hook_contents="$(cat "$hook_file")"
|
||||
assert_contains "$hook_contents" "123:"
|
||||
assert_contains "$hook_contents" "05a-batch-detail-pre-access.json:subscription"
|
||||
}
|
||||
|
||||
run_test_import_remote43_provider_subscription_prep() {
|
||||
local tmpdir fakebin artifact_dir ssh_log psql_sql
|
||||
tmpdir="$(mktemp -d)"
|
||||
trap 'rm -rf "$tmpdir"' RETURN
|
||||
fakebin="$tmpdir/bin"
|
||||
artifact_dir="$tmpdir/artifacts"
|
||||
ssh_log="$artifact_dir/ssh-log.txt"
|
||||
psql_sql="$artifact_dir/prep.sql"
|
||||
mkdir -p "$fakebin"
|
||||
|
||||
cat > "$fakebin/ssh" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
log_dir="${FAKE_REMOTE_LOG_DIR:?missing FAKE_REMOTE_LOG_DIR}"
|
||||
cmd="${*: -1}"
|
||||
printf '%s\n' "$cmd" >> "$log_dir/ssh-log.txt"
|
||||
case "$cmd" in
|
||||
*"grep ^SUB2API_CRM_ADMIN_TOKEN="*)
|
||||
printf '%s\n' 'crm-token'
|
||||
;;
|
||||
*"select value from settings where key='admin_api_key'"*)
|
||||
printf '%s\n' 'admin-key'
|
||||
;;
|
||||
*"select id from users where role='admin'"*)
|
||||
printf '%s\n' '1'
|
||||
;;
|
||||
*"select id from users where email like 'relay-sub-%@sub2api.local'"*)
|
||||
printf '%s\n' '42'
|
||||
;;
|
||||
*"select k.key from users u join api_keys k on k.user_id=u.id"*)
|
||||
printf '%s\n' 'user-key'
|
||||
;;
|
||||
*"curl -sS -D /tmp/import_headers.txt"*"/api/providers/deepseek/import"*)
|
||||
printf '%s\n' '{"batch_id":123,"batch_status":"partially_succeeded","access_status":"broken"}' > /tmp/import_body.json
|
||||
printf '%s\n' 'HTTP/1.1 200 OK' > /tmp/import_headers.txt
|
||||
;;
|
||||
"cat /tmp/import_headers.txt")
|
||||
cat /tmp/import_headers.txt
|
||||
;;
|
||||
"cat /tmp/import_body.json")
|
||||
cat /tmp/import_body.json
|
||||
;;
|
||||
*"curl -sS -H 'Authorization: Bearer *** http://127.0.0.1:18088/api/import-batches/123"*)
|
||||
printf '%s\n' '{"managed_resources":[{"ResourceType":"group","HostResourceID":"7","ResourceName":"DeepSeek 默认分组"}]}'
|
||||
;;
|
||||
*"curl -sS -D /tmp/chat_headers.txt"*)
|
||||
printf '%s\n' 'HTTP/1.1 200 OK' > /tmp/chat_headers.txt
|
||||
printf '%s\n' '{"choices":[{"message":{"content":"pong"}}]}' > /tmp/chat_body.json
|
||||
;;
|
||||
"cat /tmp/chat_headers.txt")
|
||||
cat /tmp/chat_headers.txt
|
||||
;;
|
||||
"cat /tmp/chat_body.json")
|
||||
cat /tmp/chat_body.json
|
||||
;;
|
||||
*"curl -sS -H 'Authorization: Bearer *** http://127.0.0.1:18088/api/providers/deepseek/status"*)
|
||||
printf '%s\n' '{"status":"ready"}'
|
||||
;;
|
||||
*"curl -sS -H 'Authorization: Bearer *** http://127.0.0.1:18088/api/providers/deepseek/access/status"*)
|
||||
printf '%s\n' '{"latest_access_status":"subscription_ready"}'
|
||||
;;
|
||||
*"curl -sS -X POST -H 'Authorization: Bearer *** -H 'Content-Type: application/json' http://127.0.0.1:18088/api/providers/deepseek/access/preview"*)
|
||||
printf '%s\n' '{"available":true}'
|
||||
;;
|
||||
*"curl -sS -H 'Authorization: Bearer *** http://127.0.0.1:18088/api/providers/deepseek/reconcile"*)
|
||||
printf '%s\n' '{"status":"in_sync"}'
|
||||
;;
|
||||
*"sudo -n docker exec -i sub2api-relaymgr-pg psql -U sub2api -d sub2api -At -F ''"*)
|
||||
printf '%s\n' '{"group_id":7,"subscription":{"status":"active"},"key":{"group_id":7}}'
|
||||
;;
|
||||
*"sudo -n docker exec -i sub2api-relaymgr-pg psql -U sub2api -d sub2api"*)
|
||||
CMD="$cmd" LOG_DIR="$log_dir" python3 - <<'PY'
|
||||
import base64, os, re, pathlib
|
||||
cmd = os.environ['CMD']
|
||||
log_dir = pathlib.Path(os.environ['LOG_DIR'])
|
||||
match = re.search(r"printf '%s' '([^']+)' \| base64 -d", cmd)
|
||||
if not match:
|
||||
raise SystemExit(f'failed to extract base64 payload from: {cmd}')
|
||||
log_dir.joinpath('prep.sql').write_bytes(base64.b64decode(match.group(1)))
|
||||
PY
|
||||
;;
|
||||
*"sudo -n docker exec sub2api-relaymgr-redis redis-cli FLUSHDB"*)
|
||||
printf '%s\n' 'OK'
|
||||
;;
|
||||
*)
|
||||
echo "unexpected ssh command: $cmd" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
EOF
|
||||
chmod +x "$fakebin/ssh"
|
||||
|
||||
PATH="$fakebin:$PATH" \
|
||||
FAKE_REMOTE_LOG_DIR="$artifact_dir" \
|
||||
KEY="/does/not/matter" \
|
||||
REMOTE="fake@host" \
|
||||
CRM_BASE="http://127.0.0.1:18088" \
|
||||
HOST_BASE="http://127.0.0.1:18087" \
|
||||
ROOT="$artifact_dir/root" \
|
||||
ART="$artifact_dir/run" \
|
||||
PACK_PATH="/tmp/openai-pack" \
|
||||
UPSTREAM_KEY="upstream-test-key" \
|
||||
SUBSCRIPTION_DAYS=30 \
|
||||
MIN_BALANCE=10 \
|
||||
SKIP_ROLLBACK=1 \
|
||||
bash "$ROOT_DIR/scripts/import_remote43_provider.sh" deepseek gpt-4 UPSTREAM_KEY >/dev/null
|
||||
|
||||
[[ -f "$psql_sql" ]] || fail "prep sql was not captured"
|
||||
local prep_sql
|
||||
prep_sql="$(cat "$psql_sql")"
|
||||
assert_contains "$prep_sql" "UPDATE users"
|
||||
assert_contains "$prep_sql" "UPDATE api_keys"
|
||||
assert_contains "$prep_sql" "INSERT INTO user_subscriptions"
|
||||
assert_contains "$prep_sql" "group_id = 7"
|
||||
[[ -s "$ssh_log" ]] || fail "ssh log was empty"
|
||||
}
|
||||
|
||||
run_test_build_subscription_access_prep_sql
|
||||
run_test_real_host_acceptance_after_import_hook
|
||||
run_test_import_remote43_provider_subscription_prep
|
||||
|
||||
echo "PASS: real host script regression checks"
|
||||
Reference in New Issue
Block a user