Open
Conversation
┌───────────────────────────────┬────────────────────────────────────────────────────────────────────────────────────────┐ │ File │ Action │ ├───────────────────────────────┼────────────────────────────────────────────────────────────────────────────────────────┤ │ pyproject.toml │ Rewritten — Poetry→uv, Python 3.12, FastAPI+Mangum+boto3, ruff/mypy/pytest dev deps │ ├───────────────────────────────┼────────────────────────────────────────────────────────────────────────────────────────┤ │ api.py │ Rewritten — Flask→FastAPI, Pydantic validation, CORS middleware, type hints, f-strings │ ├───────────────────────────────┼────────────────────────────────────────────────────────────────────────────────────────┤ │ handler.py │ New — Mangum Lambda entry point │ ├───────────────────────────────┼────────────────────────────────────────────────────────────────────────────────────────┤ │ template.yaml │ New — AWS SAM template (Lambda + API Gateway + scoped IAM) │ ├───────────────────────────────┼────────────────────────────────────────────────────────────────────────────────────────┤ │ samconfig.toml │ New — SAM deploy config for dev/prod │ ├───────────────────────────────┼────────────────────────────────────────────────────────────────────────────────────────┤ │ tests/ │ New — 14 pytest tests (validation, honeypot, CORS, errors) │ ├───────────────────────────────┼────────────────────────────────────────────────────────────────────────────────────────┤ │ README.md │ Updated — uv/SAM setup, deploy, test instructions │ ├───────────────────────────────┼────────────────────────────────────────────────────────────────────────────────────────┤ │ .gitignore │ Modernized — added .aws-sam/, .ruff_cache/, .mypy_cache/ │ ├───────────────────────────────┼────────────────────────────────────────────────────────────────────────────────────────┤ │ zappa_settings.json │ Removed │ ├───────────────────────────────┼────────────────────────────────────────────────────────────────────────────────────────┤ │ lambda-mailer-policy-*.json │ Removed (IAM now inline in SAM template, scoped to SES identity) │ ├───────────────────────────────┼────────────────────────────────────────────────────────────────────────────────────────┤ │ poetry.lock │ Removed (replaced by uv.lock) │ └───────────────────────────────┴────────────────────────────────────────────────────────────────────────────────────────┘
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Lambda Mailer — Modernization + Spam Fix
Context
The
lambda-mailerrepo is ~10 years old and was running on unmaintained tooling(Zappa, Flask 1, Python 3.8). Bots were also bypassing the frontend entirely and
POSTing spam directly to the Lambda endpoint, since no server-side authentication
existed.
This PR modernizes the stack and adds a shared secret to block direct bot traffic.
What changed
Stack modernization
api.py— rewritten with FastAPI, Pydantic request validation, CORS middleware,type hints, f-strings. Same behavior and honeypot logic as before.
handler.py— new Lambda entry point using Mangum (ASGI → Lambda adapter).template.yaml— SAM template replacingzappa_settings.json. Defines Lambda,API Gateway, and a scoped IAM role (SES send only, no wildcards).
samconfig.toml— deploy config for dev and prod environments.Makefile— shortcuts fordeploy-dev,deploy-prod,test,lint,logs-dev,logs-prod. Reads secrets from.envautomatically.requirements.txt— added for SAM's Python builder to bundle dependencies..env.example— documents required secrets (never committed).Spam fix — shared secret header
Bots were hitting the Lambda URL directly, bypassing reCAPTCHA and all frontend
validation. The fix is a shared secret (
FORM_SHARED_SECRET) that the Next.jsserver sends in every legitimate request via
X-Form-Secret. Requests withoutthe correct header are rejected with
403.Bot → POST Lambda directly → 403 (no secret) Browser → Next.js /api/contact → POST Lambda (with secret) → 200
The secret is never exposed to the browser.
Deployment
Two separate stacks are created — existing Zappa infrastructure is untouched:
lambda-mailer-v2-devlambda-mailer-v2-prod