2026-02-14 16:56:10 +00:00
|
|
|
import os
|
|
|
|
|
from datetime import timedelta
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Config:
|
|
|
|
|
"""Base configuration"""
|
2026-02-24 16:19:15 +00:00
|
|
|
|
2026-02-14 16:56:10 +00:00
|
|
|
SECRET_KEY = os.environ.get("SECRET_KEY") or "dev-secret-key-change-in-production"
|
|
|
|
|
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
2026-02-24 14:36:31 +00:00
|
|
|
JWT_SECRET_KEY = os.environ["JWT_SECRET_KEY"]
|
2026-02-27 12:41:44 +00:00
|
|
|
JWT_ACCESS_TOKEN_EXPIRES = timedelta(hours=12)
|
2026-02-14 16:56:10 +00:00
|
|
|
JWT_REFRESH_TOKEN_EXPIRES = timedelta(days=30)
|
|
|
|
|
CORS_ORIGINS = os.environ.get("CORS_ORIGINS", "*")
|
2026-02-24 16:19:15 +00:00
|
|
|
|
2026-02-21 18:38:19 +00:00
|
|
|
# Celery Configuration
|
|
|
|
|
CELERY = {
|
|
|
|
|
"broker_url": os.environ.get("CELERY_BROKER_URL", "redis://redis:6379/0"),
|
2026-02-24 16:19:15 +00:00
|
|
|
"result_backend": os.environ.get(
|
|
|
|
|
"CELERY_RESULT_BACKEND", "redis://redis:6379/0"
|
|
|
|
|
),
|
2026-02-21 18:38:19 +00:00
|
|
|
"task_serializer": "json",
|
|
|
|
|
"result_serializer": "json",
|
|
|
|
|
"accept_content": ["json"],
|
|
|
|
|
"timezone": "UTC",
|
|
|
|
|
"enable_utc": True,
|
|
|
|
|
"task_track_started": True,
|
|
|
|
|
"task_time_limit": 30 * 60, # 30 minutes
|
|
|
|
|
"task_soft_time_limit": 25 * 60, # 25 minutes
|
|
|
|
|
"worker_prefetch_multiplier": 1,
|
|
|
|
|
"worker_max_tasks_per_child": 100,
|
|
|
|
|
"broker_connection_retry_on_startup": True,
|
|
|
|
|
}
|
2026-02-25 18:32:57 +00:00
|
|
|
|
2026-02-25 16:48:18 +00:00
|
|
|
SQLALCHEMY_ENGINE_OPTIONS = {
|
2026-02-25 18:32:57 +00:00
|
|
|
"pool_size": 20, # Reduce from default
|
|
|
|
|
"max_overflow": 10, # Reduce overflow
|
|
|
|
|
"pool_timeout": 30,
|
|
|
|
|
"pool_recycle": 1800, # Recycle connections after 30 minutes
|
|
|
|
|
"pool_pre_ping": True, # Verify connections before using
|
2026-02-25 16:48:18 +00:00
|
|
|
}
|
2026-02-14 16:56:10 +00:00
|
|
|
|
2026-03-22 11:52:33 +00:00
|
|
|
# MinIO Configuration
|
|
|
|
|
MINIO_ENDPOINT = os.environ.get("MINIO_ENDPOINT", "minio:9000")
|
|
|
|
|
MINIO_ACCESS_KEY = os.environ.get("MINIO_ACCESS_KEY", "minioadmin")
|
|
|
|
|
MINIO_SECRET_KEY = os.environ.get("MINIO_SECRET_KEY", "minioadmin")
|
|
|
|
|
MINIO_REGION = os.environ.get("MINIO_REGION", "garage")
|
|
|
|
|
MINIO_USE_SSL = os.environ.get("MINIO_USE_SSL", "true").lower() == "true"
|
|
|
|
|
|
|
|
|
|
# MinIO Buckets
|
|
|
|
|
MINIO_IMAGES_BUCKET = "kanban-images"
|
|
|
|
|
MINIO_DOCUMENTS_BUCKET = "kanban-documents"
|
|
|
|
|
MINIO_THUMBNAILS_BUCKET = "kanban-thumbnails"
|
|
|
|
|
|
2026-02-14 16:56:10 +00:00
|
|
|
|
|
|
|
|
class DevelopmentConfig(Config):
|
|
|
|
|
"""Development configuration"""
|
2026-02-24 16:19:15 +00:00
|
|
|
|
2026-02-14 16:56:10 +00:00
|
|
|
DEBUG = True
|
2026-02-25 16:48:18 +00:00
|
|
|
SQLALCHEMY_DATABASE_URI = os.environ["DATABASE_URL"]
|
2026-02-14 16:56:10 +00:00
|
|
|
|
2026-03-20 17:17:01 +00:00
|
|
|
# MinIO Configuration for Development
|
|
|
|
|
MINIO_ENDPOINT = os.environ.get("MINIO_ENDPOINT", "minio:9000")
|
|
|
|
|
MINIO_ACCESS_KEY = os.environ.get("MINIO_ACCESS_KEY", "minioadmin")
|
|
|
|
|
MINIO_SECRET_KEY = os.environ.get("MINIO_SECRET_KEY", "minioadmin")
|
|
|
|
|
MINIO_USE_SSL = os.environ.get("MINIO_USE_SSL", "false").lower() == "true"
|
|
|
|
|
MINIO_IMAGES_BUCKET = "kanban-images"
|
|
|
|
|
MINIO_DOCUMENTS_BUCKET = "kanban-documents"
|
|
|
|
|
MINIO_THUMBNAILS_BUCKET = "kanban-thumbnails"
|
|
|
|
|
MAX_UPLOAD_SIZE_IMAGE = 10 * 1024 * 1024
|
|
|
|
|
MAX_UPLOAD_SIZE_DOCUMENT = 50 * 1024 * 1024
|
|
|
|
|
ALLOWED_IMAGE_TYPES = [
|
|
|
|
|
"image/jpeg",
|
|
|
|
|
"image/png",
|
|
|
|
|
"image/gif",
|
|
|
|
|
"image/svg+xml",
|
|
|
|
|
"image/webp",
|
|
|
|
|
]
|
|
|
|
|
ALLOWED_DOCUMENT_TYPES = ["application/pdf"]
|
|
|
|
|
|
2026-02-14 16:56:10 +00:00
|
|
|
|
|
|
|
|
class TestingConfig(Config):
|
|
|
|
|
"""Testing configuration"""
|
2026-02-24 16:19:15 +00:00
|
|
|
|
2026-02-14 16:56:10 +00:00
|
|
|
TESTING = True
|
2026-03-20 17:17:01 +00:00
|
|
|
USE_MOCK_STORAGE = True # Use in-memory mock storage for tests
|
2026-03-15 12:52:44 +00:00
|
|
|
SQLALCHEMY_DATABASE_URI = os.environ.get("TEST_DATABASE_URL")
|
2026-02-14 16:56:10 +00:00
|
|
|
WTF_CSRF_ENABLED = False
|
|
|
|
|
|
2026-02-25 18:32:57 +00:00
|
|
|
# Conservative connection pool settings for testing
|
|
|
|
|
SQLALCHEMY_ENGINE_OPTIONS = {
|
2026-04-12 10:01:45 +00:00
|
|
|
"pool_size": 1, # Only one connection in the pool
|
2026-02-27 20:26:25 +00:00
|
|
|
"max_overflow": 0, # No overflow connections allowed
|
2026-02-25 18:32:57 +00:00
|
|
|
"pool_timeout": 30,
|
|
|
|
|
"pool_recycle": 3600, # Recycle after 1 hour
|
|
|
|
|
"pool_pre_ping": True, # Verify connections before using
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-20 17:17:01 +00:00
|
|
|
# MinIO configuration (not used in tests due to USE_MOCK_STORAGE=True)
|
|
|
|
|
MINIO_ENDPOINT = "minio:9000"
|
|
|
|
|
MINIO_ACCESS_KEY = os.environ.get("MINIO_ACCESS_KEY", "minioadmin")
|
|
|
|
|
MINIO_SECRET_KEY = os.environ.get("MINIO_SECRET_KEY", "minioadmin")
|
|
|
|
|
MINIO_USE_SSL = os.environ.get("MINIO_USE_SSL", "false").lower() == "true"
|
|
|
|
|
|
|
|
|
|
# Bucket names (required for FileService even with mock storage)
|
|
|
|
|
MINIO_IMAGES_BUCKET = "kanban-images"
|
|
|
|
|
MINIO_DOCUMENTS_BUCKET = "kanban-documents"
|
|
|
|
|
MINIO_THUMBNAILS_BUCKET = "kanban-thumbnails"
|
|
|
|
|
|
|
|
|
|
# File upload limits
|
|
|
|
|
MAX_UPLOAD_SIZE_IMAGE = 10 * 1024 * 1024 # 10MB
|
|
|
|
|
MAX_UPLOAD_SIZE_DOCUMENT = 50 * 1024 * 1024 # 50MB
|
|
|
|
|
|
|
|
|
|
# Allowed file types
|
|
|
|
|
ALLOWED_IMAGE_TYPES = [
|
|
|
|
|
"image/jpeg",
|
|
|
|
|
"image/png",
|
|
|
|
|
"image/gif",
|
|
|
|
|
"image/svg+xml",
|
|
|
|
|
"image/webp",
|
|
|
|
|
]
|
|
|
|
|
ALLOWED_DOCUMENT_TYPES = ["application/pdf"]
|
|
|
|
|
|
2026-02-14 16:56:10 +00:00
|
|
|
|
|
|
|
|
class ProductionConfig(Config):
|
|
|
|
|
"""Production configuration"""
|
2026-02-24 16:19:15 +00:00
|
|
|
|
2026-02-14 16:56:10 +00:00
|
|
|
DEBUG = False
|
2026-02-25 16:48:18 +00:00
|
|
|
SQLALCHEMY_DATABASE_URI = os.environ["DATABASE_URL"]
|
2026-02-24 16:19:15 +00:00
|
|
|
|
2026-02-14 16:56:10 +00:00
|
|
|
# Security headers
|
|
|
|
|
SESSION_COOKIE_SECURE = True
|
|
|
|
|
SESSION_COOKIE_HTTPONLY = True
|
|
|
|
|
SESSION_COOKIE_SAMESITE = "Lax"
|
|
|
|
|
|
2026-03-20 17:17:01 +00:00
|
|
|
# MinIO Configuration
|
|
|
|
|
MINIO_ENDPOINT = os.environ.get("MINIO_ENDPOINT", "minio:9000")
|
|
|
|
|
MINIO_ACCESS_KEY = os.environ.get("MINIO_ACCESS_KEY", "minioadmin")
|
|
|
|
|
MINIO_SECRET_KEY = os.environ.get("MINIO_SECRET_KEY", "minioadmin")
|
|
|
|
|
MINIO_USE_SSL = os.environ.get("MINIO_USE_SSL", "true").lower() == "true"
|
|
|
|
|
|
|
|
|
|
# MinIO Buckets
|
|
|
|
|
MINIO_IMAGES_BUCKET = "kanban-images"
|
|
|
|
|
MINIO_DOCUMENTS_BUCKET = "kanban-documents"
|
|
|
|
|
MINIO_THUMBNAILS_BUCKET = "kanban-thumbnails"
|
|
|
|
|
|
|
|
|
|
# File Upload Limits (in bytes)
|
|
|
|
|
MAX_UPLOAD_SIZE_IMAGE = 10 * 1024 * 1024 # 10MB
|
|
|
|
|
MAX_UPLOAD_SIZE_DOCUMENT = 50 * 1024 * 1024 # 50MB
|
|
|
|
|
|
|
|
|
|
# Allowed File Types
|
|
|
|
|
ALLOWED_IMAGE_TYPES = [
|
|
|
|
|
"image/jpeg",
|
|
|
|
|
"image/png",
|
|
|
|
|
"image/gif",
|
|
|
|
|
"image/svg+xml",
|
|
|
|
|
"image/webp",
|
|
|
|
|
]
|
|
|
|
|
ALLOWED_DOCUMENT_TYPES = ["application/pdf"]
|
|
|
|
|
|
2026-02-14 16:56:10 +00:00
|
|
|
|
|
|
|
|
config_by_name = {
|
|
|
|
|
"dev": DevelopmentConfig,
|
|
|
|
|
"test": TestingConfig,
|
2026-02-24 16:19:15 +00:00
|
|
|
"prod": ProductionConfig,
|
|
|
|
|
}
|