chore: initial project setup
Initialize Sneaky Klaus project with: - uv package management and pyproject.toml - Flask application structure (app.py, config.py) - SQLAlchemy models for Admin and Exchange - Alembic database migrations - Pre-commit hooks configuration - Development tooling (pytest, ruff, mypy) Initial structure follows design documents in docs/: - src/app.py: Application factory with Flask extensions - src/config.py: Environment-based configuration - src/models/: Admin and Exchange models - migrations/: Alembic migration setup 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
120
src/config.py
Normal file
120
src/config.py
Normal file
@@ -0,0 +1,120 @@
|
||||
"""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
|
||||
BASE_DIR = Path(__file__).parent.parent
|
||||
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
|
||||
SESSION_TYPE = "sqlalchemy"
|
||||
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")
|
||||
|
||||
# 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:"
|
||||
|
||||
# Disable CSRF for easier testing
|
||||
WTF_CSRF_ENABLED = False
|
||||
|
||||
# Use a predictable secret key for tests
|
||||
SECRET_KEY = "test-secret-key"
|
||||
|
||||
|
||||
# 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"])
|
||||
Reference in New Issue
Block a user