85 lines
2.5 KiB
Python
85 lines
2.5 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
"""Verify Nexus production endpoints after a release."""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import argparse
|
||
|
|
import sys
|
||
|
|
import urllib.error
|
||
|
|
import urllib.request
|
||
|
|
from dataclasses import dataclass
|
||
|
|
from urllib.parse import urljoin
|
||
|
|
|
||
|
|
|
||
|
|
SENSITIVE_TEXT_MARKERS = [
|
||
|
|
"全局 API Key",
|
||
|
|
"Global API Key",
|
||
|
|
"tmp_login",
|
||
|
|
"tmp_token",
|
||
|
|
]
|
||
|
|
|
||
|
|
|
||
|
|
@dataclass
|
||
|
|
class CheckResult:
|
||
|
|
name: str
|
||
|
|
ok: bool
|
||
|
|
detail: str
|
||
|
|
|
||
|
|
|
||
|
|
def fetch(url: str, timeout: float) -> tuple[int, str]:
|
||
|
|
req = urllib.request.Request(url, headers={"User-Agent": "nexus-release-verifier/1.0"})
|
||
|
|
try:
|
||
|
|
with urllib.request.urlopen(req, timeout=timeout) as resp:
|
||
|
|
body = resp.read(1_000_000).decode("utf-8", errors="replace")
|
||
|
|
return resp.status, body
|
||
|
|
except urllib.error.HTTPError as exc:
|
||
|
|
body = exc.read(200_000).decode("utf-8", errors="replace")
|
||
|
|
return exc.code, body
|
||
|
|
|
||
|
|
|
||
|
|
def check_health(base_url: str, timeout: float) -> CheckResult:
|
||
|
|
url = urljoin(base_url.rstrip("/") + "/", "health")
|
||
|
|
status, body = fetch(url, timeout)
|
||
|
|
ok = status == 200 and body.strip() == "ok"
|
||
|
|
return CheckResult("health", ok, f"{status} {body[:80].strip()!r}")
|
||
|
|
|
||
|
|
|
||
|
|
def check_spa(base_url: str, path: str, timeout: float) -> CheckResult:
|
||
|
|
url = urljoin(base_url.rstrip("/") + "/", path.lstrip("/"))
|
||
|
|
status, body = fetch(url, timeout)
|
||
|
|
marker_hits = [m for m in SENSITIVE_TEXT_MARKERS if m in body]
|
||
|
|
ok = status == 200 and "<html" in body.lower() and not marker_hits
|
||
|
|
detail = f"status={status} html={'<html' in body.lower()} sensitive={marker_hits or 'none'}"
|
||
|
|
return CheckResult(path.rstrip("/") or path, ok, detail)
|
||
|
|
|
||
|
|
|
||
|
|
def main() -> int:
|
||
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
||
|
|
parser.add_argument("--base-url", default="https://api.synaglobal.vip")
|
||
|
|
parser.add_argument("--timeout", type=float, default=15.0)
|
||
|
|
args = parser.parse_args()
|
||
|
|
|
||
|
|
checks = [
|
||
|
|
check_health(args.base_url, args.timeout),
|
||
|
|
check_spa(args.base_url, "/app/", args.timeout),
|
||
|
|
check_spa(args.base_url, "/app-v2/", args.timeout),
|
||
|
|
]
|
||
|
|
|
||
|
|
for result in checks:
|
||
|
|
prefix = "OK" if result.ok else "FAIL"
|
||
|
|
print(f"{prefix}: {result.name}: {result.detail}")
|
||
|
|
|
||
|
|
if not all(r.ok for r in checks):
|
||
|
|
return 1
|
||
|
|
|
||
|
|
print("OK: online release verification passed")
|
||
|
|
return 0
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
try:
|
||
|
|
raise SystemExit(main())
|
||
|
|
except Exception as exc: # noqa: BLE001 - CLI should surface concise failure
|
||
|
|
print(f"ERROR: {exc}", file=sys.stderr)
|
||
|
|
raise SystemExit(1)
|