Commit Graph
37 Commits
Author SHA1 Message Date
yml2213 00125fbfc4 refactor(account): S2 读写一次切换 —— CK/健康/重登彻底搬到账号,登录记录退化为历史
S1 加好了列,本提交把**读与写一起切**过去。这是设计里「同一份数据的读与写必须
在同一个提交里切」的要求:先切读或先切写都会出现「写账号、读记录」的中间态,
跑起来不报错,只是默默用错数据。

## 核心改动

**判定「谁有 CK」不再借道登录记录**
- `cookie_holding_filter()` / `cookie_operable_filter()` / `cookie_holding_account_ids_query()`
  / `visible_douyu_task_accounts()` 全部改为作用于 `Account`;
- `cookie_operable_filter()` 的「在途排除」仍查 `login_tasks`(那是**某次尝试**的属性),
  用 `NOT EXISTS` 子查询 + 迁移 0042 新增的 `(account_id, attempt_status)` 索引;
- 删除死代码 `latest_cookie_login_task()`;`latest_cookie()` 改为直接读账号。

**CK 写入**
- `_write_login_success()` 写 `Account.cookie / cookie_updated_at / cookie_source`;
- `_mark_attempt_failed()` 改看**账号**有没有 CK(决定是否沿用旧的失败语义);
- 检测模式的 `task.cookie = ""` **不再清空账号的 CK**(见下「行为变更」)。

**自动化作业按账号调度**
- `ck_health`:`_select_tasks` → `_select_accounts`,`db.query(Account)`;新增
  `count_remaining()`(刻意不带 LIMIT,否则积压数恒等于批次上限);
- `ck_relogin`:同样按账号选行;结算改为「账号 + 本轮尝试记录」,
  退避进度写账号;`settle_fresh_results(batch_id)` 按 `attempt_batch_id` 配对,
  `run_ck_relogin` 把 **account_ids** 交给 runner;
- 顺带让**没有任何登录历史的账号**也能被巡检/重登(以前它们永远进不了队列)。

**登录批次**
- `LoginBatchRunner` 去掉 `relogin_task_ids`,重登与登录统一走 `account_ids`;
- 批次行复用逻辑重写:只保证「同一账号不并发两行」,不再关心「哪一行持有 CK」。
  失败行复用同时看 `attempt_status`(账号有 CK 时 `_mark_attempt_failed` 刻意不动
  `status`,只看 status 会永远命不中 → 每次失败刷一行)。

**删除护栏语义变更(用户可感知)**
- 去掉「持有 CK 的记录不可删」——CK 在账号上,删历史不再影响登录态,历史终于可清理;
- **保留**「在途尝试不可删」(批次线程正拿着那条记录)。

## 行为变更(需评审)

1. **账号检测不再清空 CK**。旧代码在 check 模式写 `task.cookie = ""`,而那时记录的
   cookie 就是账号唯一凭证 —— 一次「批量检测」会把账号登录态删掉。解耦后
   「检测账号凭据」与「CK 是否有效」本就是两件事(后者归 `ck_health` 判),
   继续清空等于凭空制造不可逆的数据丢失(重登当前成功率极低)。
2. **Cookie 页的记录单位从「登录记录」变成「账号」**,所有接口的 id 入参/出参
   变成 `account_id`;`DELETE /cookies/{id}` 清的是该账号的 CK,**必写审计**。
3. 重复检测从「同一账号多行持 CK」改为「**同一个 CK 出现在多个账号上**」——
   后者才是真风险(两个账号共用一个登录态)。用 sha256 指纹分组:
   CK 是 AES-GCM + 随机 nonce 加密,**同一明文每次密文都不同**,比密文永远比不出重复。

## 修掉一个真实并发缺陷

`_write_login_success` 原先被传入 `self.db`(runner 主会话),而它是在**工作线程**里
用 `worker_db` 执行的。`emit()` 会往 `account_events` 插一行,其 `account_id` 外键对
`accounts` 那一行加**共享锁**;这把锁加在另一个会话上,紧接着本会话的
`UPDATE accounts SET cookie=...` 就被自己人挡住 —— 实测 **50 秒后
`Lock wait timeout exceeded`,登录明明成功却写不回 CK**(改造前 UPDATE 打在
`login_tasks` 上所以没撞上)。改为传 `worker_db`:事件本就该和状态变更同事务提交,
顺带修掉跨线程共用一个 Session 的隐患。

## 验证

- 全量 pytest **368 passed / 6 skipped**;迁移链 3 passed;
- 前端 `tsc -b && vite build` 通过;
- `ruff check` / `ruff format --check` 全绿(顺带修掉 main 上遗留的 F821);
- 新增/改写的关键回归:**删掉全部登录历史后账号的 CK 与健康状态仍在**、
  **无登录历史的账号也能进巡检队列**、结算不碰 CK、删除护栏改为在途语义。

仍未做(设计里排期的后续阶段):`account_lifecycle` 状态收口(S3)、事件发射点补齐(S4)、
业务事实直发事件(S5)、账号页重构(S6)。注意 `status` 目前只有模型默认值与迁移回填,
所以 `disabled/archived` 还没有任何写入方——S3 落地时要把自动化选行的状态过滤一起补上。
2026-10-04 14:10:13 +08:00
yml2213 834bfdd1d0 feat(account-events): 账号详情页与账号事件历史全链路支持
1. 新增账号事件数据表 AccountEvent 与迁移版本 20261002_0040_account_events,支持审计 Cookie 变更、自动重登、健康检查及任务执行事件;
2. 后端新增 account_events_service 并扩展 /api/accounts 路由,支持事件查询、过滤与上报;
3. 前端新增 AccountDetailPage 账号详情页组件,支持账号元信息展示、事件历史瀑布流与快捷重登操作;
4. 修复 CK 重登与自动化定时任务中事件关联逻辑,补充 test_account_events 单元测试;
5. 新增生产服务器高负载排查分析报告及相关设计文档。
2026-10-03 12:58:58 +08:00
yml2213 6a25097412 fix(douyu): 替换 CK 是最后一步,只有登录成功才写回记录
上一版把可见性改成「记录里还有 CK」,账号不会再消失,但根因还在:批次启动
时就把复用的成功记录改成 pending(旧 CK 仍在),停止或失败后没人恢复状态,
记录一直显示成一次没完成的登录。

按「替换 CK 是最后一步」把两者拆开:

- login_tasks 新增 attempt_status / attempt_message / attempt_batch_id /
  attempt_at,批次只写 attempt_*,status/cookie/message 只在登录成功时更新;
- _begin_attempt 只登记尝试,_mark_attempt_failed 只记录失败(已有 CK 的记录
  原样不动),_write_login_success 是唯一替换旧 CK 的入口;
- 停止批次、代理不可用、登录异常、进程重启清理全部改为收敛 attempt_*,
  因此停止一个批次不可能再让账号失去 CK;
- 复用的记录改用「持有 CK 的记录」,不再创建第二条记录,失败记录在无 CK 时
  才复用;
- 登录任务列表/统计按 attempt_* 展示「这次登录怎么样了」,批次筛选同时匹配
  attempt_batch_id;CK 操作页的重登状态也从 attempt_* 映射;
- 迁移 20261001_0035 归一存量数据:持有 CK 的记录 status 统一为 success、
  历史状态搬进 attempt_*,无 CK 且卡在 pending/running 的收敛为 failed。

新增测试:批次失败/停止不碰旧 CK、成功后替换 CK、列表展示尝试状态、
迁移存量数据归一。
2026-10-01 18:29:49 +08:00
yml2213 64c399617a fix(douyu): 账号只要还持有 CK 就不再从工作台消失
线上事故:分配给客服的账号(如「远行者103」)在斗鱼工作台与 CK 列表里
查不到,只有账号管理里还能搜到。原因是可见性判定写成了
LoginTask.status == "success",而按设计保留旧 CK 的三种状态都不再是
success:

- 普通登录批次启动时会把复用的成功记录置为 pending/running(旧 CK 保留),
  批次被停止或失败后一直停在 pending,账号永久消失;
- CK 重登失败置为 relogin_failed(旧 CK 保留),账号从工作台消失;
- 普通登录失败置为 failed(复用记录的旧 CK 未清空),同样消失。

线上现状:12545 个账号持有 CK,但只有 11703 个能出现在工作台,842 个
(644 重登失败 + 176 排队中 + …)对客服和管理员都不可见。

改动:
- douyu_service 新增 cookie_holding_filter()/latest_cookie_login_task(),
  以「记录里还留着 Cookie」作为唯一判据,并抽出 LOGIN_IN_FLIGHT_STATUSES
  与 cookie_operable_filter() 供检测/重登入口复用;
- 斗鱼工作台账号列表、任务账号校验、账号管理 has_cookie 筛选、分配校验与
  概览、CK 管理/操作列表、首页 CK 统计统一改用该判据;
- 工作台取 CK 从 latest_success_login_task 换成 latest_cookie_login_task,
  保证重登失败/排队中的账号仍能被任务正常执行;
- 新增 cleanup_orphan_login_tasks():进程重启后把遗留的 pending/running
  收敛为 failed 并保留旧 CK,避免页面永久显示排队中。

新增回归测试覆盖上述状态下的可见性、可执行性与启动清理。
2026-10-01 18:18:02 +08:00
yml2213 09ab80e062 完成 Ruff 全量清理 2026-08-31 10:55:44 +08:00
yml2213 b58c6b4357 执行 Ruff 安全自动修复 2026-08-31 10:28:14 +08:00
yml2213 c891ac982e type: 收敛测试 schemas 与协议层类型 2026-08-30 20:35:08 +08:00
yml2213 22aa485536 fix(web): 审核回归 — 5 项修复 (渠道写入丢失/斗鱼WS生命周期/归属校验/恢复订阅/画像key稳定)
B1 routers/huya.py: 批量 Web 登录 login_channel/status 回填在 helper 内部 commit
   之后赋值且未再提交 → 丢失; 补 db.commit() 并统一 status=active
B2 routers/login.py + services/login_service.py: 斗鱼登录 WS 断线即 pop 批次,
   刷新后无法重连订阅 → 对齐虎牙模式 (BatchRegistry.mark_finished, 仅结束后清理)
B3 routers/login.py: WS 增加批次归属校验 (非 view_all 仅可订阅自建批次)
S1 LoginTasksPage: effectiveBatchId 从任务列表推导活跃批次, 刷新后停止按钮/
   日志订阅自动恢复
S4 services/huya_service.py: username 仅在为空时回填, 防止 udb_passport 覆盖
   登录名导致一号一设备绑定 key 漂移

验证: 77 后端单测 OK; tsc + vite build OK
2026-08-29 18:20:27 +08:00
yml2213 bf2f78aa70 优化客服CK重登流程 2026-08-14 14:43:21 +08:00
yml2213 948e7d7246 fix: keep one douyu cookie per account 2026-08-08 21:26:32 +08:00
yml2213 39ea0d2184 feat(cookies): Cookie 失效后可账号重新登录替换, 并修复 Cookie 列空白过多 2026-08-08 01:39:02 +08:00
yml2213 cd2d02c86e 增加斗鱼账号检测功能 2026-07-09 23:12:39 +08:00
yml2213 d7d7c0cbb0 增加另一个登录接口 2026-06-26 17:22:51 +08:00
yml2213 0b4fe1daa2 继续优化登录逻辑 2026-06-25 08:40:00 +08:00
yml2213 a62e7c192f 优化白名单逻辑 2026-06-25 08:19:18 +08:00
yml2213 9b66a4fcd9 登录任务最大重试次数改为 20 次 2026-06-25 07:18:10 +08:00
yml2213 b9481d49ee 修复登录任务无限重试导致文件描述符耗尽问题
1. docker-compose.yml:增加文件描述符限制 ulimits (65536)
2. login_service.py:限制最大重试次数为 10 次,避免无限重试
3. 解决 [Errno 24] Too many open files 错误
2026-06-25 06:58:18 +08:00
yml2213 e478ac4d09 简化登录流程:砍掉代理池,极验不内部切代理
- 新建 ProxyFetcher 替代 ProxyManager,每次从API取1个代理,无池无锁无冷却
- 极验最多3次尝试,失败直接抛异常回到login整体重试取新代理
- 删掉极验内部的 refresh_proxy/proxy_switches/_soft_fail_streak 逻辑
- login整体重试时取新代理从头走完整流程
- 代理验证超时从(5,8)缩短为(3,5),白名单同步后立即重试不等待
- login_service 删掉60行代理池预检/等待代码
- 修复邮件验证码日期比较:Roundcube分钟精度与秒级时间戳对齐
2026-06-24 15:37:48 +08:00
yml2213 83345dfdc5 增加了代理平台和日志 2026-06-24 14:22:29 +08:00
yml2213 0d03b2c242 优化登录重试与配置清理 2026-06-24 10:20:33 +08:00
yml2213 7bb50a9bbf 增强登录稳定性 2026-06-24 09:17:15 +08:00
yml2213 177ec42416 加密敏感数据并优化补CK流程 2026-06-24 09:10:31 +08:00
yml2213 4813eb039d feat: 极验验证默认无限重试,由登录整体超时兜底
- max_proxy_retries 默认值从10改为0(无限重试)
- _solve_geetest 增加 deadline 参数,超时则主动退出
- login() 计算 deadline 并传递给极验验证
- 前端/Schema/Service 默认值同步更新
2026-06-23 23:40:40 +08:00
yml2213 cc30697c97 feat: 登录流程整体重试机制 + 代理池耗尽等待恢复
- DouyuLogin.login() 添加整体重试循环,失败时换新代理从头重跑
  - 新增 max_login_retries(默认3次)和 max_total_time(默认300秒)
  - 重试前重置 session cookies + 换新代理,避免残留状态
  - 成功后归还代理到池;失败时标记代理坏
- 代理池耗尽时不再立即放弃账号,等待最多60秒恢复后再试
- API代理模式改为 DouyuLogin 内部通过 proxy_manager 自治管理
  登录过程中的代理切换(极验、整体重试)不再由服务层预取
- Schema/LoginBatchRequest 添加 max_login_retries 和 max_total_time
- 前端 API 调用改为对象参数形式,透传新参数
2026-06-23 22:06:00 +08:00
yml2213 2ab6724543 refactor: 补全services层+消除core/models.py冗余+修复network.py缩进错误
高优先级问题1 - Router层业务逻辑泄漏:
- 新建 services/proxy_service.py: 从routers/proxy.py提取ProxyService类,
  封装代理/白名单测试逻辑、全局状态管理、所有core/ import
- 新建 services/account_service.py: 从routers/accounts.py提取
  split_account_line、cookie_account_ids_query、parse_and_build_accounts,
  封装core.douyu.email_verifier的import
- 重写routers/proxy.py为薄HTTP/WS层,移除所有core/直接依赖
- 精简routers/accounts.py,从account_service导入业务函数
- 将_active_batches全局状态移入login_service.py的BatchRegistry类
- routers/login.py改为使用batch_registry单例

高优先级问题2 - 两套models.py冗余:
- 删除core/models.py(Account dataclass + ProxyConfig死代码)
- 在core/douyu/login.py新增AccountLike Protocol替代Account dataclass
- login_service.py中Account(...)构造改为SimpleNamespace(...)
- 清理core/__init__.py导出

附带修复:
- core/geetest/common/network.py的get_picture()函数缩进错误
  (缺少if match:块和data赋值语句)
2026-06-23 07:34:17 +08:00
yml2213 453a637480 refactor: 修复9项中等架构问题
安全修复:
- WebSocket 端点添加认证(cookie/token),防止未授权窃听日志
- SPA serve_spa 添加路径遍历防护(resolve + relative_to 检查)
- Token 改用 httpOnly Cookie 存储,移除前端 localStorage token(防 XSS 窃取)
- 添加安全响应头中间件(X-Content-Type-Options/X-Frame-Options/Referrer-Policy)
- HTTP 请求日志脱敏请求体中的 password/secret/token 等敏感字段
- 权限检查统一使用 user_has_permission(考虑自定义权限,修复 has_permission 忽略 custom_permissions 的缺陷)

性能与稳定性:
- cookies.py 列表接口修复 N+1 查询(改为批量查询 Account)
- login_service.py run() 结束时关闭 DB Session(防止连接泄漏)
- _active_batches/_active_tests 全局字典添加 threading.Lock(防止并发竞态)

配置优化:
- CORS 源支持环境变量 CORS_ORIGINS 配置
- Uvicorn reload 支持环境变量 UVICORN_RELOAD 控制(生产环境默认关闭)
- Cookie 安全标志支持环境变量 COOKIE_SECURE 配置(HTTPS 部署时启用)
- logs.py 权限不足返回 HTTP 403(原来返回 200 + message)
2026-06-23 06:51:01 +08:00
yml2213 da2fedd484 优化代理管理与增加请求日志系统
代理优化(P0+P1):
- 修复极验失败后代理刷新空操作bug(共享ProxyManager+白名单参数)
- 极验请求超时从(3.05,12)调大到(10,30)
- 白名单sync_ip加全局锁防并发限流,保留多个出口IP应对漂移
- 代理池缓存共享:Condition防并发获取+mark_bad移除坏代理
- 适配代理API的JSON响应格式(code/data/白名单错误)
- 简化代理验证只验斗鱼主站,减少日志噪音
- 获取代理前主动同步白名单(解决ow=1模式不报白名单错误的问题)
- 每次重试重新检测出口IP并同步白名单

请求日志系统:
- 新增HttpLogger记录请求/响应详情到JSONL文件
- login.py的_request和proxy.py的verify_proxy_url接入日志
- 新增/api/logs路由查看和清空HTTP详情日志
- 前端新增请求日志页面(筛选/搜索/分页/自动刷新/详情查看)

其他:
- 添加pysocks依赖支持SOCKS5代理
- gitignore添加*.log
2026-06-23 00:50:57 +08:00
yml2213andClaude Fable 5 8fdf8d4b69 优化代理获取和登录逻辑
- 每个账号独立获取代理,不再共用一个代理IP
- 代理预检失败只标记该账号失败,不终止整个批次
- 代理验证改为并发:verify_proxies_concurrent 多IP同时验证
- resolve_working_proxy 多IP并发验证,找到可用即返回
- parse_proxy_response 改为返回列表,支持多行/逗号分隔
- 邮箱验证码匹配收件人地址,防止多账号并发取错验证码
- 修正 mail.bdhg.xyz 旧数据端口和SSL配置
- bdhg.xyz 默认配置改为 143端口非SSL

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-22 22:44:01 +08:00
yml2213andClaude Fable 5 52102655ac 修复IMAP连接:支持非SSL端口143
- IMAP服务器 111.229.206.54 只开放143端口(非SSL),993不通
- 默认配置改为端口143、ssl=False
- EmailVerifier 增加 use_ssl 参数,支持 IMAP4 和 IMAP4_SSL
- Account 模型增加 email_imap_ssl 字段
- 数据库迁移:新增列 + 修正旧数据端口和SSL设置
- 导入账号时自动填充 ssl 配置

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-22 20:58:31 +08:00
yml2213andClaude Fable 5 2b8716cf72 登录任务增加代理切换次数设置
- 后端: LoginBatchRequest 增加 max_proxy_retries 字段,默认10,0=无限
- 核心: DouyuLogin 极验验证循环根据 max_proxy_retries 控制重试
- 前端: 筛选栏增加代理切换次数输入框,Tooltip 提示0=无限切换直到成功

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-22 20:40:30 +08:00
yml2213andClaude Fable 5 2f9b1a8eb2 重试失败任务时复用旧记录而非创建新记录
- 重试时查找该账号最近一条失败任务,更新其状态为 pending
- 避免同一账号在列表中出现多条重复记录
- 仅复用 failed/error 状态的任务,首次登录仍新建记录

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-22 19:37:18 +08:00
yml2213andClaude Fable 5 72bcd1274c 优化登录任务界面 + 修复时间显示 + 增加删除功能
- 筛选栏改为单行横排,概览精简为一行文字
- 任务列表全宽展示,日志移到底部可折叠
- 页面使用百分比布局,一屏展示无外层滚动
- 登录任务增加单条删除和批量删除功能
- 后端: datetime.utcnow 替换为 datetime.now(timezone.utc)
- 后端: schemas 序列化统一输出带时区 ISO 格式
- 前端: 新增 utils/time.ts 统一时间格式化工具
- 前端: CookiePage/LoginTasksPage 时间列使用 formatTime()

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-22 17:56:48 +08:00
yml2213 9fba12aad0 增加重试 2026-06-22 15:45:48 +08:00
yml2213 627f06d0a0 优化 ui 2026-06-22 14:21:39 +08:00
yml2213 b08f0a647d 优化代理配置 2026-06-22 13:59:03 +08:00
yml2213 63c1ffa046 彻底优化项目结构,使用 web 2026-06-22 13:39:03 +08:00
yml2213 347edb8103 初步增加 web 界面 2026-06-22 13:11:15 +08:00