Migrate email catcher to Mailpit

This commit is contained in:
gabdevele
2026-06-11 01:25:18 +02:00 Unverified
parent c6dc19269a
commit 34c713e8b3
13 changed files with 26 additions and 26 deletions
+2 -2
View File
@@ -3,6 +3,6 @@ MYSQL_DATABASE=visione
MYSQL_USER=visione MYSQL_USER=visione
MYSQL_PASSWORD=visione MYSQL_PASSWORD=visione
MYSQL_ROOT_PASSWORD=root MYSQL_ROOT_PASSWORD=root
MAILHOG_SMTP_PORT=1025 MAILPIT_SMTP_PORT=1025
MAILHOG_WEB_PORT=8025 MAILPIT_WEB_PORT=8025
CORS_ALLOWED_ORIGIN=http://localhost:3000 CORS_ALLOWED_ORIGIN=http://localhost:3000
+2 -2
View File
@@ -4,7 +4,7 @@
- This repository is the backend for **Visione**, an urban issue reporting webapp. - This repository is the backend for **Visione**, an urban issue reporting webapp.
- The backend is C++20 with Drogon. Keep code, symbols, API fields, database names and comments in English. - The backend is C++20 with Drogon. Keep code, symbols, API fields, database names and comments in English.
- User-facing product labels may be Italian when they represent UI/business vocabulary. - User-facing product labels may be Italian when they represent UI/business vocabulary.
- Docker is for local external services only: MySQL, MailHog and future Redis-like dependencies. - Docker is for local external services only: MySQL, Mailpit and future Redis-like dependencies.
## Authoritative References ## Authoritative References
- Local Drogon source: `/home/gabdevele/Projects/snitchitaly/backend/drogon` - Local Drogon source: `/home/gabdevele/Projects/snitchitaly/backend/drogon`
@@ -31,5 +31,5 @@
## Development Workflow ## Development Workflow
- Update docs when behavior, schema, setup or API contracts change. - Update docs when behavior, schema, setup or API contracts change.
- After each completed and verified feature, create a short commit. - After each completed and verified feature, create a short commit.
- Keep commit messages concise, for example `Add MailHog config` or `Add report API`. - Keep commit messages concise, for example `Add Mailpit config` or `Add report API`.
- Do not spend excessive time on exhaustive tests; cover high-risk behavior and regressions. - Do not spend excessive time on exhaustive tests; cover high-risk behavior and regressions.
+5 -5
View File
@@ -1,7 +1,7 @@
# Visione Backend # Visione Backend
C++20 API backend built with Drogon for the Visione urban reporting project. C++20 API backend built with Drogon for the Visione urban reporting project.
Docker is used only for local external services: MySQL and MailHog. Docker is used only for local external services: MySQL and Mailpit.
## Setup ## Setup
@@ -27,7 +27,7 @@ cmake --build build
``` ```
The API listens on `http://localhost:5555`. The API listens on `http://localhost:5555`.
MailHog listens on `http://localhost:8025`. Mailpit listens on `http://localhost:8025`.
Local CORS allows `http://localhost:5173` by default for the future Vue frontend. Local CORS allows `http://localhost:5173` by default for the future Vue frontend.
Multipart uploads currently accept report images and videos up to the app body Multipart uploads currently accept report images and videos up to the app body
limit configured in `config.json`. limit configured in `config.json`.
@@ -138,9 +138,9 @@ backend deployment with MySQL and a local SMTP catcher. Configure the public
domain in Dokploy against service `backend` and container port `5555`. domain in Dokploy against service `backend` and container port `5555`.
The runtime image includes `config.json`; override environment-specific values The runtime image includes `config.json`; override environment-specific values
with Dokploy environment variables. The SMTP catcher service keeps the internal with Dokploy environment variables. The SMTP catcher service uses the
name `mailhog` for compatibility, but uses the multi-architecture Mailpit image multi-architecture Mailpit image to avoid `exec format error` failures on ARM
to avoid `exec format error` failures on ARM servers. servers.
Set `DB_NAME`, `DB_USER`, `DB_PASSWORD`, `MYSQL_ROOT_PASSWORD` and Set `DB_NAME`, `DB_USER`, `DB_PASSWORD`, `MYSQL_ROOT_PASSWORD` and
`CORS_ALLOWED_ORIGIN` in Dokploy environment variables. Use the exact browser `CORS_ALLOWED_ORIGIN` in Dokploy environment variables. Use the exact browser
+2 -2
View File
@@ -13,7 +13,7 @@ services:
DB_PASSWORD: ${DB_PASSWORD:-visione} DB_PASSWORD: ${DB_PASSWORD:-visione}
DB_NAME: ${DB_NAME:-visione} DB_NAME: ${DB_NAME:-visione}
DB_TIMEOUT: ${DB_TIMEOUT:-30} DB_TIMEOUT: ${DB_TIMEOUT:-30}
SMTP_HOST: ${SMTP_HOST:-mailhog} SMTP_HOST: ${SMTP_HOST:-mailpit}
SMTP_PORT: ${SMTP_PORT:-1025} SMTP_PORT: ${SMTP_PORT:-1025}
CORS_ALLOWED_ORIGIN: ${CORS_ALLOWED_ORIGIN:-http://localhost:3000} CORS_ALLOWED_ORIGIN: ${CORS_ALLOWED_ORIGIN:-http://localhost:3000}
expose: expose:
@@ -46,7 +46,7 @@ services:
retries: 20 retries: 20
start_period: 20s start_period: 20s
mailhog: mailpit:
image: axllent/mailpit:latest image: axllent/mailpit:latest
restart: unless-stopped restart: unless-stopped
expose: expose:
+4 -4
View File
@@ -17,11 +17,11 @@ services:
retries: 20 retries: 20
start_period: 20s start_period: 20s
mailhog: mailpit:
image: mailhog/mailhog:v1.0.1 image: axllent/mailpit:latest
ports: ports:
- "${MAILHOG_SMTP_PORT:-1025}:1025" - "${MAILPIT_SMTP_PORT:-1025}:1025"
- "${MAILHOG_WEB_PORT:-8025}:8025" - "${MAILPIT_WEB_PORT:-8025}:8025"
volumes: volumes:
mysql-data: mysql-data:
+1 -1
View File
@@ -10,7 +10,7 @@ Base path: `/api/v1`.
## Auth ## Auth
- `POST /auth/register`: create a citizen account. - `POST /auth/register`: create a citizen account.
- `POST /auth/otp/request`: send an OTP email through MailHog. - `POST /auth/otp/request`: send an OTP email through Mailpit.
- `POST /auth/otp/verify`: verify OTP and create an API token. - `POST /auth/otp/verify`: verify OTP and create an API token.
- `POST /auth/login/password`: password login fallback. - `POST /auth/login/password`: password login fallback.
- `POST /auth/change-password`: change the authenticated user's password. - `POST /auth/change-password`: change the authenticated user's password.
+1 -1
View File
@@ -20,7 +20,7 @@ The codebase does not use Drogon generated models or `Mapper<T>`. Raw SQL stays
in repository/database helpers because the schema is still evolving. in repository/database helpers because the schema is still evolving.
## Local Email ## Local Email
OTP emails are sent to MailHog over SMTP. MailHog keeps messages local and OTP emails are sent to Mailpit over SMTP. Mailpit keeps messages local and
inspectable through its web UI. inspectable through its web UI.
## Password Hashing ## Password Hashing
+1 -1
View File
@@ -8,7 +8,7 @@ and use intervention summaries.
Admins manage roles, users, areas, teams, reference data, moderation and stats. Admins manage roles, users, areas, teams, reference data, moderation and stats.
The project runs locally on the host machine. Docker is used only for external The project runs locally on the host machine. Docker is used only for external
services such as MySQL and MailHog. Real production deployment and Dockerizing services such as MySQL and Mailpit. Real production deployment and Dockerizing
the backend are later delivery tasks. the backend are later delivery tasks.
Important conventions: Important conventions:
+2 -2
View File
@@ -3,10 +3,10 @@
## Implemented in this milestone ## Implemented in this milestone
- Project identity and documentation for Visione. - Project identity and documentation for Visione.
- Root `AGENTS.md` for future coding agents. - Root `AGENTS.md` for future coding agents.
- Local Docker services for MySQL and MailHog. - Local Docker services for MySQL and Mailpit.
- Initial MySQL schema and seed data. - Initial MySQL schema and seed data.
- Drogon backend structure for auth, reports, admin data and stats. - Drogon backend structure for auth, reports, admin data and stats.
- OTP email delivery through MailHog. - OTP email delivery through Mailpit.
- Focused regression tests for technical endpoints and core validation helpers. - Focused regression tests for technical endpoints and core validation helpers.
- Local admin seed: `[email protected]` / `ChangeMe123!`, with forced password change flag. - Local admin seed: `[email protected]` / `ChangeMe123!`, with forced password change flag.
- Multipart report media upload endpoint storing files under `uploads/reports/`. - Multipart report media upload endpoint storing files under `uploads/reports/`.
+1 -1
View File
@@ -34,7 +34,7 @@ Valid milestone 2 transitions:
- `REJECTED -> CLOSED`. - `REJECTED -> CLOSED`.
## MVP Scope ## MVP Scope
- Local auth with email OTP delivered through MailHog. - Local auth with email OTP delivered through Mailpit.
- MySQL persistence with migrations and seed data. - MySQL persistence with migrations and seed data.
- Report creation/list/detail/status/message/assignment/feedback/moderation flows. - Report creation/list/detail/status/message/assignment/feedback/moderation flows.
- Area polygons stored as GeoJSON and matched by backend logic. - Area polygons stored as GeoJSON and matched by backend logic.
+2 -2
View File
@@ -897,7 +897,7 @@
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"delivery": { "delivery": {
"example": "mailhog", "example": "mailpit",
"type": "string" "type": "string"
}, },
"expires_in_minutes": { "expires_in_minutes": {
@@ -2209,7 +2209,7 @@
}, },
"/api/v1/auth/otp/request": { "/api/v1/auth/otp/request": {
"post": { "post": {
"description": "Sends a one-time code by email through MailHog locally.", "description": "Sends a one-time code by email through Mailpit locally.",
"operationId": "AuthController.requestOtp", "operationId": "AuthController.requestOtp",
"parameters": [], "parameters": [],
"requestBody": { "requestBody": {
+1 -1
View File
@@ -466,6 +466,6 @@ with docs_tab:
with st.sidebar: with st.sidebar:
st.text_input("Backend API", key="base_url") st.text_input("Backend API", key="base_url")
st.text_input("Bearer token", key="token", type="password") st.text_input("Bearer token", key="token", type="password")
st.link_button("MailHog", "http://127.0.0.1:8025") st.link_button("Mailpit", "http://127.0.0.1:8025")
if st.button("Health check"): if st.button("Health check"):
show_response(request("GET", "/health", timeout=5)) show_response(request("GET", "/health", timeout=5))
+2 -2
View File
@@ -147,12 +147,12 @@ Json::Value AuthService::requestOtp(const Json::Value &body)
email::EmailService mail; email::EmailService mail;
if (!mail.sendOtp(user[0]["email"].as<std::string>(), code)) if (!mail.sendOtp(user[0]["email"].as<std::string>(), code))
{ {
LOG_WARN << "Could not send OTP email through MailHog"; LOG_WARN << "Could not send OTP email through Mailpit";
return failure("otp_email_failed"); return failure("otp_email_failed");
} }
auto response = success(); auto response = success();
response["delivery"] = "mailhog"; response["delivery"] = "mailpit";
response["expires_in_minutes"] = otpTtlMinutes(); response["expires_in_minutes"] = otpTtlMinutes();
return response; return response;
} }