Internal Documentation internal
TalkIDE internal documentation

1. Účel a scope

TalkIDE posílá z BE následující transactional emaily uživatelům:

TriggerPoužité šablonaUC
Forgot password reset linkpassword-resetUC-01005
Waitlist join confirmationwaitlist-confirmationUC-11001
Waitlist invitewaitlist-inviteUC-11004 (TBC pojmenování)
Spending limit warning (80% / 100%) (stub)TBDUC-10005
Hosting dunning (PAST_DUE notice, suspended notice) (plánováno)TBDUC-10015 (DP-3)
GDPR export ready download linkTBDUC-01010 deletion / export flow

Out-of-scope: marketing emails (newsletter, product updates) — řeší se mimo platformu (Mailchimp / dedicated marketing tool). Tento dokument se zabývá výhradně transactional e-maily generovanými uživatelovou akcí.

2. Architektura

flowchart LR
    UC[Use case<br/>ForgotPassword / Waitlist / ...]
    SVC[EmailService<br/>render template + audit]
    SENDER{EmailSender<br/>interface}
    ML[MailgunEmailSender<br/>profile: production]
    NOOP[NoopEmailSender<br/>profile: !production]
    LOG[(EmailLogRepository<br/>email_log table)]
    MG[Mailgun HTTP API<br/>api.mailgun.net]

    UC --> SVC
    SVC --> SENDER
    SVC -- audit --> LOG
    SENDER -.profile binding.-> ML
    SENDER -.profile binding.-> NOOP
    ML -- HTTPS POST multipart --> MG

2.1 EmailSender interface

Provider abstraction. Dvě beany podmíněné Spring profilem (NIKDY @Profile("prod") — viz CLAUDE.md incident #194):

BeanProfileChování
MailgunEmailSender@Profile("production")Skutečné HTTP POST na Mailgun
NoopEmailSender@Profile("!production")Loguje payload do BE loggeru, nic neodesílá

Lokální dev, integration testy a Testcontainers smoke všechny běží v !production → žádné reálné emaily se neposílají.

2.2 Audit log — email_log tabulka

Každý pokus o odeslání (úspěšný i selhaný) zapisuje jeden řádek do email_log. Účel:

  • Debugging doručitelnosti (kterému uživateli kdy co odešlo).
  • Postmortem support („proč mi nepřišel reset link” → grep podle recipient).
  • Budoucí billing usage tracking pokud bude email per-tenant fakturován.

Schéma (viz EmailLogEntity + migrace 0028-create-email-log.xml):

SloupecTypNote
idbigint identityPK
typevarchar(50)Enum EmailType.name() (např. PASSWORD_RESET, WAITLIST_CONFIRMATION)
recipientvarchar(255)Cílový email
subjectvarchar(500)Subject po renderování šablony
provider_message_idvarchar(255) NULLMailgun message-id (z API response); NULL při SENT na Noop, NULL při FAILED
statusvarchar(20)EmailStatus.name(): SENT / FAILED
errortext NULLMailgun error string nebo exception message při FAILED
created_attimestampPokus o odeslání

Status je prostý string (žádný DB enum, žádný check constraint — ADR-025 § 6). To umožní přidat nové stavy bez DB migrace (např. BOUNCED po implementaci webhook handleru).

2.3 Šablony

Šablony jsou Mustache (.mustache) v talkide-be/src/main/resources/templates/email/. Renderování v EmailService.render(template, variables) — totéž jako scaffold templates v ProjectScaffoldService. Subject i body se renderují ze stejné šablony, oddělené na základě konvence (první řádek = subject, zbytek = body) nebo dvou souborů per type.

Variables jsou typed Kotlin data class per EmailType — žádný stringly-typed map. Únik nedefinované proměnné je compile-time error.

3. Konfigurace

3.1 Spring properties

# application.yaml (default = nepoužitelné v prod, jen pro startup)
talkide:
  email:
    provider: mailgun
    from-address: noreply@mail.talkide.app
    from-name: TalkIDE
    mailgun:
      base-url: https://api.mailgun.net   # US region; EU: https://api.eu.mailgun.net
      domain: mail.talkide.app
      # api-key injektován z ENV / Secret, NIKDY v repo
# application-production.yaml
talkide:
  email:
    mailgun:
      api-key: ${MAILGUN_API_KEY}

3.2 K8s Secret

mailgun-secret v ns talkide-prod (přes External Secrets Operator nebo přímý kubectl create secret — v alfa zatím manual). BE pod mountuje jako env var MAILGUN_API_KEY.

3.3 Sending doména DNS

DNS záznamy pro mail.talkide.app musí být Mailgunem ověřené před prvním prod odesláním (viz ADR-025 § 3 — SPF TXT, DKIM TXT, tracking CNAME). Postup pro setup je v talkide-infra runbooks.

4. Bezpečnost a observability

4.1 PII

Recipient email a subject jsou PII. email_log je tedy součástí GDPR data subject access request scope. Při account deletion (UC-01010 deletion flow, viz CLAUDE.md be#138) email_log záznamy pro daného usera se anonymizují (recipient → deleted-<userId>@deleted.local, provider_message_id ponechán pro auditní trail).

4.2 Rate limiting

Per-recipient rate limit pro password reset (UC-01005) je řešen v AuthRateLimitAttempt tabulce a AuthRateLimitFilter — ne v emailové vrstvě. EmailService žádný vlastní rate limit nemá; pokud volající business logika udělá burst (např. dunning pipeline na 1000 tenantů), Mailgun absorpční kapacita je dost (1k+ msg/s pro tier účet).

4.3 Metrics & alerts

  • email_log SUM(status=‘FAILED’) / HOUR — alert nad práh (TBD pro DevOps).
  • Mailgun dashboard má vlastní delivery metrics (delivered, bounced, opened).
  • BE logger zachytává každý FAILED s exception trace.

4.4 Idempotence

Aktuálně EmailService neimplementuje idempotence — pokud business logika zavolá send() dvakrát, uživatel dostane dva e-maily. Akceptovatelné v aktuálních case (každý trigger je přirozeně sólo akce). Pokud by nový case potřeboval idempotenci (např. retry safe scheduler), přidá se idempotency_key sloupec do email_log + UNIQUE constraint, EmailService udělá SELECT-then-INSERT s explicit check.

5. Známé limitace

LimitaceStav
Webhook handlery pro delivered/bounced/failedOut-of-scope MVP. Status v email_log zůstává na SENT i pokud Mailgun následně reportoval bounce.
Multi-locale templates (en/cs)TBD — aktuálně jen en šablony. User locale (UserEntity.locale) je k dispozici v render contextu.
HTML + plain-text alternativaAktuálně jen plain-text. HTML šablony budou až s designově sjednocenými emaily (post-alpha).
Per-tenant sender domainOut-of-scope MVP. Všechny TalkIDE-platform emaily chodí z noreply@mail.talkide.app.
BYO SMTP / per-tenant providerEnterprise feature, out-of-scope.

6. Vztah k jiným specifikacím a UC

ReferenceVztah
ADR-025Architektonické rozhodnutí, zdroj pravdy
UC-01005První produkční konzument — password reset link
UC-11001Druhý produkční konzument — waitlist confirmation
model/README.mdEmailLog entita
limitations.mdWeb-wide známé limitace

Was this page helpful?

Thanks for the feedback.