Skip to content
prod 352bb92
Browse

4 · Email (SMTP)

Objective — wire transactional email and prove it actually sends — the hard part is finding where SMTP resolves at runtime, because the most common vendor family loads it from the DB and the panel’s warning banner lies about it.

Steps at a glance:

  1. Find the real source of truth — Before touching any field, decide where SMTP config actually resolves at runtime. The brand survey (page 2) already flagged whether the vendor ships a CustomConfigProvider; confirm it now.
  2. Configure SMTP in the admin panelAdmin → Config → SMTP (or Settings → Email). The human enters the password/secret field; the agent fills non-secret fields, clicks the panel test, and checks logs when browser automation is available. Inbox confirmation stays human-owned unless the operator explicitly shares that mailbox session.
  3. Set a sensible .env fallback (defence in depth) — Even when the DB path is truth, leave real MAIL_* values in shared/.env. If the email_settings row is corrupted or the provider query fails, Laravel falls back to .env.
  4. Review email templates & hygiene — audit the app’s email templates and sender hygiene, so customer-facing messages are branded, deliverable, and not leaking vendor defaults.

Transactional email is a MUST — password resets, invoices, and notifications all depend on it, and section 1 of page 3 warned you not to change the admin email until mail works. The hard part isn’t the SMTP fields; it’s knowing where the app actually reads them from — and on the most common CodeCanyon vendor family, the admin UI actively lies about that.

Before editing mail settings, read Zaj-PROJECT.md for the selected environment, mail provider, from-address, vault item pointer, and whether runtime mail config is DB-owned or .env-owned. Update those non-secret facts after verification; never store SMTP passwords there.

Before touching any field, decide where SMTP config actually resolves at runtime. The brand survey (page 2) already flagged whether the vendor ships a CustomConfigProvider; confirm it now.

flowchart TD
Q{"Does a CustomConfigProvider /<br/>SettingsProvider rewrite mail config<br/>at boot from a DB table?"}
Q -->|Yes — Froiden family| DB["DB table is truth<br/>(email_settings).<br/>Configure the ADMIN PANEL.<br/>.env MAIL_* = fallback only"]
Q -->|No admin Email tab at all| ENV["Configure shared/.env MAIL_*<br/>(see Phase 8 email infra)"]
Q -->|Panel present, no provider| PANEL["Admin panel writes config<br/>directly — use it"]
  1. Grep for a provider that rewrites mail config at runtime.

    Terminal window
    grep -rn "mail\.mailers\.smtp\|Config::set.*mail" app/Providers/ app/Http/Middleware/ 2>/dev/null | head
    find app/ -name "*ConfigProvider*" -o -name "*SettingsProvider*" 2>/dev/null
    # Expected: a provider that calls Config::set('mail...'), or no match
    • ✅ If a provider calls Config::set('mail.mailers.smtp.*', DB::table(...)), the database overrides .env and the admin panel is the source of truth; otherwise .env is.

The lesson generalizes beyond this one banner:

Admin → Config → SMTP (or Settings → Email). This is a mixed browser flow: human-owned secret fields, agent-owned non-secret fields, save/test clicks, banner checks, and log verification when browser automation is available.

  1. Fill the SMTP fields from your Phase 4 SMTP credentials + brand profile.

    FieldValue
    Mail From NameApp name (brand profile)
    Mail From Emailadmin@[DOMAIN]
    SMTP Hoste.g. smtp.hostinger.com
    SMTP Port587 (TLS) or 465 (SSL)
    Username / Passwordmailbox creds — the human enters the password from the vault; the agent never types or reads it
    EncryptionTLS (587) / SSL (465)
    • ✅ All fields are filled from real credentials; the password was human-entered from the vault and never exposed to the agent.
  2. Save, then click the panel’s own “Test SMTP” button — not artisan tinker. Tinker sends through Laravel’s default mailer, which a CLI context may not have reconfigured via the provider yet.

    • ✅ “Test SMTP” succeeds and the dashboard “SMTP details are not correct” banner disappears — that’s your real success signal.
  3. Trigger a password-reset on a test account and confirm the email arrives.

    • ✅ A real password-reset email lands end-to-end in the inbox.

3. Set a sensible .env fallback (defence in depth)

Section titled “3. Set a sensible .env fallback (defence in depth)”

Even when the DB path is truth, leave real MAIL_* values in shared/.env. If the email_settings row is corrupted or the provider query fails, Laravel falls back to .env. Real values = mail still works; garbage values = mail breaks silently.

  1. Append the fallback idempotently (Python-free here, but grep -q guards against duplicates; special-char passwords stay out of this block).

    Terminal window
    grep -q "^MAIL_HOST=" shared/.env || cat >> shared/.env <<'EOF'
    # SMTP fallback (.env defaults; runtime config overridden by CustomConfigProvider from DB)
    MAIL_MAILER=smtp
    MAIL_HOST=smtp.hostinger.com
    MAIL_PORT=465
    MAIL_ENCRYPTION=ssl
    MAIL_FROM_ADDRESS=admin@[DOMAIN]
    MAIL_FROM_NAME="[APP_NAME]"
    EOF
    # Expected: the MAIL_* block is appended once (no duplicate on re-run)
    • shared/.env carries real MAIL_* fallback values, appended only once.
  1. Review the email templates (if the vendor exposes Admin → Email Templates) — welcome, password-reset, invoice, and notification — setting the sender name, logo, and footer to match your brand (the email header logo you uploaded on page 3 lives here on some vendors).

    • ✅ Sender name, logo, and footer match the brand on every reviewed template.
  2. Confirm no SMTP credentials are committed to git — they belong in shared/.env on the server and your password manager only.

    • ✅ No SMTP secrets are tracked in git.

Do not mark this step done until every box below is checked.

  • 🤖 Source confirmed — where SMTP resolves at runtime (DB-via-provider vs .env) is confirmed; the Froiden banner was verified against source, not trusted.
  • 🔀 Panel SMTP saved — human entered secrets; agent saved/tested the panel when browser automation was available; “Test SMTP” passes and the dashboard error banner is gone.
  • 👤 Reset email received — a real password-reset email arrived end-to-end, or the mailbox-only check is tracked with owner/trigger.
  • 🤖 .env fallback setshared/.env carries real MAIL_* fallback values; no SMTP secrets in git.
  • 👤 Templates reviewed — sender, logo, footer reviewed if the vendor exposes them.
  • 🤖 Project state updatedZaj-PROJECT.md records the mail provider, from-address/name, runtime source of truth (admin DB vs .env), vault pointer, and last verified date without secrets.