From 76653bab1e3d4ab78c39a108d7b8940d64264a2f Mon Sep 17 00:00:00 2001 From: chihlasm Date: Tue, 3 Feb 2026 01:35:44 -0500 Subject: [PATCH] Fix CORS for Railway PR environments Add ALLOW_RAILWAY_ORIGINS env var that enables regex-based CORS allowing any *.up.railway.app origin. This makes PR environment testing work without manually setting FRONTEND_URL for each PR. Set ALLOW_RAILWAY_ORIGINS=true in Railway backend env vars. Co-Authored-By: Claude Opus 4.5 --- backend/app/core/config.py | 11 +++++++++++ backend/app/main.py | 35 +++++++++++++++++++++++++++-------- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/backend/app/core/config.py b/backend/app/core/config.py index a254b10e..25525df0 100644 --- a/backend/app/core/config.py +++ b/backend/app/core/config.py @@ -40,6 +40,8 @@ class Settings(BaseSettings): # CORS - set FRONTEND_URL in production (e.g., https://patherly.up.railway.app) CORS_ORIGINS: list[str] = ["http://localhost:3000", "http://localhost:5173", "http://localhost:5174"] FRONTEND_URL: Optional[str] = None + # Allow all Railway PR environments (set to True in Railway env vars) + ALLOW_RAILWAY_ORIGINS: bool = False @property def allowed_origins(self) -> list[str]: @@ -49,6 +51,15 @@ class Settings(BaseSettings): origins.append(self.FRONTEND_URL) return origins + def is_origin_allowed(self, origin: str) -> bool: + """Check if an origin is allowed, including Railway wildcard pattern.""" + if origin in self.allowed_origins: + return True + # Allow any *.up.railway.app origin for PR environments + if self.ALLOW_RAILWAY_ORIGINS and origin.endswith(".up.railway.app"): + return True + return False + class Config: env_file = ".env" case_sensitive = True diff --git a/backend/app/main.py b/backend/app/main.py index 5d963379..bd8b6117 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -41,14 +41,33 @@ app = FastAPI( app.add_middleware(ErrorLoggingMiddleware) app.add_middleware(RequestLoggingMiddleware) -# Configure CORS -app.add_middleware( - CORSMiddleware, - allow_origins=settings.allowed_origins, - allow_credentials=True, - allow_methods=["*"], - allow_headers=["*"], -) +# Configure CORS with dynamic origin checking for Railway PR environments +def get_allowed_origins(): + """Return origins list or callable for dynamic checking.""" + if settings.ALLOW_RAILWAY_ORIGINS: + # Use callable to dynamically check Railway origins + def check_origin(origin: str) -> bool: + return settings.is_origin_allowed(origin) + return check_origin + return settings.allowed_origins + +# Note: When ALLOW_RAILWAY_ORIGINS is True, we use allow_origin_regex for Railway domains +if settings.ALLOW_RAILWAY_ORIGINS: + app.add_middleware( + CORSMiddleware, + allow_origin_regex=r"https://.*\.up\.railway\.app", + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], + ) +else: + app.add_middleware( + CORSMiddleware, + allow_origins=settings.allowed_origins, + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], + ) # Include API router app.include_router(api_router, prefix=settings.API_V1_PREFIX)