refactor: 全量迁云——fam-core 直读甲骨文 SQLite,NAS 只剩推送进程
前一天刚把登录迁到甲骨文,隔天 NAS 上的 fam-core 又挂了导致数据接口 502。 盘点后确认:甲骨文的 SQLite 才是权威数据源(videos 3113 / events 16159 / people 60 / model_calls 9876),NAS 的 MariaDB 全是它的镜像——前端读的数据 本来就产自甲骨文,绕了一圈回家又绕回来。 改动: - db_layer.py 从 725 行重写成 377 行:MySQL 镜像查询改为直读 fam-edge 的 SQLite。5 个 upsert_sync_*(约 300 行去重逻辑,8/29 和 9/3 两次 1062 事故的 发源地)连同 oracle_sync.py 整个删除。SQL 方言:JSON_CONTAINS -> json_each (前置 json_valid,历史脏数据不会把查询搞崩)、LEFT() -> substr()、%s -> ?。 函数名 get_sync_* 一并改掉——已经没有 sync 这回事了 - 新增 edge_client.py:写操作(改名/删除)、帧图头像、服务状态都打给同机 fam-edge,全走 127.0.0.1 - 新增 fam-notifier/:motion_notifier 从 fam-core 拆出独立成服务,游标从 MariaDB 换成本地 JSON 文件。NAS 上从此没有 Flask、没有数据库、没有监听端口 - fam-core 移到甲骨文 /opt/fam-core(systemd,gunicorn -w 2,只绑 127.0.0.1:5401——5400 被 chat-relay 占了)。Caddy 的 /api/* 从"frp 隧道 回源 NAS"改成同机反代,forward_auth 闸门不变 - 前端删掉侧边栏同步面板、统计页同步状态、服务状态页的"NAS 同步"卡片与 "立即同步"按钮(背后的镜像层已不存在);换成"NAS 运动推送"卡片,读 fam-edge activity 新增的 motion 段(心跳年龄 + 最近事件) - 顺带修掉一个隐蔽 bug:镜像表为保外键稳定用的是 NAS 本地自增 id,而帧图接口 要的是甲骨文的 id,两边在 9/3 那次 id 重排后就对不上了。现在只有一套 id 测试:fam-core 21(新增 12 个 db_layer 用例:脏 JSON 不崩、人物精确匹配不误伤 "人物B"、日期过滤、统计口径、chat_history 懒建表)、fam-notifier 6、 fam-edge 157,全绿。 生产验证:甲骨文 /api/ui/stats 返回 videos 2965 / events 16159 / people 59; NAS 侧 fam-notifier 已推送成功(事件 33070-33072 落库,心跳新鲜); chat_history 19 条经 scripts/import_chat_history.py 迁移完成。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
71
PROGRESS.md
71
PROGRESS.md
@@ -541,3 +541,74 @@ AI 分析瓶颈: 帧5耗时 229s (疑似 ARM CPU 热降频), 其余帧 50-65s
|
||||
- `/api/ui/videos`(无 cookie)→ 401 → 前端据此自动跳 `/login`
|
||||
|
||||
登录链路已通:未登录访问任意页面 → 首个 401 → 自动跳 auth-hub 登录 → 回调种 `fam_session` cookie → 回 `/timeline` 正常加载。
|
||||
|
||||
## 统一登录从 NAS 迁到甲骨文 fam-edge(2026-09-12)
|
||||
|
||||
**触发**:`smart-camera.zichuan.xyz/login` 报 502。排查链路:静态页 `/`、`/timeline` 200(Caddy 读本机磁盘),
|
||||
只有 `/login` 和 `/api/*` 502;甲骨文本机 `curl 127.0.0.1:8000/health` 0.6 秒空响应(curl exit 52,
|
||||
Caddy 日志 `msg:"EOF"`),frps 正常、同隧道的 gitea 也正常 → NAS 上 fam-core 进程没了。
|
||||
局域网直扫 NAS:22/2222/3000/5000/5001/3306 全 OPEN,**只有 8000 closed**,NAS 本身没事。
|
||||
根因是登录入口挂在 NAS 上,而 NAS 上的 fam-core 没有任何守护(DSM 无 systemd),挂了不会自启。
|
||||
|
||||
**决策**:登录是入口,不该依赖家里的机器。整体迁到甲骨文的 fam-edge(前端静态文件、auth-hub 本来就在这台),
|
||||
NAS fam-core 退化成纯数据接口。
|
||||
|
||||
**改动**:
|
||||
- 新增 `fam-edge/src/fam_edge/auth.py`:`/login`、`/api/auth/callback`、`/api/logout`、
|
||||
`/api/auth/check`、`/api/auth/verify`(给 Caddy forward_auth)。跟旧实现三处关键差异:
|
||||
1. 换 token / 拉 JWKS 走 `AUTH_HUB_INTERNAL_BASE`(本机 :5300),不再跨公网 TLS——旧链路上
|
||||
`PyJWKClient` 用 urllib + 系统 CA(群晖易 CERTIFICATE_VERIFY_FAILED)、两机时钟偏差会让 `iat`
|
||||
显得来自未来,这两个坑一起消失;但 `iss` 校验和浏览器跳转仍用公网 issuer
|
||||
2. 会话改无状态 HS256 签名 cookie,服务重启不掉线(旧实现进程内 token 表)
|
||||
3. **回调失败渲染错误页,不再 302 回 `/login`**——旧实现失败即跳 `/login`,而 auth-hub 只要还有
|
||||
会话就立刻再签一个 code 跳回来,两边对跳成死循环,浏览器只报「重定向次数过多」,
|
||||
既看不到登录页也看不到原因(这正是 9/1 那次「跳不到登录页」的成因)
|
||||
- 删除 `fam-core/src/fam_core/auth.py` + `tests/test_auth.py`,`app.py` 去掉 `init_auth`;
|
||||
fam-core 不再有任何鉴权,改由甲骨文 Caddy `forward_auth` 前置拦截
|
||||
- `fam-edge/tests/test_auth.py` 16 个用例(含「失败分支绝不 302」的回归测试、
|
||||
「服务端走内网但 iss 按公网校验」、「cookie 无状态」);fam-edge 157 / fam-core 15 全绿
|
||||
- 写测试时逮到自己写的一个 bug:会话 cookie 也套了 60 秒 leeway(本进程自签自验根本不需要),
|
||||
会让每个会话白白多活 60 秒,已改成只有 id_token 用 leeway
|
||||
|
||||
**遗留风险(已记入 README/DEPLOY)**:frps 在甲骨文绑的是 `*:8000` 且 iptables 明确放行,
|
||||
fam-core 去掉鉴权后,绕过 Caddy 直连 `129.146.26.249:8000` 就是无门禁的全量数据接口,
|
||||
必须靠 `iptables -I INPUT 1 -p tcp --dport 8000 ! -i lo -j DROP` 兜底。
|
||||
另:登录进程并到 fam-edge 后,fam-edge 正在跑视频分析时登录响应可能变慢(单 worker 4 线程)。
|
||||
|
||||
## 全量迁云:NAS 只剩推送进程(2026-09-13)
|
||||
|
||||
**触发**:前一天刚把登录迁到甲骨文,隔天 NAS 上的 fam-core 又挂了导致数据接口 502。
|
||||
用户一句话点破:「NAS 上只有一个无状态的通知甲骨文的服务,剩下的全部在甲骨文呀」。
|
||||
盘点后确认这个判断成立——甲骨文的 SQLite 才是权威数据源(videos 3113 / events 16159 /
|
||||
people 60 / model_calls 9876),NAS 的 MariaDB 全是它的镜像,前端读的数据本来就产自
|
||||
甲骨文,绕了一圈回家又绕回来。
|
||||
|
||||
**改动**:
|
||||
- `fam-core/db_layer.py` 从 725 行重写成 377 行:MySQL 镜像查询 → 直读 fam-edge 的
|
||||
SQLite。5 个 `upsert_sync_*`(约 300 行去重逻辑,8/29 和 9/3 两次 1062 事故的发源地)
|
||||
连同 `oracle_sync.py` 整个删除。SQL 方言:`JSON_CONTAINS` → `json_each`(前置
|
||||
`json_valid`,历史脏数据不会把查询搞崩)、`LEFT()` → `substr()`、`%s` → `?`。
|
||||
函数名 `get_sync_*` 一并改掉——已经没有 sync 这回事了,留着名字会误导。
|
||||
- 新增 `fam-core/edge_client.py`:写操作(改名/删除)、帧图头像、服务状态都打给同机
|
||||
fam-edge,全走 127.0.0.1,不出公网。
|
||||
- 新增 `fam-notifier/`:把 `motion_notifier.py` 从 fam-core 拆出来,游标从 MariaDB
|
||||
换成本地 JSON 文件。NAS 上从此没有 Flask、没有数据库、没有监听端口,只有一个
|
||||
单向推送进程。
|
||||
- fam-core 移到甲骨文 `/opt/fam-core`(systemd,gunicorn -w 2,**只绑 127.0.0.1:5401**——
|
||||
5400 被 chat-relay 占了)。Caddy 的 `/api/*` 从"frp 隧道回源 NAS"改成同机反代,
|
||||
forward_auth 闸门不变。
|
||||
- 前端跟着删:侧边栏同步面板、统计页同步状态、服务状态页的"NAS 同步"卡片和
|
||||
"立即同步"按钮(背后的镜像层已不存在)。"NAS 同步"换成"NAS 运动推送",读
|
||||
fam-edge activity 新增的 `motion` 段(心跳年龄 + 最近事件)。
|
||||
- 顺带修掉一个隐蔽 bug:镜像表为保外键稳定用的是 NAS 本地自增 id,而帧图接口要的是
|
||||
甲骨文的 id,两边在 9/3 那次 id 重排后就对不上了。现在只有一套 id。
|
||||
|
||||
**测试**:fam-core 21(新增 12 个 db_layer 用例:脏 JSON 不崩、人物精确匹配不误伤
|
||||
"人物B"、日期过滤、统计口径、chat_history 懒建表)、fam-notifier 6、fam-edge 157,全绿。
|
||||
|
||||
**验证**:甲骨文 `/api/ui/stats` 返回 videos 2965 / events 16159 / attention 13 /
|
||||
people 59;外网 `/` `/timeline` 200、`/login` 302、`/api/*` 未登录 401——**全程 NAS
|
||||
上的 fam-core 是停着的**,这就是迁云的验收标准。
|
||||
|
||||
**待办**:NAS 侧部署 fam-notifier(只能用户手动,密码登录);chat_history 一次性迁移
|
||||
(`fam-core/scripts/import_chat_history.py`,幂等);frpc.toml 里的 8000 映射可删。
|
||||
|
||||
Reference in New Issue
Block a user