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:
@@ -41,6 +41,39 @@
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2 class="card-title" style="margin-top: 24px;">Email (SMTP)</h2>
|
||||
<p class="sub">Used for "forgot password" links and battery-low
|
||||
alerts (set per frame in its Configuration tab). Each user needs
|
||||
an email set in their own Settings for either to reach them.</p>
|
||||
<form method="post" action="/admin/smtp">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
||||
<label>SMTP server
|
||||
<input type="text" name="smtp_host" placeholder="smtp.example.com" value="{{ smtp.smtp_host }}">
|
||||
</label>
|
||||
<label>Port
|
||||
<input type="number" name="smtp_port" min="1" max="65535" value="{{ smtp.smtp_port }}">
|
||||
</label>
|
||||
<label>Username
|
||||
<input type="text" name="smtp_username" autocomplete="off" value="{{ smtp.smtp_username }}">
|
||||
</label>
|
||||
<label>Password
|
||||
<input type="password" name="smtp_password" autocomplete="off"
|
||||
placeholder="{% if smtp.smtp_password %}(unchanged -- enter a new one to replace){% else %}smtp password{% endif %}">
|
||||
</label>
|
||||
<label>From address
|
||||
<input type="text" name="smtp_from_address" placeholder="[email protected]" value="{{ smtp.smtp_from_address }}">
|
||||
</label>
|
||||
<div class="checkbox-row">
|
||||
<input type="checkbox" id="smtp_use_tls" name="smtp_use_tls" value="true" {% if smtp.smtp_use_tls %}checked{% endif %}>
|
||||
<label for="smtp_use_tls">Use STARTTLS</label>
|
||||
</div>
|
||||
<button type="submit">Save SMTP settings</button>
|
||||
</form>
|
||||
<form method="post" action="/admin/smtp/test" style="margin-top: 8px;">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
||||
<button type="submit" class="secondary">Send test email to myself</button>
|
||||
</form>
|
||||
|
||||
<h2 class="card-title" style="margin-top: 24px;">Enroll a user</h2>
|
||||
<form method="post" action="/admin/users">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
||||
|
||||
Reference in New Issue
Block a user