平台工程

静态站接账号系统 · 内容归 git、状态归库的双源切分

入档:2026-07-28 来源:蛛网之上(tiaozhuxiansheng.com)账号系统上线 + 任务书状态完全动态化,单会话从零到线上(above-the-web 仓库 a17900264c1044) 状态:已上线并线上验证;后端流转回归 11 项、本地真浏览器 15 项、线上访客 9 项全过

一句话总结

静态站要「动起来」,第一步不是把渲染模式升级成 SSR,而是把内容源和状态源切开——正文继续归 git 走构建期,只把真正会变的那几个字段交给数据库,前端做客户端注水。切对了,部署链路一行都不用改,服务没上线之前站点也照常可读。

背景与事实

五条可复用 insight

1. 双源切分律(核心)

「让静态站动起来」的正解是切数据源,不是升级渲染模式。 先把字段按「会不会在运行时变」分成两堆:

归 git 的 markdown归数据库的运行时状态
正文、要求、对标素材招募中 / 进行中 / 完工待打款 / 已收官
标题、报酬、截止时间、发布日期谁申请了、定给谁、交付链接、打款时间、流转记录

两边靠一份构建期导出的清单对接:Astro 的 src/pages/tasks/index.json.js 把每份任务书的 frontmatter 导出成 /tasks/index.json,随 dist 一起 rsync 到服务器;服务定时读同机文件(不走网络)做 upsert。

关键约束是同步只写正文类字段,运行时状态一律不碰;markdown 里的 status 只在第一次入库时当初值。md 里删掉的任务标记下架、页面不再展示,但认领与打款记录不删。

收益:发任务照旧写 md 然后 push(写作习惯与 git 留痕都保住),认领流转在站内实时生效;而 CI、Pages、rsync 这套部署链路一行没改。转 SSR 要重做 CI、放弃 Pages、让 Node 扛全站流量,代价和收益完全不成比例。

2. 降级要设计成默认路径,不是异常分支

注水脚本的 catch 里什么都不做:拉不到 API,页面就保持构建快照,正文照读、状态显示的是上次构建时的值。

这条的价值在上线当天兑现:代码 push 完、服务在服务器上跑起来了,但 nginx 还没接,/api/ 全是 404——站点在那段时间里完全正常。这不是运气,是”失败=回到静态”这个设计换来的。同理适用于一切给静态站挂后端的场景:先让页面在后端不存在时也成立,再让后端锦上添花。

3. 能搬运的 DOM 才能重排

状态动态化意味着卡片要在「招募中 / 进行中 / 已收官」之间移动。原设计里招募中是卡片(<a class="task-card"> 带标题摘要),已收官是 <ol><li> 细线目录——两种 DOM,跨组移动就得重建节点,注水脚本立刻复杂化。

正解:统一成一份卡片 DOM,视觉差异全部交给 CSS——归档组用 display: contents.tc-top / .tc-meta 的嵌套摊平进父级 grid,再隐藏摘要与 CTA,同一份 DOM 就压成了细线目录体,和原来几乎一样。

视觉差异该由 CSS 承担,不要由 DOM 结构承担。 结构一分叉,运行时重排就寸步难行。这条在任何「同一批数据要按状态分区展示」的页面上都成立。

4. scoped CSS 不覆盖运行时注入的 DOM ⚠️

Astro 的 <style> 是 scoped 的,实现方式是给构建期渲染的元素打 data-astro-cid-* 属性、选择器带上同样的属性。JS 用 innerHTML 注入的元素没有这个属性,scoped 样式对它整段失效。(Vue / Svelte 的 scoped 同理。)

坑的形态:不报错、不告警,只是”样式莫名其妙没生效”,很容易误判成选择器写错或缓存问题。

处置:凡是会被 JS 注入的类名,样式一律写进全局表,并在全局表里就地写明原因,免得后来者按”组件样式该跟组件走”的直觉搬回去。本次把认领面板、时间线、我的认领列表、管理台卡片这四组样式全部下沉到 global.css,组件里只留纯静态部分。

5. 登录墙架在钱前面,不是架在门口

产品判断:全站开放注册、读站不拦任何人,只有认领任务这一个动作要求登录。理由不是”降低门槛”这种泛泛之谈,而是登录墙的位置应该对齐它真正保护的东西——这里要保护的是”钱付给谁”的对应关系,那么墙就架在认领动作前,而不是架在网站入口。

配套的隐私边界:联系方式与收款方式由用户自己填,只有本人和发布方可见,不进任何公开接口;打款仍走微信手动转账,站内不接支付、不存支付凭证,只记「什么时候标记了打款」。

内测反馈渠道上线_匿名兜底与422探针验证_v1 的「入口可用性要求必须低于它所服务的故障面」是同一族判断的两个方向:反馈入口要比故障面更低,支付相关入口要比资金流更高。

部署自举:CI 做幂等安装,人只做机器不该做的那一步

首次部署不该要求人去服务器敲一串命令,但也不该把改线上 nginx 塞进 CI。本次的切法:

四个只有真部署才会暴露的坑

本地 26 项测试全绿,推上去照样连挂两次。这四个坑本地一个都碰不到:

a. .gitignore.env.* 会吞掉 .env.example 本地跑得好好的(文件就在工作区),服务器首次安装失败在”没有模板可 cp”。凡是要进库的配置模板,必须显式加否定规则:!platform/server/.env.example

b. cp -a src/server dst/dst/server 已存在时会复制成 dst/server/server 安装脚本第一次跑正常,第二次就套娃——幂等性要按”重跑一次”来验,不能按”跑一次成功”来验。正解是复制目录内容:cp -a src/server/. dst/server/

c. nginx sites-enabled/ 下的任何文件都会被加载,不看后缀。 把配置备份写在那个目录里,备份副本里的 listen 443 会和正本撞车:duplicate listen options for [::]:443,校验直接失败。备份要放到目录外(/root/)。注意 sites-available/ 里放备份是安全的——那个目录不被加载,所以”以前一直这么备份没事”会给人错误的安全感。

d. sites-enabled/ 里可能是实体文件而不是软链,且与 sites-available/ 同名文件早已不同步。 本次现场:同目录另外三个站点都是软链,唯独主站是 -rw-r--r-- 实体文件;sites-available/ 里那份是旧副本,连一个月前上线的 /vacat/ 都没有。按常规去改 available 会白改一轮,且 nginx -t 还会通过(它校验的是真正生效的那份)。判据一眼可见:ls -l /etc/nginx/sites-enabled/ 看有没有 ->

两条顺手教训

关联文档

类型/平台工程