清言(QingYan)是一个很干净、API-first 的评论与对话基础设施。当前基线聚焦后端接口与后台管理能力,可服务于 FangYuan 等内容站点的评论、验证码、页面点赞和管理需求。
它不是论坛系统、评论 SaaS,也不是强调自有前端体验的完整评论产品。当前仓库提供的是一个可自部署、可继续演进、低噪声的评论后端基线。
QingYan 与 FangYuan 通过公开 HTTP API 契约解耦:FangYuan 只是当前一个已接入的前端,不要求和 QingYan 同步发布,也不依赖 QingYan 仓库内的实现细节或发布节奏。
当前正式版本为 v0.2.3,首个 upgrade lifecycle 基线为 v0.1.0。后续涉及 schema、配置语义、settings owner 或数据格式的破坏性变化必须进入 upgrade lifecycle。
- 评论首屏 bootstrap:
GET /qingyan/api/comments/bootstrap - 评论线程分页:
GET /qingyan/api/comments/thread - 评论创建、投票、验证码验证
- bootstrap 返回
features能力开关、回复提醒defaultChecked和data.comments.form.allow / require / limits,前端可先按能力判断再动态渲染nickname | email | website必填项与输入长度 - 页面点赞
- 后台登录(管理员登录验证码 + 5 次失败永久封禁 IP)
- 后台评论审核、黑名单、白名单、页面管理、用户管理、访客管理、站点总览、站点设置、系统设置
- 后台通知页提供三条评论邮件静态诊断和 QingYan 内置双链路真实邮件测试
- 本地
logs/access与logs/app双通道日志 - 文本
.log+ 结构化.jsonl双格式落盘 - 后台可动态调整日志等级和保留天数
- 默认浏览器后台入口:
GET /qingyan/admin - SQLite + Drizzle migration 基线
- Docker / Compose 本地或单机部署
pnpm install首次部署推荐让 QingYan 进入 install mode,而不是手写完整业务配置。
pnpm dev:api当 config/qingyan.yml 不存在时,后端会启动 minimal install app,并在终端输出一次性安装地址:
install.url=http://127.0.0.1:4401/qingyan/admin/install
浏览器访问 /qingyan/admin/ 或 /qingyan/admin/install 完成安装。安装 token 由 install page 通过 HttpOnly cookie 处理,不显示在 URL 或页面正文中。
安装流程会生成 startup config、初始化 SQLite、写入管理员 bootstrap、默认站点、站点设置、完整默认系统设置,并在 startup config 同目录写入 qingyan.installed.lock 安装锁。安装完成后按 QINGYAN_INSTALL_TRANSITION_MODE 切换到正常服务;默认 reload_in_process 会在同一进程内启动正常服务,Docker Compose 可使用 exit_for_supervisor 退出后由 restart policy 拉起,受限运行时可使用 manual 提示人工重启。安装锁存在时不会启动 install app,正常后台中的 ${server.publicPath}${admin.consolePath}/install 只返回已关闭提示,不作为后台 SPA 路由。安装期间不启用管理员登录,/qingyan/admin 会跳转到安装页,正常 /qingyan/api/* 接口不会注册;安装完成后的外部后台入口由 server.publicPath + admin.consolePath 决定。
需要指定配置路径或安装 token 时可使用:
QINGYAN_CONFIG_PATH=./config/qingyan.yml QINGYAN_INSTALL_TOKEN=change-me pnpm dev:api安装完成切换方式可用 QINGYAN_INSTALL_TRANSITION_MODE=reload_in_process|exit_for_supervisor|manual 调整。Web 安装接口不会调用 qyctl、systemctl 或任意外部 shell 命令。
startup 环境变量会覆盖安装表单中对应字段,并在安装计划中标记来源。secret 环境变量只显示“已配置”;当前支持把 QINGYAN_SMTP_PASSWORD 与 QINGYAN_TURNSTILE_SECRET_KEY 作为首装 seed 写入 system_settings,响应、Admin system settings 和普通 QingYan export 都不会返回明文。
pnpm config:check
pnpm config:check:localpnpm db:generate
pnpm db:migratepnpm dev默认监听地址:
- API:
http://localhost:4401 - Admin:
http://localhost:5173/qingyan/admin - OpenAPI JSON:
http://localhost:4401/qingyan/openapi.json - OpenAPI YAML:
http://localhost:4401/qingyan/openapi.yaml - API Docs:
http://localhost:4401/qingyan/docs
pnpm dev 会同时启动后端 API 和 Admin Vite 开发服务。Admin 开发服务会按已安装的后台入口生成主入口,并把 /qingyan/api/* 代理到后端。dev mode 下 /qingyan/admin/ 始终作为额外开发入口可用;如果安装时生成或设置了其他后台路径,/qingyan/admin/ 和该路径会同时可用,/qingyan/admin/ 不会在非 dev 启动中开放为别名。只需要单独启动后端时使用 pnpm dev:api;只调试 Admin 前端时可使用 pnpm dev:admin。
pnpm dev 默认启用快速开发模式,Admin 登录固定为:
username: admin
password: admin
captcha: 2468
需要临时覆盖时可设置:
QINGYAN_DEV_ADMIN_USERNAME=admin QINGYAN_DEV_ADMIN_PASSWORD=secret QINGYAN_DEV_CAPTCHA_ANSWER=1357 pnpm dev后端启动完成后会在 shell 中输出:
admin.console.url=...
admin.username=...
admin.password=...
构建后会提供两个等价入口:
qingyanctl help
qyctl helpDocker 镜像内同样提供 qyctl 和 qingyanctl。它们用于运维、备份、恢复、升级和重置后台信息;首次安装完成后的 Web 切换不依赖 CLI 存在。
qyctl info 用于快速查看服务状态、控制台入口、管理员用户名、配置文件和数据库位置,不显示管理员密码。数据库只保存密码 hash;如需重置密码,使用:
qyctl admin repass
qyctl admin repass <password>后台入口可通过以下命令重置;不传路径时自动生成随机入口:
qyctl admin entrance
qyctl admin entrance /hidden-admin站点级 JSON 导入导出只用于评论、页面线程、访客、站点设置等业务数据迁移,不是整站备份:
qyctl export default ./site.json
qyctl import default ./site.json --dry-run整站备份使用 qyctl backup,包含数据库完整备份、配置文件、安装锁和 manifest。恢复计划可用 qyctl restore <backup> --dry-run 检查。qyctl upgrade 只负责数据升级;Docker Compose 的备份、Release 切换、镜像构建、UpgradePlan 确认和健康验收统一由 scripts/update.sh 编排。
程序更新检测基于当前仓库的 GitHub Release:
qyctl update check
qyctl update planupdate check 只检测 Virace/QingYan published release,不修改程序。Docker Compose 部署在仓库根目录运行一个入口即可完成更新:
./scripts/update.shv0.2.2 之前的 checkout 尚无该脚本,可使用固定版本的远程入口完成首次更新:
curl -fsSL https://raw.githubusercontent.com/Virace/QingYan/v0.2.3/scripts/update.sh | bash -s --脚本默认选择最高的稳定 release tag,依次执行预检、整站备份、镜像构建、UpgradePlan 确认、数据升级和健康验收;可显式传入目标 tag,并用 --yes 接受两次确认。生产侧修改过的 compose.yml 和未跟踪运维文件会被安全暂存并在新 tag 上恢复,其他已跟踪源码改动仍会阻止更新。
在 pnpm dev 下,这里会输出当前开发账号和密码,方便直接登录。即使安装时随机生成过管理员用户名和密码,dev mode 也会临时注入开发账号;非 dev 启动时,管理员入口、用户名和密码 hash 来自数据库 bootstrap 状态,安装完成页会显示一次性初始密码。
- 完整配置说明见 docs/configuration.md
- startup config 示例见
config/qingyan.example.yml - 本地实参默认使用
config/qingyan.yml - 站点、站点设置、系统设置由数据库持久化,后台管理端维护
- 首个正式版本
v0.1.0已发布;公开输入由站点级上限约束,后续破坏性升级必须走 upgrade lifecycle - 应用层滥用保护和自动黑名单可在 Admin Console 关闭;关闭时建议由 WAF、反向代理、CDN 或 API 网关承担更强限流与拦截
- 黑名单和白名单均在 Admin Console 的安全规则中管理;白名单只影响黑名单/自动黑名单优先级,不绕过页面状态、功能开关、验证码、基础限流或输入校验
- 普通 QingYan export 不包含 SMTP / captcha secret,迁移 secret 需通过环境变量、Admin Console 重新输入,或等待未来 full backup/restore 模式
- release 后破坏性升级规则由项目规范维护;开发过程设计 / 计划文档保存在仓库外
E:\Project\Docs\Web\QingYan
从 v0.1.0 起,已有实例升级必须走明确的 upgrade lifecycle。如果启动时检测到 upgrade_required,QingYan 会进入 Web Upgrade Mode,而不是启动正常评论 API 或 Admin Console。终端会输出:
upgrade.url=http://127.0.0.1:4401/qingyan/upgrade
upgrade.state=upgrade_required
upgrade.token=qy_upgrade_<一次性随机值>
浏览器访问 /qingyan/upgrade 可查看脱敏后的 UpgradePlan,输入同一段启动日志中的升级 token,并在确认备份路径和风险后输入 UPGRADE QINGYAN 执行升级。升级写入前会先创建 SQLite 数据库备份、startup config 备份和公开 UpgradePlan 备份;失败时保留 partial marker,下次启动进入 recovery_required,不会继续启动正常服务。
CLI 仍是底层运维入口,适合服务器、Docker、CI 或 Web 无法启动的场景:
pnpm qingyan:upgrade -- --dry-run --config config/qingyan.yml
pnpm qingyan:upgrade -- --apply --config config/qingyan.yml --backup-dir ./backup/upgrade--dry-run 只输出脱敏后的 UpgradePlan,不写配置、SQLite 或 upgrade ledger。--apply 必须显式提供 --backup-dir,并与 Web Upgrade Mode 复用同一个升级执行服务。
- 规格文件:
docs/openapi.yaml - 运行时 YAML:
GET /qingyan/openapi.yaml - 运行时 JSON:
GET /qingyan/openapi.json - 文档页:
GET /qingyan/docs
公开 OpenAPI 只覆盖内容站点前端会直接调用的评论、验证码、页面反馈接口,以及 Web Upgrade Mode 最小接口。Admin Console Web 使用的 /api/admin/* 管理接口不进入公开 OpenAPI;开发者调试或扩展内置后台时可参考 docs/admin-console-api.md。
评论邮件不是一个总开关,至少有三层独立配置:系统邮件与 SMTP、普通评论者回复邮件、 后台用户通知与站点人员 route。只打开其中一层,不代表另外两条链路已经可发送。
在 Admin Console 的“站点设置 → 通知”中可以:
- 查看待审核评论、直接发布评论和评论回复三条 email flow 的 saved-config blocker;
- 设置公开评论框“回复提醒”是否默认勾选;
- 输入一个评论者邮箱,使用 QingYan 内置测试页走真实 planner → queue → worker → template → email adapter 链路。
真实测试第一条线路把评论 A 发送给当前匹配的站点人员 email route,第二条线路模拟站点人员
回复并通知评论 A 的用户;不要求内容站点创建页面或提供前端。结果中的 passed / sent
表示邮件服务商接受了发送请求,不等于邮件已经进入收件箱,最终仍需核对两个收件箱、垃圾邮件
和服务商退信。
docker build -t qingyan:local .docker compose up --build默认 Compose 会:
- 暴露
4401:4401 - 挂载
./config:/app/config - 挂载
./data:/app/data - 设置
QINGYAN_INSTALL_TRANSITION_MODE=exit_for_supervisor,安装完成后交给 Compose restart policy 拉起正常服务 - 使用
/qingyan/healthz做健康检查
drizzle/ 目录需要入仓库。
原因很简单:它不是缓存,而是当前数据库结构的 migration 基线。没有它,就无法稳定重建 SQLite schema,也无法保证不同环境使用的是同一版数据库结构。
常用命令:
pnpm db:generate
pnpm db:migrate
pnpm db:studiopnpm typecheck
pnpm test
pnpm test:smoke
pnpm test:system
pnpm build
pnpm check
pnpm check:system
pnpm dev:api
pnpm dev:adminpnpm check 会串行执行格式检查、lint、typecheck、测试、构建和示例配置校验。
pnpm test:system 会运行 API/SQLite 冒烟链路与 Admin Chromium E2E;
pnpm check:system 在完整静态与构建门禁后继续运行该系统测试。
真实邮件发送使用显式的 pnpm test:smoke:commenter-email,不会进入默认门禁。
需要快速联调验证码、评论 seed 或后台场景时,可使用:
pnpm devpnpm dev 会自动启用 dev mode,并提供固定开发管理员账号:
username: admin
password: admin
可选环境变量:
QINGYAN_DEV_ADMIN_USERNAME=admin
QINGYAN_DEV_ADMIN_PASSWORD=admin
QINGYAN_DEV_CAPTCHA_ANSWER=2468
QINGYAN_DEV_ADMIN_TOKEN=dev-token
QINGYAN_DEV_ALLOWED_ORIGIN=http://localhost:4321只启动后端且需要 dev mode 时:
QINGYAN_DEV_MODE=true pnpm dev:api如果只需要运行内置 mock,不希望连接或初始化 SQLite,可使用无数据库模式:
QINGYAN_DATABASE_MODE=none QINGYAN_DEV_ADMIN_TOKEN=dev-token pnpm dev无数据库模式会自动启用 dev mode,并使用进程内存状态提供完整 dev mock 控制面和前台业务 API;进程重启后 mock 状态会丢失。
dev mode 只新增 /qingyan/api/dev/* 控制面;正常业务接口仍然是 /qingyan/api/* 与 /qingyan/admin。前端在 dev mode 下依然需要显式传 siteKey: "default"。
更详细的下游联调方式、场景调用顺序、错误处理与 UI 对接建议见 docs/dev-mode-integration.md。
