104 lines
4.2 KiB
Python
104 lines
4.2 KiB
Python
from pydantic import Field, model_validator
|
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
|
|
|
|
class Settings(BaseSettings):
|
|
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 = 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 = 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 = 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 = 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"
|
|
svg_display_hide_small_text: bool = False
|
|
svg_display_min_text_font_size: float = 8.0
|
|
svg_display_hide_technical_text: bool = False
|
|
svg_display_remove_hidden_elements: bool = True
|
|
svg_display_force_viewbox: bool = True
|
|
svg_display_technical_text_patterns: str = "debug,tech,helper,tmp,service"
|
|
|
|
storage_root_dir: str = Field(..., validation_alias="STORAGE_ROOT")
|
|
publish_preview_retention_per_variant: int = 2
|
|
publish_require_full_pricing_coverage: bool = False
|
|
|
|
model_config = SettingsConfigDict(
|
|
env_file=".env",
|
|
env_file_encoding="utf-8",
|
|
case_sensitive=False,
|
|
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.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.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}"
|
|
)
|
|
|
|
@property
|
|
def storage_original_dir(self) -> str:
|
|
return f"{self.storage_root_dir}/original"
|
|
|
|
@property
|
|
def storage_sanitized_dir(self) -> str:
|
|
return f"{self.storage_root_dir}/sanitized"
|
|
|
|
@property
|
|
def storage_normalized_dir(self) -> str:
|
|
return f"{self.storage_root_dir}/normalized"
|
|
|
|
@property
|
|
def storage_display_dir(self) -> str:
|
|
return f"{self.storage_root_dir}/display"
|
|
|
|
@property
|
|
def storage_preview_dir(self) -> str:
|
|
return f"{self.storage_root_dir}/preview"
|
|
|
|
|
|
settings = Settings()
|