Files
phil 6e8a7186cf fix: use absolute paths for database to ensure persistence
Critical bug fix: Path(__file__).parent.parent returns a relative path,
causing the database to be created in different locations depending on
the working directory. This caused data loss on container restarts.

Changes:
- Add .resolve() to BASE_DIR in src/config.py and migrations/env.py
- Fix admin dashboard showing 0 for participant count (was hardcoded)
- Fix exchange detail page to show actual participant list
- Add startup diagnostics to entrypoint.sh for troubleshooting

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-22 20:37:11 -07:00

130 lines
3.7 KiB
Python

"""Configuration management for Sneaky Klaus application.
This module defines configuration classes for different environments
(development, production, testing) with environment variable support.
"""
import os
from datetime import timedelta
from pathlib import Path
class Config:
"""Base configuration class with common settings."""
# Base paths - use resolve() to ensure absolute paths
BASE_DIR = Path(__file__).parent.parent.resolve()
DATA_DIR = BASE_DIR / "data"
# Security
SECRET_KEY = os.environ.get("SECRET_KEY") or "dev-secret-key-change-in-production"
# Database
SQLALCHEMY_DATABASE_URI = os.environ.get(
"DATABASE_URL", f"sqlite:///{DATA_DIR / 'sneaky-klaus.db'}"
)
SQLALCHEMY_TRACK_MODIFICATIONS = False
# Session management (filesystem-based to avoid race conditions with SQLAlchemy)
SESSION_TYPE = "filesystem"
SESSION_FILE_DIR = DATA_DIR / "sessions"
SESSION_PERMANENT = True
SESSION_USE_SIGNER = True
SESSION_KEY_PREFIX = "sk:"
PERMANENT_SESSION_LIFETIME = timedelta(days=7)
SESSION_COOKIE_SECURE = True # HTTPS only
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = "Lax"
# Flask-WTF CSRF Protection
WTF_CSRF_ENABLED = True
WTF_CSRF_TIME_LIMIT = None # No time limit for CSRF tokens
# Email service (Resend)
RESEND_API_KEY = os.environ.get("RESEND_API_KEY")
EMAIL_FROM = os.environ.get("EMAIL_FROM", "noreply@sneaky-klaus.com")
# Application URLs
APP_URL = os.environ.get("APP_URL", "http://localhost:5000")
# Timezone
TIMEZONE = os.environ.get("TZ", "UTC")
# Password requirements
MIN_PASSWORD_LENGTH = 12
class DevelopmentConfig(Config):
"""Development environment configuration."""
DEBUG = True
TESTING = False
SESSION_COOKIE_SECURE = False # Allow HTTP in development
SQLALCHEMY_ECHO = True # Log SQL queries
class ProductionConfig(Config):
"""Production environment configuration."""
DEBUG = False
TESTING = False
# Ensure critical environment variables are set
@classmethod
def validate(cls):
"""Validate that required production configuration is present."""
required_vars = ["SECRET_KEY", "RESEND_API_KEY", "APP_URL"]
missing_vars = [var for var in required_vars if not os.environ.get(var)]
if missing_vars:
raise ValueError(
f"Missing required environment variables: {', '.join(missing_vars)}"
)
class TestConfig(Config):
"""Test environment configuration."""
TESTING = True
DEBUG = True
SESSION_COOKIE_SECURE = False
# Use in-memory database for tests
SQLALCHEMY_DATABASE_URI = "sqlite:///:memory:"
# Use filesystem session with temp directory for tests
SESSION_TYPE = "filesystem"
SESSION_FILE_DIR = Path("/tmp/sneaky-klaus-test-sessions")
# Disable CSRF for easier testing
WTF_CSRF_ENABLED = False
# Use a predictable secret key for tests
SECRET_KEY = "test-secret-key"
# Set FLASK_ENV to development to enable dev mode in EmailService
FLASK_ENV = "development"
# Configuration dictionary for easy access
config = {
"development": DevelopmentConfig,
"production": ProductionConfig,
"testing": TestConfig,
"default": DevelopmentConfig,
}
def get_config(env: str | None = None) -> type[Config]:
"""Get configuration class based on environment.
Args:
env: Environment name (development, production, testing).
If None, uses FLASK_ENV environment variable.
Returns:
Configuration class for the specified environment.
"""
if env is None:
env = os.environ.get("FLASK_ENV", "development")
return config.get(env, config["default"])