Wire production email secrets through Forgejo deploy.
Deploy workflow now writes MAIL_* and APP_PUBLIC_BASE_URL from Actions secrets into the server .env so Resend SMTP works after domain verify. Document Resend-only setup, Forgejo secret names, and prod expose-token off. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
86fb946e33
commit
ad195fd890
6 changed files with 70 additions and 51 deletions
|
|
@ -32,11 +32,11 @@ APP_PUBLIC_BASE_URL=http://localhost:3000
|
|||
# ---------- SMTP (local Docker uses Mailpit via docker-compose.yml) ----------
|
||||
# docker compose up → view mail at http://localhost:8025
|
||||
# Leave MAIL_HOST unset in .env to use compose defaults (mailpit).
|
||||
# Production: use Resend/Brevo SMTP — see README "Email (password reset)"
|
||||
# Production (Resend SMTP) — see docs/production-email-checklist.md
|
||||
# MAIL_HOST=smtp.resend.com
|
||||
# MAIL_PORT=587
|
||||
# MAIL_USERNAME=
|
||||
# MAIL_PASSWORD=
|
||||
# MAIL_USERNAME=resend
|
||||
# MAIL_PASSWORD=re_... # API key; never commit a real value
|
||||
# MAIL_FROM=noreply@bilhej.se
|
||||
|
||||
# ---------- Production admin (prod profile only) ----------
|
||||
|
|
|
|||
|
|
@ -39,6 +39,12 @@ jobs:
|
|||
SWISH_NUMBER: ${{ secrets.SWISH_NUMBER }}
|
||||
ADMIN_EMAIL: ${{ secrets.ADMIN_EMAIL }}
|
||||
ADMIN_PASSWORD: ${{ secrets.ADMIN_PASSWORD }}
|
||||
APP_PUBLIC_BASE_URL: ${{ secrets.APP_PUBLIC_BASE_URL }}
|
||||
MAIL_HOST: ${{ secrets.MAIL_HOST }}
|
||||
MAIL_PORT: ${{ secrets.MAIL_PORT }}
|
||||
MAIL_USERNAME: ${{ secrets.MAIL_USERNAME }}
|
||||
MAIL_PASSWORD: ${{ secrets.MAIL_PASSWORD }}
|
||||
MAIL_FROM: ${{ secrets.MAIL_FROM }}
|
||||
run: |
|
||||
# Docker Compose treats $ as variable interpolation in .env files.
|
||||
# Escape literal dollar signs (e.g. in passwords) as $$.
|
||||
|
|
@ -54,6 +60,12 @@ jobs:
|
|||
printf 'SWISH_NUMBER=%s\n' "$(escape "$SWISH_NUMBER")"
|
||||
printf 'ADMIN_EMAIL=%s\n' "$(escape "$ADMIN_EMAIL")"
|
||||
printf 'ADMIN_PASSWORD=%s\n' "$(escape "$ADMIN_PASSWORD")"
|
||||
printf 'APP_PUBLIC_BASE_URL=%s\n' "$(escape "${APP_PUBLIC_BASE_URL:-https://bilhej.se}")"
|
||||
printf 'MAIL_HOST=%s\n' "$(escape "$MAIL_HOST")"
|
||||
printf 'MAIL_PORT=%s\n' "$(escape "${MAIL_PORT:-587}")"
|
||||
printf 'MAIL_USERNAME=%s\n' "$(escape "$MAIL_USERNAME")"
|
||||
printf 'MAIL_PASSWORD=%s\n' "$(escape "$MAIL_PASSWORD")"
|
||||
printf 'MAIL_FROM=%s\n' "$(escape "${MAIL_FROM:-noreply@bilhej.se}")"
|
||||
} > .env
|
||||
|
||||
- name: Build and start production stack
|
||||
|
|
|
|||
|
|
@ -188,7 +188,7 @@ entity must NOT have an address field. The address lookup and mailing are
|
|||
external/human processes in Phase 0.
|
||||
|
||||
### Local email (Mailpit)
|
||||
`docker compose up` includes Mailpit (`ghcr.io/axllent/mailpit:v1.28`); password-reset mail appears at http://localhost:8025. E2E verifies SMTP via Mailpit API (`frontend/e2e/helpers/mailpit.ts`). Production uses transactional SMTP (Resend/Brevo)—see README.
|
||||
`docker compose up` includes Mailpit (`ghcr.io/axllent/mailpit:v1.28`); password-reset mail appears at http://localhost:8025. E2E verifies SMTP via Mailpit API (`frontend/e2e/helpers/mailpit.ts`). Production uses Resend SMTP—see docs/production-email-checklist.md.
|
||||
|
||||
### Password reset test token (never in production)
|
||||
`app.password-reset.expose-token` must stay **false** in prod/default; it is only enabled in `application-docker.yml` for CI E2E so Playwright can read `testToken` from the forgot-password response.
|
||||
|
|
|
|||
23
README.md
23
README.md
|
|
@ -202,23 +202,24 @@ docker pull ghcr.io/axllent/mailpit:v1.28
|
|||
To disable Mailpit and log links only, remove `MAIL_HOST` from the backend service in
|
||||
`docker-compose.yml` or set `MAIL_HOST=` in `.env`.
|
||||
|
||||
**Production (transactional provider):** Use [Resend](https://resend.com) or
|
||||
[Brevo](https://www.brevo.com)—not a self-hosted mail server on the VPS.
|
||||
**Production:** [Resend](https://resend.com) via SMTP (no Resend Java SDK required). See
|
||||
[docs/production-email-checklist.md](docs/production-email-checklist.md).
|
||||
|
||||
1. Sign up and add domain **bilhej.se**
|
||||
2. Add the provider’s **SPF** and **DKIM** DNS records at your registrar (no MX needed for send-only)
|
||||
3. Create SMTP credentials in the provider dashboard
|
||||
4. On the production server `.env` (or Forgejo deploy secrets):
|
||||
1. Verify domain **bilhej.se** in Resend (SPF + DKIM DNS records)
|
||||
2. Create an API key (`re_...`)
|
||||
3. On the production server `.env`:
|
||||
|
||||
```bash
|
||||
APP_PUBLIC_BASE_URL=https://bilhej.se
|
||||
MAIL_HOST=smtp.resend.com # example; use your provider’s host
|
||||
MAIL_HOST=smtp.resend.com
|
||||
MAIL_PORT=587
|
||||
MAIL_USERNAME=resend # example
|
||||
MAIL_USERNAME=resend
|
||||
MAIL_PASSWORD=re_xxxxxxxx
|
||||
MAIL_FROM=noreply@bilhej.se
|
||||
```
|
||||
|
||||
`MAIL_USERNAME` is always the literal string `resend`; `MAIL_PASSWORD` is the API key.
|
||||
|
||||
5. Deploy via **Deploy to Production**, then test forgot-password on https://bilhej.se
|
||||
|
||||
See [docs/production-email-checklist.md](docs/production-email-checklist.md) for a step-by-step operator checklist.
|
||||
|
|
@ -335,6 +336,12 @@ Before the first deploy, complete these steps on the production server (`srvr.nu
|
|||
| `SWISH_NUMBER` | Swish phone number for payment instructions |
|
||||
| `ADMIN_EMAIL` | Production admin email (e.g. `admin@bilhej.se`) |
|
||||
| `ADMIN_PASSWORD` | Strong unique admin password (password manager) |
|
||||
| `APP_PUBLIC_BASE_URL` | `https://bilhej.se` (password-reset links) |
|
||||
| `MAIL_HOST` | `smtp.resend.com` |
|
||||
| `MAIL_PORT` | `587` |
|
||||
| `MAIL_USERNAME` | `resend` (literal string) |
|
||||
| `MAIL_PASSWORD` | Resend API key (`re_...`; rotate if ever exposed) |
|
||||
| `MAIL_FROM` | `noreply@bilhej.se` (must be on verified domain) |
|
||||
|
||||
Passwords may contain `$` — the deploy workflow escapes these for Docker Compose.
|
||||
Production does **not** seed `test@bilhej.se` or demo orders. On first start, the
|
||||
|
|
|
|||
|
|
@ -8,3 +8,5 @@ app:
|
|||
admin:
|
||||
email: ${ADMIN_EMAIL}
|
||||
password: ${ADMIN_PASSWORD}
|
||||
password-reset:
|
||||
expose-token: false
|
||||
|
|
|
|||
|
|
@ -1,58 +1,56 @@
|
|||
# Production email checklist (operator)
|
||||
# Production email with Resend (operator)
|
||||
|
||||
Complete these steps on the server / Forgejo—nothing in this file is applied automatically.
|
||||
BilHej sends password-reset mail via **SMTP** (Spring `JavaMailSender`). You do **not** need the
|
||||
Resend Java SDK from their onboarding snippet—only env vars on the server.
|
||||
|
||||
## Prerequisites
|
||||
## Security
|
||||
|
||||
- Domain **bilhej.se** DNS managed at your registrar
|
||||
- BilHej deployed via Forgejo **Deploy to Production**
|
||||
- Never commit `re_...` keys to git. Put them only in the server `.env`.
|
||||
- If an API key was pasted in chat or logs, **revoke it** in Resend → API Keys and create a new one.
|
||||
|
||||
## 1. Choose a transactional provider
|
||||
## 1. Verify bilhej.se in Resend
|
||||
|
||||
Recommended: [Resend](https://resend.com) or [Brevo](https://www.brevo.com) (EU, free tier).
|
||||
1. [Resend](https://resend.com) → **Domains** → add `bilhej.se`
|
||||
2. Add the DNS records Resend shows (SPF, DKIM; DMARC optional) at your domain registrar
|
||||
3. Wait until status is **Verified**
|
||||
|
||||
## 2. Verify the domain
|
||||
Until the domain is verified, `MAIL_FROM=noreply@bilhej.se` will fail. For a quick API test only,
|
||||
Resend allows `onboarding@resend.dev` → your own inbox—not for production.
|
||||
|
||||
In the provider dashboard, add **bilhej.se** and publish the DNS records they give you:
|
||||
## 2. Production `.env` (SMTP, not SDK)
|
||||
|
||||
- **SPF** (TXT)
|
||||
- **DKIM** (CNAME or TXT)
|
||||
- **DMARC** (TXT, optional but recommended)
|
||||
|
||||
You do **not** need MX records if the app only sends mail (forgot-password).
|
||||
|
||||
Wait until the provider shows the domain as verified.
|
||||
|
||||
## 3. Create SMTP credentials
|
||||
|
||||
Copy from the provider:
|
||||
|
||||
- SMTP host (e.g. `smtp.resend.com`)
|
||||
- Port (`587`)
|
||||
- Username / password or API key used as password
|
||||
|
||||
## 4. Update production `.env`
|
||||
|
||||
On the server (same file used by `docker-compose.prod.yml`):
|
||||
On the server (file used by `docker-compose.prod.yml`):
|
||||
|
||||
```bash
|
||||
APP_PUBLIC_BASE_URL=https://bilhej.se
|
||||
MAIL_HOST=<provider-smtp-host>
|
||||
MAIL_HOST=smtp.resend.com
|
||||
MAIL_PORT=587
|
||||
MAIL_USERNAME=<from-provider>
|
||||
MAIL_PASSWORD=<from-provider>
|
||||
MAIL_USERNAME=resend
|
||||
MAIL_PASSWORD=re_your_new_api_key_here
|
||||
MAIL_FROM=noreply@bilhej.se
|
||||
```
|
||||
|
||||
## 5. Deploy
|
||||
| Variable | Resend value |
|
||||
|----------|----------------|
|
||||
| `MAIL_USERNAME` | Always the literal string `resend` |
|
||||
| `MAIL_PASSWORD` | Your API key (`re_...`) |
|
||||
| `MAIL_FROM` | Any address on **verified** domain, e.g. `noreply@bilhej.se` |
|
||||
|
||||
Run **Deploy to Production** in Forgejo (do not rsync or manual compose on the server).
|
||||
## 3. Deploy
|
||||
|
||||
## 6. Smoke test
|
||||
Run **Deploy to Production** in Forgejo (pipeline only—no manual rsync).
|
||||
|
||||
1. Open https://bilhej.se/logga-in → **Glömt lösenord?**
|
||||
2. Enter an email that exists in `users`
|
||||
3. Check the inbox (and spam) for the reset message
|
||||
4. If nothing arrives: `docker logs bilhej-backend-prod 2>&1 | grep -i mail`
|
||||
## 4. Smoke test
|
||||
|
||||
Fallback without SMTP: reset links still appear in backend logs (`Password reset link for`).
|
||||
1. https://bilhej.se/logga-in → **Glömt lösenord?**
|
||||
2. Email that exists in `users`
|
||||
3. Check inbox and spam
|
||||
4. Resend dashboard → **Emails** should show the send
|
||||
5. On failure: `docker logs bilhej-backend-prod 2>&1 | grep -i mail`
|
||||
|
||||
Fallback: reset links still log when `MAIL_HOST` is empty.
|
||||
|
||||
## Local dev
|
||||
|
||||
Keep using Mailpit (`docker compose up`, http://localhost:8025). Do not point local Docker at
|
||||
Resend unless you intend to send real mail.
|
||||
|
|
|
|||
Loading…
Reference in a new issue