type: 收敛测试 schemas 与协议层类型

This commit is contained in:
yml2213
2026-08-30 20:35:08 +08:00
parent 92dc461e52
commit c891ac982e
26 changed files with 1846 additions and 882 deletions
+148 -77
View File
@@ -27,6 +27,7 @@ def _fmt_dt(dt) -> str | None:
dt = dt.replace(tzinfo=timezone.utc)
return dt.isoformat()
router = APIRouter(prefix="/api/cookies", tags=["Cookie管理"])
cookie_relogin_registry = BatchRegistry()
@@ -44,7 +45,9 @@ def _parse_account_names(raw_names: str) -> list[str]:
"""解析前端粘贴的 Excel 账号名,每行一个并去重。"""
names = []
seen = set()
for value in (raw_names or "").replace("\r\n", "\n").replace("\r", "\n").split("\n"):
for value in (
(raw_names or "").replace("\r\n", "\n").replace("\r", "\n").split("\n")
):
name = value.strip()
if name and name not in seen:
names.append(name)
@@ -60,7 +63,9 @@ def _order_cookie_tasks(query, selected_names: list[str]):
value=Account.username,
else_=len(selected_names),
)
return query.order_by(input_order, LoginTask.finished_at.desc(), LoginTask.id.desc())
return query.order_by(
input_order, LoginTask.finished_at.desc(), LoginTask.id.desc()
)
return query.order_by(LoginTask.finished_at.desc(), LoginTask.id.desc())
@@ -88,7 +93,9 @@ def _visible_cookie_operation_tasks_query(db: Session, current: User):
"""返回可检测/重登的 Cookie 记录,重登中或失败时仍保留在操作列表。"""
query = db.query(LoginTask).filter(
LoginTask.cookie != "",
LoginTask.status.in_(("success", "relogin_pending", "relogin_running", "relogin_failed")),
LoginTask.status.in_(
("success", "relogin_pending", "relogin_running", "relogin_failed")
),
)
if not user_has_permission(current, "login:view_all"):
query = query.join(Account, LoginTask.account_id == Account.id).filter(
@@ -112,7 +119,9 @@ def list_cookies(
if not user_has_permission(current, "cookie:view"):
raise HTTPException(status_code=403, detail="无权限: cookie:view")
if not include_cookie:
query = _visible_cookie_tasks_query(db, current).options(defer(LoginTask.cookie))
query = _visible_cookie_tasks_query(db, current).options(
defer(LoginTask.cookie)
)
else:
query = _visible_cookie_tasks_query(db, current).options(
joinedload(LoginTask.account).joinedload(Account.assigned_user),
@@ -137,15 +146,19 @@ def list_cookies(
if not account_joined:
query = query.join(Account, LoginTask.account_id == Account.id)
account_joined = True
query = query.outerjoin(User, Account.assigned_to == User.id).filter(or_(
Account.username.ilike(pattern),
Account.tag.ilike(pattern),
User.username.ilike(pattern),
))
query = query.outerjoin(User, Account.assigned_to == User.id).filter(
or_(
Account.username.ilike(pattern),
Account.tag.ilike(pattern),
User.username.ilike(pattern),
)
)
total = None
if page is not None:
total = query.order_by(None).with_entities(func.count(LoginTask.id)).scalar() or 0
total = (
query.order_by(None).with_entities(func.count(LoginTask.id)).scalar() or 0
)
query = _order_cookie_tasks(query, selected_names)
if page is not None:
query = query.offset((page - 1) * page_size).limit(page_size)
@@ -175,8 +188,10 @@ def list_cookies(
"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,
"assigned_to": acc.assigned_to if acc else None,
"assigned_username": acc.assigned_user.username
if acc and acc.assigned_user
else None,
"created_at": _fmt_dt(t.finished_at),
"ck_check_status": t.ck_check_status or "",
"ck_check_result": t.ck_check_result,
@@ -194,7 +209,12 @@ def list_cookies(
item["account_password"] = ""
result.append(item)
if page is not None:
return {"items": result, "total": total or 0, "page": page, "page_size": page_size}
return {
"items": result,
"total": total or 0,
"page": page,
"page_size": page_size,
}
return result
@@ -242,10 +262,12 @@ def list_cookie_operations(
if tag_value:
query = query.filter(Account.tag == tag_value)
if search_text:
query = query.filter(or_(
Account.username.ilike(f"%{search_text}%"),
Account.tag.ilike(f"%{search_text}%"),
))
query = query.filter(
or_(
Account.username.ilike(f"%{search_text}%"),
Account.tag.ilike(f"%{search_text}%"),
)
)
total = query.order_by(None).with_entities(func.count(LoginTask.id)).scalar() or 0
tasks = (
@@ -265,7 +287,9 @@ def list_cookie_operations(
"created_at": _fmt_dt(task.finished_at),
"relogin_status": task.status if task.status != "success" else "",
"relogin_message": task.message or "",
"relogin_batch_id": task.batch_id if task.status in {"relogin_pending", "relogin_running"} else "",
"relogin_batch_id": task.batch_id
if task.status in {"relogin_pending", "relogin_running"}
else "",
}
for task in tasks
],
@@ -292,7 +316,7 @@ def list_cookie_operation_tags(
.order_by(Account.tag.asc())
.all()
)
return [tag for tag, in rows if tag]
return [tag for (tag,) in rows if tag]
@router.get("/duplicates")
@@ -317,7 +341,9 @@ def find_duplicate_cookies(
if not user_has_permission(current, "login:view_all"):
query = query.filter(Account.assigned_to == current.id)
rows = query.order_by(Account.username.asc(), LoginTask.finished_at.desc(), LoginTask.id.desc()).all()
rows = query.order_by(
Account.username.asc(), LoginTask.finished_at.desc(), LoginTask.id.desc()
).all()
grouped: dict[str, dict] = {}
for row in rows:
username = (row.username or "").strip()
@@ -337,26 +363,30 @@ def find_duplicate_cookies(
group["account_names"].add(username)
group["account_ids"].add(row.account_id)
group["cookie_ids"].append(row.cookie_id)
group["records"].append({
"id": row.cookie_id,
"account_id": row.account_id,
"batch_id": row.batch_id,
"finished_at": _fmt_dt(row.finished_at),
})
group["records"].append(
{
"id": row.cookie_id,
"account_id": row.account_id,
"batch_id": row.batch_id,
"finished_at": _fmt_dt(row.finished_at),
}
)
duplicate_groups = []
for group in grouped.values():
if len(group["cookie_ids"]) < 2:
continue
duplicate_groups.append({
"account_key": group["account_key"],
"account_names": sorted(group["account_names"]),
"cookie_count": len(group["cookie_ids"]),
"account_count": len(group["account_ids"]),
"cookie_ids": group["cookie_ids"],
"account_ids": sorted(group["account_ids"]),
"records": group["records"],
})
duplicate_groups.append(
{
"account_key": group["account_key"],
"account_names": sorted(group["account_names"]),
"cookie_count": len(group["cookie_ids"]),
"account_count": len(group["account_ids"]),
"cookie_ids": group["cookie_ids"],
"account_ids": sorted(group["account_ids"]),
"records": group["records"],
}
)
duplicate_groups.sort(key=lambda item: (-item["cookie_count"], item["account_key"]))
return {
@@ -432,10 +462,16 @@ def _check_cookies(ids: str, db: Session, current: User, *, detailed: bool) -> d
id_list = [int(x) for x in ids.split(",") if x.strip().isdigit()]
if not id_list:
raise HTTPException(status_code=400, detail="无效的ID")
base_query = _visible_cookie_tasks_query(db, current) if detailed else _visible_cookie_operation_tasks_query(db, current)
tasks = base_query.filter(LoginTask.id.in_(id_list)).filter(
LoginTask.status.in_(("success", "relogin_failed"))
).all()
base_query = (
_visible_cookie_tasks_query(db, current)
if detailed
else _visible_cookie_operation_tasks_query(db, current)
)
tasks = (
base_query.filter(LoginTask.id.in_(id_list))
.filter(LoginTask.status.in_(("success", "relogin_failed")))
.all()
)
if not tasks:
raise HTTPException(status_code=404, detail="记录不存在")
@@ -447,15 +483,17 @@ def _check_cookies(ids: str, db: Session, current: User, *, detailed: bool) -> d
results.append(future.result())
except Exception as exc:
task = futures[future]
results.append({
"id": task.id,
"valid": False,
"message": f"检测异常: {exc}",
"fish_ball": None,
"nickname": None,
"level": None,
"checked_at": datetime.now(timezone.utc).isoformat(),
})
results.append(
{
"id": task.id,
"valid": False,
"message": f"检测异常: {exc}",
"fish_ball": None,
"nickname": None,
"level": None,
"checked_at": datetime.now(timezone.utc).isoformat(),
}
)
results.sort(key=lambda item: item["id"])
# 持久化检测结果,刷新/翻页不丢失
@@ -472,12 +510,14 @@ def _check_cookies(ids: str, db: Session, current: User, *, detailed: bool) -> d
"message": item.get("message", ""),
}
task.ck_checked_at = datetime.now(timezone.utc)
db.add(AuditLog(
user_id=current.id,
username=current.username,
action="cookie:check",
target=f"检测 {len(results)} 条已分配账号 CK",
))
db.add(
AuditLog(
user_id=current.id,
username=current.username,
action="cookie:check",
target=f"检测 {len(results)} 条已分配账号 CK",
)
)
db.commit()
if not detailed:
results = [
@@ -510,7 +550,13 @@ def check_cookie_operations(
return _check_cookies(ids, db, current, detailed=False)
def _start_relogin_tasks(tasks: list[LoginTask], db: Session, current: User, *, action: str = "cookie:relogin"):
def _start_relogin_tasks(
tasks: list[LoginTask],
db: Session,
current: User,
*,
action: str = "cookie:relogin",
):
"""启动重登批次:旧 Cookie 保留到新登录成功后才替换。"""
if not tasks:
raise HTTPException(status_code=404, detail="记录不存在")
@@ -538,7 +584,9 @@ def _start_relogin_tasks(tasks: list[LoginTask], db: Session, current: User, *,
if not task_ids:
db.commit()
raise HTTPException(status_code=400, detail="没有可重登的账号,请检查账号凭据或当前重登状态")
raise HTTPException(
status_code=400, detail="没有可重登的账号,请检查账号凭据或当前重登状态"
)
proxy = db.query(ProxyConfigModel).first()
thread_db = SessionLocal()
@@ -558,13 +606,15 @@ def _start_relogin_tasks(tasks: list[LoginTask], db: Session, current: User, *,
relogin_task_ids=task_ids,
)
batch_id = runner.batch_id
db.add(AuditLog(
user_id=current.id,
username=current.username,
action=action,
target=f"重登 {len(task_ids)} 条账号 CK",
detail="旧 Cookie 将在新登录成功后替换",
))
db.add(
AuditLog(
user_id=current.id,
username=current.username,
action=action,
target=f"重登 {len(task_ids)} 条账号 CK",
detail="旧 Cookie 将在新登录成功后替换",
)
)
db.commit()
cookie_relogin_registry.register(batch_id, None, None, runner, owner_id=current.id)
@@ -599,19 +649,28 @@ def stop_cookie_relogin(
_require_cookie_operation_perm(current)
batch = cookie_relogin_registry.get(batch_id)
if not batch:
raise HTTPException(status_code=404, detail="重登批次不存在、已结束或服务已重启")
if batch.get("owner_id") != current.id and not user_has_permission(current, "login:view_all"):
raise HTTPException(
status_code=404, detail="重登批次不存在、已结束或服务已重启"
)
if batch.get("owner_id") != current.id and not user_has_permission(
current, "login:view_all"
):
raise HTTPException(status_code=403, detail="无权限停止该重登批次")
batch["runner"].stop()
db.add(AuditLog(
user_id=current.id,
username=current.username,
action="cookie:relogin_stop",
target=f"停止 CK 重登批次 {batch_id}",
))
db.add(
AuditLog(
user_id=current.id,
username=current.username,
action="cookie:relogin_stop",
target=f"停止 CK 重登批次 {batch_id}",
)
)
db.commit()
return {"message": "已发送停止信号,正在登录的账号会在当前请求结束后停止", "success": True}
return {
"message": "已发送停止信号,正在登录的账号会在当前请求结束后停止",
"success": True,
}
def _start_relogin(req: CookieReloginRequest, db: Session, current: User):
@@ -667,7 +726,9 @@ def relogin_invalid_cookie_operations(
query = query.filter(Account.tag == tag.strip())
if search.strip():
pattern = f"%{search.strip()}%"
query = query.filter(or_(Account.username.ilike(pattern), Account.tag.ilike(pattern)))
query = query.filter(
or_(Account.username.ilike(pattern), Account.tag.ilike(pattern))
)
tasks = query.order_by(LoginTask.id.asc()).all()
return _start_relogin_tasks(tasks, db, current, action="cookie:relogin_invalid")
@@ -681,7 +742,9 @@ def get_cookie(
"""获取单条 Cookie 详情,供复制操作按需读取完整敏感字段。"""
if not user_has_permission(current, "cookie:view"):
raise HTTPException(status_code=403, detail="无权限: cookie:view")
task = _visible_cookie_tasks_query(db, current).filter(LoginTask.id == task_id).first()
task = (
_visible_cookie_tasks_query(db, current).filter(LoginTask.id == task_id).first()
)
if not task:
raise HTTPException(status_code=404, detail="记录不存在")
acc = db.query(Account).filter(Account.id == task.account_id).first()
@@ -691,7 +754,9 @@ def get_cookie(
"account_id": task.account_id,
"account_username": acc.username if acc else "",
"assigned_to": acc.assigned_to if acc else None,
"assigned_username": acc.assigned_user.username if acc and acc.assigned_user else None,
"assigned_username": acc.assigned_user.username
if acc and acc.assigned_user
else None,
"created_at": _fmt_dt(task.finished_at),
"ck_check_status": task.ck_check_status or "",
"ck_check_result": task.ck_check_result,
@@ -726,7 +791,11 @@ def delete_cookies_batch(
t.status = "failed"
t.message = "Cookie已清除"
db.commit()
return {"message": f"已删除 {len(tasks)}", "deleted": len(tasks), "success": True}
return {
"message": f"已删除 {len(tasks)}",
"deleted": len(tasks),
"success": True,
}
@router.delete("/{task_id}")
@@ -736,7 +805,9 @@ def delete_cookie(
current: User = Depends(require_permission("cookie:export")),
):
"""删除一条 Cookie 记录。"""
task = _visible_cookie_tasks_query(db, current).filter(LoginTask.id == task_id).first()
task = (
_visible_cookie_tasks_query(db, current).filter(LoginTask.id == task_id).first()
)
if not task:
raise HTTPException(status_code=404, detail="记录不存在")
task.cookie = ""