From 2fb128bd4f9c7a44ca9fcd90f2f07a92f8828890 Mon Sep 17 00:00:00 2001 From: Mikan <72257910+Mikan-DS@users.noreply.github.com> Date: Sun, 21 Jun 2026 00:36:02 +0300 Subject: [PATCH] rebase --- CHANGELOG.md | 8 ++++++++ app/config.py | 8 ++++++-- requirements.txt | 4 ++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 184fba9..514e58c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,14 @@ All notable changes to AI-RPG are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.0.3] — 2026-06-20 + +### Fixed +- **app/config.py**: fixed `SettingsError: error parsing value for field "cors_origins" from source "EnvSettingsSource"` that crashed the backend on startup when `CORS_ORIGINS` was set as a comma-separated string (e.g. `http://localhost:8080,http://localhost:5173,http://localhost`). + - Root cause: pydantic-settings v2 by default tries to JSON-parse complex-typed env vars before applying field validators. The comma-separated string isn't valid JSON, so parsing failed before our `@field_validator(mode="before")` could split it. + - Fix: declared `cors_origins: Annotated[list[str], NoDecode]` — `NoDecode` is a pydantic-settings marker that disables JSON pre-parsing, so the raw string reaches our `_split_cors` validator unchanged. +- **requirements.txt**: bumped `pydantic` 2.7.1 → 2.9.2 and `pydantic-settings` 2.2.1 → 2.7.0. The `NoDecode` annotation was only introduced in pydantic-settings 2.6+, so the older versions couldn't support the fix above. All 68 unit tests still pass with the new versions. + ## [1.0.2] — 2026-06-20 ### Fixed diff --git a/app/config.py b/app/config.py index 347bbac..4f40c00 100644 --- a/app/config.py +++ b/app/config.py @@ -13,9 +13,10 @@ from __future__ import annotations from functools import lru_cache from pathlib import Path +from typing import Annotated from pydantic import Field, field_validator -from pydantic_settings import BaseSettings, SettingsConfigDict +from pydantic_settings import BaseSettings, NoDecode, SettingsConfigDict class Settings(BaseSettings): @@ -37,7 +38,10 @@ class Settings(BaseSettings): jwt_algorithm: str = "HS256" access_token_expire_minutes: int = 60 * 24 # 24 hours refresh_token_expire_minutes: int = 60 * 24 * 7 # 7 days - cors_origins: list[str] = Field(default_factory=lambda: ["*"]) + # `NoDecode` tells pydantic-settings to NOT JSON-parse the env var value + # before passing it to our `_split_cors` validator. Without it, the value + # `CORS_ORIGINS=http://a,http://b` would be rejected as invalid JSON. + cors_origins: Annotated[list[str], NoDecode] = Field(default_factory=lambda: ["*"]) # === Admin setup === admin_setup_token: str = "" # if empty, will be auto-generated and stored in DB diff --git a/requirements.txt b/requirements.txt index 653c69d..3721ffb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,8 +5,8 @@ SQLAlchemy==2.0.30 asyncpg==0.29.0 alembic==1.13.1 psycopg2-binary==2.9.9 # for alembic sync migrations -pydantic==2.7.1 -pydantic-settings==2.2.1 +pydantic==2.9.2 +pydantic-settings==2.7.0 email-validator==2.2.0 # required by pydantic.EmailStr python-jose[cryptography]==3.3.0 passlib[bcrypt]==1.7.4