email-system

star 121

Complete documentation of the email subsystem including template resolution, variable pipeline, rendering engines, SMTP config, and default templates.

gjovanovicst By gjovanovicst schedule Updated 3/4/2026

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 JSONB
  • EmailTemplate (pkg/models/email_template.go) -- templates scoped to app or global
  • EmailServerConfig (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.

Install via CLI
npx skills add https://github.com/gjovanovicst/golang-auth-api --skill email-system
Repository Details
star Stars 121
call_split Forks 18
navigation Branch main
article Path SKILL.md
More from Creator
gjovanovicst
gjovanovicst Explore all skills →