feat(app): 添加多模板关于我们页面及相关路由和404页面

- 新增404页面,优化未找到页面体验,避免被搜索引擎索引
- 增加文件代理接口,隐藏真实文件服务器地址,支持文件请求代理
- 实现/article、/case、/product及/page动态路由兼容列表与详情展示
- 添加动态CMS页面兼容入口处理旧式路径,统一路由与SEO设置
- 新增模板1、模板7、模板2、模板3关于我们页面,实现多模板支持
- 模板增强支持CMS单页内容加载及SEO信息动态设置
- 配置环境变量及Git忽略文件规则辅助开发和构建环境管理
This commit is contained in:
2026-09-08 12:13:44 +08:00
commit 2b69686795
381 changed files with 59891 additions and 0 deletions
+98
View File
@@ -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()