From e6344b1916d45fac70952176c2058b941ed838e7 Mon Sep 17 00:00:00 2001 From: AskaEth Date: Sun, 26 Jul 2026 21:34:36 +0800 Subject: [PATCH] feat: validate .tsd on upload, auto-extract manifest metadata --- server/api.py | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/server/api.py b/server/api.py index 98caeb6..5303329 100644 --- a/server/api.py +++ b/server/api.py @@ -543,17 +543,47 @@ async def api_market_register(data: dict): @router.post("/market/upload") async def api_market_upload(file: UploadFile = File(...), name: str = "", category: str = "", author: str = "", token: str = ""): - import httpx + import httpx, io, zipfile, json as _json + + # Validate .tsd + if not file.filename or not file.filename.endswith('.tsd'): + return {"error": "仅支持 .tsd 文件"} + + try: + zip_data = await file.read() + with zipfile.ZipFile(io.BytesIO(zip_data), 'r') as zf: + names = zf.namelist() + if 'manifest.json' not in names: + return {"error": "无效的仪表盘文件:缺少 manifest.json"} + if 'index.html' not in names: + return {"error": "无效的仪表盘文件:缺少 index.html"} + + manifest = _json.loads(zf.read('manifest.json').decode('utf-8')) + actual_name = name or manifest.get('name', file.filename.replace('.tsd', '')) + actual_category = category or manifest.get('category', 'community') + actual_author = author or manifest.get('author', 'Unknown') + actual_version = manifest.get('version', '1.0.0') + supported = manifest.get('supported_games', 'all') + icon = manifest.get('config', {}).get('icon', '📦') + desc = manifest.get('description', '') + except zipfile.BadZipFile: + return {"error": "无效的 zip 文件"} + except Exception as e: + return {"error": f"文件解析失败: {str(e)}"} + cfg = get_config() url = cfg.get("market_url", "http://127.0.0.1:5301") try: async with httpx.AsyncClient(timeout=30) as client: - files_httpx = {"file": (file.filename, await file.read(), file.content_type)} resp = await client.post( f"{url}/api/collections/dashboards/records", headers={"Authorization": f"Bearer {token}"}, - data={"name": name, "category": category, "author": author, "version": "1.0.0", "dashb_pending": true}, - files=files_httpx, + data={ + "name": actual_name, "category": actual_category, "author": actual_author, + "version": actual_version, "supported_games": supported, + "icon": icon, "description": desc, "dashb_pending": True, + }, + files={"file": (file.filename, zip_data, file.content_type)}, ) return resp.json() except Exception as e: