import os from pathlib import Path from datetime import timedelta import logging from logging.handlers import RotatingFileHandler # Base directories BASE_DIR = Path(__file__).parent.parent INSTANCE_DIR = BASE_DIR / 'instance' # Default values DEFAULT_NET_BASE = 12.5 # Allowed emails for registration ALLOWED_EMAILS = { 'jenny.tay@adidas.com', 'gema.yostisiano@adidas.com', 'dirgasapto.nugratama@adidas.com', 'test@example.com', # Original test email 'test1@example.com', 'test2@example.com', 'test3@example.com', 'test4@example.com', 'test5@example.com', 'test6@example.com', 'test7@example.com', 'test8@example.com', 'test9@example.com', 'test10@example.com' } # Tax rates by country TAX_RATES = { 'China': 0.06, 'Vietnam': 0.10, 'Indonesia': 0.07 } # Cost factors for calculations COST_FACTORS = { 'mold': { 'Flat & Thin': 1.0, 'Flat & Thick': 1.25, 'Flat with Top/Heat Tip': 1.35, 'Top PL': 1.05, 'Short PL (Heat/Forefoot)': 1.18, 'Visible PL': 1.28, 'Wave PL': 1.32, '2 Plates Mold': 1.0, '3 Plates Mold': 1.35, 'Breathable + In-mold channel': 1.45, 'With Upper Bandage': 1.15, '2 plate without Bandage': 1.05, 'Co-shot mold': 1.25, 'Foaming mold': 1.4 }, 'complexity': { 'high_sidewall': 0.18, 'slider': 0.1, 'cavity': 0.12 }, 'texture': { 'standard': 0.0, 'custom': 0.15, 'laser': 0.2 } } class Config: """Base configuration class.""" # Flask configuration SECRET_KEY = os.getenv('SECRET_KEY') # Get SECRET_KEY directly from environment FLASK_APP = 'app' FLASK_ENV = 'development' FLASK_DEBUG = True # Security configuration SESSION_COOKIE_SECURE = True SESSION_COOKIE_HTTPONLY = True SESSION_COOKIE_SAMESITE = 'Lax' PERMANENT_SESSION_LIFETIME = timedelta(days=7) REMEMBER_COOKIE_DURATION = timedelta(days=30) REMEMBER_COOKIE_SECURE = True REMEMBER_COOKIE_HTTPONLY = True REMEMBER_COOKIE_SAMESITE = 'Lax' # Database configuration SQLALCHEMY_DATABASE_URI = None # Will be set from environment variable SQLALCHEMY_TRACK_MODIFICATIONS = False SQLALCHEMY_ENGINE_OPTIONS = { 'pool_size': 10, 'pool_recycle': 3600, 'pool_pre_ping': True } # Email configuration MAIL_SERVER = None # Will be set from environment variable MAIL_PORT = 587 MAIL_USE_TLS = True MAIL_USERNAME = None # Will be set from environment variable MAIL_PASSWORD = None # Will be set from environment variable MAIL_DEFAULT_SENDER = None # Will be set from environment variable # Admin account configuration ADMIN_EMAIL = None # Will be set from environment variable ADMIN_PASSWORD = None # Will be set from environment variable # Logging configuration LOG_LEVEL = 'INFO' LOG_TO_STDOUT = True LOG_FORMAT = '%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]' LOG_DATE_FORMAT = '%Y-%m-%d %H:%M:%S' # Application config ALLOWED_EMAILS = ALLOWED_EMAILS TAX_RATES = TAX_RATES DEFAULT_NET_BASE = DEFAULT_NET_BASE WTF_CSRF_ENABLED = True # Rate Limiting Configuration RATELIMIT_DEFAULT = "200 per day;50 per hour;10 per minute" RATELIMIT_STORAGE_URL = "memory://" RATELIMIT_STRATEGY = "fixed-window" RATELIMIT_HEADERS_ENABLED = True RATELIMIT_HEADERS_RESET = True RATELIMIT_HEADERS_RETRY_AFTER = True # Cache Configuration CACHE_TYPE = "SimpleCache" CACHE_DEFAULT_TIMEOUT = 300 CACHE_THRESHOLD = 1000 @classmethod def init_app(cls, app): """Initialize application with configuration.""" # Load configuration from environment variables db_user = os.getenv('POSTGRES_USER') db_password = os.getenv('POSTGRES_PASSWORD') db_host = os.getenv('DATABASE_HOST') db_name = os.getenv('POSTGRES_DB') if db_user and db_password and db_host and db_name: cls.SQLALCHEMY_DATABASE_URI = f"postgresql://{db_user}:{db_password}@{db_host}/{db_name}" else: cls.SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_URL') # Fallback for other setups cls.MAIL_SERVER = os.getenv('MAIL_SERVER') cls.MAIL_USERNAME = os.getenv('MAIL_USERNAME') cls.MAIL_PASSWORD = os.getenv('MAIL_PASSWORD') cls.MAIL_DEFAULT_SENDER = os.getenv('MAIL_DEFAULT_SENDER') cls.ADMIN_EMAIL = os.getenv('ADMIN_EMAIL') cls.ADMIN_PASSWORD = os.getenv('ADMIN_PASSWORD') cls.LOG_LEVEL = os.getenv('LOG_LEVEL', 'INFO') cls.LOG_TO_STDOUT = os.getenv('LOG_TO_STDOUT', 'True').lower() == 'true' # Ensure SECRET_KEY is set if not cls.SECRET_KEY: raise ValueError("SECRET_KEY environment variable is not set") # Set SQLALCHEMY_DATABASE_URI on the app config if not cls.SQLALCHEMY_DATABASE_URI: raise ValueError("Database connection URI is not set. Please check DATABASE_URL or its components.") app.config['SQLALCHEMY_DATABASE_URI'] = cls.SQLALCHEMY_DATABASE_URI # Configure logging if not os.path.exists('logs'): os.mkdir('logs') file_handler = RotatingFileHandler('logs/app.log', maxBytes=10240, backupCount=10) file_handler.setFormatter(logging.Formatter(cls.LOG_FORMAT)) file_handler.setLevel(logging.INFO) app.logger.addHandler(file_handler) app.logger.setLevel(logging.INFO) app.logger.info('Application startup') class DevelopmentConfig(Config): DEBUG = True TESTING = False REMEMBER_COOKIE_SECURE = False SESSION_COOKIE_SECURE = False SQLALCHEMY_ECHO = True LOG_LEVEL = 'DEBUG' WTF_CSRF_ENABLED = True # Development-specific settings RATELIMIT_DEFAULT = "1000 per day;100 per hour;20 per minute" CACHE_TYPE = "SimpleCache" class ProductionConfig(Config): DEBUG = False TESTING = False REMEMBER_COOKIE_SECURE = True SESSION_COOKIE_SECURE = True SQLALCHEMY_ECHO = False LOG_LEVEL = 'INFO' WTF_CSRF_ENABLED = True # Production-specific settings CACHE_TYPE = "SimpleCache" CACHE_DEFAULT_TIMEOUT = 300 class TestingConfig(Config): TESTING = True DEBUG = True SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:' WTF_CSRF_ENABLED = False LOG_LEVEL = 'DEBUG' # Testing-specific settings SECRET_KEY = 'testing-secret-key-not-used-in-production' MAIL_SUPPRESS_SEND = True RATELIMIT_ENABLED = False RATELIMIT_DEFAULT = "1000 per day;100 per hour;20 per minute" CACHE_TYPE = "SimpleCache" SQLALCHEMY_ENGINE_OPTIONS = {} def get_config(): """Get the appropriate configuration class based on environment.""" config_name = os.getenv('FLASK_CONFIG', 'development') config_map = { 'development': DevelopmentConfig, 'production': ProductionConfig, 'testing': TestingConfig } return config_map.get(config_name, DevelopmentConfig)