set -euo pipefail
BASE=http://127.0.0.1:48090
ENV=/srv/sub2api-80/shared/env/sub2api-80.env
# source 但不 set -a，避免所有变量进入 curl 环境
source "$ENV"
export PGPASSWORD="$DATABASE_PASSWORD"
psql_base=(psql -h "$DATABASE_HOST" -p "$DATABASE_PORT" -U "$DATABASE_USER" -d "$DATABASE_DBNAME" -Atq)
PREV_TURNSTILE=$("${psql_base[@]}" -c "select value from settings where key='turnstile_enabled';")
"${psql_base[@]}" -c "update settings set value='false', updated_at=now() where key='turnstile_enabled';" >/tmp/node80_turnstile_disable.txt
restore(){ "${psql_base[@]}" -c "update settings set value='${PREV_TURNSTILE}', updated_at=now() where key='turnstile_enabled';" >/tmp/node80_turnstile_restore.txt || true; }
trap restore EXIT
EMAIL="image-studio-r2-node80-$(date +%Y%m%d%H%M%S)@example.com"
PASS="ImageR2Test$(date +%s)!"
admin_body=/tmp/node80_admin_login.json
admin_code=$(jq -cn --arg email "$ADMIN_EMAIL" --arg password "$ADMIN_PASSWORD" '{email:$email,password:$password}' | curl -sS -w "%{http_code}" -o "$admin_body" -X POST "$BASE/api/v1/auth/login" -H "Content-Type: application/json" --data-binary @-)
ADMIN_JWT=$(jq -r ".data.access_token // .access_token // empty" "$admin_body")
[ "$admin_code" = "200" ] && [ -n "$ADMIN_JWT" ] || { echo "admin_login_failed code=$admin_code"; cat "$admin_body"; exit 20; }
create_body=/tmp/node80_create_user.json
create_code=$(jq -cn --arg email "$EMAIL" --arg password "$PASS" '{email:$email,password:$password,username:"Image Studio R2 Node80",notes:"temp image studio r2 acceptance 20260516",balance:0,concurrency:1}' | curl -sS -w "%{http_code}" -o "$create_body" -X POST "$BASE/api/v1/admin/users" -H "Authorization: Bearer $ADMIN_JWT" -H "Content-Type: application/json" --data-binary @-)
USER_ID=$(jq -r ".data.id // .id // empty" "$create_body")
[ "$create_code" = "200" ] && [ -n "$USER_ID" ] || { echo "create_user_failed code=$create_code"; cat "$create_body"; exit 21; }
cleanup_user(){ curl -sS -X DELETE "$BASE/api/v1/admin/users/$USER_ID" -H "Authorization: Bearer $ADMIN_JWT" >/tmp/node80_delete_user.json || true; }
trap "cleanup_user; restore" EXIT
login_body=/tmp/node80_user_login.json
login_code=$(jq -cn --arg email "$EMAIL" --arg password "$PASS" '{email:$email,password:$password}' | curl -sS -w "%{http_code}" -o "$login_body" -X POST "$BASE/api/v1/auth/login" -H "Content-Type: application/json" --data-binary @-)
JWT=$(jq -r ".data.access_token // .access_token // empty" "$login_body")
[ "$login_code" = "200" ] && [ -n "$JWT" ] || { echo "user_login_failed code=$login_code"; cat "$login_body"; exit 22; }
PNG=/tmp/node80_one_pixel.png
python3 - <<'PY' "$PNG"
import base64,sys
open(sys.argv[1],"wb").write(base64.b64decode("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+/p9sAAAAASUVORK5CYII="))
PY
asset_body=/tmp/node80_asset.json
asset_code=$(curl -sS -w "%{http_code}" -o "$asset_body" -X POST "$BASE/api/v1/image-studio/assets" -H "Authorization: Bearer $JWT" -F "file=@$PNG;type=image/png")
ASSET_URL=$(jq -r ".data.url // .url // empty" "$asset_body")
[ "$asset_code" = "200" ] && [ -n "$ASSET_URL" ] || { echo "asset_failed code=$asset_code"; cat "$asset_body"; exit 23; }
asset_url_code=$(curl -sS -o /tmp/node80_asset_download.bin -w "%{http_code}" "$ASSET_URL")
img_payload=/tmp/node80_image_payload.json
cat > "$img_payload" <<'JSON'
{"model":"gpt-image-2","prompt":"A tiny red cube on a white desk, simple product photo","size":"1024x1024","quality":"low","response_format":"url"}
JSON
img_body=/tmp/node80_image_generation.json
img_code=$(curl -sS -m 240 -w "%{http_code}" -o "$img_body" -X POST "$BASE/api/v1/image-studio/images/generations" -H "Authorization: Bearer $JWT" -H "Content-Type: application/json" --data-binary @"$img_payload")
IMG_URL=$(jq -r ".data[0].url // .data.data[0].url // empty" "$img_body")
img_url_code="missing"
if [ -n "$IMG_URL" ]; then img_url_code=$(curl -sS -o /tmp/node80_image_download.bin -w "%{http_code}" "$IMG_URL"); fi
jq -n --arg node "80" --arg current "$(readlink -f /srv/sub2api-80/current)" --arg user_id "$USER_ID" --arg email "$EMAIL" --arg prev_turnstile "$PREV_TURNSTILE" --arg asset_code "$asset_code" --arg asset_url_code "$asset_url_code" --arg image_code "$img_code" --arg image_url_code "$img_url_code" --arg asset_url_prefix "${ASSET_URL:0:80}" --arg image_url_prefix "${IMG_URL:0:80}" '{node:$node,current:$current,temp_user_id:$user_id,temp_email:$email,turnstile_previous:$prev_turnstile,asset_http:$asset_code,asset_url_http:$asset_url_code,image_generation_http:$image_code,image_url_http:$image_url_code,asset_url_prefix:$asset_url_prefix,image_url_prefix:$image_url_prefix}'
