Strip trailing slashes from PUBLIC_URL, CORS_ORIGINS, FRONTEND_BASE_URL
A trailing slash on CORS_ORIGINS never matches a browser's Origin header (which never includes a path), silently breaking CORS with no error message beyond the browser's generic "No Access-Control-Allow-Origin header" — exactly what happened in production after switching domains. A trailing slash on PUBLIC_URL/FRONTEND_BASE_URL similarly produced double-slash URLs in API responses and reset-password links. All three are now normalized at config-load time so a .env typo can't cause this again. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
23a14a9710
commit
7384a6c05a
@ -1,4 +1,5 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
from pydantic import field_validator
|
||||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||||
|
|
||||||
|
|
||||||
@ -45,9 +46,20 @@ class Settings(BaseSettings):
|
|||||||
admin_password: str = "change-me"
|
admin_password: str = "change-me"
|
||||||
admin_session_ttl_hours: int = 12
|
admin_session_ttl_hours: int = 12
|
||||||
|
|
||||||
|
@field_validator("public_url", "frontend_base_url", mode="after")
|
||||||
|
@classmethod
|
||||||
|
def _strip_trailing_slash(cls, v: str) -> str:
|
||||||
|
# Every call site concatenates a leading-slash path directly onto
|
||||||
|
# these (e.g. f"{public_url}/mcp/sse") — a trailing slash in .env
|
||||||
|
# would silently produce a double-slash URL.
|
||||||
|
return v.rstrip("/")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def cors_origins_list(self) -> list[str]:
|
def cors_origins_list(self) -> list[str]:
|
||||||
return [o.strip() for o in self.cors_origins.split(",") if o.strip()]
|
# A trailing slash here would never match a browser's Origin header
|
||||||
|
# (which never includes a path/slash), silently breaking CORS —
|
||||||
|
# strip it defensively so a .env typo can't cause that again.
|
||||||
|
return [o.strip().rstrip("/") for o in self.cors_origins.split(",") if o.strip()]
|
||||||
|
|
||||||
|
|
||||||
settings = Settings()
|
settings = Settings()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user