feat: structure

This commit is contained in:
2026-04-06 23:30:19 +02:00
commit 46a0d7aba8
41 changed files with 3480 additions and 0 deletions

130
backend/app/config.py Normal file
View File

@@ -0,0 +1,130 @@
"""
Application configuration using Pydantic Settings
"""
from pydantic_settings import BaseSettings
from pydantic import BaseModel, Field
from typing import List, Optional
import os
import yaml
from pathlib import Path
class ThumbnailSettings(BaseModel):
"""Thumbnail generation settings"""
small: int = 240
medium: int = 640
large: int = 1280
quality: int = 85
format: str = "webp"
class ScannerSettings(BaseModel):
"""File scanner settings"""
watch: bool = True
initial_scan_on_start: bool = True
batch_size: int = 100
concurrent_workers: int = 4
class SourceRoot(BaseModel):
"""Source root directory configuration"""
name: str
path: str
class TrashSettings(BaseModel):
"""Trash settings"""
path: str = "/data/trash"
auto_empty_days: Optional[int] = 30
class PerformanceSettings(BaseModel):
"""Performance tuning settings"""
max_concurrent_thumbnails: int = 10
cache_ttl: int = 3600
db_pool_size: int = 20
db_pool_recycle: int = 3600
class MulitaConfig(BaseModel):
"""Main configuration from YAML file"""
source_roots: List[SourceRoot] = []
thumbnails: ThumbnailSettings = ThumbnailSettings()
scanner: ScannerSettings = ScannerSettings()
trash: TrashSettings = TrashSettings()
performance: PerformanceSettings = PerformanceSettings()
class Settings(BaseSettings):
"""Application settings"""
# Database
database_url: str = Field(
default="sqlite+aiosqlite:///data/db/mulita.db",
env="DATABASE_URL"
)
# Redis
redis_url: str = Field(
default="redis://localhost:6379",
env="REDIS_URL"
)
# Celery
celery_broker_url: str = Field(
default="redis://localhost:6379",
env="CELERY_BROKER_URL"
)
celery_result_backend: str = Field(
default="redis://localhost:6379",
env="CELERY_RESULT_BACKEND"
)
# Photo directories
photo_dirs: str = Field(
default="/photos",
env="PHOTO_DIRS"
)
# API settings
api_host: str = Field(default="0.0.0.0", env="API_HOST")
api_port: int = Field(default=8000, env="API_PORT")
# App configuration from YAML
_config: Optional[MulitaConfig] = None
@property
def config(self) -> MulitaConfig:
"""Load configuration from YAML file"""
if self._config is None:
config_path = Path("/app/config/mulita.yml")
if not config_path.exists():
config_path = Path("mulita.yml")
if config_path.exists():
with open(config_path, "r") as f:
config_data = yaml.safe_load(f)
self._config = MulitaConfig(**config_data)
else:
self._config = MulitaConfig()
return self._config
@property
def thumbnails(self) -> ThumbnailSettings:
return self.config.thumbnails
@property
def scanner(self) -> ScannerSettings:
return self.config.scanner
@property
def trash(self) -> TrashSettings:
return self.config.trash
@property
def performance(self) -> PerformanceSettings:
return self.config.performance
@property
def source_roots(self) -> List[SourceRoot]:
return self.config.source_roots
class Config:
env_file = ".env"
case_sensitive = False
# Global settings instance
settings = Settings()