refactor: rename Wizamart to Orion across entire codebase

Replace all ~1,086 occurrences of Wizamart/wizamart/WIZAMART/WizaMart
with Orion/orion/ORION across 184 files. This includes database
identifiers, email addresses, domain references, R2 bucket names,
DNS prefixes, encryption salt, Celery app name, config defaults,
Docker configs, CI configs, documentation, seed data, and templates.

Renames homepage-wizamart.html template to homepage-orion.html.
Fixes duplicate file_pattern key in api.yaml architecture rule.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-14 16:46:56 +01:00
parent 34ee7bb7ad
commit e9253fbd84
184 changed files with 1227 additions and 1228 deletions

View File

@@ -145,7 +145,7 @@ api_endpoint_rules:
- Dependencies (app/api/deps.py) - authentication/authorization validation
- Services (app/services/) - business logic validation
The global exception handler catches all WizamartException subclasses and
The global exception handler catches all OrionException subclasses and
converts them to appropriate HTTP responses.
WRONG (endpoint raises exception):
@@ -213,7 +213,6 @@ api_endpoint_rules:
file_pattern:
- "app/api/v1/vendor/**/*.py"
- "app/modules/*/routes/api/store*.py"
file_pattern:
- "app/api/v1/storefront/**/*.py"
- "app/modules/*/routes/api/storefront*.py"
discouraged_patterns:

View File

@@ -44,17 +44,17 @@ exception_rules:
- "exc_info=True"
- id: "EXC-004"
name: "Domain exceptions must inherit from WizamartException"
name: "Domain exceptions must inherit from OrionException"
severity: "error"
description: |
All custom domain exceptions must inherit from WizamartException (or its
All custom domain exceptions must inherit from OrionException (or its
subclasses like ResourceNotFoundException, ValidationException, etc.).
This ensures the global exception handler catches and converts them properly.
pattern:
file_pattern:
- "app/exceptions/**/*.py"
- "app/modules/*/exceptions.py"
required_base_class: "WizamartException"
required_base_class: "OrionException"
example_good: |
class VendorNotFoundException(ResourceNotFoundException):
def __init__(self, vendor_code: str):
@@ -65,7 +65,7 @@ exception_rules:
severity: "error"
description: |
The global exception handler must be set up in app initialization to
catch WizamartException and convert to HTTP responses.
catch OrionException and convert to HTTP responses.
pattern:
file_pattern: "app/main.py"
required_patterns:

View File

@@ -157,7 +157,7 @@ javascript_rules:
- Page URLs (not API calls) like window.location.href = `/vendor/${vendorCode}/...`
Why this matters:
- Including vendorCode causes 404 errors ("/vendor/wizamart/orders" not found)
- Including vendorCode causes 404 errors ("/vendor/orion/orders" not found)
- The JWT token already identifies the vendor
- Consistent with the API design pattern
pattern:

View File

@@ -154,16 +154,16 @@ module_rules:
severity: "warning"
description: |
Self-contained modules should have an exceptions.py file defining
module-specific exceptions that inherit from WizamartException.
module-specific exceptions that inherit from OrionException.
Structure:
app/modules/{module}/exceptions.py
Example:
# app/modules/analytics/exceptions.py
from app.exceptions import WizamartException
from app.exceptions import OrionException
class AnalyticsException(WizamartException):
class AnalyticsException(OrionException):
"""Base exception for analytics module."""
pass

View File

@@ -6,7 +6,7 @@ DEBUG=False
# =============================================================================
# PROJECT INFORMATION
# =============================================================================
PROJECT_NAME=Wizamart - Multi-Store Marketplace Platform
PROJECT_NAME=Orion - Multi-Store Marketplace Platform
DESCRIPTION=Multi-tenants multi-themes ecommerce application
VERSION=2.2.0
@@ -14,17 +14,17 @@ VERSION=2.2.0
# DATABASE CONFIGURATION (PostgreSQL required)
# =============================================================================
# Default works with: docker-compose up -d db
DATABASE_URL=postgresql://wizamart_user:secure_password@localhost:5432/wizamart_db
DATABASE_URL=postgresql://orion_user:secure_password@localhost:5432/orion_db
# For production, use your PostgreSQL connection string:
# DATABASE_URL=postgresql://username:password@production-host:5432/wizamart_db
# DATABASE_URL=postgresql://username:password@production-host:5432/orion_db
# =============================================================================
# ADMIN INITIALIZATION
# =============================================================================
# These are used by init_production.py to create the platform admin
# ⚠️ CHANGE THESE IN PRODUCTION!
ADMIN_EMAIL=admin@wizamart.com
ADMIN_EMAIL=admin@orion.lu
ADMIN_USERNAME=admin
ADMIN_PASSWORD=change-me-in-production
ADMIN_FIRST_NAME=Platform
@@ -49,9 +49,9 @@ API_PORT=8000
# Development
DOCUMENTATION_URL=http://localhost:8001
# Staging
# DOCUMENTATION_URL=https://staging-docs.wizamart.com
# DOCUMENTATION_URL=https://staging-docs.orion.lu
# Production
# DOCUMENTATION_URL=https://docs.wizamart.com
# DOCUMENTATION_URL=https://docs.orion.lu
# =============================================================================
# RATE LIMITING
@@ -70,7 +70,7 @@ LOG_FILE=logs/app.log
# PLATFORM DOMAIN CONFIGURATION
# =============================================================================
# Your main platform domain
PLATFORM_DOMAIN=wizamart.com
PLATFORM_DOMAIN=orion.lu
# Custom domain features
# Enable/disable custom domains
@@ -85,7 +85,7 @@ SSL_PROVIDER=letsencrypt
AUTO_PROVISION_SSL=False
# DNS verification
DNS_VERIFICATION_PREFIX=_wizamart-verify
DNS_VERIFICATION_PREFIX=_orion-verify
DNS_VERIFICATION_TTL=3600
# =============================================================================
@@ -103,8 +103,8 @@ STRIPE_TRIAL_DAYS=30
# =============================================================================
# Provider: smtp, sendgrid, mailgun, ses
EMAIL_PROVIDER=smtp
EMAIL_FROM_ADDRESS=noreply@wizamart.com
EMAIL_FROM_NAME=Wizamart
EMAIL_FROM_ADDRESS=noreply@orion.lu
EMAIL_FROM_NAME=Orion
EMAIL_REPLY_TO=
# SMTP Settings (used when EMAIL_PROVIDER=smtp)
@@ -185,7 +185,7 @@ STORAGE_BACKEND=local
R2_ACCOUNT_ID=
R2_ACCESS_KEY_ID=
R2_SECRET_ACCESS_KEY=
R2_BUCKET_NAME=wizamart-media
R2_BUCKET_NAME=orion-media
# Public URL for R2 bucket (optional - for custom domain)
# If not set, uses Cloudflare's default R2 public URL

View File

@@ -45,11 +45,11 @@ jobs:
postgres:
image: postgres:15
env:
POSTGRES_DB: wizamart_test
POSTGRES_DB: orion_test
POSTGRES_USER: test_user
POSTGRES_PASSWORD: test_password
options: >-
--health-cmd "pg_isready -U test_user -d wizamart_test"
--health-cmd "pg_isready -U test_user -d orion_test"
--health-interval 10s
--health-timeout 5s
--health-retries 5
@@ -57,8 +57,8 @@ jobs:
env:
# act_runner executes jobs in Docker containers on the same network as services,
# so use the service name (postgres) as hostname with the internal port (5432)
TEST_DATABASE_URL: "postgresql://test_user:test_password@postgres:5432/wizamart_test"
DATABASE_URL: "postgresql://test_user:test_password@postgres:5432/wizamart_test"
TEST_DATABASE_URL: "postgresql://test_user:test_password@postgres:5432/orion_test"
DATABASE_URL: "postgresql://test_user:test_password@postgres:5432/orion_test"
steps:
- uses: actions/checkout@v4

2
.gitignore vendored
View File

@@ -183,5 +183,5 @@ tailadmin-free-tailwind-dashboard-template/
static/shared/css/tailwind.css
# Export files
wizamart_letzshop_export_*.csv
orion_letzshop_export_*.csv
exports/

View File

@@ -43,13 +43,13 @@ pytest:
alias: postgres
variables:
# PostgreSQL service configuration
POSTGRES_DB: wizamart_test
POSTGRES_DB: orion_test
POSTGRES_USER: test_user
POSTGRES_PASSWORD: test_password
# Application database URL for tests
TEST_DATABASE_URL: "postgresql://test_user:test_password@postgres:5432/wizamart_test"
TEST_DATABASE_URL: "postgresql://test_user:test_password@postgres:5432/orion_test"
# Skip database validation during import (tests use TEST_DATABASE_URL)
DATABASE_URL: "postgresql://test_user:test_password@postgres:5432/wizamart_test"
DATABASE_URL: "postgresql://test_user:test_password@postgres:5432/orion_test"
before_script:
- pip install uv
- uv sync --frozen

View File

@@ -116,7 +116,7 @@ return {
### Duplicate /shop/ Prefix
**Problem:** Routes like `/stores/wizamart/shop/shop/products/4`
**Problem:** Routes like `/stores/orion/shop/shop/products/4`
**Root Cause:**
```python
@@ -136,7 +136,7 @@ All routes in `shop_pages.py` fixed.
### Missing /shop/ in Template Links
**Problem:** Links went to `/stores/wizamart/products` instead of `/shop/products`
**Problem:** Links went to `/stores/orion/products` instead of `/shop/products`
**Fix:** Updated all templates:
- `shop/base.html` - Header, footer, navigation
@@ -290,15 +290,15 @@ Comprehensive guide covering:
### Test URLs
```
Landing Pages:
- http://localhost:8000/stores/wizamart/
- http://localhost:8000/stores/orion/
- http://localhost:8000/stores/fashionhub/
- http://localhost:8000/stores/bookstore/
Shop Pages:
- http://localhost:8000/stores/wizamart/shop/
- http://localhost:8000/stores/wizamart/shop/products
- http://localhost:8000/stores/wizamart/shop/products/1
- http://localhost:8000/stores/wizamart/shop/cart
- http://localhost:8000/stores/orion/shop/
- http://localhost:8000/stores/orion/shop/products
- http://localhost:8000/stores/orion/shop/products/1
- http://localhost:8000/stores/orion/shop/cart
```
## Breaking Changes

View File

@@ -1,4 +1,4 @@
# Wizamart Multi-Tenant E-Commerce Platform Makefile
# Orion Multi-Tenant E-Commerce Platform Makefile
# Cross-platform compatible (Windows & Linux)
.PHONY: install install-dev install-docs install-all dev test test-coverage lint format check docker-build docker-up docker-down clean help tailwind-install tailwind-dev tailwind-build tailwind-watch arch-check arch-check-file arch-check-object test-db-up test-db-down test-db-reset test-db-status celery-worker celery-beat celery-dev flower celery-status celery-purge urls
@@ -132,7 +132,7 @@ seed-tiers:
# First-time installation - Complete setup with configuration validation
platform-install:
@echo "🚀 WIZAMART PLATFORM INSTALLATION"
@echo "🚀 ORION PLATFORM INSTALLATION"
@echo "=================================="
$(PYTHON) scripts/seed/install.py
@@ -235,7 +235,7 @@ test-db-status:
# =============================================================================
# Test database URL
TEST_DB_URL := postgresql://test_user:test_password@localhost:5433/wizamart_test
TEST_DB_URL := postgresql://test_user:test_password@localhost:5433/orion_test
# Build pytest marker expression from module= and frontend= params
MARKER_EXPR :=
@@ -530,7 +530,7 @@ endif
# =============================================================================
help:
@echo "Wizamart Platform Development Commands"
@echo "Orion Platform Development Commands"
@echo ""
@echo "=== SETUP ==="
@echo " install - Install production dependencies"
@@ -681,4 +681,4 @@ help-db:
@echo " - Email provider settings (SMTP/SendGrid/Mailgun/SES)"
@echo " - ADMIN_PASSWORD (strong password)"
@echo " 2. make platform-install # Validates + initializes"
@echo " 3. DO NOT run seed-demo in production!"
@echo " 3. DO NOT run seed-demo in production!"

View File

@@ -34,7 +34,7 @@ This FastAPI application provides a complete ecommerce backend solution designed
### Project Structure
```
wizamart/
orion/
├── main.py # FastAPI application entry point
├── app/
│ ├── core/
@@ -179,8 +179,8 @@ make qa
```bash
# Clone the repository
git clone <wizamart-repo>
cd wizamart-repo
git clone <orion-repo>
cd orion-repo
# Create virtual environment
python -m venv venv
@@ -447,7 +447,7 @@ PROD002,"Super Gadget","A fantastic gadget",19.99,EUR,GadgetInc,9876543210987,Am
- `POST /api/v1/marketplace/import-product` - Start CSV import
- `GET /api/v1/marketplace/import-status/{job_id}` - Check import status
- `GET /api/v1/marketplace/import-jobs` - List import jobs
-
-
### Inventory Endpoints
- `POST /api/v1/inventory` - Set inventory quantity
- `POST /api/v1/inventory/add` - Add to inventory
@@ -700,7 +700,7 @@ make help
This will display all available commands organized by category:
- **Setup**: Installation and environment setup
- **Development**: Development servers and workflows
- **Development**: Development servers and workflows
- **Documentation**: Documentation building and deployment
- **Testing**: Various test execution options
- **Code Quality**: Formatting, linting, and quality checks
@@ -734,4 +734,4 @@ This will display all available commands organized by category:
- **Health Check**: http://localhost:8000/health
- **Version Info**: http://localhost:8000/
For issues and feature requests, please create an issue in the repository.
For issues and feature requests, please create an issue in the repository.

View File

@@ -11,7 +11,7 @@
If you discover a security vulnerability in this project, please report it responsibly:
1. **Do not** open a public issue
2. Email the security team at: security@wizamart.com
2. Email the security team at: security@orion.lu
3. Include:
- Description of the vulnerability
- Steps to reproduce

View File

@@ -1,6 +1,6 @@
# Terminology Guide
This document defines the standard terminology used throughout the Wizamart codebase.
This document defines the standard terminology used throughout the Orion codebase.
## Core Multi-Tenant Entities

View File

@@ -6,12 +6,12 @@ Landing pages have been created for three stores with different templates.
## 📍 Test URLs
### 1. WizaMart - Modern Template
### 1. Orion - Modern Template
**Landing Page:**
- http://localhost:8000/stores/wizamart/
- http://localhost:8000/stores/orion/
**Shop Page:**
- http://localhost:8000/stores/wizamart/shop/
- http://localhost:8000/stores/orion/shop/
**What to expect:**
- Full-screen hero section with animations
@@ -93,8 +93,8 @@ db.close()
"
```
Then visit: http://localhost:8000/stores/wizamart/
- Should automatically redirect to: http://localhost:8000/stores/wizamart/shop/
Then visit: http://localhost:8000/stores/orion/
- Should automatically redirect to: http://localhost:8000/stores/orion/shop/
---
@@ -111,17 +111,17 @@ Or programmatically:
```python
from scripts.create_landing_page import create_landing_page
# Change WizaMart to default template
create_landing_page('wizamart', template='default')
# Change Orion to default template
create_landing_page('orion', template='default')
# Change to minimal
create_landing_page('wizamart', template='minimal')
create_landing_page('orion', template='minimal')
# Change to full
create_landing_page('wizamart', template='full')
create_landing_page('orion', template='full')
# Change back to modern
create_landing_page('wizamart', template='modern')
create_landing_page('orion', template='modern')
```
---
@@ -130,7 +130,7 @@ create_landing_page('wizamart', template='modern')
| Store | Subdomain | Template | Landing Page URL |
|--------|-----------|----------|------------------|
| WizaMart | wizamart | **modern** | http://localhost:8000/stores/wizamart/ |
| Orion | orion | **modern** | http://localhost:8000/stores/orion/ |
| Fashion Hub | fashionhub | **minimal** | http://localhost:8000/stores/fashionhub/ |
| The Book Store | bookstore | **full** | http://localhost:8000/stores/bookstore/ |
@@ -146,7 +146,7 @@ sqlite3 letzshop.db "SELECT id, store_id, slug, title, template, is_published FR
Expected output:
```
8|1|landing|Welcome to WizaMart|modern|1
8|1|landing|Welcome to Orion|modern|1
9|2|landing|Fashion Hub - Style & Elegance|minimal|1
10|3|landing|The Book Store - Your Literary Haven|full|1
```
@@ -180,7 +180,7 @@ Expected output:
## ✅ Success Checklist
- [ ] WizaMart landing page loads (modern template)
- [ ] Orion landing page loads (modern template)
- [ ] Fashion Hub landing page loads (minimal template)
- [ ] Book Store landing page loads (full template)
- [ ] "Shop Now" buttons work correctly

View File

@@ -19,7 +19,7 @@ def upgrade() -> None:
"platforms",
sa.Column("id", sa.Integer(), primary_key=True, index=True),
sa.Column("code", sa.String(50), unique=True, nullable=False, index=True, comment="Unique platform identifier (e.g., 'oms', 'loyalty', 'sites')"),
sa.Column("name", sa.String(100), nullable=False, comment="Display name (e.g., 'Wizamart OMS')"),
sa.Column("name", sa.String(100), nullable=False, comment="Display name (e.g., 'Orion OMS')"),
sa.Column("description", sa.Text(), nullable=True, comment="Platform description for admin/marketing purposes"),
sa.Column("domain", sa.String(255), unique=True, nullable=True, index=True, comment="Production domain (e.g., 'oms.lu', 'loyalty.lu')"),
sa.Column("path_prefix", sa.String(50), unique=True, nullable=True, index=True, comment="Development path prefix (e.g., 'oms' for localhost:9999/oms/*)"),

View File

@@ -1,6 +1,6 @@
# app/core/celery_config.py
"""
Celery configuration for Wizamart background task processing.
Celery configuration for Orion background task processing.
This module configures Celery with Redis as the broker and result backend.
It includes:
@@ -66,7 +66,7 @@ def get_all_task_modules() -> list[str]:
# Create Celery application
celery_app = Celery(
"wizamart",
"orion",
broker=REDIS_URL,
backend=REDIS_URL,
include=get_all_task_modules(),

View File

@@ -27,7 +27,7 @@ class Settings(BaseSettings):
# =============================================================================
# PROJECT INFORMATION
# =============================================================================
project_name: str = "Wizamart - Multi-Store Marketplace Platform"
project_name: str = "Orion - Multi-Store Marketplace Platform"
version: str = "2.2.0"
# Clean description without HTML
@@ -47,12 +47,12 @@ class Settings(BaseSettings):
# =============================================================================
# DATABASE (PostgreSQL only)
# =============================================================================
database_url: str = "postgresql://wizamart_user:secure_password@localhost:5432/wizamart_db"
database_url: str = "postgresql://orion_user:secure_password@localhost:5432/orion_db"
# =============================================================================
# ADMIN INITIALIZATION (for init_production.py)
# =============================================================================
admin_email: str = "admin@wizamart.com"
admin_email: str = "admin@orion.lu"
admin_username: str = "admin"
admin_password: str = "admin123" # CHANGE IN PRODUCTION!
admin_first_name: str = "Platform"
@@ -96,7 +96,7 @@ class Settings(BaseSettings):
# =============================================================================
# PLATFORM DOMAIN CONFIGURATION
# =============================================================================
platform_domain: str = "wizamart.com"
platform_domain: str = "orion.lu"
# Custom domain features
allow_custom_domains: bool = True
@@ -107,7 +107,7 @@ class Settings(BaseSettings):
auto_provision_ssl: bool = False
# DNS verification
dns_verification_prefix: str = "_wizamart-verify"
dns_verification_prefix: str = "_orion-verify"
dns_verification_ttl: int = 3600
# =============================================================================
@@ -130,8 +130,8 @@ class Settings(BaseSettings):
# =============================================================================
# Provider: smtp, sendgrid, mailgun, ses
email_provider: str = "smtp"
email_from_address: str = "noreply@wizamart.com"
email_from_name: str = "Wizamart"
email_from_address: str = "noreply@orion.lu"
email_from_name: str = "Orion"
email_reply_to: str = "" # Optional reply-to address
# SMTP Settings (used when email_provider=smtp)
@@ -201,7 +201,7 @@ class Settings(BaseSettings):
r2_account_id: str | None = None
r2_access_key_id: str | None = None
r2_secret_access_key: str | None = None
r2_bucket_name: str = "wizamart-media"
r2_bucket_name: str = "orion-media"
r2_public_url: str | None = None # Custom domain for public access (e.g., https://media.yoursite.com)
# =============================================================================

View File

@@ -9,7 +9,7 @@ Detection priority:
1. Admin subdomain (admin.oms.lu)
2. Path-based admin/store (/admin/*, /store/*, /api/v1/admin/*)
3. Custom domain lookup (mybakery.lu -> STOREFRONT)
4. Store subdomain (wizamart.oms.lu -> STOREFRONT)
4. Store subdomain (orion.oms.lu -> STOREFRONT)
5. Storefront paths (/storefront/*, /api/v1/storefront/*)
6. Default to PLATFORM (marketing pages)
@@ -62,7 +62,7 @@ class FrontendDetector:
Detect frontend type from request.
Args:
host: Request host header (e.g., "oms.lu", "wizamart.oms.lu", "localhost:8000")
host: Request host header (e.g., "oms.lu", "orion.oms.lu", "localhost:8000")
path: Request path (e.g., "/admin/stores", "/storefront/products")
has_store_context: True if request.state.store is set (from middleware)
@@ -110,7 +110,7 @@ class FrontendDetector:
logger.debug("[FRONTEND_DETECTOR] Detected PLATFORM from path")
return FrontendType.PLATFORM
# 3. Store subdomain detection (wizamart.oms.lu)
# 3. Store subdomain detection (orion.oms.lu)
# If subdomain exists and is not reserved -> it's a store storefront
if subdomain and subdomain not in cls.RESERVED_SUBDOMAINS:
logger.debug(
@@ -138,7 +138,7 @@ class FrontendDetector:
@classmethod
def _get_subdomain(cls, host: str) -> str | None:
"""
Extract subdomain from host (e.g., 'wizamart' from 'wizamart.oms.lu').
Extract subdomain from host (e.g., 'orion' from 'orion.oms.lu').
Returns None for localhost, IP addresses, or root domains.
Handles special case of admin.localhost for development.

View File

@@ -32,13 +32,13 @@ async def lifespan(app: FastAPI):
# === STARTUP ===
app_logger = setup_logging()
app_logger.info("Starting Wizamart multi-tenant platform")
app_logger.info("Starting Orion multi-tenant platform")
logger.info("[OK] Application startup completed")
yield
# === SHUTDOWN ===
app_logger.info("Shutting down Wizamart platform")
app_logger.info("Shutting down Orion platform")
# Add cleanup tasks here if needed

View File

@@ -33,16 +33,16 @@ from .base import (
BusinessLogicException,
ConflictException,
ExternalServiceException,
OrionException,
RateLimitException,
ResourceNotFoundException,
ServiceUnavailableException,
ValidationException,
WizamartException,
)
__all__ = [
# Base exception class
"WizamartException",
"OrionException",
# Validation and business logic
"ValidationException",
"BusinessLogicException",

View File

@@ -11,7 +11,7 @@ This module provides classes and functions for:
from typing import Any
class WizamartException(Exception):
class OrionException(Exception):
"""Base exception class for all custom exceptions."""
def __init__(
@@ -39,7 +39,7 @@ class WizamartException(Exception):
return result
class ValidationException(WizamartException):
class ValidationException(OrionException):
"""Raised when request validation fails."""
def __init__(
@@ -60,7 +60,7 @@ class ValidationException(WizamartException):
)
class AuthenticationException(WizamartException):
class AuthenticationException(OrionException):
"""Raised when authentication fails."""
def __init__(
@@ -77,7 +77,7 @@ class AuthenticationException(WizamartException):
)
class AuthorizationException(WizamartException):
class AuthorizationException(OrionException):
"""Raised when user lacks permission for an operation."""
def __init__(
@@ -94,7 +94,7 @@ class AuthorizationException(WizamartException):
)
class ResourceNotFoundException(WizamartException):
class ResourceNotFoundException(OrionException):
"""Raised when a requested resource is not found."""
def __init__(
@@ -120,7 +120,7 @@ class ResourceNotFoundException(WizamartException):
)
class ConflictException(WizamartException):
class ConflictException(OrionException):
"""Raised when a resource conflict occurs."""
def __init__(
@@ -137,7 +137,7 @@ class ConflictException(WizamartException):
)
class BusinessLogicException(WizamartException):
class BusinessLogicException(OrionException):
"""Raised when business logic rules are violated."""
def __init__(
@@ -154,7 +154,7 @@ class BusinessLogicException(WizamartException):
)
class ExternalServiceException(WizamartException):
class ExternalServiceException(OrionException):
"""Raised when an external service fails."""
def __init__(
@@ -175,7 +175,7 @@ class ExternalServiceException(WizamartException):
)
class RateLimitException(WizamartException):
class RateLimitException(OrionException):
"""Raised when rate limit is exceeded."""
def __init__(
@@ -196,7 +196,7 @@ class RateLimitException(WizamartException):
)
class ServiceUnavailableException(WizamartException):
class ServiceUnavailableException(OrionException):
"""Raised when service is unavailable."""
def __init__(self, message: str = "Service temporarily unavailable"):

View File

@@ -19,7 +19,7 @@ from fastapi.responses import JSONResponse, RedirectResponse
from app.modules.enums import FrontendType
from middleware.frontend_type import get_frontend_type
from .base import WizamartException
from .base import OrionException
from .error_renderer import ErrorPageRenderer
logger = logging.getLogger(__name__)
@@ -28,8 +28,8 @@ logger = logging.getLogger(__name__)
def setup_exception_handlers(app):
"""Setup exception handlers for the FastAPI app."""
@app.exception_handler(WizamartException)
async def custom_exception_handler(request: Request, exc: WizamartException):
@app.exception_handler(OrionException)
async def custom_exception_handler(request: Request, exc: OrionException):
"""Handle custom exceptions with context-aware rendering."""
# Special handling for auth errors on HTML page requests (redirect to login)

View File

@@ -37,7 +37,7 @@ class MerchantSubscription(Base, TimestampMixin):
Example:
Merchant "Boucherie Luxembourg" subscribes to:
- Wizamart OMS (Professional tier)
- Orion OMS (Professional tier)
- Loyalty+ (Essential tier)
Their stores inherit features from the merchant's subscription.

View File

@@ -119,7 +119,7 @@ async def signup_success_page(
Shown after successful account creation.
"""
context = get_platform_context(request, db)
context["page_title"] = "Welcome to Wizamart!"
context["page_title"] = "Welcome to Orion!"
context["store_code"] = store_code
return templates.TemplateResponse(

View File

@@ -324,7 +324,7 @@ function storeInvoices() {
try {
// Get the token for authentication
const token = localStorage.getItem('wizamart_token') || localStorage.getItem('store_token');
const token = localStorage.getItem('orion_token') || localStorage.getItem('store_token');
if (!token) {
throw new Error('Not authenticated');
}

View File

@@ -2,7 +2,7 @@
{# Standalone Pricing Page #}
{% extends "platform/base.html" %}
{% block title %}{{ _("cms.platform.pricing.title") }} - Wizamart{% endblock %}
{% block title %}{{ _("cms.platform.pricing.title") }} - Orion{% endblock %}
{% block content %}
<div x-data="{ annual: false }" class="py-16 lg:py-24">

View File

@@ -2,7 +2,7 @@
{# Multi-step Signup Wizard #}
{% extends "platform/base.html" %}
{% block title %}Start Your Free Trial - Wizamart{% endblock %}
{% block title %}Start Your Free Trial - Orion{% endblock %}
{% block extra_head %}
{# Stripe.js for payment #}

View File

@@ -133,7 +133,7 @@
"creating_account": "Erstelle Ihr Konto..."
},
"success": {
"title": "Willkommen bei Wizamart!",
"title": "Willkommen bei Orion!",
"subtitle": "Ihr Konto wurde erstellt und Ihre {trial_days}-tägige kostenlose Testphase hat begonnen.",
"what_next": "Was kommt als Nächstes?",
"step_connect": "Letzshop verbinden:",
@@ -149,7 +149,7 @@
},
"cta": {
"title": "Bereit, Ihre Bestellungen zu optimieren?",
"subtitle": "Schließen Sie sich Letzshop-Händlern an, die Wizamart für ihre Bestellverwaltung vertrauen. Starten Sie heute Ihre {trial_days}-tägige kostenlose Testversion.",
"subtitle": "Schließen Sie sich Letzshop-Händlern an, die Orion für ihre Bestellverwaltung vertrauen. Starten Sie heute Ihre {trial_days}-tägige kostenlose Testversion.",
"button": "Kostenlos testen"
},
"footer": {
@@ -157,7 +157,7 @@
"quick_links": "Schnelllinks",
"platform": "Plattform",
"contact": "Kontakt",
"copyright": "© {year} Wizamart. Entwickelt für den luxemburgischen E-Commerce.",
"copyright": "© {year} Orion. Entwickelt für den luxemburgischen E-Commerce.",
"privacy": "Datenschutzerklärung",
"terms": "Nutzungsbedingungen",
"about": "Über uns",
@@ -188,7 +188,7 @@
"how_step1": "Letzshop verbinden",
"how_step1_desc": "Geben Sie Ihre Letzshop-API-Zugangsdaten ein. In 2 Minuten erledigt, keine technischen Kenntnisse erforderlich.",
"how_step2": "Bestellungen kommen rein",
"how_step2_desc": "Bestellungen werden automatisch synchronisiert. Bestätigen und Tracking direkt von Wizamart hinzufügen.",
"how_step2_desc": "Bestellungen werden automatisch synchronisiert. Bestätigen und Tracking direkt von Orion hinzufügen.",
"how_step3": "Rechnungen erstellen",
"how_step3_desc": "Ein Klick, um konforme PDF-Rechnungen mit korrekter MwSt für jedes EU-Land zu erstellen.",
"how_step4": "Ihr Geschäft ausbauen",

View File

@@ -133,7 +133,7 @@
"creating_account": "Creating your account..."
},
"success": {
"title": "Welcome to Wizamart!",
"title": "Welcome to Orion!",
"subtitle": "Your account has been created and your {trial_days}-day free trial has started.",
"what_next": "What's Next?",
"step_connect": "Connect Letzshop:",
@@ -149,7 +149,7 @@
},
"cta": {
"title": "Ready to Streamline Your Orders?",
"subtitle": "Join Letzshop stores who trust Wizamart for their order management. Start your {trial_days}-day free trial today.",
"subtitle": "Join Letzshop stores who trust Orion for their order management. Start your {trial_days}-day free trial today.",
"button": "Start Free Trial"
},
"footer": {
@@ -157,7 +157,7 @@
"quick_links": "Quick Links",
"platform": "Platform",
"contact": "Contact",
"copyright": "© {year} Wizamart. Built for Luxembourg e-commerce.",
"copyright": "© {year} Orion. Built for Luxembourg e-commerce.",
"privacy": "Privacy Policy",
"terms": "Terms of Service",
"about": "About Us",
@@ -188,7 +188,7 @@
"how_step1": "Connect Letzshop",
"how_step1_desc": "Enter your Letzshop API credentials. Done in 2 minutes, no technical skills needed.",
"how_step2": "Orders Flow In",
"how_step2_desc": "Orders sync automatically. Confirm and add tracking directly from Wizamart.",
"how_step2_desc": "Orders sync automatically. Confirm and add tracking directly from Orion.",
"how_step3": "Generate Invoices",
"how_step3_desc": "One click to create compliant PDF invoices with correct VAT for any EU country.",
"how_step4": "Grow Your Business",

View File

@@ -133,7 +133,7 @@
"creating_account": "Création de votre compte..."
},
"success": {
"title": "Bienvenue sur Wizamart !",
"title": "Bienvenue sur Orion !",
"subtitle": "Votre compte a été créé et votre essai gratuit de {trial_days} jours a commencé.",
"what_next": "Et maintenant ?",
"step_connect": "Connecter Letzshop :",
@@ -149,7 +149,7 @@
},
"cta": {
"title": "Prêt à optimiser vos commandes ?",
"subtitle": "Rejoignez les vendeurs Letzshop qui font confiance à Wizamart pour leur gestion de commandes. Commencez votre essai gratuit de {trial_days} jours aujourd'hui.",
"subtitle": "Rejoignez les vendeurs Letzshop qui font confiance à Orion pour leur gestion de commandes. Commencez votre essai gratuit de {trial_days} jours aujourd'hui.",
"button": "Essai gratuit"
},
"footer": {
@@ -157,7 +157,7 @@
"quick_links": "Liens rapides",
"platform": "Plateforme",
"contact": "Contact",
"copyright": "© {year} Wizamart. Conçu pour le e-commerce luxembourgeois.",
"copyright": "© {year} Orion. Conçu pour le e-commerce luxembourgeois.",
"privacy": "Politique de confidentialité",
"terms": "Conditions d'utilisation",
"about": "À propos",
@@ -188,7 +188,7 @@
"how_step1": "Connecter Letzshop",
"how_step1_desc": "Entrez vos identifiants API Letzshop. Fait en 2 minutes, aucune compétence technique requise.",
"how_step2": "Les commandes arrivent",
"how_step2_desc": "Les commandes se synchronisent automatiquement. Confirmez et ajoutez le suivi directement depuis Wizamart.",
"how_step2_desc": "Les commandes se synchronisent automatiquement. Confirmez et ajoutez le suivi directement depuis Orion.",
"how_step3": "Générer des factures",
"how_step3_desc": "Un clic pour créer des factures PDF conformes avec la TVA correcte pour tout pays UE.",
"how_step4": "Développez votre entreprise",

View File

@@ -133,7 +133,7 @@
"creating_account": "Erstellt Äre Kont..."
},
"success": {
"title": "Wëllkomm bei Wizamart!",
"title": "Wëllkomm bei Orion!",
"subtitle": "Äre Kont gouf erstallt an Är {trial_days}-Deeg gratis Testversioun huet ugefaang.",
"what_next": "Wat kënnt duerno?",
"step_connect": "Letzshop verbannen:",
@@ -149,7 +149,7 @@
},
"cta": {
"title": "Prett fir Är Bestellungen ze optiméieren?",
"subtitle": "Schléisst Iech Letzshop Händler un déi Wizamart fir hir Bestellungsverwaltung vertrauen. Fänkt haut Är {trial_days}-Deeg gratis Testversioun un.",
"subtitle": "Schléisst Iech Letzshop Händler un déi Orion fir hir Bestellungsverwaltung vertrauen. Fänkt haut Är {trial_days}-Deeg gratis Testversioun un.",
"button": "Gratis Testen"
},
"footer": {
@@ -157,7 +157,7 @@
"quick_links": "Séier Linken",
"platform": "Plattform",
"contact": "Kontakt",
"copyright": "© {year} Wizamart. Gemaach fir de lëtzebuergeschen E-Commerce.",
"copyright": "© {year} Orion. Gemaach fir de lëtzebuergeschen E-Commerce.",
"privacy": "Dateschutzrichtlinn",
"terms": "Notzungsbedéngungen",
"about": "Iwwer eis",
@@ -188,7 +188,7 @@
"how_step1": "Letzshop verbannen",
"how_step1_desc": "Gitt Är Letzshop API Zougangsdaten an. An 2 Minutte fäerdeg, keng technesch Kenntnisser néideg.",
"how_step2": "Bestellunge kommen eran",
"how_step2_desc": "Bestellunge ginn automatesch synchroniséiert. Confirméiert an Tracking direkt vu Wizamart derbäisetzen.",
"how_step2_desc": "Bestellunge ginn automatesch synchroniséiert. Confirméiert an Tracking direkt vu Orion derbäisetzen.",
"how_step3": "Rechnunge generéieren",
"how_step3_desc": "Ee Klick fir konform PDF Rechnunge mat korrekter TVA fir all EU Land ze erstellen.",
"how_step4": "Äert Geschäft ausbauen",

View File

@@ -165,8 +165,8 @@ async def homepage(
logger.info(f"[HOMEPAGE] Rendering CMS homepage with template: {template_path}")
return templates.TemplateResponse(template_path, context)
# Fallback: Default wizamart homepage (no CMS content)
logger.info("[HOMEPAGE] No CMS homepage found, using default wizamart template")
# Fallback: Default orion homepage (no CMS content)
logger.info("[HOMEPAGE] No CMS homepage found, using default orion template")
context = get_platform_context(request, db)
context["tiers"] = _get_tiers_data(db)
@@ -204,7 +204,7 @@ async def homepage(
]
return templates.TemplateResponse(
"cms/platform/homepage-wizamart.html",
"cms/platform/homepage-orion.html",
context,
)

View File

@@ -160,7 +160,7 @@ async def store_content_page(
Generic content page handler for store shop (CMS).
Handles dynamic content pages like:
- /stores/wizamart/about, /stores/wizamart/faq, /stores/wizamart/contact, etc.
- /stores/orion/about, /stores/orion/faq, /stores/orion/contact, etc.
Features:
- Two-tier system: Store overrides take priority, fallback to platform defaults

View File

@@ -1,9 +1,9 @@
{# app/templates/platform/homepage-modern.html #}
{# Wizamart OMS - Luxembourg-focused homepage inspired by Veeqo #}
{# Orion OMS - Luxembourg-focused homepage inspired by Veeqo #}
{% extends "platform/base.html" %}
{% block title %}
Wizamart - The Back-Office for Letzshop Sellers
Orion - The Back-Office for Letzshop Sellers
{% endblock %}
{% block extra_head %}
@@ -85,7 +85,7 @@
<div class="w-3 h-3 rounded-full bg-red-500"></div>
<div class="w-3 h-3 rounded-full bg-yellow-500"></div>
<div class="w-3 h-3 rounded-full bg-green-500"></div>
<span class="ml-4 text-gray-400 text-sm">Wizamart Dashboard</span>
<span class="ml-4 text-gray-400 text-sm">Orion Dashboard</span>
</div>
{# Mock dashboard content #}
<div class="p-6 space-y-4">
@@ -219,7 +219,7 @@
<div class="bg-white dark:bg-gray-900 rounded-2xl p-8 shadow-lg h-full">
<div class="w-12 h-12 rounded-full bg-blue-500 text-white flex items-center justify-center font-bold text-xl mb-6">2</div>
<h3 class="text-xl font-bold text-gray-900 dark:text-white mb-3">Orders Flow In</h3>
<p class="text-gray-600 dark:text-gray-400">Orders sync automatically. Confirm and add tracking directly from Wizamart.</p>
<p class="text-gray-600 dark:text-gray-400">Orders sync automatically. Confirm and add tracking directly from Orion.</p>
</div>
<div class="hidden lg:block absolute top-1/2 -right-4 w-8 h-0.5 bg-blue-300"></div>
</div>

View File

@@ -1,9 +1,9 @@
{# app/templates/platform/homepage-wizamart.html #}
{# Wizamart Marketing Homepage - Letzshop OMS Platform #}
{# app/templates/platform/homepage-orion.html #}
{# Orion Marketing Homepage - Letzshop OMS Platform #}
{% extends "platform/base.html" %}
{% from 'shared/macros/inputs.html' import toggle_switch %}
{% block title %}Wizamart - Order Management for Letzshop Sellers{% endblock %}
{% block title %}Orion - Order Management for Letzshop Sellers{% endblock %}
{% block meta_description %}Lightweight OMS for Letzshop stores. Manage orders, inventory, and invoicing. Start your 30-day free trial today.{% endblock %}
{% block content %}
@@ -212,7 +212,7 @@
{# CTA Button #}
{% if tier.is_enterprise %}
<a href="mailto:sales@wizamart.com?subject=Enterprise%20Plan%20Inquiry"
<a href="mailto:sales@orion.lu?subject=Enterprise%20Plan%20Inquiry"
class="block w-full py-3 px-4 bg-gray-200 dark:bg-gray-700 text-gray-900 dark:text-white font-semibold rounded-xl text-center hover:bg-gray-300 dark:hover:bg-gray-600 transition-colors">
{{ _("cms.platform.pricing.contact_sales") }}
</a>

View File

@@ -7,10 +7,10 @@ Exceptions for core platform functionality including:
- Settings management
"""
from app.exceptions import WizamartException
from app.exceptions import OrionException
class CoreException(WizamartException): # noqa: MOD-025
class CoreException(OrionException): # noqa: MOD-025
"""Base exception for core module."""

View File

@@ -663,11 +663,11 @@ def send_test_email(
email_log = email_service.send_raw(
to_email=request.to_email,
to_name=None,
subject="Wizamart Platform - Test Email",
subject="Orion Platform - Test Email",
body_html=f"""
<html>
<body style="font-family: Arial, sans-serif; padding: 20px;">
<h2 style="color: #6b46c1;">Test Email from Wizamart</h2>
<h2 style="color: #6b46c1;">Test Email from Orion</h2>
<p>This is a test email to verify your platform email configuration.</p>
<p>If you received this email, your email settings are working correctly!</p>
<hr style="border: none; border-top: 1px solid #e5e7eb; margin: 20px 0;">
@@ -678,7 +678,7 @@ def send_test_email(
</body>
</html>
""",
body_text=f"Test email from Wizamart platform.\n\nProvider: {app_settings.email_provider}\nFrom: {app_settings.email_from_address}",
body_text=f"Test email from Orion platform.\n\nProvider: {app_settings.email_provider}\nFrom: {app_settings.email_from_address}",
is_platform_email=True,
)

View File

@@ -616,7 +616,7 @@ class OnboardingService:
"success": True,
"step_completed": True,
"onboarding_completed": True,
"message": "Onboarding complete! Welcome to Wizamart.",
"message": "Onboarding complete! Welcome to Orion.",
"redirect_url": f"/store/{store_code}/dashboard",
}

View File

@@ -437,7 +437,7 @@ function storeLetzshop() {
});
// Get the token for authentication
const token = localStorage.getItem('wizamart_token');
const token = localStorage.getItem('orion_token');
if (!token) {
throw new Error('Not authenticated');
}

View File

@@ -16,7 +16,7 @@ const onboardingLog = window.LogConfig?.createLogger('ONBOARDING') || console;
// Onboarding translations
const onboardingTranslations = {
en: {
title: 'Welcome to Wizamart',
title: 'Welcome to Orion',
subtitle: 'Complete these steps to set up your store',
steps: {
merchant_profile: 'Merchant Profile',
@@ -71,7 +71,7 @@ const onboardingTranslations = {
},
step4: {
title: 'Historical Order Import',
description: 'Import your existing orders from Letzshop to start managing them in Wizamart.',
description: 'Import your existing orders from Letzshop to start managing them in Orion.',
days_back: 'Import orders from last',
days: 'days',
start_import: 'Start Import',
@@ -94,7 +94,7 @@ const onboardingTranslations = {
},
},
fr: {
title: 'Bienvenue sur Wizamart',
title: 'Bienvenue sur Orion',
subtitle: 'Complétez ces étapes pour configurer votre boutique',
steps: {
merchant_profile: 'Profil Entreprise',
@@ -149,7 +149,7 @@ const onboardingTranslations = {
},
step4: {
title: 'Import Historique des Commandes',
description: 'Importez vos commandes existantes de Letzshop pour commencer à les gérer dans Wizamart.',
description: 'Importez vos commandes existantes de Letzshop pour commencer à les gérer dans Orion.',
days_back: 'Importer les commandes des derniers',
days: 'jours',
start_import: 'Démarrer l\'Import',
@@ -172,7 +172,7 @@ const onboardingTranslations = {
},
},
de: {
title: 'Willkommen bei Wizamart',
title: 'Willkommen bei Orion',
subtitle: 'Führen Sie diese Schritte aus, um Ihren Shop einzurichten',
steps: {
merchant_profile: 'Firmenprofil',
@@ -227,7 +227,7 @@ const onboardingTranslations = {
},
step4: {
title: 'Historischer Bestellimport',
description: 'Importieren Sie Ihre bestehenden Bestellungen von Letzshop, um sie in Wizamart zu verwalten.',
description: 'Importieren Sie Ihre bestehenden Bestellungen von Letzshop, um sie in Orion zu verwalten.',
days_back: 'Bestellungen der letzten importieren',
days: 'Tage',
start_import: 'Import Starten',

View File

@@ -2,7 +2,7 @@
{# Letzshop Store Finder Page #}
{% extends "platform/base.html" %}
{% block title %}{{ _("cms.platform.find_shop.title") }} - Wizamart{% endblock %}
{% block title %}{{ _("cms.platform.find_shop.title") }} - Orion{% endblock %}
{% block content %}
<div x-data="storeFinderData()" class="py-16 lg:py-24">

View File

@@ -5,7 +5,7 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Welcome to Wizamart - Setup Your Account</title>
<title>Welcome to Orion - Setup Your Account</title>
<link href="/static/shared/fonts/inter.css" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="{{ url_for('static', path='store/css/tailwind.output.css') }}" />
@@ -22,7 +22,7 @@
<div class="w-10 h-10 rounded-lg bg-purple-600 flex items-center justify-center">
<span class="text-white font-bold text-xl">W</span>
</div>
<span class="text-xl font-semibold text-gray-800 dark:text-white">Wizamart</span>
<span class="text-xl font-semibold text-gray-800 dark:text-white">Orion</span>
</div>
<!-- Logout Button -->
<button @click="handleLogout()"

View File

@@ -11,7 +11,7 @@ Architecture:
- Stores MUST configure email settings to send transactional emails
- Platform emails (billing, subscription) still use platform settings
- Advanced providers (SendGrid, Mailgun, SES) are tier-gated (Business+)
- "Powered by Wizamart" footer is added for Essential/Professional tiers
- "Powered by Orion" footer is added for Essential/Professional tiers
"""
import enum

View File

@@ -291,7 +291,7 @@ def _get_sample_variables(template_code: str) -> dict[str, Any]:
"login_url": "https://example.com/login",
"trial_days": "14",
"tier_name": "Business",
"platform_name": "Wizamart",
"platform_name": "Orion",
},
"order_confirmation": {
"customer_name": "Jane Doe",
@@ -300,13 +300,13 @@ def _get_sample_variables(template_code: str) -> dict[str, Any]:
"order_items_count": "3",
"order_date": "2024-01-15",
"shipping_address": "123 Main St, Luxembourg City, L-1234",
"platform_name": "Wizamart",
"platform_name": "Orion",
},
"password_reset": {
"customer_name": "John Doe",
"reset_link": "https://example.com/reset?token=abc123",
"expiry_hours": "1",
"platform_name": "Wizamart",
"platform_name": "Orion",
},
"team_invite": {
"invitee_name": "Jane",
@@ -315,7 +315,7 @@ def _get_sample_variables(template_code: str) -> dict[str, Any]:
"role": "Admin",
"accept_url": "https://example.com/accept",
"expires_in_days": "7",
"platform_name": "Wizamart",
"platform_name": "Orion",
},
"subscription_welcome": {
"store_name": "Acme Corp",
@@ -324,7 +324,7 @@ def _get_sample_variables(template_code: str) -> dict[str, Any]:
"amount": "€49.99",
"next_billing_date": "2024-02-15",
"dashboard_url": "https://example.com/dashboard",
"platform_name": "Wizamart",
"platform_name": "Orion",
},
"payment_failed": {
"store_name": "Acme Corp",
@@ -332,15 +332,15 @@ def _get_sample_variables(template_code: str) -> dict[str, Any]:
"amount": "€49.99",
"retry_date": "2024-01-18",
"update_payment_url": "https://example.com/billing",
"support_email": "support@wizamart.com",
"platform_name": "Wizamart",
"support_email": "support@orion.lu",
"platform_name": "Orion",
},
"subscription_cancelled": {
"store_name": "Acme Corp",
"tier_name": "Business",
"end_date": "2024-02-15",
"reactivate_url": "https://example.com/billing",
"platform_name": "Wizamart",
"platform_name": "Orion",
},
"trial_ending": {
"store_name": "Acme Corp",
@@ -349,7 +349,7 @@ def _get_sample_variables(template_code: str) -> dict[str, Any]:
"trial_end_date": "2024-01-18",
"upgrade_url": "https://example.com/upgrade",
"features_list": "Unlimited products, API access, Priority support",
"platform_name": "Wizamart",
"platform_name": "Orion",
},
}
return samples.get(template_code, {"platform_name": "Wizamart"})
return samples.get(template_code, {"platform_name": "Orion"})

View File

@@ -184,9 +184,9 @@ def preview_template(
variables = {
**_get_sample_variables(code),
**preview_data.variables,
"platform_name": "Wizamart",
"platform_name": "Orion",
"store_name": store.name if store else "Your Store",
"support_email": store.contact_email if store else "support@wizamart.com",
"support_email": store.contact_email if store else "support@orion.lu",
}
return service.preview_store_template(
@@ -216,9 +216,9 @@ def send_test_email(
variables = {
**_get_sample_variables(code),
**test_data.variables,
"platform_name": "Wizamart",
"platform_name": "Orion",
"store_name": store.name if store else "Your Store",
"support_email": store.contact_email if store else "support@wizamart.com",
"support_email": store.contact_email if store else "support@orion.lu",
}
try:

View File

@@ -51,24 +51,24 @@ from app.modules.messaging.models import (
logger = logging.getLogger(__name__)
# Platform branding constants
PLATFORM_NAME = "Wizamart"
PLATFORM_SUPPORT_EMAIL = "support@wizamart.com"
PLATFORM_NAME = "Orion"
PLATFORM_SUPPORT_EMAIL = "support@orion.lu"
PLATFORM_DEFAULT_LANGUAGE = "en"
SUPPORTED_LANGUAGES = ["en", "fr", "de", "lb"]
# Tiers that get white-label (no "Powered by Wizamart" footer)
# Tiers that get white-label (no "Powered by Orion" footer)
WHITELABEL_TIERS = {"business", "enterprise"}
# Powered by Wizamart footer (added for Essential/Professional tiers)
# Powered by Orion footer (added for Essential/Professional tiers)
POWERED_BY_FOOTER_HTML = """
<div style="margin-top: 30px; padding-top: 20px; border-top: 1px solid #e5e7eb; text-align: center;">
<p style="color: #9ca3af; font-size: 12px; margin: 0;">
Powered by <a href="https://wizamart.com" style="color: #6b46c1; text-decoration: none;">Wizamart</a>
Powered by <a href="https://orion.lu" style="color: #6b46c1; text-decoration: none;">Orion</a>
</p>
</div>
"""
POWERED_BY_FOOTER_TEXT = "\n\n---\nPowered by Wizamart - https://wizamart.com"
POWERED_BY_FOOTER_TEXT = "\n\n---\nPowered by Orion - https://orion.lu"
@dataclass
@@ -1045,7 +1045,7 @@ class EmailService:
def _should_add_powered_by_footer(self, store_id: int | None) -> bool:
"""
Check if "Powered by Wizamart" footer should be added.
Check if "Powered by Orion" footer should be added.
Footer is added for Essential and Professional tiers.
Business and Enterprise tiers get white-label (no footer).
@@ -1066,7 +1066,7 @@ class EmailService:
store_id: int | None,
) -> tuple[str, str | None]:
"""
Inject "Powered by Wizamart" footer if needed based on tier.
Inject "Powered by Orion" footer if needed based on tier.
Returns:
Tuple of (modified_html, modified_text)
@@ -1168,7 +1168,7 @@ class EmailService:
store_logo_url=store.get_logo_url(),
is_whitelabel=True,
)
# Standard: Wizamart branding with store details
# Standard: Orion branding with store details
return BrandingContext(
platform_name=PLATFORM_NAME,
platform_logo_url=None, # Use default platform logo
@@ -1401,11 +1401,11 @@ class EmailService:
For store emails (when store_id is provided and is_platform_email=False):
- Uses store's SMTP/provider settings if configured
- Uses store's from_email, from_name, reply_to
- Adds "Powered by Wizamart" footer for Essential/Professional tiers
- Adds "Powered by Orion" footer for Essential/Professional tiers
For platform emails (is_platform_email=True or no store_id):
- Uses platform's email settings from config
- No "Powered by Wizamart" footer
- No "Powered by Orion" footer
Args:
is_platform_email: If True, always use platform settings (for billing, etc.)
@@ -1437,7 +1437,7 @@ class EmailService:
from_name = from_name or self._platform_config.get("from_name", settings.email_from_name)
reply_to = reply_to or self._platform_config.get("reply_to") or settings.email_reply_to or None
# Inject "Powered by Wizamart" footer for non-whitelabel tiers
# Inject "Powered by Orion" footer for non-whitelabel tiers
if store_id and not is_platform_email:
body_html, body_text = self._inject_powered_by_footer(
body_html, body_text, store_id

View File

@@ -284,12 +284,12 @@ class StoreEmailSettingsService:
def _send_smtp_test(self, settings: StoreEmailSettings, to_email: str) -> None:
"""Send test email via SMTP."""
msg = MIMEMultipart("alternative")
msg["Subject"] = "Wizamart Email Configuration Test"
msg["Subject"] = "Orion Email Configuration Test"
msg["From"] = f"{settings.from_name} <{settings.from_email}>"
msg["To"] = to_email
text_content = (
"This is a test email from Wizamart.\n\n"
"This is a test email from Orion.\n\n"
"Your email settings are configured correctly!\n\n"
f"Provider: SMTP\n"
f"Host: {settings.smtp_host}\n"
@@ -298,7 +298,7 @@ class StoreEmailSettingsService:
<html>
<body style="font-family: Arial, sans-serif; padding: 20px;">
<h2 style="color: #6b46c1;">Email Configuration Test</h2>
<p>This is a test email from <strong>Wizamart</strong>.</p>
<p>This is a test email from <strong>Orion</strong>.</p>
<p style="color: #22c55e; font-weight: bold;">
Your email settings are configured correctly!
</p>
@@ -341,12 +341,12 @@ class StoreEmailSettingsService:
message = Mail(
from_email=(settings.from_email, settings.from_name),
to_emails=to_email,
subject="Wizamart Email Configuration Test",
subject="Orion Email Configuration Test",
html_content="""
<html>
<body style="font-family: Arial, sans-serif; padding: 20px;">
<h2 style="color: #6b46c1;">Email Configuration Test</h2>
<p>This is a test email from <strong>Wizamart</strong>.</p>
<p>This is a test email from <strong>Orion</strong>.</p>
<p style="color: #22c55e; font-weight: bold;">
Your SendGrid settings are configured correctly!
</p>
@@ -374,12 +374,12 @@ class StoreEmailSettingsService:
data={
"from": f"{settings.from_name} <{settings.from_email}>",
"to": to_email,
"subject": "Wizamart Email Configuration Test",
"subject": "Orion Email Configuration Test",
"html": """
<html>
<body style="font-family: Arial, sans-serif; padding: 20px;">
<h2 style="color: #6b46c1;">Email Configuration Test</h2>
<p>This is a test email from <strong>Wizamart</strong>.</p>
<p>This is a test email from <strong>Orion</strong>.</p>
<p style="color: #22c55e; font-weight: bold;">
Your Mailgun settings are configured correctly!
</p>
@@ -417,14 +417,14 @@ class StoreEmailSettingsService:
Source=f"{settings.from_name} <{settings.from_email}>",
Destination={"ToAddresses": [to_email]},
Message={
"Subject": {"Data": "Wizamart Email Configuration Test"},
"Subject": {"Data": "Orion Email Configuration Test"},
"Body": {
"Html": {
"Data": """
<html>
<body style="font-family: Arial, sans-serif; padding: 20px;">
<h2 style="color: #6b46c1;">Email Configuration Test</h2>
<p>This is a test email from <strong>Wizamart</strong>.</p>
<p>This is a test email from <strong>Orion</strong>.</p>
<p style="color: #22c55e; font-weight: bold;">
Your Amazon SES settings are configured correctly!
</p>

View File

@@ -236,7 +236,7 @@ function emailTemplatesPage() {
expires_in_days: '7'
}
};
return samples[templateCode] || { platform_name: 'Wizamart' };
return samples[templateCode] || { platform_name: 'Orion' };
},
// Test Email

View File

@@ -35,8 +35,8 @@ class TestEmailProviders:
subject="Test Subject",
body_html="<h1>Hello</h1>",
body_text="Hello",
from_email="noreply@wizamart.com",
from_name="Wizamart",
from_email="noreply@orion.lu",
from_name="Orion",
)
assert success is True
@@ -53,9 +53,9 @@ class TestEmailProviders:
subject="Test Subject",
body_html="<h1>Hello</h1>",
body_text=None,
from_email="noreply@wizamart.com",
from_name="Wizamart",
reply_to="support@wizamart.com",
from_email="noreply@orion.lu",
from_name="Orion",
reply_to="support@orion.lu",
)
assert success is True

View File

@@ -253,7 +253,7 @@
<span class="text-gray-600 dark:text-gray-400" x-html="$icon('cog', 'w-5 h-5')"></span>
<span class="text-sm font-medium text-gray-700 dark:text-gray-300">Platform Settings</span>
</a>
<a href="https://docs.wizamart.com/architecture/capacity-planning/" target="_blank" class="flex items-center gap-3 p-3 rounded-lg border border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors">
<a href="https://docs.orion.lu/architecture/capacity-planning/" target="_blank" class="flex items-center gap-3 p-3 rounded-lg border border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors">
<span class="text-blue-600 dark:text-blue-400" x-html="$icon('book-open', 'w-5 h-5')"></span>
<span class="text-sm font-medium text-gray-700 dark:text-gray-300">Capacity Planning Docs</span>
</a>

View File

@@ -12,9 +12,9 @@ from typing import Any
from app.exceptions.base import (
BusinessLogicException,
OrionException,
ResourceNotFoundException,
ValidationException,
WizamartException,
)
__all__ = [
@@ -182,7 +182,7 @@ class InvoiceSettingsNotFoundException(ResourceNotFoundException):
)
class InvoiceSettingsAlreadyExistException(WizamartException): # noqa: MOD-025
class InvoiceSettingsAlreadyExistException(OrionException): # noqa: MOD-025
"""Raised when trying to create invoice settings that already exist."""
def __init__(self, store_id: int):
@@ -205,7 +205,7 @@ class InvoiceValidationException(BusinessLogicException):
)
class InvoicePDFGenerationException(WizamartException):
class InvoicePDFGenerationException(OrionException):
"""Raised when PDF generation fails."""
def __init__(self, invoice_id: int, reason: str):

View File

@@ -13,9 +13,9 @@ from app.exceptions.base import (
BusinessLogicException,
ConflictException,
ExternalServiceException,
OrionException,
ResourceNotFoundException,
ValidationException,
WizamartException,
)
# =============================================================================
@@ -116,7 +116,7 @@ class UserAlreadyExistsException(ConflictException):
# =============================================================================
class PlatformNotFoundException(WizamartException):
class PlatformNotFoundException(OrionException):
"""Raised when a platform is not found."""
def __init__(self, code: str):
@@ -128,7 +128,7 @@ class PlatformNotFoundException(WizamartException):
)
class PlatformInactiveException(WizamartException): # noqa: MOD-025
class PlatformInactiveException(OrionException): # noqa: MOD-025
"""Raised when trying to access an inactive platform."""
def __init__(self, code: str):
@@ -140,7 +140,7 @@ class PlatformInactiveException(WizamartException): # noqa: MOD-025
)
class PlatformUpdateException(WizamartException): # noqa: MOD-025
class PlatformUpdateException(OrionException): # noqa: MOD-025
"""Raised when platform update fails."""
def __init__(self, code: str, reason: str):

View File

@@ -34,7 +34,7 @@ class MerchantDomain(Base, TimestampMixin):
Examples:
- myloyaltyprogram.lu -> Merchant "WizaCorp" (all stores inherit)
- Store WIZAMART overrides with StoreDomain -> mysuperloyaltyprogram.lu
- Store ORION overrides with StoreDomain -> mysuperloyaltyprogram.lu
- Store WIZAGADGETS -> inherits myloyaltyprogram.lu
"""

View File

@@ -32,7 +32,7 @@ class Platform(Base, TimestampMixin):
Represents a business offering/product line.
Examples:
- Wizamart OMS (Order Management System)
- Orion OMS (Order Management System)
- Loyalty+ (Loyalty Program Platform)
- Site Builder (Website Builder for Local Businesses)
@@ -62,7 +62,7 @@ class Platform(Base, TimestampMixin):
name = Column(
String(100),
nullable=False,
comment="Display name (e.g., 'Wizamart OMS')",
comment="Display name (e.g., 'Orion OMS')",
)
description = Column(

View File

@@ -40,8 +40,8 @@ class StorePlatform(Base, TimestampMixin):
- Store platform-specific settings
Example:
- Store "WizaMart" is on OMS platform (Professional tier)
- Store "WizaMart" is also on Loyalty platform (Basic tier)
- Store "Orion" is on OMS platform (Professional tier)
- Store "Orion" is also on Loyalty platform (Basic tier)
"""
__tablename__ = "store_platforms"

View File

@@ -249,7 +249,7 @@ def verify_merchant_domain_ownership(
Verify merchant domain ownership via DNS TXT record (Admin only).
**Verification Process:**
1. Queries DNS for TXT record: `_wizamart-verify.{domain}`
1. Queries DNS for TXT record: `_orion-verify.{domain}`
2. Checks if verification token matches
3. If found, marks domain as verified

View File

@@ -247,14 +247,14 @@ def verify_domain_ownership(
Verify domain ownership via DNS TXT record (Admin only).
**Verification Process:**
1. Queries DNS for TXT record: `_wizamart-verify.{domain}`
1. Queries DNS for TXT record: `_orion-verify.{domain}`
2. Checks if verification token matches
3. If found, marks domain as verified
**Requirements:**
- Store must have added TXT record to their DNS
- DNS propagation may take 5-15 minutes
- Record format: `_wizamart-verify.domain.com` TXT `{token}`
- Record format: `_orion-verify.domain.com` TXT `{token}`
**After verification:**
- Domain can be activated

View File

@@ -42,7 +42,7 @@ def get_store_info(
**Returns only active stores** to prevent access to disabled accounts.
Args:
store_code: The store's unique code (e.g., 'WIZAMART')
store_code: The store's unique code (e.g., 'ORION')
db: Database session
Returns:

View File

@@ -293,7 +293,7 @@ class MerchantDomainService:
Verify merchant domain ownership via DNS TXT record.
The merchant must add a TXT record:
Name: _wizamart-verify.{domain}
Name: _orion-verify.{domain}
Value: {verification_token}
"""
try:
@@ -304,7 +304,7 @@ class MerchantDomainService:
try:
txt_records = dns.resolver.resolve(
f"_wizamart-verify.{domain.domain}", "TXT"
f"_orion-verify.{domain.domain}", "TXT"
)
for txt in txt_records:
@@ -331,7 +331,7 @@ class MerchantDomainService:
except dns.resolver.NXDOMAIN:
raise DomainVerificationFailedException(
domain.domain,
f"DNS record _wizamart-verify.{domain.domain} not found",
f"DNS record _orion-verify.{domain.domain} not found",
)
except dns.resolver.NoAnswer:
raise DomainVerificationFailedException(
@@ -368,7 +368,7 @@ class MerchantDomainService:
},
"txt_record": {
"type": "TXT",
"name": "_wizamart-verify",
"name": "_orion-verify",
"value": domain.verification_token,
"ttl": 3600,
},

View File

@@ -284,7 +284,7 @@ class StoreDomainService:
Verify domain ownership via DNS TXT record.
The store must add a TXT record:
Name: _wizamart-verify.{domain}
Name: _orion-verify.{domain}
Value: {verification_token}
Args:
@@ -309,7 +309,7 @@ class StoreDomainService:
# Query DNS TXT records
try:
txt_records = dns.resolver.resolve(
f"_wizamart-verify.{domain.domain}", "TXT"
f"_orion-verify.{domain.domain}", "TXT"
)
# Check if verification token is present
@@ -333,7 +333,7 @@ class StoreDomainService:
except dns.resolver.NXDOMAIN:
raise DomainVerificationFailedException(
domain.domain,
f"DNS record _wizamart-verify.{domain.domain} not found",
f"DNS record _orion-verify.{domain.domain} not found",
)
except dns.resolver.NoAnswer:
raise DomainVerificationFailedException(
@@ -382,7 +382,7 @@ class StoreDomainService:
},
"txt_record": {
"type": "TXT",
"name": "_wizamart-verify",
"name": "_orion-verify",
"value": domain.verification_token,
"ttl": 3600,
},

View File

@@ -5,7 +5,7 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Merchant Login - Wizamart</title>
<title>Merchant Login - Orion</title>
<!-- Fonts: Local fallback + Google Fonts -->
<link href="/static/shared/fonts/inter.css" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet" />

View File

@@ -514,7 +514,7 @@ class TestMerchantDomainServiceInstructions:
assert "instructions" in instructions
assert "txt_record" in instructions
assert instructions["txt_record"]["type"] == "TXT"
assert instructions["txt_record"]["name"] == "_wizamart-verify"
assert instructions["txt_record"]["name"] == "_orion-verify"
assert "common_registrars" in instructions
def test_get_verification_instructions_not_found(self, db):

View File

@@ -411,7 +411,7 @@ class TestStoreDomainServiceInstructions:
assert "instructions" in instructions
assert "txt_record" in instructions
assert instructions["txt_record"]["type"] == "TXT"
assert instructions["txt_record"]["name"] == "_wizamart-verify"
assert instructions["txt_record"]["name"] == "_orion-verify"
assert "common_registrars" in instructions
def test_get_verification_instructions_not_found(self, db):

View File

@@ -4,7 +4,7 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{% block title %}Merchant Portal{% endblock %} - Wizamart</title>
<title>{% block title %}Merchant Portal{% endblock %} - Orion</title>
<!-- Fonts: Local fallback + Google Fonts -->
<link href="/static/shared/fonts/inter.css" rel="stylesheet" />

View File

@@ -7,7 +7,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{# Dynamic page title #}
<title>{% block title %}Wizamart - Order Management for Letzshop Sellers{% endblock %}</title>
<title>{% block title %}Orion - Order Management for Letzshop Sellers{% endblock %}</title>
{# SEO Meta Tags #}
<meta name="description" content="{% block meta_description %}Lightweight OMS for Letzshop stores in Luxembourg. Order management, inventory, and invoicing made simple.{% endblock %}">
@@ -64,7 +64,7 @@
<span class="text-white font-bold text-xl">W</span>
</div>
<span class="text-xl font-bold text-gray-900 dark:text-white">
Wizamart
Orion
</span>
</a>
</div>
@@ -190,7 +190,7 @@
<span class="text-white font-bold text-xl">W</span>
</div>
<span class="text-xl font-bold text-gray-900 dark:text-white">
Wizamart
Orion
</span>
</div>
<p class="text-gray-600 dark:text-gray-400 text-sm">
@@ -225,7 +225,7 @@
</a>
</li>
<li>
<a href="/store/wizamart/login" class="text-gray-600 dark:text-gray-400 hover:text-primary transition-colors">
<a href="/store/orion/login" class="text-gray-600 dark:text-gray-400 hover:text-primary transition-colors">
{{ _("cms.platform.nav.store_login") }}
</a>
</li>

View File

@@ -19,7 +19,7 @@ logger = logging.getLogger(__name__)
# Salt for key derivation - fixed to ensure consistent encryption/decryption
# In production, this should be stored securely and not changed
_ENCRYPTION_SALT = b"wizamart_encryption_salt_v1"
_ENCRYPTION_SALT = b"orion_encryption_salt_v1"
class EncryptionError(Exception):

View File

@@ -36,7 +36,7 @@ from main import app
# Use environment variable or default to local Docker test database
TEST_DATABASE_URL = os.getenv(
"TEST_DATABASE_URL",
"postgresql://test_user:test_password@localhost:5433/wizamart_test"
"postgresql://test_user:test_password@localhost:5433/orion_test"
)

View File

@@ -5,7 +5,7 @@ services:
image: postgres:15
restart: "no"
environment:
POSTGRES_DB: wizamart_test
POSTGRES_DB: orion_test
POSTGRES_USER: test_user
POSTGRES_PASSWORD: test_password
ports:
@@ -13,7 +13,7 @@ services:
tmpfs:
- /var/lib/postgresql/data # Use RAM for faster tests
healthcheck:
test: ["CMD-SHELL", "pg_isready -U test_user -d wizamart_test"]
test: ["CMD-SHELL", "pg_isready -U test_user -d orion_test"]
interval: 5s
timeout: 5s
retries: 5

View File

@@ -4,8 +4,8 @@ services:
image: postgres:15
restart: always
environment:
POSTGRES_DB: wizamart_db
POSTGRES_USER: wizamart_user
POSTGRES_DB: orion_db
POSTGRES_USER: orion_user
POSTGRES_PASSWORD: secure_password
volumes:
- postgres_data:/var/lib/postgresql/data
@@ -13,7 +13,7 @@ services:
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U wizamart_user -d wizamart_db"]
test: ["CMD-SHELL", "pg_isready -U orion_user -d orion_db"]
interval: 30s
timeout: 10s
retries: 3
@@ -37,7 +37,7 @@ services:
ports:
- "8001:8000" # Use 8001 to avoid conflict with local dev server
environment:
DATABASE_URL: postgresql://wizamart_user:secure_password@db:5432/wizamart_db
DATABASE_URL: postgresql://orion_user:secure_password@db:5432/orion_db
JWT_SECRET_KEY: ${JWT_SECRET_KEY:-your-super-secret-key}
REDIS_URL: redis://redis:6379/0
USE_CELERY: "true"
@@ -63,7 +63,7 @@ services:
- full # Only start with: docker compose --profile full up -d
command: celery -A app.core.celery_config worker --loglevel=info -Q default,long_running,scheduled
environment:
DATABASE_URL: postgresql://wizamart_user:secure_password@db:5432/wizamart_db
DATABASE_URL: postgresql://orion_user:secure_password@db:5432/orion_db
REDIS_URL: redis://redis:6379/0
depends_on:
db:

View File

@@ -22,7 +22,7 @@
## System Overview
The Wizamart platform uses a **context-based authentication system** with three isolated security domains:
The Orion platform uses a **context-based authentication system** with three isolated security domains:
- **Admin Portal** - Platform administration and management
- **Store Portal** - Multi-tenant shop management

View File

@@ -73,7 +73,7 @@ from app.exceptions import (
## Error Response Format
All custom exceptions (inheriting from `WizamartException`) return a structured JSON format:
All custom exceptions (inheriting from `OrionException`) return a structured JSON format:
```json
{

View File

@@ -1,6 +1,6 @@
# API Overview
The Wizamart API provides comprehensive endpoints for managing products, shops, users, and marketplace imports. This section provides high-level guidance and concepts for working with our API.
The Orion API provides comprehensive endpoints for managing products, shops, users, and marketplace imports. This section provides high-level guidance and concepts for working with our API.
## Interactive Documentation
@@ -15,7 +15,7 @@ For hands-on API exploration and testing, use our interactive documentation:
### Base URL
```
Production: https://wizamart.com/api/v1
Production: https://orion.lu/api/v1
Development: http://localhost:8000/api/v1
```
@@ -182,4 +182,4 @@ X-RateLimit-Reset: 1640995200
- **[Authentication Guide](authentication.md)** - Learn about API authentication
- **[Error Handling](error-handling.md)** - Understanding API errors
- **[Rate Limiting](rate-limiting.md)** - Rate limiting details
- **[Interactive Docs](http://localhost:8000/docs)** - Try the API live
- **[Interactive Docs](http://localhost:8000/docs)** - Try the API live

View File

@@ -24,17 +24,17 @@ The Storefront API provides customer-facing endpoints for browsing products, man
All Storefront API endpoints automatically receive store context from the `StoreContextMiddleware`:
1. **Browser makes API call** from storefront page (e.g., `/stores/wizamart/storefront/products`)
2. **Browser automatically sends Referer header**: `http://localhost:8000/stores/wizamart/storefront/products`
1. **Browser makes API call** from storefront page (e.g., `/stores/orion/storefront/products`)
2. **Browser automatically sends Referer header**: `http://localhost:8000/stores/orion/storefront/products`
3. **Middleware extracts store** from Referer path/subdomain/domain
4. **Middleware sets** `request.state.store = <Store: wizamart>`
4. **Middleware sets** `request.state.store = <Store: orion>`
5. **API endpoint accesses store**: `store = request.state.store`
6. **No store_id needed in URL!**
### Supported Store Detection Methods
- **Path-based**: `/stores/wizamart/storefront/products` → extracts `wizamart`
- **Subdomain**: `wizamart.platform.com` → extracts `wizamart`
- **Path-based**: `/stores/orion/storefront/products` → extracts `orion`
- **Subdomain**: `orion.platform.com` → extracts `orion`
- **Custom domain**: `customshop.com` → looks up store by domain
---
@@ -92,7 +92,7 @@ Get paginated list of products for current store.
```http
GET /api/v1/storefront/products?skip=0&limit=20&is_featured=true
Referer: http://localhost:8000/stores/wizamart/shop/products
Referer: http://localhost:8000/stores/orion/shop/products
```
**Response (200 OK):**
@@ -142,7 +142,7 @@ Get detailed information for a specific product.
```http
GET /api/v1/storefront/products/1
Referer: http://localhost:8000/stores/wizamart/shop/products
Referer: http://localhost:8000/stores/orion/shop/products
```
**Response (200 OK):**
@@ -200,7 +200,7 @@ Retrieve cart contents for a session.
```http
GET /api/v1/storefront/cart/session-abc-123
Referer: http://localhost:8000/stores/wizamart/shop/cart
Referer: http://localhost:8000/stores/orion/shop/cart
```
**Response (200 OK):**

View File

@@ -99,7 +99,7 @@ Developers must remember:
### Broken Features
**Current Issue:** CMS pages not loading at `/stores/wizamart/about`
**Current Issue:** CMS pages not loading at `/stores/orion/about`
**Root Cause:**
- CMS API exists at `/api/v1/shop/content-pages/{slug}`

View File

@@ -21,9 +21,9 @@ Updated `StoreContextMiddleware` to support shop API routes:
- Shop API now receives store context from the page that made the API call
**How it works:**
1. Browser JavaScript on `/stores/wizamart/shop/products` calls `/api/v1/shop/products`
2. Browser automatically sends `Referer: http://localhost:8000/stores/wizamart/shop/products`
3. Middleware extracts `wizamart` from Referer path
1. Browser JavaScript on `/stores/orion/shop/products` calls `/api/v1/shop/products`
2. Browser automatically sends `Referer: http://localhost:8000/stores/orion/shop/products`
3. Middleware extracts `orion` from Referer path
4. Queries database to get Store object
5. Sets `request.state.store` for the API endpoint

View File

@@ -286,7 +286,7 @@ def get_products(
"sub": "user_id",
"username": "john.doe",
"store_id": 123, Store context
"store_code": "WIZAMART", Store code
"store_code": "ORION", Store code
"store_role": "Owner" Store role
}
```

View File

@@ -6,7 +6,7 @@ This document defines the harmonized architecture for all background tasks in th
## Task Queue Infrastructure
Wizamart uses **Celery with Redis** for production-grade background task processing:
Orion uses **Celery with Redis** for production-grade background task processing:
| Component | Purpose | Port |
|-----------|---------|------|

View File

@@ -1,6 +1,6 @@
# Capacity Planning & Infrastructure Sizing
This document provides comprehensive capacity planning guidelines for the Wizamart platform, including resource requirements, scaling thresholds, and monitoring recommendations.
This document provides comprehensive capacity planning guidelines for the Orion platform, including resource requirements, scaling thresholds, and monitoring recommendations.
> **Related:** [Pricing Strategy](../marketing/pricing.md) for tier definitions and limits

View File

@@ -175,16 +175,16 @@ Customer → DNS → Server → Nginx → FastAPI
FastAPI Middleware:
host = "store1.platform.com"
Step 1: Custom domain? NO (ends with .platform.com)
Step 2: Subdomain? YES
Extract "store1"
Query: SELECT * FROM stores
Query: SELECT * FROM stores
WHERE subdomain = 'store1'
Result: Store 1
request.state.store = Store 1
Route → Render Store 1's shop
```
@@ -195,18 +195,18 @@ Customer → localhost:8000/store/store1/
FastAPI Middleware:
host = "localhost:8000"
path = "/store/store1/"
Step 1: Custom domain? NO (localhost)
Step 2: Subdomain? NO (localhost has no subdomain)
Step 3: Path-based? YES
Extract "store1" from path
Query: SELECT * FROM stores
Query: SELECT * FROM stores
WHERE subdomain = 'store1'
Result: Store 1
request.state.store = Store 1
request.state.clean_path = "/" (strip /store/store1)
Route → Render Store 1's shop
```
@@ -402,7 +402,7 @@ Value: 123.45.67.89
TTL: 3600
Type: TXT
Name: _wizamart-verify
Name: _orion-verify
Value: abc123xyz (verification token from your platform)
TTL: 3600
```

View File

@@ -237,7 +237,7 @@
│ Exception Handler │
│ app/exceptions/handler.py │
│ │
│ @app.exception_handler(WizamartException) │
│ @app.exception_handler(OrionException) │
│ async def custom_exception_handler(...): │
│ return JSONResponse( │
│ status_code=exc.status_code, │
@@ -331,7 +331,7 @@ Step 2: Get Instructions
┌────────────────────────────────────┐
│ System returns instructions: │
│ "Add TXT record: │
│ _wizamart-verify.myshop.com │
│ _orion-verify.myshop.com │
│ Value: abc123..." │
└────────────────────────────────────┘
@@ -343,7 +343,7 @@ Step 3: Store Adds DNS Record
┌────────────────────────────────────┐
│ DNS Provider (GoDaddy/etc) │
│ _wizamart-verify.myshop.com TXT │
│ _orion-verify.myshop.com TXT │
│ "abc123..." │
└────────────────────────────────────┘

View File

@@ -10,7 +10,7 @@ The application serves multiple frontends from a single codebase:
|----------|-------------|--------------|
| **ADMIN** | Platform administration | `/admin/*`, `/api/v1/admin/*`, `admin.oms.lu/*` |
| **STORE** | Store dashboard | `/store/*`, `/api/v1/store/*` |
| **STOREFRONT** | Customer-facing shop | `/storefront/*`, `/stores/*`, `wizamart.oms.lu/*` |
| **STOREFRONT** | Customer-facing shop | `/storefront/*`, `/stores/*`, `orion.oms.lu/*` |
| **PLATFORM** | Marketing pages | `/`, `/pricing`, `/about` |
The `FrontendDetector` class provides centralized, consistent detection of which frontend a request targets.
@@ -70,7 +70,7 @@ The `FrontendDetector` uses the following priority order:
- /store/* or /api/v1/store/* → STORE
- /storefront/*, /shop/*, /stores/* → STOREFRONT
- /api/v1/platform/* → PLATFORM
3. Store subdomain (wizamart.oms.lu) → STOREFRONT
3. Store subdomain (orion.oms.lu) → STOREFRONT
4. Store context set by middleware → STOREFRONT
5. Default → PLATFORM
```
@@ -133,7 +133,7 @@ from app.modules.enums import FrontendType
# Full detection
frontend_type = FrontendDetector.detect(
host="wizamart.oms.lu",
host="orion.oms.lu",
path="/products",
has_store_context=True
)
@@ -160,7 +160,7 @@ if FrontendDetector.is_storefront(host, path, has_store_context=True):
| Store dashboard | localhost | /store/settings | STORE |
| Store API | localhost | /api/v1/store/products | STORE |
| Storefront | localhost | /storefront/products | STOREFRONT |
| Storefront (path-based) | localhost | /stores/wizamart/products | STOREFRONT |
| Storefront (path-based) | localhost | /stores/orion/products | STOREFRONT |
| Marketing | localhost | /pricing | PLATFORM |
### Production Mode (domains)
@@ -168,7 +168,7 @@ if FrontendDetector.is_storefront(host, path, has_store_context=True):
| Request | Host | Path | Frontend |
|---------|------|------|----------|
| Admin subdomain | admin.oms.lu | /dashboard | ADMIN |
| Store subdomain | wizamart.oms.lu | /products | STOREFRONT |
| Store subdomain | orion.oms.lu | /products | STOREFRONT |
| Custom domain | mybakery.lu | /products | STOREFRONT |
| Platform root | oms.lu | /pricing | PLATFORM |

View File

@@ -1,6 +1,6 @@
# Language & Internationalization (i18n) Architecture
This document defines **strict rules** for implementing language support across the Wizamart platform.
This document defines **strict rules** for implementing language support across the Orion platform.
> **IMPORTANT:** These rules are mandatory. Violations will cause runtime errors, inconsistent UX, or security issues.
@@ -49,7 +49,7 @@ language = "lux" # ❌ Use "lb"
Language is resolved in this order (highest to lowest priority):
1. **URL parameter** (`?lang=fr`)
2. **Cookie** (`wizamart_language`)
2. **Cookie** (`orion_language`)
3. **User preference** (database: `preferred_language`)
4. **Store default** (database: `storefront_language` or `dashboard_language`)
5. **Accept-Language header** (browser)
@@ -270,7 +270,7 @@ async def set_language(request: LanguageSetRequest, response: Response):
raise HTTPException(status_code=400, detail="Unsupported language")
response.set_cookie(
key="wizamart_language",
key="orion_language",
value=request.language,
max_age=365 * 24 * 60 * 60, # 1 year
httponly=True,

View File

@@ -2,7 +2,7 @@
## Executive Summary
This document defines the complete architecture for integrating Wizamart with multiple external marketplaces (Letzshop, Amazon, eBay) and digital product suppliers (CodesWholesale). The integration is **bidirectional**, supporting both inbound flows (products, orders) and outbound flows (inventory sync, fulfillment status).
This document defines the complete architecture for integrating Orion with multiple external marketplaces (Letzshop, Amazon, eBay) and digital product suppliers (CodesWholesale). The integration is **bidirectional**, supporting both inbound flows (products, orders) and outbound flows (inventory sync, fulfillment status).
**Key Capabilities:**
@@ -26,7 +26,7 @@ graph TB
AZ[Amazon<br/>API]
EB[eBay<br/>API]
CW[CodesWholesale<br/>Digital Supplier API]
WS[Store Storefront<br/>Wizamart Shop]
WS[Store Storefront<br/>Orion Shop]
end
subgraph "Integration Layer"
@@ -42,7 +42,7 @@ graph TB
end
end
subgraph "Wizamart Core"
subgraph "Orion Core"
MP[Marketplace Products]
P[Store Products]
O[Unified Orders]
@@ -263,7 +263,7 @@ graph TB
```python
class OrderChannel(str, Enum):
"""Order source channel."""
STOREFRONT = "storefront" # Store's own Wizamart shop
STOREFRONT = "storefront" # Store's own Orion shop
LETZSHOP = "letzshop"
AMAZON = "amazon"
EBAY = "ebay"
@@ -1325,7 +1325,7 @@ def map_codeswholesale_product(cw_product: dict) -> dict:
### Order Status Mapping
| Wizamart Status | Letzshop | Amazon | eBay |
| Orion Status | Letzshop | Amazon | eBay |
|-----------------|----------|--------|------|
| PENDING | PENDING | Pending | - |
| CONFIRMED | PAID | Unshipped | Paid |

View File

@@ -1,6 +1,6 @@
# Menu Management Architecture
The Wizamart platform provides a **module-driven menu system** where each module defines its own menu items. The `MenuDiscoveryService` aggregates menus from all enabled modules, applying visibility configuration and permission filtering.
The Orion platform provides a **module-driven menu system** where each module defines its own menu items. The `MenuDiscoveryService` aggregates menus from all enabled modules, applying visibility configuration and permission filtering.
## Overview

View File

@@ -2,7 +2,7 @@
## Overview
The Wizamart platform implements a hierarchical multi-tenant architecture where **Merchants** are the primary business entities and **Stores** are storefronts/brands that operate under merchants.
The Orion platform implements a hierarchical multi-tenant architecture where **Merchants** are the primary business entities and **Stores** are storefronts/brands that operate under merchants.
```
Merchant (Business Entity)

View File

@@ -103,11 +103,11 @@ INFO Response: 200 for GET /admin/dashboard (0.143s)
**Example**:
```
Request: https://wizamart.platform.com/shop/products
Request: https://orion.platform.com/shop/products
Middleware detects: store_code = "wizamart"
Middleware detects: store_code = "orion"
Queries database: SELECT * FROM stores WHERE code = 'wizamart'
Queries database: SELECT * FROM stores WHERE code = 'orion'
Injects: request.state.store = <Store object>
request.state.store_id = 1
@@ -141,7 +141,7 @@ Injects: request.state.store = <Store object>
- /store/* or /api/v1/store/* STORE
- /storefront/*, /shop/*, /stores/* STOREFRONT
- /api/v1/platform/* PLATFORM
3. Store subdomain (wizamart.oms.lu) STOREFRONT
3. Store subdomain (orion.oms.lu) STOREFRONT
4. Store context set by middleware STOREFRONT
5. Default PLATFORM
```
@@ -165,8 +165,8 @@ Injects: request.state.store = <Store object>
{
"primary_color": "#3B82F6",
"secondary_color": "#10B981",
"logo_url": "/static/stores/wizamart/logo.png",
"favicon_url": "/static/stores/wizamart/favicon.ico",
"logo_url": "/static/stores/orion/logo.png",
"favicon_url": "/static/stores/orion/favicon.ico",
"custom_css": "/* store-specific styles */"
}
```
@@ -376,7 +376,7 @@ async def get_products(request: Request):
### Example: Shop Product Page Request
**URL**: `https://wizamart.myplatform.com/shop/products`
**URL**: `https://orion.myplatform.com/shop/products`
**Middleware Processing**:
@@ -386,9 +386,9 @@ async def get_products(request: Request):
↓ Logs: "Request: GET /shop/products from 192.168.1.100"
2. StoreContextMiddleware
↓ Detects subdomain: "wizamart"
↓ Queries DB: store = get_store_by_code("wizamart")
↓ Sets: request.state.store = <Store: Wizamart>
↓ Detects subdomain: "orion"
↓ Queries DB: store = get_store_by_code("orion")
↓ Sets: request.state.store = <Store: Orion>
↓ Sets: request.state.store_id = 1
↓ Sets: request.state.clean_path = "/shop/products"
@@ -496,13 +496,13 @@ from middleware.store_context import StoreContextManager
def test_store_detection_subdomain():
# Mock request
request = create_mock_request(host="wizamart.platform.com")
request = create_mock_request(host="orion.platform.com")
# Test detection
manager = StoreContextManager()
store = manager.detect_store_from_subdomain(request)
assert store.code == "wizamart"
assert store.code == "orion"
```
### Integration Testing
@@ -513,11 +513,11 @@ Test the full middleware stack:
def test_shop_request_flow(client):
response = client.get(
"/shop/products",
headers={"Host": "wizamart.platform.com"}
headers={"Host": "orion.platform.com"}
)
assert response.status_code == 200
assert "Wizamart" in response.text
assert "Orion" in response.text
```
**See**: [Testing Guide](../testing/testing-guide.md)

View File

@@ -1,6 +1,6 @@
# Module System Architecture
The Wizamart platform uses a **plug-and-play modular architecture** where modules are fully self-contained and automatically discovered. Simply create a module directory with the required structure, and the framework handles registration, routing, and resource loading automatically.
The Orion platform uses a **plug-and-play modular architecture** where modules are fully self-contained and automatically discovered. Simply create a module directory with the required structure, and the framework handles registration, routing, and resource loading automatically.
## Key Features
@@ -1029,11 +1029,11 @@ __all__ = ["process_import", "export_products"]
### Exceptions
Module-specific exceptions inherit from `WizamartException`.
Module-specific exceptions inherit from `OrionException`.
| Location | Base Class | Usage |
|----------|------------|-------|
| `exceptions.py` | `WizamartException` | Domain errors |
| `exceptions.py` | `OrionException` | Domain errors |
**Structure:**
```
@@ -1044,9 +1044,9 @@ app/modules/{module}/
**Example:**
```python
# app/modules/orders/exceptions.py
from app.exceptions import WizamartException
from app.exceptions import OrionException
class OrderException(WizamartException):
class OrderException(OrionException):
"""Base exception for orders module."""
pass

View File

@@ -2,7 +2,7 @@
## Overview
The Multi-Platform CMS enables Wizamart to serve multiple business offerings (OMS, Loyalty, Site Builder) from a single codebase, each with its own marketing site and store ecosystem.
The Multi-Platform CMS enables Orion to serve multiple business offerings (OMS, Loyalty, Site Builder) from a single codebase, each with its own marketing site and store ecosystem.
## Three-Tier Content Hierarchy
@@ -71,7 +71,7 @@ CREATE TABLE platforms (
code VARCHAR(50) UNIQUE NOT NULL, -- 'oms', 'loyalty', 'sitebuilder'
name VARCHAR(100) NOT NULL, -- 'Order Management System'
description TEXT,
domain VARCHAR(255), -- 'oms.wizamart.lu'
domain VARCHAR(255), -- 'oms.orion.lu'
path_prefix VARCHAR(50), -- '/oms'
logo VARCHAR(255),
logo_dark VARCHAR(255),
@@ -122,7 +122,7 @@ The system uses different URL patterns for development vs production:
- Platform sites: `localhost:9999/platforms/{code}/` → specific platform
**Production (custom domains):**
- Main marketing site: `wizamart.lu/``main` platform
- Main marketing site: `orion.lu/``main` platform
- Platform sites: `oms.lu/`, `loyalty.lu/` → specific platform
### Request Processing
@@ -284,6 +284,6 @@ Request: GET /about
| Platform | Code | Dev URL | Prod URL |
|----------|------|---------|----------|
| Main Marketing | `main` | `localhost:9999/` | `wizamart.lu/` |
| Main Marketing | `main` | `localhost:9999/` | `orion.lu/` |
| OMS | `oms` | `localhost:9999/platforms/oms/` | `oms.lu/` |
| Loyalty | `loyalty` | `localhost:9999/platforms/loyalty/` | `loyalty.lu/` |

View File

@@ -4,7 +4,7 @@ Complete guide to the multi-tenant architecture supporting custom domains, subdo
## Overview
The Wizamart platform supports **three deployment modes** for multi-tenancy, allowing each store to have their own isolated shop while sharing the same application instance and database.
The Orion platform supports **three deployment modes** for multi-tenancy, allowing each store to have their own isolated shop while sharing the same application instance and database.
**Key Concept**: One application, multiple isolated store shops, each accessible via different URLs.
@@ -148,15 +148,15 @@ For path-based routing, clean paths are extracted:
**Path-Based Shop Routes** (Development):
```
Original: /stores/WIZAMART/shop/products
Extracted: store_code = "WIZAMART"
Original: /stores/ORION/shop/products
Extracted: store_code = "ORION"
Clean: /shop/products
```
**Store Dashboard Routes** (All environments):
```
Original: /store/WIZAMART/dashboard
Extracted: store_code = "WIZAMART"
Original: /store/ORION/dashboard
Extracted: store_code = "ORION"
Clean: /dashboard
```
@@ -197,13 +197,13 @@ CREATE TABLE store_domains (
```sql
-- Stores
INSERT INTO stores (code, name) VALUES
('wizamart', 'Wizamart Shop'),
('orion', 'Orion Shop'),
('techstore', 'Tech Store'),
('fashionhub', 'Fashion Hub');
-- Custom Domains
INSERT INTO store_domains (store_id, domain) VALUES
(1, 'wizamart.com'),
(1, 'orion.lu'),
(2, 'mytechstore.net');
```
@@ -421,16 +421,16 @@ Host: customdomain.com
**Request**:
```http
GET /shop/products HTTP/1.1
Host: wizamart.myplatform.com
Host: orion.myplatform.com
```
**Processing**:
```
1. StoreContextMiddleware
- Checks: host != "myplatform.com"
- Extracts: subdomain = "wizamart"
- Queries: stores WHERE code = "wizamart"
- Sets: request.state.store = <Store "wizamart">
- Extracts: subdomain = "orion"
- Queries: stores WHERE code = "orion"
- Sets: request.state.store = <Store "orion">
2-4. Same as Example 1
```
@@ -439,7 +439,7 @@ Host: wizamart.myplatform.com
**Request**:
```http
GET /stores/WIZAMART/shop/products HTTP/1.1
GET /stores/ORION/shop/products HTTP/1.1
Host: myplatform.com
```
@@ -447,15 +447,15 @@ Host: myplatform.com
```
1. StoreContextMiddleware
- Checks: path starts with "/store/"
- Extracts: code = "WIZAMART"
- Queries: stores WHERE code = "WIZAMART"
- Extracts: code = "ORION"
- Queries: stores WHERE code = "ORION"
- Sets: request.state.store = <Store>
- Sets: request.state.clean_path = "/shop/products"
2. FastAPI Router
- Routes registered with prefix: /stores/{store_code}/shop
- Matches: /stores/WIZAMART/shop/products
- store_code path parameter = "WIZAMART"
- Matches: /stores/ORION/shop/products
- store_code path parameter = "ORION"
3-4. Same as previous examples (Context, Theme middleware)
```
@@ -491,9 +491,9 @@ def test_shop_page_multi_tenant(client):
# Test subdomain routing
response = client.get(
"/shop/products",
headers={"Host": "wizamart.platform.com"}
headers={"Host": "orion.platform.com"}
)
assert "Wizamart" in response.text
assert "Orion" in response.text
# Test different store
response = client.get(

View File

@@ -1,6 +1,6 @@
# Observability Framework
The Wizamart platform includes a comprehensive observability framework for monitoring application health, collecting metrics, and tracking errors. This is part of the Framework Layer - infrastructure that modules depend on.
The Orion platform includes a comprehensive observability framework for monitoring application health, collecting metrics, and tracking errors. This is part of the Framework Layer - infrastructure that modules depend on.
## Overview

View File

@@ -1,10 +1,10 @@
# System Architecture
High-level overview of the Wizamart multi-tenant e-commerce platform architecture.
High-level overview of the Orion multi-tenant e-commerce platform architecture.
## Overview
Wizamart is a **multi-tenant e-commerce platform** that supports three distinct interfaces:
Orion is a **multi-tenant e-commerce platform** that supports three distinct interfaces:
- **Admin** - Platform administration and store management
- **Store** - Store dashboard for managing shops
- **Shop** - Customer-facing storefronts

View File

@@ -1,6 +1,6 @@
# Request Flow
Complete journey of a request through the Wizamart platform, from client to response.
Complete journey of a request through the Orion platform, from client to response.
## Overview
@@ -42,8 +42,8 @@ graph TB
```http
# Shop page request (subdomain mode)
GET https://wizamart.platform.com/shop/products
Host: wizamart.platform.com
GET https://orion.platform.com/shop/products
Host: orion.platform.com
# API request
GET https://platform.com/api/v1/products?store_id=1
@@ -86,13 +86,13 @@ logger.info(f"Request: GET /shop/products from 192.168.1.100")
```python
# Input
host = "wizamart.platform.com"
host = "orion.platform.com"
path = "/shop/products"
# Detection logic
if host != settings.platform_domain:
# Subdomain detected
store_code = host.split('.')[0] # "wizamart"
store_code = host.split('.')[0] # "orion"
# Query database
store = db.query(Store).filter(
@@ -107,7 +107,7 @@ if host != settings.platform_domain:
**Request State After**:
```python
request.state.store = <Store: Wizamart>
request.state.store = <Store: Orion>
request.state.store_id = 1
request.state.clean_path = "/shop/products"
```
@@ -127,10 +127,10 @@ request.state.clean_path = "/shop/products"
app.include_router(shop_pages.router, prefix="/shop")
app.include_router(shop_pages.router, prefix="/stores/{store_code}/shop")
# Request: /stores/WIZAMART/shop/products
# Request: /stores/ORION/shop/products
# Matches: Second router (/stores/{store_code}/shop)
# Route: @router.get("/products")
# store_code available as path parameter = "WIZAMART"
# store_code available as path parameter = "ORION"
```
**Note:** Previous implementations used `PathRewriteMiddleware` to rewrite paths. This has been replaced with FastAPI's native routing via double router mounting.
@@ -194,7 +194,7 @@ if hasattr(request.state, 'store_id'):
request.state.theme = {
"primary_color": "#3B82F6",
"secondary_color": "#10B981",
"logo_url": "/static/stores/wizamart/logo.png",
"logo_url": "/static/stores/orion/logo.png",
"custom_css": "..."
}
```
@@ -289,7 +289,7 @@ async def shop_products_page(
<!DOCTYPE html>
<html>
<head>
<title>Wizamart - Products</title>
<title>Orion - Products</title>
<style>
:root {
--primary-color: #3B82F6;
@@ -298,7 +298,7 @@ async def shop_products_page(
</style>
</head>
<body>
<h1>Wizamart Shop</h1>
<h1>Orion Shop</h1>
<div class="products">
<div class="product-card">
<h2>Product 1</h2>
@@ -412,7 +412,7 @@ sequenceDiagram
Logging->>Store: Pass request
Store->>DB: Query store by subdomain
DB-->>Store: Store object
Note over Store: Set store, store_id, clean_path
Note over Store: Set store, store_id, clean_path
Store->>Path: Pass request
Note over Path: Path already clean
Path->>Context: Pass request
@@ -443,14 +443,14 @@ Initial State: {}
store_id: 1,
clean_path: "/shop/products"
}
After FrontendTypeMiddleware:
{
store: <Store: Orion>,
store_id: 1,
clean_path: "/shop/products",
frontend_type: FrontendType.STOREFRONT
}
}
After ThemeContextMiddleware:
{
@@ -458,14 +458,14 @@ After FrontendTypeMiddleware:
store_id: 1,
clean_path: "/shop/products",
frontend_type: FrontendType.STOREFRONT,
theme: {
theme: {
primary_color: "#3B82F6",
secondary_color: "#10B981",
logo_url: "/static/stores/orion/logo.png",
custom_css: "..."
}
}
```
```
## Performance Metrics

View File

@@ -1,8 +1,8 @@
# Wizamart Multi-Tenant URL Routing Guide
# Orion Multi-Tenant URL Routing Guide
## Quick Answer
**How do customers access a store's storefront in Wizamart?**
**How do customers access a store's storefront in Orion?**
There are three ways depending on the deployment mode:
@@ -13,8 +13,8 @@ There are three ways depending on the deployment mode:
https://STORE_SUBDOMAIN.platform.com/storefront/products
Example:
https://acme.wizamart.com/storefront/products
https://techpro.wizamart.com/storefront/categories/electronics
https://acme.orion.lu/storefront/products
https://techpro.orion.lu/storefront/categories/electronics
```
### 2. **CUSTOM DOMAIN MODE** (Production - Premium)
@@ -39,7 +39,7 @@ http://localhost:8000/platforms/loyalty/stores/techpro/storefront/checkout
## Multi-Platform URL Routing
Wizamart supports multiple platforms (OMS, Loyalty, Site Builder), each with its own marketing site and store ecosystem.
Orion supports multiple platforms (OMS, Loyalty, Site Builder), each with its own marketing site and store ecosystem.
### Platform URL Structure
@@ -61,8 +61,8 @@ Wizamart supports multiple platforms (OMS, Loyalty, Site Builder), each with its
| URL | What it serves |
|-----|----------------|
| `wizamart.lu/` | Main marketing site homepage |
| `wizamart.lu/about` | Main marketing site about page |
| `orion.lu/` | Main marketing site homepage |
| `orion.lu/about` | Main marketing site about page |
| `oms.lu/` | OMS platform homepage |
| `oms.lu/pricing` | OMS platform pricing page |
| `oms.lu/admin/` | Admin panel for OMS platform |
@@ -138,7 +138,7 @@ Request arrives
| Platform | Code | Dev URL | Prod Domain |
|----------|------|---------|-------------|
| Main Marketing | `main` | `localhost:8000/` | `wizamart.lu` |
| Main Marketing | `main` | `localhost:8000/` | `orion.lu` |
| OMS | `oms` | `localhost:8000/platforms/oms/` | `oms.lu` |
| Loyalty | `loyalty` | `localhost:8000/platforms/loyalty/` | `loyalty.lu` |
| Site Builder | `site-builder` | `localhost:8000/platforms/site-builder/` | `sitebuilder.lu` |
@@ -155,12 +155,12 @@ Request arrives
**Example:**
- Store subdomain: `acme`
- Platform domain: `wizamart.com`
- Customer Storefront URL: `https://acme.wizamart.com/storefront/products`
- Product Detail: `https://acme.wizamart.com/storefront/products/123`
- Platform domain: `orion.lu`
- Customer Storefront URL: `https://acme.orion.lu/storefront/products`
- Product Detail: `https://acme.orion.lu/storefront/products/123`
**How It Works:**
1. Customer visits `https://acme.wizamart.com/storefront/products`
1. Customer visits `https://acme.orion.lu/storefront/products`
2. `store_context_middleware` detects subdomain `"acme"`
3. Queries: `SELECT * FROM stores WHERE subdomain = 'acme'`
4. Finds Store with ID=1 (ACME Store)
@@ -171,7 +171,7 @@ Request arrives
9. Renders template with ACME's colors, logo, and products
**Advantages:**
- Single SSL certificate for all stores (*.wizamart.com)
- Single SSL certificate for all stores (*.orion.lu)
- Easy to manage DNS (just add subdomains)
- Customers don't need to bring their own domain
@@ -199,7 +199,7 @@ id | store_id | domain | is_active | is_verified
**How It Works:**
1. Customer visits `https://store.acme-corp.com/storefront/products`
2. `store_context_middleware` detects custom domain (not *.wizamart.com, not localhost)
2. `store_context_middleware` detects custom domain (not *.orion.lu, not localhost)
3. Normalizes domain to `"store.acme-corp.com"`
4. Queries: `SELECT * FROM store_domains WHERE domain = 'store.acme-corp.com'`
5. Finds `StoreDomain` with `store_id = 1`
@@ -249,17 +249,17 @@ id | store_id | domain | is_active | is_verified
### Subdomain/Custom Domain (PRODUCTION)
```
https://acme.wizamart.com/storefront/ → Homepage
https://acme.wizamart.com/storefront/products → Product Catalog
https://acme.wizamart.com/storefront/products/123 → Product Detail
https://acme.wizamart.com/storefront/categories/electronics → Category Page
https://acme.wizamart.com/storefront/cart → Shopping Cart
https://acme.wizamart.com/storefront/checkout → Checkout
https://acme.wizamart.com/storefront/search?q=laptop → Search Results
https://acme.wizamart.com/storefront/account/login → Customer Login
https://acme.wizamart.com/storefront/account/dashboard → Account Dashboard (Auth Required)
https://acme.wizamart.com/storefront/account/orders → Order History (Auth Required)
https://acme.wizamart.com/storefront/account/profile → Profile (Auth Required)
https://acme.orion.lu/storefront/ → Homepage
https://acme.orion.lu/storefront/products → Product Catalog
https://acme.orion.lu/storefront/products/123 → Product Detail
https://acme.orion.lu/storefront/categories/electronics → Category Page
https://acme.orion.lu/storefront/cart → Shopping Cart
https://acme.orion.lu/storefront/checkout → Checkout
https://acme.orion.lu/storefront/search?q=laptop → Search Results
https://acme.orion.lu/storefront/account/login → Customer Login
https://acme.orion.lu/storefront/account/dashboard → Account Dashboard (Auth Required)
https://acme.orion.lu/storefront/account/orders → Order History (Auth Required)
https://acme.orion.lu/storefront/account/profile → Profile (Auth Required)
```
### Path-Based (DEVELOPMENT)
@@ -304,7 +304,7 @@ POST /api/v1/storefront/stores/1/products/{id}/reviews → Add product review
### Example: No Cross-Store Leakage
```python
# Customer on acme.wizamart.com tries to access TechPro's products
# Customer on acme.orion.lu tries to access TechPro's products
# They make API call to /api/v1/storefront/stores/2/products
# Backend checks:
@@ -317,14 +317,14 @@ if store.id != requested_store_id: # if 1 != 2
## Request Lifecycle: Complete Flow
### Scenario: Customer visits `https://acme.wizamart.com/storefront/products`
### Scenario: Customer visits `https://acme.orion.lu/storefront/products`
```
┌─────────────────────────────────────────────────────────────────┐
│ 1. REQUEST ARRIVES │
└─────────────────────────────────────────────────────────────────┘
method: GET
host: acme.wizamart.com
host: acme.orion.lu
path: /storefront/products
┌─────────────────────────────────────────────────────────────────┐
@@ -332,7 +332,7 @@ if store.id != requested_store_id: # if 1 != 2
└─────────────────────────────────────────────────────────────────┘
A) store_context_middleware
├─ Detects host: "acme.wizamart.com"
├─ Detects host: "acme.orion.lu"
├─ Extracts subdomain: "acme"
├─ Queries: SELECT * FROM stores WHERE subdomain = 'acme'
└─ Sets: request.state.store = Store(ACME Store)
@@ -391,7 +391,7 @@ if store.id != requested_store_id: # if 1 != 2
Each store's storefront is fully branded with their custom theme:
```python
# Theme loaded for https://acme.wizamart.com
# Theme loaded for https://acme.orion.lu
request.state.theme = {
"theme_name": "modern",
"colors": {
@@ -438,7 +438,7 @@ In Jinja2 template:
- Each store looks completely separate and branded
### 2. Store Perspective
- Stores can use a subdomain (free/standard): `acme.wizamart.com`
- Stores can use a subdomain (free/standard): `acme.orion.lu`
- Or their own custom domain (premium): `store.acme-corp.com`
- Both routes go to the exact same backend code
@@ -471,7 +471,7 @@ app.include_router(storefront_pages.router, prefix="/stores/{store_code}/storefr
**How This Works:**
1. **For Subdomain/Custom Domain Mode:**
- URL: `https://acme.wizamart.com/storefront/products`
- URL: `https://acme.orion.lu/storefront/products`
- Matches: First router with `/storefront` prefix
- Route: `@router.get("/products")` → Full path: `/storefront/products`
@@ -528,4 +528,4 @@ Set-Cookie: customer_token=eyJ...; Path=/storefront; HttpOnly; SameSite=Lax
---
Generated: January 30, 2026
Wizamart Version: Current Development
Orion Version: Current Development

View File

@@ -5,7 +5,7 @@
**Last Updated:** 2026-01-28
**Target:** v1.0.0
This is the unified migration plan for transforming Wizamart into a fully modular architecture.
This is the unified migration plan for transforming Orion into a fully modular architecture.
---

View File

@@ -42,15 +42,15 @@ Transform the single-platform OMS into a multi-platform system supporting indepe
```bash
# 1. Backup database first
pg_dump wizamart > wizamart_backup_$(date +%Y%m%d).sql
pg_dump orion > orion_backup_$(date +%Y%m%d).sql
# 2. Run migration
alembic upgrade head
# 3. Verify migration
psql -d wizamart -c "SELECT * FROM platforms;"
psql -d wizamart -c "SELECT COUNT(*) FROM store_platforms;"
psql -d wizamart -c "SELECT platform_id, is_platform_page, COUNT(*) FROM content_pages GROUP BY 1, 2;"
psql -d orion -c "SELECT * FROM platforms;"
psql -d orion -c "SELECT COUNT(*) FROM store_platforms;"
psql -d orion -c "SELECT platform_id, is_platform_page, COUNT(*) FROM content_pages GROUP BY 1, 2;"
```
### 2.2 Register PlatformContextMiddleware in main.py
@@ -78,7 +78,7 @@ Changes completed:
File: `app/routes/platform/homepage.py`
Current state (BROKEN):
- Uses hardcoded `homepage-wizamart.html` template
- Uses hardcoded `homepage-orion.html` template
- Admin CMS changes are saved but ignored by route
Fix required:
@@ -101,7 +101,7 @@ if page:
)
else:
# Fallback to hardcoded (temporary)
return templates.TemplateResponse("platform/homepage-wizamart.html", {...})
return templates.TemplateResponse("platform/homepage-orion.html", {...})
```
### 2.5 Update Content Page Routes
@@ -255,7 +255,7 @@ Inserts Loyalty platform with:
| Platform | Code | Dev URL | Prod URL |
|----------|------|---------|----------|
| Main Marketing | `main` | `localhost:9999/` | `wizamart.lu/` |
| Main Marketing | `main` | `localhost:9999/` | `orion.lu/` |
| OMS | `oms` | `localhost:9999/platforms/oms/` | `oms.lu/` |
| Loyalty | `loyalty` | `localhost:9999/platforms/loyalty/` | `loyalty.lu/` |
@@ -324,7 +324,7 @@ Included in `docs/architecture/multi-platform-cms.md`:
### Three-Tier Content Resolution
```
Customer visits: oms.lu/stores/wizamart/about
Customer visits: oms.lu/stores/orion/about
┌─────────────────────────────────────────────────────────────┐
@@ -366,7 +366,7 @@ All phases are complete. Use these commands to verify:
```bash
# 1. Check platforms in database
psql -d wizamart -c "SELECT code, name, domain FROM platforms;"
psql -d orion -c "SELECT code, name, domain FROM platforms;"
# Expected: main, oms, loyalty
# 2. Test main marketing site

View File

@@ -23,7 +23,7 @@ The platform is evolving from a single OMS product to a **multi-platform busines
| Issue | Description |
|-------|-------------|
| **Conflated page types** | Platform pages and store defaults share `store_id = NULL`, making them indistinguishable |
| **Hardcoded homepage** | Platform homepage uses `homepage-wizamart.html` directly, ignoring CMS |
| **Hardcoded homepage** | Platform homepage uses `homepage-orion.html` directly, ignoring CMS |
| **Non-functional admin UI** | `/admin/platform-homepage` saves to CMS but route doesn't use it |
| **Single platform assumption** | Architecture assumes one platform, can't scale to multiple offerings |
| **No platform isolation** | No way to have separate About/FAQ/Pricing pages per platform |
@@ -48,7 +48,7 @@ ContentPage (store_id = NULL)
│ PLATFORM LEVEL │
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │
│ │ Platform A │ │ Platform B │ │ Platform C │ │
│ │ (Wizamart OMS) │ │ (Loyalty+) │ │ (Site Builder) │ │
│ │ (Orion OMS) │ │ (Loyalty+) │ │ (Site Builder) │ │
│ │ │ │ │ │ │ │
│ │ • Homepage │ │ • Homepage │ │ • Homepage │ │
│ │ • About │ │ • About │ │ • About │ │
@@ -76,7 +76,7 @@ ContentPage (store_id = NULL)
┌─────────────────────────────────────────────────────────────────────────────┐
│ STORE LEVEL (isolated) │
│ ┌─────────────────────────┐ ┌─────────────────────────┐ │
│ │ Store 1 (WizaMart) │ │ Store 2 (TechStore) │ │
│ │ Store 1 (Orion) │ │ Store 2 (TechStore) │ │
│ │ Platform A, Tier: Pro │ │ Platform A, Tier: Basic │ │
│ │ │ │ │ │
│ │ Override Pages: │ │ Override Pages: │ │
@@ -114,14 +114,14 @@ When a customer visits `store1.example.com/about`:
class Platform(Base):
"""
Represents a business offering/product line.
Examples: Wizamart OMS, Loyalty+, Site Builder
Examples: Orion OMS, Loyalty+, Site Builder
"""
__tablename__ = "platforms"
id = Column(Integer, primary_key=True)
code = Column(String(50), unique=True, nullable=False) # "oms", "loyalty", "sites"
name = Column(String(100), nullable=False) # "Wizamart OMS"
domain = Column(String(255), nullable=True) # "wizamart.lu"
name = Column(String(100), nullable=False) # "Orion OMS"
domain = Column(String(255), nullable=True) # "orion.lu"
# Branding
logo = Column(String(500), nullable=True)
@@ -219,8 +219,8 @@ class Store(Base):
|-----------|:-----------:|:---------:|:----------------:|---------|
| Platform Marketing Page | ✓ | NULL | TRUE | Platform A's homepage, pricing |
| Store Default Page | ✓ | NULL | FALSE | Generic "About Our Store" template |
| Store Override Page | ✓ | ✓ | FALSE | WizaMart's custom About page |
| Store Custom Page | ✓ | ✓ | FALSE | WizaMart's "Store Locations" page |
| Store Override Page | ✓ | ✓ | FALSE | Orion's custom About page |
| Store Custom Page | ✓ | ✓ | FALSE | Orion's "Store Locations" page |
---
@@ -237,7 +237,7 @@ class Store(Base):
3. Admin fills in:
- Code: "loyalty"
- Name: "Loyalty+"
- Domain: "loyalty.wizamart.lu"
- Domain: "loyalty.orion.lu"
- Logo, theme colors
4. Admin saves platform
5. System creates platform record
@@ -250,7 +250,7 @@ class Store(Base):
- Contact (is_platform_page=True)
8. Each page can use different templates (modern, minimal, etc.)
9. Admin publishes pages
10. Platform marketing site is now live at loyalty.wizamart.lu
10. Platform marketing site is now live at loyalty.orion.lu
```
### Journey 2: Platform Admin Creates Store Defaults
@@ -308,7 +308,7 @@ class Store(Base):
### Journey 4: Store Overrides a Default Page
**Actor:** Store (WizaMart)
**Actor:** Store (Orion)
**Goal:** Customize the About page with store-specific content
```
@@ -318,21 +318,21 @@ class Store(Base):
4. Clicks "Override" button
5. System creates a copy with store_id set
6. Store edits content:
- Title: "About WizaMart"
- Content: "WizaMart was founded in 2020 in Luxembourg..."
- Title: "About Orion"
- Content: "Orion was founded in 2020 in Luxembourg..."
- Adds store images
7. Store saves and publishes
8. Page list now shows:
┌─────────────────────────────────────────────────────────┐
│ About Us │ Custom Override │ Published │ Edit │
└─────────────────────────────────────────────────────────┘
9. Customer visits wizamart.example.com/about
10. Sees WizaMart's custom About page
9. Customer visits orion.example.com/about
10. Sees Orion's custom About page
```
### Journey 5: Store Creates a Custom Page
**Actor:** Store (WizaMart)
**Actor:** Store (Orion)
**Goal:** Add a new page that doesn't exist in defaults
```
@@ -346,12 +346,12 @@ class Store(Base):
- Show in footer: Yes
5. Store saves and publishes
6. Page appears in storefront footer navigation
7. Accessible at wizamart.example.com/store-locations
7. Accessible at orion.example.com/store-locations
```
### Journey 6: Store Reverts Override to Default
**Actor:** Store (WizaMart)
**Actor:** Store (Orion)
**Goal:** Remove customization and use platform default again
```
@@ -372,19 +372,19 @@ class Store(Base):
**Goal:** Read store policies before purchasing
```
1. Customer visits wizamart.example.com
1. Customer visits orion.example.com
2. Browses products, adds to cart
3. Wants to check return policy
4. Clicks "Returns" in footer
5. System resolves page:
- Check: WizaMart override? NO
- Check: Orion override? NO
- Check: Platform A default? YES
- Serve: Platform A default "Return Policy" page
6. Customer reads return policy
7. Customer clicks "About Us" in footer
8. System resolves page:
- Check: WizaMart override? YES
- Serve: WizaMart's custom "About WizaMart" page
- Check: Orion override? YES
- Serve: Orion's custom "About Orion" page
9. Customer sees store-specific About page
```
@@ -539,7 +539,7 @@ GET /api/v1/platform/{code}/pages/navigation # Get platform nav
```sql
-- 1. Create default platform
INSERT INTO platforms (code, name, domain, is_active)
VALUES ('oms', 'Wizamart OMS', 'localhost:8000', true);
VALUES ('oms', 'Orion OMS', 'localhost:8000', true);
-- 2. Update all existing content_pages
UPDATE content_pages
@@ -567,7 +567,7 @@ SET platform_id = (SELECT id FROM platforms WHERE code = 'oms');
## Open Questions
1. **Domain routing**: How to route requests to correct platform?
- Option A: Separate domains (oms.wizamart.lu, loyalty.wizamart.lu)
- Option A: Separate domains (oms.orion.lu, loyalty.orion.lu)
- Option B: Path-based (/oms/*, /loyalty/*)
- Option C: Subdomain detection

View File

@@ -95,7 +95,7 @@ from models.database.base import TimestampMixin
class StoreDomain(Base, TimestampMixin):
"""
Custom domain mapping for stores.
Allows stores to use their own domains (e.g., myshop.com)
instead of subdomains (store1.platform.com).
"""
@@ -103,11 +103,11 @@ class StoreDomain(Base, TimestampMixin):
# Primary Key
id = Column(Integer, primary_key=True, index=True)
# Foreign Keys
store_id = Column(
Integer,
ForeignKey("stores.id", ondelete="CASCADE"),
Integer,
ForeignKey("stores.id", ondelete="CASCADE"),
nullable=False,
index=True
)
@@ -116,12 +116,12 @@ class StoreDomain(Base, TimestampMixin):
domain = Column(String(255), nullable=False, unique=True, index=True)
is_primary = Column(Boolean, default=False, nullable=False)
is_active = Column(Boolean, default=True, nullable=False)
# Verification
is_verified = Column(Boolean, default=False, nullable=False)
verification_token = Column(String(100), unique=True, nullable=True)
verified_at = Column(DateTime(timezone=True), nullable=True)
# SSL Status
ssl_status = Column(String(50), default="pending")
ssl_verified_at = Column(DateTime(timezone=True), nullable=True)
@@ -171,9 +171,9 @@ class StoreDomain(Base, TimestampMixin):
class Store(Base, TimestampMixin):
__tablename__ = "stores"
# ... existing fields ...
# Add relationship
domains = relationship(
"StoreDomain",
@@ -181,7 +181,7 @@ class Store(Base, TimestampMixin):
cascade="all, delete-orphan",
order_by="StoreDomain.is_primary.desc()"
)
# Helper method
@property
def primary_domain(self):
@@ -270,10 +270,10 @@ class StoreDomainCreate(BaseModel):
"""Validate and normalize domain."""
# Remove protocol if present
domain = v.replace("https://", "").replace("http://", "")
# Remove trailing slash
domain = domain.rstrip("/")
# Convert to lowercase
domain = domain.lower().strip()
@@ -331,7 +331,7 @@ class StoreDomainResponse(BaseModel):
class StoreDomainListResponse(BaseModel):
"""Schema for paginated store domain list."""
domains: List[StoreDomainResponse]
total: int
@@ -535,7 +535,7 @@ class DNSVerificationException(ExternalServiceException):
| 409 | `ConflictException` | Resource conflicts |
| 422 | `ValidationException` | Input validation errors |
| 429 | `RateLimitException` | Rate limiting |
| 500 | `WizamartException` | Generic errors |
| 500 | `OrionException` | Generic errors |
| 502 | `ExternalServiceException` | Third-party failures |
### Step 2: Update Exception Exports
@@ -561,7 +561,7 @@ from .store_domain import (
__all__ = [
# ... existing exports ...
# Store Domain
"StoreDomainNotFoundException",
"StoreDomainAlreadyExistsException",
@@ -1133,7 +1133,7 @@ def delete_store_domain(
Delete a custom domain (Admin only).
**Warning:** This is permanent and cannot be undone.
**Raises:**
- 404: Domain not found
"""
@@ -1230,7 +1230,7 @@ async def admin_store_domains_page(
Manage custom domains for {{ store_code }}
</p>
</div>
<button
<button
onclick="openAddDomainModal()"
class="px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700">
Add Domain
@@ -1248,32 +1248,32 @@ async def admin_store_domains_page(
<div class="flex items-center justify-center min-h-screen px-4">
<div class="bg-white dark:bg-gray-800 rounded-lg max-w-md w-full p-6">
<h2 class="text-2xl font-semibold mb-4">Add Custom Domain</h2>
<form id="addDomainForm">
<div class="mb-4">
<label class="block text-sm font-medium mb-2">Domain</label>
<input
type="text"
<input
type="text"
name="domain"
placeholder="myshop.com"
class="w-full px-3 py-2 border rounded-lg"
required>
</div>
<div class="mb-4">
<label class="flex items-center">
<input type="checkbox" name="is_primary" class="mr-2">
<span class="text-sm">Set as primary domain</span>
</label>
</div>
<div class="flex gap-2">
<button
<button
type="submit"
class="flex-1 bg-purple-600 text-white py-2 rounded-lg hover:bg-purple-700">
Add Domain
</button>
<button
<button
type="button"
onclick="closeAddDomainModal()"
class="flex-1 bg-gray-300 text-gray-700 py-2 rounded-lg hover:bg-gray-400">
@@ -1305,7 +1305,7 @@ async function loadStoreId() {
'Authorization': `Bearer ${localStorage.getItem('token')}`
}
});
if (response.ok) {
const store = await response.json();
storeId = store.id;
@@ -1317,14 +1317,14 @@ async function loadStoreId() {
async function loadDomains() {
if (!storeId) return;
try {
const response = await fetch(`/api/v1/admin/stores/${storeId}/domains`, {
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`
}
});
if (response.ok) {
const data = await response.json();
renderDomains(data.domains);
@@ -1336,12 +1336,12 @@ async function loadDomains() {
function renderDomains(domains) {
const container = document.getElementById('domainsList');
if (domains.length === 0) {
container.innerHTML = '<p class="text-gray-500">No domains added yet.</p>';
return;
}
container.innerHTML = domains.map(domain => `
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
<div class="flex items-center justify-between">
@@ -1358,13 +1358,13 @@ function renderDomains(domains) {
</div>
<div class="flex gap-2">
${!domain.is_verified ? `
<button
<button
onclick="verifyDomain(${domain.id})"
class="px-3 py-1 bg-green-600 text-white text-sm rounded hover:bg-green-700">
Verify
</button>
` : ''}
<button
<button
onclick="deleteDomain(${domain.id})"
class="px-3 py-1 bg-red-600 text-white text-sm rounded hover:bg-red-700">
Delete
@@ -1386,13 +1386,13 @@ function closeAddDomainModal() {
document.getElementById('addDomainForm').addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const data = {
domain: formData.get('domain'),
is_primary: formData.get('is_primary') === 'on'
};
try {
const response = await fetch(`/api/v1/admin/stores/${storeId}/domains`, {
method: 'POST',
@@ -1402,7 +1402,7 @@ document.getElementById('addDomainForm').addEventListener('submit', async (e) =>
},
body: JSON.stringify(data)
});
if (response.ok) {
closeAddDomainModal();
await loadDomains();
@@ -1425,7 +1425,7 @@ async function verifyDomain(domainId) {
'Authorization': `Bearer ${localStorage.getItem('token')}`
}
});
if (response.ok) {
await loadDomains();
alert('Domain verified successfully!');
@@ -1441,7 +1441,7 @@ async function verifyDomain(domainId) {
async function deleteDomain(domainId) {
if (!confirm('Are you sure you want to delete this domain?')) return;
try {
const response = await fetch(`/api/v1/admin/stores/domains/${domainId}`, {
method: 'DELETE',
@@ -1449,7 +1449,7 @@ async function deleteDomain(domainId) {
'Authorization': `Bearer ${localStorage.getItem('token')}`
}
});
if (response.ok) {
await loadDomains();
alert('Domain deleted successfully!');
@@ -1497,9 +1497,9 @@ def test_add_domain_success(db_session, test_store, service):
domain="test.com",
is_primary=True
)
domain = service.add_domain(db_session, test_store.id, domain_data)
assert domain.domain == "test.com"
assert domain.is_primary is True
assert domain.is_verified is False
@@ -1509,7 +1509,7 @@ def test_add_domain_success(db_session, test_store, service):
def test_add_domain_already_exists(db_session, test_store, existing_domain, service):
"""Test adding duplicate domain raises exception."""
domain_data = StoreDomainCreate(domain=existing_domain.domain)
with pytest.raises(StoreDomainAlreadyExistsException):
service.add_domain(db_session, test_store.id, domain_data)
@@ -1520,7 +1520,7 @@ def test_add_domain_max_limit_reached(db_session, test_store, service):
for i in range(service.max_domains_per_store):
domain_data = StoreDomainCreate(domain=f"test{i}.com")
service.add_domain(db_session, test_store.id, domain_data)
# Try adding one more
domain_data = StoreDomainCreate(domain="overflow.com")
with pytest.raises(MaxDomainsReachedException):
@@ -1549,7 +1549,7 @@ def test_add_domain_endpoint(client, admin_headers, test_store):
json={"domain": "newshop.com", "is_primary": False},
headers=admin_headers
)
assert response.status_code == 201
data = response.json()
assert data["domain"] == "newshop.com"
@@ -1563,7 +1563,7 @@ def test_add_domain_invalid_format(client, admin_headers, test_store):
json={"domain": "admin.example.com"}, # Reserved subdomain
headers=admin_headers
)
assert response.status_code == 422
assert "reserved" in response.json()["message"].lower()
@@ -1574,7 +1574,7 @@ def test_list_domains_endpoint(client, admin_headers, test_store, test_domain):
f"/api/v1/admin/stores/{test_store.id}/domains",
headers=admin_headers
)
assert response.status_code == 200
data = response.json()
assert data["total"] >= 1
@@ -1587,7 +1587,7 @@ def test_delete_domain_endpoint(client, admin_headers, test_domain):
f"/api/v1/admin/stores/domains/{test_domain.id}",
headers=admin_headers
)
assert response.status_code == 200
assert "deleted successfully" in response.json()["message"]
@@ -1598,7 +1598,7 @@ def test_verify_domain_not_found(client, admin_headers):
"/api/v1/admin/stores/domains/99999/verify",
headers=admin_headers
)
assert response.status_code == 404
assert response.json()["error_code"] == "STORE_DOMAIN_NOT_FOUND"
```
@@ -1794,6 +1794,6 @@ For questions or issues:
---
**Last Updated:** 2025-01-15
**Version:** 1.0
**Last Updated:** 2025-01-15
**Version:** 1.0
**Maintainer:** Development Team

View File

@@ -4,7 +4,7 @@ Guide for developing backend features, services, and API endpoints.
## Overview
The Wizamart backend is built with FastAPI and follows a service-oriented architecture pattern. This guide covers backend development practices, patterns, and technical references.
The Orion backend is built with FastAPI and follows a service-oriented architecture pattern. This guide covers backend development practices, patterns, and technical references.
## Backend Structure

View File

@@ -55,7 +55,7 @@ def get_product(
│ "sub": "user_id", │
│ "username": "john.doe", │
│ "store_id": 123, ← Store context in token │
│ "store_code": "WIZAMART", ← Store code in token │
│ "store_code": "ORION", ← Store code in token │
│ "store_role": "Owner" ← Store role in token │
│ } │
└─────────────────────────────────────────────────────────────────┘
@@ -436,14 +436,14 @@ def test_store_in_token():
token_data = auth_manager.create_access_token(
user=user,
store_id=123,
store_code="WIZAMART",
store_code="ORION",
store_role="Owner",
)
# Verify token contains store data
payload = jwt.decode(token_data["access_token"], secret_key)
assert payload["store_id"] == 123
assert payload["store_code"] == "WIZAMART"
assert payload["store_code"] == "ORION"
assert payload["store_role"] == "Owner"
def test_api_endpoint_uses_token_store():
@@ -517,7 +517,7 @@ The architecture enforces a strict layered pattern for where exceptions should b
┌────────────────────────────────────────────────────────────────────────────┐
│ GLOBAL EXCEPTION HANDLER - app/exceptions/handler.py │
│ │
│ ✅ Catches all WizamartException subclasses │
│ ✅ Catches all OrionException subclasses │
│ ✅ Converts to appropriate HTTP responses │
│ ✅ Provides consistent error formatting │
└────────────────────────────────────────────────────────────────────────────┘

View File

@@ -607,7 +607,7 @@ HTTP 403 Forbidden
"message": "You don't have permission to perform this action",
"details": {
"required_permission": "products.delete",
"store_code": "wizamart"
"store_code": "orion"
}
}
```
@@ -622,7 +622,7 @@ HTTP 403 Forbidden
"message": "This operation requires store owner privileges",
"details": {
"operation": "team management",
"store_code": "wizamart"
"store_code": "orion"
}
}
```

Some files were not shown because too many files have changed in this diff Show More