本文由 FlintHub 开发文档归档发布。原始文档日期见标题前缀,来源:合并篇(见正文内 merged_from 列表)。
FlintHub 增量包改动总览(update/1 ~ update/99 · 2026-08-07 ~ 2026-10-02)
本篇由 38 份源文件汇总去重而成:35 份包说明(32 份编号增量包文件——29 个编号中 update/3、/12、/13 在 8 月 SoSite 期与 9 月 FlintHub 期各有一份——+ 08-07 基础整改包 + 1.1.0-beta1/beta2 两个全量包)+ 3 份附录/汇总文档(update/9 附带的 Hooks 目录、update/26 附带的社区更新公告、
WWW/docs/升级说明.md)。按发布日期排列,保留每包改了什么,去掉逐包的安装/备份步骤重复说明。编号约定:
update/N的目录号 N =version.php版本末位(如update/22→ V1.0.22)。注意 update/3、/12、/13 在 8 月 SoSite 期与 9 月 FlintHub 期各有一个同名包,内容不同,表中以"〔SoSite 期〕/版本号"区分。
汇总口径
- 每包只提取"改了什么",逐包的备份步骤、覆盖顺序、回归清单、MD5/SHA256 校验表均不收录(需要时查各源文件)。
- "类型"与"关键改动"为读原文后归纳,不照抄标题;同一包内多个并列改动用
<br>分隔,便于表格阅读。 - 版本号以各包
version.php为准;站点根版本滞后于包内目标版本的情况(update/33~40 集中出现)在对应行注明。
一、总览
| 时间跨度 | 阶段 / 版本区间 | 包数 | 主要方向 |
|---|---|---|---|
| 2026-08-07 ~ 08-11 | SoSite 阶段(整改包 + 8 个编号包,无 version.php) | 9 | 代码整改与孤儿文件清理、安全加固(XSS/RCE/路径穿越/越权)、附件权限体系、通知中心与勋章中心两个首批大插件 |
| 2026-09-15 ~ 09-20 | V1.0.2 ~ V1.0.25 | 11 | 统计增量与查询次数优化、PHP 8.4/8.5 兼容、可视化编辑器增强、史上最大安全修复 update/20、附件体系改造、SEO 插件增强 |
| 2026-09-21 ~ 09-30 | V1.0.26 ~ V1.0.46 | 12 | 用户报障修复、楼中楼通知与深链、插件审计整改(P0+P1+P2)、云存储插件、六插件完整包、右栏改造、登录回跳五层贯通、SplitDB 核查整改、清理性能优化 |
| 2026-10-01 ~ 10-02 | V1.1.0(beta1/beta2)+ V1.0.99 | 3 | 内置在线升级系统落地(RSA 签名清单 + 备份回滚),核心与插件分开发布,增量包转入留档 |
update/1 说明:无
version.php与改动说明,包内 20 个文件(app/Helpers/Plugin.php、install.php、nginx-baota.conf、17 个plugins/*/plugin.json)与flinthub1.0.0.zip逐字节一致,视为 V1.0.0 发行基线,本身不构成一次升级(据WWW/docs/升级说明.md)。8 月批次署名 SoSite(V5.0.0 阶段,以"覆盖部署说明"形式交付);9 月起改署名 FlintHub 并启用
V1.0.x版本号体系(以"改动说明"形式交付)。
分阶段读法
- 8 月(9 个包):以"整改 + 加固"为主——先把孤儿文件、内联样式、模板悬空引用清理干净,再逐项补安全洞(XSS、RCE、路径穿越、越权上传),同时把附件权限体系和通知中心、勋章中心两个基础设施插件立起来。这一阶段的包普遍不含
version.php,交付物以"文件清单 + 回归验证清单"为主。 - 9 月上中旬(11 个包):转入 FlintHub
V1.0.x编号体系,重心转向性能与兼容性——统计改增量、查询次数收敛、PHP 8.4/8.5 兼容、编辑器增强;update/20是这一阶段的顶点,一次交付 A/B/C/D 四批共 96 个文件的集中修复。 - 9 月下旬(12 个包):从"修核心"扩展到"修插件 + 修数据层"——插件审计整改(P0+P1+P2 全量)、缤纷云存储、六插件完整包、楼中楼通知与深链、右栏签到解耦、登录回跳五层贯通、SplitDB 核查整改、孤儿附件清理性能优化,绝大多数包都带
version.php并明确"覆盖后必须清页面缓存"的部署口径。 - 10 月初(3 个包):1.1.0 落地内置在线升级系统,核心与插件分开发布;
update/99刻意把版本号停在 V1.0.99 以实测升级链路,两个 beta 全量包则给出"只含核心"的纯净包形态。
贯穿全程的部署共性(各包重复出现,此处统一说明)
config.php是安装生成物、含站点本地参数,从 update/20 起一律不进包,需按各包说明手工合并新增段落。- 改了 CSS/JS 的包依赖两级破缓存:
TemplateCompiler::asset()的filemtime()?v=+sw.js缓存名版本派生;只含 PHP 的包则连sw.js都不含。 - 改了导航钩子、CSS 或
plugins_cache.json的包,部署后普遍要求后台「维护 → 清理缓存」——data/runtime/pages/*.html是永久缓存、不按 TTL 过期。 - 涉及插件钩子/插件列表变化的包,
plugins/plugins_cache.json必须一起覆盖,否则新钩子不分发、后台入口不出现。 - 增量包表达不了"删除文件",需要删除的包(update/20 的幽灵路由、update/28 的两个文件、update/39 的一个钩子文件等)都在说明里给出手工步骤。
二、改动总表
按发布日期排序;"关键改动"为读原文后归纳,保留模块名/方法名。来源路径相对
D:\phpstudy_pro\。
| 包号 | 日期 | 类型 | 关键改动 | 来源文件 |
|---|---|---|---|---|
| 整改更新包(8月12日备份根目录) | 08-07 | 代码整改 | 删除 9 个孤儿/死文件(app/Views/admin/settings.php 孤儿视图、schema_mysql/sqlite.sql 遗留建库脚本、blog/forum/pages/layout.css 零引用死文件)<br>新增通用 404 视图 app/Views/errors/404.php,修复 user_profile 悬空引用<br>前后台 16 个视图内联样式收敛为 mn-* / admin.css 工具类,移除 forum.css / pages.css / layout.css 引用<br>Helpers / Models 共 13 个零调用方法加 @deprecated 标注(纯注释) | 8月12日的备份/update/README-部署说明.md |
| update/3〔SoSite 期〕 | 08-08 | 安全修复 | Segmenter 分词过滤单字 token,防单字检索拖垮数据库<br>Thread.php / Blog.php 的 batchDelete 改动态占位符;附件删除前文件名白名单校验 + 孤儿清理跳过符号链接(防路径穿越)<br>TemplateCompiler 的 {if} 表达式禁止函数调用(防模板 RCE),保留 isset/empty 白名单<br>Permission 最小权限:权限体系已启用但用户组未配置时不再默认可见全站版块<br>editor.js 粘贴内容立即净化后插入,消除 200ms 恶意 HTML 渲染窗口 | 8月12日的备份/update/3/README-部署说明.md |
| update/5 | 08-09 | 功能 | 前端库改名:alpine.min.js→sostate.js、htmx.min.js→soajax.js,主布局与安装页引用同步<br>后台基本信息新增"启用 Gzip 压缩输出"开关(enable_gzip,默认关闭)<br>app/init.php 在 enable_gzip=1 时 ob_start('ob_gzhandler'),按 Accept-Encoding 协商,只压文本类不影响二进制 | 8月12日的备份/update/5/README-部署说明.md |
| update/7 | 08-09 | 功能 | 后台"功能开关"新增"开启邮箱验证"(email_verify_enabled,默认开启)<br>AuthController 注册/登录按开关走验证链路,关闭时注册即生效、未验证可登录<br>用户编辑页新增邮箱验证状态徽标 + "标记为已验证"手动通过(User::markEmailVerified()) | 8月12日的备份/update/7/README-部署说明.md |
| update/9 | 08-09 | 功能/权限 | category_permissions 新增 can_attach 列,后台版块权限矩阵新增"附件"列,逐用户组控制上传<br>新增 GET /attachment/{id} 下载入口,权限跟随版块 can_view,替代原 uploads 静态直链<br>下载前触发 attachment_download_before 钩子,预留积分/付费下载扩展<br>附带 sosite-plugin-dev/references/02-hooks-catalog.md 全量钩子目录(随包登记新钩子与 trigger 点) | 8月12日的备份/update/9/README-部署说明.md;同包 02-hooks-catalog.md |
| update/11 | 08-11 | 插件 | 新增通知中心插件 plugins/notifications:聚合回复 / 点赞 / 积分变动 / 注册欢迎 / 审核结果五类,PC 顶栏 + 移动端双入口、未读徽标、进页自动已读<br>核心新增 vote_after / points_award_after / points_deduct_after 三个钩子<br>content_review 的 approve()/reject() 新增 review_result_after 广播,与自有通知表双写双保险<br>新表 notifications 由 Plugin::activate() 幂等创建 | 8月12日的备份/update/11/README-部署说明.md |
| update/12〔SoSite 期〕 | 08-11 | 体验优化 | 左栏 nav_plugin_links 钩子调用传入 __nav_uri,插件页面入口高亮 mn-cat-active<br>覆盖签到 / 骰子 / 图标 / 猜题 / 红包 5 个入口,路由前缀匹配天然覆盖子路由<br>移动端不传参天然跳过,行为不变 | 8月12日的备份/update/12/README-部署说明.md |
| update/13〔SoSite 期〕 | 08-11 | 体验优化 | 每日签到 / 积分红包 / 个人主页去掉 600~720px 限宽,占满 PC 中栏<br>modern.css 的 html 补 overflow-y:auto; scrollbar-gutter:stable,长短页切换宽度不跳动<br>详情页右栏发帖人信息栏按钮文案"个人中心"→"用户主页" | 8月12日的备份/update/13/README-部署说明.md |
| update/14 | 08-11 | 插件 | 新增勋章中心插件 plugins/medal:后台定义勋章、手动颁发/回收、3 种自动规则(发帖≥N / 回帖≥N / 注册满 N 天 / 积分≥N)<br>用户可佩戴 1-3 枚,展示于帖子楼层 / 右栏 / 个人主页,controller_view_before 批量注入避免 N+1<br>新表 medals / user_medals / user_medal_wears 激活时幂等创建 | 8月12日的备份/update/14/README-部署说明.md |
| update/2 | 09-15 | 性能/机制 | Post / Thread 的 insert() 同步分支由 runtimeBuild() 改 runtimeIncr() 增量累加;Settings::runtimeIncr() 首次调用时惰性注册 flushCounters(防 CLI/cron 入口增量丢失)<br>reply_index 补 status 索引:新装内置 + marker 门控的存量站自动补丁<br>cli/worker.php / cron_trigger.php stats 合并节流 + 每日计数器校准兜底<br>部署红线:queue_mode 保持 cron,切勿改为 sync | WWW/update/2/改动说明.md |
| update/3(V1.0.3) | 09-15 | 兼容性 | PHP 8.4 起 session_set_save_handler() 传独立回调弃用:6 个闭包迁入新增 app/Core/SessionHandler.php(实现 SessionHandlerInterface)<br>删除已废弃的 curl_close() / imagedestroy() / finfo_close() 调用共 11 处,ViewCounter 补可空类型、CoverTemplate 补多字节前置判断<br>刻意不实现 SessionUpdateTimestampHandlerInterface,保持会话固定攻击防护判定路径不变;TTL 1800 秒与原实现一致<br>共 7 个文件、均为"删一个调用/补一个字符"级;已在 PHP 8.4.25 / 8.5.10 真机验证,向下兼容 8.0+ | WWW/update/3/改动说明.md |
| update/10 | 09-17 | 功能 | 编辑器快捷键体系:Ctrl+B/I/U/K、Ctrl+Shift+7/8/9、Ctrl+Enter 提交、Esc 退全屏;中文输入法组合期(isComposing/229)放行<br>新增代码块命令(<pre><code class="language-*">)+ pre>code 复制按钮(clipboard.writeText 优先、execCommand 回退)+ 快捷键帮助面板<br>删除线双向互通:htmlToMarkdown 注册自定义 ~~ 规则,MD_HIDDEN_CMDS 移除 strikeThrough<br>新增 _t() i18n 英文回退;修复复制按钮泄漏;sw.js CACHE_VERSION v1.0.1→v1.0.2 | WWW/update/10/改动说明.md |
| update/12(V1.0.12) | 09-17 | 功能/插件机制 | 新增通用侧栏钩子 sidebar_above_friend_links(PC right_sidebar.php 与移动端 mobile_sidebar.php 双端触发,友链始终垫底)<br>Plugin.php 新增 sort_order 稳定排序(sortPlugins())+ 插件编辑页"排序权重"在线编辑<br>修复"禁用友链插件后前台仍显示空面板":显隐判断加 Plugin::isActivated('friend_links')<br>申请友链入口图标 / 配色 / 留白优化(FA 系统图标 + 文字加深) | WWW/update/12/改动说明.md |
| update/13(V1.0.13) | 09-17 | 缺陷修复 | 修复网页粘贴带入的内联 color 落入 <pre> 代码块的"深底深字"不可读(实测 #1e1e2e 底 + #334155 字)<br>Content::sanitizeCssStyle() 增加代码块模式、新增 inCodeBlock() 判定,代码块内剥离 color / background-color<br>改在渲染期净化层执行(ThreadController → Content::formatPostContent()),存量帖立即生效、无需重新编辑、不改已存内容 | WWW/update/13/改动说明.md |
| update/18 | 09-17 | 性能优化 | 新增请求级缓存 app/Helpers/UserPrefs.php(一次查询取 theme / night_theme_auto / hide_right_sidebar / home_mode 等 6 字段);Theme 三处 + ThemeController::readPref() 改读它<br>Settings::reconcileWithDb() 复用已加载全量 KV + 60s 对账节流;新增 getUnreadBadgeCounts() 一条 UNION ALL 合并通知+私信未读数<br>Thread::decorate() 同请求同 uid/cid 只查一次;Points::getLevelConfig() 改一次性载入整表(文件缓存 + 1 小时 TTL)<br>优化前实测(登录态)首页 24 / 论坛 23 / 详情 29 / 主题设置 21 次查询,本包逐项收敛 | WWW/update/18/改动说明.md |
| update/20 | 09-18 | 安全修复+机制修复(四批,体量最大) | A 批 8 项安全止血:存储型 XSS(净化器删 raw-text 元素)、分页反射型 XSS、附件上传越权、后台封禁无效、$_GET 数组参数 500、APCu 日志刷屏、密钥治理(代码侧)、幽灵路由<br>B 批 7 组机制修复:计数器漂移三因同修、跨库写路径原子性(ExternStorage/Merger 锁与缓存)、后台审计补全、插件设置写入权限下沉、RateLimiter 原子化、权限默认拒绝、改密吊销记住登录、CSRF 时效等<br>C 批 9 项契约断裂专项:权限矩阵回跳丢版块、插件分类静默丢弃、自动扩容阈值被改写、博客封面无法移除、搜索用户标签空白、搜索高亮双重转义、分页丢筛选参数、私信收发件人恒为空、重置密码页误收起表单<br>D 批 D1~D9 批量硬化:模板表达式校验绕过封堵、BASE_PATH 家族收敛、script/CSS 上下文口径、服务端长度校验、LIKE ESCAPE 修复搜索漏检、通知聚合计数、sw.js 缓存名改由站点版本号派生<br>共 96 个代码文件(含根目录 version.php、sw.js、install.php),约 17 万字 | WWW/update/20/改动说明.md |
| update/22 | 09-20 | 功能改造 | 附件体系改造:Upload 单一收敛 API(url()/path()/thumbUrl()/formatSize()),全类型按月分目录 YYYY-MM/,缩略图双档(thumb_ 400×300 / md_ 800×600)<br>新增 AttachmentController:版块权限校验 + ?s=thumb 直出 + 分级 Cache-Control + download_count 原子自增(attachments 新列自动补齐)<br>列表摘要图改读缩略图 + 摘要图网格规范化;正文图渲染期分组;新增 lightbox.js 灯箱(全页连读 + 预载 + 键盘/滑动 + 缩放)<br>C 线:后台回收站只读预览 + 彻底删除补审计日志(内容随后独立成 update/23)<br>附件卡 / 灯箱两个公共组件 + cli/migrate_uploads_by_month.php 迁移工具(本次不跑) | WWW/update/22/改动说明.md |
| update/23 | 09-20 | 功能 | Thread 新增 getDeletedById($id):镜像 getById() 但只接受 status=STATUS_DELETED 索引行并带出 deleted_at;唯一调用点在管理员校验之后,越权面收敛为 0<br>/thread/{id}?preview_deleted=1 管理员只读预览:主帖 + 全部回复 + 附件,跳过浏览量计数与惰性归档、强制 canReply=false、隐藏一切写入口<br>回收站列表标题链接改带 preview_deleted=1;getById() / show() 既有行为一字未改 | WWW/update/23/改动说明.md |
| update/24 | 09-20 | 功能 | 门户首页(site_mode=portal)新增第三种首页模式 cms(杂志式门户):头条大图 + 热读榜 + 图文网格 + 博客封面卡 + 版块栏目 + 标签云<br>Theme::getHomeMode() 站点默认 / 用户偏好 / 注释共 3 处白名单加 cms;HomeController 视图映射 home/index_cms + 游客前置缓存键 home_1_m<br>数据全部取自 HomeController::index() 已聚合变量,不新增任何查询;portal / community 既有行为一字未改 | WWW/update/24/改动说明.md |
| update/25 | 09-20 | 插件增强 | SEO 插件 A 档:noindex 指令、canonical 修正、Open Graph 基础标签与首图、description 摘要质量、sitemap 转义与 lastmod<br>B 档批 1:JSON-LD 结构化数据(DiscussionForumPosting / Article / WebSite);批 2:Twitter Card、BreadcrumbList 面包屑、作者权威信号、文章元数据 meta<br>批 3:llms.txt(AI 爬虫内容索引,文件缓存 TTL 60min)+ data/ 目录防护文件 + robots.txt 注释声明<br>插件版本 1.2.0→1.5.0;controller_view_before 的 stash() 暂存已加载行,省掉一次重查询 | WWW/update/25/改动说明.md |
| update/26 | 09-21 | 缺陷修复(用户报障) | 退出登录 403:Csrf::verify() 校验通过后滑动续期 csrf_time;AuthController::logout() 由 verifyOrDie() 改 verify() + 审计 + 降级放行<br>编辑器粘贴后光标消失:insertAtCursor() 插入前捕获 lastNode,连带修复外链图片本地化永久失效<br>工具栏插入的代码块无复制按钮:afterEdit() 收编 attachCopyButtons()<br>删除线双向不通:抽 STRIKE_TAGS 常量 + isSelectionInAnyTag(),覆盖 <s>/<del>/<strike><br>通知删除后铃铛徽标残留:新增 Notification::invalidateUnreadCache(),接入 add() / markAllRead() / deleteByIds()<br>附带面向用户的《社区更新公告》(6 条口径一致的说明) | WWW/update/26/改动说明.md;同目录 社区更新公告.md |
| update/27 | 09-21 | 功能 | 楼中楼通知:Notification::notifyFloorReply()(约 75 行),楼层回复 / 被 @ / 通知楼主三类开关可配(默认楼主关)<br>三段式深链:通知链接带 ?pid= 经 Post::pageOf() 换算页码;楼层容器加 id="post-{pid}" 锚点;楼中楼列表支持 ?focus=<id> 定位高亮<br>floor_reply 补后台设置页(新增 AdminController + admin_route_register 路由 + 插件三语 11 键),assets/script.js 整体重写(196 行)<br>核心 8 文件 + 插件 17 文件;fr_replies 表结构未动 | WWW/update/27/改动说明.md |
| update/28 | 09-21 | 安全/规范整改(插件审计) | 按《插件审计报告》(2026-09-21,28 插件 / 390 PHP 文件)一次修完 P0(2) + P1(121) + P2(119),共 24 个整改项<br>User.php 只加不改:getProfileById()(受限列集合,不含 password / email_verify_token)与 setStatus(),收敛插件侧 13 处对 users 表直读直写<br>invite P0:beginTransaction() + catch 内 inTransaction() 守卫,负数 award() 改 Points::deduct() 失败回滚;插件建表统一 Plugin::ensureSchema()<br>15 个插件 CSS 主题变量化;icon_generator 71 键 ×3 语 i18n 前缀改名(plugin.icon_gen.→plugin.icon_generator.);重建 plugins_cache.json 移除 post_favorite 死钩子<br>64 个文件(44 PHP / 3 JS / 15 CSS / 2 JSON)+ 2 个必须手工删除的文件 + version.php V1.0.28 | WWW/update/28/改动说明.md |
| update/29 | 09-22 | 插件(云存储) | 新增缤纷云 Bitiful S4 对象存储插件:附件本地落盘后镜像到公开桶,前台 URL 改写为 CDN 地址,未同步文件自动回退本地<br>S3Client(纯函数 SigV4 签名 + 6 个 API)+ SecretBox(AES-256-GCM,主密钥四来源解析)+ SyncQueue(1115 行:入队/原子取批/退避失败/排空)+ UrlRewriter(白名单文件缓存 + 六道闸门)<br>核心仅 5 处"只加不改语义":Upload::url()/thumbUrl() 过滤钩子 + upload_after 事件钩子 + Plugin::http() 任意 HTTP 方法与 headers 返回<br>8 钩子 + 3 权限(route:admin/system:settings/net:fetch);默认未启用,后台停用即整体回退本地 | WWW/update/29/改动说明.md |
| update/30 | 09-22 | 插件完整包 | 完整收录积分商城 / 摇骰子 / 猜题对决 / 问答社区 / 信任等级 / 第三方登录六个插件的全部代码 + 6 份说明.md(139 项)<br>修复 3 个插件目录名含下划线导致核心生命周期回调静默失效(class_alias 兼容别名:points_mall / quiz_duel / third_party_login)<br>第三方登录 3 处运行期缺陷:CSS 未定义变量(夜间可读性)、短信登录成功后误报"验证码错误"、init_after 每请求 6 条 DDL<br>127 项原样收录、实际改动 12 项;不含插件 data/ 独立库与建表戳,覆盖不碰服务器插件数据 | WWW/update/30/改动说明.md |
| update/33 | 09-23 | 缺陷修复 | 限流 429 按当前记录算真实剩余等待秒数(RateLimiter::waitFromRecords():需过期 N-max+1 条,取升序第 N-max 条时间戳),不再报整窗口值<br>Retry-After 头、页面提示、JSON retry_after、登录 IP 锁定倒计时四处口径统一<br>三语 lang 去掉写死"15 分钟",改 {seconds} 占位符;version.php V1.0.31→V1.0.33(跳过 1.0.32) | WWW/update/33/改动说明.md |
| update/35 | 09-26 | 缺陷修复 | 后台备份列表操作列"删除"按钮整块不可见、"恢复"被切一半:.admin-table td:nth-child(4) 描述列限宽规则按列序硬编码,误伤操作列<br>修法一:admin-table-actions 定向豁免限宽(max-width:none; overflow:visible);修法二:恢复确认框默认收起、点一次才展开(x-data="{armed:false}")<br>diff 仅 +6/−3 与 +11 行;后台页不走 PageCache,无需清缓存 | WWW/update/35/改动说明.md |
| update/38 | 09-27 | 缺陷修复 | /tag/{id} 分页是死代码:版块权限过滤后用"本页条数"覆盖"全站总数",ceil(total/perPage) 恒为 1,pagination() 直接返回空串;连带"共 N 帖"标题失真<br>Tag::getThreads() 新增第 4 参 $allowedCategoryIds:全量取回→过滤→排序(抽 sortThreads())→切片,按过滤后真实总数返回 total<br>/tags 标签云补分页:getAllWithCount() 支持 limit/offset,新增 countAll() / maxThreadCount(),视图挂 pagination() | WWW/update/38/改动说明.md |
| update/39 | 09-27 | 功能/解耦 | 核心右栏删除硬编码 include 签到模板与 #checkinHint 块——未装插件时页面显示键名(plugin.daily_checkin.title)而非空白<br>用户栏"签到"位改为发帖按钮,签到插件改右栏周视图面板(周一→周日 7 格),钩子由 controller_view_before 改挂 right_sidebar_after 自取数据<br>新增用户栏内通用钩子 right_sidebar_user_actions_above(.mn-user-quick 2 列网格),与 right_sidebar_after(整块面板)位置粒度不同、不可互替<br>建议手工删除 daily_checkin/hook/controller_view_before.php(增量包表达不了删除,不删也无功能影响) | WWW/update/39/改动说明.md |
| update/40 | 09-28 | 缺陷修复(五层贯通) | 新增唯一回跳守卫 Auth::sanitizeRedirectTarget()(只允许站内相对路径,堵开放重定向),配套 sanitizeQuery() / captureRedirectTarget() / loginUrl()<br>Csrf::verifyOrDie() 未登录改跳登录并记录来源(不再是 403 死胡同);回帖 POST 改"先 requireLogin 后 CSRF"<br>核心 / 视图 / API / JS / 插件五批 27 项:ApiController 401 附 login_url、auth.js 新增 htmx:responseError 处理器、editor.js 新增 redirectOnAuthError()(原生 XHR 不触发 htmx 事件)<br>后台补齐 admin_redirect_after_verify 唯一消费点;12 个插件视图登录链接统一改 $this->loginUrl() | WWW/update/40/改动说明.md |
| update/45 | 09-29 | 安全/数据修复(核查整改) | 对账《核查结果和修复建议报告》P0/P1/P2/P3 共 20 项,18 项实施、2 项因技术前提不成立暂缓<br>F-02 归档合并与编辑竞态(可静默丢失正文):ExternMerger::mergeGroup() 重构为全程同一 .lock 临界区<br>M-05 锁失败必须中止(不再退化为"尽力而为");Queue / QueueConsumer / ShardRouter / DBFactory / ViewCounter / Schema / SearchIndexStore 等 13 个 SplitDB 文件同批对齐<br>APP 系列:Permission 新增解释层,首页/论坛/搜索/个人中心控制器统一改用;editor.js / auth.js / fhajax.js 三处前端修复<br>当日追加修复"帖子详情页浏览量恒为 0"(Thread.php / ThreadController.php / ViewCounter.php 三个镜像追加进包) | WWW/update/45/改动说明.md |
| update/46 | 09-30 | 性能优化 | "清理孤儿附件"耗时从 ~3.2s 涨到 ~9.5s,微基准证明瓶颈是每个全新 SQLite 连接的首个查询(同连接重复 COUNT 仅 0.8~1.1ms),而非 I/O 总量<br>方案 A1:第 4 段 bucket 枚举改 data/extern 直扫;方案 A2:抽 scanPluginUploads() 并加插件库动态白名单<br>Settings.php 新增 6 个方法 + 2 个常量;维护页新增"重建插件扫描白名单"按钮<br>仅 6 个文件 + version.php V1.0.46,sha256 逐字节一致 | WWW/update/46/改动说明.md |
| update/99 | 10-02 | 功能(在线升级系统) | V1.1.0 主体新增后台「系统升级」页:检查更新 → 差异预览 → 环境预检 → 取包校验 → 备份 → 应用 → 回滚,全流程无需 SSH<br>分层 P0~P4:版本比对 / latest.json 解析、差异预览(新增/修改/删除/冲突)、9+1 项预检(含版本区间校验)、写盘前三道门禁、备份+双路径回滚+RSA-2048 清单签名(默认 fail-closed,公钥内置 app/Helpers/release_keys.php)<br>version.php 刻意写 V1.0.99(代码已是 1.1.0),用于实测"老站 → 手工覆盖 → 在线升级"完整链路<br>四次同步修复:新增"删除备份"端点(三层安全)、"备份"按钮取包守卫、apply() 进度游标 offset 漏数 version.php 导致"19/20 却已完成"、删除守卫过窄化 + 错误信息改渲染在「回滚」区<br>1.1.0 起改发全量包 release/core/flinthub-core-1.1.0.zip,本包仅作留档 | WWW/update/99/改动说明.md |
| flinthub1.1.0-beta1(全量包) | 10-01 | 全量包 | FlintHub 1.1.0-beta1 纯净包:312 文件,只含核心(app/ assets/ lang/ cli/ docs/),不含任何插件与第三方主题<br>主体改动同 V1.1.0 在线升级系统:清单 RSA-2048/RS256 签名、openssl_verify() 验签、fail-closed 拒收未签名清单<br>不含 plugins/、assets/themes/、assets/uploads/、config.php 与 data/、protected/ 实际内容<br>可全新安装(install.php)或覆盖升级现有站点 | _flinthub_beta_backup_20261002/core/flinthub1.1.0-beta1/改动说明.md |
| flinthub1.1.0-beta2(全量包) | 10-01 | 全量包 | 1.1.0-beta2 纯净包(312 文件),为全量包口径,逐项改动详见仓库 CHANGELOG.md<br>升级后建议:后台「维护 → 清理缓存」、本机先跑 php cli/update_probe.php 看差异、覆盖前备份 config.php 与 data/<br>不含 plugins/ 与 assets/themes/——覆盖升级不会动已有插件目录与第三方主题 | _flinthub_beta_backup_20261002/core/flinthub1.1.0-beta2/改动说明.md |
跨包交叉参考一:数据库结构变更汇总(全部走幂等补丁,无手工迁移脚本)
| 包号 | 变更 | 落地方式 |
|---|---|---|
| update/2 | reply_index 补 status 索引 | SplitDB/Schema.php 新装内置 + marker 门控的存量站自动补丁 |
| update/9 | category_permissions 新增 can_attach 列(默认 1,不破坏存量行为) | Permission::ensureCanAttachColumn() 幂等,后台保存权限时触发 |
| update/11 | 新表 notifications(user_id/type/title/link/is_read 等) | 插件 Plugin::activate() 建表 + hook/init_after.php 懒建兜底 |
| update/14 | 新表 medals / user_medals / user_medal_wears(唯一键防重复颁发/佩戴) | 激活时 CREATE TABLE IF NOT EXISTS 双保险幂等执行 |
| update/20 | notifications 增 agg_count 列;tags.name 补 UNIQUE 索引 uk_tags_name(先合并重复再建);PRAGMA user_version 建表版本标记 | 均走 ensureColumnPatches 首次访问自动补齐 |
| update/22 | attachments 增 download_count 列 | Schema.php 补丁清单同步,存量库首次请求自动 ALTER |
| update/27~28、30、45、46 | 核心均无表结构变更 | — |
| update/29 | 插件 bs_objects / bs_logs 两表 | Plugin::ensureSchema() 幂等建,老站不存在该插件、首次启用才建 |
跨包交叉参考二:新增插件钩子一览(钩子机制见 update/9 附带的 Hooks 目录)
| 包号 | 新增钩子 / 扩展点 | 用途 |
|---|---|---|
| update/9 | attachment_download_before(附件下载输出前) | 积分/付费下载等下载前拦截,约定 $GLOBALS['attachment_download_error'] 非空即 403 |
| update/11 | vote_after、points_award_after、points_deduct_after(核心)+ review_result_after(content_review 插件) | 通知中心聚合点赞、积分变动、审核结果 |
| update/12 | sidebar_above_friend_links(PC + 移动端右栏) | 插件在友链面板上方追加内容面板 |
| update/29 | upload_url / upload_thumb_url(过滤,引用传参)+ upload_after(事件,6 参数) | 云存储 URL 改写与同步队列的唯一入口 |
| update/39 | right_sidebar_user_actions_above(用户栏内,2 列网格) | 在用户栏操作按钮上方放日常操作小按钮 |
跨包交叉参考三:version.php 与缓存机制演进
| 阶段 | 机制 |
|---|---|
| 8 月各包 | 无 version.php;部署后清 protected/cache/templates/ 模板编译缓存 |
| update/10~18 | sw.js 的 CACHE_VERSION 手工递增(v1.0.1 → v1.0.2 → v1.0.3) |
| update/20 起 | sw.js 缓存名改由站点版本号派生(注册时带 ?v=<FLINTHUB_VERSION>)——升 version.php 即自动换缓存名、清旧缓存,老访客无需手动强刷 |
| update/22 起 | CSS/JS 破缓存双保险:TemplateCompiler::asset() 用 filemtime() 生成 ?v= + SW 缓存名派生 |
| update/25 起 | 游客列表页 data/runtime/pages/*.html 为永久缓存、不按 TTL 过期,后台保存类操作需主动 PageCache::invalidate(),部署后普遍要求"维护 → 清理缓存" |
跨包交叉参考四:新增 settings 配置键一览(均按"缺失即默认"处理,老库不补键也能工作)
| 包号 | 配置键 | 默认 |
|---|---|---|
| update/5 | enable_gzip | 0(关闭,不改变现状) |
| update/7 | email_verify_enabled | 1(与原行为一致) |
| update/25 | seo_enable_og / seo_og_default_image / seo_enable_llms | 开 / 空(用站点图标兜底)/ 开 |
| update/27 | floor_reply_notify_floor_author / _notify_mention / _notify_thread_owner / _auto_expand | 1 / 1 / 0 / 1 |
| update/29 | bitiful_storage_* 共 13 个 | 由 Plugin::DEFAULTS 种入,键存在且非空则不动 |
| update/12 | 插件级 sort_order(plugin.json 字段,非 settings 键) | 未声明按 0,对既有插件零影响 |
跨包交叉参考五:三语 i18n 字典规模变化(核心三语始终保持键集合一致、键序一致)
| 包号 | 变化 |
|---|---|
| update/10 | 核心补编辑器新增 i18n key(简/繁/英三份同步) |
| update/20 | 核心三语同步新增 5 键(各 1303 键,集合完全一致);C 批再同步 blog.cover_remove(各 1302 键) |
| update/27 | 核心 1340 → 1343 键;floor_reply 插件 18 → 29 键 |
| update/28 | 插件补 1 键(plugin.code_audit.truncated,43 → 44 键);核心字典键集合零变化(1343 键) |
| update/29 | 新增插件三语各 122 键(从 0 起);核心字典零变化 |
| update/33 | 三语 auth.lock_ip 去掉写死"15 分钟",改 {seconds} 占位 |
| update/99 | 核心三语各 1450 键(本包 +4 键,键序一致) |
三、重点改动详述
3.1 update/20(09-18)——A/B/C/D 四批集中修复(体量最大)
改了什么问题
- 上线前安全审计集中暴露四类问题:A 批是可被直接利用的安全漏洞;B 批是计数器漂移、跨库写路径原子性等机制缺陷;C 批是前端与后端契约断裂导致的功能失灵;D 批是全项目扫描出的批量硬化项。
怎么改的
- A 批(8 项止血):
Content.php净化器整块删除 raw-text 元素 + 输出侧 CDATA 兜底(S-1 存储型 XSS);TemplateCompiler::pagination()三出口统一转义 +SearchController::$type加白名单(分页反射型 XSS);ApiController的/api/upload、/api/fetch-image上传权限默认拒绝(越权);Auth::getCurrentUser()/getCurrentUserWithPassword()/RememberMe/AuthController四处补封禁拦截;index.php规整$_GET标量并删除/admin/database/convert幽灵路由;ViewCounter新增apcuUsable()真实探测(APCu 日志刷屏);config.php的MAIL_PASS_KEY/CRON_KEY支持环境变量与protected/*_key.php覆盖并在仪表盘渲染告警条。 - B 批(7 组机制):计数器漂移三因同修——
Post::softDelete/restore下沉维护reply_count+ 幂等守卫,Thread::hardDelete/batchDelete只递减未软删主题;ExternStorage/ExternMerger/ExternIdxRepairer锁与缓存口径对齐(同一临界区 + 写完apcu_delete);UserController/PluginController/GroupController/TrashedController/CategoryController/DatabaseController共 7 处后台操作补审计日志(带 before 值);Setting::setValue()用debug_backtrace反查plugins/<name>/做请求级缓存,插件设置写入权限下沉;RateLimiter把"读-判断-追加"收进同一LOCK_EX临界区、损坏 json 保守拒绝;Permission无权限记录默认拒绝(仅整表为空时兜底放行);改密成功后RememberMe::clearTokens();Vote改条件写。 - C 批(9 项契约断裂):
GroupController权限矩阵保存回跳携带category_id;PluginController编辑保存透传category;DatabaseController自动扩容阈值改"字段存在才写";blog/edit.php补"移除现有封面"复选框;search/index.php补用户结果卡片分支、3 处高亮改传原始文本去双重转义、分页 URL 补time_range;blog/index.php分页携带archive;Message/MessageController收发件箱别名对齐sender_username/receiver_username;AuthController的tokenValid不再被表单校验错误驱动。 - D 批(D1~D9 批量硬化):
TemplateCompiler::safeExpr()由"只查第一个函数调用"改preg_match_all遍历全部调用(封堵isset($x) && system("id")绕过);新增cssValue()统一 CSS 上下文口径、JS 统一json_encode + JSON_HEX_*;抽currentPath()收敛 9 处REQUEST_URI直读;新增Content::lengthError()统一服务端长度校验收敛 7 个写入点;SQLiteLIKE显式ESCAPE '\'修复含%/_搜索漏检;notifications增agg_count列;sw.js缓存名改由站点版本号派生——顺带解决"老访客一直命中旧 CSS"。
影响范围
- 96 个代码文件(A 批 22 + B 批 29 − 重叠 3 + C 批新增 5 + D 批持续追加),覆盖核心、三语字典(各 1300+ 键)、多个插件;
config.php因含站点本地参数被移出包外需手工合并;D 批为增量批次"进行中",后续多包(22/26/33/45)都建立在它建立的机制之上。
关键文件与位置
app/Helpers/Content.php(A1 净化器)、app/Core/TemplateCompiler.php(A2/A5 分页转义、D7safeExpr()全调用遍历)app/Controllers/ApiController.php(A3 上传默认拒绝)、app/Helpers/Auth.php与RememberMe.php(A4 封禁拦截)app/Helpers/Settings.php(B1 计数器/B5 插件写入校验)、app/Models/Setting.php(B5debug_backtrace反查插件来源)app/SplitDB/ExternStorage.php/ExternMerger.php/ExternIdxRepairer.php(B3 锁与缓存口径对齐)、app/Helpers/RateLimiter.php(B6 原子化)app/Core/Model.php(B7/M-5 列白名单)、app/Helpers/Csrf.php(时效判定)、index.php(A5 规整$_GET/ A8 幽灵路由)
遗留事项
- D 批自述"增量批次、进行中、尚未冻结",后续由 update/22 起的各包继续衔接;B 批 M-10 的
points_log唯一索引经评估不做——会造成回帖/摇骰/邀请码等正常重复业务写入失败。 config.php因含站点本地参数被移出包外,新增段落需按第七节手工合并(后续多个包沿用同一约定)。
3.2 update/22 + update/23(09-20)——附件体系改造与回收站只读预览
改了什么问题
- 附件路径此前由各控制器/助手自行拼字符串,头像、封面、正文图、附件各写一套;列表摘要图直接读原图浪费带宽;灯箱关闭按钮点击无反应;后台回收站点标题必然落到
/?msg=not_found,必须"恢复"后才能看内容。
怎么改的
app/Helpers/Upload.php成为唯一收敛层:url()/path()/absPath()/thumbUrl()/formatSize()/makeRelPath(),file()分月落盘(YYYY-MM/),makeThumbnail()双档(thumb_400×300 /md_800×600);博客封面收敛到covers/YYYY-MM/(新增CoverTemplate.php),头像落users.avatar存相对路径。- 新增
AttachmentController:版块权限校验(改读main_index.topic_index)、?s=thumb缩略图直出、分级Cache-Control、download_count原子自增(attachments新列走ensureColumnPatches自动补齐);路径全部走Upload::path()。 - 视图层新增
uploadUrl()/uploadThumb()/excerptThumbs()三个入口和attachment_card.php、image_lightbox.php两个公共组件;Content净化器 img 分支src→md_、原图进data-fullurl(幂等);新增lightbox.js(全页连读 + 预载 + 键盘/滑动 + 缩放,closest()判点击修掉 × 失效)。 - update/23 把 22 的 C 线独立成包:
Thread::getDeletedById()与getById()完全同一条装饰路径,但只接受status=STATUS_DELETED索引行;ThreadController::show()顶部加?preview_deleted=1管理员预览分支(跳过浏览量计数与惰性归档标记、强制canReply=false);视图加只读横幅并隐藏编辑/删除/引用/回复表单/点赞等全部写入口,分页链接带上preview_deleted=1。
影响范围
- update/22:48 个代码文件 +
version.php+ 9 个md_缩略图;存量上传文件可由cli/migrate_uploads_by_month.php迁移(本次不跑)。update/23:7 个代码文件 + 三语各 3 键;软删数据本就完整保留(桶行、extern 正文、回复、附件原样在),"看不到"纯粹是读取入口被挡住,故预览功能不需要任何迁移或重建索引;getById()作为全站读取入口保持原样,避免把"读到已删帖"的能力开放给所有调用点。
关键文件与位置
app/Helpers/Upload.php(唯一收敛层)、app/Controllers/AttachmentController.php(新增)、app/Helpers/CoverTemplate.php(新增,封面路径收敛covers/YYYY-MM/)app/Views/_components/attachment_card.php、image_lightbox.php(两个新增公共组件)、assets/js/lightbox.js(新增)、assets/js/thread.js(新增,事件委托)- update/23:
app/Models/Thread.php(getDeletedById())、app/Controllers/ThreadController.php(预览分支)、app/Views/thread/show.php(只读横幅 + 隐藏写入口)、app/Views/admin/trashed.php(链接带preview_deleted=1)
遗留事项
- 旧上传文件的按月迁移工具
cli/migrate_uploads_by_month.php本次不跑,按站点存量规模另行安排;正文图md_回填由cli/backfill_md_thumbs.php幂等执行(生成后比filesize()大则回退原图)。
3.3 update/26(09-21)——用户报障修复(附社区更新公告)
改了什么问题
- 站长实测反馈 + 《四问题核查与解决方案》列出五类用户可感知缺陷:退出登录 403、粘贴后光标消失、代码块无复制按钮、删除线不通、通知徽标残留。
怎么改的
- 退出登录 403:根因是 CSRF 用"固定签发窗口"——
Csrf::token()只在超时轮换,verify()却按csrf_time + 1800s判死,页面渲染满 30 分钟必 403;Token 又是会话级单值,任何页面渲染轮换都会废掉其他标签页的旧 Token。修法三件套:Csrf::verify()校验通过后滑动续期csrf_time;AuthController::logout()由Csrf::verifyOrDie()改verify()+ 审计 + 降级放行(失效也能正常退出);Session 过期重建无 CSRF 的场景一并覆盖。 - 粘贴后光标消失:
insertAtCursor()在range.insertNode(frag)之后才取frag.lastChild——insertNode 会把 fragment 子节点全部搬入文档,此后frag.lastChild恒为 null,回退到 DocumentFragment 自身又导致setStartAfter()抛InvalidNodeTypeError,异常发生在removeAllRanges()之后,选区已清未能重建。修法:插入前先捕获lastNode;同源的removeInlineFormatting()("清除格式"后光标丢失)一并修复;该异常原本还中断了processExternalImages(),故"外链图片本地化永久失效"被连带修好。 - 代码块无复制按钮:
attachCopyButtons()原本只有初始化与 MD/可视化切换两个调用点,工具栏codeblock命令走afterEdit()路径 → 新插的代码块没按钮。修法:afterEdit()收编attachCopyButtons()。 - 删除线不通:
COMMANDS.strikeThrough用toggleWrap('s')只判<s>,而 marked 产出<del>、execCommand 产出<strike>→ 误判再套一层;updateBtnStates()漏<del>→ 按钮不高亮。修法:抽STRIKE_TAGS常量 + 新增isSelectionInAnyTag(),两处共用。 - 通知徽标残留:未读徽标有 Session 级(30s TTL)+
Settings请求级两层缓存,deleteByIds()两层都没清。修法:新增Notification::invalidateUnreadCache()接入add()/markAllRead()/deleteByIds(),通知页 POST 写操作后再兜底失效。
影响范围
- 5 个代码文件 +
version.php(V1.0.23→V1.0.26),全部为修改;未改任何 lang / CSS / 视图;随包附带面向用户的《社区更新公告》,把六条修复翻译成用户语言(含"清除格式"与"快捷键留待下一版"的明确取舍)。破缓存无需人工:asset()的 filemtime?v=+ SW 缓存名派生两层自动生效。
关键文件与位置
assets/js/editor.js(4 处:insertAtCursor()捕获 lastNode、removeInlineFormatting()同源补漏、STRIKE_TAGS+isSelectionInAnyTag()、afterEdit()收编attachCopyButtons())app/Helpers/Csrf.php(verify()滑动续期)、app/Controllers/AuthController.php(logout()降级放行 + 审计)app/Helpers/Notification.php、app/Controllers/NotificationController.php(徽标缓存两层失效)
取舍说明
- 本批明确不含"3.1 快捷键"与"3.4 i18n 英文回退"两项(用户明确本批不做),留待后续版本;按核查报告推荐方案执行,未重新审计、未扩大问题范围。
3.4 update/27(09-21)——楼中楼通知 + 三段式深链直达
改了什么问题
floor_reply(楼中楼)的写入路径是"独立库fr_replies→ 核心计数联动 → 清页面缓存",从头到尾不调Notification::add()——在楼中楼被回复或被 @,对方完全没有感知;且通知链接一直是/thread/{id}裸链接落在第 1 页,目标楼层不在第 1 页时"点通知进去找不到刚才那条"。
怎么改的
Notification新增const TYPE_FLOOR_REPLY与notifyFloorReply()(约 75 行)+ 私有floorReplyNotifyOwner();floor_reply的FrontController::reply()保存addReply()返回的$replyId并调用它;三类通知(楼层回复 / 被 @ / 通知楼主)各配一个开关,默认楼主关,activate()只写缺失键、老站不写也按默认兜底。- 三段式深链:① 通知链接带
?pid=,ThreadController::show()用新增的Post::pageOf(int $threadId, int $postId, int $perPage = 20)换算页码并夹到[1, $totalPages];② 楼层容器加id="post-{pid}"锚点(此前整帖只有一个post-body-{threadId},没有任何按楼层的锚点);③ 楼中楼列表支持?focus=<id>,条目加id="fr-{rid}"、命中加fr-focus类(新增一条 CSS 规则)。 - 插件补后台设置页:新增
AdminController+hook/admin_route_register.php(GET /floor-reply→ index、POST /floor-reply/save→ save)+ 表单视图,照seo插件范式补getAllSettings()/saveSettings()/isEnabled();plugin.json版本 1.0.0→1.1.1、icon 改核心图标表内的chat、permissions补["route:admin","system:settings"];assets/script.js整体重写(196 行:deepPid/deepFr解析、deepLink()、focusItem()、loading 防重复请求)。
影响范围
- 核心 8 文件(含三语各 +3 键,1340→1343 键)+ 插件 17 文件(含三语各 +11 键,18→29 键);
fr_replies表结构与索引完全未动、无数据迁移;plugins/plugins_cache.json必须一起覆盖(否则admin_route_register钩子不分发、后台入口不出现);部署后必须清缓存(游客列表页是永久缓存)。
关键文件与位置
- 核心:
app/Helpers/Notification.php(notifyFloorReply())、app/Models/Post.php(pageOf())、app/Controllers/ThreadController.php(?pid=换算)、app/Views/thread/show.php(楼层锚点)、app/Views/notifications/index.php(图标/配色补 3 项) - 插件:
plugins/floor_reply/FrontController.php(通知 + focus)、Plugin.php(设置范式 +pageOfReply())、AdminController.php与views/admin.php(新增)、assets/script.js(重写)、assets/style.css(+1 条.fr-item.fr-focus规则)
遗留事项
plugins/plugins_cache.json重建后的 diff 确认仅floor_reply变化、其余 27 个插件字节一致;回滚以 update/26 同名文件逐个覆盖即可。
3.5 update/28(09-21)——插件审计整改(P0 + P1 + P2 全量)
改了什么问题
- 《插件审计报告》(2026-09-21,28 个插件 / 390 个 PHP 文件)列出 P0 2 项、P1 121 项、P2 119 项:插件直读直写
users表、activate()里裸$db->exec($ddl)建表、CSS 硬编码色值不跟随主题、i18n 键前缀不统一、死钩子注册残留、每请求重复 DDL 等。
怎么改的
- 核心只加一个文件的两个方法:
User::getProfileById()(受限列集合,不含password/email_verify_token)与User::setStatus();插件侧 13 处直查直写 users(content_review、medal、daily_checkin等)全部改走它们。 - 插件建表统一
Plugin::ensureSchema()(ai_assistant2 处、invite3 处、medal3 处、content_review1 处);invite的 P0 修事务:beginTransaction()+ catch 内inTransaction()守卫,负数award()改Points::deduct()且失败回滚。 - 15 个插件的
assets/style.css主题变量化(如icon_generator69 处,含 17 处硬编码主色 →var(--mn-primary));icon_generator的 71 键 ×3 语 i18n 前缀plugin.icon_gen.→plugin.icon_generator.(键序不变);plugins_cache.json重建,移除post_favorite死钩子nav_pc_user_links。 - 另修:
draft_saver内联<script>改<meta>传用户 ID、edit_info重写为"会话守卫 + 惰性建表"、forum_required_tag内联 style 改自定义属性注入、content_review的IN ({$in})改占位符。
影响范围
- 64 个文件(44 PHP / 3 JS / 15 CSS / 2 JSON)+ 2 个必须手工删除的文件(增量包表达不了删除);核心字典键集合零变化(1343 键三语零差异);部署后必须后台清理缓存(改了导航钩子与 CSS,游客页是永久缓存);回滚用 update/27 及更早包同名文件逐个覆盖。
关键文件与位置
- 核心:
app/Models/User.php(getProfileById()/setStatus()两个新方法)、plugins/plugins_cache.json(重建,移除死钩子) - 代表性插件:
plugins/invite/InviteController.php(P0 事务 +Points::deduct)、plugins/daily_checkin/CheckinController.php(直写改Points::award())、plugins/content_review/(3 处)、plugins/draft_saver/(meta 传参 + draft.js 读取)、plugins/icon_generator/*(71 键 ×3 语前缀改名,共 213 处)
遗留事项
- 两处文件删除增量包表达不了,须按部署步骤手工执行;
plugins_cache.json不覆盖则post_favorite的死钩子注册仍留在缓存里。
3.6 update/29(09-22)——缤纷云存储插件(bitiful_storage)
改了什么问题
- 附件全部存本地磁盘,IIS 服务器既费带宽又无 CDN 加速;若直接改造上传链路,一旦云侧故障全站附件不可用,需要一个"可随时整体关停"的云存储方案。
怎么改的
- 形态是"本地落盘后镜像":
Upload::file()成功分支return前触发upload_after事件钩子(6 参数),插件把文件异步入SyncQueue(1115 行:入队 / 原子取批 / 退避失败 / 排空 / 跨库可见性判定),再由S3Client(634 行,纯函数 SigV4 签名与网络分离)镜像到缤纷云公开桶。 - 前台 URL 改写:
Upload::url()/thumbUrl()末尾的过滤钩子(引用传参,thumbUrl多带prefix档位语义)由UrlRewriter消费——白名单文件缓存 + 六道闸门 + 云端地址拼接;命中改 CDN 地址,未同步文件自动回退本地。 - 密钥安全:
SecretBox用 AES-256-GCM 加解密,主密钥四来源解析(环境变量 →config.php→protected/bitiful_key.php→ 首次自动生成);包内不含任何密钥,data/secret.key由activate()首次生成。 - 核心只加不改语义:
Upload.php3 处钩子 +Plugin.php的http()支持任意 HTTP 方法(白名单 7 种)、返回值新增status与 PSR-7 风格headers键。
影响范围
- 27 个文件(24 个新增插件文件 + 2 个核心 +
plugins_cache.json),插件数 28 → 29;插件默认未启用(activated: false),需后台手工开启;最轻回滚 = 后台停用插件,URL 改写与队列立刻全部失效、前台回到本地地址;新增bs_objects/bs_logs两表由ensureSchema()幂等创建,老站首次启用才建。
关键文件与位置
plugins/bitiful_storage/:Plugin.php(531 行,主类)、S3Client.php(634 行,SigV4)、SecretBox.php(391 行,AES-256-GCM)、ConfigStore.php(471 行,13 个配置字段)、SyncQueue.php(1115 行,同步队列)、UrlRewriter.php(284 行,六道闸门)、AdminController.php(402 行,三个 Tab)+ 8 个 hook 文件 + 三语各 122 键- 核心:
app/Helpers/Upload.php(3 处钩子)、app/Helpers/Plugin.php(http()2 处增强)
遗留事项
- 运行时产物刻意不进包:
data/bitiful_storage.sqlite(队列台账)、data/secret.key(加密主密钥)、data/done_keys.json(改写白名单缓存);13 个bitiful_storage_*settings 键由激活时按Plugin::DEFAULTS种入。
3.7 update/39(09-27)——右侧栏改造(签到解耦 + 发帖按钮 + 用户栏通用钩子)
改了什么问题
- 核心右栏视图
right_sidebar.php硬编码include('plugins/daily_checkin/_sidebar')和一段#checkinHint:插件目录不存在时TemplateCompiler::include()静默返回(按钮位空一格),#checkinHint又无Plugin::isActivated()门控,缺键时I18n::get()直接回显键名——未装插件的可见症状不是空白,是键名。 - 用户栏操作区只有"签到 + 个人信息"两格,"签到"占着主位,却与社区最高频的"发帖"争位置;签到面板信息量极低(只有"今日已签/未签"一枚按钮)。
怎么改的
- 删掉硬编码 include 与
#checkinHint块;签到插件的钩子由controller_view_before改挂right_sidebar_after——渲染钩子在闭包作用域拿不到视图数据,改为自取数据;views/_sidebar.php由"今日签到按钮"重写为"周一 → 周日 7 格周视图"(钩子路径与 htmx 回包共用本模板)。 - 用户栏"签到"位改为发帖按钮,个人信息 / 发帖按钮平均分布宽度;新增用户栏内通用钩子
right_sidebar_user_actions_above(.mn-user-quick2 列网格)——与right_sidebar_after(锚点在整个用户面板之后、只能塞整块.mn-panel)位置和粒度都不同,不可互替。 daily_checkin删死样式.dc-checkin-hint、新增dc-week-*一组样式;三语各 +1 键msg_tomorrow;CheckinController::buildAreaData()增补本周查询与week_days;建议手工删除已无消费方的hook/controller_view_before.php。
影响范围
- 包内 14 项 = 12 镜像 +
version.php(V1.0.39)+ 改动说明,另有 1 项需手工删除;必须走后台「维护 → 清理缓存」(data/runtime/pages/*.html是永久缓存、不按 TTL 过期,不清则老访客仍看旧右栏);签到记录与积分发放逻辑未改动。
关键文件与位置
app/Views/_components/right_sidebar.php(删硬编码 include 与#checkinHint、加钩子容器)、assets/css/modern.css(.mn-user-quick,+7 行)plugins/daily_checkin/views/_sidebar.php(重写为周视图)、CheckinController.php(buildAreaData()补week_days)、assets/style.css(删.dc-checkin-hint、新增dc-week-*一组)plugins/daily_checkin/hook/right_sidebar_after.php(新增,自取数据)、hook/controller_view_before.php(删除)
遗留事项
- 站点根
version.php保持 V1.0.31、由部署方覆盖升版——与 update/33/35/38 同属"包带目标版本、站点根滞后"的一类;plugins_cache.json随包下发保证新钩子被注册。
3.8 update/40(09-28)——登录失效回跳修复(核心 / 视图 / 接口 / JS / 插件贯通)
改了什么问题
- 会话过期后重新登录无法返回原页面;CSRF 校验失败且未登录时直接 403 白屏死胡同;API 401 不告诉前端登录地址(前端只能整页卡住);后台二次验证后不回跳来源页——"记录来源 → 跳登录 → 登录成功回跳"这条链路五处各自断掉。
怎么改的
Auth.php新增唯一回跳守卫sanitizeRedirectTarget()(只允许站内相对路径,堵开放重定向),配套sanitizeQuery()/captureRedirectTarget()/loginUrl();改造rememberRedirectAfter()/consumeRedirectAfter(),isAuthPath()补 4 条认证路径;AuthController::login()顶部调captureRedirectTarget(),登录页加Cache-Control: no-store。Csrf::verifyOrDie()未登录时改为跳登录并记录来源(不再是死胡同);ThreadController回复 POST 由"先校验 CSRF 后 requireLogin"改为"先 requireLogin 后 CSRF"——否则未登录用户先撞上 403,永远到不了登录页。- API 层:
ApiController新增captureApiRedirect()/apiLoginUrl(),jsonError()在 401 时自动附login_url,vote()的HX-Redirect改用它,fetchImage()401 由默认 400 纠正为 401;前端auth.js新增 document 级htmx:responseError处理器(401 且无HX-Redirect头时按login_url整页跳转);editor.js新增redirectOnAuthError()——原生 XHR 不触发 htmx 事件,/api/upload与/api/fetch-image两处单独接。 - 插件层 12 项:
floor_reply/props/user_profile/red_packet/reply_reward/post_poll/qa/points_mall/third_party_login/suiyu的视图登录链接统一改$this->loginUrl()(钩子内无$this,用htmlspecialchars(Auth::loginUrl()));props4 处POST 顺序修正;后台补齐admin_redirect_after_verify的唯一消费点。
影响范围
- 27 项镜像,按批次 1 → 2 → 3 → 3.5 → 4 → 5 分组交付;纯代码覆盖,无需清页面缓存(未改视图 HTML 结构,只改链接
href的取值方式);回跳来源只动 Session 键redirect_after/admin_redirect_after_verify,不碰任何业务数据。
关键文件与位置
app/Helpers/Auth.php(唯一回跳守卫 + 4 个新方法)、app/Helpers/Csrf.php(verifyOrDie改造)、app/Controllers/AuthController.php/ThreadController.php/ApiController.phpassets/js/auth.js(htmx:responseError处理器)、assets/js/editor.js(redirectOnAuthError())、app/Core/TemplateCompiler.php(loginUrl()视图助手)- 插件层 12 项:
floor_reply/props/user_profile/red_packet/reply_reward/post_poll/qa/points_mall/third_party_login/suiyu的视图与钩子
遗留事项
- 站点根
version.php由 V1.0.39 覆盖为 V1.0.40;此前 update/33→35→38→39 的版本号跳跃(31→33→35→38→39)均为各包自带目标版本所致,部署时切勿用旧包覆盖version.php造成版本倒退。
3.9 update/45(09-29)——核查报告整改(SplitDB / 访问控制 / 前端)
改了什么问题
- 对
审计/assets_audit_report.html与审计/splitdb_audit_report.html的逐条核查,收敛出 P0 / P1 / P2 / P3 共 20 项修复建议:F-02 归档合并与编辑竞态可静默丢失正文、M-05 锁失败退化为"尽力而为"继续执行、多个 SplitDB 组件的锁/缓存/版本标记口径不一、APP-01 权限判定多处重复实现等。
怎么改的
- F-02:
ExternMerger::mergeGroup()重构为"先取$idxPath.lock→glob收集.txt清单 → 读.idx→ 追加.bin→ 写.idx→ 删.txt",全程同一临界区、finally统一解锁。原实现"glob早于加锁 + 删.txt在锁外",并发编辑下会用旧快照覆盖.idx并删掉编辑刚写入的新.txt,导致正文变空白且无任何日志。 - M-05:
ExternStorage索引锁与ExternMerger组锁,fopen或flock(LOCK_EX|LOCK_NB)失败即记录错误并放弃本次读改写,不再降级执行;ExternIdxRepairer/ExternNamespaceMigrator/BucketAutoScaler/Queue/QueueConsumer/ShardRouter/DBFactory/ViewCounter/Schema等 13 个 SplitDB 文件同批对齐。 - APP 系列:
Permission新增解释层,HomeController/ForumController/SearchController/ProfileController/layouts/main.php统一改用它;SearchIndexStore修 APP-04;editor.js/auth.js修 A-01/02/04,fhajax.js仅关闭动态求值(CSP 未动)。 - 2026-09-29 晚追加(不属于核查范围):"帖子详情页浏览量恒为 0"修复——
Thread.php、ThreadController.php、ViewCounter.php三个镜像追加进包(读取源对齐 + 本页即时体现 +inc()返回值语义收紧,M-07 修复内容原样保留)。
影响范围
- 26 个镜像 +
version.php(V1.0.31→V1.0.45),全部 SHA256 逐字节一致、php -l21 个 PHP 文件与node --check3 个 JS 全过;version.php必须随包覆盖——sw.js缓存名由站点版本号派生,不改版本号则浏览器继续命中旧editor.js/auth.js/fhajax.js,前端修复不生效。
关键文件与位置
app/SplitDB/12 个文件(ExternStorage / ExternMerger / ExternNamespaceMigrator / ExternIdxRepairer / BucketAutoScaler / Queue / QueueConsumer / ShardRouter / DBFactory / ViewCounter / Schema / SearchIndexStore)+index.php(A-02)app/Helpers/Permission.php(新增解释层)、app/Helpers/Search.php、HomeController/ForumController/SearchController/ProfileController/ApiController、layouts/main.php- 追加进包的三个镜像:
app/Models/Thread.php、app/Controllers/ThreadController.php、app/SplitDB/ViewCounter.php(覆盖第 11 项)
遗留事项
- 20 项建议中 2 项因技术前提不成立暂缓(未实施);当日追加的"浏览量恒为 0"修复与原 11 项 M-07 内容并存于同一个
ViewCounter.php(inc()返回值语义收紧,修复内容原样保留)。
3.10 update/99 与 1.1.0 beta(10-01 ~ 10-02)——内置在线升级系统落地
改了什么问题
- 此前升级全靠手工覆盖增量包:版本号靠人记、缓存靠人清、回滚靠人事先备份,包与包之间的版本区间没有任何约束,漏覆盖
sw.js就出现"老访客一直命中旧 CSS"这类问题。
怎么改的
- V1.1.0 主体新增后台「系统升级」页,全流程:检查更新 → 差异预览(新增/修改/删除/冲突)→ 环境预检 → 取包并校验(sha256)→ 备份 → 应用 → 回滚,无需 SSH。分层设计 P0(版本比对 /
latest.json解析 / 频道)→ P1(差异预览)→ P2(预检 9 项 + 下载校验解压)→ P3(写盘前三道门禁:白名单 ⊆、路径大小写、防目录穿越)→ P4(备份 + 应用 + 双路径回滚)。 - 安全:清单
manifest-<ver>.json使用 RSA-2048 / RS256 签名,公钥内置app/Helpers/release_keys.php,站点端openssl_verify()验签,默认 fail-closed(拒收未签名清单);preflight 第 10 项校验"版本区间"(累积增量包要求base_version ≤ 当前版本 < 目标版本)。 update/99的version.php刻意写V1.0.99(代码已是 1.1.0):覆盖后站点自报 V1.0.99,后台就能看到"远端 1.1.0 可用",用来实测"老站 → 手工覆盖 → 在线升级"完整链路。- 同日四轮同步修复站长实测问题:① 新增"删除备份"端点(CSRF →
safeBackupDir()必须真实存在于data/backup/之下 → 拒删正在使用的备份),后因"列表里往往只有一份谁也别想删"收窄为"仅step=apply且未完成时拦截"并自动清空悬空的state.last_backup;② "备份"按钮加取包守卫(未取包置灰 +runBackup()前置拦截);③ 修"进度 19/20 却已升级完成"——apply()的offset漏数单独写的version.php,done=true时补到total(未完成时绝不补);④ 错误信息从页面顶部横幅改渲染在「回滚」区内部(按钮在底部,原先"点了没反应")。 - beta1 / beta2 是 1.1.0 的 312 文件纯净包:只含核心,不含
plugins/、assets/themes/、assets/uploads/、config.php与data/实际内容——1.1.0 起核心与插件分开发布。
影响范围
- 1.1.0 起站点只发布全量包
release/core/flinthub-core-1.1.0.zip,update/增量包仅作留档(唯一例外:实测升级链路时随最新修复同步一次);"取消更新通道"(不再分稳定/测试版)、"更新源只读"(只能从官网拉取)等调整使Updater类成为后续维护的新入口。
关键文件与位置
app/Helpers/release_keys.php(内置 RSA-2048 公钥,openssl_verify()验签 fail-closed)、后台「系统升级」页的控制器与视图、Updater类(取包 / sha256 校验 / 备份 / 应用 / 双路径回滚)index.php与cron_trigger.php顶部ini_set('display_errors','0')(防 warning 污染 JSON 响应);update/99共 18 个文件(增量口径),1.1.0 正式全量为release/core/flinthub-core-1.1.0.zip
遗留事项
- 在线升级链路仍在实测迭代:三轮同步修复(删除守卫收窄、备份取包守卫、进度游标补齐)均来自站长实测反馈;正式版相对 beta 还取消了"稳定/测试"双通道、更新源改为只读(只能从官网拉取)。
- 1.1.0 起核心与插件分开发布:核心全量包不含
plugins/,覆盖升级不会动已有插件目录;第三方主题assets/themes/同样不受影响。
四、版本演进小结
从 2026-08-07 的 SoSite 整改包到 2026-10-02 的 V1.1.0,两个月的 35 个包呈现五条清晰脉络:
- 安全从"补洞"走向"机制化":8 月是单点加固——
update/3〔SoSite 期〕一次修掉单字检索拖库、路径穿越、模板 RCE、默认可见全站、粘贴净化窗口期五类洞;9 月update/20一次打出 A/B/C/D 四批,把"锁、审计、默认拒绝、服务端校验"固化为机制(RateLimiter 原子化、权限默认拒绝、Csrf 时效、Model 列白名单、后台审计补全);此后update/28(插件审计,收敛插件对 users 表的直读直写)、update/45(SplitDB 锁失败必须中止、归档合并竞态)把"审计整改"变成常规动作;最终update/99/ 1.1.0 用 RSA-2048 签名清单 + fail-closed 给发布链路收口。运维方式也从"人肉备份回滚"走到"签名验签 + 双路径回滚"。
- 性能优化沿"统计 → 查询 → I/O"递进:
update/2把站点统计从同步重建改增量累加(runtimeIncr+ 惰性flushCounters,并补reply_index.status索引);update/18用请求级缓存收敛重复查询(UserPrefs一次取 6 字段、UNION ALL合并未读数、Thread::decorate()按 uid/cid 去重、level_config 文件缓存);update/46再证明"清理孤儿附件"的瓶颈是 SQLite 连接数而非 I/O 总量,改为 extern 直扫 + 插件库动态白名单。每一轮都先用微基准/查询计数定位,再动手。
- 插件生态从可用走向规范:
update/9随包沉淀 Hooks 目录(钩子机制、可用变量、路径安全、单钩子异常不影响页面);update/11/update/14落地通知中心、勋章中心两个首批大插件;update/25把 SEO 插件从"基础技术 SEO"升级为"进阶 SEO + GEO"(JSON-LD / Twitter Card / 面包屑 / llms.txt);update/29新增云存储、update/30六插件完整包补齐文档;update/28一次性整改 P0+P1+P2,确立"插件不直读 users 表、建表走ensureSchema、CSS 必须主题变量化"的规范。通用钩子池持续扩容:attachment_download_before→vote_after/points_award_after/points_deduct_after/review_result_after→sidebar_above_friend_links→upload_url/upload_thumb_url/upload_after→right_sidebar_user_actions_above。
- 前台体验持续精细化:整改包清理 9 个孤儿文件、统一样式基线;
update/10/update/26两轮打磨编辑器(快捷键与代码块复制、删除线双向互通、粘贴光标丢失、外链图片本地化);update/13修代码块"深底深字";update/24增加杂志式门户首页(cms模式,不新增查询);update/39右栏签到解耦改周视图、让出主位给发帖按钮;update/40把"登录失效回跳"做到核心 / 视图 / 接口 / JS / 插件五层贯通,并堵掉开放重定向。
- 工程化与发布方式换代:
update/20起sw.js缓存名由站点版本号派生,破缓存从手工变自动;形成"目录号 =version.php版本末位"的编号约定与"包带目标版本、站点根由部署方覆盖"的协作方式;1.1.0 起核心与插件分包发布,并内置在线升级系统(差异预览 / 取包校验 / 备份 / 双路径回滚 / RSA 签名),update/增量包体系完成历史使命转入留档——这份总览覆盖的两月,正是 FlintHub 从"手工覆盖补丁"走向"自带升级引擎"的完整过程。
汇总口径说明:总表覆盖 35 个包(29 个编号增量包 + 08-07 基础整改包 + 1.1.0-beta1/beta2 两个全量包);
update/1为 V1.0.0 发行基线(无代码变更,未列入总表);update/9附带的 Hooks 目录、update/26附带的社区更新公告已并入对应行;WWW/docs/升级说明.md为 update/1~23 的总升级说明,用作交叉核对来源。
附录:本汇总的整理边界
收录但未单独展开的内容
- 编号不连续,总表按实际存在的包排列:
WWW/update/现存 24 个编号包(2、3、10、12、13、18、20、22~30、33、35、38~40、45、46、99),4~9、11、14~17、19、21、31、32、34、36、37、41~44、47~98 等号不存在;8 月 SoSite 期的 /3、/5、/7、/9、/11、/12、/13、/14 在《8月12日的备份》另一份快照里,其中 /3、/12、/13 与 WWW 同名包内容不同,表中以"〔SoSite 期〕/版本号"区分。 - 各包的"覆盖前必须备份什么""回归验证清单""MD5/SHA256 校验表""回滚步骤"统一不收录,部署时以各源文件为准。
- 各包内部的逐文件 diff 明细(改动行数、文件大小)只在对定位根因有帮助时保留(如 update/35 的 +6/−3 行、update/38 的行数变化)。
未收录的内容
- 插件自身的
说明.md文档更新(update/25~30 多个插件同步了文档,属文档维护,未计入关键改动)。 待办事宜/、审计/、.workbuddy-ai/等开发过程产物;release/目录下的正式全量包构建产物(1.1.0 起启用,本汇总只覆盖到 beta 留档态)。- 服务器侧(IIS + 腾讯云函数 cron)的环境运维事项,各包仅在"部署关键点"里提及,本汇总不展开。