From 1403bced5252a6ed7e75df8b155d3084e5a5ff4e Mon Sep 17 00:00:00 2001 From: ymq Date: Tue, 18 Aug 2026 16:04:00 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20/download=20=E4=B8=8B=E8=BD=BD=E7=AB=AF?= =?UTF-8?q?=E7=82=B9=20download=5Fproject.dspy=20+=20RBAC=20=E6=9D=83?= =?UTF-8?q?=E9=99=90=E6=B3=A8=E5=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/load_path.py | 1 + wwwroot/api/download_project.dspy | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 wwwroot/api/download_project.dspy diff --git a/scripts/load_path.py b/scripts/load_path.py index 4f3b6c3..50e27db 100644 --- a/scripts/load_path.py +++ b/scripts/load_path.py @@ -104,6 +104,7 @@ PATHS_LOGINED = [ f"/{MOD}/api/workspace_files.dspy", f"/{MOD}/api/workspace_open.dspy", f"/{MOD}/api/workspace_file.dspy", + f"/{MOD}/api/download_project.dspy", f"/{MOD}/api/workspace_upload.dspy", f"/{MOD}/api/workspace_delete.dspy", f"/{MOD}/api/workspace_popup.dspy", diff --git a/wwwroot/api/download_project.dspy b/wwwroot/api/download_project.dspy new file mode 100644 index 0000000..1d568ce --- /dev/null +++ b/wwwroot/api/download_project.dspy @@ -0,0 +1,26 @@ +# download_project.dspy - 下载项目备份 tgz 文件(/download 命令生成下载链接) +import os +from urllib.parse import quote +from aiohttp.web_fileresponse import FileResponse + +path = (params_kw or {}).get('path', '').strip() + +uid = await get_user() +if not uid: + return {"widgettype": "Message", "options": {"title": "错误", "message": "请先登录"}} + +# 安全校验:只允许 _archive 备份归档目录下的 .tgz 文件 +if not path or not path.endswith('.tgz'): + return {"widgettype": "Message", "options": {"title": "错误", "message": "非法文件(仅支持 .tgz 备份文件)"}} + +real = os.path.realpath(path) +if '/_archive/' not in real: + return {"widgettype": "Message", "options": {"title": "错误", "message": "非法路径(仅允许 _archive 备份归档目录)"}} + +if not os.path.isfile(path): + return {"widgettype": "Message", "options": {"title": "错误", "message": "文件不存在"}} + +filename = os.path.basename(path) +safe_name = quote(filename) +headers = {'Content-Disposition': 'attachment; filename="%s"; filename*=UTF-8\'\'%s' % (filename, safe_name)} +return FileResponse(path, headers=headers)