feat: validate .tsd on upload, auto-extract manifest metadata

This commit is contained in:
2026-07-26 21:34:36 +08:00
parent c4675f7a15
commit e6344b1916
+34 -4
View File
@@ -543,17 +543,47 @@ async def api_market_register(data: dict):
@router.post("/market/upload") @router.post("/market/upload")
async def api_market_upload(file: UploadFile = File(...), name: str = "", category: str = "", author: str = "", token: str = ""): 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() cfg = get_config()
url = cfg.get("market_url", "http://127.0.0.1:5301") url = cfg.get("market_url", "http://127.0.0.1:5301")
try: try:
async with httpx.AsyncClient(timeout=30) as client: async with httpx.AsyncClient(timeout=30) as client:
files_httpx = {"file": (file.filename, await file.read(), file.content_type)}
resp = await client.post( resp = await client.post(
f"{url}/api/collections/dashboards/records", f"{url}/api/collections/dashboards/records",
headers={"Authorization": f"Bearer {token}"}, headers={"Authorization": f"Bearer {token}"},
data={"name": name, "category": category, "author": author, "version": "1.0.0", "dashb_pending": true}, data={
files=files_httpx, "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() return resp.json()
except Exception as e: except Exception as e: