M1-T07: align docs to single-DB reality and re-export OpenAPI
Rewrite README (single app.db + one alembic_app chain, legacy data moved once via scripts.migrate_legacy_data, accurate test list) and remove the Grafana Provisioning section. Update architecture-overview to the unified data layer (one Base, app-DB engine with WAL) and retire the alembic_location / alembic_poo sections. Mark M1 done in the roadmap. Re-export openapi/, which catches the spec up to the already-existing /config/smtp/test and /public-ip/check endpoints (purely additive; M1's DB-session dependency swap produced no schema change). pytest 95 passed; ruff clean (pre-existing only); OpenAPI export idempotent.
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
当前系统已经包含:
|
||||
|
||||
- FastAPI Web 应用与服务端模板页面
|
||||
- SQLite + SQLAlchemy + Alembic 的三库结构
|
||||
- SQLite + SQLAlchemy + Alembic 的单库结构
|
||||
- username/password + server-side session 鉴权
|
||||
- runtime config 页面与 app DB 持久化
|
||||
- public IPv4 monitor、历史持久化与定时检查
|
||||
@@ -23,41 +23,32 @@
|
||||
|
||||
## 当前配置现实
|
||||
|
||||
当前系统仍然是三个独立的 SQLite 数据库文件,而不是单一数据库:
|
||||
当前系统使用单一 SQLite 数据库文件(`app.db`),所有数据表都在其中:
|
||||
|
||||
- `app` 级共享数据使用自己的 DB 文件
|
||||
- `location` 模块使用自己的 DB 文件
|
||||
- `poo` 模块使用自己的 DB 文件
|
||||
- auth(单个 admin 用户、server-side session)
|
||||
- runtime config 持久化(`app_config` 表)
|
||||
- public IPv4 当前状态与变化历史
|
||||
- location 记录(`location` 表)
|
||||
- poo 记录(`poo_records` 表)
|
||||
|
||||
当前阶段明确不借这次重构把这些 DB 合并。配置层已经显式反映这一点:
|
||||
配置层只保留一个数据库环境变量:
|
||||
|
||||
- `APP_DATABASE_URL`
|
||||
- `LOCATION_DATABASE_URL`
|
||||
- `POO_DATABASE_URL`
|
||||
|
||||
目前 auth、`location` 和 `poo` 都已经接到各自独立的数据库文件。
|
||||
`app.db` 不会在应用启动时自动创建,需要先运行:
|
||||
|
||||
其中 `app` 级共享 DB 当前主要用于:
|
||||
```bash
|
||||
python -m scripts.run_migrations
|
||||
```
|
||||
|
||||
- 单个 admin 用户
|
||||
- server-side session
|
||||
- runtime config 持久化
|
||||
- public IPv4 当前状态与变化历史
|
||||
|
||||
这部分现在也使用 Alembic 管理:
|
||||
|
||||
- `app db` 不会在应用启动时自动创建
|
||||
- 需要先运行 `python scripts/app_db_adopt.py`
|
||||
- 这个脚本会创建新 DB 并建好 schema
|
||||
该命令会通过 Alembic 将 `app.db` 初始化或升级到最新 head(含 `location` / `poo_records` 表)。
|
||||
|
||||
## 当前目录
|
||||
|
||||
主要目录如下:
|
||||
|
||||
- `app/`: FastAPI 应用代码
|
||||
- `alembic_app/`: App DB 的 Alembic migration 环境
|
||||
- `alembic_location/`: Location DB 的 Alembic migration 环境
|
||||
- `alembic_poo/`: Poo DB 的 Alembic migration 环境
|
||||
- `alembic_app/`: App DB 的 Alembic migration 环境(同时管理 `location` / `poo_records` 表)
|
||||
- `tests/`: pytest 测试
|
||||
- `docs/`: 当前系统说明文档
|
||||
- `scripts/`: 辅助脚本,例如 OpenAPI 导出
|
||||
@@ -128,24 +119,22 @@ uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
|
||||
|
||||
## 数据库与 Alembic
|
||||
|
||||
当前默认使用 SQLite,并区分三个数据库文件:
|
||||
当前使用单一 SQLite 数据库文件:
|
||||
|
||||
- App DB:`sqlite:///./data/app.db`
|
||||
- Location DB:`sqlite:///./data/locationRecorder.db`
|
||||
- Poo DB:`sqlite:///./data/pooRecorder.db`
|
||||
- 数据目录:`./data/`
|
||||
|
||||
初始化 migration 环境后,可继续添加模型并生成迁移:
|
||||
所有模型(auth / config / public_ip / location / poo)共用同一个 `Base`,均通过单一 Alembic 链管理:
|
||||
|
||||
当前 `app`、`location` 和 `poo` 都已经有各自独立的 Alembic 链路。
|
||||
|
||||
- App Alembic 环境:`alembic_app.ini` + `alembic_app/`
|
||||
- Location Alembic 环境:`alembic_location.ini` + `alembic_location/`
|
||||
- Poo Alembic 环境:`alembic_poo.ini` + `alembic_poo/`
|
||||
- Alembic 环境:`alembic_app.ini` + `alembic_app/`
|
||||
- 统一 migration job:`python -m scripts.run_migrations`
|
||||
- App DB 初始化:`python scripts/app_db_adopt.py`
|
||||
- Location DB 接管 / 初始化:`python scripts/location_db_adopt.py`
|
||||
- Poo DB 接管 / 初始化:`python scripts/poo_db_adopt.py`
|
||||
- App DB 接管 / 初始化:`python scripts/app_db_adopt.py`
|
||||
|
||||
历史 location / poo 数据(旧版本遗留的独立 DB 文件)已通过以下脚本一次性迁移至 `app.db`(幂等,不删除旧文件):
|
||||
|
||||
```bash
|
||||
python -m scripts.migrate_legacy_data
|
||||
```
|
||||
|
||||
## 基础鉴权
|
||||
|
||||
@@ -197,7 +186,7 @@ uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
|
||||
|
||||
这意味着:
|
||||
|
||||
- location / poo / app DB 地址仍然属于 bootstrap 范畴
|
||||
- app DB 地址(`APP_DATABASE_URL`)仍然属于 bootstrap 范畴
|
||||
- 运行时可编辑配置主要通过 `app_config` 表持久化
|
||||
- token / secret 这类运行时必须可取回的配置,目前允许明文存储在 config 表中
|
||||
- 登录密码仍然单独使用 Argon2 哈希,不走 config 表明文存储
|
||||
@@ -318,55 +307,6 @@ docker compose -f docker-compose.yml up -d
|
||||
docker compose logs -f app
|
||||
```
|
||||
|
||||
## Grafana Provisioning
|
||||
|
||||
当前仓库支持通过 Grafana provisioning 自动加载 SQLite datasource 和 repo 内的 dashboard 导出文件。
|
||||
|
||||
需要保留的文件路径如下:
|
||||
|
||||
- `grafana/provisioning/datasources/locationrecorder.yaml`
|
||||
- `grafana/provisioning/datasources/poorecorder.yaml`
|
||||
- `grafana/provisioning/dashboards/provider.yaml`
|
||||
- `grafana/dashboards/locationrecorder.json`
|
||||
- `grafana/dashboards/poorecorder.json`
|
||||
|
||||
这些文件的职责分别是:
|
||||
|
||||
- `grafana/provisioning/datasources/locationrecorder.yaml`:声明 `locationrecorder` SQLite datasource,并指向 `/data/home-automation/locationRecorder.db`
|
||||
- `grafana/provisioning/datasources/poorecorder.yaml`:声明 `poorecorder` SQLite datasource,并指向 `/data/home-automation/pooRecorder.db`
|
||||
- `grafana/provisioning/dashboards/provider.yaml`:告诉 Grafana 从 `/var/lib/grafana/dashboards` 扫描并加载 dashboard JSON
|
||||
- `grafana/dashboards/locationrecorder.json`:location recorder dashboard 导出文件,内容本身不需要在 compose 中改写
|
||||
- `grafana/dashboards/poorecorder.json`:poo recorder dashboard 导出文件,内容本身不需要在 compose 中改写
|
||||
|
||||
当前 `docker-compose.yml` 中,Grafana service 需要挂载以下目录:
|
||||
|
||||
- `./grafana/provisioning -> /etc/grafana/provisioning:ro`
|
||||
- `./grafana/dashboards -> /var/lib/grafana/dashboards:ro`
|
||||
|
||||
同时保留现有 named volume `homeautomation_grafana_storage:/var/lib/grafana` 作为 Grafana 运行态数据存储。
|
||||
|
||||
一键启动前,至少需要以下文件已经存在:
|
||||
|
||||
- `grafana/provisioning/datasources/locationrecorder.yaml`
|
||||
- `grafana/provisioning/datasources/poorecorder.yaml`
|
||||
- `grafana/provisioning/dashboards/provider.yaml`
|
||||
- `grafana/dashboards/locationrecorder.json`
|
||||
- `grafana/dashboards/poorecorder.json`
|
||||
|
||||
启动方式:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
启动后会发生的事情:
|
||||
|
||||
- Grafana 容器会安装 `frser-sqlite-datasource` 插件
|
||||
- Grafana 会读取 `/etc/grafana/provisioning/datasources/` 下的 datasource YAML
|
||||
- Grafana 会读取 `/etc/grafana/provisioning/dashboards/provider.yaml`
|
||||
- Grafana 会从 `/var/lib/grafana/dashboards/` 自动导入两个 dashboard JSON
|
||||
- 现有 Grafana named volume 继续负责保存 Grafana 运行态数据,不会覆盖 repo 内的 dashboard 与 provisioning 文件
|
||||
|
||||
## Container Image CI
|
||||
|
||||
项目提供了一个 release image workflow:
|
||||
@@ -411,9 +351,16 @@ pytest
|
||||
|
||||
当前测试包含:
|
||||
|
||||
- app 基本启动测试
|
||||
- `/status` endpoint 测试
|
||||
- 登录 / session 基础流程测试
|
||||
- app 启动与 `/status` 检查
|
||||
- 登录 / session / 鉴权流程
|
||||
- runtime config 读写
|
||||
- public IPv4 monitor
|
||||
- SMTP 配置与测试发信
|
||||
- location / poo recorder 端点
|
||||
- Home Assistant inbound 集成
|
||||
- TickTick OAuth
|
||||
- 部署与迁移(`run_migrations`)
|
||||
- legacy 数据迁移脚本(`migrate_legacy_data`)
|
||||
|
||||
## OpenAPI 导出
|
||||
|
||||
|
||||
@@ -23,10 +23,8 @@
|
||||
- 基础路由注册
|
||||
- `config.py`
|
||||
- 环境变量驱动的 settings
|
||||
- `auth_db.py`
|
||||
- app 级共享 auth 数据库
|
||||
- `db.py`
|
||||
- SQLAlchemy engine / session / Base
|
||||
- 统一数据层:一个 `Base`、一个绑定 `app_database_url` 的 cached engine(SQLite WAL)、`get_engine` / `get_session_local` / `reset_db_caches` / `get_db_session`
|
||||
- `dependencies.py`
|
||||
- 通用依赖注入
|
||||
- `api/`
|
||||
@@ -37,7 +35,7 @@
|
||||
- 当前已迁入 `POST /poo/record` 与 `GET /poo/latest`
|
||||
- `models/`
|
||||
- SQLAlchemy models
|
||||
- 当前 `auth`、`location` 与 `poo` 使用各自独立的数据库 base
|
||||
- 所有模型(auth / config / public_ip / location / poo)共用同一个 `Base`,均落在单一 `app.db` 中
|
||||
- `schemas/`
|
||||
- Pydantic schemas
|
||||
- `services/`
|
||||
@@ -53,17 +51,9 @@
|
||||
- `static/`
|
||||
- 极简静态资源
|
||||
|
||||
### `alembic_location/`
|
||||
|
||||
Location DB 的 migration 基础设施。
|
||||
|
||||
### `alembic_app/`
|
||||
|
||||
App DB 的 migration 基础设施。
|
||||
|
||||
### `alembic_poo/`
|
||||
|
||||
Poo DB 的 migration 基础设施。
|
||||
App DB 的唯一 Alembic migration 链,同时管理 `location` / `poo_records` 表。M1 将三个独立 DB 合并进 `app.db` 后,`alembic_location/` 与 `alembic_poo/` 已退役,全部由此链统一管理。
|
||||
|
||||
### `tests/`
|
||||
|
||||
|
||||
+2
-2
@@ -34,7 +34,7 @@
|
||||
|
||||
| 里程碑 | 主题 | 一句话 |
|
||||
| --- | --- | --- |
|
||||
| **M1** | 单库化地基 | 把三库合并成单一 `app.db`,清理散落数据层,删掉 Grafana |
|
||||
| **M1** ✅ | 单库化地基 | 把三库合并成单一 `app.db`,清理散落数据层,删掉 Grafana |
|
||||
| **M2** | 前端 v2 | React SPA 取代 Jinja,承载 config + 可视化 + 记录增删改 |
|
||||
| **M3** | 开放与移动端(远期试水) | token 鉴权 + React Native 移动端 |
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
|
||||
---
|
||||
|
||||
## M1 — 单库化地基
|
||||
## M1 — 单库化地基(✅ 已完成)
|
||||
|
||||
### 目标
|
||||
|
||||
|
||||
@@ -249,6 +249,27 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/config/smtp/test": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"pages"
|
||||
],
|
||||
"summary": "Smtp Test Submit",
|
||||
"operationId": "smtp_test_submit_config_smtp_test_post",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"text/html": {
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/homeassistant/publish": {
|
||||
"post": {
|
||||
"tags": [
|
||||
@@ -325,6 +346,27 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/public-ip/check": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"public-ip"
|
||||
],
|
||||
"summary": "Run Public Ip Check",
|
||||
"operationId": "run_public_ip_check_public_ip_check_get",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/PublicIPCheckResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ticktick/auth/start": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -443,6 +485,36 @@
|
||||
"type": "object",
|
||||
"title": "HTTPValidationError"
|
||||
},
|
||||
"PublicIPCheckResponse": {
|
||||
"properties": {
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"first_seen",
|
||||
"unchanged",
|
||||
"changed",
|
||||
"error"
|
||||
],
|
||||
"title": "Status"
|
||||
},
|
||||
"checked_at": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"title": "Checked At"
|
||||
},
|
||||
"changed": {
|
||||
"type": "boolean",
|
||||
"title": "Changed"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"status",
|
||||
"checked_at",
|
||||
"changed"
|
||||
],
|
||||
"title": "PublicIPCheckResponse"
|
||||
},
|
||||
"StatusResponse": {
|
||||
"properties": {
|
||||
"status": {
|
||||
|
||||
@@ -155,6 +155,19 @@ paths:
|
||||
text/html:
|
||||
schema:
|
||||
type: string
|
||||
/config/smtp/test:
|
||||
post:
|
||||
tags:
|
||||
- pages
|
||||
summary: Smtp Test Submit
|
||||
operationId: smtp_test_submit_config_smtp_test_post
|
||||
responses:
|
||||
'200':
|
||||
description: Successful Response
|
||||
content:
|
||||
text/html:
|
||||
schema:
|
||||
type: string
|
||||
/homeassistant/publish:
|
||||
post:
|
||||
tags:
|
||||
@@ -203,6 +216,19 @@ paths:
|
||||
content:
|
||||
application/json:
|
||||
schema: {}
|
||||
/public-ip/check:
|
||||
get:
|
||||
tags:
|
||||
- public-ip
|
||||
summary: Run Public Ip Check
|
||||
operationId: run_public_ip_check_public_ip_check_get
|
||||
responses:
|
||||
'200':
|
||||
description: Successful Response
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PublicIPCheckResponse'
|
||||
/ticktick/auth/start:
|
||||
get:
|
||||
tags:
|
||||
@@ -285,6 +311,29 @@ components:
|
||||
title: Detail
|
||||
type: object
|
||||
title: HTTPValidationError
|
||||
PublicIPCheckResponse:
|
||||
properties:
|
||||
status:
|
||||
type: string
|
||||
enum:
|
||||
- first_seen
|
||||
- unchanged
|
||||
- changed
|
||||
- error
|
||||
title: Status
|
||||
checked_at:
|
||||
type: string
|
||||
format: date-time
|
||||
title: Checked At
|
||||
changed:
|
||||
type: boolean
|
||||
title: Changed
|
||||
type: object
|
||||
required:
|
||||
- status
|
||||
- checked_at
|
||||
- changed
|
||||
title: PublicIPCheckResponse
|
||||
StatusResponse:
|
||||
properties:
|
||||
status:
|
||||
|
||||
Reference in New Issue
Block a user