diff --git a/app/api/routers/rbac_desktop_updates.py b/app/api/routers/rbac_desktop_updates.py index 0b8cbe9..5cb5939 100644 --- a/app/api/routers/rbac_desktop_updates.py +++ b/app/api/routers/rbac_desktop_updates.py @@ -88,6 +88,12 @@ class DesktopArtifactConfirmRequest(BaseModel): signature_text: str = Field("", description=".sig 文件内容(明文 minisign 或 Tauri v2 base64)") +class DesktopArtifactSignatureRequest(BaseModel): + """仅登记更新包 minisign 签名(更新包已上传,只补/换 .sig)。""" + target: str + signature_text: str = Field(..., description=".sig 文件内容(明文 minisign 或 Tauri v2 base64)") + + class DesktopInstallerConfirmRequest(BaseModel): """标准安装包直传完成后确认登记(无签名)。""" target: str @@ -581,6 +587,40 @@ async def confirm_desktop_artifact( return updated +@router.post("/desktop-versions/{vid}/artifacts/signature", summary="仅登记更新包 minisign 签名(更新包已上传时补/换 .sig)") +async def set_desktop_artifact_signature( + vid: str, + payload: DesktopArtifactSignatureRequest, + db: Database = Depends(get_db), + actor: dict = Depends(require_permission("menu:admin_desktop_updates")), +): + if payload.target not in DESKTOP_TARGETS: + raise HTTPException(status_code=400, detail=f"target 仅支持 {', '.join(DESKTOP_TARGETS)}") + version = await _get_version(db, vid) + if version["status"] == "archived": + raise HTTPException(status_code=400, detail="已下架版本不可再登记签名") + artifacts = dict(version.get("artifacts") or {}) + art = artifacts.get(payload.target) + if not art or not (art.get("url") or "").strip(): + raise HTTPException( + status_code=400, + detail=f"target {payload.target} 尚未上传更新包,请先上传更新包后再登记签名", + ) + signature = _verify_signature(payload.signature_text) + art = dict(art) + art["signature"] = signature + artifacts[payload.target] = art + updated = await db.desktop_versions.update( + vid, {"artifacts_json": json.dumps(artifacts, ensure_ascii=False)} + ) + await write_audit( + db, action="desktop_version.artifact_signature", resource="desktop_version", resource_id=vid, + detail=f"version={version['version']} target={payload.target} signature=updated", + user=actor, + ) + return updated + + @router.post("/desktop-versions/{vid}/installers/presign", summary="生成标准安装包 OSS 预签名直传链接") async def presign_desktop_installer( vid: str,