youtube-summarizer/backend/core/config.py

159 lines
6.0 KiB
Python

"""Configuration settings for YouTube Summarizer backend."""
import os
from typing import Optional
from pydantic_settings import BaseSettings
from pydantic import Field
class Settings(BaseSettings):
"""Application settings."""
# Database
DATABASE_URL: str = Field(
default="sqlite:///./data/youtube_summarizer.db",
env="DATABASE_URL"
)
# Authentication
JWT_SECRET_KEY: str = Field(
default="your-secret-key-change-in-production",
env="JWT_SECRET_KEY"
)
JWT_ALGORITHM: str = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES: int = 15
REFRESH_TOKEN_EXPIRE_DAYS: int = 7
EMAIL_VERIFICATION_EXPIRE_HOURS: int = 24
PASSWORD_RESET_EXPIRE_MINUTES: int = 30
# Email settings (for development use MailHog)
SMTP_HOST: str = Field(default="localhost", env="SMTP_HOST")
SMTP_PORT: int = Field(default=1025, env="SMTP_PORT") # MailHog default
SMTP_USER: Optional[str] = Field(default=None, env="SMTP_USER")
SMTP_PASSWORD: Optional[str] = Field(default=None, env="SMTP_PASSWORD")
SMTP_FROM_EMAIL: str = Field(
default="noreply@youtube-summarizer.local",
env="SMTP_FROM_EMAIL"
)
SMTP_TLS: bool = Field(default=False, env="SMTP_TLS")
SMTP_SSL: bool = Field(default=False, env="SMTP_SSL")
# OAuth2 Google (optional)
GOOGLE_CLIENT_ID: Optional[str] = Field(default=None, env="GOOGLE_CLIENT_ID")
GOOGLE_CLIENT_SECRET: Optional[str] = Field(default=None, env="GOOGLE_CLIENT_SECRET")
GOOGLE_REDIRECT_URI: str = Field(
default="http://localhost:3000/auth/google/callback",
env="GOOGLE_REDIRECT_URI"
)
# Security
CORS_ORIGINS: list[str] = Field(
default=["http://localhost:3000", "http://localhost:8000"],
env="CORS_ORIGINS"
)
SECRET_KEY: str = Field(
default="your-app-secret-key-change-in-production",
env="SECRET_KEY"
)
# API Rate Limiting
RATE_LIMIT_PER_MINUTE: int = Field(default=60, env="RATE_LIMIT_PER_MINUTE")
# AI Services (DeepSeek required, others optional)
DEEPSEEK_API_KEY: Optional[str] = Field(default=None, env="DEEPSEEK_API_KEY") # Primary AI service
OPENAI_API_KEY: Optional[str] = Field(default=None, env="OPENAI_API_KEY") # Alternative model
ANTHROPIC_API_KEY: Optional[str] = Field(default=None, env="ANTHROPIC_API_KEY") # Alternative model
GOOGLE_API_KEY: Optional[str] = Field(default=None, env="GOOGLE_API_KEY")
# YouTube Data API
YOUTUBE_API_KEY: Optional[str] = Field(default=None, env="YOUTUBE_API_KEY")
# Service Configuration
USE_MOCK_SERVICES: bool = Field(default=False, env="USE_MOCK_SERVICES")
ENABLE_REAL_TRANSCRIPT_EXTRACTION: bool = Field(default=True, env="ENABLE_REAL_TRANSCRIPT_EXTRACTION")
ENABLE_REAL_CACHE: bool = Field(default=False, env="ENABLE_REAL_CACHE")
# Redis Configuration (for real cache)
REDIS_URL: Optional[str] = Field(default="redis://localhost:6379", env="REDIS_URL")
REDIS_ENABLED: bool = Field(default=False, env="REDIS_ENABLED")
# Password Requirements
PASSWORD_MIN_LENGTH: int = 8
PASSWORD_REQUIRE_UPPERCASE: bool = True
PASSWORD_REQUIRE_LOWERCASE: bool = True
PASSWORD_REQUIRE_DIGITS: bool = True
PASSWORD_REQUIRE_SPECIAL: bool = False
# Session Management
SESSION_TIMEOUT_MINUTES: int = Field(default=30, env="SESSION_TIMEOUT_MINUTES")
MAX_LOGIN_ATTEMPTS: int = 5
LOCKOUT_DURATION_MINUTES: int = 15
# Application
APP_NAME: str = "YouTube Summarizer"
APP_VERSION: str = "3.1.0"
DEBUG: bool = Field(default=False, env="DEBUG")
ENVIRONMENT: str = Field(default="development", env="ENVIRONMENT")
FRONTEND_URL: str = Field(default="http://localhost:3001", env="FRONTEND_URL")
class Config:
env_file = ".env"
env_file_encoding = "utf-8"
case_sensitive = False
extra = "ignore" # Ignore extra environment variables
# Create global settings instance
settings = Settings()
class AuthSettings:
"""Authentication-specific settings."""
_cached_jwt_key: Optional[str] = None
@classmethod
def get_jwt_secret_key(cls) -> str:
"""Get JWT secret key, generate if needed and cache it."""
if settings.JWT_SECRET_KEY != "your-secret-key-change-in-production":
return settings.JWT_SECRET_KEY
# Generate and cache a secure key for development
if cls._cached_jwt_key is None:
import secrets
cls._cached_jwt_key = secrets.token_urlsafe(32)
return cls._cached_jwt_key
@staticmethod
def get_password_hash_rounds() -> int:
"""Get bcrypt hash rounds based on environment."""
if settings.ENVIRONMENT == "production":
return 12 # Higher security in production
return 10 # Faster in development
@staticmethod
def validate_password_requirements(password: str) -> tuple[bool, str]:
"""Validate password against requirements."""
if len(password) < settings.PASSWORD_MIN_LENGTH:
return False, f"Password must be at least {settings.PASSWORD_MIN_LENGTH} characters long"
if settings.PASSWORD_REQUIRE_UPPERCASE and not any(c.isupper() for c in password):
return False, "Password must contain at least one uppercase letter"
if settings.PASSWORD_REQUIRE_LOWERCASE and not any(c.islower() for c in password):
return False, "Password must contain at least one lowercase letter"
if settings.PASSWORD_REQUIRE_DIGITS and not any(c.isdigit() for c in password):
return False, "Password must contain at least one digit"
if settings.PASSWORD_REQUIRE_SPECIAL:
special_chars = "!@#$%^&*()_+-=[]{}|;:,.<>?"
if not any(c in special_chars for c in password):
return False, "Password must contain at least one special character"
return True, "Password meets all requirements"
# Export auth settings instance
auth_settings = AuthSettings()