docs: document Gmail SMTP configuration

Co-Authored-By: Claude <noreply@anthropic.com>

Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
SJCoetz 2026-08-03 01:21:22 +02:00
parent c94c3d36ad
commit 135a6e9fe6
4 changed files with 7 additions and 46 deletions

View file

@ -17,7 +17,7 @@ Rebuild of a Flask back-office for a SA fuel-delivery business. Customers, deliv
- **TanStack Query / Table** installed but we mostly use RSC for data
- **Radix UI** primitives wrapped as shadcn/ui-style components
- **pdf-lib** for PDFs in Node (statements + 7 report types)
- **Resend** for transactional email
- **Gmail SMTP via Nodemailer** for transactional email
- **date-fns**, **lucide-react**, **zod** (Zod 4 — `error.issues`, not `error.errors`)
## Dev server
@ -70,9 +70,11 @@ Rebuild of a Flask back-office for a SA fuel-delivery business. Customers, deliv
- IDs: `pbId()` (15 lowercase chars) in `lib/pb.ts`
- Cleaning empty form values: `cleanEmpty()` in `lib/delivery-notes.ts`
## Email (Resend)
- Set `RESEND_API_KEY` and `RESEND_FROM` in `.env.local` to send statements
- Without it, statement send fails with a clear error; preview still works
## Email (Gmail SMTP)
- Set `GMAIL_SMTP_USER` to `rjbulk01@gmail.com` and `GMAIL_SMTP_APP_PASSWORD` to a Google app password in `.env.local` or the production environment.
- Gmail SMTP uses `smtp.gmail.com` on port 587 with STARTTLS.
- The app sends statements and test emails from `rjbulk01@gmail.com` and does not use a default CC.
- Without the Gmail variables, statement sending and test-email sending fail with a clear error; statement preview still works.
## Build
- `pnpm build` compiles cleanly

View file

@ -3,7 +3,7 @@
#
# Build context: apps/web/ (set "Base Directory" in Coolify)
# Required env at runtime: PORT (default 4004), HOSTNAME=0.0.0.0,
# POCKETBASE_URL, RESEND_API_KEY, RESEND_FROM
# POCKETBASE_URL, GMAIL_SMTP_USER, GMAIL_SMTP_APP_PASSWORD
# ─── Stage 1: install deps ────────────────────────────────────────────────────
FROM node:22-bookworm-slim AS deps

View file

@ -40,7 +40,6 @@
"react-dom": "19.2.4",
"react-hook-form": "^7.81.0",
"recharts": "^3.9.2",
"resend": "^6.17.2",
"sonner": "^2.0.7",
"tailwind-merge": "^3.6.0",
"zod": "^4.4.3"

View file

@ -95,9 +95,6 @@ importers:
recharts:
specifier: ^3.9.2
version: 3.9.2(@types/react@19.2.17)(react-dom@19.2.4(react@19.2.4))(react-is@16.13.1)(react@19.2.4)(redux@5.0.1)
resend:
specifier: ^6.17.2
version: 6.17.2
sonner:
specifier: ^2.0.7
version: 2.0.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
@ -1157,9 +1154,6 @@ packages:
'@rtsao/scc@1.1.0':
resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==}
'@stablelib/base64@1.0.1':
resolution: {integrity: sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==}
'@standard-schema/spec@1.1.0':
resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
@ -2043,9 +2037,6 @@ packages:
fast-levenshtein@2.0.6:
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
fast-sha256@1.3.0:
resolution: {integrity: sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==}
fastq@1.20.1:
resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==}
@ -2697,9 +2688,6 @@ packages:
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
engines: {node: '>= 0.4'}
postal-mime@2.7.4:
resolution: {integrity: sha512-0WdnFQYUrPGGTFu1uOqD2s7omwua8xaeYGdO6rb88oD5yJ/4pPHDA4sdWqfD8wQVfCny563n/HQS7zTFft+f/g==}
postcss@8.4.31:
resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==}
engines: {node: ^10 || ^12 || >=14}
@ -2830,15 +2818,6 @@ packages:
reselect@5.2.0:
resolution: {integrity: sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw==}
resend@6.17.2:
resolution: {integrity: sha512-hbaXEORFIFfT2Bh03NsA/akTTTKkD1hiuJ88ke64c5dWVV6DyoLxzFve3OiZqVOQ+JKLJ5uVkPR6hlUslpJFoA==}
engines: {node: '>=20'}
peerDependencies:
'@react-email/render': '*'
peerDependenciesMeta:
'@react-email/render':
optional: true
resolve-from@4.0.0:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
engines: {node: '>=4'}
@ -2944,9 +2923,6 @@ packages:
stable-hash@0.0.5:
resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==}
standardwebhooks@1.0.0:
resolution: {integrity: sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==}
stop-iteration-iterator@1.1.0:
resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==}
engines: {node: '>= 0.4'}
@ -4128,8 +4104,6 @@ snapshots:
'@rtsao/scc@1.1.0': {}
'@stablelib/base64@1.0.1': {}
'@standard-schema/spec@1.1.0': {}
'@standard-schema/utils@0.3.0': {}
@ -5151,8 +5125,6 @@ snapshots:
fast-levenshtein@2.0.6: {}
fast-sha256@1.3.0: {}
fastq@1.20.1:
dependencies:
reusify: 1.1.0
@ -5751,8 +5723,6 @@ snapshots:
possible-typed-array-names@1.1.0: {}
postal-mime@2.7.4: {}
postcss@8.4.31:
dependencies:
nanoid: 3.3.16
@ -5921,11 +5891,6 @@ snapshots:
reselect@5.2.0: {}
resend@6.17.2:
dependencies:
postal-mime: 2.7.4
standardwebhooks: 1.0.0
resolve-from@4.0.0: {}
resolve-pkg-maps@1.0.0: {}
@ -6077,11 +6042,6 @@ snapshots:
stable-hash@0.0.5: {}
standardwebhooks@1.0.0:
dependencies:
'@stablelib/base64': 1.0.1
fast-sha256: 1.3.0
stop-iteration-iterator@1.1.0:
dependencies:
es-errors: 1.3.0