chore: checkpoint admin restore state

This commit is contained in:
john
2026-06-30 20:26:33 +08:00
parent f2172322fd
commit f9a7584ad4
63 changed files with 7555 additions and 1030 deletions
+117
View File
@@ -0,0 +1,117 @@
# memind_adm 103 收口方案(2026-06-26
## 当前判断
`memind_adm` 是三个仓库里最需要继续做“差异回收”的一个。
已确认:
1. `103` 运行目录:`/Users/john/Project/memind_adm`
2. `103` 目录仍是 Git 工作树,但存在大量工作区漂移。
3. 本地 `test-memindadm``103` 的主要分叉集中在用户详情、空间额度、套餐同步、启动装配。
## 当前高价值差异
### 线上独有
- `server/llm-provider-loader.mjs`
说明:
线上仍保留一层 provider 加载包装逻辑,本地主线已经切到共享实现。
### 本地主线独有
- `server/plan-sync.mjs`
- 用户详情空间字段相关前后端改动
- 生产发布规则与 `release-prod.sh`
说明:
这部分更像你现在真正想推进的新主线能力,不应该被线上旧装配回压掉。
### 双方都改了
- `server/app.mjs`
- `server/bootstrap.mjs`
- `server/index.mjs`
- `src/admin/pages/BillingPage.tsx`
- `src/admin/pages/UserDetailPage.tsx`
- `src/admin/pages/UsersPage.tsx`
- `src/api/client.ts`
- `src/types.ts`
## 收口目标
`memind_adm` 从“线上和本地双向分叉”收口成:
1. 本地 `test-memindadm` 是唯一源码真相。
2. `103` 不再保留未回收的独立逻辑分叉。
3. 用户详情、空间额度、套餐同步、共享用户体系都以本地主线为准。
## 具体执行步骤
### 第一步:锁定必须保留的本地主线能力
这一组应默认保留:
1. 用户详情页独立拉取
2. `spaceQuotaBytes`
3. `spaceUsedBytes`
4. `spaceReservedBytes`
5. `spaceAvailableBytes`
6. 计划同步与同步结果展示
原因:
这组能力与近期真实需求直接相关,不应为了回收线上旧逻辑而退回。
### 第二步:单独审查线上独有装配逻辑
重点只看:
- `server/llm-provider-loader.mjs`
审查目标:
1. 它是否只是旧兼容层
2. 它是否包含线上必需但本地没吸收的行为
处理原则:
1. 如果只是兼容包装,就把必要逻辑回收到本地主线并删除分叉。
2. 如果确有线上必需行为,就先吸收进本地主线,再发布。
### 第三步:不要再把线上旧代码当默认正确答案
`103` 这里的作用只有两个:
1. 证明线上曾经这样跑过
2. 提供必须回收的旧逻辑证据
它不是:
1. 后续开发主线
2. 可继续直接修代码的地方
### 第四步:做第一次本地主线收口发布
1. 在本地完成必要逻辑吸收。
2. 整理成最小 commit。
3.`bash scripts/release-prod.sh` 发布到 `103`
4. 发布后验证:
- `http://127.0.0.1:8085/health`
- `http://127.0.0.1:5174/`
- 真实登录
- `/admin-api/users`
- 用户详情页
- 空间字段展示与更新
## 何时算收口完成
满足以下条件即可视为完成:
1. `server/llm-provider-loader.mjs` 的必要行为已被明确处理
2. 空间额度与用户详情逻辑只在本地主线维护
3. 最近一次线上版本来自本地发布包
4. 共享用户体系与后台业务路径验收通过
+149 -17
View File
@@ -1,25 +1,61 @@
# memind_adm 部署与重启
管理后台前端(React + Vite)部署到局域网 100 服务器(Tailscale `100.99.38.66`)。
> 2026-06-26 起,103 / Studio 正式禁止 `rsync` 发布。
> 唯一合法入口是 `bash scripts/release-prod.sh`。
>
> 本仓库是 **本机 Mac 开发仓库**。每次开发都必须形成 Git commit;本机不允许直接 `rsync` 到 `103` 或 `105`,只能从本地 commit 打包发布。
管理后台前端(React + Vite)部署到生产 Studio 服务器(SSH 优先 `john@10.10.0.2:22`,局域网不通时退回 `john@58.38.22.103:22`)。
## 环境概览
| 项 | 默认值 |
|---|---|
| 部署目标 | `john@100.99.38.66:/Users/john/Project/memind_adm` |
| 部署目标 | `john@10.10.0.2:/Users/john/Project/memind_adm`fallback `john@58.38.22.103` |
| 服务端口 | `5174` |
| 运行方式 | `vite preview`(静态资源 + API 反代) |
| 后端 API | 本仓库 `server/``ADM_API_PORT` 默认 `8085`,与 Memind portal 解耦) |
| 日志 | `/Users/john/Project/memind_adm/adm-preview.log` |
## 硬规则
`memind_adm` 允许项目独立,**不允许用户体系、权限体系、技能体系、策略体系独立**。
必须遵守:
1. 用户、登录会话、能力、策略、技能、账单都必须复用 `Memind` 主实现与主表。
2. `memind_adm` 不得再引入或恢复独立后台用户表,例如 `auth_users``auth_sessions` 这类本地表实现。
3. admin 账号必须是 `Memind` 主用户表中的同一账号,`npm run admin:init` 只允许更新主表账号密码,不允许新建旁路 admin 体系。
4. 生产环境必须显式指向 `Memind` 业务模块目录,不能依赖“碰巧命中”的相对路径。
5. 如果页面出现“用户、能力、技能、策略都是空的”,优先怀疑线上仍在跑旧代码,或 `MEMIND_LIB_ROOT` / 数据库连接未生效,不要先怀疑前端。
## 本次事故结论
2026-06-20 这次事故的根因不是生产库没数据,而是 **线上 `memind_adm` 实际仍在跑旧版独立鉴权代码**
1. 旧代码仍在查询 `auth_users`,没有切到 `Memind``createUserAuth()`
2. 远端 `.env` 虽然已经指向共享库和主库,但只改环境变量不等于新代码已生效。
3. `scripts/rsync_to_server.sh` 默认 **不会覆盖远端已有 `.env`**,所以改了本地环境模板后,线上配置不一定会自动更新。
结论:
1. “页面空”时,先查远端 `adm-api.log` 有没有 `auth_users`
2. 只看“部署脚本成功”不够,必须验远端实际代码和接口返回。
3. 涉及共享用户体系的改动后,必须做登录和 `/admin-api/users` 实测。
## 前置条件
1. 本机可 SSH 到 100 服务器(Tailscale 已连接)
1. 本机可 SSH 到 Studio 生产机
```bash
ssh john@100.99.38.66
ssh john@10.10.0.2
```
2. 本机已安装 Node.js,项目依赖已安装(`npm install`)。
3. 100 服务器上 **memind_adm Admin API** 在 `8085` 运行(`remote_restart.sh` 会自动启动),MySQL 与 Memind 共用。
3. Studio 服务器上 **memind_adm Admin API** 在 `8085` 运行(`remote_restart.sh` 会自动启动),MySQL 与 Memind 共用。
4. 103 服务器上必须存在可读的 `Memind` 业务目录,例如 `/Users/john/Project/Memind`。
5. 远端 `.env` 必须包含正确的共享环境变量:
`DATABASE_URL` 或 `MYSQL_*`
`MEMIND_LIB_ROOT=/Users/john/Project/Memind`
`H5_USERS_ROOT=/Users/john/Project/Memind/users`
## gadm 独立登录
@@ -31,13 +67,50 @@
| 会话 | 设置 `H5_PUBLIC_BASE_URL=https://gadm.tkmind.cn`**不要**设 `H5_COOKIE_DOMAIN=.tkmind.cn` |
| 路径 | `https://gadm.tkmind.cn/` 直达超管后台;Plaza 运营在 `/ops``/ops/admin` 已废弃 |
| API | `ADM_DEV_BACKEND=http://127.0.0.1:8085`preview 反代目标) |
| 初始化 admin | 首次部署后运行 `npm run admin:init`,密码只写入数据库,不放 `.env` |
| 初始化 admin | `npm run admin:init` 只更新 `Memind` 主用户表中的 admin 密码,不创建独立后台账号 |
nginx 示例见 `scripts/gadm-nginx.conf.example``/ops/` → preview`/auth` `/admin-api` `/api` → Admin API `8085`)。
## 更新部署(推荐)
## 当前规则
在本机项目根目录执行:
1. 本地当前工作区先打成发布包,不直接覆盖 103。
2. 发布来源必须是可追溯的本地 commit,不允许从不明工作区直接上线。
3. 103 在切换前必须做 `memind_adm` 全量备份。
4. 发布包不能带 `.env`、日志、pid 文件、`.mindops/` 等运行态资产。
5. 103 必须先在 release 目录完成解包、`npm install`、`npm run build`,再切换 live 目录。
6. 切换后必须通过 Admin API 和前台首页健康检查;失败立即回滚。
## 唯一入口
```bash
bash scripts/release-prod.sh --dry-run
bash scripts/release-prod.sh
```
## 产物发布流程
1. 本地生成 `memind-adm-<release-id>.tar.gz` 和发布清单。
2. 通过 `scp` 上传到 103 的 `incoming/memind_adm/`。
3. 103 备份当前 `/Users/john/Project/memind_adm` 为时间戳压缩包。
4. 在 `/Users/john/Project/releases/` 下解包新版本。
5. 从当前 live 目录继承运行态配置与日志文件。
6. 在 release 目录安装依赖、构建。
7. 原子切换 `memind_adm` live 目录。
8. 调用 `scripts/remote_restart.sh` 重启 `8085` 和 `5174`。
9. 检查 `127.0.0.1:8085/health` 与 `127.0.0.1:5174/`。
## 禁止事项
- 禁止 `scripts/rsync_to_server.sh`
- 禁止手工拖文件覆盖 103
- 禁止在 103 直接改源码后继续跑
- 禁止跳过备份和健康检查
## 更新部署(历史)
下方 `rsync` 流程只保留作事故回溯背景。生产发布不要再照做。
旧流程示例:
```bash
./scripts/rsync_to_server.sh
@@ -51,7 +124,13 @@ nginx 示例见 `scripts/gadm-nginx.conf.example``/ops/` → preview`/auth
4. 若远端尚无 `.env`,写入默认配置(**不覆盖已有 `.env`**
5. 调用 `scripts/remote_restart.sh` 重启服务
### 常用参数
注意:
1. 如果本次改动涉及 `DATABASE_URL`、`MYSQL_*`、`MEMIND_LIB_ROOT`、`H5_USERS_ROOT`,需要**手动同步远端 `.env`**`rsync_to_server.sh` 不会覆盖已有远端 `.env`。
2. 如果本次改动涉及共享用户体系,部署完成后必须执行下文“共享用户体系验收”。
3. 如果希望“套餐管理”自动同步到生产后台,还需要在远端 `.env` 配置 `PLAN_SYNC_TARGET_BASE_URL`、`PLAN_SYNC_USERNAME`、`PLAN_SYNC_PASSWORD`。
### 常用参数(历史)
```bash
# 跳过本地构建(沿用当前 dist/)
@@ -61,7 +140,7 @@ nginx 示例见 `scripts/gadm-nginx.conf.example``/ops/` → preview`/auth
./scripts/rsync_to_server.sh --no-restart
# 环境变量覆盖
DEPLOY_HOST=john@100.99.38.66 \
DEPLOY_HOST=john@10.10.0.2 \
REMOTE_DIR=/Users/john/Project/memind_adm \
ADM_PORT=5174 \
./scripts/rsync_to_server.sh
@@ -72,7 +151,7 @@ ADM_PORT=5174 \
代码已在远端、无需重新同步时:
```bash
ssh john@100.99.38.66 'ADM_ROOT=/Users/john/Project/memind_adm ADM_PORT=5174 bash -s' \
ssh john@10.10.0.2 'ADM_ROOT=/Users/john/Project/memind_adm ADM_PORT=5174 bash -s' \
< scripts/remote_restart.sh
```
@@ -90,14 +169,14 @@ ADM_PORT=5174 ./scripts/remote_restart.sh
本机通过 SSH 检查:
```bash
ssh john@100.99.38.66 'curl -sI http://127.0.0.1:5174/ | head -1'
ssh john@10.10.0.2 'curl -sI http://127.0.0.1:5174/ | head -1'
# 期望: HTTP/1.1 200 OK
```
查看远端日志:
```bash
ssh john@100.99.38.66 'tail -f /Users/john/Project/memind_adm/adm-preview.log'
ssh john@10.10.0.2 'tail -f /Users/john/Project/memind_adm/adm-preview.log'
```
## 远端配置
@@ -111,18 +190,63 @@ ADM_DEV_BACKEND=http://127.0.0.1:8085
H5_PUBLIC_BASE_URL=https://gadm.tkmind.cn
VITE_BASE_PATH=/ops
# 共享 Memind 主库与主实现
DATABASE_URL=mysql://<user>:<password>@<host>:3306/<db>
MEMIND_LIB_ROOT=/Users/john/Project/Memind
H5_USERS_ROOT=/Users/john/Project/Memind/users
# 套餐自动同步到生产后台(可选)
PLAN_SYNC_TARGET_BASE_URL=https://gadm.tkmind.cn
PLAN_SYNC_USERNAME=admin
PLAN_SYNC_PASSWORD=<admin-password>
PLAN_SYNC_TIMEOUT_MS=10000
# 「返回对话」跳转主 H5(可选)
VITE_MAIN_APP_URL=https://h5.tkmind.cn
```
修改 `.env` 后需重启服务生效。
## 共享用户体系验收
每次涉及用户/权限/技能/策略相关改动后,部署完成必须至少执行一次:
1. 看远端代码是否已切到共享实现:
```bash
ssh john@10.10.0.2 'cd /Users/john/Project/memind_adm && sed -n "1,80p" server/bootstrap.mjs'
```
期望:出现 `createUserAuth`,而不是 `createLocalUserAuth`。
2. 看远端 `server/local-auth.mjs` 是否只剩 cookie 工具:
```bash
ssh john@10.10.0.2 'cd /Users/john/Project/memind_adm && sed -n "1,80p" server/local-auth.mjs'
```
期望:**没有** `auth_users`、`auth_sessions`、`createLocalUserAuth`。
3. 看启动日志是否仍出现旧表查询:
```bash
ssh john@10.10.0.2 'cd /Users/john/Project/memind_adm && tail -n 120 adm-api.log'
```
期望:**没有** `auth_users`;出现 `Admin DB connected` 与 `Memind lib: /Users/john/Project/Memind`。
4. 实测登录和用户接口:
```bash
ssh john@10.10.0.2 'python3 - <<\"PY\"
import requests
s = requests.Session()
base = "http://127.0.0.1:8085"
print(s.post(base + "/auth/login", json={"username":"admin","password":"<admin-password>"}).status_code)
resp = s.get(base + "/admin-api/users", params={"page": 1, "pageSize": 5})
print(resp.status_code)
print(resp.text[:800])
PY'
```
期望:`/admin-api/users` 返回 200,且结果中能看到共享主表用户数据,不是空列表。
## 故障排查
| 现象 | 处理 |
|---|---|
| `无法 SSH 到 john@100.99.38.66` | 确认 Tailscale 在线;必要时 `ssh-copy-id john@100.99.38.66` |
| 首页 200 但登录失败 | 检查 100 上 `8085` Admin API 是否运行(`curl http://127.0.0.1:8085/health` |
| `无法 SSH 到 john@10.10.0.2` | 确认局域网可达;不通时改走 `john@58.38.22.103`,必要时再执行 `ssh-copy-id john@58.38.22.103` |
| 首页 200 但登录失败 | 检查 Studio 上 `8085` Admin API 是否运行(`curl http://127.0.0.1:8085/health` |
| 用户、能力、技能、策略全空 | 先查 `adm-api.log` 是否仍有 `auth_users`;再核对远端 `server/bootstrap.mjs` 是否已切到 `createUserAuth()`;再核对远端 `.env` 的 `MEMIND_LIB_ROOT` 与 `DATABASE_URL` |
| 打开后跳 localhost:5173 | nginx 仍指向 Memind/ops dev;改反代到 memind_adm `:5174`,并重新部署 |
| `dist 不存在` | 先在本机 `npm run build`,或完整执行 `./scripts/rsync_to_server.sh` |
| 启动失败 | 查看 `adm-preview.log`;确认远端 Node 在 PATH 中(需 Homebrew `node@22` 等) |
@@ -132,6 +256,14 @@ VITE_MAIN_APP_URL=https://h5.tkmind.cn
| 脚本 | 说明 |
|---|---|
| `scripts/rsync_to_server.sh` | 构建 + rsync + 重启(一键部署) |
| `scripts/release-prod.sh` | 本地打发布包并推送到 103,再由 103 备份、解包、切换、重启 |
| `scripts/rsync_to_server.sh` | 已禁用,仅保留禁用提示 |
| `scripts/remote_restart.sh` | 仅在远端重启 `vite preview` |
| `scripts/.rsync-exclude-lan` | rsync 排除规则 |
| `scripts/.releaseignore-prod` | 生产发布包排除规则 |
## 规则文档
- `ENGINEERING_WORKFLOW_RULES.md`
- `DEVELOPMENT_RELEASE_RULES.md`
- `TEST_RELEASE_RULES.md`
- `PRODUCTION_RELEASE_RULES.md`