Fix CORS configuration to allow requests from Vercel frontend
This commit is contained in:
parent
d756051c4e
commit
2b5afe103b
16
README.md
16
README.md
@ -193,10 +193,24 @@ The API has CORS (Cross-Origin Resource Sharing) enabled with the following conf
|
||||
- Allowed origins:
|
||||
- http://localhost
|
||||
- http://localhost:3000
|
||||
- http://127.0.0.1
|
||||
- http://127.0.0.1:3000
|
||||
- https://v0-ecommerce-app-build-swart.vercel.app
|
||||
- https://*.vercel.app (for preview deployments)
|
||||
- * (wildcard for development)
|
||||
|
||||
- Allowed methods: GET, POST, PUT, DELETE, OPTIONS, PATCH
|
||||
- Allowed headers: Content-Type, Authorization, Accept, Origin, X-Requested-With, X-CSRF-Token, Access-Control-Allow-Credentials
|
||||
- Exposed headers: Content-Length, Content-Type
|
||||
- Credentials support: Enabled
|
||||
- Max age for preflight requests: 600 seconds (10 minutes)
|
||||
- Max age for preflight requests: 600 seconds (10 minutes)
|
||||
|
||||
### Custom CORS Handling
|
||||
|
||||
This application uses both FastAPI's built-in CORSMiddleware and a custom CORS middleware that provides enhanced handling of preflight OPTIONS requests. The custom middleware also supports wildcard pattern matching for origins (e.g., https://*.vercel.app) to better support deployment platforms.
|
||||
|
||||
### Environment Variables
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| USE_CUSTOM_CORS_ONLY | Whether to use only the custom CORS middleware | False |
|
@ -24,11 +24,25 @@ class Settings(BaseSettings):
|
||||
|
||||
# CORS settings
|
||||
CORS_ORIGINS: List[str] = [
|
||||
# Local development
|
||||
"http://localhost",
|
||||
"http://localhost:3000",
|
||||
"https://v0-ecommerce-app-build-swart.vercel.app"
|
||||
"http://127.0.0.1",
|
||||
"http://127.0.0.1:3000",
|
||||
|
||||
# Vercel frontend
|
||||
"https://v0-ecommerce-app-build-swart.vercel.app",
|
||||
|
||||
# Other common Vercel domains (in case of redirects or preview deployments)
|
||||
"https://*.vercel.app",
|
||||
|
||||
# Wildcard as a fallback (less secure but helps with debugging)
|
||||
"*"
|
||||
]
|
||||
|
||||
# Whether to use only the custom CORS middleware
|
||||
USE_CUSTOM_CORS_ONLY: bool = False
|
||||
|
||||
# Security settings
|
||||
PASSWORD_HASH_ROUNDS: int = 12
|
||||
|
||||
|
81
main.py
81
main.py
@ -1,10 +1,65 @@
|
||||
import uvicorn
|
||||
from fastapi import FastAPI
|
||||
from fastapi import FastAPI, Request, Response
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from starlette.middleware.base import BaseHTTPMiddleware
|
||||
|
||||
from app.api.v1.api import api_router
|
||||
from app.core.config import settings
|
||||
|
||||
|
||||
class CustomCORSMiddleware(BaseHTTPMiddleware):
|
||||
"""Custom middleware to ensure CORS headers are set correctly for all responses."""
|
||||
|
||||
def is_origin_allowed(self, origin: str) -> bool:
|
||||
"""Check if the origin is allowed based on the CORS_ORIGINS settings."""
|
||||
if not origin:
|
||||
return False
|
||||
|
||||
# Direct match
|
||||
if origin in settings.CORS_ORIGINS:
|
||||
return True
|
||||
|
||||
# Wildcard match
|
||||
if "*" in settings.CORS_ORIGINS:
|
||||
return True
|
||||
|
||||
# Check for pattern matching (e.g., https://*.vercel.app)
|
||||
for allowed_origin in settings.CORS_ORIGINS:
|
||||
if "*" in allowed_origin:
|
||||
pattern = allowed_origin.replace("*", "")
|
||||
if origin.startswith(pattern.split("*")[0]) and origin.endswith(pattern.split("*")[-1]):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
async def dispatch(self, request: Request, call_next):
|
||||
origin = request.headers.get("origin", "")
|
||||
|
||||
if request.method == "OPTIONS":
|
||||
# Handle preflight requests
|
||||
response = Response()
|
||||
|
||||
# Check if the origin is allowed
|
||||
if self.is_origin_allowed(origin):
|
||||
response.headers["Access-Control-Allow-Origin"] = origin
|
||||
response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS, PATCH"
|
||||
response.headers["Access-Control-Allow-Headers"] = "Content-Type, Authorization, Accept, Origin, X-Requested-With, X-CSRF-Token, Access-Control-Allow-Credentials"
|
||||
response.headers["Access-Control-Allow-Credentials"] = "true"
|
||||
response.headers["Access-Control-Max-Age"] = "600"
|
||||
response.status_code = 200
|
||||
|
||||
return response
|
||||
|
||||
# Process regular requests
|
||||
response = await call_next(request)
|
||||
|
||||
# Set CORS headers for the response
|
||||
if self.is_origin_allowed(origin):
|
||||
response.headers["Access-Control-Allow-Origin"] = origin
|
||||
response.headers["Access-Control-Allow-Credentials"] = "true"
|
||||
|
||||
return response
|
||||
|
||||
app = FastAPI(
|
||||
title=settings.PROJECT_NAME,
|
||||
description=settings.PROJECT_DESCRIPTION,
|
||||
@ -14,16 +69,20 @@ app = FastAPI(
|
||||
redoc_url="/redoc",
|
||||
)
|
||||
|
||||
# Set up CORS
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=settings.CORS_ORIGINS,
|
||||
allow_credentials=True,
|
||||
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"],
|
||||
allow_headers=["Content-Type", "Authorization", "Accept", "Origin", "X-Requested-With", "X-CSRF-Token", "Access-Control-Allow-Credentials"],
|
||||
expose_headers=["Content-Length", "Content-Type"],
|
||||
max_age=600, # 10 minutes cache for preflight requests
|
||||
)
|
||||
# Add our custom CORS middleware first (higher priority)
|
||||
app.add_middleware(CustomCORSMiddleware)
|
||||
|
||||
# Set up standard CORS middleware as a backup if not disabled
|
||||
if not settings.USE_CUSTOM_CORS_ONLY:
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=settings.CORS_ORIGINS,
|
||||
allow_credentials=True,
|
||||
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"],
|
||||
allow_headers=["Content-Type", "Authorization", "Accept", "Origin", "X-Requested-With", "X-CSRF-Token", "Access-Control-Allow-Credentials"],
|
||||
expose_headers=["Content-Length", "Content-Type"],
|
||||
max_age=600, # 10 minutes cache for preflight requests
|
||||
)
|
||||
|
||||
# Include API router
|
||||
app.include_router(api_router)
|
||||
|
Loading…
x
Reference in New Issue
Block a user