diff --git a/README.md b/README.md index 56ca8ac9..874df12f 100644 --- a/README.md +++ b/README.md @@ -301,6 +301,14 @@ pnpm run dev 修改 backend/.env.dev 的环境变量 **REDIS_URL** +如需调整前端跨域来源,可在 `backend/.env.dev` 中配置: + +```bash +CORS_ALLOW_ORIGINS=http://localhost:5173,http://127.0.0.1:5173 +``` + +不建议在需要携带凭据的场景下使用 `CORS_ALLOW_ORIGINS=*`。 + 配置API Key 1. 使用 WebUI diff --git a/README_EN.md b/README_EN.md index cc563098..145adcac 100644 --- a/README_EN.md +++ b/README_EN.md @@ -114,6 +114,14 @@ It is recommended to use models with strong capabilities and large parameter cou Copy `/frontend/.env.example` to `/frontend/.env.development` (remove the `.example` suffix) +If you need to customize allowed frontend origins, set this in `backend/.env.dev`: + +```bash +CORS_ALLOW_ORIGINS=http://localhost:5173,http://127.0.0.1:5173 +``` + +Avoid `CORS_ALLOW_ORIGINS=*` when requests need credentials. + 2. Install Dependencies Clone the project diff --git a/backend/app/config/setting.py b/backend/app/config/setting.py index 897b3cf7..0f5a0914 100644 --- a/backend/app/config/setting.py +++ b/backend/app/config/setting.py @@ -10,29 +10,39 @@ class ApiType(str, Enum): """LLM API 类型。""" + OPENAI_CHAT = "openai-chat" OPENAI_RESPONSES = "openai-responses" ANTHROPIC = "anthropic" -def parse_cors(value: str) -> list[str]: - """将 CORS 配置字符串解析为 URL 列表。 +DEFAULT_CORS_ALLOW_ORIGINS = ["http://localhost:5173", "http://127.0.0.1:5173"] + + +def parse_cors(value: str | list[str]) -> list[str]: + """将 CORS 配置解析为 URL 列表。 Args: - value: 逗号分隔的 URL 字符串,或 "*" 表示允许所有来源。 + value: 逗号分隔的 URL 字符串、URL 列表,或 "*" 表示允许所有来源。 Returns: 解析后的 URL 列表。 """ + if isinstance(value, list): + return [url.strip() for url in value if url.strip()] + + value = value.strip() + if not value: + return [] if value == "*": return ["*"] - if "," in value: - return [url.strip() for url in value.split(",")] - return [value] + + return [url.strip() for url in value.split(",") if url.strip()] class Settings(BaseSettings): """全局应用配置,从环境变量和 .env 文件加载。""" + ENV: str = "dev" COORDINATOR_API_TYPE: Optional[ApiType] = None @@ -70,7 +80,9 @@ class Settings(BaseSettings): DEBUG: bool = True REDIS_URL: str = "redis://redis:6379/0" REDIS_MAX_CONNECTIONS: int = 10 - CORS_ALLOW_ORIGINS: Annotated[list[str] | str, BeforeValidator(parse_cors)] = "*" + CORS_ALLOW_ORIGINS: Annotated[list[str], BeforeValidator(parse_cors)] = ( + DEFAULT_CORS_ALLOW_ORIGINS + ) SERVER_HOST: str = "http://localhost:8000" DEEPSEEK_MODEL: Optional[str] = None DEEPSEEK_BASE_URL: Optional[str] = None diff --git a/backend/app/main.py b/backend/app/main.py index 8003d08a..5b2180c4 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -8,6 +8,7 @@ from app.utils.log_util import logger from fastapi.staticfiles import StaticFiles from app.utils.cli import get_ascii_banner, center_cli_str +from app.config.setting import settings @asynccontextmanager @@ -37,10 +38,13 @@ async def lifespan(app: FastAPI): # 跨域 CORS +cors_allow_origins = settings.CORS_ALLOW_ORIGINS +cors_allow_credentials = "*" not in cors_allow_origins + app.add_middleware( CORSMiddleware, - allow_origins=["*"], - allow_credentials=True, + allow_origins=cors_allow_origins, + allow_credentials=cors_allow_credentials, allow_methods=["*"], allow_headers=["*"], expose_headers=["*"], # 暴露所有响应头