t2m / src /app /core /config.py
thanhkt's picture
implement core api
50a7bf0
"""
Application configuration using Pydantic BaseSettings.
Supports environment-based configuration with validation.
"""
import os
from typing import Optional, List, Union
from pydantic import Field, field_validator
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
"""Application settings with environment variable support."""
# Application settings
app_name: str = Field(default="FastAPI Video Backend", env="APP_NAME")
app_version: str = Field(default="0.1.0", env="APP_VERSION")
debug: bool = Field(default=False, env="DEBUG")
environment: str = Field(default="development", env="ENVIRONMENT")
# Server settings
host: str = Field(default="0.0.0.0", env="HOST")
port: int = Field(default=8000, env="PORT")
reload: bool = Field(default=True, env="RELOAD")
# API settings
api_v1_prefix: str = Field(default="/api/v1", env="API_V1_PREFIX")
docs_url: str = Field(default="/docs", env="DOCS_URL")
redoc_url: str = Field(default="/redoc", env="REDOC_URL")
openapi_url: str = Field(default="/openapi.json", env="OPENAPI_URL")
# CORS settings
allowed_origins: str = Field(
default="http://localhost:3000,http://localhost:8080",
env="ALLOWED_ORIGINS"
)
allowed_methods: str = Field(
default="GET,POST,PUT,DELETE,OPTIONS",
env="ALLOWED_METHODS"
)
allowed_headers: str = Field(
default="*",
env="ALLOWED_HEADERS"
)
# Redis settings
redis_url: str = Field(default="redis://localhost:6379/0", env="REDIS_URL")
redis_host: str = Field(default="localhost", env="REDIS_HOST")
redis_port: int = Field(default=6379, env="REDIS_PORT")
redis_db: int = Field(default=0, env="REDIS_DB")
redis_password: Optional[str] = Field(default=None, env="REDIS_PASSWORD")
redis_max_connections: int = Field(default=20, env="REDIS_MAX_CONNECTIONS")
redis_socket_timeout: int = Field(default=5, env="REDIS_SOCKET_TIMEOUT")
redis_socket_connect_timeout: int = Field(default=5, env="REDIS_SOCKET_CONNECT_TIMEOUT")
# Clerk authentication settings
clerk_secret_key: str = Field(default="", env="CLERK_SECRET_KEY")
clerk_publishable_key: str = Field(default="", env="CLERK_PUBLISHABLE_KEY")
clerk_webhook_secret: Optional[str] = Field(default=None, env="CLERK_WEBHOOK_SECRET")
clerk_jwt_verification: bool = Field(default=True, env="CLERK_JWT_VERIFICATION")
# Job queue settings
job_queue_name: str = Field(default="video_generation_queue", env="JOB_QUEUE_NAME")
job_queue_max_size: int = Field(default=1000, env="JOB_QUEUE_MAX_SIZE")
job_default_timeout: int = Field(default=3600, env="JOB_DEFAULT_TIMEOUT") # 1 hour
job_retry_attempts: int = Field(default=3, env="JOB_RETRY_ATTEMPTS")
# File storage settings
upload_dir: str = Field(default="./uploads", env="UPLOAD_DIR")
file_storage_path: str = Field(default="./storage", env="FILE_STORAGE_PATH")
max_file_size: int = Field(default=100 * 1024 * 1024, env="MAX_FILE_SIZE") # 100MB
allowed_file_types: List[str] = Field(
default=["image/jpeg", "image/png", "image/gif", "video/mp4", "text/plain"],
env="ALLOWED_FILE_TYPES"
)
# Rate limiting settings
rate_limit_requests: int = Field(default=100, env="RATE_LIMIT_REQUESTS")
rate_limit_window: int = Field(default=60, env="RATE_LIMIT_WINDOW") # seconds
rate_limit_per_user: int = Field(default=50, env="RATE_LIMIT_PER_USER")
# Logging settings
log_level: str = Field(default="INFO", env="LOG_LEVEL")
log_format: str = Field(default="json", env="LOG_FORMAT") # json or text
log_file: Optional[str] = Field(default=None, env="LOG_FILE")
log_rotation: str = Field(default="1 day", env="LOG_ROTATION")
log_retention: str = Field(default="30 days", env="LOG_RETENTION")
# Security settings
secret_key: str = Field(default="dev-secret-key-change-in-production", env="SECRET_KEY")
access_token_expire_minutes: int = Field(default=30, env="ACCESS_TOKEN_EXPIRE_MINUTES")
refresh_token_expire_days: int = Field(default=7, env="REFRESH_TOKEN_EXPIRE_DAYS")
# Video generation settings
video_output_dir: str = Field(default="./videos", env="VIDEO_OUTPUT_DIR")
video_quality_default: str = Field(default="medium", env="VIDEO_QUALITY_DEFAULT")
video_max_duration: int = Field(default=600, env="VIDEO_MAX_DURATION") # 10 minutes
# Health check settings
health_check_interval: int = Field(default=30, env="HEALTH_CHECK_INTERVAL") # seconds
health_check_timeout: int = Field(default=5, env="HEALTH_CHECK_TIMEOUT") # seconds
def get_allowed_origins(self) -> List[str]:
"""Parse CORS origins from string."""
return [origin.strip() for origin in self.allowed_origins.split(",")]
def get_allowed_methods(self) -> List[str]:
"""Parse CORS methods from string."""
return [method.strip() for method in self.allowed_methods.split(",")]
def get_allowed_headers(self) -> List[str]:
"""Parse CORS headers from string."""
return [header.strip() for header in self.allowed_headers.split(",")]
def get_allowed_file_types(self) -> List[str]:
"""Get allowed file types list."""
return self.allowed_file_types
@field_validator("allowed_file_types", mode="before")
@classmethod
def parse_file_types(cls, v):
"""Parse allowed file types from string or list."""
if isinstance(v, str) and not v.startswith('['):
# Handle comma-separated string format
return [file_type.strip() for file_type in v.split(",")]
return v
@field_validator("log_level")
@classmethod
def validate_log_level(cls, v):
"""Validate log level."""
valid_levels = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
if v.upper() not in valid_levels:
raise ValueError(f"Log level must be one of: {valid_levels}")
return v.upper()
@field_validator("log_format")
@classmethod
def validate_log_format(cls, v):
"""Validate log format."""
valid_formats = ["json", "text"]
if v.lower() not in valid_formats:
raise ValueError(f"Log format must be one of: {valid_formats}")
return v.lower()
@field_validator("environment")
@classmethod
def validate_environment(cls, v):
"""Validate environment."""
valid_environments = ["development", "staging", "production", "testing"]
if v.lower() not in valid_environments:
raise ValueError(f"Environment must be one of: {valid_environments}")
return v.lower()
@field_validator("video_quality_default")
@classmethod
def validate_video_quality(cls, v):
"""Validate video quality."""
valid_qualities = ["low", "medium", "high", "ultra"]
if v.lower() not in valid_qualities:
raise ValueError(f"Video quality must be one of: {valid_qualities}")
return v.lower()
@property
def is_development(self) -> bool:
"""Check if running in development mode."""
return self.environment == "development"
@property
def is_production(self) -> bool:
"""Check if running in production mode."""
return self.environment == "production"
@property
def is_testing(self) -> bool:
"""Check if running in testing mode."""
return self.environment == "testing"
def get_redis_url(self) -> str:
"""Get Redis URL with proper formatting."""
if self.redis_url:
return self.redis_url
auth_part = f":{self.redis_password}@" if self.redis_password else ""
return f"redis://{auth_part}{self.redis_host}:{self.redis_port}/{self.redis_db}"
model_config = {
"env_file": ".env",
"env_file_encoding": "utf-8",
"case_sensitive": False,
"extra": "ignore", # Ignore extra fields from .env
}
# Global settings instance
# settings = Settings()
def get_settings() -> Settings:
"""Get application settings instance."""
return Settings()