121
web/backend/core/config.py
Normal file
121
web/backend/core/config.py
Normal file
@@ -0,0 +1,121 @@
|
||||
"""
|
||||
Unified Configuration Manager
|
||||
|
||||
Provides a single source of truth for all configuration values with a clear hierarchy:
|
||||
1. Environment variables (highest priority)
|
||||
2. .env file
|
||||
3. Database settings
|
||||
4. Hardcoded defaults (lowest priority)
|
||||
"""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Optional
|
||||
from functools import lru_cache
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from .env file
|
||||
PROJECT_ROOT = Path(__file__).parent.parent.parent.parent
|
||||
ENV_PATH = PROJECT_ROOT / '.env'
|
||||
if ENV_PATH.exists():
|
||||
load_dotenv(ENV_PATH)
|
||||
|
||||
|
||||
class Settings:
|
||||
"""Centralized configuration settings"""
|
||||
|
||||
# Project paths
|
||||
PROJECT_ROOT: Path = PROJECT_ROOT
|
||||
DB_PATH: Path = PROJECT_ROOT / 'database' / 'media_downloader.db'
|
||||
CONFIG_PATH: Path = PROJECT_ROOT / 'config' / 'settings.json'
|
||||
LOG_PATH: Path = PROJECT_ROOT / 'logs'
|
||||
TEMP_DIR: Path = PROJECT_ROOT / 'temp'
|
||||
COOKIES_DIR: Path = PROJECT_ROOT / 'cookies'
|
||||
DATA_DIR: Path = PROJECT_ROOT / 'data'
|
||||
VENV_BIN: Path = PROJECT_ROOT / 'venv' / 'bin'
|
||||
MEDIA_BASE_PATH: Path = Path(os.getenv("MEDIA_BASE_PATH", "/opt/immich/md"))
|
||||
REVIEW_PATH: Path = Path(os.getenv("REVIEW_PATH", "/opt/immich/review"))
|
||||
RECYCLE_PATH: Path = Path(os.getenv("RECYCLE_PATH", "/opt/immich/recycle"))
|
||||
|
||||
# External tool paths (use venv by default)
|
||||
YT_DLP_PATH: Path = VENV_BIN / 'yt-dlp'
|
||||
GALLERY_DL_PATH: Path = VENV_BIN / 'gallery-dl'
|
||||
PYTHON_PATH: Path = VENV_BIN / 'python3'
|
||||
|
||||
# Database configuration
|
||||
DATABASE_BACKEND: str = os.getenv("DATABASE_BACKEND", "sqlite")
|
||||
DATABASE_URL: str = os.getenv("DATABASE_URL", "")
|
||||
DB_POOL_SIZE: int = int(os.getenv("DB_POOL_SIZE", "20"))
|
||||
DB_CONNECTION_TIMEOUT: float = float(os.getenv("DB_CONNECTION_TIMEOUT", "30.0"))
|
||||
|
||||
# Process timeouts (seconds)
|
||||
PROCESS_TIMEOUT_SHORT: int = int(os.getenv("PROCESS_TIMEOUT_SHORT", "2"))
|
||||
PROCESS_TIMEOUT_MEDIUM: int = int(os.getenv("PROCESS_TIMEOUT_MEDIUM", "10"))
|
||||
PROCESS_TIMEOUT_LONG: int = int(os.getenv("PROCESS_TIMEOUT_LONG", "120"))
|
||||
|
||||
# WebSocket configuration
|
||||
WEBSOCKET_TIMEOUT: float = float(os.getenv("WEBSOCKET_TIMEOUT", "30.0"))
|
||||
|
||||
# Background task intervals (seconds)
|
||||
ACTIVITY_LOG_INTERVAL: int = int(os.getenv("ACTIVITY_LOG_INTERVAL", "300"))
|
||||
EMBEDDING_QUEUE_CHECK_INTERVAL: int = int(os.getenv("EMBEDDING_QUEUE_CHECK_INTERVAL", "30"))
|
||||
EMBEDDING_BATCH_SIZE: int = int(os.getenv("EMBEDDING_BATCH_SIZE", "10"))
|
||||
EMBEDDING_BATCH_LIMIT: int = int(os.getenv("EMBEDDING_BATCH_LIMIT", "50000"))
|
||||
|
||||
# Thumbnail generation
|
||||
THUMBNAIL_DB_TIMEOUT: float = float(os.getenv("THUMBNAIL_DB_TIMEOUT", "30.0"))
|
||||
THUMBNAIL_SIZE: tuple = (300, 300)
|
||||
|
||||
# Video proxy configuration
|
||||
PROXY_FILE_CACHE_DURATION: int = int(os.getenv("PROXY_FILE_CACHE_DURATION", "300"))
|
||||
|
||||
# Redis cache configuration
|
||||
REDIS_HOST: str = os.getenv("REDIS_HOST", "127.0.0.1")
|
||||
REDIS_PORT: int = int(os.getenv("REDIS_PORT", "6379"))
|
||||
REDIS_DB: int = int(os.getenv("REDIS_DB", "0"))
|
||||
REDIS_TTL: int = int(os.getenv("REDIS_TTL", "300"))
|
||||
|
||||
# API configuration
|
||||
API_VERSION: str = "13.13.1"
|
||||
API_TITLE: str = "Media Downloader API"
|
||||
API_DESCRIPTION: str = "Web API for managing media downloads"
|
||||
|
||||
# CORS configuration
|
||||
ALLOWED_ORIGINS: list = os.getenv(
|
||||
"ALLOWED_ORIGINS",
|
||||
"http://localhost:5173,http://localhost:3000,http://127.0.0.1:5173,http://127.0.0.1:3000"
|
||||
).split(",")
|
||||
|
||||
# Security
|
||||
SECURE_COOKIES: bool = os.getenv("SECURE_COOKIES", "false").lower() == "true"
|
||||
SESSION_SECRET_KEY: str = os.getenv("SESSION_SECRET_KEY", "")
|
||||
CSRF_SECRET_KEY: str = os.getenv("CSRF_SECRET_KEY", "")
|
||||
|
||||
# Rate limiting
|
||||
RATE_LIMIT_DEFAULT: str = os.getenv("RATE_LIMIT_DEFAULT", "100/minute")
|
||||
RATE_LIMIT_STRICT: str = os.getenv("RATE_LIMIT_STRICT", "10/minute")
|
||||
|
||||
@classmethod
|
||||
def get(cls, key: str, default: Any = None) -> Any:
|
||||
"""Get a configuration value by key"""
|
||||
return getattr(cls, key, default)
|
||||
|
||||
@classmethod
|
||||
def get_path(cls, key: str) -> Path:
|
||||
"""Get a path configuration value, ensuring it's a Path object"""
|
||||
value = getattr(cls, key, None)
|
||||
if value is None:
|
||||
raise ValueError(f"Configuration key {key} not found")
|
||||
if isinstance(value, Path):
|
||||
return value
|
||||
return Path(value)
|
||||
|
||||
|
||||
@lru_cache()
|
||||
def get_settings() -> Settings:
|
||||
"""Get cached settings instance"""
|
||||
return Settings()
|
||||
|
||||
|
||||
# Convenience exports
|
||||
settings = get_settings()
|
||||
Reference in New Issue
Block a user