name: email-system description: Complete documentation of the email subsystem including template resolution, variable pipeline, rendering engines, SMTP config, and default templates. license: MIT
Architecture
The email system is in internal/email/ and follows a multi-layered pipeline:
Service (orchestrator)
|-- VariableResolver -> resolves template variables from 4 sources
|-- Renderer -> renders templates with 3 supported engines
|-- Sender -> sends via SMTP (or logs in dev mode)
|-- Repository -> DB access for types, templates, server configs
Entry point: email.Service.SendEmailWithContext(appID, emailTypeCode, toEmail, userID, vars)
Send Pipeline
1. VariableResolver.ResolveVariables() -- build final variable map
2. resolveTemplate() -- find the right template
3. Renderer.RenderTemplate() -- render subject, HTML, text
4. resolveSMTPConfigForTemplate() -- find the right SMTP config
5. Sender.Send() -- deliver via SMTP
Template Resolution Chain
Defined in internal/email/service.go method resolveTemplate:
1. DB: app-specific template
WHERE app_id = ? AND email_type_id = ? AND is_active = true
2. DB: global default template
WHERE app_id IS NULL AND email_type_id = ? AND is_active = true
3. Hardcoded fallback
defaults.go -> GetDefaultTemplate(typeCode)
SMTP Resolution Chain
Defined in internal/email/service.go method resolveSMTPConfig:
1. DB: per-app config
WHERE app_id = ? AND is_active = true AND is_default = true
2. DB: global config
WHERE app_id IS NULL AND is_active = true AND is_default = true
3. Empty config -> dev/fallback mode (log to stdout)
With template override (resolveSMTPConfigForTemplate):
1. If template has ServerConfigID -> use that specific config by ID
2. Otherwise -> standard chain above
3. Apply template-level FromEmail/FromName overrides on top
Variable Resolution Pipeline
Defined in internal/email/resolver.go. Resolution priority (lowest to highest):
| Layer | Source | Method | Example Variables |
|---|---|---|---|
| 1 (lowest) | Static defaults | applyStaticDefaults |
DefaultValue from email_types.variables JSONB |
| 2 | App/system settings | applySettingsVars |
app_name, frontend_url |
| 3 | User profile | applyUserVars |
user_email, user_name, first_name, last_name, locale, profile_picture |
| Always | Fallback | -- | user_email = toEmail if not set |
| 4 (highest) | Explicit caller vars | direct map copy | verification_link, code, reset_link, etc. |
Variable Sources
| Source | Variables | How Resolved |
|---|---|---|
user (auto) |
user_email, user_name, first_name, last_name, locale, profile_picture |
From DB user record |
setting (auto) |
app_name, frontend_url |
app name from DB/env, frontend_url from env |
explicit (caller) |
verification_link, verification_token, reset_link, code, expiration_minutes, change_time, magic_link |
Must be passed by calling code |
All variable constants are in internal/email/types.go.
Template Engines
Defined in internal/email/renderer.go:
| Engine | Constant | Syntax | HTML Escaping |
|---|---|---|---|
| Go Template | go_template |
{{.AppName}} or {{.app_name}} |
Yes (automatic) |
| Placeholder | placeholder |
{app_name} |
No |
| Raw HTML | raw_html |
{{.AppName}} |
No (unsafe) |
The renderer converts all variables to both snake_case and PascalCase forms, so templates can use either style.
Subject lines always use Go template syntax regardless of engine.
Email Types (7 built-in)
Constants in internal/email/types.go:
| Code | Constant | Variables Used |
|---|---|---|
email_verification |
TypeEmailVerification | app_name, verification_link, user_email |
password_reset |
TypePasswordReset | app_name, reset_link, expiration_minutes |
two_fa_code |
TypeTwoFACode | app_name, code, expiration_minutes |
welcome |
TypeWelcome | app_name, user_email |
account_deactivated |
TypeAccountDeactivated | app_name, user_email |
password_changed |
TypePasswordChanged | app_name, change_time, user_email |
magic_link |
TypeMagicLink | app_name, magic_link, expiration_minutes |
Default Templates
Defined in internal/email/defaults.go. All use go_template engine, provide both HTML and plain text bodies, and use responsive table-based layout with consistent styling (indigo #4f46e5 primary color, 600px max width).
Service Methods
Typed Send Methods (in internal/email/service.go)
| Method | Email Type | Extra Vars |
|---|---|---|
SendVerificationEmail |
email_verification | verification_link (built from FRONTEND_URL + token) |
SendPasswordResetEmail |
password_reset | reset_link, expiration_minutes=60 |
Send2FACodeEmail |
two_fa_code | code, expiration_minutes=5 |
SendWelcomeEmail |
welcome | (none) |
SendAccountDeactivatedEmail |
account_deactivated | (none) |
SendPasswordChangedEmail |
password_changed | change_time |
SendMagicLinkEmail |
magic_link | magic_link, expiration_minutes=10 |
SendAdmin2FACodeEmail |
N/A (hardcoded HTML) | Bypasses app-scoped resolution |
SendAdminMagicLinkEmail |
N/A (hardcoded HTML) | Bypasses app-scoped resolution |
Management Methods
Template CRUD: GetTemplatesByApp, GetGlobalDefaultTemplates, GetTemplateByID, SaveAppTemplate, SaveGlobalTemplate, DeleteTemplate, ResetTemplateToDefault, PreviewTemplate
Email type CRUD: GetAllEmailTypes, GetEmailTypeByCode, CreateEmailType, UpdateEmailType, DeleteEmailType
Server config CRUD: GetServerConfig, SaveServerConfig, DeleteServerConfig, GetAllServerConfigs, etc.
SMTP Sending (internal/email/sender.go)
Uses gopkg.in/mail.v2.
Dev mode: If SMTP host is empty or "smtp.example.com", logs email to stdout (no error).
TLS handling:
- Port 465: implicit SSL (
d.SSL = true) - Other ports:
MandatoryStartTLS, MinVersion TLS 1.2
Error behavior:
Send(): on SMTP failure, logs email as fallback and returns nil (email failure doesn't break business flow)SendTest(): always returns errors (for admin testing)
Database Models
EmailType(pkg/models/email_type.go) -- types with variable definitions as JSONBEmailTemplate(pkg/models/email_template.go) -- templates scoped to app or globalEmailServerConfig(pkg/models/email_server_config.go) -- SMTP configs scoped to app or global
When To Use This Skill
Load this skill when working on email sending, email templates, SMTP configuration, template variables, or any email-related feature.