2026-04-19 20:19:58 +02:00
|
|
|
|
# Python 骨架架构概览
|
|
|
|
|
|
|
|
|
|
|
|
本文档说明当前 Python skeleton 的职责边界与目录组织。它描述的是“后续迁移承载体”,不是完整业务实现。
|
|
|
|
|
|
|
|
|
|
|
|
## 当前目标
|
|
|
|
|
|
|
|
|
|
|
|
这一轮的目标是提供一个稳定、轻量、可持续扩展的基础工程,使后续可以逐步迁移:
|
|
|
|
|
|
|
|
|
|
|
|
- TickTick integration
|
|
|
|
|
|
- Home Assistant integration
|
|
|
|
|
|
- poo records
|
|
|
|
|
|
- location / life trajectory
|
|
|
|
|
|
|
|
|
|
|
|
## 目录设计
|
|
|
|
|
|
|
|
|
|
|
|
### `app/`
|
|
|
|
|
|
|
|
|
|
|
|
应用核心代码目录。
|
|
|
|
|
|
|
|
|
|
|
|
- `main.py`
|
|
|
|
|
|
- FastAPI app factory
|
|
|
|
|
|
- lifespan
|
|
|
|
|
|
- 基础路由注册
|
|
|
|
|
|
- `config.py`
|
|
|
|
|
|
- 环境变量驱动的 settings
|
|
|
|
|
|
- `db.py`
|
2026-06-12 17:13:28 +02:00
|
|
|
|
- 统一数据层:一个 `Base`、一个绑定 `app_database_url` 的 cached engine(SQLite WAL)、`get_engine` / `get_session_local` / `reset_db_caches` / `get_db_session`
|
2026-04-19 20:19:58 +02:00
|
|
|
|
- `dependencies.py`
|
|
|
|
|
|
- 通用依赖注入
|
|
|
|
|
|
- `api/`
|
|
|
|
|
|
- HTTP routes
|
2026-06-13 12:01:34 +02:00
|
|
|
|
- `api/routes/api/`:JSON API(`/api/*` 前缀),供 React SPA 调用:会话/鉴权、配置读写、数据查询、记录 CRUD
|
|
|
|
|
|
- 裸 ingestion 端点:`GET /public-ip/check`、`POST /homeassistant/publish`、`POST /poo/record`、`GET /poo/latest`、TickTick OAuth 等
|
2026-04-19 20:19:58 +02:00
|
|
|
|
- `models/`
|
|
|
|
|
|
- SQLAlchemy models
|
2026-06-12 17:13:28 +02:00
|
|
|
|
- 所有模型(auth / config / public_ip / location / poo)共用同一个 `Base`,均落在单一 `app.db` 中
|
2026-04-19 20:19:58 +02:00
|
|
|
|
- `schemas/`
|
|
|
|
|
|
- Pydantic schemas
|
|
|
|
|
|
- `services/`
|
|
|
|
|
|
- 业务服务层
|
2026-04-20 15:56:10 +02:00
|
|
|
|
- 当前已迁入 config page 的 DB 持久化逻辑
|
2026-04-29 13:07:59 +02:00
|
|
|
|
- 当前已迁入 public IPv4 检查、状态持久化与变化通知逻辑
|
|
|
|
|
|
- 当前已迁入 SMTP 发信与测试发信逻辑
|
2026-04-19 20:19:58 +02:00
|
|
|
|
- `integrations/`
|
2026-04-20 10:11:02 +02:00
|
|
|
|
- 外部系统适配层
|
|
|
|
|
|
- 当前已迁入 Home Assistant outbound adapter
|
2026-04-19 20:19:58 +02:00
|
|
|
|
- `static/`
|
|
|
|
|
|
- 极简静态资源
|
|
|
|
|
|
|
2026-04-20 15:16:47 +02:00
|
|
|
|
### `alembic_app/`
|
|
|
|
|
|
|
2026-06-12 17:13:28 +02:00
|
|
|
|
App DB 的唯一 Alembic migration 链,同时管理 `location` / `poo_records` 表。M1 将三个独立 DB 合并进 `app.db` 后,`alembic_location/` 与 `alembic_poo/` 已退役,全部由此链统一管理。
|
2026-04-19 20:19:58 +02:00
|
|
|
|
|
|
|
|
|
|
### `tests/`
|
|
|
|
|
|
|
|
|
|
|
|
pytest 测试目录。后续可以在这里自然扩展:
|
|
|
|
|
|
|
|
|
|
|
|
- unit tests
|
|
|
|
|
|
- mock tests
|
|
|
|
|
|
- integration tests
|
|
|
|
|
|
|
2026-06-13 12:01:34 +02:00
|
|
|
|
### `frontend/`
|
|
|
|
|
|
|
|
|
|
|
|
React SPA 前端(M2 引入)。Vite + React + TypeScript + Mantine,由 FastAPI 同源托管。
|
|
|
|
|
|
|
|
|
|
|
|
- `src/`:React 源码
|
|
|
|
|
|
- `src/api/`:由 `openapi/openapi.json` 生成的类型化 client(`schema.d.ts`)+ fetch 封装
|
|
|
|
|
|
- `dist/`:`npm run build` 产物,由 FastAPI 的 `SPA_DIST_DIR` 挂载并对非 `/api` 路径做 fallback
|
|
|
|
|
|
|
2026-04-19 20:19:58 +02:00
|
|
|
|
### `scripts/`
|
|
|
|
|
|
|
2026-06-21 23:01:50 +02:00
|
|
|
|
辅助脚本目录。当前包含:
|
|
|
|
|
|
|
|
|
|
|
|
- `export_openapi.py`:导出 OpenAPI schema 静态产物
|
|
|
|
|
|
- `run_migrations.py`:运行 Alembic migration
|
|
|
|
|
|
- `app_db_adopt.py`:App DB 接管 / 初始化
|
|
|
|
|
|
- `migrate_legacy_data.py`:一次性历史数据搬迁脚本
|
|
|
|
|
|
- `admin_cli.py`:Admin CLI 逃生通道(M4),见下方"登录加固"说明
|
2026-06-13 12:01:34 +02:00
|
|
|
|
|
|
|
|
|
|
### `openapi/`
|
|
|
|
|
|
|
|
|
|
|
|
OpenAPI schema 静态产物(`openapi.json` / `openapi.yaml`),由 `python scripts/export_openapi.py` 生成,纳入版本控制。前端 codegen 以此为契约源。
|
2026-04-19 20:19:58 +02:00
|
|
|
|
|
2026-06-21 23:01:50 +02:00
|
|
|
|
## 登录加固(M4)
|
|
|
|
|
|
|
|
|
|
|
|
M4 在基础 Argon2 + server-side session 鉴权之上叠加了三层防御:
|
|
|
|
|
|
|
|
|
|
|
|
**防爆破 / 指数退避**:`app/services/login_throttle.py` 按 client IP 与 username 双键记失败计数,失败超过 3 次后指数增长等待时间(最长 15 分钟),`POST /api/auth/login` 在退避窗口内直接返回 `429 + Retry-After`,不执行 Argon2 验证。成功登录后清零。退避是延迟而非永久封号;全局开关 `AUTH_LOGIN_THROTTLE_ENABLED`(CONFIG_FIELDS,默认开);反代后需设 `AUTH_TRUST_FORWARDED_FOR=true`(`.env` 部署级,默认 false)。
|
|
|
|
|
|
|
|
|
|
|
|
**CLI 逃生通道**:`scripts/admin_cli.py`(入口 `python -m scripts.admin_cli`)直连本地 DB,**无需 HTTP 服务运行、无需任何已存凭据**,支持:重置密码(`reset-password`)、解锁退避(`unlock`)、关停 TOTP(`disable-totp`,零凭据最终逃生)、重新发放 TOTP secret(`reissue-totp`)、查看用户列表(`list-admin`)。CLI 只动 auth 行,不触碰用户数据表。
|
|
|
|
|
|
|
|
|
|
|
|
**可选 TOTP 二次验证**:admin 可在设置页自选启用 RFC 6238 TOTP。启用后登录为两步(密码 → 6 位动态码或一次性恢复码);不启用维持纯密码。TOTP secret 明文存库(与其他 secret 一致,靠文件权限保护);恢复码以 Argon2 哈希存储,使用后消费(一次性)。后端使用 `pyotp`,二维码由前端 `qrcode.react` 渲染。issuer 标签由 `AUTH_TOTP_ISSUER`(`.env` 部署级)配置,默认回退 `app_name`。
|
|
|
|
|
|
|
|
|
|
|
|
详细说明:[`docs/auth.md`](./auth.md)
|
|
|
|
|
|
|
2026-04-19 20:19:58 +02:00
|
|
|
|
## 当前约束
|
|
|
|
|
|
|
|
|
|
|
|
- 当前数据库继续使用 SQLite
|
2026-06-13 12:01:34 +02:00
|
|
|
|
- ~~当前不引入前后端分离~~ **已退役(M2)**:现为 React SPA + JSON `/api` 层,由 FastAPI 同源托管
|
2026-04-19 20:19:58 +02:00
|
|
|
|
- 当前不设计 Notion 模块
|
2026-04-29 13:07:59 +02:00
|
|
|
|
- 当前通知能力仍保持极小范围,不引入独立通知中心或多渠道抽象
|
2026-04-19 20:19:58 +02:00
|
|
|
|
|
|
|
|
|
|
## 关于 Notion
|
|
|
|
|
|
|
|
|
|
|
|
Notion 在 Go 版本中仍是现状模块,但在 Python 重构中已经明确属于 removed scope。
|
|
|
|
|
|
|
|
|
|
|
|
因此当前 Python skeleton:
|
|
|
|
|
|
|
|
|
|
|
|
- 不提供 Notion integration 模块
|
|
|
|
|
|
- 不提供 Notion schema
|
|
|
|
|
|
- 不预留 Notion 相关业务流
|
|
|
|
|
|
|
|
|
|
|
|
如果未来需要回顾其历史作用,应继续参考 Go 版本和现有迁移盘点文档,而不是在 Python 骨架中保留它。
|