feat: market scenes tab - upload/install with dependency check

This commit is contained in:
2026-07-26 21:37:24 +08:00
parent e6344b1916
commit 47341f1f76
2 changed files with 136 additions and 14 deletions
+87 -1
View File
@@ -557,8 +557,13 @@ async def api_market_upload(file: UploadFile = File(...), name: str = "", catego
return {"error": "无效的仪表盘文件:缺少 manifest.json"}
if 'index.html' not in names:
return {"error": "无效的仪表盘文件:缺少 index.html"}
manifest = _json.loads(zf.read('manifest.json').decode('utf-8'))
if not manifest.get('id'):
return {"error": "manifest.json 缺少 id"}
if not manifest.get('name'):
return {"error": "manifest.json 缺少 name"}
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')
@@ -604,3 +609,84 @@ async def api_market_install(id: str = "", filename: str = ""):
return {"ok": ok}
except Exception as e:
return {"ok": False, "error": str(e)}
# ---- Market Scenes ----
@router.get("/market/scenes")
async def api_market_scenes():
import httpx
cfg = get_config()
url = cfg.get("market_url", "http://127.0.0.1:5301")
try:
async with httpx.AsyncClient(timeout=10) as client:
resp = await client.get(f"{url}/api/collections/scenes/records?sort=-created&perPage=50")
return resp.json()
except Exception:
return {"items": []}
@router.post("/market/scenes/upload")
async def api_market_upload_scene(file: UploadFile = File(...), token: str = ""):
import io, zipfile, json as _json
if not file.filename or not file.filename.endswith('.tss'):
return {"error": "仅支持 .tss 文件"}
try:
zip_data = await file.read()
with zipfile.ZipFile(io.BytesIO(zip_data), 'r') as zf:
if 'scene.json' not in zf.namelist():
return {"error": "缺少 scene.json"}
scene = _json.loads(zf.read('scene.json').decode('utf-8'))
if not scene.get('name'):
return {"error": "scene.json 缺少 name"}
deps = set()
for canvas in scene.get('canvases', []):
for p in canvas.get('placements', []):
if p.get('dashboard_id'):
deps.add(p['dashboard_id'])
except zipfile.BadZipFile:
return {"error": "无效的 zip 文件"}
except Exception as e:
return {"error": str(e)}
import httpx as _httpx
cfg = get_config()
url = cfg.get("market_url", "http://127.0.0.1:5301")
try:
async with _httpx.AsyncClient(timeout=30) as client:
resp = await client.post(
f"{url}/api/collections/scenes/records",
headers={"Authorization": f"Bearer {token}"},
data={"name": scene.get('name'), "description": scene.get('description',''), "game_id": scene.get('game_id',''), "author": scene.get('author','') or "Unknown", "dashb_pending": True},
files={"file": (file.filename, zip_data, file.content_type)},
)
result = resp.json()
result["dependencies"] = list(deps)
return result
except Exception as e:
return {"error": str(e)}
@router.get("/market/scenes/install")
async def api_market_install_scene(id: str = "", filename: str = ""):
import httpx, io, zipfile, json as _json
cfg = get_config()
url = cfg.get("market_url", "http://127.0.0.1:5301")
try:
async with httpx.AsyncClient(timeout=60, follow_redirects=True) as client:
resp = await client.get(f"{url}/api/files/scenes/{id}/{filename}")
zip_data = resp.content
missing = []
with zipfile.ZipFile(io.BytesIO(zip_data), 'r') as zf:
if 'scene.json' in zf.namelist():
scene = _json.loads(zf.read('scene.json').decode('utf-8'))
from models.dashboard import dashboard_manager
for canvas in scene.get('canvases', []):
for p in canvas.get('placements', []):
did = p.get('dashboard_id', '')
if did and not dashboard_manager.get(did):
missing.append(did)
from models.scene import scene_manager
ok = scene_manager.import_scene_zip(zip_data)
return {"ok": ok, "missing_dashboards": missing}
except Exception as e:
return {"ok": False, "error": str(e)}