[开发]SoSite 三功能落地方案:在线升级 · 插件仓库 · 定时任务

👑Lv.11 元老 🌞 骨干会员
2026-10-25 00:50:06

本文由 FlintHub 开发文档归档发布。原始文档日期见标题前缀,来源:8月13日备份/SoSite-三功能落地方案-在线升级-插件仓库-定时任务.md。

SoSite 三功能落地方案:在线升级 · 插件仓库 · 定时任务

文档日期:2026-08-07

参考对象:bbs/(bbs1.org 极简论坛,单文件 PHP,GitHub 开源)

目标程序:SoSite V5.0.0(自研 MVC,PHP 8.2 / MySQL 5.7-8.0,零 Composer 依赖)

说明:本方案为设计文档,非最终实现代码;落地前需按实际代码结构复核函数签名与路由注册方式。

⚠️ 2026-08-07 决策:定时任务经评估对现有程序价值低(现有"周期性"维护需求均为访问触发 + 节流模式,如 config.php 会话 GC、邀请码/红包过期按访问判断),暂不开发。确有需要时按 §4 以插件形式实施。


一、背景与目标

用户希望借鉴 bbs1.org(下称 bbs)的三项能力:

能力bbs 的实现SoSite 现状
在线升级后台一键检测 GitHub 版本、逐文件差异下载、原子替换、失败回滚、自动同步数据库结构手动下载 update 包 → 覆盖 → 删孤儿文件 → 清缓存(全手动,易漏)
插件仓库远程市场 JSON 源、一键安装/更新/卸载、sha256 校验、安装后默认停用插件仅本地手动放置 + 后台启停;无市场、无远程安装
定时任务?a=cron 统一调度、租约防并发、失败暂停重试、任务日志、插件 manifest 声明任务无任何定时任务机制(仅 AuditLog::purgeOldLogs 注释"可 cron 调用")

目标:在不改动 SoSite 现有 14 个插件结构与核心架构的前提下,新增三项能力,全部走"后台管理员操作 + 强校验 + 备份回滚"的安全路径。


二、bbs 参考实现机制拆解

2.1 在线升级(Setup.php)

核心思路:以"逐文件内容哈希比对"代替"整包下载"。

  1. 版本检测:调用 GitHub API repos/{repo}/commits/{branch} 获取最新 commit sha,再递归拉取文件树,得到每个文件的 git blob sha(sha1('blob ' . 长度 . "\0" . 内容))。
  2. 本地比对:对本地每个文件算同样的 blob sha,与远端清单对比 → 得出 新增 / 更新 / 删除 三类变更清单(us_local_changes())。
  3. 升级执行(setup_update_run() → us_install_files()):
  • 二次校验远端 sha 未漂移(防 TOCTOU);
  • 逐个下载 raw.githubusercontent.com/{repo}/{sha}/{path},用远端清单里的 blob sha 校验下载内容;
  • 先把原文件全部备份到临时目录(含哈希核对);
  • 原子替换:copy(临时→swap) + rename(swap→目标),替换后再次校验;
  • 全部成功后才写入版本记录 update-state.json(sha / updated_at / files);
  • 任一步失败:按逆序回滚所有已替换文件,并回滚版本记录。
  1. 并发防护:us_acquire_lock() 用 flock(LOCK_EX|LOCK_NB) 占升级锁,重复触发直接拒绝。
  2. 数据库结构同步:文件更新完成后不立即执行建表(新代码尚未生效),而是"延迟到下一个请求"(us_defer_schema_after_update() → 下次访问时 us_run_deferred_schema() 执行 schema 差异同步)。
  3. 依赖强制:勾选升级 index.php 时,强制同时带上 app/optional/Cron.php 与 Plugin.php(防止核心与可选模块版本错配)。
  4. 更新通知:后台检测到新版本后写入 update-state.json,通过站内通知提醒管理员升级。
  5. 其他:升级前后刷新 opcache(us_refresh_opcache_after_update());升级页提示"勾选文件的本地修改将被覆盖"。

可借鉴点:内容哈希逐文件比对(精准、增量、省流量)、备份-替换-回滚三阶段、flock 并发锁、延迟 schema 同步、升级通知。

2.2 插件仓库(Plugin.php)

核心思路:单文件插件模型 + 远程 JSON 市场 + 强校验安装。

  1. 插件模型:每个插件 = app/plugins/{id}/plugin.php 单个 PHP 文件,文件内 return [manifest](id/name/version/description/author/hooks/routes/admin_tabs/assets/cron/install/uninstall)。注册表 app_plugins 表存 manifest 快照,普通请求不扫描目录。
  2. 市场端点:PLUGIN_MARKET_ENDPOINT?action=plugin_market_feed 返回 JSON 列表,每条含元数据 + code(完整插件源码,need_code=true 时才下发)+ sha256。本地有 5 分钟缓存,失败回退到过期缓存。
  3. 安装流程(plugin_market_install()):
  • 校验:<?php 开头、sha256(源码) 与市场一致、源码内嵌 'id' => xxx 与市场 ID 一致;
  • 备份现有插件文件 → tmp + rename 原子写入 → opcache_invalidate;
  • 注册表行置为停用(安装后必须手动启用,防止未审查代码立即执行);
  • 禁用该插件的全部 cron 任务;保存市场 sha256/topic_id 元数据;标记资源重建。
  1. 注册表同步(plugin_registry_sync()):扫描插件目录 → opcache_invalidate + include 读 manifest → 白名单校验(hook/route/callback 函数名、cron interval 范围)→ upsert 注册表 → 删除已消失插件的注册与 cron 任务 → 重建合并资源 plugins.css/js。
  2. 版本对比:version_compare(远端, 本地) > 0 → 市场页显示"可更新"。
  3. 市场页面:后台"插件"tab → 市场视图(搜索、安装/更新/重新安装、展示 sha256 摘要)。

可借鉴点:远程 JSON 市场 + 本地缓存、sha256 强校验、安装后默认停用、注册表与磁盘同步、资源自动合并。

2.3 定时任务(Cron.php)

核心思路:数据库任务表 + 租约(lease)领取防并发 + 失败暂停重试。

  1. 入口:index.php?a=cron(外部服务如 cron-job.org 每分钟 GET 触发),或 CLI php index.php cron(自动把 argv 映射为 a=cron)。路由执行 set_time_limit(0) + ignore_user_abort(true)。
  2. 任务表 app_cron_tasks:plugin_id / task_name / callback / interval_seconds / enabled / status / attempts / failure_count / retry_limit / pause_seconds / pause_until / available_at / last_started_at / last_success_at / last_error / lease_token / lease_until。
  3. 租约领取(cron_task_claim()):在事务内 SELECT ... WHERE enabled=1 AND available_at<=now ORDER BY available_at LIMIT 1,随即 UPDATE 抢占(写 lease_token/lease_until,rowCount=1 才成功)——多个并发 cron 请求同时进来也不会重复执行同一任务;已领任务被再次 claim 视为"租约超时",回收并记录失败。最多重试 3 次抢占。
  4. 执行(cron_run()):循环领取到期任务 → 校验插件启用/回调存在 → 调用 callback($plugin, $definition) → 写任务日志 app_cron_logs(status/message/时间)→ cron_task_finish() 释放租约并计算下次 available_at。
  5. 失败策略:失败计数累加,达到 retry_limit 后 pause_until = now + pause_seconds(默认暂停 30 分钟);暂停到期后清零失败计数恢复调度——失败任务不会永久阻塞队列。
  6. 任务注册(plugin_cron_sync()):插件注册表同步时,把 manifest 中 cron 声明(name → callback/interval)同步进任务表;interval 支持固定秒数(60~31536000)或"返回秒数的函数名"(后台可配间隔)。
  7. 后台页:插件 tab → 定时任务日志视图(admin_plugins_cron_logs_page_html())。

可借鉴点:事务内租约抢占(比锁文件更稳、可跨进程)、available_at 时间片调度、失败暂停-恢复、任务日志表、插件 manifest 声明式注册。


三、SoSite 现状与差距

3.1 SoSite 现有架构(与方案相关部分)

模块现状
路由app/Core/Router.php:get/post/any/group + dispatch(),支持 {param};入口在 index.php,路由注册集中在某处(需按实际代码确认)
后台app/Controllers/Admin/*:BaseController(Auth::isAdmin() 拦截)+ Dashboard / Database(备份/导入/恢复)/ Blog / Category 等控制器
插件app/Helpers/Plugin.php:loadPlugins() 扫描 plugins/*/plugin.json + 写 plugins_cache.php 缓存;hook() 按 hook 名调用插件注册的 hook 文件;activate/deactivate/refresh/uninstall 已有
插件结构多文件:plugins/{id}/plugin.json(声明 hooks → hook/*.php 文件)+ Plugin.php + views/ + assets/ + hook/;现有 14 个插件
数据库迁移protected/database/init.php → initDatabaseSchema()(幂等:CREATE IF NOT EXISTS + try/catch ALTER),install.php 已复用
配置config.php 常量(SOSITE_VERSION);站点设置存 settings 表(app/Helpers/Settings.php)
升级现状update/ 目录 = 手动整改包(README 说明:备份 → 覆盖 → 删 9 个孤儿文件 → 清模板缓存 → 回归验证);main.zip 为源码包
审计app/Helpers/AuditLog.php(purgeOldLogs() 已注释"可 cron 调用")

3.2 差距清单

  1. 无定时任务:无任务表、无调度入口、无后台管理页;签到/红包等插件的"每日重置"类逻辑目前靠访问触发或缓存标记(.daily_checkin_table_cache 等)。
  2. 无在线升级:只有手动 update 包;无版本检测、无文件差异比对、无回滚。
  3. 无插件市场:插件只能本地放置;无远程安装/更新。
  4. 插件为多文件结构:市场分发不能像 bbs 那样单文件下发源码,需 zip 分发包(zip 解压安全性是新增关注点)。
  5. 无 CLI 入口:定时任务可纯 HTTP 触发(/cron 路由),CLI 为可选增强。

四、落地方案一:定时任务(⚠ 暂缓,按需实施;建议插件化)

独立性强、无外部依赖、收益最高,且 bbs 的租约设计可完整迁移。

4.1 新增表(MySQL)

CREATE TABLE IF NOT EXISTS app_cron_tasks (
  id INT AUTO_INCREMENT PRIMARY KEY,
  plugin_id VARCHAR(50) NOT NULL DEFAULT 'core',   -- 核心任务 'core',插件任务用插件 ID
  task_name VARCHAR(64) NOT NULL,
  callback VARCHAR(100) NOT NULL,                   -- 函数名(白名单校验)
  interval_seconds INT NOT NULL DEFAULT 86400,
  enabled TINYINT(1) NOT NULL DEFAULT 0,
  status VARCHAR(20) NOT NULL DEFAULT 'idle',       -- idle/running/paused/error
  attempts INT NOT NULL DEFAULT 0,
  failure_count INT NOT NULL DEFAULT 0,
  retry_limit INT NOT NULL DEFAULT 3,
  pause_seconds INT NOT NULL DEFAULT 1800,          -- 失败暂停时长(秒)
  pause_until INT NOT NULL DEFAULT 0,               -- 暂停截止(unix 时间戳)
  available_at INT NOT NULL DEFAULT 0,              -- 下次可执行时间
  last_started_at INT NOT NULL DEFAULT 0,
  last_success_at INT NOT NULL DEFAULT 0,
  last_error VARCHAR(500) NOT NULL DEFAULT '',
  lease_token VARCHAR(32) NOT NULL DEFAULT '',
  lease_until INT NOT NULL DEFAULT 0,               -- 租约截止,超时回收
  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  UNIQUE KEY uk_task (plugin_id, task_name)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE IF NOT EXISTS app_cron_logs (
  id INT AUTO_INCREMENT PRIMARY KEY,
  plugin_id VARCHAR(50) NOT NULL DEFAULT 'core',
  task_name VARCHAR(64) NOT NULL,
  status VARCHAR(20) NOT NULL,                      -- success/failed/timeout
  message VARCHAR(500) NOT NULL DEFAULT '',
  started_at INT NOT NULL DEFAULT 0,
  finished_at INT NOT NULL DEFAULT 0,
  INDEX idx_task (plugin_id, task_name),
  INDEX idx_finished (finished_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

表结构需加入 initDatabaseSchema() 的幂等建表流程(新增一个 createCronTables() 步骤即可)。

4.2 新增核心文件

app/Helpers/Cron.php          -- 调度执行器(租约领取/释放/超时回收/失败暂停/日志)
app/Controllers/CronController.php -- /cron 入口(校验 cron_key)
app/Views/admin/cron.php      -- 后台任务列表页
app/Views/admin/cron_logs.php -- 后台任务日志页
app/Controllers/Admin/CronController.php -- 后台管理(启用/停用/立即执行/清日志)

4.3 调度入口

// index.php 或路由注册处(按现有 Router 方式注册)
$router->get('/cron', [CronController::class, 'run']);

// CronController::run()
// 1) 校验 cron_key:settings 表存随机密钥,URL /cron?key=xxxx
//    (bbs 未做 key,但被恶意高频触发会浪费资源,SoSite 建议加;
//     密钥在安装时生成,管理员可在后台重置)
// 2) set_time_limit(0); ignore_user_abort(true);
// 3) Cron::run() 循环领取并执行到期任务
// 4) 输出纯文本摘要(due/success/failed)

部署方式(README 说明):

  • 方式 A:cron-job.org / 宝塔计划任务 / Windows 计划任务,每分钟 curl -s "https://域名/cron?key=xxx"
  • 方式 B(可选增强):新增 cli.php 入口,php cli.php cron 直连数据库执行(不依赖 Web)

4.4 任务注册与执行

  • 核心任务:plugin_id='core',安装/升级时注册(首批接入:AuditLog::purgeOldLogs() 日志清理、签到插件"昨日数据归档"、red_packet 过期红包回收等)。
  • 插件任务:plugin.json 新增可选 cron 声明:
{
  "name": "每日签到",
  "version": "1.1.0",
  "cron": {
    "daily_reset":   { "callback": "daily_checkin_reset",   "interval": 86400 },
    "monthly_stats": { "callback": "monthly_stats_run",     "interval": "monthly_stats_interval" }
  }
}

  • Plugin::loadPlugins() 或 refresh() 时同步 app_cron_tasks(仿 plugin_cron_sync():insert-ignore + 更新 interval/callback/enabled;manifest 删除的任务移除)。
  • 执行器:事务内 SELECT ... FOR UPDATE 或"SELECT + UPDATE 抢占(rowCount=1)"领取 → 调用回调(容错 try/catch)→ 写日志 → 释放租约并计算下次 available_at = max(now, last_started_at) + interval。回调必须幂等、可重入(租约保证同一时刻单实例执行,跨实例仍需回调自身幂等)。
  • 失败处理:failure_count+1,达 retry_limit 置 pause_until;pause_until <= now 时恢复并清零。

4.5 后台页面

  • 后台"系统设置"或独立"定时任务"入口:任务列表(任务/插件/间隔/上次执行/状态/失败次数)、启用停用、立即执行、日志查看、密钥重置。
  • 复用 BaseController 的鉴权与 CSRF。

五、落地方案二:插件仓库(建议 P1)

核心挑战:SoSite 插件是多文件结构,市场分发必须走 zip 包,需补齐解压安全。

5.1 市场端点约定(自建或第三方静态 JSON)

GET https://plugins.sosite.example/api/plugin-market
{
  "ok": 1,
  "plugins": [
    {
      "id": "friend_links",
      "name": "友情链接",
      "version": "1.2.0",
      "description": "首页与侧边栏友情链接管理",
      "author": "SoSite",
      "updated_at": 1750000000,
      "size": 24576,
      "sha256": "hex...",                     // 分发包 zip 的 sha256(强校验)
      "download_url": "https://plugins.sosite.example/packs/friend_links-1.2.0.zip"
    }
  ]
}

  • 本地缓存 5 分钟 + 失败回退过期缓存(仿 bbs plugin_market_cache_*,缓存放 protected/cache/)。
  • 市场地址与 cron_key 一样存 settings 表,后台可改。

5.2 分发包规范

{id}-{version}.zip
└── {id}/
    ├── plugin.json          -- 与现有插件 manifest 一致(含 version)
    ├── Plugin.php
    ├── hook/*.php
    ├── views/**             -- 可选
    └── assets/**            -- 可选

5.3 安装器(app/Helpers/PluginMarket.php,新增)

流程(后台 POST + CSRF + Auth::isAdmin()):

  1. 下载 zip(限大小,如 10MB)→ sha256 与市场声明比对,不一致拒绝;
  2. 校验 zip 结构:{id}/plugin.json 存在、plugin.json.id 与市场 ID 一致、manifest 字段白名单校验(仿 plugin_manifest_validate);
  3. 解压到临时目录,逐条目做路径穿越防护(realpath 校验每个 entry 的落点在目标插件目录内,拒绝 ../、绝对路径、符号链接);
  4. 备份现有 plugins/{id}/ 到 protected/backups/plugins/;
  5. 原子移入 plugins/{id}/(先 rename 旧目录到备份、再 rename 临时目录到位);
  6. Plugin::refresh() 重建 plugins_cache.php;新插件默认停用,后台提示"已安装,请手动启用"(仿 bbs 的自动停用策略);
  7. 写 plugin_{id}_market_meta(version/sha256/updated_at)用于更新检测。

更新 = 同流程(备份旧版后覆盖);卸载复用现有 Plugin::uninstall()。

5.4 更新检测与后台市场页

  • 本地 plugin.json.version vs 市场 version,version_compare > 显示"可更新"。
  • 后台插件管理页新增"插件市场"tab(复用现有后台布局):列表 / 搜索 / 安装 / 更新 / 重新安装 / 卸载,按钮带二次确认。

5.5 安全红线

  • 插件代码 = 站点代码权限(能执行任意 PHP)。措施:仅官方/可信市场源、zip sha256 强校验、解压路径白名单、安装后默认停用需人工启用、安装前展示版本与 sha256 摘要、protected/backups/plugins/ 可回滚。
  • 文档明示:第三方市场插件视为不信任代码,与 bbs 同理念。

六、落地方案三:在线升级(建议 P2)

核心挑战:SoSite 无公开 GitHub 仓库,不能照搬 GitHub API;改为自建更新服务器 + JSON 清单,比对算法从 git blob sha 换成普通 sha256。

6.1 更新源约定

GET https://updates.sosite.example/update.json
{
  "version": "V5.1.0",
  "published_at": "2026-08-08T10:00:00+08:00",
  "message": "……更新说明(前台可展示)……",
  "files": {
    "app/Core/Router.php": "sha256hex",
    "install.php":        "sha256hex",
    "index.php":          "sha256hex"
  },
  "delete": ["assets/css/forum.css", "assets/css/layout.css"],
  "sensitive": ["protected/database/init.php"]   // 允许但需特别确认的文件
}

  • 比对:本地文件 sha256 vs 清单 → 新增 / 更新 / 删除 三类变更(delete 仅删除"本次升级声明删除"的文件,仿 bbs 的受保护路径排除)。
  • 路径白名单:默认排除 config.php、protected/(数据/备份/缓存)、plugins/(插件归插件市场管)、assets/uploads/(用户数据)。需要升级的文件(如 protected/database/init.php)显式列在 sensitive 并强确认。

6.2 升级执行(后台"系统升级"页)

复用 install.php 已验证的可靠模式,流程对齐 bbs:

  1. 检测:拉 update.json → 逐文件 sha256 比对 → 展示变更清单(可勾选,提示"本地修改将被覆盖");
  2. 执行(POST + CSRF + 管理员 + flock 防并发):
  • 逐个下载 https://updates.sosite.example/files/{version}/{path} → sha256 强校验 → 失败即中止;
  • 备份全部待替换文件(含原文件 + 版本记录 protected/version-state.json)到 protected/backups/upgrade/;
  • 原子替换(copy→swap + rename),替换后复核 sha256;
  • 执行 delete 清单(仅升级声明文件);
  • 刷新 opcache(opcache_invalidate,已存在此模式);
  • 数据库结构同步:文件更新后调用 initDatabaseSchema()(SoSite 已有幂等迁移器,比 bbs 的延迟 schema 更简单——但注意新代码尚未加载的问题:应延迟到下一个请求执行,或接受"迁移器独立于业务代码"的现状,推荐前者,仿 bbs 的 deferred schema);
  • 写版本记录 protected/version-state.json(version/sha/files/时间);
  1. 失败回滚:任一步失败按逆序回滚已替换文件 + 还原版本记录(仿 bbs us_install_files 的 catch 分支);
  2. 升级通知:后台检测到新版本 → AuditLog 或站内通知提醒管理员。

6.3 后台页面

  • 后台新增"系统升级"页:本地版本(SOSITE_VERSION + version-state)/ 远端版本 / 变更清单勾选 / 执行按钮 / 升级日志。
  • 前置提示:备份数据库、附件、插件目录(复用 Admin\DatabaseController 的备份入口做快捷入口)。

6.4 与现有 update 包的关系

  • 保留 update/ 手动包作为离线兜底;在线升级是同一发布流程的自动化产物(发布方生成 update.json + files/{version}/ 即可,可用现成的"整改包"目录做第一批发布物)。

6.5 参考结论(xiunox 全量 zip 路线,2026-08-07 复核后推荐)

对照 bbs(GitHub 逐文件 diff + git blob sha + 原子替换 + 自动回滚)与 xiunox(Gitee Release 全量 zip + 黑名单跳过 + 维护锁 + 独立步骤化迁移器)后,推荐 SoSite 走 xiunox 的全量 zip 路线:升级低频、包体小,bbs 的逐文件 diff 属过度设计;并吸收两侧安全增强:

xiunox 做法SoSite 采纳
Gitee Release(或自建源)发全量 zip,Release tag 作版本号✅ 采纳;下载后 sha256 校验(xiunox 只验大小>0,我们加强)
黑名单目录跳过覆盖:conf/、upload/、log/、tmp/、plugin/✅ 采纳:config.php、protected/、plugins/、assets/uploads/、log 跳过
维护模式锁(写 maintenance.lock → 站点进维护页)✅ 采纳:升级期间入口拦截显示维护页(与 bbs 的 flock 防并发互补,锁防并发、维护锁防访问)
步骤化前端编排(download→extract→db→cleanup→off)+ 单步重试标红 + 只读 diagnose✅ 采纳
文件覆盖后加载新代码再跑数据库迁移✅ 采纳:复用现有幂等 initDatabaseSchema();可选增强"版本化步骤记录"
升级后兜底递增 static_version / 清缓存✅ 采纳:清模板编译缓存 + opcache_invalidate + 更新版本标记,防浏览器/模板走旧缓存
备份责任前置(流程内不自动备份,升级前强提示)✅ 采纳:升级前强提示,复用后台数据库备份;可加"被覆盖文件备份到 upgrade_backup_*"轻量回滚
直接 copy 覆盖不校验、SSL verify off❌ 不采纳:保持 zip sha256 + 覆盖后复核 + HTTPS 正规证书
无自动回滚⚠️ 部分采纳:以"备份被覆盖文件"做轻量回滚,不学 bbs 的完整回滚(成本过高)


七、实施计划

阶段内容涉及文件预估
P0 定时任务(暂缓)建表(并入 initDatabaseSchema)+ app/Helpers/Cron.php 执行器 + /cron 路由与 cron_key + 后台任务/日志页 + AuditLog::purgeOldLogs 接入 + 1 个插件(签到)接入 cron 声明表结构、Cron.php、CronController×2、后台视图×2、Plugin.php 同步逻辑、plugin.json 示例2~3 天(暂不实施)
P1 插件仓库市场端点约定文档 + app/Helpers/PluginMarket.php 安装器(zip 校验/解压/备份/移入)+ 后台市场页 + 打包发布脚本(生成 zip + sha256 + feed JSON)PluginMarket.php、Admin 控制器、市场视图、发布脚本2~3 天
P2 在线升级全量 zip 发布包(Gitee Release 或自建源)+ app/Helpers/Upgrader.php(下载/sha256 校验/解压/黑名单跳过/覆盖/备份回滚)+ 维护模式锁 + 步骤化编排与只读诊断 + 复用 initDatabaseSchema()Upgrader.php、Admin 控制器、升级视图、维护页拦截、版本记录3~4 天
P3 收尾部署文档(cron 触发方式、市场/更新源配置)、回归测试、install.php 联动(安装时生成 cron_key)README/部署说明、install.php1 天

合计约 1.5~2 周(单人)。三项相互独立,可任选顺序;当前建议:插件仓库 → 在线升级;定时任务暂缓(2026-08-07 决策)。

八、风险与安全措施

风险措施
远程代码执行面扩大(升级/插件安装即执行远端代码)仅管理员 + CSRF + 强 sha256 校验 + 官方/可信源 + 安装后默认停用 + 全量备份可回滚 + 升级/安装全程日志(AuditLog)
zip 解压路径穿越逐 entry realpath 落点校验、拒绝 ..//绝对路径/符号链接、大小与条目数上限
定时任务被滥用(恶意高频触发)cron_key 校验 + 租约防并发 + 回调幂等 + pause 机制;CLI 方式可选
升级覆盖本地定制变更清单逐文件勾选 + 升级前自动备份 + 提示本地修改将被覆盖
升级中代码/数据库不一致延迟 schema 同步(新代码就绪后的下一请求执行)+ 迁移器幂等
与现有 14 个插件兼容不改插件目录结构;cron 声明为增量字段;市场分发包保持现有 manifest 格式
失败任务永久阻塞队列retry_limit + pause_seconds 暂停-恢复机制(仿 bbs)

九、附录

9.1 关键新增文件总览(建议)

app/Helpers/Cron.php
app/Helpers/PluginMarket.php
app/Helpers/Upgrader.php
app/Controllers/CronController.php
app/Controllers/Admin/CronController.php
app/Controllers/Admin/UpgradeController.php
app/Controllers/Admin/PluginMarketController.php
app/Views/admin/cron.php
app/Views/admin/cron_logs.php
app/Views/admin/upgrade.php
app/Views/admin/plugin_market.php
cli.php                        (可选,CLI 定时任务入口)
protected/version-state.json   (升级版本记录,运行时生成)
protected/backups/plugins/     (插件安装/升级备份目录)
protected/backups/upgrade/     (程序升级备份目录)

9.2 需复核的 SoSite 内部接口(实现前确认)

  • 路由注册位置与方式(index.php / app/init.php / Router 调用点);
  • app/Helpers/Plugin.php 的 loadPlugins()/refresh() 缓存重建机制(plugins_cache.php),确定 cron 同步的挂载点;
  • settings 表读写封装(Settings::get/set)以存放 cron_key、市场地址、更新源地址;
  • initDatabaseSchema() 的扩展方式(新增建表步骤的接入点);
  • 站内通知实现(AuditLog 或消息表)用于"新版本/新插件更新"提醒。

9.3 参考对照(bbs 源函数 → 本方案对应)

bbs 函数本方案对应
Setup::us_remote_release / us_local_changesUpgrader::fetchManifest / Upgrader::diffFiles
Setup::us_install_files(备份/原子替换/回滚)Upgrader::apply(同模式,sha256)
Setup::us_acquire_lock / us_run_deferred_schemaUpgrader::acquireLock / Upgrader::runDeferredSchema
Plugin::plugin_market_install / plugin_registry_syncPluginMarket::install / 现有 Plugin::refresh() 扩展
Cron::cron_run / cron_task_claim / plugin_cron_syncCron::run / Cron::claim / Plugin::syncCronTasks

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

全部回复 (0)

暂无回复

请 登录