feat(account-management): add upload and download cookie file buttons

- Add '上传Cookie文件' and '下载Cookie文件' buttons to account management page
- Implement frontend functionality for cookie file upload and download
- Add backend API endpoints for handling cookie file operations:
  - /uploadCookie: Handle cookie file upload with validation
  - /downloadCookie: Handle secure cookie file download
- Implement security measures to prevent path traversal attacks
- Add proper error handling and user feedback
- Maintain existing functionality while adding new features
This commit is contained in:
Deroino
2025-11-13 10:37:19 +08:00
parent 4ee6c2b09c
commit 4688c41b9b
2 changed files with 203 additions and 1 deletions
+121
View File
@@ -476,6 +476,127 @@ def postVideoBatch():
"data": None
}), 200
# Cookie文件上传API
@app.route('/uploadCookie', methods=['POST'])
def upload_cookie():
try:
if 'file' not in request.files:
return jsonify({
"code": 500,
"msg": "没有找到Cookie文件",
"data": None
}), 400
file = request.files['file']
if file.filename == '':
return jsonify({
"code": 500,
"msg": "Cookie文件名不能为空",
"data": None
}), 400
if not file.filename.endswith('.json'):
return jsonify({
"code": 500,
"msg": "Cookie文件必须是JSON格式",
"data": None
}), 400
# 获取账号信息
account_id = request.form.get('id')
platform = request.form.get('platform')
if not account_id or not platform:
return jsonify({
"code": 500,
"msg": "缺少账号ID或平台信息",
"data": None
}), 400
# 从数据库获取账号的文件路径
with sqlite3.connect(Path(BASE_DIR / "db" / "database.db")) as conn:
conn.row_factory = sqlite3.Row
cursor = conn.cursor()
cursor.execute('SELECT filePath FROM user_info WHERE id = ?', (account_id,))
result = cursor.fetchone()
if not result:
return jsonify({
"code": 500,
"msg": "账号不存在",
"data": None
}), 404
# 保存上传的Cookie文件到对应路径
cookie_file_path = Path(BASE_DIR / "cookiesFile" / result['filePath'])
cookie_file_path.parent.mkdir(parents=True, exist_ok=True)
file.save(str(cookie_file_path))
# 更新数据库中的账号信息(可选,比如更新更新时间)
# 这里可以根据需要添加额外的处理逻辑
return jsonify({
"code": 200,
"msg": "Cookie文件上传成功",
"data": None
}), 200
except Exception as e:
print(f"上传Cookie文件时出错: {str(e)}")
return jsonify({
"code": 500,
"msg": f"上传Cookie文件失败: {str(e)}",
"data": None
}), 500
# Cookie文件下载API
@app.route('/downloadCookie', methods=['GET'])
def download_cookie():
try:
file_path = request.args.get('filePath')
if not file_path:
return jsonify({
"code": 500,
"msg": "缺少文件路径参数",
"data": None
}), 400
# 验证文件路径的安全性,防止路径遍历攻击
cookie_file_path = Path(BASE_DIR / "cookiesFile" / file_path).resolve()
base_path = Path(BASE_DIR / "cookiesFile").resolve()
if not cookie_file_path.is_relative_to(base_path):
return jsonify({
"code": 500,
"msg": "非法文件路径",
"data": None
}), 400
if not cookie_file_path.exists():
return jsonify({
"code": 500,
"msg": "Cookie文件不存在",
"data": None
}), 404
# 返回文件
return send_from_directory(
directory=str(cookie_file_path.parent),
path=cookie_file_path.name,
as_attachment=True
)
except Exception as e:
print(f"下载Cookie文件时出错: {str(e)}")
return jsonify({
"code": 500,
"msg": f"下载Cookie文件失败: {str(e)}",
"data": None
}), 500
# 包装函数:在线程中运行异步函数
def run_async_function(type,id,status_queue):
match type:
+82 -1
View File
@@ -57,6 +57,8 @@
<el-table-column label="操作">
<template #default="scope">
<el-button size="small" @click="handleEdit(scope.row)">编辑</el-button>
<el-button size="small" type="primary" @click="handleDownloadCookie(scope.row)">下载Cookie文件</el-button>
<el-button size="small" type="info" @click="handleUploadCookie(scope.row)">上传Cookie文件</el-button>
<el-button size="small" type="danger" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
@@ -120,6 +122,8 @@
<el-table-column label="操作">
<template #default="scope">
<el-button size="small" @click="handleEdit(scope.row)">编辑</el-button>
<el-button size="small" type="primary" @click="handleDownloadCookie(scope.row)">下载Cookie文件</el-button>
<el-button size="small" type="info" @click="handleUploadCookie(scope.row)">上传Cookie文件</el-button>
<el-button size="small" type="danger" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
@@ -183,6 +187,8 @@
<el-table-column label="操作">
<template #default="scope">
<el-button size="small" @click="handleEdit(scope.row)">编辑</el-button>
<el-button size="small" type="primary" @click="handleDownloadCookie(scope.row)">下载Cookie文件</el-button>
<el-button size="small" type="info" @click="handleUploadCookie(scope.row)">上传Cookie文件</el-button>
<el-button size="small" type="danger" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
@@ -246,6 +252,8 @@
<el-table-column label="操作">
<template #default="scope">
<el-button size="small" @click="handleEdit(scope.row)">编辑</el-button>
<el-button size="small" type="primary" @click="handleDownloadCookie(scope.row)">下载Cookie文件</el-button>
<el-button size="small" type="info" @click="handleUploadCookie(scope.row)">上传Cookie文件</el-button>
<el-button size="small" type="danger" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
@@ -309,6 +317,8 @@
<el-table-column label="操作">
<template #default="scope">
<el-button size="small" @click="handleEdit(scope.row)">编辑</el-button>
<el-button size="small" type="primary" @click="handleDownloadCookie(scope.row)">下载Cookie文件</el-button>
<el-button size="small" type="info" @click="handleUploadCookie(scope.row)">上传Cookie文件</el-button>
<el-button size="small" type="danger" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
@@ -547,7 +557,7 @@ const handleDelete = (row) => {
try {
// 调用API删除账号
const response = await accountApi.deleteAccount(row.id)
if (response.code === 200) {
// 从状态管理中删除账号
accountStore.deleteAccount(row.id)
@@ -568,6 +578,77 @@ const handleDelete = (row) => {
})
}
// 下载Cookie文件
const handleDownloadCookie = (row) => {
// 从后端获取Cookie文件
const baseUrl = import.meta.env.VITE_API_BASE_URL || 'http://localhost:5409'
const downloadUrl = `${baseUrl}/downloadCookie?filePath=${encodeURIComponent(row.filePath)}`
// 创建一个隐藏的链接来触发下载
const link = document.createElement('a')
link.href = downloadUrl
link.download = `${row.name}_cookie.json`
link.target = '_blank'
link.style.display = 'none'
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}
// 上传Cookie文件
const handleUploadCookie = (row) => {
// 创建一个隐藏的文件输入框
const input = document.createElement('input')
input.type = 'file'
input.accept = '.json'
input.style.display = 'none'
document.body.appendChild(input)
input.onchange = async (event) => {
const file = event.target.files[0]
if (!file) return
// 检查文件类型
if (!file.name.endsWith('.json')) {
ElMessage.error('请选择JSON格式的Cookie文件')
document.body.removeChild(input)
return
}
try {
// 创建FormData对象
const formData = new FormData()
formData.append('file', file)
formData.append('id', row.id)
formData.append('platform', row.platform)
// 发送上传请求
const baseUrl = import.meta.env.VITE_API_BASE_URL || 'http://localhost:5409'
const response = await fetch(`${baseUrl}/uploadCookie`, {
method: 'POST',
body: formData
})
const result = await response.json()
if (result.code === 200) {
ElMessage.success('Cookie文件上传成功')
// 刷新账号列表以显示更新
fetchAccounts()
} else {
ElMessage.error(result.msg || 'Cookie文件上传失败')
}
} catch (error) {
console.error('上传Cookie文件失败:', error)
ElMessage.error('Cookie文件上传失败')
} finally {
document.body.removeChild(input)
}
}
input.click()
}
// SSE事件源对象
let eventSource = null