217 lines
7.3 KiB
Python
217 lines
7.3 KiB
Python
"""
|
|
Environment configuration for secure handling of sensitive data.
|
|
|
|
This module provides secure environment variable handling and configuration
|
|
management for the Aniworld server application.
|
|
"""
|
|
|
|
import os
|
|
import secrets
|
|
from typing import Optional, Dict, Any
|
|
from dotenv import load_dotenv
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Load environment variables from .env file
|
|
load_dotenv()
|
|
|
|
|
|
class EnvironmentConfig:
|
|
"""Manages environment variables and secure configuration."""
|
|
|
|
# Security
|
|
SECRET_KEY: str = os.getenv('SECRET_KEY', secrets.token_urlsafe(32))
|
|
JWT_SECRET_KEY: str = os.getenv('JWT_SECRET_KEY', secrets.token_urlsafe(32))
|
|
PASSWORD_SALT: str = os.getenv('PASSWORD_SALT', secrets.token_hex(32))
|
|
|
|
# Database
|
|
DATABASE_URL: str = os.getenv('DATABASE_URL', 'sqlite:///data/aniworld.db')
|
|
DATABASE_PASSWORD: Optional[str] = os.getenv('DATABASE_PASSWORD')
|
|
|
|
# Redis (for caching and sessions)
|
|
REDIS_URL: str = os.getenv('REDIS_URL', 'redis://localhost:6379/0')
|
|
REDIS_PASSWORD: Optional[str] = os.getenv('REDIS_PASSWORD')
|
|
|
|
# API Keys and External Services
|
|
ANIME_PROVIDER_API_KEY: Optional[str] = os.getenv('ANIME_PROVIDER_API_KEY')
|
|
TMDB_API_KEY: Optional[str] = os.getenv('TMDB_API_KEY')
|
|
|
|
# Email Configuration (for password reset)
|
|
SMTP_SERVER: str = os.getenv('SMTP_SERVER', 'localhost')
|
|
SMTP_PORT: int = int(os.getenv('SMTP_PORT', '587'))
|
|
SMTP_USERNAME: Optional[str] = os.getenv('SMTP_USERNAME')
|
|
SMTP_PASSWORD: Optional[str] = os.getenv('SMTP_PASSWORD')
|
|
SMTP_USE_TLS: bool = os.getenv('SMTP_USE_TLS', 'true').lower() == 'true'
|
|
FROM_EMAIL: str = os.getenv('FROM_EMAIL', 'noreply@aniworld.local')
|
|
|
|
# Security Settings
|
|
SESSION_TIMEOUT_HOURS: int = int(os.getenv('SESSION_TIMEOUT_HOURS', '24'))
|
|
MAX_FAILED_LOGIN_ATTEMPTS: int = int(os.getenv('MAX_FAILED_LOGIN_ATTEMPTS', '5'))
|
|
LOCKOUT_DURATION_MINUTES: int = int(os.getenv('LOCKOUT_DURATION_MINUTES', '30'))
|
|
|
|
# Rate Limiting
|
|
RATE_LIMIT_PER_MINUTE: int = int(os.getenv('RATE_LIMIT_PER_MINUTE', '60'))
|
|
API_RATE_LIMIT_PER_MINUTE: int = int(os.getenv('API_RATE_LIMIT_PER_MINUTE', '100'))
|
|
|
|
# Application Settings
|
|
DEBUG: bool = os.getenv('DEBUG', 'false').lower() == 'true'
|
|
HOST: str = os.getenv('HOST', '127.0.0.1')
|
|
PORT: int = int(os.getenv('PORT', '5000'))
|
|
|
|
# Anime Directory and Download Settings
|
|
ANIME_DIRECTORY: str = os.getenv('ANIME_DIRECTORY', './downloads')
|
|
MAX_CONCURRENT_DOWNLOADS: int = int(os.getenv('MAX_CONCURRENT_DOWNLOADS', '3'))
|
|
DOWNLOAD_SPEED_LIMIT: Optional[int] = int(os.getenv('DOWNLOAD_SPEED_LIMIT', '0')) or None
|
|
|
|
# Logging
|
|
LOG_LEVEL: str = os.getenv('LOG_LEVEL', 'INFO')
|
|
LOG_FILE: str = os.getenv('LOG_FILE', './logs/aniworld.log')
|
|
|
|
@classmethod
|
|
def get_database_config(cls) -> Dict[str, Any]:
|
|
"""Get database configuration."""
|
|
return {
|
|
'url': cls.DATABASE_URL,
|
|
'password': cls.DATABASE_PASSWORD,
|
|
'pool_size': int(os.getenv('DATABASE_POOL_SIZE', '10')),
|
|
'max_overflow': int(os.getenv('DATABASE_MAX_OVERFLOW', '20')),
|
|
'pool_timeout': int(os.getenv('DATABASE_POOL_TIMEOUT', '30')),
|
|
'pool_recycle': int(os.getenv('DATABASE_POOL_RECYCLE', '3600'))
|
|
}
|
|
|
|
@classmethod
|
|
def get_redis_config(cls) -> Dict[str, Any]:
|
|
"""Get Redis configuration."""
|
|
return {
|
|
'url': cls.REDIS_URL,
|
|
'password': cls.REDIS_PASSWORD,
|
|
'max_connections': int(os.getenv('REDIS_MAX_CONNECTIONS', '10')),
|
|
'retry_on_timeout': True,
|
|
'socket_timeout': int(os.getenv('REDIS_SOCKET_TIMEOUT', '5'))
|
|
}
|
|
|
|
@classmethod
|
|
def get_email_config(cls) -> Dict[str, Any]:
|
|
"""Get email configuration."""
|
|
return {
|
|
'server': cls.SMTP_SERVER,
|
|
'port': cls.SMTP_PORT,
|
|
'username': cls.SMTP_USERNAME,
|
|
'password': cls.SMTP_PASSWORD,
|
|
'use_tls': cls.SMTP_USE_TLS,
|
|
'from_email': cls.FROM_EMAIL
|
|
}
|
|
|
|
@classmethod
|
|
def get_security_config(cls) -> Dict[str, Any]:
|
|
"""Get security configuration."""
|
|
return {
|
|
'secret_key': cls.SECRET_KEY,
|
|
'jwt_secret_key': cls.JWT_SECRET_KEY,
|
|
'password_salt': cls.PASSWORD_SALT,
|
|
'session_timeout_hours': cls.SESSION_TIMEOUT_HOURS,
|
|
'max_failed_attempts': cls.MAX_FAILED_LOGIN_ATTEMPTS,
|
|
'lockout_duration_minutes': cls.LOCKOUT_DURATION_MINUTES,
|
|
'rate_limit_per_minute': cls.RATE_LIMIT_PER_MINUTE,
|
|
'api_rate_limit_per_minute': cls.API_RATE_LIMIT_PER_MINUTE
|
|
}
|
|
|
|
@classmethod
|
|
def validate_config(cls) -> bool:
|
|
"""Validate that required configuration is present."""
|
|
required_vars = [
|
|
'SECRET_KEY',
|
|
'JWT_SECRET_KEY',
|
|
'PASSWORD_SALT'
|
|
]
|
|
|
|
missing_vars = []
|
|
for var in required_vars:
|
|
if not getattr(cls, var):
|
|
missing_vars.append(var)
|
|
|
|
if missing_vars:
|
|
logger.error(f"Missing required environment variables: {missing_vars}")
|
|
return False
|
|
|
|
return True
|
|
|
|
@classmethod
|
|
def generate_env_template(cls, file_path: str = '.env.template') -> bool:
|
|
"""Generate a template .env file with all available configuration options."""
|
|
try:
|
|
template_content = """# Aniworld Server Environment Configuration
|
|
# Copy this file to .env and fill in your values
|
|
|
|
# Security (REQUIRED - Generate secure random values)
|
|
SECRET_KEY=your_secret_key_here
|
|
JWT_SECRET_KEY=your_jwt_secret_here
|
|
PASSWORD_SALT=your_password_salt_here
|
|
|
|
# Database Configuration
|
|
DATABASE_URL=sqlite:///data/aniworld.db
|
|
# DATABASE_PASSWORD=your_db_password_here
|
|
DATABASE_POOL_SIZE=10
|
|
DATABASE_MAX_OVERFLOW=20
|
|
DATABASE_POOL_TIMEOUT=30
|
|
DATABASE_POOL_RECYCLE=3600
|
|
|
|
# Redis Configuration (for caching and sessions)
|
|
REDIS_URL=redis://localhost:6379/0
|
|
# REDIS_PASSWORD=your_redis_password_here
|
|
REDIS_MAX_CONNECTIONS=10
|
|
REDIS_SOCKET_TIMEOUT=5
|
|
|
|
# Email Configuration (for password reset emails)
|
|
SMTP_SERVER=localhost
|
|
SMTP_PORT=587
|
|
# SMTP_USERNAME=your_smtp_username
|
|
# SMTP_PASSWORD=your_smtp_password
|
|
SMTP_USE_TLS=true
|
|
FROM_EMAIL=noreply@aniworld.local
|
|
|
|
# External API Keys
|
|
# ANIME_PROVIDER_API_KEY=your_anime_provider_api_key
|
|
# TMDB_API_KEY=your_tmdb_api_key
|
|
|
|
# Security Settings
|
|
SESSION_TIMEOUT_HOURS=24
|
|
MAX_FAILED_LOGIN_ATTEMPTS=5
|
|
LOCKOUT_DURATION_MINUTES=30
|
|
|
|
# Rate Limiting
|
|
RATE_LIMIT_PER_MINUTE=60
|
|
API_RATE_LIMIT_PER_MINUTE=100
|
|
|
|
# Application Settings
|
|
DEBUG=false
|
|
HOST=127.0.0.1
|
|
PORT=5000
|
|
|
|
# Anime and Download Settings
|
|
ANIME_DIRECTORY=./downloads
|
|
MAX_CONCURRENT_DOWNLOADS=3
|
|
# DOWNLOAD_SPEED_LIMIT=1000000 # bytes per second
|
|
|
|
# Logging
|
|
LOG_LEVEL=INFO
|
|
LOG_FILE=./logs/aniworld.log
|
|
"""
|
|
|
|
with open(file_path, 'w', encoding='utf-8') as f:
|
|
f.write(template_content)
|
|
|
|
logger.info(f"Environment template created at {file_path}")
|
|
return True
|
|
except Exception as e:
|
|
logger.error(f"Error creating environment template: {e}")
|
|
return False
|
|
|
|
|
|
# Create global instance
|
|
env_config = EnvironmentConfig()
|
|
|
|
# Validate configuration on import
|
|
if not env_config.validate_config():
|
|
logger.warning("Invalid environment configuration detected. Please check your .env file.") |