个人主页与博客前台的纯静态单页应用,自身不启动任何常驻进程。
- 首页:Hero、About、最新三篇文章、Contact。
- 博客列表
/blog:按标题、摘要、标签在前端本地过滤,并显示文章总数。 - 文章详情
/blog/:slug:marked渲染后经 DOMPurify 消毒;代码块高亮、行号、一键复制。 - 合集列表
/blog/collections与合集详情/blog/collections/:slug,展示合集下的文章与篇数。 - 目录:收集正文里的
h1/h2/h3,锚点用纯序号sec-N,渲染为普通链接列表(不再有侧栏 / 抽屉)。 - 草稿预览:URL 带
?preview=<短时效令牌>时,服务端放行未发布文章,标题区标注 Draft preview。 - 深浅色主题:
auto/light/dark,存在localStorage的site_theme,首帧前落地以免闪回。 - 文章链接优先用雪花 ID
public_id,无public_id的旧文章回退slug。 - 窄屏不隐藏导航:顶栏是普通行内链接,窄屏自动换行。
全站为朴素排版:不引入 UI 库 / CSS 框架 / CSS-in-JS,除正文限宽、图片视频不溢出、代码块与表格横向滚动等最小必要规则外,样式一律交回浏览器默认(约定见 AGENTS.md,组件见 docs/COMPONENTS.md)。
npm install
npm run dev # Vite dev server
npm run build # 构建,产物写入 /var/www/homepage
npm run preview # 预览构建产物本地开发需要博客后端(atrium-blog,:4000)在运行;dev 走相对路径 /api,需自行配置代理。
vite.config.ts 把 outDir 默认写为 /var/www/homepage(可用 BUILD_OUT_DIR 覆盖)且 emptyOutDir: true,构建即部署:npm run build 会先清空该目录,构建后无需重启任何进程。仓库里没有 dist/。
本仓库不持有任何环境变量,代码不读取 import.meta.env.VITE_*,.gitignore 仅拦截本地文件(node_modules/、dist/、data/、*.db、.env)。
接口地址是硬编码的相对路径,见 src/api.ts:
| 名称 | 默认值 | 说明 |
|---|---|---|
API_BASE |
/api/blog |
全部博客接口前缀,同域反代到后端 |
- 形态:纯静态 SPA,无 Node 进程、无 systemd 单元。nginx 直接托管构建产物
/var/www/homepage。 - 同域反代(nginx 层):
/api/blog/*公开读 →127.0.0.1:4000(博客后端atrium-blog)。/api/blog/admin/*与/api/admin/*→ Auth Gateway(127.0.0.1:18920)鉴权后反代,管理员接口由网关注入X-Auth-User。配置里没有auth_request探针。
/admin/:管理后台前端由 nginx 单独挂载,源码不在本仓库(后台前端仓库atrium-console)。本仓库的 SPA 不包含后台代码,也没有后台路由。- 站点域名由部署决定,仓库内不含
server_name。示例统一用example.com。
- 渲染任何来自接口的 Markdown / HTML 前必须经 DOMPurify 消毒,再交给
dangerouslySetInnerHTML。 - 管理接口不做前端鉴权,统一由 Auth Gateway 在 nginx 层拦截并注入
X-Auth-User。 - 详情页唯一的
<h1>是标题区post.title;正文里的h1渲染时一律降级为h2。 - 副标题只读
post.subtitle字段,为空不渲染;不从正文推断。
主站可安装、可离线阅读。Service Worker 是手写单文件 public/sw.js(无构建步骤、无 workbox、无新增依赖),生产构建下由 src/pwa.ts 注册,scope 为 /。
- 缓存白名单:只缓存导航请求(network-first,依次回退同路径缓存、SPA 外壳
/index.html、offline.html)、/assets/*(cache-first)、/hljs/*、/icons/*与图片(stale-while-revalidate)、以及公开只读GET /api/blog/*(stale-while-revalidate,离线读博客的来源)。 - 其余一律不缓存、直接放行:
/api/blog/admin/*、/api/admin/*、/_auth/*、/s/*、/term/*、/admin*、任何非 GET、跨域请求,以及 URL 含preview=的草稿预览请求。 - 更新机制:
install时skipWaiting();activate时删掉所有不属于当前版本的缓存并clients.claim();页面在controllerchange时自动刷新一次,保证重建部署后刷新或重开即可拿到新版本。开发态不注册,并主动注销遗留 worker。 sw.js不得被长缓存:它自身不在 SW 缓存白名单内;部署时 nginx 需对/sw.js下发短缓存(当前对/sw.js、/service-worker.js、/workbox-*.js返回 404 的历史防御需由运维在部署时改掉)。- 图标重新生成:
python3 scripts/gen-pwa-icons.py(依赖本机 PIL,幂等,构图与index.html内联 favicon 一致,产物写入public/icons/)。 - 验证构建必须用独立输出目录:禁止裸跑
npm run build(会清空/var/www/homepage),改用BUILD_OUT_DIR=/tmp/homepage-verify npm run build。
MIT,见 LICENSE。