Fix CORS_ORIGINS env parsing crash (pydantic-settings NoDecode), bump 0.1.1

list[str] settings fed from env were JSON-decoded by pydantic-settings
before the field validator ran, so a plain string like
CORS_ORIGINS=http://host:5009 raised JSONDecodeError on startup.
Annotate list env fields with NoDecode and parse CSV/JSON in the validator.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
menzelj
2026-06-07 16:20:36 +00:00
co-authored by Claude Opus 4.8
parent 21f2852259
commit 7775128c07
2 changed files with 17 additions and 7 deletions
+16 -6
View File
@@ -3,9 +3,10 @@ from __future__ import annotations
import secrets
from functools import lru_cache
from typing import Annotated
from pydantic import field_validator
from pydantic_settings import BaseSettings, SettingsConfigDict
from pydantic_settings import BaseSettings, NoDecode, SettingsConfigDict
class Settings(BaseSettings):
@@ -25,18 +26,22 @@ class Settings(BaseSettings):
UPDATE_CHECK_INTERVAL_MINUTES: int = 60
# Notifications (webhook URLs)
NOTIFY_WEBHOOKS: list[str] = []
NOTIFY_WEBHOOKS: Annotated[list[str], NoDecode] = []
# Docker
DOCKER_SOCKET: str = "/var/run/docker.sock"
HOST_PROC_PATH: str = "/host_proc"
# Host browser sandbox roots
ALLOWED_BROWSE_ROOTS: list[str] = ["/", "/mnt", "/media", "/srv", "/opt"]
ALLOWED_BROWSE_ROOTS: Annotated[list[str], NoDecode] = [
"/", "/mnt", "/media", "/srv", "/opt",
]
HOST_ROOT_PREFIX: str = "" # e.g. "/host_root" when host / is bind-mounted
# CORS
CORS_ORIGINS: list[str] = ["http://localhost:5009", "http://localhost:5173"]
CORS_ORIGINS: Annotated[list[str], NoDecode] = [
"http://localhost:5009", "http://localhost:5173",
]
# Server
PORT: int = 5008
@@ -55,8 +60,13 @@ class Settings(BaseSettings):
v = v.strip()
if not v:
return []
if v.startswith("["): # JSON list
return v
if v.startswith("["): # tolerate a JSON list too
import json
try:
return json.loads(v)
except json.JSONDecodeError:
pass
return [item.strip() for item in v.split(",") if item.strip()]
return v
+1 -1
View File
@@ -31,7 +31,7 @@ async def lifespan(app: FastAPI):
yield
app = FastAPI(title="StackPilot", version="0.1.0", lifespan=lifespan)
app = FastAPI(title="StackPilot", version="0.1.1", lifespan=lifespan)
app.add_middleware(
CORSMiddleware,