From 9a1cc5bda3d18a820d6046245dcd7a01def3368e Mon Sep 17 00:00:00 2001 From: Merrino Date: Tue, 3 Feb 2026 16:22:43 -0500 Subject: [PATCH] feat: add custom domain support for CORS - API Gateway CORS now includes var.domain_name when set - Lambda env gets CLOUDFRONT_URL for fallback origin - S3 attachments CORS includes custom domain - FastAPI backend handles both FRONTEND_URL and CLOUDFRONT_URL - CI/CD workflow passes DOMAIN_NAME secret to terraform --- .github/workflows/ci-cd.yml | 4 ++++ backend/app/main.py | 4 ++++ backend/app/utils/config.py | 1 + terraform/lambda.tf | 16 ++++++++++------ terraform/main.tf | 13 ++++++++----- 5 files changed, 27 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 4a35bd1..e173da1 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -160,6 +160,7 @@ jobs: TF_VAR_ses_email: ${{ secrets.SES_EMAIL }} TF_VAR_supabase_url: ${{ secrets.SUPABASE_URL }} TF_VAR_supabase_jwt_secret: ${{ secrets.SUPABASE_JWT_SECRET }} + TF_VAR_domain_name: ${{ secrets.DOMAIN_NAME }} TF_VAR_environment: staging run: terraform plan -out=tfplan @@ -169,6 +170,7 @@ jobs: TF_VAR_ses_email: ${{ secrets.SES_EMAIL }} TF_VAR_supabase_url: ${{ secrets.SUPABASE_URL }} TF_VAR_supabase_jwt_secret: ${{ secrets.SUPABASE_JWT_SECRET }} + TF_VAR_domain_name: ${{ secrets.DOMAIN_NAME }} TF_VAR_environment: staging run: terraform apply -auto-approve tfplan @@ -277,6 +279,7 @@ jobs: TF_VAR_ses_email: ${{ secrets.SES_EMAIL }} TF_VAR_supabase_url: ${{ secrets.SUPABASE_URL }} TF_VAR_supabase_jwt_secret: ${{ secrets.SUPABASE_JWT_SECRET }} + TF_VAR_domain_name: ${{ secrets.DOMAIN_NAME }} TF_VAR_environment: prod run: terraform plan -out=tfplan @@ -286,6 +289,7 @@ jobs: TF_VAR_ses_email: ${{ secrets.SES_EMAIL }} TF_VAR_supabase_url: ${{ secrets.SUPABASE_URL }} TF_VAR_supabase_jwt_secret: ${{ secrets.SUPABASE_JWT_SECRET }} + TF_VAR_domain_name: ${{ secrets.DOMAIN_NAME }} TF_VAR_environment: prod run: terraform apply -auto-approve tfplan diff --git a/backend/app/main.py b/backend/app/main.py index ee901f3..bf0af39 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -46,6 +46,10 @@ async def lifespan(app: FastAPI): if settings.FRONTEND_URL: origins.append(settings.FRONTEND_URL) +# Add CloudFront URL if using custom domain (both need to work) +if settings.CLOUDFRONT_URL and settings.CLOUDFRONT_URL not in origins: + origins.append(settings.CLOUDFRONT_URL) + app.add_middleware( CORSMiddleware, allow_origins=origins, diff --git a/backend/app/utils/config.py b/backend/app/utils/config.py index 295e8d0..810f9cb 100644 --- a/backend/app/utils/config.py +++ b/backend/app/utils/config.py @@ -34,6 +34,7 @@ class Settings(BaseSettings): # Frontend FRONTEND_URL: str = "http://localhost:5173" + CLOUDFRONT_URL: str = "" # CloudFront URL when using custom domain class Config: env_file = ".env" diff --git a/terraform/lambda.tf b/terraform/lambda.tf index 8441e1f..2ec268e 100644 --- a/terraform/lambda.tf +++ b/terraform/lambda.tf @@ -134,7 +134,8 @@ resource "aws_lambda_function" "api" { SUPABASE_URL = var.supabase_url SUPABASE_JWT_SECRET = var.supabase_jwt_secret SES_EMAIL = var.ses_email - FRONTEND_URL = "https://${aws_cloudfront_distribution.frontend.domain_name}" + FRONTEND_URL = var.domain_name != "" ? "https://${var.domain_name}" : "https://${aws_cloudfront_distribution.frontend.domain_name}" + CLOUDFRONT_URL = "https://${aws_cloudfront_distribution.frontend.domain_name}" } } @@ -168,11 +169,14 @@ resource "aws_apigatewayv2_api" "main" { allow_credentials = true allow_headers = ["Content-Type", "Authorization", "X-Amz-Date", "X-Api-Key"] allow_methods = ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"] - allow_origins = [ - "http://localhost:3000", - "http://localhost:5173", - "https://${aws_cloudfront_distribution.frontend.domain_name}" - ] + allow_origins = concat( + [ + "http://localhost:3000", + "http://localhost:5173", + "https://${aws_cloudfront_distribution.frontend.domain_name}" + ], + var.domain_name != "" ? ["https://${var.domain_name}"] : [] + ) expose_headers = ["*"] max_age = 3600 } diff --git a/terraform/main.tf b/terraform/main.tf index d7cf67a..e857394 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -224,11 +224,14 @@ resource "aws_s3_bucket_cors_configuration" "attachments" { cors_rule { allowed_headers = ["*"] allowed_methods = ["GET", "PUT", "POST", "DELETE"] - allowed_origins = [ - "http://localhost:3000", - "http://localhost:5173", - "https://${aws_cloudfront_distribution.frontend.domain_name}" - ] + allowed_origins = concat( + [ + "http://localhost:3000", + "http://localhost:5173", + "https://${aws_cloudfront_distribution.frontend.domain_name}" + ], + var.domain_name != "" ? ["https://${var.domain_name}"] : [] + ) expose_headers = ["ETag"] max_age_seconds = 3000 }