Skip to content

Commit 582140f

Browse files
committed
SES Forwarding setup for @coders.operationcode.org email addresses
1 parent 54056cb commit 582140f

File tree

20 files changed

+2138
-7
lines changed

20 files changed

+2138
-7
lines changed

.github/workflows/terraform.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@ on:
44
push:
55
branches:
66
- main
7+
paths:
8+
- 'terraform/**'
9+
- 'lambda/**'
710
pull_request:
11+
paths:
12+
- 'terraform/**'
13+
- 'lambda/**'
814

915
jobs:
1016
terraform:

.gitignore

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,30 @@
1+
# Terraform
12
.terraform/
3+
*.tfstate
4+
*.tfstate.*
5+
*.tfvars
6+
7+
# Python
8+
*.pyc
9+
*.pyo
10+
*.pyd
11+
__pycache__/
12+
*.py[cod]
13+
*$py.class
14+
.Python
15+
.venv/
16+
venv/
17+
ENV/
18+
env/
19+
*.egg-info/
20+
.pytest_cache/
21+
.mypy_cache/
22+
23+
# Build artifacts
24+
*.zip
25+
26+
# Local environment
27+
.envrc
28+
29+
# OS
230
.DS_Store

README.md

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,34 @@
1-
# Operation Code Infra
2-
Platform infrastructure for the [Operation Code site](https://operationcode.org/).
1+
# Operation Code Infrastructure
32

4-
## Setup
3+
Terraform-managed AWS infrastructure for [Operation Code](https://operationcode.org/).
54

6-
### Operation Code's ECS Cluster
7-
Greetings! Much of Operation Code's web site runs in an AWS ECS cluster. These instructions will guide you through setting up access to our cluster so you can run rails console, tail logs, and more!
5+
## Overview
6+
7+
ECS cluster running containerized services on EC2 spot instances, fronted by an Application Load Balancer.
8+
9+
### Active Services
10+
- **Python Backend** (prod/staging) - `backend.operationcode.org`, `api.operationcode.org`
11+
- **Pybot** (prod) - Slack integration bot at `pybot.operationcode.org`
12+
13+
### Stack
14+
- **Region:** us-east-2
15+
- **Compute:** ECS with Fargate + spot instances
16+
- **Routing:** ALB with host-based routing
17+
- **Logs:** CloudWatch (7-day retention)
18+
- **State:** S3 backend
19+
20+
## Structure
21+
```
22+
terraform/
23+
├── ecs.tf # ECS cluster config
24+
├── apps.tf # Service definitions
25+
├── alb.tf # Load balancer
26+
├── asg.tf # Auto-scaling groups
27+
├── python_backend/ # Backend service module
28+
└── pybot/ # Pybot service module
29+
```
30+
31+
## License
32+
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
833

9-
## Licensing
10-
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
1134
Operation Code Infra is under the [MIT License](/LICENSE).
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Python dependencies installed for Lambda packaging
2+
python/
3+
4+
# Virtual environments
5+
.venv/
6+
venv/
7+
8+
# Python cache
9+
__pycache__/
10+
*.pyc
11+
12+
# Pytest
13+
.pytest_cache/
14+
15+
# IDE
16+
.vscode/
17+
.idea/
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# SES Email Forwarder Lambda Function
2+
3+
This Lambda function forwards emails received by AWS SES to personal email addresses based on alias mappings stored in Airtable.
4+
5+
## Overview
6+
7+
When a donor with recurring donations receives a custom email alias (e.g., `john@coders.operationcode.org`), this Lambda function:
8+
1. Receives the email via SES
9+
2. Checks Airtable for the alias mapping
10+
3. Validates the donor's status is "active"
11+
4. Forwards the email to the donor's personal email address
12+
13+
## Environment Variables
14+
15+
- `EMAIL_BUCKET` - S3 bucket name where SES stores incoming emails
16+
- `AIRTABLE_SECRET_NAME` - Name of the secret in AWS Secrets Manager containing Airtable credentials
17+
- `FORWARD_FROM_EMAIL` - Email address to use as the "From" address (e.g., noreply@coders.operationcode.org)
18+
- `AWS_SES_REGION` - AWS region for SES (us-east-1)
19+
- `ENVIRONMENT` - Environment name for Sentry (prod/staging)
20+
21+
## Secrets Manager Schema
22+
23+
The secret referenced by `AIRTABLE_SECRET_NAME` must contain:
24+
```json
25+
{
26+
"airtable_api_key": "patXXXXXXXXXXXXXX",
27+
"airtable_base_id": "appXXXXXXXXXXXXXX",
28+
"airtable_table_name": "Email Aliases",
29+
"sentry_dsn": "https://xxxxx@oxxxxx.ingest.sentry.io/xxxxx"
30+
}
31+
```
32+
33+
## Local Testing
34+
35+
```bash
36+
# Create virtual environment
37+
python3 -m venv venv
38+
source venv/bin/activate
39+
40+
# Install dependencies
41+
pip install -r requirements.txt
42+
pip install pytest moto urllib3
43+
44+
# Run tests
45+
pytest tests/ -v
46+
```
47+
48+
## Architecture
49+
50+
- **Region**: us-east-1 (required for SES email receiving)
51+
- **Runtime**: Python 3.12
52+
- **Memory**: 256 MB
53+
- **Timeout**: 30 seconds
54+
- **Architecture**: ARM64 (Graviton)
55+
56+
## Email Flow
57+
58+
1. Email sent to `alias@coders.operationcode.org`
59+
2. SES receives email and stores it in S3
60+
3. SES invokes Lambda function
61+
4. Lambda:
62+
- Retrieves email from S3
63+
- Queries Airtable for alias mapping
64+
- Validates donor status is "active"
65+
- Rewrites headers (From, Reply-To)
66+
- Sends email via SES to personal email
67+
5. Original sender receives replies via Reply-To header
68+
69+
## Error Handling
70+
71+
Errors are logged to:
72+
- CloudWatch Logs: `/aws/lambda/ses-email-forwarder`
73+
- Sentry: For alerting and monitoring

0 commit comments

Comments
 (0)