mirror of
https://github.com/screentinker/screentinker.git
synced 2026-08-13 13:53:12 -06:00
Adds a pluggable email transport so self-hosters without Azure/M365 can send
mail through any standard SMTP server (Postfix, Gmail, Mailgun, SendGrid, corp
relay). Graph stays the default; behavior is byte-for-byte unchanged when
EMAIL_TRANSPORT is unset or "graph".
- config: EMAIL_TRANSPORT ("graph"|"smtp", default graph) + SMTP_HOST/PORT/
SECURE/USER/PASSWORD/FROM.
- services/email.js: branch by transport behind the SAME public sendEmail()/
isConfigured() surface. SMTP via nodemailer (lazy-required, like MSAL).
Shared across both transports: the "[ScreenTinker] " subject prefix (unless
rawSubject), the GRAPH_DEV_RESTRICT_TO allow-list, html-from-text derivation,
and the never-throws contract (failures log + return sent:false). SMTP_SECURE
true=implicit TLS(465)/false=STARTTLS(587). Auth optional (unauthenticated
relay ok); SMTP_USER without SMTP_PASSWORD is flagged. SMTP_FROM parses
"Name <addr>". New emailConfigStatus() for startup diagnostics.
- server.js: startup logs the transport and a LOUD error when the selected
transport is partially configured (some fields set, others missing) or when
EMAIL_TRANSPORT is invalid (falls back to graph). A fully-unset transport
stays a silent stdout fallback (unchanged dev behavior).
- nodemailer ^6.9.16 added as a production dep (bundled in the Docker image).
- .env.example + README: SMTP config section, Gmail example, transport table.
- test/email-transport.test.js: 15 tests — transport selection, config
validation (missing/partial/invalid), SMTP message building (from/prefix/
fromName override/text alt), sendEmail routing (mocked nodemailer), rawSubject,
dev-restrict on smtp, and the smtp_error never-throws path.
462/462 server tests pass. Boot verified for all four states (configured,
misconfigured, invalid, default).
Closes #173
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
74 lines
3.6 KiB
Plaintext
74 lines
3.6 KiB
Plaintext
# ScreenTinker server configuration — process environment variables.
|
|
#
|
|
# NOTE: the app reads these from the *process environment* (your systemd unit's
|
|
# Environment=/EnvironmentFile=, your container runtime, or your shell). It does
|
|
# NOT auto-load this file. Copy the values you need into your process manager.
|
|
# This file exists only to document the available options.
|
|
|
|
# --- Self-hosting ---
|
|
# Set to "true" on your own instance. When true, the hosted signup emails
|
|
# (welcome to the user + admin notification) are disabled, so a self-hosted
|
|
# instance never emits mail from a domain that isn't yours.
|
|
SELF_HOSTED=true
|
|
|
|
# Hide the Subscription/billing UI (nav item + pricing cards) and bounce #/billing to
|
|
# the dashboard. Opt-in; default off (billing shown). For instances that bill customers
|
|
# externally and don't sell plans through the app. UI-only — does not change SELF_HOSTED
|
|
# or disable any /api/subscription endpoints.
|
|
HIDE_BILLING=true
|
|
|
|
# Close public self-service registration — for instances where all accounts are
|
|
# provisioned by your team (admin "Add user" / invites). When true, the public
|
|
# signup route is blocked (OAuth auto-signup with it) AND the login page hides
|
|
# its "Create account" button so the UI matches the backend. First-user setup on
|
|
# an empty DB is still allowed so a fresh install can be initialized.
|
|
# DISABLE_REGISTRATION=true
|
|
|
|
# Redirect "/" to the app (/app) instead of serving the marketing landing page.
|
|
# For internal-only deployments that don't want the public homepage shown.
|
|
# DISABLE_HOMEPAGE=true
|
|
|
|
# Where new-signup admin notifications are sent. Leave UNSET to disable admin
|
|
# notifications entirely — the user's welcome email is unaffected. Self-hosters
|
|
# who want to be notified of signups set this to their own address.
|
|
# ADMIN_NOTIFY_EMAIL=you@example.com
|
|
|
|
# Marks THIS deployment as the hosted (screentinker.com) instance. Gates the
|
|
# daily activation-nudge sweep (the T+3 "haven't paired a screen yet?" email).
|
|
# Leave UNSET on self-hosted instances so a daily bulk sweep never emails your
|
|
# user base with our onboarding mail. Only the hosted instance sets this true.
|
|
# HOSTED_INSTANCE=true
|
|
|
|
# --- Outbound email ---
|
|
# Email transport: "graph" (default, Microsoft Graph) or "smtp" (any mail server).
|
|
# Required for ANY email (welcome, offline alerts, admin notify) to actually send.
|
|
# Leave the selected transport blank and the app logs "[EMAIL] not configured"
|
|
# instead of sending. A partially-configured transport logs a clear error at boot.
|
|
# EMAIL_TRANSPORT=graph
|
|
|
|
# --- Email via Microsoft Graph (client-credentials flow; EMAIL_TRANSPORT=graph) ---
|
|
# GRAPH_TENANT_ID=
|
|
# GRAPH_CLIENT_ID=
|
|
# GRAPH_CLIENT_SECRET=
|
|
# GRAPH_SENDER_EMAIL=signage@example.com
|
|
# GRAPH_SENDER_NAME=ScreenTinker
|
|
|
|
# --- Email via SMTP (alternative to Microsoft Graph; EMAIL_TRANSPORT=smtp) ---
|
|
# For self-hosters without Azure/M365: Postfix, Gmail, Mailgun, SendGrid, a corp
|
|
# relay, etc. SMTP_SECURE=true is implicit TLS on 465; false is STARTTLS on 587.
|
|
# SMTP_USER/SMTP_PASSWORD are optional (omit both for an unauthenticated relay);
|
|
# if SMTP_USER is set, SMTP_PASSWORD is required. SMTP_FROM accepts "Name <addr>".
|
|
# EMAIL_TRANSPORT=smtp
|
|
# SMTP_HOST=mail.example.com
|
|
# SMTP_PORT=587
|
|
# SMTP_SECURE=false
|
|
# SMTP_USER=noreply@example.com
|
|
# SMTP_PASSWORD=your-smtp-password
|
|
# SMTP_FROM=ScreenTinker <noreply@example.com>
|
|
|
|
# Dev safety net (applies to BOTH transports): comma-separated allow-list of
|
|
# recipients. When set, mail to any address NOT in the list is suppressed
|
|
# (logged, not sent). Leave UNSET in production. Useful locally so test signups
|
|
# can't email real users.
|
|
# GRAPH_DEV_RESTRICT_TO=me@example.com
|