Skip to content

Releases: imqi1-github/imqi1-cms

v1.2.1 — MCP 工具扩容 / 日志配置化 / 点赞修复 / 安全加固 / 性能优化

Choose a tag to compare

@imqi1-github imqi1-github released this 08 Oct 07:21

🎉 新功能

🤖 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_contents likes 口径排序写法不兼容 Prisma 7 运行时(类型检查可过),改为关系名嵌套形态

其它

  • 邮件通知三个子开关补种子行,缺行视为关,全链路对齐
  • v1.2.0 新增功能两轮安全审计修复:内存泄漏面 / 时区 / 绕过 / 注入
  • bun test 共享进程三类状态泄漏修复(mock 泄漏 / sharedFake 快照 / 隔离策略)

⚡ 性能优化

  • 六项无感服务端优化:缓存粒度 / 渲染缓存 / 设置缓存 / 仓库代理

📚 文档

  • README 新增「升级已有部署的数据库」章节,明确仅升不降
  • 修正 Docker 镜像加速 daemon.json 路径(Linux 用 /etc/docker,Docker Desktop 走 GUI)

v1.2.0 — Feed / 点赞 / 草稿云同步 / 定时发布 / 限流 / MCP / 日志系统

Choose a tag to compare

@imqi1-github imqi1-github released this 06 Oct 09:38

🎉 新功能

📡 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+UA SHA256 指纹去重,复合唯一约束防并发写穿,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 处,含 /feed 500 的根因)
  • 验证码:保持 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 目录分级

Choose a tag to compare

@imqi1-github imqi1-github released this 30 Sep 19:07

本次覆盖三层自动化测试的完整搭建:单元、集成(真 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——配置体系收敛与安全加固

Choose a tag to compare

@imqi1-github imqi1-github released this 20 Sep 10:07

本版本聚焦配置体系收敛与安全加固: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 — 文档站接入与构建产物导出

Choose a tag to compare

@imqi1-github imqi1-github released this 13 Sep 10:08

功能

  • 新增 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-缓存与后台更新

Choose a tag to compare

@imqi1-github imqi1-github released this 11 Sep 05:58

修复

  • 实况照片切页报错 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 — 灯箱切图与实况照片修复

Choose a tag to compare

@imqi1-github imqi1-github released this 11 Sep 04:36

修一轮独立代码评审查出的问题,都在 v1.1.0 的灯箱切图与实况照片新逻辑里。

图片灯箱

  • 连着划拉浏览时(切图后 300ms 内再次拖动),说明文字不跟手——现在跟图片一起走
  • 上一张的尺寸可能在它加载完成前被记成 16:9 兜底,导致下一次切换时它滑出去的那一下体积跳变
  • 切图后立刻关闭(ESC/点背景)时,上一张不再以全不透明度继续滑动,说明文字也不再闪
  • 「减少动态效果」下说明文字曾会淡入,已修正

实况照片

  • 切到新图、视频还没就位的窗口里点播放,可能放的是上一张的片段;偶发还会卡成
    「按钮显示暂停、点一次没反应」——现在播放前会核对媒体身份
  • 静态图切片改为严格校验(JPEG 结束标记必须紧贴内嵌视频起点),杜绝切出半张图并被缓存复用
  • 媒体缓存的释放与「提取进行中」不再互相打架(此前可能重复请求、个别 blob 永不释放)
  • 标了实况却没内嵌视频的文件,不再把整包文件驻留内存
  • 文章页点目录锚点不再清空媒体缓存(此前会导致该页实况照片的视频播不了)

右键菜单

  • 实况照片「复制图片链接」不再得到 blob 地址,「下载图片」的文件名恢复为原文件名

v1.1.0 — 自研图片灯箱与切图过渡

Choose a tag to compare

@imqi1-github imqi1-github released this 11 Sep 03:25

图片灯箱改自研(替换掉商用许可的 Fancybox),并把「切图」做成真正的过渡动画。

图片灯箱

  • 整层重写为原生 <dialog> + top layer:天然盖住页脚悬浮按钮,白拿 ESC 关闭、焦点陷阱与背景 inert
  • 切图过渡:相邻切换左右滑片——拖动时相邻那张跟着手指一起露出来(两张之间留 16px 间距),
    松手由入场层接着滑到位;点远端缩略图跨多张时仍走交叉淡化
  • 说明文字与图片同一位移与动画,拖动时读作一个整体
  • 开合动画的圆角按缩放反向补偿,收敛回原图时角不再变尖

实况照片

  • 切图即停播并退回静态图(此前播放中切图会整块透明)
  • 静态图改用已提取的字节(与视频同出一包),省掉一次请求,也修掉
    「视频已就绪、图还在下载」的错帧;<video> 补 poster,首帧不再顶替照片

许可证

  • LICENSE 全文更正为 AGPL-3.0(此前仍是 GPL-3.0,与 README 的声明不一致),
    并去掉自加的文件头——那正是 GitHub 许可证徽标一直显示 NOASSERTION(not identifiable)
    的原因,现在已识别为 AGPL-3.0
  • package.json 补 "license": "AGPL-3.0-or-later"

其他

  • 地图:访客分布 InfoWindow 不再显示二维码
  • 本地脚本:file-server 打印局域网访问地址,支持 HOST 覆盖监听地址

v1.0.2-缓存更新

Choose a tag to compare

@imqi1-github imqi1-github released this 09 Sep 08:47

v1.0.2 — 缓存同步优化

本次聚焦缓存一致性与失效策略,内容变更后相关页面缓存即时失效,不再等 30 分钟 revalidate。

内容变更 → ISR 缓存即时失效

  • 新增 invalidateContentCaches() 基建:写库成功后按「数据 → 受影响页面」精确失效整页 ISR 缓存,无需等 revalidate。
  • 全模块挂钩:文章 / 分类 / 标签 / 评论 / 友链 / 订阅 / 更新日志 / 旅行 / 附件 / 用户,改完即时生效。
  • 修复缓存键前缀误用(nitro/routes → 冒号 nitro:routes:_)导致的失效 0 命中问题。

统一缓存与刷新策略

  • 统一缓存与刷新策略,部署后不再强制刷新。

Nginx 缓存分级

  • 动态页禁止 CDN 缓存、API 保留 s-maxage、静态资源长缓存。

v1.0.1——Service Worker 优化

Choose a tag to compare

@imqi1-github imqi1-github released this 09 Sep 03:12

Service Worker 更新

  • 更新策略改为提示式(autoUpdate → prompt):此前 Service Worker 一激活即 window.location.reload 强制整页刷新,部署新版会打断当前浏览;现在改为部署后只提示用户,由用户手动确认刷新。
  • 预缓存收敛为三个稳定资源:Workbox globPatterns 只保留 manifest.webmanifest、favicon.ico、fonts/font.css;sw.js 字节跨发版保持不变,不再每次发版都触发 Service Worker 更新。