# 涩花塘磁力助手 - 项目架构与部署说明 > 更新时间: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 / 登录态 - 服务器侧权限判断