Repository navigation
Releases: imqi1-github/imqi1-cms
Releases · imqi1-github/imqi1-cms
Release list
v1.2.1 — MCP 工具扩容 / 日志配置化 / 点赞修复 / 安全加固 / 性能优化
🎉 新功能
🤖 MCP 内容工具扩容(15 → 19)
- 新增
list_archives全站归档时间线(按 UTC 年月分组,year可过滤)、list_travels旅行足迹地图(地点 + 关联游记)、get_popular_contents热门文章(按点赞/评论数倒序)、list_content_images文章配图(封面 + 图片附件) get_content支持slug直查与独立页面(type=page),长文offset/maxChars分页续读,响应带next_offset续读指针search_content支持offset翻页与正文命中摘录match_excerpt(命中词前 40 后 80 字符)- 带站内链接的响应顶层统一下发
site_url,AI 客户端可直接拼接绝对地址 - 工具能力增强:搜索过滤、运维统计口径、排序与缓存命中率诊断完善
📋 日志系统配置化
site.config新增日志配置区:总开关 / 分类开关、行格式迁入、按大小切分与保留清理- 环境变量统一为
LOGS_DIR(裸机日志目录与 Docker 挂载宿主路径一名双义) - docker compose 内置日志挂卷(
${LOGS_DIR:-../logs}→ 容器/app/logs)
🐛 问题修复
❤️ 点赞
- 列表接口内联
like_num,修复 SSR 下点赞数被 referer 门禁 403 恒为 0 - 落库 ip 改走
getClientIp,修复反代/Docker 下审计 ip 塌缩成网关 IP
🤖 MCP
get_site_info读错 informations 键名,站点描述/备案恒为空get_popular_contentslikes 口径排序写法不兼容 Prisma 7 运行时(类型检查可过),改为关系名嵌套形态
其它
- 邮件通知三个子开关补种子行,缺行视为关,全链路对齐
- v1.2.0 新增功能两轮安全审计修复:内存泄漏面 / 时区 / 绕过 / 注入
- bun test 共享进程三类状态泄漏修复(mock 泄漏 / sharedFake 快照 / 隔离策略)
⚡ 性能优化
- 六项无感服务端优化:缓存粒度 / 渲染缓存 / 设置缓存 / 仓库代理
📚 文档
- README 新增「升级已有部署的数据库」章节,明确仅升不降
- 修正 Docker 镜像加速 daemon.json 路径(Linux 用 /etc/docker,Docker Desktop 走 GUI)
v1.2.0 — Feed / 点赞 / 草稿云同步 / 定时发布 / 限流 / MCP / 日志系统
🎉 新功能
📡 RSS Feed 体系
- 新增分类订阅
/feed/category/:slug、标签订阅/feed/tag/:slug、文章评论订阅/feed/comments/:cid - 对应前台页面 head 自动注入
<link rel="alternate">,阅读器一键订阅 - 主 Feed 重构为共享 helper,统一 RSS 2.0 + Media RSS 输出
❤️ 文章点赞
- 前台点赞按钮:心形填充 + pulse + 8 粒子迸发动画,带 tooltip
- 公开 API:
IP+UASHA256 指纹去重,复合唯一约束防并发写穿,Redis 5 分钟限速 - 点赞数全站展示:首页/归档/分类/标签/搜索/文章详情 + 批量接口
/api/likes/counts - 后台文章管理新增「点赞数」列,点击查看明细 Dialog,支持单条删除(清理刷赞)
- 点赞变更自动失效相关 ISR 缓存
📝 草稿云端同步
- 编辑文章时每 30s 自动将正文 PATCH 到服务端,换设备/清缓存不丢稿
- 仅同步 content 字段,不覆盖标题/分类/封面等元数据
⏰ 定时发布
- 文章编辑页新增「定时发布」时间控件
- Nitro 内置 cron 插件每分钟扫描,到点自动发布并失效前台缓存
🛡️ API 限流中间件
- 覆盖评论发布/点赞/验证码/搜索/友链申请/邮件/错误上报 7 类端点
- 429 + 标准
Retry-After/X-RateLimit-*响应头;Redis 优先、内存兜底、异常 fail-open
🤖 MCP Server
POST /api/mcp暴露 5 个只读工具:搜索文章/读正文/最新列表/分类/标签- 供 Claude / Cursor 等 AI 客户端检索站点内容;严格白名单,无任何写工具
- 新增 5 个运维诊断工具:系统健康快照/结构化日志读取/内容数据概览/Redis 缓存诊断/缓存清理
- 运维工具由
MCP_OPS_TOKEN门禁(fail-closed,timingSafeEqual校验),清理类操作须显式 confirm - 修复中文入参 mojibake:
readRawBody二进制模式双重编码导致中文搜索恒为空,改为 Buffer 直传
📋 日志系统
- 统一日志工具:8 分类(访问/审计/认证/外部/缓存/限流/任务/监控)
- console 与文件双写,按天落地
LOG_DIR/<分类>/<日期>.log(LOG_DIR环境变量可配) - 行内中文标签,
logWithCaller()可附带文件:行号 - 全量接入:登录/2FA/登出、24 处后台写操作审计、COS 上传删除、外联 fetch、缓存失效、限流命中、定时任务
- 原邮件日志迁入统一体系;错误监控:5xx 拦截 → 日志 + 邮件通知站主(5 分钟抑制防刷屏)
🔔 邮件通知细分开关
- 「通知站长」总开关下新增三个子开关(默认开,与既有行为一致):错误监控 / 评论通知 / 友链申请通知
- 语义:总开关关 → 一律不发;子开关关 → 对应类别不发;「回复评论者」通知不受此管
- 后台设置页呈现为总开关 + 缩进子开关,总开关关闭时子区置灰
🔧 优化与修复
- 前台 SSR 化:全站导航/热力图/相关文章改用
useFetch,首屏零 API 请求、控制台干净 - 测试库隔离:bun preload 强制测试连
imqi1_test,根除测试误清开发库数据的问题 - 测试 mock 修复:
test/helpers/fake-prisma.ts补isPrismaUniqueConstraintError导出,修bun run test的 1 fail + 1 error(server/utils/rss.ts、server/api/contents/likes/[cid].post.ts 已切到该函数的 duck-type 检测) - 依赖显式声明:
postgres-array@3.0.4加入dependencies,防npm prune/ 部分上传导致生产机报ERR_MODULE_NOT_FOUND(原作为@prisma/adapter-pg的 transitive dep,某些安装路径会漏掉) - 构建产物软链解实:nft 会把部分依赖软链到
.nitro/缓存目录,SFTP 上传后软链悬空 → 生产 502;构建后递归全树把软链替换为真实目录(嵌套 7 处,含/feed500 的根因) - 验证码:保持 4 字符可读性,增强干扰(曲线笔触/高密度噪点/字符粘连/±35° 旋转)
- 登录密码错误后立即显示并刷新验证码
- 搜索页骨架屏与真实结果对齐;后台文章管理骨架屏列宽/内边距对齐
- file-server 日志 ANSI 彩色输出(TTY 检测,管道自动降级)
- 错误通知邮件中文化
⚠️ BREAKING CHANGES 与数据库迁移
- 后台「记录邮件日志」开关移除,邮件日志始终记录
error_logs表不再创建(错误监控改为日志 + 邮件)
数据库升级(v1.1.5 → v1.2.0)需手动执行以下 SQL(init-db.sql 仅用于空库初始化,不在旧库上运行):
-- 1) 点赞表
CREATE TABLE "likes" (
"id" SERIAL NOT NULL,
"cid" INTEGER NOT NULL,
"fingerprint" VARCHAR(64) NOT NULL,
"ip" VARCHAR(45),
"user_agent" VARCHAR(255),
"create_time" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY ("id"),
CONSTRAINT "Likes_cid_fkey" FOREIGN KEY ("cid") REFERENCES "contents"("cid") ON DELETE CASCADE ON UPDATE CASCADE
);
CREATE UNIQUE INDEX "Likes_cid_fingerprint_key" ON "likes"("cid", "fingerprint");
CREATE INDEX "Likes_cid_create_time_idx" ON "likes"("cid", "create_time");
-- 2) 定时发布
ALTER TABLE "contents" ADD COLUMN "scheduled_at" TIMESTAMP(3);
CREATE INDEX "Contents_status_scheduled_at_idx" ON "contents"("status", "scheduled_at");Full Changelog: https://github.com/imqi1-github/imqi1-cms/releases/tag/v1.1.5
v1.1.5-E2E 测试体系与 test 目录分级
本次覆盖三层自动化测试的完整搭建:单元、集成(真 PostgreSQL)与浏览器端到端测试,并把 test/ 目录按层级重组。
- 前期大量 commit 累计建成 test/unit/ 与 test/integration/ 两条主轴。unit/ 下按源码镜像成 app/、server/、shared/ 三个目录,外加 real/ 直接跑真实实现、不用 mock(验证码、Prisma 与 IP 归属地库的用例在这里);integration/ 用真实 PostgreSQL,db/ 直连 Prisma Client 验证表约束、外键级联、事务与关系查询,real/ 走完整接口 handler 流程,以管理员身份验证后台增删改查,_setup 默认在 ${DB_NAME}_test 旁路库建表。helpers/(Prisma 假件、登录态假件、Nitro 自动导入的运行时替身)、types/ 与 zz-late/(mock 泄漏兜底)保持顶层。
- 期间真实集成测试还顺手暴露并修复了三个 bug:评论状态转换的 RMW race(comment_num +2 应为 +1)、search.get 的 cache stampede(50 并发 cold miss 落到 50 次 DB,缺 single-flight)、admin/cache/clear 的 redis 兜底缺失(scanAndUnlink 抛错冒泡)。每条 bug 都挂 fail 测试持续可见,修复后改 expect 即 PASS。
- test/e2e/ 新增 Playwright 框架:复用 localhost:3001 上的常驻 dev,没有时按同端口自启,Chromium 走 npmmirror 镜像内置。测试专用依赖(happy-dom、bun-types、@playwright/test)隔离进 test/package.json,模式与 scripts/ 子包相同,根目录的 scripts/test.ts 忽略 test/e2e 与 test/node_modules,两套测试互不影响。
- 用例按目录分组:admin/ 覆盖文章编辑与保存、分类、标签、评论管理、友链审批、订阅、旅行地点、附件上传、系统设置、账户资料;user-actions/ 覆盖登录、发表评论与嵌套回复、留言、搜索、友链申请、主题切换;根目录的 public-pages.spec.ts、api-smoke.spec.ts 与 not-found.spec.ts 检查公开页面渲染、公开接口可达性与 404 页面;smoke/ 收五项第三方服务只读冒烟(COS、高德、百度内容审核、SMTP、GitHub 限流),对应服务未配置时自动跳过。凭据与开发库不一致的用例会跳过而不是改开发库数据。
v1.1.4——配置体系收敛与安全加固
本版本聚焦配置体系收敛与安全加固:site.config.ts 重构为六区组织,Redis 配置迁入统一配置文件,评论能力补齐服务端校验与小程序独立开关。包含若干配置变更,升级前请务必阅读「升级须知」。
⚠️ 升级须知
以下变更涉及配置迁移,直接升级可能导致构建失败或行为不一致:
- Redis 配置迁移:Redis 连接配置已从
.env迁入site.config.ts,请将原REDIS_*环境变量移至配置文件(Docker 部署请改用对应的 compose 文件区分环境)。 site.config.ts结构调整:重构为六区组织(站点 / 构建 / 安全 / SEO / 页面 / 功能),旧结构的配置需按新区块重新归位。baiduAppId已删除:无消费点的冗余配置,请从.env及部署环境中移除相关引用。- 高德配置精简:改为
amap.proxy/amap.entry,旧配置键不再生效。 - 静态资源路径:前端统一走
publicAsset(),不再依赖 301 兜底;如有自定义模板引用静态资源,请同步调整。 - 脚本端口变更:
audit-shared-session.sh默认端口改为3001。
✨ 新特性
- 小程序评论独立开关上线,
features.miniComment收敛统一 TRUSTED_PROXY支持 CIDR 网段,SSR 内部请求加密钥- 后台账户界面新增骨架屏,加载体验更平滑
- 补齐响应式视图中缺失的操作入口(此前部分断点下无操作按钮)
- SECURITY.md 扩展至 12 条不变式,补充威胁模型与已知局限
🐛 修复
- 修复后台 2FA「信任此设备」的设备名称逻辑
- 评论接口补齐服务端校验,评论总开关在前台与后台统一处理
- 无 Redis 时不再假 ISR——页面缓存规则仅在配置 Redis 时生成
- 补回 MySQL 时代丢失的
VARCHAR(191) - 修复 3 个 ESLint 报错
🔧 重构与配置
site.config.ts重构为六区组织(站点 / 构建 / 安全 / SEO / 页面 / 功能)- cookie 与 CSP 的生产判定改用
process.env.NODE_ENV - SEO 站点地址统一跟随后台设置
- 百度审核密钥与其他密钥管理方式对齐
- 运维脚本全量转 TS,
update-sw-cdn不再正则扒源码 - 清理环境变量与部署脚本的冗余入口
- 移除
parseUserAgent两层 re-export 桥接,统一指向shared/
v1.1.3 — 文档站接入与构建产物导出
功能
- 新增 Docker bundle 阶段及
export:bundle构建产物导出,支持 CDN 部署。 - 新增构建哈希与 Docker 部署识别,后台系统信息新增「构建哈希」「部署方式」。
文档
- 接入文档站
docs.qi1.website,更新 README、贡献指南及 Issue 模板。 - 优化
.env.example环境变量说明。
杂项
- 修正 Docker 部署文案。
- 统一使用
public/manifest.webmanifest,移除无用 manifest 配置。 - 移除 sitemap.xsl 残留引用。
- 同步
imqi1-cms子仓库。
完整变更:v1.1.2...v1.1.3
v1.1.2-缓存与后台更新
修复
- 实况照片切页报错
net::ERR_FILE_NOT_FOUND:路由切换时清缓存会当场 revoke 所有 Blob,而此刻旧页可能仍在过渡动画里、新页也可能刚命中同一份 Blob,<img>/<video>随即指向已失效的 blob: 地址。改为「退役队列」——清理时只 revoke 上一轮退役的 Blob,本轮先挪进退役列表暂不释放。这样与清缓存、新页 mount 的先后无关,任何组件都不会指向被当场 revoke 的资源。
改进
- 服务启动时清空 Redis 缓存(新增 Nitro 启动插件
redis-cache-reset):进程重启即flushdb(),清掉上次运行 / 旧构建残留的页面 ISR(nitro:*)、search:*、custom:footprint等缓存键,新进程从干净缓存开始。会话存 file/database,清空不影响登录态;未配 Redis 时跳过,启动期抖动只记日志、不阻断启动。
后台
- 更新日志管理:删除改为批量操作——去掉每行单删按钮,改为行复选框 + 「全选 / 删除选中 (N)」工具栏(含半选态、选中行高亮、删除中 loading)。新增
POST /api/admin/changelogs/batch-delete,带getUser+ CSRF 校验 + ids 严格校验,删后失效/与changelogs的 ISR 缓存。 - 加载态改用骨架屏:替代原先的 spinner,卡片壳 + 日期/操作行 + 条目行,风格与订阅页一致。
重构
- 灯箱触发指令
data-fancybox→data-lightbox:自研灯箱早已取代 Fancybox,标记却一直沿用旧库的属性名。本次把渲染器(markdown 渲染 / Markdown 组件)、净化白名单、客户端解析器与页面硬编码标记一并改名。此前不改是怕 markdown 读时生成 + ISR 缓存导致新旧标记不一致;现在有了启动清缓存,部署即清旧 ISR,改名才安全。
完整变更:v1.1.1...v1.1.2
v1.1.1 — 灯箱切图与实况照片修复
修一轮独立代码评审查出的问题,都在 v1.1.0 的灯箱切图与实况照片新逻辑里。
图片灯箱
- 连着划拉浏览时(切图后 300ms 内再次拖动),说明文字不跟手——现在跟图片一起走
- 上一张的尺寸可能在它加载完成前被记成 16:9 兜底,导致下一次切换时它滑出去的那一下体积跳变
- 切图后立刻关闭(ESC/点背景)时,上一张不再以全不透明度继续滑动,说明文字也不再闪
- 「减少动态效果」下说明文字曾会淡入,已修正
实况照片
- 切到新图、视频还没就位的窗口里点播放,可能放的是上一张的片段;偶发还会卡成
「按钮显示暂停、点一次没反应」——现在播放前会核对媒体身份 - 静态图切片改为严格校验(JPEG 结束标记必须紧贴内嵌视频起点),杜绝切出半张图并被缓存复用
- 媒体缓存的释放与「提取进行中」不再互相打架(此前可能重复请求、个别 blob 永不释放)
- 标了实况却没内嵌视频的文件,不再把整包文件驻留内存
- 文章页点目录锚点不再清空媒体缓存(此前会导致该页实况照片的视频播不了)
右键菜单
- 实况照片「复制图片链接」不再得到 blob 地址,「下载图片」的文件名恢复为原文件名
v1.1.0 — 自研图片灯箱与切图过渡
图片灯箱改自研(替换掉商用许可的 Fancybox),并把「切图」做成真正的过渡动画。
图片灯箱
- 整层重写为原生
<dialog>+ top layer:天然盖住页脚悬浮按钮,白拿 ESC 关闭、焦点陷阱与背景 inert - 切图过渡:相邻切换左右滑片——拖动时相邻那张跟着手指一起露出来(两张之间留 16px 间距),
松手由入场层接着滑到位;点远端缩略图跨多张时仍走交叉淡化 - 说明文字与图片同一位移与动画,拖动时读作一个整体
- 开合动画的圆角按缩放反向补偿,收敛回原图时角不再变尖
实况照片
- 切图即停播并退回静态图(此前播放中切图会整块透明)
- 静态图改用已提取的字节(与视频同出一包),省掉一次请求,也修掉
「视频已就绪、图还在下载」的错帧;<video>补poster,首帧不再顶替照片
许可证
LICENSE全文更正为 AGPL-3.0(此前仍是 GPL-3.0,与 README 的声明不一致),
并去掉自加的文件头——那正是 GitHub 许可证徽标一直显示NOASSERTION(not identifiable)
的原因,现在已识别为 AGPL-3.0package.json补"license": "AGPL-3.0-or-later"
其他
- 地图:访客分布 InfoWindow 不再显示二维码
- 本地脚本:file-server 打印局域网访问地址,支持
HOST覆盖监听地址
v1.0.2-缓存更新
v1.0.2 — 缓存同步优化
本次聚焦缓存一致性与失效策略,内容变更后相关页面缓存即时失效,不再等 30 分钟 revalidate。
内容变更 → ISR 缓存即时失效
- 新增
invalidateContentCaches()基建:写库成功后按「数据 → 受影响页面」精确失效整页 ISR 缓存,无需等 revalidate。 - 全模块挂钩:文章 / 分类 / 标签 / 评论 / 友链 / 订阅 / 更新日志 / 旅行 / 附件 / 用户,改完即时生效。
- 修复缓存键前缀误用(
nitro/routes→ 冒号nitro:routes:_)导致的失效 0 命中问题。
统一缓存与刷新策略
- 统一缓存与刷新策略,部署后不再强制刷新。
Nginx 缓存分级
- 动态页禁止 CDN 缓存、API 保留
s-maxage、静态资源长缓存。
v1.0.1——Service Worker 优化
Service Worker 更新
- 更新策略改为提示式(autoUpdate → prompt):此前 Service Worker 一激活即
window.location.reload强制整页刷新,部署新版会打断当前浏览;现在改为部署后只提示用户,由用户手动确认刷新。 - 预缓存收敛为三个稳定资源:Workbox
globPatterns只保留manifest.webmanifest、favicon.ico、fonts/font.css;sw.js字节跨发版保持不变,不再每次发版都触发 Service Worker 更新。