fix(core): stabilize editor lifecycle, transactional versions, and runtime config

This commit is contained in:
greebo
2026-03-20 12:38:10 +03:00
parent 0f9c2a1cbd
commit 239b32a246
17 changed files with 1224 additions and 457 deletions

View File

@@ -1,29 +1,32 @@
from pydantic import Field, model_validator
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
app_name: str = "svg-service"
app_env: str = "development"
app_port: int = 9020
api_v1_prefix: str = "/api/v1"
app_name: str = Field(..., validation_alias="APP_NAME")
app_env: str = Field(..., validation_alias="APP_ENV")
app_port: int = Field(..., validation_alias="BACKEND_PORT")
api_v1_prefix: str = Field(..., validation_alias="API_V1_PREFIX")
auth_header_name: str = "X-API-Key"
admin_api_key: str = "admin-local-dev-key"
viewer_api_key: str = "viewer-local-dev-key"
auth_header_name: str = Field(..., validation_alias="AUTH_HEADER_NAME")
api_keys_admin: str = Field(..., validation_alias="API_KEYS_ADMIN")
api_keys_operator: str = Field(..., validation_alias="API_KEYS_OPERATOR")
api_keys_viewer: str = Field(..., validation_alias="API_KEYS_VIEWER")
postgres_host: str = "postgres"
postgres_port: int = 5432
postgres_db: str = "svg_service"
postgres_user: str = "svg_service"
postgres_password: str = "svg_service_dev_password"
postgres_host: str = Field(..., validation_alias="POSTGRES_HOST")
postgres_port: int = Field(..., validation_alias="POSTGRES_PORT")
postgres_db: str = Field(..., validation_alias="POSTGRES_DB")
postgres_user: str = Field(..., validation_alias="POSTGRES_USER")
postgres_password: str = Field(..., validation_alias="POSTGRES_PASSWORD")
database_url_raw: str | None = Field(default=None, validation_alias="DATABASE_URL")
svg_max_file_size_bytes: int = 10 * 1024 * 1024
svg_max_elements: int = 25000
svg_max_file_size_bytes: int = Field(10 * 1024 * 1024, validation_alias="SVG_MAX_FILE_SIZE_BYTES")
svg_max_elements: int = Field(25000, validation_alias="SVG_MAX_ELEMENTS")
svg_allow_internal_use_references_only: bool = True
svg_forbid_foreign_object_v1: bool = True
svg_forbid_style_v1: bool = False
svg_forbid_image_v1: bool = True
svg_allow_internal_use_references_only: bool = Field(True, validation_alias="SVG_ALLOW_INTERNAL_USE_REFERENCES_ONLY")
svg_forbid_foreign_object_v1: bool = Field(True, validation_alias="SVG_FORBID_FOREIGN_OBJECT_V1")
svg_forbid_style_v1: bool = Field(False, validation_alias="SVG_FORBID_STYLE_V1")
svg_forbid_image_v1: bool = Field(True, validation_alias="SVG_FORBID_IMAGE_V1")
svg_display_enabled: bool = True
svg_display_mode: str = "passthrough"
@@ -34,7 +37,7 @@ class Settings(BaseSettings):
svg_display_force_viewbox: bool = True
svg_display_technical_text_patterns: str = "debug,tech,helper,tmp,service"
storage_root_dir: str = "/data"
storage_root_dir: str = Field(..., validation_alias="STORAGE_ROOT")
publish_preview_retention_per_variant: int = 2
publish_require_full_pricing_coverage: bool = False
@@ -45,16 +48,32 @@ class Settings(BaseSettings):
extra="ignore",
)
@model_validator(mode="after")
def validate_database_config(self) -> "Settings":
assembled_database_url = (
f"postgresql+asyncpg://{self.postgres_user}:{self.postgres_password}"
f"@{self.postgres_host}:{self.postgres_port}/{self.postgres_db}"
)
if self.database_url_raw and self.database_url_raw != assembled_database_url:
raise ValueError("DATABASE_URL must match POSTGRES_HOST/PORT/DB/USER/PASSWORD")
return self
@property
def admin_keys(self) -> set[str]:
return {item.strip() for item in self.admin_api_key.split(",") if item.strip()}
return {item.strip() for item in self.api_keys_admin.split(",") if item.strip()}
@property
def operator_keys(self) -> set[str]:
return {item.strip() for item in self.api_keys_operator.split(",") if item.strip()}
@property
def viewer_keys(self) -> set[str]:
return {item.strip() for item in self.viewer_api_key.split(",") if item.strip()}
return {item.strip() for item in self.api_keys_viewer.split(",") if item.strip()}
@property
def database_url(self) -> str:
if self.database_url_raw:
return self.database_url_raw
return (
f"postgresql+asyncpg://{self.postgres_user}:{self.postgres_password}"
f"@{self.postgres_host}:{self.postgres_port}/{self.postgres_db}"