""" config.py Configuration classes for Development, Production, and Testing. Loaded by both app factories via: from config import config """ import os from datetime import timedelta from dotenv import load_dotenv load_dotenv() class Config: SECRET_KEY = os.environ.get("SECRET_KEY", "dev-fallback-secret-change-me") SQLALCHEMY_DATABASE_URI = os.environ.get( "DATABASE_URL", "mysql+pymysql://root:password@localhost/salon_pos" ) SQLALCHEMY_TRACK_MODIFICATIONS = False SQLALCHEMY_ENGINE_OPTIONS = {"pool_recycle": 280, "pool_pre_ping": True} MAIL_SERVER = os.environ.get("MAIL_SERVER", "localhost") MAIL_PORT = int(os.environ.get("MAIL_PORT", 587)) MAIL_USE_TLS = os.environ.get("MAIL_USE_TLS", "true").lower() == "true" MAIL_USERNAME = os.environ.get("MAIL_USERNAME") MAIL_PASSWORD = os.environ.get("MAIL_PASSWORD") MAIL_DEFAULT_SENDER = os.environ.get("MAIL_USERNAME", "noreply@mydomain.com") JWT_ACCESS_TOKEN_EXPIRES = timedelta(seconds=int(os.environ.get("JWT_ACCESS_TOKEN_EXPIRES", 900))) JWT_REFRESH_TOKEN_EXPIRES = timedelta(seconds=int(os.environ.get("JWT_REFRESH_TOKEN_EXPIRES", 604800))) JWT_TOKEN_LOCATION = ["cookies"] JWT_COOKIE_SECURE = True JWT_COOKIE_SAMESITE = "Lax" JWT_COOKIE_CSRF_PROTECT = True SESSION_COOKIE_SECURE = True SESSION_COOKIE_HTTPONLY = True SESSION_COOKIE_SAMESITE = "Lax" RATELIMIT_STORAGE_URI = os.environ.get("RATELIMIT_STORAGE_URI", "memory://") RATELIMIT_DEFAULT = "200 per minute" RATELIMIT_ENABLED = True ADMIN_DOMAIN = os.environ.get("ADMIN_DOMAIN", "admin.mydomain.com") TENANT_DOMAIN = os.environ.get("TENANT_DOMAIN", "mydomain.com") DEMO_TENANT_SLUG = os.environ.get("DEMO_TENANT_SLUG", "demo") ADMIN_IP_ALLOWLIST = os.environ.get("ADMIN_IP_ALLOWLIST", "") MAX_LOGIN_ATTEMPTS = int(os.environ.get("MAX_LOGIN_ATTEMPTS", 5)) LOGIN_LOCKOUT_MINUTES = int(os.environ.get("LOGIN_LOCKOUT_MINUTES", 15)) STAFF_PASSCODE_MIN_LENGTH = int(os.environ.get("STAFF_PASSCODE_MIN_LENGTH", 4)) STAFF_PASSCODE_MAX_LENGTH = int(os.environ.get("STAFF_PASSCODE_MAX_LENGTH", 6)) MAX_CONTENT_LENGTH = 5 * 1024 * 1024 UPLOAD_EXTENSIONS = {"jpg", "jpeg", "png", "gif"} UPLOAD_FOLDER = os.environ.get("UPLOAD_FOLDER", "/var/www/salon_pos_uploads") WTF_CSRF_ENABLED = True WTF_CSRF_TIME_LIMIT = 3600 class DevelopmentConfig(Config): DEBUG = True SQLALCHEMY_DATABASE_URI = os.environ.get( "DATABASE_URL", "mysql+pymysql://root:password@localhost/salon_pos_dev" ) SESSION_COOKIE_SECURE = False JWT_COOKIE_SECURE = False RATELIMIT_ENABLED = False SESSION_TIMEOUT_TENANT = 1800 SESSION_TIMEOUT_ADMIN = 3600 class ProductionConfig(Config): DEBUG = False TESTING = False SESSION_TIMEOUT_TENANT = int(os.environ.get("SESSION_TIMEOUT_TENANT", 1800)) SESSION_TIMEOUT_ADMIN = int(os.environ.get("SESSION_TIMEOUT_ADMIN", 3600)) class TestingConfig(Config): TESTING = True DEBUG = True SQLALCHEMY_DATABASE_URI = "sqlite:///:memory:" WTF_CSRF_ENABLED = False JWT_COOKIE_SECURE = False SESSION_COOKIE_SECURE = False RATELIMIT_ENABLED = False SESSION_TIMEOUT_TENANT = 1800 SESSION_TIMEOUT_ADMIN = 3600 BCRYPT_LOG_ROUNDS = 4 config = { "development": DevelopmentConfig, "production": ProductionConfig, "testing": TestingConfig, "default": DevelopmentConfig, } def get_config(): """Return the config class for the current FLASK_ENV. Used by app factories.""" import os env = os.environ.get("FLASK_ENV", "development") return config.get(env, DevelopmentConfig)