Skip to content

系统模式前置模块化 - #148

Merged
HorkyChen merged 16 commits into
PoxenStudio:developfrom
shiningsprk-arch:modaudio-20261028
Nov 4, 2026
Merged

HorkyChen merged 16 commits into
PoxenStudio:developfrom
shiningsprk-arch:modaudio-20261028

Conversation

@shiningsprk-arch

@shiningsprk-arch shiningsprk-arch commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

背景

这是 issue #123(系统模式:个人 / 标准 / 专业)的前置工作:先把「功能」从字段级设置里抽出来,做成模块级开关——一个模块 = 一个勾选框。关掉后入口消失、设置不渲染、接口拒绝,而不是散在几十个设置项里逐个关。

本 PR 落地基础设施 + 有声书、社区互动、实体书、AI、用户与账号 五个模块,把机制跑通;分享与推送、阅读增强、协议服务、工具箱、运维等按同一套机制后续单独提。

机制

三层,不做几十个平级开关:

模块开关(ENABLE_MODULE_<ID>,默认开启)
  └─ 模块内设置项(沿用现有键,不新增开关,只随模块显隐)
       └─ 字段(不动)

一个模块关闭时,三件事一起做:

  1. 能力不开放:ModuleGateMixin 在 prepare 里提前拦截 —— JSON 接口返回 {"err": "module.disabled"},文件流 / RSS 订阅源返回 404(避免把 JSON 或 HTML 错误页喂给播放器、播客客户端)。
  2. 入口隐藏:导航、页脚、书籍详情页按钮、导入页、工具箱卡片、首页卡片。
  3. 设置不显示:区块级与字段级两种 module 标签。字段级是必须的 —— 音频字幕寄居在「日志与调试」区块、站内留言开关寄居在「首页与列表」区块,按区块收会连带关掉无关设置。

老实例升级默认全开,行为零变化;关闭只隐藏,不删除任何数据。

模块一:有声书(audio)

后端

  • webserver/base/modules.py:模块表、module_enabled()、snapshot()、module_disabled_error()、ModuleGateMixin(漏写 module_id 直接抛错)
  • ENABLE_MODULE_AUDIO 默认 True,进 ADMIN_SETTING_KEYS;sys.modules 随站点信息下发
  • 门禁覆盖:audio.py 9 个 handler、podcast.py 基类(10 条路由)、音频导入 2 条、tts.py 1 条、Mimo-TTS 里会写音频目录的 4 条
  • 模块关闭时的降级:书籍列表不再回填 has_audio、sys.audiobooks 不再扫音频目录、详情接口 audios 返回空结构
  • AdminSettings.post 按模块表 setdefault 回填模块键(args 会被 dumpfile() 整份写回,少键等于抹掉开关)

前端

  • 侧边栏「有声书」、页脚 Podcast
  • 详情页顶部按钮、处理菜单音频项、转写确认框、音频列表框,并停止音频进度轮询
  • 导入页「导入有声书」按钮、批量删除提示、音频导入状态轮询
  • 工具箱 Mimo-TTS 卡片;module-gate 拦 /audiobooks、/audio/*、/toolbox/mimo_tts
  • 设置页「功能模块」区块;播客订阅区块级隐藏,音频日志 / 字幕字段级隐藏

模块二:社区互动(community)

五个子能力打包成一个勾选框:站内留言、缺书登记、书单、友情链接、私藏书籍。评论与评分不并入(已有独立开关 ENABLE_BOOK_REVIEW)。

后端

  • 门禁:user.py 留言 / 缺书 2 条、booklist.py 13 个 handler、book.py 私藏 2 条
  • /api/booklists/homepage 例外:模块关闭返回空列表而不是 module.disabled(首页按 err=="ok" 读)
  • sys 降级:allow.user_memos 强制 False、friends 不下发(顺带跳过 favicon 抓取)
  • 首页书单卡运行期强制隐藏;设置页用 apply_modules=False 取原样配置,避免关闭期间保存一次把「强制隐藏」固化进 HOMEPAGE_CONFIG
  • 公开书单的 guest_read 访客授权随模块关闭一并失效(否则会留下「书单页面没了、访客照样能读」的暗门),只影响判定、不动数据
  • 管理端导入的「设为私藏」:模块关闭时隐藏该选项,后端丢弃 sole 标记但不阻断导入本身
  • 私藏隐私过滤(别人设了私藏的书不给我看)不随模块关闭——那是防泄漏逻辑,不是 UI 能力

前端

  • 顶栏用户菜单(私藏 / 缺书)、管理菜单(缺书管理 / 留言管理)、书单组、友情链接、留言入口与弹窗
  • 书籍详情页:「加入书单」快捷菜单、私藏徽标、「设为私藏」菜单项
  • module-gate 拦 /memos、/expected、/soledbooks、/booklists/*、/booklist/:id 与两个管理页(含尾斜杠变体)
  • 设置页:新增「社区互动」勾选;站内留言字段级隐藏、友链区块级隐藏、侧边栏「书单 / 友链」勾选项改为锁定而非移除;首页卡片编辑器隐藏书单卡行但保留 order,保存不丢配置

模块三:实体书(physical)

覆盖实体书登记(ISBN / 手动录入)、册数与书架位置、电子书↔实体书互转。

后端

  • 门禁:/api/printbooks、/api/book/add、/api/book/add/manual、/api/book/exchange_type、/api/book/:id/location、管理端 CSV 批量添加(/api/admin/batch_add/run|status)
  • BookEdit 在模块关闭时丢弃 book_type / book_count / location,避免老缓存页造出看不全也改不了的物理书
  • sys 降级:allow.physical_books 强制 False(导航 / OPDS / 类型筛选 / 上传按钮统一失效),physicals 计数不再扫全库;统计缓存键带上模块状态,重开不会读到旧快照
  • OPDS 位置分类:顶层目录、nav / category / categorygroup 直连 URL 一律 404(含 which 后缀覆盖的绕过路径)
  • 后台 CSV 批量添加在服务入口与每行循环复查模块,关闭时在行边界停止写入

前端

  • /printbooks 路由守卫;导航入口随 allow.physical_books 隐藏
  • 详情页互转入口、实体书标签/册数/书架位置、编辑页物理字段
  • 管理端类型筛选与列、批量互转、导入页批量添加、缺书登记的实体书上传方式
  • 首页 / 书卡 / 自定义卡片的实体书角标与位置 chip

封面图章(addstamp)不并入实体书模块:它作用于电子书封面(2026-10-19 设计),保持独立开关。

模块四:AI(ai)

覆盖 AI 助手、AI 更新元数据(单书 + 批量)、MyNexus、MCP。

后端

  • 门禁:/api/book/:id/aifill、/api/admin/book/aifill、/api/admin/ai/test、MyNexus 全部路由、MCP stream / health
  • 助手 WebSocket:open 与每条 on_message 都复查模块,关闭后新连接与旧会话都返回 4004;服务启动时的助手初始化也跳过
  • sys 降级:aiEnabled / mynexus_enabled 强制 False;详情页不再下发 ai_companion_available / ai_reading_guide / mynexus_analysis
  • AIFillInfoService 在服务层兜底:模块关闭时拒绝新任务;运行中的批量任务在批次边界、每次 AI 调用前、apply 前复查,关闭后停止并把后台任务标记失败,不再写元数据
  • MyNexus 后台状态轮询每轮复查模块,关闭后停止外呼
  • MCP edit_book 在实体书模块关闭时对 book_count / book_type / location 明确报错,避免助手收到「修改成功」的假象

前端

  • 详情页 AI 更新、MyNexus 解析 / 伴读 / 对话框 / 状态卡
  • 管理端批量 AI 更新入口;模块关闭时停止 AI 状态轮询
  • 顶栏 AI 助手按钮与对话框
  • 设置页整个「AI 能力」区块(含 MyNexus / MCP)区块级隐藏

模块五:用户与账号(users)

覆盖用户管理、自助注册/激活、注册审批、邀请/访问码、新用户默认权限、社交登录,以及访客权限 / 下载配额 / 强制登录的设置项。

登录、退出、个人资料、密码重置、访问码保持核心可用——关闭模块不等于关闭登录。

后端

  • 门禁:AdminUsers(用户 CRUD / 配额 / 重置密码)、SignUp / UserNew / 激活接口(子类继承门禁)、社交登录 OAuth 三条路由(/auth/login|complete|disconnect,运行期按模块判定,关闭返回 404)
  • sys 降级:allow.register 强制 false、socials 下发空列表(登录页注册按钮与社交登录按钮消失)
  • 只隐藏设置、不改运行策略:访客权限(ALLOW_GUEST_*)、下载配额、强制登录(REQUIRE_LOGIN)是站点策略,关闭用户模块不会静默改变访客访问;这些设置随模块隐藏,需要调整时先重新开启模块
  • 分享链接 ENABLE_SHARE_LINK 属于「分享与推送」模块,保持可见

前端

  • 管理菜单「用户管理」、/admin/users 与 /signup 路由守卫
  • 登录页注册按钮 / 社交登录按钮由后端 sys 降级驱动隐藏
  • 设置页:新增「用户与账号」开关;注册/默认权限/社交登录区块级隐藏,访客权限与强制登录字段级隐藏
  • 修复 REQUIRE_LOGIN=true 时 /signup 被公开路径规则放过的问题:module-gate 现在对公开路径也执行模块跳转

附带改动:工具箱默认顺序整理

工具箱卡片的默认展示顺序改为按处理流程分组,前端「置顶工具」优先级不变:

  1. EPUB:EPUB美化 → EPUB修复 → EPUB合并 → EPUB拆分
  2. 文本:TXT编码修复 → 繁简转换 → 正文查找替换 → 文本处理工具
  3. PDF / 音频:PDF瘦身器 → Mimo-TTS有声书
  4. 书库整理:作者清理 → 书名语言检测 → 格式合并 → 格式精简
  5. 下载类:古书下载器 → 书栈接收器(走下载路径,本次不调整,保持末尾)

实现仅调整 webserver/toolbox/toolset.py 中 ToolSet.register() 的注册顺序(注册顺序即默认展示顺序),不改工具功能、路由和置顶逻辑。已在 WSL 环境实际执行 collect_tools() 核对顺序,py_compile 与 flake8 通过。

兼容与数据安全

  • 默认开启;auto.py 里没有该键时按开启处理
  • 不删音频文件、不清缓存、不删书单/留言/缺书记录、不重置私藏标记,重新勾选即恢复
  • 不中断已运行的音频转写 / 文件导入任务,只在入口拒绝新任务;AI 批量任务与 CSV 批量添加例外——它们在下一个批次 / 行边界停止,避免继续外呼或写入
  • 侧边栏勾选项关闭期间呈现为未勾选但保存时按存储值补回,不会「关闭期间改一次侧边栏就把原来的勾选永久删掉」

测试

  • tests/test_modules.py:48 passed / 2 skipped / 83 subtests passed
    • 逐路由反射(音频 / 社区 / 实体书 / AI / 用户与账号每条路由都挂门禁且 module_id 正确;addstamp 明确断言保持独立)
    • 登录、退出、个人资料、密码重置、访问码等核心账号接口断言不挂门禁;社交 OAuth 四条路由与 prepare 404
    • 首页书单接口开 / 关两分支;apply_module_visibility 双口径;AdminSettings.get 真实调用点断言 apply_modules=False
    • ImportRun 的 sole 丢弃 / 透传三条路径;助手 WebSocket 新连接与旧会话的 4004;OPDS 位置分类判定;MCP 物理字段报错;sys 降级(AI/MyNexus/物理/注册/社交)
    • 2 个 skip 为环境限制(toolbox / opds 路由依赖本机不可导入的 calibre)
  • tests/test_book_access_service.py 19 passed(含「模块关闭收回书单 guest_read、但分享链接与全局访客可读不受影响」)
  • test_homepage 12 / test_booklist_service 11 / test_admin_settings 13 passed
  • test_main.py 失败集合与 develop 基线逐条一致(45 个存量红)
  • eslint 0 error(1 个存量 warning);flake8 改动文件与基线逐条一致(零新增)

验收步骤

  1. 系统设置 → 站点与外观 → 功能模块,取消勾选「有声书」/「社区互动」/「实体书」/「AI」/「用户与账号」并保存
  2. 对应侧边栏、页脚、书籍详情页、导入页、工具箱、首页卡片、管理菜单、登录页入口应当同时消失
  3. 直接访问 /audiobooks、/audio/1、/memos、/booklists/publics、/soledbooks、/printbooks、/signup、/admin/users 应被弹回首页;直接调对应 API 应返回 module.disabled(助手 WebSocket 返回 4004,OPDS 位置分类与社交 OAuth 返回 404)
  4. 重新勾选并保存,入口与已有数据(音频、留言、书单、私藏标记、实体书记录、AI 解析结果、用户账号)应当原样恢复

已知限制(后续处理)

  • 设置页的「未保存计数」仍会算上被隐藏的字段:保存时一并提交,不会丢值,只是「跳到下一处修改」跳不到它们
  • 嵌套 groups 里的字段暂不支持模块过滤(当前五个模块都没有嵌套字段)
  • 模块关闭期间,旧缓存页 / 脚本调用导入接口时携带的 sole=true 会被静默丢弃(导入成功、书籍公开),不额外提示
  • 管理端只读统计(LibraryStats 的物理计数)与书籍列表 type 筛选仍可读到实体书数据——按「数据视图不是能力入口」保留,写接口全部已门禁
  • 封面图章不随实体书模块关闭(电子书封面功能,独立开关)
  • MCP 工具目录仍会列出书单类工具,实际调用走已门禁的 HTTP 接口,会返回 module.disabled
  • MCP edit_book 的 schema 未声明 location;模块关闭时对 book_count / book_type / location 明确报错
  • OPDS 位置分类的自动化测试在缺 calibre 的本地环境会 skip(与仓库既有 OPDS 测试同一限制)
  • 用户模块关闭期间:访客权限、下载配额、强制登录、重置邮件模板、邀请配置仍按已存值生效,但设置项隐藏;需要修改时先重新开启模块
  • 社交-only 账号(含管理员):关闭用户模块后社交 OAuth 全部 404,这类账号没有密码、通常也没有邮箱,无法自助重置;需要在服务器端重新开启模块,或先为其设置密码
  • 后续模块:分享与推送、阅读增强、协议服务、工具箱、运维

一个模块 = 一个勾选框:关掉后入口消失、设置不渲染、接口拒绝,数据只隐藏不删除。

后端
- 新增 webserver/base/modules.py:模块表 + module_enabled/snapshot + ModuleGateMixin
  (JSON 返回 module.disabled,文件流/订阅源走 404;漏写 module_id 直接抛错)
- ENABLE_MODULE_AUDIO 默认开启,进 ADMIN_SETTING_KEYS;sys 下发 modules 快照
- 门禁:audio 9 个 handler、podcast 基类(10 条路由)、音频导入 2 条、
  TTS 1 条、Mimo-TTS 会写音频目录的 4 条
- 模块关闭时不再回填 has_audio、不再扫 audiobooks 计数、详情 audios 返回空结构
- AdminSettings.post 按模块表 setdefault 回填,避免 dumpfile() 整份写回时抹掉开关

前端
- 侧边栏、页脚 Podcast、详情页按钮/菜单/两个对话框、导入页按钮与状态轮询、
  工具箱 Mimo-TTS 卡片全部跟随模块开关
- 新增 module-gate 路由守卫,拦 /audiobooks、/audio/*、/toolbox/mimo_tts
- 设置页新增「功能模块」区块;播客区块级隐藏,音频日志/字幕字段级隐藏,
  搜索索引同步收缩;侧边栏「有声书」勾选项改为锁定而非移除,避免关闭期间
  改侧边栏把已存值永久删掉

测试
- tests/test_modules.py:17 passed / 1 skipped,含逐路由反射(防漏挂 Mixin)
- test_main.py 失败集合与 develop 基线完全一致(45 个存量红)
留言、缺书登记、书单、友情链接、私藏书籍打包成一个勾选框,关闭后入口隐藏、
设置不渲染、接口拒绝,数据只隐藏不删除。评论与评分不并入(已有独立开关)。

后端
- MODULE_COMMUNITY 登记进模块表,ENABLE_MODULE_COMMUNITY 默认开启并入
  ADMIN_SETTING_KEYS / PERSISTABLE_KEYS
- 门禁:user.py 留言/缺书 2 条、booklist.py 13 个 handler、book.py 私藏 2 条
- 首页书单接口模块关闭时返回空列表而不是 module.disabled——首页按 err==ok 读
- sys 降级:allow.user_memos 强制 False、friends 不下发(顺带跳过 favicon 抓取)
- 首页书单卡由 get_homepage_config 运行期强制隐藏;设置页用 apply_modules=False
  取原样配置,避免关闭期间保存一次把「强制隐藏」固化进用户配置
- 私藏过滤逻辑不动:那是防泄漏别人私藏书的隐私语义,不是 UI 能力

前端
- module-gate 拦 /memos、/expected、/soledbooks、/booklists/*、/booklist/:id 与对应
  管理页
- 顶栏用户菜单/管理菜单、书单组、友链、留言入口与弹窗跟随模块开关
- 详情页:加入书单、私藏徽标与「设为私藏」菜单项
- 设置页:新增「社区互动」勾选;ENABLE_USER_MEMOS 字段级隐藏、site-friends 区块级
  隐藏、侧边栏勾选项 booklists/friends 改为锁定;首页卡片编辑器隐藏书单卡行但保留
  order,保存不丢配置
- 三语文案

测试
- tests/test_modules.py 扩到 27 passed / 1 skipped / 42 subtests passed:逐路由反射
  community 门禁、首页书单空返回、homepage 强制隐藏双口径、sys 降级
- test_homepage/test_booklist_service/test_admin_settings 36 passed;
  test_main 失败集合与 develop 基线一致(45 红)
- 书单 guest_read 访客授权随社区模块关闭一并失效:书单入口与接口都下线后,
  公开书单的 guest_read 仍会放行书籍/朗读接口,属于模块关闭后的暗门。
  只影响判定,不动书单数据,重开即恢复(补测试)
- 管理端导入的「设为私藏」随模块隐藏,ImportRun 对 sole=true 返回 module.disabled,
  避免模块关闭期间造出既看不了也取消不了的私藏书
- module-gate 补 /admin/user-memos/ 与 /admin/all-expected/ 尾斜杠变体
- ENABLE_BOOKLIST_COVER_SCROLL 加 community 字段级隐藏(只作用于书单卡,模块关闭后无消费方)
- 测试补强:设置页真实调用点断言 apply_modules=False、首页书单接口开启分支、
  默认值断言改为「键存在且等于模块表默认值」
- ImportRun 语义统一:私藏标记属于社区能力,模块关闭时丢弃 sole 而不是
  拒绝整次导入(导入是核心能力;旧缓存页/脚本带 sole=true 也不该连累导入)
- 补 sole 正反向测试:模块关+sole 丢弃、模块关+不设 sole 正常导入、
  模块开 sole 原样透传;测试对 do_import 打桩,不会真扫盘
- 导入页「设为私藏」的 v-if 上移到 v-col,模块关闭时不再留空列
- guest_read 修复补组合用例:模块关闭不影响分享链接与全局访客可读的优先级
- AdminSettings 调用点测试用 patch.dict 包住 CONF,避免全局配置被写脏
两个新模块沿用同一套机制:关闭后接口拒绝、入口隐藏、设置不渲染,数据只隐藏
不删除;老实例默认全开、行为零变化。

实体书(physical)
- MODULE_PHYSICAL 登记;门禁 printbooks / book/add / book/add/manual /
  exchange_type;BookEdit 在模块关闭时丢弃 book_type/book_count/location,
  避免老缓存页造出看不全也改不了的物理书
- sys 降级:allow.physical_books 强制 False(导航/OPDS/筛选/上传按钮统一失效)、
  physicals 计数不再扫全库;OPDS 的「书架位置」分类同步隐藏
- 前端:详情页互转与册数/位置、编辑页物理字段、管理端类型筛选与批量互转、
  首页/书卡/自定义卡片角标、缺书登记的实体书上传方式
- 封面图章不并入:它作用于电子书封面(见 2026-10-19 设计),保持独立开关

AI(ai)
- MODULE_AI 登记;门禁单书 aifill、管理端批量 aifill、AI 连通性测试、
  MyNexus 全部路由、MCP 全部路由;助手 WebSocket 在 open 阶段以 4004 关闭,
  服务启动时的助手初始化也跳过
- sys 降级:aiEnabled / mynexus_enabled 强制 False,详情页不再下发
  ai_companion_available / ai_reading_guide / mynexus_analysis
- AIFillInfoService 在服务层兜底:队列里的旧任务/脚本调用在模块关闭时直接
  返回失败,不再请求外部 AI
- 前端:详情页 AI 更新、MyNexus 解析/伴读/对话框、管理端批量 AI 更新、
  顶栏 AI 助手入口与对话框、设置页整个 AI 能力区块(含 MyNexus/MCP)

测试
- tests/test_modules.py 39 passed / 1 skipped / 60 subtests:四模块快照、物理与
  AI 逐路由反射、助手 WebSocket 4004、sys 降级(AI/MyNexus/物理)
- test_homepage / test_booklist_service / test_admin_settings / test_book_access_service
  全绿;test_main 45 红与 develop 基线一致
- flake8 改动文件零新增;eslint 0 error(1 个存量 warning)
- BookLocation(/api/book/:id/location)补实体书门禁:这是位置写入的独立路径,
  BookEdit 的字段剥离覆盖不到
- 管理端 CSV 批量添加实体书(/api/admin/batch_add/run|status)补实体书门禁:
  它同样会写 #book_type/#book_count/#location
- OPDS 位置分类从「只隐藏顶层目录」改为直接 URL 也 404(nav/category/group)
- 助手 WebSocket 在 on_message 复查模块:已建立的会话在模块关闭后不能再调用 AI
- AI 批量任务在批次边界与每次 AI 调用前复查模块,关闭时停止并把后台任务标记失败,
  避免继续产生外部调用与元数据写入
- sys 统计缓存键带上 audio/physical 模块状态:切换模块不再沿用 60 秒旧快照
- MCP edit_book 在实体书模块关闭时对 book_count/book_type 明确报错,
  避免助手收到「修改成功」的假象
- 补测试:位置写入、批量添加、助手已连接会话、OPDS 分类判定、MCP 物理字段
- OPDS opds_category 的 "I" 分支会用 which 后缀覆盖 category,
  category=tags + which=I<id>:#location 可绕过位置分类下线;覆盖后再查一次
- CSV 批量添加实体书:服务入口与每行循环复查实体书模块,关闭时停止写入;
  前端轮询收到 module.disabled 时复位 import/batch 任务态,不再卡在加载中
- MyNexus 后台状态轮询每轮复查 AI 模块,关闭后停止外呼与写解析状态
- AI 批量任务在 apply 已取回结果前复查模块,关闭后不再写元数据
- MCP edit_book 的物理字段报错补上 location
# Conflicts:
#	app/src/components/AppHeader.vue
@shiningsprk-arch
shiningsprk-arch marked this pull request as ready for review October 30, 2026 12:09
@shiningsprk-arch
shiningsprk-arch marked this pull request as draft October 30, 2026 12:10
关闭后用户管理、注册/激活与社交登录下线;登录、退出、个人资料与密码重置
保持核心可用。访客权限/下载配额/强制登录属于站点策略:设置项随模块隐藏,
运行行为保持已存值,不做静默改动。

后端
- MODULE_USERS 登记;门禁 AdminUsers、SignUp/UserNew/激活接口
- 社交登录 OAuth 三条路由包一层 404 门禁(open 时按模块判定,支持运行期切换)
- sys 降级:allow.register 强制 false、socials 下发空列表(登录页注册与社交按钮消失)

前端
- 管理菜单「用户管理」、/admin/users 与 /signup 路由守卫
- 设置页:用户与账号模块开关;users-reg / users-default / users-social 区块级隐藏;
  访客权限与强制登录字段级隐藏(分享链接仍归 M9,保持可见)
- 三语文案

测试
- test_modules.py 47 passed / 2 skipped / 80 subtests:用户管理/注册/激活逐路由反射、
  登录/重置等核心接口断言不门禁、社交路由与 prepare 404、register/socials 降级
- test_homepage / test_admin_settings / test_book_access_service / test_locale_i18n_keys 全绿
- test_main 45 红与 develop 基线一致;flake8 零新增;eslint 0 error
- module-gate 在 REQUIRE_LOGIN=true 的站点上对公开路径(/signup)也执行模块跳转:
  之前为了保留 next 直接放行,导致「必须登录 + 关闭用户模块」时注册页仍会渲染
- 测试拆分 user/admin 路由反射,避免无 calibre 环境把 AdminUsers 门禁一起跳过;
  核心路由补 UserInfo/WhoAmI/UserAppearance;users 相关 mock 改为按模块 id 判定,
  防止检查错模块也假绿
- zh-TW 文案统一为既有用词(社群網路登入 / 重設密碼)
@HorkyChen
HorkyChen force-pushed the develop branch 2 times, most recently from 58d5601 to 67753eb Compare October 31, 2026 00:38
@HorkyChen

HorkyChen commented Oct 31, 2026 •

Copy link
Copy Markdown
Collaborator

可以将社区互动定义为外部较多账号时才会使用到的功能。其它功能是 单用户及少量用户(家庭或者小范围)时会使用的功能。

  1. 用户与账号改为多用户支持 方便理解
  2. 去掉社区互动里的书单,增加共享阅读,涵盖:推荐、书单、书评和读书卡片。[依赖打开多用户支持]
  3. 社区互动里的私藏书籍需要去掉,单用户也会有私藏书籍的需求 (向其他人展示不会直接显示出来) [依赖多用户支持]
  4. 社区互动里增加一项:分享链接, 包括分享链接创建和系统管理里的分享管理
  5. 有声书可以去掉,作为一个基本功能保留。

这样需要用户按大模块选择的就是4项, 在安装配置时就可以进行勾选,默认启用多用户支持和共享阅读。大部分情况是单人和小范围熟人使用。

在系统设置以一个独立模块,很少会使用。如果也和现在的设置项一样,可能会被误解,我的建议是独立一个弹窗来设置,说明影响。

关于设置项的处理有两个方案:

  1. 模块选择作为一级开关,相关设置项为二级设置项
    大类下设置项的处理,例如关闭了社区互动,之前设置里相关功能选项是显示还是不显示,是否允许变更?如果设计成一个一级开关,相关功能项是二级开关,从用户角度就可能经常遇到设置不生效的问题。要做到一致,对应功能项就是要禁止修改。
  2. 模块选择作为一个设置集合,只在选择时一次性变更相关设置项
    模块设置时只对应变更相关设置项的配置,不添加新的设置项,功能上仍以各功能设置项判断。用户可以在设置上变更。
    无论哪个方案,尽量降低让用户产生歧义的概率就可以。这个做完,确实不用再单独实现系统模式了。

@shiningsprk-arch

shiningsprk-arch commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor Author

修订方案(待修改草案,2026-11-02)

已按审查意见把方案改为 4 个模块。当前分支代码仍是旧的 5 模块实现(audio / community / physical / ai / users),尚未按新方案重写;以下为修订方案,供审查确认后再改代码与测试。

1. 模块模型

模块 id / 配置键 覆盖能力 依赖
多用户支持 multiuser / ENABLE_MODULE_MULTIUSER 用户管理、注册/审批、邀请、新用户默认权限、社交登录;留言、缺书登记、友情链接;分享链接创建与系统管理 无
共享阅读 shared_reading / ENABLE_MODULE_SHARED_READING 推荐、书单、书评与评分、读书卡片 多用户支持
实体书 physical / ENABLE_MODULE_PHYSICAL 实体书登记、互转、册数/位置 无
AI ai / ENABLE_MODULE_AI AI 助手、AI 元数据、MyNexus、MCP 无

移出模块、恢复为核心能力:有声书;私藏书籍;登录、退出、个人资料、修改密码、密码重置、访问码登录;封面图章保持独立。

2. 设置语义

  • 模块关闭:把模块内的主开关写成安全默认值,然后隐藏对应设置;子选项、密钥、列表数据只隐藏不改。运行期模块闸门继续兜底,防止手工改 auto.py 绕过。
  • 模块开启:只恢复该模块的主功能开关(共享阅读的推荐/书评/共享笔记、实体书的实体书开关、AI 的 AI_ENABLED);访问控制(必须登录、访客权限、注册、分享链接)不自动恢复,避免重开模块把私人站点重新暴露。
  • 不删除数据:书籍、音频、用户、留言、书单、书评、私藏标记、实体书记录、分享记录、AI 配置全部保留。

3. 多用户支持关闭

关闭时写入:

设置 写入
REQUIRE_LOGIN True
ALLOW_GUEST_READ/DOWNLOAD/UPLOAD/PUSH False
ENABLE_SHARE_LINK False
ALLOW_REGISTER False
INVITE_MODE False(保留访问码文本)

行为与例外:

  • 所有访客一律要求登录;访客阅读/下载/上传/推送、分享链接、注册、社交登录失效。
  • 已有非管理员账号仍可登录,不删号、不停用;要严格单人需在关闭模块前先停用其他账号。
  • 仅用社交登录、没有密码的账号会因 OAuth 路由 404 无法登录;关闭前需设置密码。
  • 已注册未激活账号的激活完成接口建议保持核心可用,避免永久卡死。
  • 密码重置邮件模板、下载配额、阅读范围策略保持可见(仍作用于已有账号/核心密码重置)。
  • ENABLE_USER_MEMOS 不写死:由模块闸门和 sys.allow.user_memos=false 关闭,重开恢复原设置。

4. OPDS 匿名访问

新增 OPDS_ALLOW_ANONYMOUS(默认 False),解决个人用户关闭多用户后阅读设备无法登录的问题。

REQUIRE_LOGIN ENABLE_OPDS_AUTH OPDS_ALLOW_ANONYMOUS OPDS 结果
否 否 任意 匿名可访问
否 是 任意 401 Basic
是 是 任意 401 Basic
是 否 否 401 Basic
是 否 是 匿名可访问

ENABLE_OPDS_AUTH 优先级最高;OPDS_ALLOW_ANONYMOUS 默认关闭。

5. 共享阅读关闭

  • 写入 RECOMMEND_ENABLE=False、ENABLE_BOOK_REVIEW=False、ENABLE_SHARED_NOTES=False(共享笔记是否并入本模块待确认)。
  • 只隐藏不改:RECOMMEND_USE_CROWD、REVIEW_REQUIRES_APPROVAL、ENABLE_BOOK_RECOMMEND_TO_OTHERS、ENABLE_HOMEPAGE_BOOKLISTS、ENABLE_BOOKLIST_COVER_SCROLL、HOMEPAGE_CONFIG、SIDEBAR_ITEMS。
  • 书单/书评/推荐接口拒绝;首页回落随机/最近;公开书单的 guest_read 授权失效;数据保留。

6. 实体书 / AI

  • 实体书关闭:ENABLE_PHYSICAL_BOOKS=False;子选项与记录保留。
  • AI 关闭:AI_ENABLED=False、ENABLE_MYNEXUS=False;模型、API Key、MCP Token 等配置保留。

7. 设置页与安装页

  • 功能模块改为设置页顶部入口 + 独立弹窗;弹窗先预览「将写入 / 将恢复」的开关。
  • 安装页增加模块勾选:默认多用户支持 + 共享阅读;实体书、AI 默认不勾选;未勾选模块写入安全默认值。
  • 升级实例模块键缺失时默认全开,行为零变化。

8. 待确认

  • 共享笔记是否并入共享阅读(当前按并入处理)。
  • 已有非管理员账号保持可登录(当前按保持处理);严格单人需先停用账号。
  • 已有社交-only 账号在关闭多用户前需设置密码。

9. 下一步

按本方案重写代码与测试(模块表、依赖、安全默认值归一化、OPDS 匿名开关、设置弹窗、安装页),更新测试结果后再请求 review;在此之前保持 draft。

- 多用户支持/社区互动/有声书/实体书/AI 互不依赖,可单独启停
- 模块关闭写安全默认值并隐藏入口、接口与设置项,数据保留
- 书单不再随社区互动关闭:接口、首页卡片、访客可读、侧边栏选项全部保留
- 必须登录默认开启;多用户关闭时强制勾选+灰置;OPDS 匿名开关优先于认证项
- 设置页模块弹窗只保留 5 个勾选,安装页提供模块预选,默认不勾社区互动
- 新增 ENABLE_MODULE_SHARED_READING,涵盖推荐、书单、书评与读书卡片
- 社区互动保留留言、缺书登记、友情链接、分享链接与共享笔记
- 书单接口、书评、推荐与首页两张卡片改挂共享阅读闸门
- 配置功能模块弹窗补 6 项关闭后果注释,安装页新增共享阅读默认勾选
- /get/* 的 from=opds 旁路收窄到封面/缩略图,正文/进度/工具资源回落到 401
- 匿名 OPDS 下载校验 OPDS 服务开关与 sole 私藏书
- 补 /get/extract|progress|tool 不放行、私藏/服务关闭拒绝下载的测试
- 社区关闭时 /s/<token> 分享页回首页;清理过期注释与搜索别名
@shiningsprk-arch

Copy link
Copy Markdown
Contributor Author
image

@shiningsprk-arch

shiningsprk-arch commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor Author

PR 说明(最新版,6 模块口径)

本评论替代旧描述:旧版按 5 模块编写,且把书单放在「社区互动」;当前 head c0245c78 已调整为 6 个互不依赖的模块,书单归「共享阅读」。

背景

issue #123(系统模式:个人 / 标准 / 专业)的前置工作:把功能从字段级设置抽成模块级开关——一个模块一个勾选框。关闭后入口隐藏、设置项不渲染、接口拒绝;只隐藏,不删除任何已有数据。

六个模块与归属

模块 配置键 覆盖范围
多用户支持 ENABLE_MODULE_MULTIUSER 用户管理、注册/审批、邀请、新用户默认权限、社交登录、访客权限、必须登录
社区互动 ENABLE_MODULE_COMMUNITY 站内留言、缺书登记
共享阅读 ENABLE_MODULE_SHARED_READING 推荐、书单、书评、读书卡片、友情链接、分享链接、共享笔记
有声书 ENABLE_MODULE_AUDIO 有声书列表、播放、转有声书、字幕、播客订阅、语音合成
实体书 ENABLE_MODULE_PHYSICAL 实体书登记、册数/书架位置、电子书↔实体书互转
AI ENABLE_MODULE_AI AI 助手、AI 更新元数据、MyNexus、MCP

模块之间没有依赖(MODULE_DEPENDENCIES = {}),可以任意单独启停。

开关语义

  • 六个模块默认全部开启;老实例升级行为不变。
  • 关闭写安全默认值:
    • 多用户:REQUIRE_LOGIN=True,访客阅读/下载/上传/推送、注册、邀请全部关闭;
    • 社区:ENABLE_USER_MEMOS=False;
    • 共享阅读:RECOMMEND_ENABLE、ENABLE_BOOK_REVIEW、ENABLE_SHARE_LINK、ENABLE_SHARED_NOTES=False;
    • 实体书:ENABLE_PHYSICAL_BOOKS=False;
    • AI:AI_ENABLED、ENABLE_MYNEXUS=False;
    • 有声书没有单一主开关,由模块闸门负责入口与接口。
  • 重新开启只恢复主功能开关,不恢复多用户的访问控制:访客权限、注册、邀请不会因为重开模块自动放开;共享阅读重开会恢复推荐/书评/分享链接/共享笔记。唯一例外是多用户开启时把 REQUIRE_LOGIN 钉回 True(更安全的一侧),管理员仍可手动取消。
  • 模块关闭期间隐藏的设置项不会丢值:前端保存时按存储值补回(含 SIDEBAR_ITEMS 锁定项),重开原样恢复。
  • REQUIRE_LOGIN 默认值改为 True:新装或配置里缺该键时默认要求登录;已经显式保存过该键的实例保持原值。

安装与设置 UI

  • 安装页提供模块选择,默认勾选:多用户支持、共享阅读、有声书、实体书、AI;社区互动默认不勾选。
  • 「功能模块」卡片只放在「系统与性能」;弹窗只有 6 个勾选项,每项下方一句关闭后果说明。
  • 设置页按模块隐藏对应区块/字段(支持区块级与字段级),侧边栏「显示项目」里对应选项也隐藏;保存仍保留原值。
  • 必读的显示口径:必须登录生效时(REQUIRE_LOGIN=True 或多用户关闭),REQUIRE_LOGIN 与 ENABLE_OPDS_AUTH 显示为勾选+灰置;OPDS_ALLOW_ANONYMOUS 始终可勾选。

OPDS 与登录

  • OPDS_ALLOW_ANONYMOUS 是协议级显式开关,优先于「必须登录」和 ENABLE_OPDS_AUTH:勾选后 OPDS 目录、封面/缩略图、/api/book/<id>.<fmt>?from=opds 下载均可匿名使用。
  • 匿名放行只覆盖 OPDS 协议需要的路径,不是整站 /get/* 旁路:/get/extract(网页阅读器正文)、/get/progress(转换日志)、/get/tool(工具静态资源)仍按登录闸或各自授权处理。
  • 匿名 OPDS 下载额外校验:ENABLE_OPDS_SERVICE 关闭时旧直链失效;sole 私藏书不在 OPDS 目录里,也不允许匿名按 id 猜测下载。
  • ENABLE_OPDS_AUTH 与匿名开关的展示/实际优先级已统一:匿名勾选后即使认证项显示勾选,OPDS 仍可匿名。

后端实现

  • webserver/base/modules.py:模块表、MODULE_OFF_SWITCHES / MODULE_ON_SWITCHES、apply_module_switch_effects()、require_login_effective()、opds_anonymous_allowed()、ModuleGateMixin。
  • 模块闸门覆盖:书单全部 handler、书评/书评管理、推荐路径、分享链接、友情链接、共享笔记、留言、缺书、音频/播客/TTS、实体书、AI/MyNexus/MCP、用户注册/社交登录、管理端对应用户与评论页面。
  • 首页 CARD_MODULES:index.socialRecommendation 与 index.booklistRecommendation 归共享阅读;书单/推荐卡片运行期强制隐藏,但设置页读取原配置,不会把隐藏状态写死。
  • 公开书单的访客阅读授权随共享阅读关闭一并失效;书单数据不清。
  • 新增模块键全部进入 ADMIN_SETTING_KEYS / PERSISTABLE_KEYS;旧缓存页或脚本不带键时按当前值回填,避免 dumpfile() 整份覆盖时抹掉开关。

验证

  • test_modules:44 passed / 1 skipped / 96 subtests passed(逐路由反射、开关效果、首页卡片、OPDS、AI/实体书归属等)。
  • test_require_login:51 passed(含匿名 OPDS 只放行封面/下载、/get/extract|progress|tool 仍 401、必须登录与多用户交互)。
  • test_book_access_service:23 passed(含共享阅读关闭收回书单 guest_read 与分享链接、社区关闭不影响分享/书单)。
  • test_homepage 12 / test_booklist_service 11 / test_admin_settings 14 passed。
  • ESLint 0 error(1 条存量 warning);build-spa 通过;flake8 改动文件无新增告警。
  • 本地 test_book_review 有 12 条失败,是预览库 readers 表缺 read_limit 的旧 schema 环境问题,与本次改动无关。

兼容与数据安全

  • 不新增数据库表/字段;模块开关只写入 auto.py。
  • 不删除音频文件、书单、留言、缺书记录、实体书记录、AI 解析结果、用户账号;重新勾选即恢复入口。
  • 旧草稿期的 ENABLE_MODULE_USERS 键未做迁移:它只存在于本 PR 的中间提交,从未发布,不影响正式实例。

已知限制

  • 设置页「未保存计数」仍会算上被隐藏字段,保存不会丢值,只是「跳到下一处修改」跳不到它们。
  • 嵌套 groups 字段暂不支持模块过滤(当前六个模块没有嵌套字段)。
  • MCP 工具目录仍会列出书单类工具,实际调用由已门禁的 HTTP 接口拒绝。
  • 管理端只读统计与书籍列表筛选仍可读到实体书数据(按「数据视图不是能力入口」保留)。

验收步骤

  1. 系统设置 → 系统与性能 → 功能模块,取消勾选任一模块并保存。
  2. 对应侧边栏、详情页按钮、导入页、工具箱、首页卡片、管理菜单入口应当同时消失。
  3. 直接访问对应页面应回首页;直接调接口应返回 module.disabled(OPDS 相关路径按上文规则)。
  4. 重新勾选并保存,入口与已有数据应原样恢复。

- 社区互动只保留站内留言与缺书登记
- 共享阅读收编友情链接、分享链接、共享笔记,前后端闸门与设置项同步
- 重开共享阅读恢复推荐/书评/分享链接/共享笔记
- 设置页弹窗注释与三语文案同步更新
@shiningsprk-arch
shiningsprk-arch marked this pull request as ready for review November 3, 2026 11:26
注册顺序即卡片默认顺序:EPUB → 文本 → PDF/音频 → 书库整理。
古书下载器/书栈接收器走下载路径,暂不调整,保持在末尾。
@HorkyChen
HorkyChen merged commit 4f72e23 into PoxenStudio:develop Nov 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants