Files
live-hub-py/web/backend/routers/cookies.py
T
yml2213andClaude Fable 5 1b20317b42 Cookie导出增加格式选择
- 后端: export接口增加 format 参数,支持 csv 和 custom 格式
- custom 格式: 账号----密码----ck,导出为 txt
- 后端: 导出用批量查 Account 替代逐条查询,消除 N+1
- 前端: 导出按钮改为下拉菜单可选格式
- 修复: blob 导出时不再取 .data(拦截器已提取)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-22 20:20:59 +08:00

134 lines
4.3 KiB
Python

"""Cookie 管理路由"""
from datetime import timezone
from fastapi import APIRouter, Depends, HTTPException
from fastapi.responses import StreamingResponse
from sqlalchemy.orm import Session
import io
import csv
from ..database import get_db
from ..models import User, LoginTask, Account
from ..deps import get_current_user, require_permission
from ..permissions import has_permission
def _fmt_dt(dt) -> str | None:
"""将 datetime 格式化为带时区的 ISO 字符串。"""
if dt is None:
return None
if dt.tzinfo is None:
dt = dt.replace(tzinfo=timezone.utc)
return dt.isoformat()
router = APIRouter(prefix="/api/cookies", tags=["Cookie管理"])
@router.get("")
def list_cookies(
db: Session = Depends(get_db),
current: User = Depends(get_current_user),
):
"""查看登录成功的 Cookie 列表。"""
query = db.query(LoginTask).filter(LoginTask.status == "success")
# 客服只能看自己账号的
if not has_permission(current.role, "login:view_all"):
query = query.join(Account, LoginTask.account_id == Account.id).filter(
Account.assigned_to == current.id
)
tasks = query.order_by(LoginTask.finished_at.desc()).all()
result = []
for t in tasks:
acc = db.query(Account).filter(Account.id == t.account_id).first()
item = {
"id": t.id,
"batch_id": t.batch_id,
"account_id": t.account_id,
"account_username": acc.username if acc else "",
"assigned_to": acc.assigned_to,
"assigned_username": acc.assigned_user.username if acc and acc.assigned_user else None,
"created_at": _fmt_dt(t.finished_at),
}
# 只有有 cookie:view 权限才返回 cookie 内容
if has_permission(current.role, "cookie:view"):
cookie = t.cookie or ""
item["cookie"] = cookie
item["cookie_preview"] = cookie[:50] + "..." if len(cookie) > 50 else cookie
else:
item["cookie"] = ""
item["cookie_preview"] = "***"
result.append(item)
return result
@router.get("/export")
def export_cookies(
format: str = "csv",
db: Session = Depends(get_db),
current: User = Depends(require_permission("cookie:export")),
):
"""导出 Cookie,支持 csv 和 custom 格式。
csv: 账号, Cookie, 时间
custom: 账号----密码----ck
"""
tasks = db.query(LoginTask).filter(
LoginTask.status == "success"
).order_by(LoginTask.finished_at.desc()).all()
# 批量查账号,避免 N+1
account_ids = [t.account_id for t in tasks]
accounts_map = {}
if account_ids:
accs = db.query(Account).filter(Account.id.in_(account_ids)).all()
accounts_map = {a.id: a for a in accs}
if format == "custom":
# 账号----密码----ck
lines = []
for t in tasks:
acc = accounts_map.get(t.account_id)
username = acc.username if acc else ""
password = acc.password if acc else ""
ck = t.cookie or ""
lines.append(f"{username}----{password}----{ck}")
content = "\n".join(lines)
filename = "cookies_custom.txt"
media = "text/plain"
else:
# CSV: 账号, Cookie, 时间
output = io.StringIO()
writer = csv.writer(output)
writer.writerow(["账号", "Cookie", "时间"])
for t in tasks:
acc = accounts_map.get(t.account_id)
username = acc.username if acc else ""
writer.writerow([username, t.cookie or "", _fmt_dt(t.finished_at) or ""])
content = output.getvalue()
filename = "cookies.csv"
media = "text/csv"
return StreamingResponse(
iter([content]),
media_type=media,
headers={"Content-Disposition": f"attachment; filename={filename}"},
)
@router.delete("/{task_id}")
def delete_cookie(
task_id: int,
db: Session = Depends(get_db),
current: User = Depends(require_permission("cookie:export")),
):
"""删除一条 Cookie 记录。"""
task = db.query(LoginTask).filter(LoginTask.id == task_id).first()
if not task:
raise HTTPException(status_code=404, detail="记录不存在")
task.cookie = ""
task.status = "failed"
task.message = "Cookie已清除"
db.commit()
return {"message": "已删除", "success": True}