Files
mold_cost_online_tool/src/app/config.py
T
2025-06-24 15:03:16 +02:00

227 lines
7.1 KiB
Python

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)