From bdb38970e0122db3a7f085079f8bfcde87302710 Mon Sep 17 00:00:00 2001 From: QiuSW Date: Tue, 15 Sep 2026 16:27:36 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E6=96=B0=E5=A2=9E=E9=83=A8=E7=BD=B2?= =?UTF-8?q?=E4=B8=8E=E8=BF=90=E7=BB=B4=E9=A1=B5=E9=9D=A2=E6=98=A0=E5=B0=84?= =?UTF-8?q?=20(#15)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 从 docs/templates/deployment.md 建立 Deployment-and-Operations 页面映射,由 harness.py sync 导出核心镜像。 --- docs/11-deployment-and-operations.md | 197 +++++++++++++++++++++++++++ wiki-docs.json | 4 + 2 files changed, 201 insertions(+) create mode 100644 docs/11-deployment-and-operations.md diff --git a/docs/11-deployment-and-operations.md b/docs/11-deployment-and-operations.md new file mode 100644 index 0000000..ba2af3c --- /dev/null +++ b/docs/11-deployment-and-operations.md @@ -0,0 +1,197 @@ + +generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件) +wiki_page: Deployment-and-Operations +wiki_url: https://git.ilapage.cn/OPC/lexgo/wiki/Deployment-and-Operations.- +wiki_revision: 2b08a33c0c246841deb52450d7fa411d76dc2eee +synchronized_at: 2026-09-15T08:25:07Z + + +# 部署与运维 + +本页面向 LexGo 的内部维护者:在一台干净的机器上首次部署、日常运维、健康检查、升级回滚与备份恢复。所有命令以仓库根为工作目录。**本页只写配置项名称与来源,不写任何真实密码、令牌或生产数据库地址。** + +## 本页用途 + +让维护者在一台新机器上完成首次部署,并在需要时把实例从备份恢复回来,同时知道哪些步骤不可逆、哪些证据必须留下。 + +## 安全边界 + +- 凭据只从运维密码库或 `.env.local`(已被 `.gitignore` 忽略)读取;`.env.local` 权限应为仅本人可读写。 +- 备份文件含全部用户数据(账号、书籍原文、词条、复习记录、审计日志、词典归档),必须按个人数据对待:存放位置受控、不进入 Git、不进入工单附件。 +- `restore` 会写入数据库:默认只写空库,覆盖已有库必须显式 `--force`,任何情况下都必须 `--confirm`。 +- 服务默认只监听 `127.0.0.1`;对外提供访问时经反向代理,不要直接暴露应用端口。 + +## 服务概览 + +| 项目 | 内容 | +|---|---| +| 组件 | Go 后端(`server/`)、学习端 SPA(`learner/`)、管理端 SPA(`admin/`)、MySQL 8 | +| 后端监听 | `LEXGO_LISTEN`,默认 `127.0.0.1:8000` | +| 前端托管 | 反向代理(nginx)托管已构建的 `dist`,同一域名下把 `/api/` 转发到后端 | +| 数据库 | MySQL 8(本机验证版本 8.4.3);库名经 `LEXGO_DB_NAME` 指定 | +| 运行时 | 后端为单个静态二进制,无需运行时依赖;构建需要 Go 1.26.5、Node 22、pnpm 9 | +| 进程托管 | supervisor(本项目开发机即为 `lexgo-api` / `lexgo-learner` / `lexgo-admin` 三个 program) | +| 日志 | supervisor 的 `stdout`/`stderr` 日志文件;应用自身不写文件日志 | +| 备份对象 | MySQL 全库(用户数据、原文、词典归档、审计)+ `.env.local`(单独从密码库取) | + +## 环境要求 + +| 组件 | 版本要求 | 检查命令 | 预期结果 | +|---|---|---|---| +| 操作系统 | Windows 或 Linux | — | 本项目在 Windows 开发机验证;Linux 步骤为等价命令 | +| MySQL 服务端 | 8.x | `mysql --version`(服务端 `SELECT VERSION()`) | 8.4.3 已验证 | +| MySQL 客户端 | **不低于服务端** | `python scripts/ops.py install-check` | 「客户端版本不低于服务端」为 ok;5.7 客户端连 8.4 服务端会被判 fail | +| Go | 1.26.5 | `go version` | 供 `scripts/server.py` 固定工具链构建 | +| Node / pnpm | Node 22、pnpm 9 | `node --version`、`pnpm --version` | 仅在需要构建前端时要求 | +| WordNet 资源 | `server/wordnet-resource.json` 固定的 ZIP | `python scripts/ops.py install-check` | 「WordNet 资源 pin」为 ok;资源只在显式导入时使用,不在运行时下载 | + +## 首次部署 + +### 1. 取得代码并检查依赖 + +```bash +git clone <仓库地址> <部署目录> +cd <部署目录> +git rev-parse HEAD # 记录本次部署的提交哈希 +python scripts/ops.py install-check +``` + +预期结果:提交哈希被记录到部署记录;`install-check` 全部 `[ok]`,否则按其提示补齐后再继续。 + +### 2. 创建数据库与配置 + +```bash +python scripts/ops.py init-database --database lexgo_prod +``` + +预期结果:输出「已创建库」并打印需要授予的最小权限(`SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, INDEX, DROP, REFERENCES`,仅限该库)。不要用管理员账号运行应用。 + +把 `.env.local`(从 `.env.example` 复制)填好:`LEXGO_DB_HOST/PORT/NAME/USER/PASSWORD`、`LEXGO_BOOTSTRAP_USERNAME/PASSWORD`、`LEXGO_LISTEN`。凭据值取自运维密码库,不进 Git、不进日志。 + +### 3. 迁移与初始化管理员 + +```bash +python scripts/server.py migrate # 只有这一步会改表结构 +python scripts/server.py bootstrap # 首次建立唯一管理员 +``` + +预期结果:`migrate` 输出 schema 版本;`bootstrap` 输出创建成功。**库中已有账号时 `bootstrap` 会拒绝执行**,不会覆盖既有管理员。空库不含默认密码与任何演示数据。 + +### 4. 构建产物 + +```bash +python scripts/server.py build # 产出 server/lexgo(.exe) +cd learner && pnpm install && pnpm run build +cd ../admin && pnpm install && pnpm run build +``` + +预期结果:后端二进制与两份 `dist` 生成。前端由反向代理托管;反向代理需把未知路径回落到 `index.html`(SPA 路由),并把 `/api/` 转发到后端。 + +### 5. 词典资源 + +词典归档在显式导入后存于 MySQL,备份与恢复会一并带走,运行时不下载。 + +### 6. 启动与健康检查 + +按 supervisor 配置启动三个 program(后端 + 两个静态站点或由反向代理托管)。然后执行下方「健康检查」全部命令。 + +## 配置与凭据来源 + +| 配置项 | 用途 | 来源 | 是否敏感 | +|---|---|---|---| +| `LEXGO_DB_HOST/PORT/NAME/USER/PASSWORD` | 数据库连接 | `.env.local`,值取自运维密码库 | 是(密码) | +| `LEXGO_BOOTSTRAP_USERNAME/PASSWORD` | 首次建立管理员 | 同上 | 是(密码) | +| `LEXGO_LISTEN` | 后端监听地址 | `.env.local` | 否 | +| `LEXGO_MYSQL_BIN` | 指定 MySQL 客户端目录 | 运维环境变量 | 否 | +| `LEXGO_TEST_DB_NAME` | 集成测试库(仅开发) | 环境变量 | 否 | + +`scripts/ops.py` 的 manifest 与日志只记录配置项**名称**、库名、schema 版本与提交哈希,不记录任何凭据值。 + +## 日常运维 + +| 操作 | 命令 | 预期结果 | +|---|---|---| +| 查看状态 | `supervisorctl status lexgo-api` | `RUNNING`,uptime 持续增长 | +| 重启后端 | `supervisorctl restart lexgo-api` | `stopped` 后 `started` | +| 查看日志 | `supervisorctl tail -f lexgo-api stderr` | 持续输出应用日志 | +| 备份 | `python scripts/ops.py backup --out <目录>` | 生成 `lexgo-<时间戳>.sql.gz` 与 `manifest.json` | +| 校验实例 | `python scripts/ops.py verify --database <库名>` | 打印每项检查结果,最后「校验通过」 | +| 清理过期审计 | `python scripts/server.py audit-cleanup` | 只清理两张审计表中超过 90 天的记录 | +| 关闭确认 | `supervisorctl stop lexgo-api` | 服务停止;学习端与反代仍在,接口不可用 | + +## 健康检查 + +```bash +supervisorctl status lexgo-api +curl -sS -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8000/healthz +python scripts/ops.py verify --database <库名> +``` + +预期结果:状态 `RUNNING`;`curl` 返回 `200`;`verify` 全部 `[ok]`。任何一项不符合时不视为部署成功,按「升级与回滚」处理。 + +## 升级与回滚 + +### 升级 + +```bash +git rev-parse HEAD # 记录升级前提交,回滚需要 +python scripts/ops.py backup --out <目录> # 有迁移时必须先备份 +git fetch --all && git checkout <目标提交或标签> +python scripts/server.py build +cd learner && pnpm install && pnpm run build && cd ../admin && pnpm install && pnpm run build +python scripts/server.py migrate # 只有迁移单需要 +supervisorctl restart lexgo-api +``` + +预期结果:重启后状态 `RUNNING`,健康检查全部通过。前端 `dist` 更新后由反代直接生效,无需重启后端。 + +### 回滚 + +```bash +git checkout <升级前记录的提交> +python scripts/server.py build +supervisorctl restart lexgo-api +``` + +预期结果:健康检查全部通过。 + +> **注意**:已执行的迁移通常不能用切回代码撤销。LexGo 的用法是:每个版本的迁移只新增对象(`CREATE TABLE IF NOT EXISTS`),旧二进制认版本号,因此回退到旧二进制前要把 `lexgo_schema.version` 写回旧版本号;若迁移删改了数据或旧二进制无法读写新结构,则按「备份与恢复」处理。**本节只描述规则,真实回滚尚未演练过,属于已知限制。** + +## 备份与恢复 + +### 备份对象与频率 + +- **数据库**:一个实例的全部持久数据(账号与会话、书籍与章节原文、导入任务、词典归档、词条、复习排期与作答、阅读进度、审计日志)。 +- **环境配置**:`.env.local`(含凭据)单独从运维密码库保存,**不放进备份目录**。 +- **代码与二进制**:由 Git 提交哈希重建,manifest 中记录了该哈希。 +- 建议频率:每次升级或有迁移前必须备份;日常按使用强度自行决定(本项目未启用定时任务)。保留份数与存放位置由运维决定。 + +```bash +python scripts/ops.py backup --out <备份目录> +``` + +预期结果:目录中出现 `lexgo-<时间戳>.sql.gz` 与 `manifest.json`(schema 版本、提交哈希、逐表行数、dump 的 sha256、客户端与服务端版本)。manifest 不含凭据。 + +### 恢复步骤 + +```bash +python scripts/ops.py restore --dump <备份目录>/lexgo-<时间戳>.sql.gz --database <新库名> --confirm +python scripts/ops.py verify --database <新库名> --manifest <备份目录>/manifest.json \ + --api http://127.0.0.1:<演练端口> --user <账号前缀> --password-env <密码环境变量名> +``` + +预期结果:恢复写入空库、自动校验通过;`verify` 打印完整性与两账号隔离检查,最后「校验通过」。恢复**不会**写入备份里的源库,工具会在恢复前后比对源库的内容校验和(本机演练已验证)。 + +> **注意**:`restore` 默认拒绝写入已有数据的库;覆盖必须 `--force`,执行前先备份当前库。库名必须包含 `lexgo` 且不能是 MySQL 系统库。 + +### 恢复演练记录 + +2026-09-15 在本机完成完整演练:空库安装(init-database → migrate → bootstrap → 管理员建两个演练账号 → 走通粘贴/阅读/查词/保存/复习/完成章节/进度)与「备份 lexgo_dev → 恢复到空库 → 起第二个 API 实例 → 两账号闭环与越权校验」,恢复后逐表行数与内容校验和都和源库一致,演练库用后删除。详细命令与结果见工单 #15 与本地开发页。 + +## 已知限制 + +- **真实回滚演练未做**:本文档给出规则,但没有在真实实例上执行过「升级 → 回滚」全过程。 +- 未验证 HTTPS、域名、多机与灰度部署;本机演练只用 `127.0.0.1` 与模拟触摸视口。 +- 未启用定时备份、监控与告警;备份由人工触发。 +- **附件(封面/音频)尚未实现(工单 #21)**:本页的备份恢复范围目前只覆盖数据库;#21 落地后必须把附件存储纳入备份与恢复验收。 +- 恢复演练是单机顺序执行,未验证大库恢复耗时与磁盘空间上限。 diff --git a/wiki-docs.json b/wiki-docs.json index 450f275..4496343 100644 --- a/wiki-docs.json +++ b/wiki-docs.json @@ -83,6 +83,10 @@ { "page": "Go-Analysis-Alternative", "path": "docs/linguacafe-go-analysis.md" + }, + { + "page": "Deployment-and-Operations", + "path": "docs/11-deployment-and-operations.md" } ] }