feat(app): 添加多模板关于我们页面及相关路由和404页面
- 新增404页面,优化未找到页面体验,避免被搜索引擎索引 - 增加文件代理接口,隐藏真实文件服务器地址,支持文件请求代理 - 实现/article、/case、/product及/page动态路由兼容列表与详情展示 - 添加动态CMS页面兼容入口处理旧式路径,统一路由与SEO设置 - 新增模板1、模板7、模板2、模板3关于我们页面,实现多模板支持 - 模板增强支持CMS单页内容加载及SEO信息动态设置 - 配置环境变量及Git忽略文件规则辅助开发和构建环境管理
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
#!/usr/bin/env python3
|
||||
"""批量:上传本地图到文件服务,并把完整URL写回CMS记录的 image 字段。
|
||||
用法: python3 cms_fill_image.py <mapping.json> <result.json>
|
||||
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()
|
||||
Reference in New Issue
Block a user