diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..122ec6f --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +server/node_modules/ +.env +*.log +.DS_Store diff --git a/PROJECT_ARCHITECTURE.md b/PROJECT_ARCHITECTURE.md new file mode 100644 index 0000000..f3d04f5 --- /dev/null +++ b/PROJECT_ARCHITECTURE.md @@ -0,0 +1,700 @@ +# 涩花塘磁力助手 - 项目架构与部署说明 + +> 更新时间:2026-03-14 +> 说明:本文档描述当前项目的整体架构、核心数据流、缓存策略、云同步设计、线上部署信息(**不包含任何敏感凭据**),以及当前已知问题与优化方向。 + +--- + +## 1. 项目概览 + +本项目由两部分组成: + +1. **Chrome 扩展端** + 负责在涩花塘论坛页面中: + - 识别列表页 / 帖子页 + - 批量抓取帖子与磁力链接 + - 本地缓存帖子、范围快照、页快照、磁力结果 + - 展示搜索结果、缓存总览、收藏夹、云同步入口 + +2. **云端共享缓存服务** + 负责: + - 共享线程缓存(shared thread cache) + - 共享范围缓存(shared coverage cache) + - 共享页缓存(shared page cache) + - 覆盖规划(coverage planning) + - 私有保险柜(vault) + - 账号注册 / 登录 / 鉴权 + +--- + +## 2. 目录结构 + +### 扩展端根目录 + +- `manifest.json`:Chrome 扩展清单(MV3) +- `content.js`:内容脚本,挂载 UI、执行抓取、合并缓存、结果展示 +- `background.js`:Service Worker,负责 IndexedDB、本地状态、云接口、跨页请求 +- `popup.html` / `popup.js`:扩展独立云同步中心 + 快捷复制入口 + +### 服务端目录 + +- `server/package.json`:服务端依赖与脚本 +- `server/src/index.js`:Fastify 入口 +- `server/src/config.js`:环境变量配置 +- `server/src/db.js`:MySQL 连接池 +- `server/src/redis.js`:Redis 连接与 JSON 缓存 +- `server/src/crypto.js`:AES-GCM 加密与哈希 +- `server/src/auth.js`:认证辅助逻辑 +- `server/src/routes/auth.js`:注册 / 登录 / me / logout +- `server/src/routes/vault.js`:私有保险柜 push / pull +- `server/src/routes/shared-cache.js`:共享缓存查找 / 写入 / coverage planning +- `server/sql/001_init.sql`:数据库初始化表结构 + +--- + +## 3. 扩展端架构 + +### 3.1 页面层(content.js) + +`content.js` 负责: + +- 判断当前页面是: + - 列表页(forumdisplay / forum-x-y.html / normalthread tbody) + - 帖子页(thread-xxx / tid=xxx) +- 创建浮动面板 UI +- 管理: + - 关键词搜索 + - 页码范围 + - 速度模式 + - 结果列表 + - 收藏页 + - 缓存页 + - 云同步页入口 +- 调用后台: + - 读取 / 保存本地缓存 + - 读取 / 保存进度状态 + - 触发云同步相关动作 + +### 3.2 后台层(background.js) + +`background.js` 是当前项目的核心调度层,负责: + +- IndexedDB 缓存存取 +- 云缓存接口调用 +- 共享缓存 planning 调用 +- 上传抑制(hash 去重 + TTL 节流) +- 本地历史缓存回填到云端 +- 私有保险柜加解密 +- 云账号登录状态维护 + +### 3.3 本地存储层 + +当前本地存储分三类: + +1. **IndexedDB(结构化缓存)** + - `threads` + - `coverages` + - `pageCoverages` + +2. **chrome.storage.local(扩展私有存储)** + - 收藏夹 + - 搜索历史 + - 云同步状态 + - 上传元数据去重表 + - 进度状态 + +3. **运行时内存态** + - 当前抓取状态 + - session backup 临时状态 + +--- + +## 4. 服务端架构 + +### 4.1 服务端职责 + +云服务现在不仅是“存数据”,还承担: + +- 共享缓存存储 +- 去重写入 +- coverage planning +- Redis 缓存 planning 结果 +- 账号认证 +- 私有保险柜同步 + +### 4.2 路由职责 + +#### 认证 +- `POST /api/auth/register` +- `POST /api/auth/login` +- `GET /api/auth/me` +- `POST /api/auth/logout` + +#### 私有保险柜 +- `POST /api/vault/push` +- `POST /api/vault/pull` + +#### 共享缓存 +- `POST /api/shared-cache/threads/lookup` +- `POST /api/shared-cache/threads/upsert` +- `POST /api/shared-cache/coverages/lookup` +- `POST /api/shared-cache/coverages/upsert` +- `POST /api/shared-cache/pages/lookup` +- `POST /api/shared-cache/pages/upsert` +- `POST /api/shared-cache/coverages/plan` + +#### 健康检查 +- `GET /health` +- `GET /ready` + +--- + +## 5. 本地缓存模型 + +### 5.1 `threads` +存储唯一帖子级信息: + +- `forumKey` +- `threadKey` +- `url` +- `title` +- `lastSeenAt` +- `magnets` +- `lastMagnetSyncAt` + +特点: +- 同一板块 + 同一帖子只保留一份 +- 线程磁链缓存优先从这里命中 + +### 5.2 `coverages` +存储范围级快照: + +- `forumKey` +- `startPage` +- `endPage` +- `strategy` +- `frontRefreshPages` +- `threadKeys` + +特点: +- 表示某个页范围对应的帖子集合 +- 用于 exact coverage / shifted coverage / 历史 coverage 碎片复用 + +### 5.3 `pageCoverages` +存储页级快照: + +- `forumKey` +- `page` +- `threadKeys` + +特点: +- 用来组装连续页块 +- 当前大范围命中优化的重要基础 + +--- + +## 6. 云端缓存模型 + +### 6.1 共享线程缓存 `shared_thread_cache` +- 唯一键:`(forum_key, thread_key)` +- 数据库存储加密:AES-GCM +- 用于跨用户复用帖子级磁链结果 + +### 6.2 共享范围缓存 `shared_coverage_cache` +- 唯一键:`(forum_key, start_page, end_page, strategy)` +- 用于整段范围的覆盖规划 + +### 6.3 共享页缓存 `shared_page_cache` +- 唯一键:`(forum_key, page)` +- 用于连续页块拼装 + +### 6.4 私有保险柜 `vault_items` +- 用户级密文存储 +- 当前用于: + - 收藏夹 + - 搜索历史 + - UI 设置 + - 进度状态 + +--- + +## 7. 抓取流程(当前实际运行路径) + +### 7.1 点击“开始获取”后的流程 + +1. 读取页码范围 / 关键词 / 速度配置 +2. 构建 `searchContext` +3. 显示: + - `正在规划缓存:检查本地缓存 / 云端规划 / 复用块...` +4. 调用 `getCachedCoveragePlan(...)` +5. 依次判断: + - exact coverage + - assembled page coverage + - 服务端 planning + - cachedBlocks + - shiftedCoverage +6. 对缺口区间执行 live 抓取 +7. 结果与缓存合并后,再写回本地与云端 + +### 7.2 实际搜索执行路径 + +主要函数: + +- `fetchFromPage(...)` +- `fetchLivePageRange(...)` +- `processCachedThreadBatch(...)` +- `applyCachedMagnetHits(...)` + +--- + +## 8. 去重与上传抑制策略 + +### 8.1 本地去重 + +- 线程去重:`normalizeCachedThreads()` +- 磁链去重:`normalizeMagnetList()` +- coverage 合并去重:`mergeCoverageThreads()` +- coverage 保存前合并:`mergeCoverageThreadLists()` + +### 8.2 云端去重 + +数据库唯一索引 + `ON DUPLICATE KEY UPDATE` + +### 8.3 上传抑制 + +本地维护 upload meta: + +- `threads` +- `coverages` +- `pages` + +每类记录: +- `payloadHash` +- `lastUploadedAt` + +TTL: + +- 线程:10 分钟 +- 页:30 分钟 +- 范围:60 分钟 + +作用: +- 降低重复上传 +- 降低重复加密 +- 降低数据库写入压力 + +--- + +## 9. 当前“智能更新”逻辑 + +### 已实现 + +1. front refresh(从第一页开始时可刷新前段) +2. shiftedCoverage 复用 +3. page cache block 复用 +4. intersecting coverage fragments 复用 +5. 服务端 coverage planning +6. Redis 缓存 planning 结果 + +### 当前仍不够理想的点 + +1. **thread cache 很多,但未完全反向沉淀成 page/coverage 索引** + 这会导致“明明有很多历史线程缓存,但大范围规划不够聪明”。 + +2. **coverage 合并仍然偏保守** + 少量前页更新后,虽然已经补了历史 coverage 合并,但仍建议继续增强“旧帖保留连续性”。 + +3. **前段刷新策略仍是经验型策略** + 当前已从“固定刷新前 20 页”改为“前面没有缓存覆盖才刷新”,但仍属于启发式策略。 + +4. **启动前规划仍然是同步等待** + 现在已经加了状态提示,但未来可以做: + - 本地快速规划优先 + - 云端规划异步补充 + +--- + +## 10. 线上部署信息(不含敏感凭据) + +### 服务器基础信息 + +- 公网 IP:`47.238.173.98` +- 域名:`s.52oai.com` +- 系统:CentOS 7(当前会话已确认) +- 部署方式:Nginx + PM2 + Node.js + MySQL + Redis +- 面板:宝塔面板(BT Panel) + +### 当前服务端组成 + +- Nginx:反向代理入口 +- Node 服务:`magnet-cloud-cache` +- PM2:进程守护 +- MySQL:主数据存储 +- Redis:coverage planning 结果缓存 + +### 线上访问入口 + +- `https://s.52oai.com/health` +- `https://s.52oai.com/ready` + +### Redis 状态 + +当前已验证: + +- Redis 可连接 +- 可写入/读取 JSON +- coverage planning key 已落库 + +--- + +## 11. 当前安全边界 + +### 已完成的安全改造 + +1. 云同步登录/注册 UI 已迁移到扩展独立页面 +2. 收藏/搜索历史已迁移到扩展私有存储 +3. session 进度备份已不再依赖页面 `sessionStorage` +4. shared-cache 写接口已加写入令牌鉴权 +5. shared-cache 已避免旧写覆盖新写 + +### 仍建议后续加强 + +1. `vaultKeyBase64` 改为 session-only +2. auth/vault/shared-cache 更细粒度限流 +3. 服务端请求体大小再收紧 +4. 共享缓存写审计日志 + +--- + +## 12. 当前最值得继续优化的方向 + +### P1 - 正确性优先 + +1. **thread cache 反向沉淀成 page/coverage 索引** + 这是目前提升“大范围缓存命中率”的最高价值项。 + +2. **coverage 合并策略继续增强** + 尤其是前段少量更新后,历史旧帖不应被挤出当前范围搜索。 + +3. **规划器结果可观测化** + 建议增加 debug summary: + - 命中 exact coverage + - 命中 page blocks 数 + - 命中 intersecting coverage 数 + - 实际缺口页数 + +### P2 - 性能优先 + +1. 本地快速规划优先,云端 planning 异步补充 +2. planning 结果本地短时缓存 +3. 回填队列分批化 + +--- + +## 12.1 当前抓取合并 / 去重 / 智能更新逻辑复盘 + +### 一、当前逻辑里已经做对的部分 + +1. **线程级去重已经明确** + - 本地通过 `threadKey` 去重 + - 云端通过 `(forum_key, thread_key)` 唯一约束去重 + +2. **磁链级去重已经明确** + - 本地通过 `normalizeMagnetList()` 去重 + - 云端共享缓存目前按线程保存,磁链集合在单线程内部去重 + +3. **coverage 规划已经从“只看精确范围”进化为多层来源** + 当前 `getCachedCoveragePlan()` 已经综合: + - exact coverage + - assembled page coverage + - 服务端 planning + - cachedBlocks(页块) + - intersecting coverages(相交范围碎片) + - shiftedCoverage + +4. **上传抑制已经开始发挥作用** + - thread/page/coverage 都有 upload meta + - hash + TTL 可以减少重复上云 + +### 二、当前逻辑不够理想的核心点 + +#### 1. 线程缓存与页/范围索引仍然是“半脱节” + +这是目前最关键的问题: + +- `threads` 里可能已经积累了大量帖子与磁链 +- 但这些历史数据没有系统性地回灌成: + - `pageCoverages` + - `coverages` + +结果就是: + +> 看起来缓存很多,但真正能参与“大范围规划”的缓存块不够多。 + +#### 2. 智能更新仍然偏启发式,而不是差异驱动 + +当前 front refresh / shiftedCoverage 还是基于: + +- 从第 1 页开始时,倾向刷新前段 +- 历史范围快照的最近性 + +但它缺少真正的: + +- 基于“帖子是否变化”的差异判断 +- 基于线程游标 / 内容指纹 / 页指纹 的更新模型 + +#### 3. coverage 合并虽然已经补强,但仍然不够结构化 + +当前已经补了: + +- 保存新 coverage 前,合并历史相交 coverage 的线程 + +但这个策略仍是: + +- 按线程集合合并 +- 不是按“真实页结构”或“线程页位置偏移”合并 + +所以在论坛前段轻微更新时,仍可能出现: + +- 范围快照连续性不足 +- 旧帖可见性波动 + +#### 4. 服务端 planning 已加入,但仍然是“辅助规划器” + +现在服务端已经能返回: + +- `exactCoverage` +- `cachedBlocks` +- `shiftedCoverage` + +但扩展端仍保留了大量本地规划逻辑,导致: + +- 本地规划与服务端规划并存 +- 逻辑复杂度上升 +- 调试成本高 + +### 三、当前最值得优先继续优化的技术方向 + +#### A. `threads -> pageCoverages / coverages` 反向沉淀 + +这是正确性和命中率的第一优先级优化。 + +目标: + +- 当系统手里已经有大量 thread cache 时 +- 能定期 / 按需将其重建为: + - page coverage block + - coverage fragment + +收益: + +- “缓存明明很多却命中不上”的问题会明显缓解 + +#### B. 引入“线程或页面指纹”作为智能更新依据 + +例如: + +- 页面线程列表 hash +- 范围线程集合 hash +- 线程集合版本号 + +这样 front refresh 就能从“经验刷新”变成: + +- 先比对指纹 +- 只有前段真的变化才刷新 + +#### C. 最终把 planning 统一到服务端主导 + +建议目标不是“双规划器”,而是: + +- 服务端负责 coverage planning +- 扩展端只负责执行计划 +- 本地逻辑只保留兜底回退 + +这样架构会明显更稳。 + +### P3 - 运维优先 + +1. 共享缓存保留策略 / 归档策略 +2. 写入审计与异常监控 +3. 密钥轮换机制 + +--- + +## 13. 当前版本的整体评价 + +### 已经具备的能力 + +- 本地线程 / 范围 / 页缓存 +- 云端共享缓存 +- 私有保险柜 +- 上传抑制与 TTL 节流 +- coverage planning + Redis planning cache +- 扩展端 UI / 云同步中心 / 收藏 / 搜索历史 + +### 当前最核心的短板 + +> 缓存数据已经很多,但“如何把这些缓存组织成最优复用计划”仍然不够强。 + +也就是说,当前系统最难的不是“存缓存”,而是: + +> **把已有缓存规划成正确、完整、连续的搜索路径** + +--- + +## 14. 文档使用建议 + +如果后续你继续迭代项目,建议把本文档当成: + +1. **新人接手说明** +2. **线上部署说明** +3. **缓存/同步逻辑总览** +4. **后续重构路线图** + +--- + +## 15. 敏感信息说明 + +本文档**不记录**以下信息: + +- SSH 密码 +- MySQL 密码 +- write token +- 加密密钥 +- `.env` 明文内容 + +这些信息应只保留在安全的部署环境与秘密管理系统中。 + +--- + +## 16. Chrome 分发与更新能力边界 + +结合当前平台限制和架构评估,下面是**真实可行**与**不可行**的边界。 + +### 16.1 对普通 Chrome 用户,不可直接做到的事 + +以下能力在普通非企业托管 Chrome 中,**不现实或不受支持**: + +1. **从网站自动把扩展装进 Chrome** +2. **通过主页密码后,静默安装 CRX** +3. **普通用户使用自托管 CRX 自动更新** + +也就是说: + +> `s.52oai.com` 可以做“分发引导页”,但不能真正实现“输密码后自动装到 Chrome 里”。 + +### 16.2 最现实的分发方式 + +对于普通用户,最推荐: + +1. **发布到 Chrome Web Store(建议 unlisted)** +2. `s.52oai.com` 做密码门禁页 +3. 门禁通过后展示: + - CWS 安装链接 + - 安装说明 + - 使用说明 + +这样可以获得: + +- 官方安装路径 +- Chrome 自动更新 +- 最低用户支持成本 + +### 16.3 自托管更新什么时候才成立 + +只有在以下场景,自托管更新才是现实方案: + +- **企业托管 Chrome** +- 管理员通过策略安装 +- 使用企业设备 / 组织统一管理浏览器 + +这不是普通公网用户场景。 + +### 16.4 当前项目建议的分发策略 + +如果未来要正式分发,推荐顺序: + +1. **Chrome Web Store(最好 Unlisted)** +2. `s.52oai.com` 做密码门禁与引导页 +3. 扩展内做“最低支持版本”检查 +4. 如果版本太低,则提示用户去商店更新 + +### 16.5 站点密码的真实作用 + +主页密码(例如 `123456`)只能充当: + +- 引导门槛 +- 简单访问控制 +- 降低无关访问 + +但它**不能视为真正安全边界**。真正的权限控制仍然必须放在: + +- 后端账号系统 +- API 鉴权 +- 服务端授权逻辑 + + +--- + +## 16. Chrome 分发与更新能力边界(部署到主页的现实约束) + +### 16.1 对普通 Chrome 用户,不能直接做到的事 + +对于**普通、非企业托管**的 Chrome 用户,当前不支持: + +1. 从 `s.52oai.com` 网页直接自动安装扩展 +2. 从自有网站静默安装 CRX +3. 对普通用户使用自托管 CRX 做自动更新 + +也就是说: + +> 网站可以做“入口页 / 密码门禁页 / 引导页”,但不能真正代替 Chrome 官方安装渠道完成自动安装。 + +### 16.2 对普通用户最现实的方案 + +最可行方案是: + +1. 发布到 **Chrome Web Store(建议 Unlisted)** +2. `s.52oai.com` 作为密码门禁页 +3. 输入密码后展示: + - Chrome Web Store 安装链接 + - 使用说明 + - 更新说明 + +这样可以获得: + +- 标准安装体验 +- Chrome 自动更新 +- 最低的用户支持成本 + +### 16.3 自托管自动更新什么时候可行 + +只有在: + +- 企业托管 Chrome +- 管理员策略安装 +- 受管设备/受管浏览器环境 + +这种情况下,自托管 CRX + update manifest 才是现实路径。 + +### 16.4 当前项目推荐的分发模式 + +推荐顺序: + +1. **Chrome Web Store(Unlisted)** 作为主分发渠道 +2. `s.52oai.com` 做密码门禁 + 安装引导页 +3. 扩展内调用服务端 `/ext/config`(后续可实现)检查最低支持版本 +4. 如果版本过低,提示用户跳转到商店升级 + +### 16.5 密码门禁的真实作用 + +密码门禁只能起到: + +- 隐藏安装入口 +- 控制谁能看到链接 + +但它**不能视为真正的安全边界**。真正的权限控制必须放在: + +- 后端账号体系 +- API token / 登录态 +- 服务器侧权限判断 diff --git a/popup.html b/popup.html index a55d398..5be8d89 100644 --- a/popup.html +++ b/popup.html @@ -2,7 +2,7 @@
- +