- Add ActionBar component with expandable toolbar for mobile - Add @mention functionality with autocomplete dropdown - Add browser notification system (push, sound, vibration) - Add NotificationSettings modal for user preferences - Add mention badges on room list cards - Add ReportPreview with Markdown rendering and copy/download - Add message copy functionality with hover actions - Add backend mentions field to messages with Alembic migration - Add lots field to rooms, remove templates - Optimize WebSocket database session handling - Various UX polish (animations, accessibility) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
114 lines
3.6 KiB
Python
114 lines
3.6 KiB
Python
"""Application configuration loaded from environment variables"""
|
|
from pydantic_settings import BaseSettings
|
|
from pydantic import field_validator
|
|
from functools import lru_cache
|
|
from typing import List
|
|
import logging
|
|
|
|
|
|
class Settings(BaseSettings):
|
|
"""Application settings"""
|
|
|
|
# Database
|
|
DATABASE_URL: str
|
|
|
|
# Security
|
|
FERNET_KEY: str
|
|
|
|
# AD API
|
|
AD_API_URL: str
|
|
AD_API_TIMEOUT_SECONDS: int = 10 # AD API request timeout
|
|
|
|
# Session Settings
|
|
SESSION_INACTIVITY_DAYS: int = 3
|
|
TOKEN_REFRESH_THRESHOLD_MINUTES: int = 5
|
|
MAX_REFRESH_ATTEMPTS: int = 3
|
|
|
|
# Server
|
|
HOST: str = "0.0.0.0"
|
|
PORT: int = 8000
|
|
DEBUG: bool = False # Default to False for security
|
|
LOG_LEVEL: str = "INFO" # DEBUG, INFO, WARNING, ERROR
|
|
|
|
# CORS Configuration
|
|
CORS_ORIGINS: str = "http://localhost:3000" # Comma-separated list of allowed origins
|
|
|
|
# System Admin
|
|
SYSTEM_ADMIN_EMAIL: str = "" # System administrator email with special permissions
|
|
|
|
# Realtime Messaging Settings
|
|
MESSAGE_EDIT_TIME_LIMIT_MINUTES: int = 15 # Time limit for editing messages
|
|
TYPING_TIMEOUT_SECONDS: int = 3 # Typing indicator timeout
|
|
|
|
# File Upload Size Limits (in MB)
|
|
IMAGE_MAX_SIZE_MB: int = 10
|
|
DOCUMENT_MAX_SIZE_MB: int = 20
|
|
LOG_MAX_SIZE_MB: int = 5
|
|
|
|
# MinIO Object Storage
|
|
MINIO_ENDPOINT: str = "localhost:9000"
|
|
MINIO_ACCESS_KEY: str = "minioadmin"
|
|
MINIO_SECRET_KEY: str = "minioadmin"
|
|
MINIO_BUCKET: str = "task-reporter-files"
|
|
MINIO_SECURE: bool = False # Use HTTPS
|
|
|
|
# DIFY AI Service
|
|
DIFY_BASE_URL: str = "https://dify.theaken.com/v1"
|
|
DIFY_API_KEY: str = "" # Required for report generation
|
|
DIFY_TIMEOUT_SECONDS: int = 120 # AI generation can take time
|
|
|
|
# Report Generation
|
|
REPORT_MAX_MESSAGES: int = 200 # Summarize if exceeded
|
|
REPORT_STORAGE_PATH: str = "reports" # MinIO path prefix for reports
|
|
|
|
# Database Connection Pool
|
|
DB_POOL_SIZE: int = 20 # Number of persistent connections
|
|
DB_MAX_OVERFLOW: int = 30 # Max additional connections beyond pool_size
|
|
DB_POOL_TIMEOUT: int = 10 # Seconds to wait for available connection
|
|
DB_POOL_RECYCLE: int = 1800 # Recycle connections after 30 minutes
|
|
|
|
@field_validator("LOG_LEVEL")
|
|
@classmethod
|
|
def validate_log_level(cls, v: str) -> str:
|
|
"""Validate log level"""
|
|
valid_levels = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
|
|
v_upper = v.upper()
|
|
if v_upper not in valid_levels:
|
|
raise ValueError(f"LOG_LEVEL must be one of {valid_levels}")
|
|
return v_upper
|
|
|
|
def get_cors_origins(self) -> List[str]:
|
|
"""Parse CORS_ORIGINS into a list"""
|
|
if not self.CORS_ORIGINS:
|
|
return []
|
|
return [origin.strip() for origin in self.CORS_ORIGINS.split(",") if origin.strip()]
|
|
|
|
def get_image_max_size_bytes(self) -> int:
|
|
"""Get image max size in bytes"""
|
|
return self.IMAGE_MAX_SIZE_MB * 1024 * 1024
|
|
|
|
def get_document_max_size_bytes(self) -> int:
|
|
"""Get document max size in bytes"""
|
|
return self.DOCUMENT_MAX_SIZE_MB * 1024 * 1024
|
|
|
|
def get_log_max_size_bytes(self) -> int:
|
|
"""Get log file max size in bytes"""
|
|
return self.LOG_MAX_SIZE_MB * 1024 * 1024
|
|
|
|
def configure_logging(self) -> None:
|
|
"""Configure application logging based on LOG_LEVEL"""
|
|
logging.basicConfig(
|
|
level=getattr(logging, self.LOG_LEVEL),
|
|
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
|
)
|
|
|
|
class Config:
|
|
env_file = ".env"
|
|
case_sensitive = True
|
|
|
|
|
|
@lru_cache()
|
|
def get_settings() -> Settings:
|
|
"""Get cached settings instance"""
|
|
return Settings()
|