工程实践

把博客评论留在自己的服务器上

静态站点没有后端也能有真评论:一个零依赖Node服务、一个JSON文件、一份前端组件,数据完全自己掌握。

静态博客加评论,通常有两个极端:接第三方服务要注册账号、要加载外部脚本、数据不在自己手里;自己搭一套完整后端又太重。其实中间还有一条很窄但够用的路。

需求先砍到最小

个人博客的评论只需要四件事:按文章取列表、提交一条、不被人灌爆、数据能自己导出。至于登录、点赞、无限嵌套、邮件通知,都不是刚需。

于是后端可以只是两个接口:

GET  /api/comments?slug=xxx
POST /api/comments

数据存一个JSON文件,进程内用队列串行化写入,写临时文件再原子rename。这个量级下,文件存储比引数据库更省事,备份就是拷一个文件。

服务同时托管静态产物

既然已经有一个Node进程,让它顺带把dist/目录发出去,页面和API同源,CORS、混合内容、额外域名全都省了。

const server = http.createServer((req, res) => {
  const url = new URL(req.url, `http://${req.headers.host}`);
  if (url.pathname.startsWith('/api/')) return handleApi(req, res, url);
  return serveStatic(req, res, url);
});

静态部分只需处理三件事:路径穿越校验、目录补index.html、按文件类型给缓存头。HTML短缓存、带哈希的构建资源长缓存即可,压缩交给前置Nginx。

必须挡在服务端的事

前端做校验只是体验,后端才是边界:

  • 限制请求体大小,超过直接断开;
  • 昵称、内容长度设上限,去掉控制字符;
  • 蜜罐字段:表单里放一个对真人隐藏的输入,机器人填了就静默丢弃;
  • 按IP做简单时间窗限流,例如60秒最多3条;
  • 邮箱只存不回传,列表接口里剔除隐私字段;
  • 评论内容一律当纯文本存,前端用textContent渲染,绝不拼innerHTML。

最后一条是XSS的关键。评论属于典型的不可信输入,任何”支持一下Markdown”的想法,都要先想清楚转义链路。

前端做成可插拔

把评论组件和后端解耦,配置里给三种模式:

模式适用场景
self-hosted有自己的服务器,同源部署
local只上传静态文件,留言存在浏览器本地,用于演示
waline想用现成托管评论服务

接口失败时给明确提示而不是静默空白,这样部署方式变了也能快速定位。

这套东西的价值不在功能多,而在于没有外部依赖:不注册第三方账号、不加载境外CDN、不担心服务关停,删库就是删一个JSON文件。对已备案的个人站点来说,这点尤其省心。

评论 0

评论保存在本站服务器,无需登录。带「作者」标识的是站主回复,请友善交流。

    评论内容至少2个字、最多1000字,按纯文本展示,不支持HTML。