#!/usr/bin/env python3 """批量:上传本地图到文件服务,并把完整URL写回CMS记录的 image 字段。 用法: python3 cms_fill_image.py mapping.json: [{"module":"cms-article","id":10570,"file":"/path/to/img.png"}, ...] """ import os, json, sys, subprocess, time, urllib.request, urllib.error TOKEN = os.environ.get("TOKEN", "") UPLOAD = "https://server.websoft.top/api/file/upload" CMS = "https://cms-api.websoft.top/api" FILE_BASE = "https://file.websoft.top/api/file/" HDR = {"TenantId": "10626", "Authorization": "Bearer " + TOKEN} def _retry(fn, tries=4, sleep=2): last = None for i in range(tries): try: return fn() except Exception as e: last = e time.sleep(sleep * (i + 1)) raise last def upload(local_path: str) -> str: def _do(): out = subprocess.run( ["curl", "-s", "-m", "60", "-X", "POST", UPLOAD, "-H", "TenantId: 10626", "-H", f"Authorization: Bearer {TOKEN}", "-F", f"file=@{local_path}"], capture_output=True, text=True, ) try: d = json.loads(out.stdout) except Exception: raise RuntimeError(f"upload non-json: {out.stdout[:200]}") if d.get("code") != 0: raise RuntimeError(f"upload code={d.get('code')} msg={d.get('message')} err={str(d.get('error'))[:120]}") p = (d.get("data") or {}).get("path", "").lstrip("/") if not p: raise RuntimeError("upload returned empty path") return FILE_BASE + p return _retry(_do) def get_item(module: str, id_: int): def _do(): url = f"{CMS}/cms/{module}/{id_}" req = urllib.request.Request(url, headers=HDR) try: with urllib.request.urlopen(req, timeout=30) as r: return json.loads(r.read())["data"] except urllib.error.HTTPError as e: body = e.read()[:300] raise RuntimeError(f"GET {url} -> HTTP {e.code}: {body}") return _retry(_do) def put_item(module: str, obj: dict): def _do(): url = f"{CMS}/cms/{module}" req = urllib.request.Request( url, data=json.dumps(obj).encode(), headers={**HDR, "Content-Type": "application/json"}, method="PUT", ) with urllib.request.urlopen(req, timeout=30) as r: return json.loads(r.read()) return _retry(_do) def main(): mapping = json.load(open(sys.argv[1])) result_path = sys.argv[2] if len(sys.argv) > 2 else "/tmp/fill_result.json" results = [] for it in mapping: mod, iid, fpath = it["module"], it["id"], it["file"] rec = {"module": mod, "id": iid} try: url = upload(fpath) obj = get_item(mod, iid) obj["image"] = url out = put_item(mod, obj) rec["url"] = url rec["put_code"] = out.get("code") rec["ok"] = out.get("code") == 0 print(f"[{mod} {iid}] upload OK -> PUT code={out.get('code')} {'OK' if rec['ok'] else 'FAIL'} | {url[:70]}") except Exception as e: rec["error"] = str(e)[:300] print(f"[{mod} {iid}] ERROR: {e}") results.append(rec) time.sleep(1) json.dump(results, open(result_path, "w"), ensure_ascii=False, indent=2) print(f"\n=== done {len(results)} items, saved {result_path} ===") if __name__ == "__main__": main()