#!/usr/bin/env python3 """POST 安全探测:仅探测搜索/计算/详情语义的接口(空最小请求体)。 绝不触碰:支付、短信、账号绑定、删除/更新类、文件上传类接口。""" import json, os, re, time, urllib.request, urllib.error BASE = "https://zzc-serv.thinkerx.com/api/" TOKEN = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczpcL1wvenpjLXNlcnYudGhpbmtlcnguY29tXC9hcGlcL3dlY2hhdC1sb2dpbi13aXRoLXBob25lIiwiaWF0IjoxNzg5OTA5NTIwLCJleHAiOjE3ODk5OTU5MjAsIm5iZiI6MTc4OTkwOTUyMCwianRpIjoiTGVyV0J4VTZONTVOMlNwQSIsInN1YiI6MzU2Mjg1NiwicHJ2IjoiNTRjMjZlNjE2MjNhYTQzMzZkZTQ1YTcxNGIwYzdhYWIxZTJhYzA3NSJ9.MXBrpBh3gseq8I91EQklxsgLfHafUKwNxc24JdcUkcc" OUT = r"D:\Code\huamenchuang\data\full-site\post" os.makedirs(OUT, exist_ok=True) # 只读/计算语义白名单(详情查询、列表搜索、纯计算、受付费墙保护) SAFE_POST = [ "orders/", "sunny_house_order/", "after_sales_orders/", "windoor_default", "order-by-pid", "get_order_process", "record_by_contract_id", "contract_bars_with_stock", "contract_share", "contract_share/", "cut_solution", "offer-price", "offer-price/", "optimize1d-ai", "optimize1d-test", "three-d-data", "product_by_warehouse", "product-order-format", "product-order-format/", "order_format_by_warehouse", "is-repeat-order", "cj-orders-status", "xf_contract_workshop", "ai_product", ] def safe_name(ep): return re.sub(r'[^A-Za-z0-9_-]', '_', ep).strip('_') or 'root' manifest = [] for ep in SAFE_POST: body = json.dumps({"page": 1}).encode() req = urllib.request.Request(BASE + ep, method="POST", headers={ "Authorization": f"Bearer {TOKEN}", "X-Requested-With": "XMLHttpRequest", "Accept": "application/json", "Content-Type": "application/json", }, data=body) try: with urllib.request.urlopen(req, timeout=20) as r: status, text = r.status, r.read().decode("utf-8", "replace") except urllib.error.HTTPError as e: status, text = e.code, e.read().decode("utf-8", "replace")[:4000] except Exception as e: status, text = -1, str(e)[:200] entry = {"endpoint": ep, "method": "POST", "status": status, "size": len(text)} try: j = json.loads(text) if isinstance(j, dict): entry["code"] = j.get("code") entry["message"] = (j.get("message") or "")[:50] entry["data_type"] = type(j.get("data")).__name__ # 记录校验错误里的字段清单(Laravel validation) if j.get("errors"): entry["required_fields"] = list(j["errors"].keys()) if j.get("data") and isinstance(j.get("data"), list): entry["data_len"] = len(j["data"]) except Exception: pass json.dump({"endpoint": ep, "status": status, "body": text[:100000]}, open(f"{OUT}\\{safe_name(ep)}.json", "w", encoding="utf-8"), ensure_ascii=False) manifest.append(entry) print(f"{ep:38s} HTTP {status} code={entry.get('code')} {entry.get('message','')}", flush=True) time.sleep(0.3) json.dump(manifest, open(r"D:\Code\huamenchuang\data\full-site\post-manifest.json", "w", encoding="utf-8"), ensure_ascii=False, indent=1) print(f"\n完成: {len(SAFE_POST)} 个 POST 探测")