project-page active pal-e-mail
project-pal-e-mail updated 2026-03-22

Vision

Centralized, multi-tenant email service for the Pal-E platform. Any project sends branded emails via one API call. Email content lives here — not in app repos. Sending infrastructure (Gmail OAuth, token refresh, delivery logging) is platform-level, not duplicated per project. Templates compile from MJML repos and deploy to MinIO CDN. Custom composed emails wrap in project-specific brand layouts. Self-hosted, zero SaaS dependencies.

User Stories

Role Key Story Success Metric
Platform Admin (Lucas) trigger-send Trigger bulk emails from admin panels (jersey reminders, tryout announcements, roster exports) without managing Gmail OAuth in each app repo One POST /send call per recipient. Zero email code in app repos.
Platform Admin (Lucas) delivery-log See who got what email, when, and whether it failed — across all projects from one log GET /log with project, recipient, template, timestamp, status filters
Platform Admin (Lucas) schedule-send Schedule an email for future delivery so I can prepare now and send later POST /send with send_at queues for delivery. GET /scheduled to view/cancel pending.
Platform Admin (Lucas) sender-management See which Gmail senders are healthy vs expired and re-authorize from a browser — no SSH or kubectl GET /senders shows status. Browser-based re-auth flow when token expires.
Platform Admin (Lucas) template-update Update email templates without redeploying app code CI compiles MJML → uploads to MinIO CDN → pal-e-mail fetches at send time
App Service (automated) api-send Send registration confirmations, password resets, and notifications via centralized service App calls POST /send. No Gmail logic, no OAuth, no HTML rendering in app repo.
End User (parents) branded-email Receive professional, mobile-responsive emails that render in Gmail/Outlook/Apple Mail MJML-compiled HTML with brand wrapper renders correctly across all major clients

Consumer Projects

Projects that send email through pal-e-mail:

Project Sender Email Types Status
Westside Basketball westsidebasketball@gmail.com Jersey reminders, tryout announcements, roster exports, profile reminders, registration confirmations, password resets Phase 4 migration (currently in basketball-api)
mcd-tracker (future) Receipt confirmations, weekly summaries Backlog
pal-e-docs (future) System notifications, share invitations Backlog

Plan

Active: plan-pal-e-mail (to be created)

Board

board-pal-e-mail (to be created)

Status

Phase 2 complete. Core Send API merged (PR #4). POST /send supports template mode (CDN fetch + {{var}} replacement), custom HTML mode (brand wrapper), and plain text. GET /log provides cross-project queryable email history with filtering and pagination. 28 tests, Woodpecker CI green. Phase 1 deployed: pod healthy, ArgoCD synced, Postgres with email_log table, Gmail OAuth PVC. MinIO network policy applied (pal-e-platform#144 — merged). Template CDN fetching unblocked.

Milestones

None yet.

Architecture

System Overview

Any App API                          pal-e-mail service
  POST /send ──────────────────────► FastAPI
  {sender, to, template, data}         │
                                       ├─► Fetch template from MinIO CDN (public URL)
                                       │   https://minio-api.ts.net/assets/email-templates/{project}/
                                       │
                                       ├─► String replace {{variables}}
                                       │
                                       ├─► Load Gmail OAuth creds for sender
                                       │   /secrets/gmail/{sender}.json
                                       │
                                       ├─► Gmail API send
                                       │
                                       └─► Log to email_log table (project, to, template, status)

Key Architectural Decisions

  • Templates on MinIO CDN, not ConfigMaps — MJML CI compiles and uploads to assets/email-templates/{project}/. Public-read. Service fetches via HTTP at send time. No k8s volume mounts, no pod restarts for template changes.
  • minio-api for uploads, CDN for reads — CI uploads templates through authenticated minio-api. Service reads via public CDN URL. Clean separation.
  • Multi-tenant Gmail OAuth — one token file per sender identity. Service loads the right credentials based on sender field. Tokens on writable volume for refresh.
  • Template OR custom HTML — POST /send accepts either template (fetched from CDN) or html (raw HTML, wrapped in brand layout). Supports both templated and ad-hoc emails.
  • No Jinja2, no template engine — simple {{variable}} string replacement. MJML handles the rendering. Python handles the data injection.
  • Westside-emails migrates in — existing westside-emails repo continues to own MJML source. CI uploads compiled HTML to MinIO. pal-e-mail fetches from there.

Repos

Repo Platform Role Status
pal-e-mail Forgejo FastAPI email sending service — multi-tenant, template or custom, Gmail OAuth planned
westside-emails Forgejo MJML templates for Westside Basketball (compiles to HTML, uploads to MinIO) active (migrates to CDN delivery)

Inbox

Query: list_board_items(board_slug="board-pal-e-mail", column="backlog")