Files
orion/app/core/config.py
Samir Boulahtit c87bdfa129 feat: add configurable currency locale and fix vendor JS init
Currency Locale Configuration:
- Add platform-level storefront settings (locale, currency)
- Create PlatformSettingsService with resolution chain:
  vendor → AdminSetting → environment → hardcoded fallback
- Add storefront_locale nullable field to Vendor model
- Update shop routes to resolve and pass locale to templates
- Add window.SHOP_CONFIG for frontend JavaScript access
- Centralize formatPrice() in shop-layout.js using SHOP_CONFIG
- Remove local formatPrice functions from shop templates

Vendor JS Bug Fix:
- Fix vendorCode being null on all vendor pages
- Root cause: page components overriding init() without calling parent
- Add parent init call to 14 vendor JS files
- Add JS-013 architecture rule to prevent future regressions
- Validator now checks vendor JS files for parent init pattern

Files changed:
- New: app/services/platform_settings_service.py
- New: alembic/versions/s7a8b9c0d1e2_add_storefront_locale_to_vendors.py
- Modified: 14 vendor JS files, shop templates, validation scripts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 21:26:12 +01:00

310 lines
11 KiB
Python

# app/core/config.py
"""
Application configuration using Pydantic Settings.
This module provides classes and functions for:
- Configuration management via environment variables
- Database settings
- JWT and authentication configuration
- Platform domain and multi-tenancy settings
- Admin initialization settings
Note: Environment detection is handled by app.core.environment module.
This module focuses purely on configuration storage and validation.
"""
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
"""
Settings class for application configuration.
Environment detection is delegated to app.core.environment.
This class focuses on configuration values only.
"""
# =============================================================================
# PROJECT INFORMATION
# =============================================================================
project_name: str = "Wizamart - Multi-Vendor Marketplace Platform"
version: str = "2.2.0"
# Clean description without HTML
description: str = """
Marketplace product import and management system with multi-vendor support.
**Features:**
- JWT Authentication with role-based access
- Multi-marketplace product import (CSV processing)
- Inventory management across multiple locations
- Vendor management with individual configurations
**Documentation:** Visit /documentation for complete guides
**API Testing:** Use /docs for interactive API exploration
"""
# =============================================================================
# DATABASE
# =============================================================================
database_url: str = "sqlite:///./wizamart.db"
# =============================================================================
# ADMIN INITIALIZATION (for init_production.py)
# =============================================================================
admin_email: str = "admin@wizamart.com"
admin_username: str = "admin"
admin_password: str = "admin123" # CHANGE IN PRODUCTION!
admin_first_name: str = "Platform"
admin_last_name: str = "Administrator"
# =============================================================================
# JWT AUTHENTICATION
# =============================================================================
jwt_secret_key: str = "change-this-in-production"
jwt_expire_hours: int = 24
jwt_expire_minutes: int = 30
# =============================================================================
# API SERVER
# =============================================================================
api_host: str = "0.0.0.0"
api_port: int = 8000
debug: bool = True
# =============================================================================
# DOCUMENTATION
# =============================================================================
documentation_url: str = "http://localhost:8001"
# =============================================================================
# MIDDLEWARE & SECURITY
# =============================================================================
allowed_hosts: list[str] = ["*"] # Configure for production
# Rate Limiting
rate_limit_enabled: bool = True
rate_limit_requests: int = 100
rate_limit_window: int = 3600
# =============================================================================
# LOGGING
# =============================================================================
log_level: str = "INFO"
log_file: str | None = None
# =============================================================================
# PLATFORM DOMAIN CONFIGURATION
# =============================================================================
platform_domain: str = "wizamart.com"
# Custom domain features
allow_custom_domains: bool = True
require_domain_verification: bool = True
# SSL/TLS configuration for custom domains
ssl_provider: str = "letsencrypt" # or "cloudflare", "manual"
auto_provision_ssl: bool = False
# DNS verification
dns_verification_prefix: str = "_wizamart-verify"
dns_verification_ttl: int = 3600
# =============================================================================
# PLATFORM LIMITS
# =============================================================================
max_vendors_per_user: int = 5
max_team_members_per_vendor: int = 50
invitation_expiry_days: int = 7
# =============================================================================
# STRIPE BILLING
# =============================================================================
stripe_secret_key: str = ""
stripe_publishable_key: str = ""
stripe_webhook_secret: str = ""
stripe_trial_days: int = 30 # 1-month free trial (card collected upfront but not charged)
# =============================================================================
# EMAIL CONFIGURATION
# =============================================================================
# Provider: smtp, sendgrid, mailgun, ses
email_provider: str = "smtp"
email_from_address: str = "noreply@wizamart.com"
email_from_name: str = "Wizamart"
email_reply_to: str = "" # Optional reply-to address
# SMTP Settings (used when email_provider=smtp)
smtp_host: str = "localhost"
smtp_port: int = 587
smtp_user: str = ""
smtp_password: str = ""
smtp_use_tls: bool = True
smtp_use_ssl: bool = False # For port 465
# SendGrid (used when email_provider=sendgrid)
sendgrid_api_key: str = ""
# Mailgun (used when email_provider=mailgun)
mailgun_api_key: str = ""
mailgun_domain: str = ""
# Amazon SES (used when email_provider=ses)
aws_access_key_id: str = ""
aws_secret_access_key: str = ""
aws_region: str = "eu-west-1"
# Email behavior
email_enabled: bool = True # Set to False to disable all emails
email_debug: bool = False # Log emails instead of sending (for development)
# =============================================================================
# STOREFRONT DEFAULTS
# =============================================================================
# These can be overridden by AdminSetting in the database
default_storefront_locale: str = "fr-LU" # Currency/number formatting locale
default_currency: str = "EUR" # Default currency code
# =============================================================================
# DEMO/SEED DATA CONFIGURATION
# =============================================================================
# Controls for demo data seeding
seed_demo_vendors: int = 3 # Number of demo vendors to create
seed_customers_per_vendor: int = 15 # Customers per vendor
seed_products_per_vendor: int = 20 # Products per vendor
seed_orders_per_vendor: int = 10 # Orders per vendor
model_config = {"env_file": ".env"}
# Singleton settings instance
settings = Settings()
# =============================================================================
# ENVIRONMENT UTILITIES - Module-level functions
# =============================================================================
# Import environment detection utilities
from app.core.environment import (
get_environment,
is_development,
is_production,
is_staging,
should_use_secure_cookies,
)
def get_current_environment() -> str:
"""
Get current environment.
Convenience function that delegates to app.core.environment.
Use this when you need just the environment string.
"""
return get_environment()
def is_production_environment() -> bool:
"""
Check if running in production.
Convenience function that delegates to app.core.environment.
Use this for production-specific logic.
"""
return is_production()
def is_development_environment() -> bool:
"""
Check if running in development.
Convenience function that delegates to app.core.environment.
Use this for development-specific logic.
"""
return is_development()
def is_staging_environment() -> bool:
"""
Check if running in staging.
Convenience function that delegates to app.core.environment.
Use this for staging-specific logic.
"""
return is_staging()
# =============================================================================
# VALIDATION FUNCTIONS
# =============================================================================
def validate_production_settings() -> list[str]:
"""
Validate settings for production environment.
Returns:
List of warning messages if configuration is insecure
"""
warnings = []
if is_production():
# Check for default/insecure values
if settings.admin_password == "admin123":
warnings.append("⚠️ Using default admin password in production!")
if settings.jwt_secret_key == "change-this-in-production":
warnings.append("⚠️ Using default JWT secret key in production!")
if settings.debug:
warnings.append("⚠️ Debug mode enabled in production!")
if "*" in settings.allowed_hosts:
warnings.append("⚠️ ALLOWED_HOSTS is set to wildcard (*) in production!")
return warnings
def print_environment_info():
"""Print current environment configuration."""
print("\n" + "=" * 70)
print(f" ENVIRONMENT: {get_environment().upper()}")
print("=" * 70)
print(f" Database: {settings.database_url}")
print(f" Debug mode: {settings.debug}")
print(f" API port: {settings.api_port}")
print(f" Platform: {settings.platform_domain}")
print(f" Secure cookies: {should_use_secure_cookies()}")
print("=" * 70 + "\n")
# Show warnings if in production
if is_production():
warnings = validate_production_settings()
if warnings:
print("\n⚠️ PRODUCTION WARNINGS:")
for warning in warnings:
print(f" {warning}")
print()
# =============================================================================
# PUBLIC API
# =============================================================================
__all__ = [
# Settings singleton
"settings",
# Environment detection (re-exported from app.core.environment)
"get_environment",
"is_development",
"is_production",
"is_staging",
"should_use_secure_cookies",
# Convenience functions
"get_current_environment",
"is_production_environment",
"is_development_environment",
"is_staging_environment",
# Validation
"validate_production_settings",
"print_environment_info",
]