159 lines
6.0 KiB
Python
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() |