# 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)