65 lines
7.5 KiB
Markdown
65 lines
7.5 KiB
Markdown
# AGENTS.md
|
||
|
||
RainWeb 个人云平台(博客 + 论坛 + 密码管理器 + 管理面板聚合)。Node.js 22+(推荐 22 LTS)、CommonJS、Express 4、better-sqlite3(原生 SQLite)。前端 **React + Vite + React Router**(前台原生样式 Material Design 3,后台 MUI 独立应用)。**无测试、无 lint**——前端改动进 `frontend/src/`,构建产物 `public/dist/`(gitignored)由 server 静态服务,部署必须 `npm run build`。
|
||
|
||
## 常用命令
|
||
|
||
```bash
|
||
npm start # 生产:node server.js(serve 构建产物 + /api)
|
||
npm run dev # 开发:vite dev server(5173,/api 与 /uploads 代理到 3101)
|
||
npm run build # 构建 React 前端 → public/dist/(index.html + admin.html + assets)
|
||
npm run preview # 预览构建产物
|
||
node cli.js <cmd> # status | health | db-check | start | stop | restart | port [N] | password | captcha | config | backup | upgrade
|
||
```
|
||
|
||
- 端口:`.env.json`(gitignored)`{"port": N}` 或环境变量 `PORT`,默认 3001;vite 代理目标硬编码 3101(vite.config.js),后端端口改了就同步改。
|
||
- CLI:`status`、`health`、`db-check`、`config`、`backup list` 支持 `--json`;`health`/`db-check` 检查失败返回非 0。`backup prune` 必须明确 `--keep N`,默认只预览,只有 `--yes` 才删除;`backup restore <file>` 必须服务停止并带 `--yes`,不会默认覆盖。
|
||
- CLI 的 `start/stop/restart` 只操作经过 cwd 与命令行双重校验、确认属于本项目 `server.js` 的 PID,停止会先优雅等待。`upgrade` 仅供自托管机器本地执行:升级前备份,要求 git 工作区干净,执行 `git pull --ff-only`、`npm install`、`npm run build`,服务原本运行时重启并健康检查;失败返回非 0。
|
||
- CLI 密码命令默认交互输入;`config` 对密码、secret、token、JWT、私钥和 API key 等敏感配置统一脱敏。验证码参数需使用受支持的类型和值。
|
||
- 验证方式:后端改动启动后 `curl` 接口;前端改动 `npm run dev` 热更新,或 `npm run build` 后刷新验证(需强刷一次,见下)。
|
||
- **部署后浏览器必须拿新 HTML**:`serveIndex` 响应带 `Cache-Control: no-store`;构建产物 assets 文件名带 hash,经 `/assets` 挂载长期缓存(immutable)。若用户白屏且 console 报 "MIME type text/html",先查是否 `npm run build` 缺失/旧产物。
|
||
- 首次启动自动建库播种:管理员 `admin / admin123`,并写入示例博文/论坛帖子;`/setup.html` 初始化向导(`POST /api/setup/complete` 用 `setup_complete` 门禁,非 adminOnly——新装无 token)。
|
||
|
||
## 数据层(db.js)
|
||
|
||
- better-sqlite3 是原生 SQLite:`data/rainweb.db` 单文件持久化,写操作经事务落盘。CLI 与 server 并发写库会等待(busy_timeout 5s)而非互相覆盖,但建议不要同时执行写操作。
|
||
- 建表在 `initTables()`;列迁移用 try/catch 包裹 `ALTER TABLE ... ADD COLUMN`(幂等、静默失败)确保列存在。**新增列必须沿用此模式**,否则旧库会崩。版本化迁移在 `migrateSchema()`:`PRAGMA user_version` 记录 schema 版本,migrations 数组按 version 升序执行(`current < m.version` 才运行并推进版本号)——**新迁移写进 migrations 数组,不要散落 try/catch**。
|
||
- 站点设置存 `site_settings`(key/value),通过 `db.getSetting` / `setSetting` 读写。
|
||
- **cli.js 与 server.js 统一使用 db.js 的 `data/rainweb.db`**;旧版 `data.db` 已由 server.js 首次启动时迁移为 `data/rainweb.db`(旧文件改名 `data.db.bak`)。
|
||
|
||
## 路由(server.js)
|
||
|
||
- 集中挂载所有 `/api/*`(auth、admin-links、announcements、forum、blog、passwords、settings、email、profile、captcha、upload、setup、proxy、import)。**新路由必须在此挂载**——后面有 SPA catch-all:非 `/api/` 一律回 dist/index.html。
|
||
- **挂载顺序关键**:`serveIndex('/')` → `/assets`(dist/assets,immutable)→ static(public) → `/uploads` → `/api/*` → `/admin*`(dist/admin.html)→ SSR 区(/blog/:id 等)→ catch-all。新路由注意别被 catch-all 吞掉。
|
||
- `middleware/auth.js`:Bearer JWT;`SECRET` = `process.env.JWT_SECRET`(**无硬编码回退**——server.js 启动时若 `.env.json` 无 `jwt_secret` 则随机生成写入并设置环境变量);`adminOnly` 会查库复查角色(用户被删/降权立即失效)。
|
||
- `routes/setup.js`:`/complete` 用 `setup_complete` 门禁('1' 后 403),新密码禁止等于默认 `admin123`。
|
||
- `routes/proxy.js`:面板嵌入代理——支持 `Authorization` header 或 `?token=` query(iframe 无法带 header);有内网地址拦截(SSRF)。
|
||
- 验证码:内置 SVG + reCAPTCHA/Turnstile 第三方,服务端统一 `resolveCaptcha` 校验(builtin 走 proof JWT,第三方走 siteverify)。
|
||
|
||
## 前端(React + Vite)
|
||
|
||
- 源码在 `frontend/`:`src/main.jsx`(前台入口)、`src/App.jsx`(前台布局 Layout + 路由表)、`src/pages/`(前台页面)、`src/components/`(Layout/MarkdownRenderer/CaptchaModal/MusicEmbed/BlogSidebar)、`src/admin/`(**后台独立应用** MUI:main.jsx + AdminLayout + pages/)、`src/api/`(按模块封装的 fetch 层,`client.js` 管 token)、`src/lib/utils.js`、`src/theme.jsx`。
|
||
- 前后台是**两个独立入口**(Vite 多入口),仅通过整页跳转连接:前台导航「管理后台」=`<a href="/admin">`(后台路由 basename `/admin`)。
|
||
- 路由保持 v1 的 `.html` 后缀路径(`/blog.html`、`/forum.html` 等),SPA fallback 已支持,链接不用改。
|
||
- `frontend/index.html` 含 `${site_name}` / `${site_description}` / `${site_favicon}` 占位符,构建后由 server.js `serveIndex` 按 site_settings **运行时替换**(务必保留这 3 个占位符)。
|
||
- 主题:`data-theme="dark"` 属性在 `<html>` 上(`public/css/style.css` 的 `[data-theme="dark"]` 选择器),非 `.dark` 类,非 prefers-color-scheme。切换逻辑在 `src/theme.jsx`。
|
||
- 构建:`npm run build` → `public/dist/`(gitignored,不入库)。**生产部署必须构建**,否则页面 500/白屏。
|
||
- 博文/帖子内容中的 `[image:文件名]` / `[file:文件名]` 标签:前台由 `MarkdownRenderer`(marked + DOMPurify 净化)、SEO 页由 `ssr.js` 渲染为 `/uploads/` 链接。**所有 markdown 渲染必须过 DOMPurify**。
|
||
- 登录态:token 存 localStorage(key `token`),变更通过 `authchange` 事件通知 Layout 刷新(`api/client.js` 的 `notifyAuthChange`)。
|
||
|
||
## SSR 与 SEO
|
||
|
||
- `ssr.js`:`/blog/:id`、`/forum/:id`、`/sitemap.xml`(robots.txt 的域名取自 `site_url` 设置)。SSR 页引用 `/css/style.css`(public/css 保留,勿删)。
|
||
- 已发布博文(`published=1`)才会 SSR 渲染,未发布返回 404。
|
||
|
||
## 版本与更新
|
||
|
||
- 版本号在根目录 `VERSION` 文件,与 `package.json` 的 version 需同步。
|
||
- **Web 更新接口已移除**(P0 删除 `/api/update/check`、`/api/update/run`,无 RCE 面)。升级只走自托管机器本地的 `cli.js upgrade`:升级前备份、检查干净工作区、`git pull --ff-only`(本地 origin:git.rainnya.asia 镜像)+ npm install + npm run build + 服务重启/健康检查。
|
||
- 上传附件在 `uploads/`(gitignored,头像在 `uploads/avatars/`,白名单扩展名),壁纸在 `public/wallpaper/`(仅 .gitkeep 入库)。
|
||
|
||
## 约定
|
||
|
||
- UI 文案、代码注释、commit message 均为中文,保持一致。
|
||
- 不要提交:`node_modules/`、`data/*`、`uploads/*`、`.env.json`、`server.pid`、`releases/`、`public/dist/`。
|