| from functools import lru_cache |
| from pathlib import Path |
| from typing import Optional |
| from pydantic_settings import BaseSettings, SettingsConfigDict |
| from pydantic import field_validator |
|
|
|
|
| class Settings(BaseSettings): |
| model_config = SettingsConfigDict( |
| env_file=".env", |
| env_file_encoding="utf-8", |
| extra="ignore" |
| ) |
| |
| database_url: str |
| |
| supabase_url: str |
| supabase_key: str |
| supabase_jwt_secret: str |
| supabase_bucket: str = "city-issues" |
| |
| supabase_s3_endpoint: Optional[str] = None |
| supabase_s3_region: str = "ap-southeast-1" |
| supabase_s3_access_key: Optional[str] = None |
| supabase_s3_secret_key: Optional[str] = None |
| |
| model_path: Path = Path("Backend/agents/vision/model.pt") |
| model_confidence_threshold: float = 0.25 |
| model_input_size: int = 512 |
| |
| local_temp_dir: Path = Path("static/temp") |
| |
| sla_critical_hours: int = 4 |
| sla_high_hours: int = 12 |
| sla_medium_hours: int = 48 |
| sla_low_hours: int = 168 |
| |
| api_host: str = "0.0.0.0" |
| api_port: int = 8000 |
| api_workers: int = 4 |
|
|
| db_pool_size: int = 5 |
| db_max_overflow: int = 5 |
| db_pool_timeout: int = 30 |
| db_pool_recycle: int = 1800 |
| db_statement_cache_size: int = 0 |
| db_prepared_statement_cache_size: int = 0 |
| |
| max_upload_size_mb: int = 10 |
| allowed_extensions: set[str] = {"jpg", "jpeg", "png", "webp"} |
| |
| duplicate_radius_meters: float = 50.0 |
| |
| debug: bool = False |
| |
| resend_api_key: Optional[str] = None |
| google_client_id: Optional[str] = None |
| gemini_api_key: Optional[str] = None |
| google_client_secret: Optional[str] = None |
| project_id: Optional[str] = None |
| sender_email: str = "noreply@CityTrack.city" |
| admin_email: str = "admin@CityTrack.city" |
|
|
| frontend_url: Optional[str] = None |
| |
| cors_origins: list[str] = [] |
| jwt_algorithm: str = "HS256" |
| jwt_expire_hours: int = 24 |
| |
| @field_validator("database_url") |
| @classmethod |
| def validate_database_url(cls, v: str) -> str: |
| if not v.startswith("postgresql"): |
| raise ValueError("DATABASE_URL must be a PostgreSQL connection string") |
| return v |
| |
| @field_validator("supabase_jwt_secret") |
| @classmethod |
| def validate_jwt_secret(cls, v: str) -> str: |
| if len(v) < 32: |
| raise ValueError("SUPABASE_JWT_SECRET must be at least 32 characters") |
| return v |
|
|
|
|
| @lru_cache |
| def get_settings() -> Settings: |
| return Settings() |
|
|
|
|
| settings = get_settings() |
|
|