diff --git a/README.md b/README.md index fdaaba9..9fd0eee 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ - SQLite + SQLAlchemy + Alembic 的三库结构 - username/password + server-side session 鉴权 - runtime config 页面与 app DB 持久化 +- public IPv4 monitor、历史持久化与定时检查 +- SMTP 配置、测试发信与 public IPv4 changed 邮件通知 - location recorder - poo recorder - Home Assistant inbound / outbound integration @@ -40,6 +42,7 @@ - 单个 admin 用户 - server-side session - runtime config 持久化 +- public IPv4 当前状态与变化历史 这部分现在也使用 Alembic 管理: @@ -199,6 +202,79 @@ uvicorn app.main:app --reload --host 0.0.0.0 --port 8000 - token / secret 这类运行时必须可取回的配置,目前允许明文存储在 config 表中 - 登录密码仍然单独使用 Argon2 哈希,不走 config 表明文存储 +当前已经接入 config 页面的运行时配置包括: + +- 基础系统配置 +- auth cookie 相关配置 +- SMTP 基础配置 +- TickTick OAuth 配置 +- Home Assistant 配置 + +其中 SMTP password 与其他 secret 字段一致: + +- 页面不明文回显 +- 留空提交时保留旧值 +- 用于测试发信与自动通知时不会写入响应 + +## Public IPv4 Monitor + +当前系统已经提供最小可用的 public IPv4 monitor: + +- 使用单一 provider 检查当前公网 IPv4 +- 将状态与变化历史持久化到 app DB +- 提供受保护的手动检查入口:`GET /public-ip/check` +- 启动时注册 APScheduler job,默认每 4 小时检查一次 + +当前 app DB 中与此功能相关的新表: + +- `public_ip_state` +- `public_ip_history` + +状态语义如下: + +- `first_seen`:首次发现当前公网 IPv4 +- `unchanged`:与上次状态一致 +- `changed`:公网 IPv4 发生变化 +- `error`:provider 请求失败或返回无效值 + +## SMTP 与邮件通知 + +当前系统已经提供最小可用的 SMTP 能力: + +- SMTP 配置可在 `/config` 页面填写并保存到 `app_config` +- 可通过 config 页面发送测试邮件 +- 邮件 `From` 头支持显示名,例如 `Home Automation ` + +当前 SMTP 配置项包括: + +- `SMTP_ENABLED` +- `SMTP_HOST` +- `SMTP_PORT` +- `SMTP_USERNAME` +- `SMTP_PASSWORD` +- `SMTP_FROM_NAME` +- `SMTP_FROM_ADDRESS` +- `SMTP_TO_ADDRESS` +- `SMTP_USE_STARTTLS` + +当前 public IPv4 monitor 已与 SMTP sender 接通,但只处理一个很小的通知场景: + +- 当 public IPv4 check 结果为 `changed` 时,自动发送一封英文纯文本邮件 + +以下情况不会发邮件: + +- `first_seen` +- `unchanged` +- `error` + +当前通知邮件内容固定,不提供模板系统,正文会包含: + +- previous IP +- current IP +- detected time + +手动测试时,如果需要再次模拟一次 IP 变化,可以临时修改 `public_ip_state.current_ipv4` 为一个保留测试地址,然后再次调用 `GET /public-ip/check`。 + ## OpenAPI 可使用下面的脚本重新导出当前 API 定义: diff --git a/docs/architecture-overview.md b/docs/architecture-overview.md index 7c1c5db..c7c853c 100644 --- a/docs/architecture-overview.md +++ b/docs/architecture-overview.md @@ -32,6 +32,7 @@ - `api/` - HTTP routes - 当前已迁入 `/login`、`/logout`、`/admin` + - 当前已迁入 `GET /public-ip/check` - 当前已迁入 `POST /homeassistant/publish` 第一版入口 - 当前已迁入 `POST /poo/record` 与 `GET /poo/latest` - `models/` @@ -42,6 +43,8 @@ - `services/` - 业务服务层 - 当前已迁入 config page 的 DB 持久化逻辑 + - 当前已迁入 public IPv4 检查、状态持久化与变化通知逻辑 + - 当前已迁入 SMTP 发信与测试发信逻辑 - `integrations/` - 外部系统适配层 - 当前已迁入 Home Assistant outbound adapter @@ -80,6 +83,7 @@ pytest 测试目录。后续可以在这里自然扩展: - 当前数据库继续使用 SQLite - 当前不引入前后端分离 - 当前不设计 Notion 模块 +- 当前通知能力仍保持极小范围,不引入独立通知中心或多渠道抽象 ## 关于 Notion diff --git a/docs/public-ip-monitor.md b/docs/public-ip-monitor.md new file mode 100644 index 0000000..b80e7b1 --- /dev/null +++ b/docs/public-ip-monitor.md @@ -0,0 +1,126 @@ +# Public IPv4 Monitor 与邮件通知 + +本文档说明当前 public IPv4 monitor 与 SMTP 邮件通知能力的职责边界和运行方式。 + +## 当前范围 + +当前实现只覆盖一个很小的通知能力: + +- 定期或手动检查当前公网 IPv4 +- 将当前状态和变化历史持久化到 app DB +- 仅在公网 IPv4 发生变化时发送一封英文纯文本邮件 + +当前明确不包含: + +- Namecheap API 自动更新 +- IPv6 检查 +- 错误告警邮件 +- 重复提醒 / 升级告警 +- Telegram / Slack / Discord 通知 +- 完整通知中心或模板系统 + +## 数据存储 + +当前数据全部进入 app DB。 + +相关表: + +- `public_ip_state` + - 保存当前状态 + - 逻辑上通常只有一行 +- `public_ip_history` + - 保存首次发现和变化历史 + +当前不会把 public IP 状态放进 `app_config`。 + +## 检查结果语义 + +一次检查会返回以下四种结果之一: + +- `first_seen` +- `unchanged` +- `changed` +- `error` + +行为约束: + +- `first_seen`:写入当前 IP 和首条 history,但不发通知邮件 +- `unchanged`:只更新时间和状态,不写 history,不发邮件 +- `changed`:更新 `previous_ipv4` / `current_ipv4` / `last_changed_at`,写入 history,并发送邮件 +- `error`:保留已有有效 IP,不写伪 history,也不发邮件 + +## 手动检查与定时检查 + +手动检查入口: + +- `GET /public-ip/check` + +约束: + +- 需要现有鉴权 +- 响应不暴露 IP 本身 +- 只返回非敏感检查结果 + +定时检查: + +- 应用启动时注册 APScheduler job +- 默认每 4 小时执行一次 +- 与手动检查复用同一套 public IP check + notify 逻辑 + +## SMTP 通知 + +当前通知发信复用现有 SMTP sender。 + +依赖的配置项: + +- `SMTP_ENABLED` +- `SMTP_HOST` +- `SMTP_PORT` +- `SMTP_USERNAME` +- `SMTP_PASSWORD` +- `SMTP_FROM_NAME` +- `SMTP_FROM_ADDRESS` +- `SMTP_TO_ADDRESS` +- `SMTP_USE_STARTTLS` + +其中: + +- `SMTP_FROM_NAME` 用于邮件头显示名 +- `From` 头会渲染成 `Name ` +- SMTP envelope sender 仍然使用纯邮箱地址,保持兼容性 + +## 通知触发条件 + +只有在 `changed` 时发邮件。 + +不会发邮件的情况: + +- `first_seen` +- `unchanged` +- `error` + +这使得同一 IP 状态不会被重复通知,因为在首次变更之后,后续重复检查会变成 `unchanged`。 + +## 邮件内容 + +当前邮件标题固定为: + +- `Public IP changed` + +正文为英文纯文本,至少包含: + +- previous IP +- current IP +- detected time + +当前正文还会附带一句 Namecheap trusted IP 的人工更新提示。 + +## 失败处理 + +当前通知发送是“尽力而为”的附加动作: + +- public IP 状态持久化先完成 +- 邮件发送失败不会回滚 public IP 状态 +- 失败只记录 warning 日志 + +这样可以避免通知链路反过来影响主检查流程。 \ No newline at end of file