[开发]FlintHub 1.1.7 方案与落地记录(2026-10-05)

👑Lv.11 元老 🌞 骨干会员
2026-10-24 00:34:39

本文由 FlintHub 开发文档归档发布。原始文档日期见标题前缀,来源:WWW/待办事宜/FlintHub_1.1.7_方案.md。

FlintHub 1.1.7 方案与落地记录(2026-10-05)

状态:站长已确认,四项任务全部落地,核心包已出(release/core/flinthub-core-1.1.7.zip)。

核查基线:站点根 D:/phpstudy_pro/WWW,PHP 8.2.9,站点根 version.php = V1.1.4(正常,见 §三)。

本文档原为「仅核查与规划」,现按实际落地更新;未采纳项单独标注。


〇、站长确认结论(原待确认 6 项)

#事项确认结果
1任务一修复方案A + B(A 自愈 + B 一键复位)
2备案占位图flinthub-badge.svg(站点根已有,不新增文件)
3公安盾牌图标填写信息后自动配图标(用内置字体图标,无需上传图)
4备案信息显示位置与原徽标同位置;ICP + 公安同一行(margin-left:14px 间隔)
5nginx-*.conf 的 server_name 提示改进不并入(本次不做)
6index.php SITE_URL 兼容补丁并入 1.1.7(原样保留)
7(新增) 后台「密钥仍用默认值」告警加「不再提示」按钮(全局关闭)

中途修正:后台「基本信息」页里,备案字段最初插在「站点域名」与「站点部署路径」之间,把这对相邻的部署项拆开 → 已把两个备案 form-group 整体移到「站点部署路径」之后。


一、升级页「删除备份」被拦 —— 属实,已修(A + B)

1.1 原拦截点(与「升级锁」无关)

app/Controllers/Admin/UpdateController.php(原 :759-763):拦的是进度文件 data/runtime/update_progress.json,不是 data/lock/update.lock(:738 注释明确「⚠️ 不要求升级锁」)。

1.2 为什么会被永久拦住

环节说明
done 何时为 trueUpdater::apply() 只在 ok && offset>=count(ops) && version_written 时置 true → 任何错误或没跑完,done 恒 false
进度何时被清clearProgress() 只在 apply 完全成功分支调用
有无超时自愈原无(对比升级锁有 LOCK_TTL = 1800)

⇒ apply 中途失败/中断(超时、进程被杀、批次报错)→ 进度残留 done=false → 「删除备份」被永久拦住。

1.3 实际落地

A 半 —— 抽出单一真相源(app/Helpers/Updater.php,clearProgress() 之后新增):

/**
 * 「apply 是否正在跑」—— 供后台「删除备份」等操作判定(1.1.7)。
 * 判据:`update_progress.json` 为 `step=apply && done=false`,**且 `at` 新鲜**(口径同 LOCK_TTL)。
 */
public static function applyInProgress(): bool
{
    $p = self::readProgress();
    if (!is_array($p) || (string) ($p['step'] ?? '') !== 'apply' || !empty($p['done'])) {
        return false;
    }
    $at = (int) ($p['at'] ?? 0);
    return ($at > 0) && ((time() - $at) <= self::LOCK_TTL);
}

UpdateController::deleteBackup() 的拦截改为调用它(只读判定):

if (Updater::applyInProgress()) {
    $this->json(['ok' => false, 'error' => '正在应用升级,不能删除备份(请等升级跑完或回滚后再删)'], 200);
    return;
}

B 半 —— 「重置升级状态」按钮(UpdateController::resetState() + 路由 + 前端):

public function resetState()
{
    Csrf::verifyOrDie($_POST['csrf'] ?? '');
    $had = Updater::readProgress();
    Updater::clearProgress();                       // 清进度残留
    $lock     = Updater::lockStatus();
    $unlocked = false;
    if (!empty($lock['locked']) && !empty($lock['stale'])) {
        $unlocked = Updater::releaseLock($this->lockOwner());   // 只放陈旧锁
    }
    AuditLog::log('update_state_reset', 'core', 0, '…');
    $this->json(['ok' => true, 'had_progress' => is_array($had), 'unlocked' => $unlocked,
                 'lock' => Updater::lockStatus(), 'progress' => Updater::readProgress()], 200);
}

  • 路由:index.php 内 /admin/update/reset-state → POST。
  • 前端:app/Views/admin/update.php 回滚区新增按钮(window.confirm → POST → 刷新状态)。
  • i18n:admin.update_reset_state_btn / _resetting / _reset_state_hint / _confirm / _ok。

1.4 验证

  • 探针 fh_117_apply_in_progress_test.php 14/14 PASS:边界 †1799 / †1800 / †1801、中断 1 天残留、done=true、缺 at、step=fetch、JSON 损坏、clearProgress()、lockStatus() 含 stale。
  • 进度文件字节级还原(sha256 0d5ea53c… 前后一致,零残留)。

顺带:update.php:322-323 原有注释说明「报错走 backupErr、渲染在本区块内」——提示可见性此前已修过,未再动。


二、ICP / 公安备案 —— 已实现

2.1 落地方式(站长口径)

  • 不新增图片资源:两个字段都空 → 保持原徽标 flinthub-badge.svg;任一有值 → 显示备案文字。
  • 公安图标:用内置字体图标 $this->icon('shield', 13)(TemplateCompiler::icon() 的 'shield' => &#xf132;),无需上传盾牌图。
  • ICP + 公安同一行,间距 margin-left:14px。

2.2 改动点(实际)

#文件改动
1app/Views/_components/footer_beian.php新建(核心交付物,见下)
2app/Views/layouts/main.php:409-410前台页脚改调组件(badgeClass='mn-footer-badge')
3assets/themes/Xenforo/layouts/main.php:431-432同步(该目录不进核心包,见 §三.3)
4app/Controllers/Admin/SettingsController.php白名单加 icp_number / police_number,并做去标签 + 60 字截断清洗
5app/Views/admin/settings_basic.php两个 form-group,位于「站点域名 → 站点部署路径」之后
6lang/{zh,en,zh_tw}.php各 +6 键(admin.icp_number{,_placeholder,_hint}、admin.police_number{,_placeholder,_hint})

2.3 组件逻辑(_components/footer_beian.php)

$badgeClass = $badgeClass ?? 'mn-footer-badge';
$badgeStyle = $badgeStyle ?? 'height:20px;width:138px;';
$icp    = \trim((string) \app\Helpers\Settings::get('icp_number', ''));
$police = \trim((string) \app\Helpers\Settings::get('police_number', ''));
// 皆空 → 输出原徽标(?v 由 filemtime 派生);否则输出备案文字

  • 公安链接:https://beian.mps.gov.cn/#/query/webSearch?code=<纯数字>(从备案号里 preg_replace('/\D+/','') 提取)。
  • 全部输出走 $this->e() 转义。
  • ⚠️ include($tpl, $extra) 的 $extra 只给 extract()、不写回 $this->data → 组件内必须读局部变量,不能用 $this->getData()。

2.4 验证

  • 组件探针 22/22 PASS:皆空→徽标(class/style);仅 ICP;仅公安→data-icon="shield" + code=…;两者→mn-beian 单行 + margin-left:14px;XSS 转义;Xenforo 空 style 分支;纯空白→回徽标。零写入。
  • HTTP 侧「移走组件 → 复现 → 恢复」在未被 PageCache 缓存的 /login 上验证:50,223 B → 50,114 B → 50,223 B(109 B = 组件输出体积)→ 证明 include 真实生效。

> ⚠️ 首页 / 与 /forum 有磁盘缓存,直接冒烟会假阴性。


三、1.1.7 打包 —— 已出包

3.1 版本号机制(澄清)

  • 站点根 version.php = V1.1.4 属正常:打包器「站点根 version.php 不随建包改动」(cli/release_build.php:1139),包内 version.php 由 --version 生成(:1148)。
  • ⇒ 打 1.1.7 无需改站点根 version.php,只传 --version=1.1.7。

3.2 实际命令(★不用 --base)

cd /d/phpstudy_pro/WWW
. .workbuddy-ai/tmp/fh_key_env.sh          # 导出 FH_RELEASE_KEY / _PASS(口令只从文件读)
"$PHP" cli/release_build.php --type=core --version=1.1.7 --dry-run --list   # 预演
"$PHP" cli/release_build.php --type=core --version=1.1.7 --sign --key-id=flinthub-2026  # 正式
# 出包后必做:
"$PHP" -r 'is_file("data/runtime/update_check.json") && unlink("data/runtime/update_check.json");'
"$PHP" .workbuddy-ai/tmp/fh_pkg_vs_site.php     # 必须 316/316 逐字节一致

⚠️ --base 是半成品:它只改 manifest 元数据(base_version / full)与改动说明文案,并不裁剪包内容 → 会产出「自称增量、实为全量」的误导性说明。故本次不加,与历史包(1.1.4~1.1.6 均 full=true)保持一致。

3.3 产物与验收(2026-10-05 实出)

项值
包release/core/flinthub-core-1.1.7.zip,1,853,151 B,sha256 1a52143c1ba1e6d2…
清单release/core/manifest-1.1.7.json,317 条目,sha256 3e00f782b43c9098…
签名RS256 / key_id=flinthub-2026 / 256 B,站点端自验通过
latest.jsoncore → 1.1.7(全量包,base_version=null)
门禁9 项全过(#1 i18n 1539 键零差异、键序一致;#2 PHP 8.0/8.2 双版本 194 文件全过;#8 签名链路 PASS)
包 ↔ 站点fh_pkg_vs_site.php:316/316 逐字节一致,0 偏差 0 缺失
zip 结构360 条目(318 文件 + 42 目录);data/、protected/ 各仅 .htaccess + web.config;无 config.php / plugins/ / assets/themes/ / assets/uploads/

包内文件数:纳入 316 = 1.1.6 的 315 个非 version.php 文件 + 新增 app/Views/_components/footer_beian.php;再加生成的 version.php → manifest 317 条。

3.4 1.1.7 包内容清单

index.php(SITE_URL 补丁 + 2 新路由)· Updater.php(applyInProgress) · UpdateController.php(resetState + deleteBackup) · DashboardController.php(dismissKeyWarn) · SettingsController.php(备案) · settings_basic.php · admin/index.php · admin/update.php · layouts/main.php · _components/footer_beian.php(新) · lang/{zh,en,zh_tw}.php

⚠️ assets/themes/Xenforo/ 不在核心包内(Updater::ASSETS_EXCLUDES),且核心包不含 plugins/。Xenforo 主题的页脚同步改动不随核心包分发 —— 站长已明确「Xenforo 那个别管」。

3.5 发布后续(待办)

  1. 上传 release/ 到官网(/release/ 须公网可读,禁 deny all)。
  2. 出包后清 data/runtime/update_check.json(已清,6h 缓存)。
  3. 同步 release/plugins/index.json:本次无插件改动 → 不动。

四、(新增) 后台「密钥仍在使用源码内默认值」告警 —— 已加「不再提示」

  • 触发:DashboardController.php 读 MAIL_PASS_KEY_IS_DEFAULT / CRON_KEY_IS_DEFAULT(定义在 config.php,不进包)。
  • 落地:设置项 key_warn_dismissed('1' = 全局关闭):
  • index():$keyWarnOff = (Settings::get('key_warn_dismissed','0') === '1') → 两个告警位与之取反。
  • dismissKeyWarn():Csrf::verifyOrDie → setValue('key_warn_dismissed','1') → Settings::buildCache() → AuditLog → PRG 重定向回 /admin。
  • 视图 admin/index.php:告警条下加 POST 表单按钮(admin.key_warn_dismiss)。
  • 路由:index.php 内 /admin/dismiss-key-warn → POST。
  • ⚠️ 上线新设置项必须清一次 settings_cache.php(buildCache() 已在保存时调用)。

五、静态校验汇总

项结果
php -l13 个改动文件全绿
php_var_cjk_check.php全 OK(无「全角标点紧跟 $var」隐患)
fh_i18n_check.php55 PASS / 0 FAIL(核心三语各 1539 键、集合零差异、键序一致)
路由注册POST /admin/dismiss-key-warn → 302、POST /admin/update/reset-state → 302(对照 /admin/nope-xyz → 404)
清理临时探针 2 个已删;冒烟生成的 forum_0_1_e1l180.html 已删;无 .fhbak 残留

未做(低优先,可补):任务一 B(resetState)与任务四(dismissKeyWarn)的端到端 HTTP 业务验证(路由层 302 已验)。

轻量级、高性能、零 MySQL 依赖的PHP社区系统。
| 浏览 4 次 | 回复 0 次

全部回复 (0)

暂无回复

请 登录