feat(gateway): serve the development mail inbox at mail.<domain> - #6934
Conversation
The development stack now runs Mailpit, which catches every email the stack sends and shows it rendered. Nothing routed to it, so opening the inbox meant remembering a container port. Add a vhost beside the existing `website.` and `docs.` blocks, proxying `mail.<domain>` to Mailpit's web UI. It is gated on development and on the enterprise edition, like the `idp.` block above it: both the mail code and the Mailpit container ship with the cloud repository, so a community stack has no upstream here and the name would only ever 502. `TestCaddyfileServesTheDevelopmentMailInbox` pins both halves of that gating, because an edition-gated block is invisible until someone runs that edition.
|
Claude finished @geovannewashington's task in 1m 3s —— View job Code Review CompleteReviewed 2 files across code quality, security, testing, language patterns, and architecture — no new issues found. The code looks good as-is. The new If you push additional changes and want a new review, tag |
What
Routes
mail.<domain>to Mailpit's web UI in the development stack, so the inbox is opened by name likedocs.<domain>andwebsite.<domain>rather than by remembering a container port.Part of https://github.com/shellhub-io/team/issues/223
Companion PR (cloud repo, the SMTP provider and the Mailpit container): shellhub-io/cloud/pull/2505 That one is where the work actually is: this PR only routes to it. On its own, this block has no upstream and the hostname would 502. Merge order doesn't matter.
Why
The cloud repo's development stack now runs Mailpit, which catches every email the stack sends and shows it rendered. Nothing routed to it. The alternative was publishing its UI port to the host, which every other development-only service in this stack avoids.
Changes
One vhost in
gateway/Caddyfile.tmpl, proxyingmail.<domain>tomailpit:8025and importing the sharedforwardedsnippet like every other route in the file.It sits inside two conditionals:
.Development, alongside thewebsite.anddocs.blocks, andEnableEnterprise, alongsideidp.. The second guard is there because both the mail code and the Mailpit container ship with the cloud repository, so a community stack has no upstream here and the name would only ever error. That is the same reason the SAML test IdP is gated the way it is.Testing
TestCaddyfileServesTheDevelopmentMailInboxpins both halves of the gating: the hostname is present in the development enterprise config, absent from a community development stack, and absent from a non-development enterprise stack. An edition-gated block is invisible until someone runs that edition, which is what makes this worth a test rather than a read.The existing
TestCaddyfileAdaptsalready covers the block for free: it hands every configuration shape to the real Caddy adapter, so a typo or an unresolvable directive fails there rather than at boot.TestEveryUpstreamReceivesTheClientAddresslikewise checks the newreverse_proxycarries the client address.To exercise it, run a development stack with
SHELLHUB_EDITION=cloud(orenterprise, though nothing will send mail on that edition) and openhttp://mail.localhost. Mailpit's UI answers through the gateway:Note that
curl -Ireturns 405 here: Mailpit's UI does not acceptHEAD. That is the proxy working, not failing. Verified against a running stack: 200 on the UI and on its/api/v1/messagesendpoint.