Repository navigation
Conversation
一个模块 = 一个勾选框:关掉后入口消失、设置不渲染、接口拒绝,数据只隐藏不删除。 后端 - 新增 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
关闭后用户管理、注册/激活与社交登录下线;登录、退出、个人资料与密码重置 保持核心可用。访客权限/下载配额/强制登录属于站点策略:设置项随模块隐藏, 运行行为保持已存值,不做静默改动。 后端 - 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 文案统一为既有用词(社群網路登入 / 重設密碼)
58d5601 to
67753eb
Compare
|
可以将社区互动定义为外部较多账号时才会使用到的功能。其它功能是 单用户及少量用户(家庭或者小范围)时会使用的功能。
这样需要用户按大模块选择的就是4项, 在安装配置时就可以进行勾选,默认启用多用户支持和共享阅读。大部分情况是单人和小范围熟人使用。 在系统设置以一个独立模块,很少会使用。如果也和现在的设置项一样,可能会被误解,我的建议是独立一个弹窗来设置,说明影响。 关于设置项的处理有两个方案:
|
修订方案(待修改草案,2026-11-02)已按审查意见把方案改为 4 个模块。当前分支代码仍是旧的 5 模块实现(audio / community / physical / ai / users),尚未按新方案重写;以下为修订方案,供审查确认后再改代码与测试。 1. 模块模型
移出模块、恢复为核心能力:有声书;私藏书籍;登录、退出、个人资料、修改密码、密码重置、访问码登录;封面图章保持独立。 2. 设置语义
3. 多用户支持关闭关闭时写入:
行为与例外:
4. OPDS 匿名访问新增
5. 共享阅读关闭
6. 实体书 / AI
7. 设置页与安装页
8. 待确认
9. 下一步按本方案重写代码与测试(模块表、依赖、安全默认值归一化、OPDS 匿名开关、设置弹窗、安装页),更新测试结果后再请求 review;在此之前保持 draft。 |
db43196 to
f48040a
Compare
- 多用户支持/社区互动/有声书/实体书/AI 互不依赖,可单独启停 - 模块关闭写安全默认值并隐藏入口、接口与设置项,数据保留 - 书单不再随社区互动关闭:接口、首页卡片、访客可读、侧边栏选项全部保留 - 必须登录默认开启;多用户关闭时强制勾选+灰置;OPDS 匿名开关优先于认证项 - 设置页模块弹窗只保留 5 个勾选,安装页提供模块预选,默认不勾社区互动
- 新增 ENABLE_MODULE_SHARED_READING,涵盖推荐、书单、书评与读书卡片 - 社区互动保留留言、缺书登记、友情链接、分享链接与共享笔记 - 书单接口、书评、推荐与首页两张卡片改挂共享阅读闸门 - 配置功能模块弹窗补 6 项关闭后果注释,安装页新增共享阅读默认勾选
- /get/* 的 from=opds 旁路收窄到封面/缩略图,正文/进度/工具资源回落到 401 - 匿名 OPDS 下载校验 OPDS 服务开关与 sole 私藏书 - 补 /get/extract|progress|tool 不放行、私藏/服务关闭拒绝下载的测试 - 社区关闭时 /s/<token> 分享页回首页;清理过期注释与搜索别名
PR 说明(最新版,6 模块口径)
背景issue #123(系统模式:个人 / 标准 / 专业)的前置工作:把功能从字段级设置抽成模块级开关——一个模块一个勾选框。关闭后入口隐藏、设置项不渲染、接口拒绝;只隐藏,不删除任何已有数据。 六个模块与归属
模块之间没有依赖( 开关语义
安装与设置 UI
OPDS 与登录
后端实现
验证
兼容与数据安全
已知限制
验收步骤
|
- 社区互动只保留站内留言与缺书登记 - 共享阅读收编友情链接、分享链接、共享笔记,前后端闸门与设置项同步 - 重开共享阅读恢复推荐/书评/分享链接/共享笔记 - 设置页弹窗注释与三语文案同步更新
注册顺序即卡片默认顺序:EPUB → 文本 → PDF/音频 → 书库整理。 古书下载器/书栈接收器走下载路径,暂不调整,保持在末尾。

背景
这是 issue #123(系统模式:个人 / 标准 / 专业)的前置工作:先把「功能」从字段级设置里抽出来,做成模块级开关——一个模块 = 一个勾选框。关掉后入口消失、设置不渲染、接口拒绝,而不是散在几十个设置项里逐个关。
本 PR 落地基础设施 + 有声书、社区互动、实体书、AI、用户与账号 五个模块,把机制跑通;分享与推送、阅读增强、协议服务、工具箱、运维等按同一套机制后续单独提。
机制
三层,不做几十个平级开关:
一个模块关闭时,三件事一起做:
ModuleGateMixin在prepare里提前拦截 —— JSON 接口返回{"err": "module.disabled"},文件流 / RSS 订阅源返回 404(避免把 JSON 或 HTML 错误页喂给播放器、播客客户端)。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.py9 个 handler、podcast.py基类(10 条路由)、音频导入 2 条、tts.py1 条、Mimo-TTS 里会写音频目录的 4 条has_audio、sys.audiobooks不再扫音频目录、详情接口audios返回空结构AdminSettings.post按模块表setdefault回填模块键(args会被dumpfile()整份写回,少键等于抹掉开关)前端
module-gate拦/audiobooks、/audio/*、/toolbox/mimo_tts模块二:社区互动(
community)五个子能力打包成一个勾选框:站内留言、缺书登记、书单、友情链接、私藏书籍。评论与评分不并入(已有独立开关
ENABLE_BOOK_REVIEW)。后端
user.py留言 / 缺书 2 条、booklist.py13 个 handler、book.py私藏 2 条/api/booklists/homepage例外:模块关闭返回空列表而不是module.disabled(首页按err=="ok"读)sys降级:allow.user_memos强制 False、friends不下发(顺带跳过 favicon 抓取)apply_modules=False取原样配置,避免关闭期间保存一次把「强制隐藏」固化进HOMEPAGE_CONFIGguest_read访客授权随模块关闭一并失效(否则会留下「书单页面没了、访客照样能读」的暗门),只影响判定、不动数据sole标记但不阻断导入本身前端
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计数不再扫全库;统计缓存键带上模块状态,重开不会读到旧快照nav/category/categorygroup直连 URL 一律 404(含which后缀覆盖的绕过路径)前端
/printbooks路由守卫;导航入口随allow.physical_books隐藏模块四:AI(
ai)覆盖 AI 助手、AI 更新元数据(单书 + 批量)、MyNexus、MCP。
后端
/api/book/:id/aifill、/api/admin/book/aifill、/api/admin/ai/test、MyNexus 全部路由、MCPstream/healthopen与每条on_message都复查模块,关闭后新连接与旧会话都返回 4004;服务启动时的助手初始化也跳过sys降级:aiEnabled/mynexus_enabled强制 False;详情页不再下发ai_companion_available/ai_reading_guide/mynexus_analysisAIFillInfoService在服务层兜底:模块关闭时拒绝新任务;运行中的批量任务在批次边界、每次 AI 调用前、apply 前复查,关闭后停止并把后台任务标记失败,不再写元数据edit_book在实体书模块关闭时对book_count/book_type/location明确报错,避免助手收到「修改成功」的假象前端
模块五:用户与账号(
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路由守卫REQUIRE_LOGIN=true时/signup被公开路径规则放过的问题:module-gate 现在对公开路径也执行模块跳转附带改动:工具箱默认顺序整理
工具箱卡片的默认展示顺序改为按处理流程分组,前端「置顶工具」优先级不变:
实现仅调整
webserver/toolbox/toolset.py中ToolSet.register()的注册顺序(注册顺序即默认展示顺序),不改工具功能、路由和置顶逻辑。已在 WSL 环境实际执行collect_tools()核对顺序,py_compile与 flake8 通过。兼容与数据安全
auto.py里没有该键时按开启处理测试
tests/test_modules.py:48 passed / 2 skipped / 83 subtests passedmodule_id正确;addstamp明确断言保持独立)apply_module_visibility双口径;AdminSettings.get真实调用点断言apply_modules=FalseImportRun的sole丢弃 / 透传三条路径;助手 WebSocket 新连接与旧会话的 4004;OPDS 位置分类判定;MCP 物理字段报错;sys降级(AI/MyNexus/物理/注册/社交)tests/test_book_access_service.py19 passed(含「模块关闭收回书单 guest_read、但分享链接与全局访客可读不受影响」)test_homepage12 /test_booklist_service11 /test_admin_settings13 passedtest_main.py失败集合与develop基线逐条一致(45 个存量红)验收步骤
/audiobooks、/audio/1、/memos、/booklists/publics、/soledbooks、/printbooks、/signup、/admin/users应被弹回首页;直接调对应 API 应返回module.disabled(助手 WebSocket 返回 4004,OPDS 位置分类与社交 OAuth 返回 404)已知限制(后续处理)
groups里的字段暂不支持模块过滤(当前五个模块都没有嵌套字段)sole=true会被静默丢弃(导入成功、书籍公开),不额外提示LibraryStats的物理计数)与书籍列表type筛选仍可读到实体书数据——按「数据视图不是能力入口」保留,写接口全部已门禁module.disablededit_book的 schema 未声明location;模块关闭时对book_count/book_type/location明确报错