first commit

This commit is contained in:
2026-01-17 15:21:41 -05:00
commit b93d41c1ae
36 changed files with 3391 additions and 0 deletions

154
settings.py Normal file
View File

@@ -0,0 +1,154 @@
"""
Unified configuration that reads secrets from environment variables.
Non-secret configuration (like CORS origins) can have defaults per environment.
"""
import os
class ApplicationConfig:
"""
Application configuration loaded from environment variables.
All secrets MUST be provided via environment variables.
"""
# Current environment mode
CURRENT_SETTINGS = os.environ.get('MODE', 'DEVELOPMENT')
# ===========================================
# DATABASE CONFIGURATION (Required)
# ===========================================
POSTGRES_USERNAME = os.environ.get('POSTGRES_USERNAME')
POSTGRES_PW = os.environ.get('POSTGRES_PASSWORD')
POSTGRES_SERVER = os.environ.get('POSTGRES_SERVER')
POSTGRES_PORT = os.environ.get('POSTGRES_PORT', '5432')
POSTGRES_DBNAME00 = os.environ.get('POSTGRES_DBNAME')
# ===========================================
# JWT CONFIGURATION (Required)
# ===========================================
JWT_SECRET_KEY = os.environ.get('JWT_SECRET_KEY')
JWT_ALGORITHM = os.environ.get('JWT_ALGORITHM', 'HS256')
# Token expiry in minutes (default: 30 min for prod, can override for dev)
JWT_ACCESS_TOKEN_EXPIRE_MINUTES = int(os.environ.get('JWT_ACCESS_TOKEN_EXPIRE_MINUTES', '30'))
# ===========================================
# AUTHORIZE.NET CONFIGURATION (Required for payments)
# ===========================================
AUTH_NET_API_LOGIN_ID = os.environ.get('AUTH_NET_API_LOGIN_ID')
AUTH_NET_TRANSACTION_KEY = os.environ.get('AUTH_NET_TRANSACTION_KEY')
# ===========================================
# SMTP CONFIGURATION (Required for password reset)
# ===========================================
SMTP_SERVER = os.environ.get('SMTP_SERVER', 'smtp.gmail.com')
SMTP_PORT = int(os.environ.get('SMTP_PORT', '587'))
SMTP_USERNAME = os.environ.get('SMTP_USERNAME')
SMTP_PASSWORD = os.environ.get('SMTP_PASSWORD')
SMTP_FROM_EMAIL = os.environ.get('SMTP_FROM_EMAIL')
# ===========================================
# FRONTEND URL (Required for password reset links)
# ===========================================
FRONTEND_URL = os.environ.get('FRONTEND_URL', 'http://localhost:3000')
# ===========================================
# CORS ORIGINS - Environment specific defaults
# ===========================================
_origins_env = os.environ.get('CORS_ORIGINS', '')
if _origins_env:
# Use comma-separated list from environment
origins = [origin.strip() for origin in _origins_env.split(',') if origin.strip()]
else:
# Default origins based on MODE
_mode = os.environ.get('MODE', 'DEVELOPMENT')
if _mode == 'PRODUCTION':
origins = [
"https://oil.edwineames.com",
"https://apiauto.edwineames.com",
"https://portal.auburnoil.com",
]
elif _mode == 'LOCAL':
origins = [
"http://192.168.1.204:9000",
"http://192.168.1.204:9613",
"http://192.168.1.204:9614",
"http://192.168.1.204:9612",
"http://192.168.1.204:9611",
]
else: # DEVELOPMENT
origins = [
"http://localhost:9000",
"https://localhost:9513",
"http://localhost:9514",
"http://localhost:9512",
"http://localhost:9511",
"http://localhost:5173",
"http://localhost:8000",
]
# Legacy compatibility - build SQLAlchemy URI if all parts provided
if all([POSTGRES_USERNAME, POSTGRES_PW, POSTGRES_SERVER, POSTGRES_DBNAME00]):
SQLALCHEMY_DATABASE_URI = "postgresql+psycopg2://{}:{}@{}:{}/{}".format(
POSTGRES_USERNAME,
POSTGRES_PW,
POSTGRES_SERVER,
POSTGRES_PORT,
POSTGRES_DBNAME00
)
SQLALCHEMY_BINDS = {POSTGRES_DBNAME00: SQLALCHEMY_DATABASE_URI}
else:
SQLALCHEMY_DATABASE_URI = None
SQLALCHEMY_BINDS = {}
@classmethod
def validate_required(cls):
"""Validate that all required environment variables are set."""
required = {
'POSTGRES_USERNAME': cls.POSTGRES_USERNAME,
'POSTGRES_PASSWORD': cls.POSTGRES_PW,
'POSTGRES_SERVER': cls.POSTGRES_SERVER,
'POSTGRES_DBNAME': cls.POSTGRES_DBNAME00,
'JWT_SECRET_KEY': cls.JWT_SECRET_KEY,
}
missing = [key for key, value in required.items() if not value]
if missing:
raise ValueError(
f"Missing required environment variables: {', '.join(missing)}\n"
"Please set these in your .env file or docker-compose environment."
)
# Warn about weak JWT secret in production
if cls.CURRENT_SETTINGS == 'PRODUCTION':
if cls.JWT_SECRET_KEY and len(cls.JWT_SECRET_KEY) < 32:
print("\033[93mWARNING: JWT_SECRET_KEY should be at least 32 characters for production\033[0m")
return True
@classmethod
def validate_payment_config(cls):
"""Validate payment configuration (call before processing payments)."""
if not cls.AUTH_NET_API_LOGIN_ID or not cls.AUTH_NET_TRANSACTION_KEY:
raise ValueError(
"Payment processing requires AUTH_NET_API_LOGIN_ID and AUTH_NET_TRANSACTION_KEY"
)
return True
@classmethod
def validate_email_config(cls):
"""Validate email configuration (call before sending emails)."""
required = {
'SMTP_USERNAME': cls.SMTP_USERNAME,
'SMTP_PASSWORD': cls.SMTP_PASSWORD,
'SMTP_FROM_EMAIL': cls.SMTP_FROM_EMAIL,
}
missing = [key for key, value in required.items() if not value]
if missing:
raise ValueError(
f"Email sending requires: {', '.join(missing)}"
)
return True