feat: SMTP email support for invite links + profile.json for user email storage

- email_utils.py: send_email() via smtplib.SMTP_SSL (port 465, same server
  as AE API); send_invite_email() renders plain-text + HTML invite template
- config.py: smtp_server, smtp_port, smtp_username, smtp_password,
  smtp_from_email, smtp_from_name, cortex_base_url settings
- manage_passwords.py:
  - profile.json helpers (get/set email stored in home/{username}/profile.json)
  - invite command now accepts optional email arg, sends invite automatically;
    falls back to stored email; prints link either way
  - new 'email' command to store/update a user's email address
  - 'list' command now shows email alongside password status
- .env.default: SMTP_* and CORTEX_BASE_URL documented

Usage after adding SMTP_PASSWORD to .env:
  python manage_passwords.py invite holly holly@example.com
  → generates token, stores email, sends invite, prints link as fallback

All 80 tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Scott Idem
2026-03-20 23:19:09 -04:00
parent 46b65d087c
commit 69f38ca7dc
4 changed files with 219 additions and 18 deletions

View File

@@ -18,6 +18,16 @@ USER_NAME=Scott
JWT_SECRET=change-me-in-dotenv
JWT_EXPIRE_DAYS=30
# ── SMTP (invite emails + future notifications) ───────────────────────────────
SMTP_SERVER=linode.oneskyit.com
SMTP_PORT=465
SMTP_USERNAME=send_mail
SMTP_PASSWORD=
SMTP_FROM_EMAIL=noreply@oneskyit.com
SMTP_FROM_NAME=Cortex
# Base URL included in invite links
CORTEX_BASE_URL=https://cortex.dgrzone.com
# ── Server ──────────────────────────────────────────────────────────────────
HOST=0.0.0.0
PORT=8000