Add SMTP email: password reset + per-frame battery-threshold alerts
Build and push server image / build-and-push (push) Successful in 40s
Build and push server image / build-and-push (push) Successful in 40s
Admin-configured SMTP (server/port/username/password/from address/ STARTTLS, a singleton server_settings row set from /admin -- not env vars, since it's operator infrastructure a household admin sets up once through the UI) powers two features, both requiring the relevant user to have an email set in their own Settings: - "Forgot password?" on /login emails a one-hour single-use reset link (password_reset_tokens table). The endpoint always returns the same generic "check your email" response regardless of whether the address matched an account, so it can't be used to enumerate registered users. - A frame's Configuration tab can set a battery-alert threshold (Frame.battery_alert_threshold_pct, -1 = disabled); POST /frame/battery emails the owner the first time a report drops to or below it, then stays quiet for the rest of that discharge cycle (battery_alert_sent, reset alongside battery_history whenever the existing recharge-jump detection fires) -- not once per wake. New app/mail.py wraps stdlib smtplib (no new dependency); send_email() never raises, so a broken mail server can't 500 a battery report or a password-reset request. Schema migration v2 adds users.email and the two frame columns via ALTER TABLE (safe against the live, already- populated database) plus the two new tables via the existing create_all-based migration runner. Verified against a real (already-migrated, real user/frame data) database: the v1->v2 migration, admin SMTP config + test-email button, full forgot/reset-password roundtrip (including single-use token invalidation and the no-enumeration response), and the battery alert firing exactly once per crossing against a hand-rolled fake SMTP server -- all via curl end-to-end, plus the standing legacy-device curl suite to confirm the device protocol is untouched.
This commit is contained in:
+17
-2
@@ -79,6 +79,18 @@ algorithm itself -- it just streams the response straight to the panel.
|
||||
view current + upcoming, "show next", advance, back -- nothing else.
|
||||
The share QR stays public (it creates a 30-minute Immich share link
|
||||
for exactly the photo shown).
|
||||
- **Email (optional).** An admin sets an SMTP server once (`/admin` --
|
||||
server, port, username/password, from address, STARTTLS on/off; a
|
||||
"send test email to myself" button, delivered to the admin's own
|
||||
email); each user sets their own email in Settings. Once both are in
|
||||
place: **"Forgot password?"** on the login page emails a one-hour
|
||||
reset link (a generic "check your email" response either way, so the
|
||||
endpoint can't be used to enumerate accounts), and a frame's
|
||||
Configuration tab can set a **battery-alert threshold** -- an email to
|
||||
the frame's owner the first time a report drops to or below it, not
|
||||
again until a recharge is detected and it crosses again. No SMTP
|
||||
configured, or no email on the relevant account, and both features
|
||||
silently no-op rather than erroring.
|
||||
|
||||
## Endpoints
|
||||
|
||||
@@ -117,7 +129,9 @@ Pages: `/` (routing hub), `/setup`, `/login`, `/claim`, `/settings`,
|
||||
flat-scalar parser.
|
||||
- `POST /frame/battery` -- `{"percent": 0-100}`; per-discharge-cycle
|
||||
history (feeds the runtime estimate) plus a permanent per-frame
|
||||
battery log (the Stats chart). Only sent on battery power.
|
||||
battery log (the Stats chart). Only sent on battery power. Also where
|
||||
the battery-alert threshold (below) is checked and, at most once per
|
||||
discharge cycle, emailed to the owner.
|
||||
- `GET /frame/firmware` -- streams the frame's staged OTA image.
|
||||
|
||||
### Web API (`/api/frames/{id}/...` -- session auth; *view* for reads, *control* for writes)
|
||||
@@ -138,7 +152,8 @@ Pages: `/` (routing hub), `/setup`, `/login`, `/claim`, `/settings`,
|
||||
on-device manage overlay still renders native, a known limitation),
|
||||
`quiet_hours_*` + `timezone` (a pure server-side decision shaping
|
||||
what `refresh_interval_s` gets handed to the device),
|
||||
`firmware_update_repo_url`, `firmware_auto_update`).
|
||||
`firmware_update_repo_url`, `firmware_auto_update`,
|
||||
`battery_alert_threshold_pct` -- percent, or `-1`/blank to disable).
|
||||
- `POST .../take-control` -- always succeeds for a linked user.
|
||||
- `GET .../stats`, `GET .../battery-log`, `GET .../thumbnail/{asset_id}`.
|
||||
- `POST .../firmware` (manual .bin upload, esp_app_desc_t-validated),
|
||||
|
||||
Reference in New Issue
Block a user