middleware fix for path-based vendor url
This commit is contained in:
@@ -0,0 +1,380 @@
|
||||
# Enhanced Database Seeder - Implementation Guide
|
||||
|
||||
## Overview
|
||||
|
||||
I've created a comprehensive database seeder for your Wizamart platform that significantly expands coverage beyond your original implementation. The new seeder creates realistic test data across all your database models.
|
||||
|
||||
## What's New
|
||||
|
||||
### Original Seeder Coverage
|
||||
- ✓ Admin user
|
||||
- ✓ 2 Vendors (TESTVENDOR, WIZAMART)
|
||||
|
||||
### Enhanced Seeder Coverage
|
||||
- ✓ Admin user + multiple test users (vendors, customers)
|
||||
- ✓ 3 Vendors with different themes and configurations
|
||||
- ✓ Custom domains for vendors
|
||||
- ✓ Vendor themes with different presets (modern, classic, vibrant)
|
||||
- ✓ 5 Sample marketplace products
|
||||
- ✓ Vendor-product relationships
|
||||
- ✓ Multiple customers with addresses
|
||||
- ✓ Sample orders with order items
|
||||
- ✓ Import jobs
|
||||
- ✓ Admin settings (platform configuration)
|
||||
- ✓ Platform alerts (system monitoring)
|
||||
|
||||
## Files Created
|
||||
|
||||
1. **seed_database.py** - Enhanced seeder script (placed in /mnt/user-data/outputs/)
|
||||
2. **makefile_additions.txt** - Commands to add to your Makefile
|
||||
|
||||
## Makefile Integration
|
||||
|
||||
Add these commands to your Makefile in the DATABASE section (after backup-db):
|
||||
|
||||
```makefile
|
||||
seed:
|
||||
@echo Seeding database with comprehensive test data...
|
||||
$(PYTHON) scripts/seed_database.py
|
||||
@echo Seeding completed successfully
|
||||
|
||||
seed-minimal:
|
||||
@echo Seeding database with minimal data (admin + 1 vendor)...
|
||||
$(PYTHON) scripts/seed_database.py --minimal
|
||||
@echo Minimal seeding completed
|
||||
|
||||
seed-reset:
|
||||
@echo WARNING: This will DELETE ALL existing data!
|
||||
$(PYTHON) scripts/seed_database.py --reset
|
||||
@echo Database reset and seeded
|
||||
|
||||
# Complete database setup (migrate + seed)
|
||||
db-setup: migrate-up seed
|
||||
@echo Database setup complete!
|
||||
@echo Run 'make dev' to start development server
|
||||
|
||||
db-reset: migrate-down migrate-up seed-reset
|
||||
@echo Database completely reset!
|
||||
```
|
||||
|
||||
Add these help entries to the help section (under === DATABASE ===):
|
||||
|
||||
```makefile
|
||||
@echo seed - Seed database with comprehensive test data
|
||||
@echo seed-minimal - Seed minimal data (admin + 1 vendor)
|
||||
@echo seed-reset - Reset and seed database (destructive!)
|
||||
@echo db-setup - Complete database setup (migrate + seed)
|
||||
@echo db-reset - Complete database reset
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### 1. Install the Seeder
|
||||
Copy the seed_database.py file to your scripts directory:
|
||||
```bash
|
||||
cp seed_database.py scripts/seed_database.py
|
||||
```
|
||||
|
||||
### 2. Run Seeding Commands
|
||||
|
||||
#### Option A: Full Comprehensive Seeding (Recommended for Development)
|
||||
```bash
|
||||
make seed
|
||||
# or
|
||||
python scripts/seed_database.py
|
||||
```
|
||||
|
||||
This creates:
|
||||
- 1 admin user
|
||||
- 3 test users (2 vendors, 1 customer)
|
||||
- 3 vendors (WIZAMART, FASHIONHUB, BOOKSTORE)
|
||||
- 5 marketplace products
|
||||
- 10 vendor-product links
|
||||
- 4 customers
|
||||
- 8 addresses
|
||||
- 2 orders
|
||||
- 2 import jobs
|
||||
- 4 admin settings
|
||||
- 2 platform alerts
|
||||
|
||||
#### Option B: Minimal Seeding (Quick Setup)
|
||||
```bash
|
||||
make seed-minimal
|
||||
# or
|
||||
python scripts/seed_database.py --minimal
|
||||
```
|
||||
|
||||
This creates only:
|
||||
- 1 admin user
|
||||
- 1 vendor (WIZAMART)
|
||||
|
||||
#### Option C: Reset and Seed (Fresh Start)
|
||||
```bash
|
||||
make seed-reset
|
||||
# or
|
||||
python scripts/seed_database.py --reset
|
||||
```
|
||||
|
||||
⚠️ **WARNING**: This DELETES ALL existing data!
|
||||
|
||||
#### Option D: Complete Database Setup (Migration + Seed)
|
||||
```bash
|
||||
make db-setup
|
||||
```
|
||||
|
||||
This runs:
|
||||
1. `make migrate-up` (apply migrations)
|
||||
2. `make seed` (seed data)
|
||||
|
||||
## Test Data Details
|
||||
|
||||
### Users Created
|
||||
|
||||
| Username | Email | Password | Role |
|
||||
|----------|-------|----------|------|
|
||||
| admin | admin@wizamart.com | admin123 | admin |
|
||||
| vendor1 | vendor1@example.com | password123 | vendor |
|
||||
| vendor2 | vendor2@example.com | password123 | vendor |
|
||||
| customer1 | customer1@example.com | password123 | customer |
|
||||
|
||||
### Vendors Created
|
||||
|
||||
| Code | Name | Subdomain | Theme | Custom Domain |
|
||||
|------|------|-----------|-------|---------------|
|
||||
| WIZAMART | WizaMart | wizamart | modern | wizamart.shop |
|
||||
| FASHIONHUB | Fashion Hub | fashionhub | vibrant | fashionhub.store |
|
||||
| BOOKSTORE | The Book Store | bookstore | classic | (none) |
|
||||
|
||||
### Products Created
|
||||
|
||||
1. **Wireless Bluetooth Headphones** - $149.99
|
||||
- Brand: AudioTech
|
||||
- GTIN: 1234567890123
|
||||
|
||||
2. **Smart Watch Pro** - $299.99
|
||||
- Brand: TechWear
|
||||
- GTIN: 1234567890124
|
||||
|
||||
3. **Portable Bluetooth Speaker** - $79.99
|
||||
- Brand: AudioTech
|
||||
- GTIN: 1234567890125
|
||||
|
||||
4. **Wireless Charging Pad** - $39.99
|
||||
- Brand: ChargeMax
|
||||
- GTIN: 1234567890126
|
||||
|
||||
5. **4K Webcam** - $129.99
|
||||
- Brand: VisionTech
|
||||
- GTIN: 1234567890127
|
||||
|
||||
### Theme Presets Available
|
||||
|
||||
The seeder includes 5 built-in theme presets:
|
||||
|
||||
1. **default** - Indigo and purple, Inter font
|
||||
2. **modern** - Blue and cyan, Poppins font, transparent header
|
||||
3. **classic** - Deep blue and purple, Georgia serif font
|
||||
4. **vibrant** - Pink and orange, colorful background
|
||||
5. **minimal** - Black and white, Helvetica font
|
||||
|
||||
## Features
|
||||
|
||||
### 1. Idempotent Operations
|
||||
- Safe to run multiple times without --reset flag
|
||||
- Checks for existing records before creating
|
||||
- Reports what was created vs. what already existed
|
||||
|
||||
### 2. Comprehensive Coverage
|
||||
- Tests all major database models
|
||||
- Creates realistic relationships between entities
|
||||
- Includes edge cases (pending orders, completed orders, etc.)
|
||||
|
||||
### 3. Better Output
|
||||
- Beautiful formatted output with boxes and sections
|
||||
- Color-coded success/info/error messages
|
||||
- Detailed summary with database statistics
|
||||
- Quick reference for login credentials and URLs
|
||||
|
||||
### 4. Command-Line Arguments
|
||||
```bash
|
||||
python scripts/seed_database.py [--reset] [--minimal]
|
||||
|
||||
--reset : Drop all data before seeding (destructive!)
|
||||
--minimal : Create only essential data (admin + 1 vendor)
|
||||
```
|
||||
|
||||
### 5. Proper Error Handling
|
||||
- Verifies database is ready before seeding
|
||||
- Rolls back on errors
|
||||
- Provides helpful error messages
|
||||
- Suggests solutions for common issues
|
||||
|
||||
## Testing Access
|
||||
|
||||
### Admin Panel
|
||||
- URL: `http://localhost:8000/admin/login`
|
||||
- Username: `admin`
|
||||
- Password: `admin123`
|
||||
|
||||
### Vendor Shops
|
||||
- WIZAMART: `http://localhost:8000/shop/WIZAMART`
|
||||
- FASHIONHUB: `http://localhost:8000/shop/FASHIONHUB`
|
||||
- BOOKSTORE: `http://localhost:8000/shop/BOOKSTORE`
|
||||
|
||||
### Theme Editors
|
||||
- WIZAMART Theme: `http://localhost:8000/admin/vendors/WIZAMART/theme`
|
||||
- FASHIONHUB Theme: `http://localhost:8000/admin/vendors/FASHIONHUB/theme`
|
||||
- BOOKSTORE Theme: `http://localhost:8000/admin/vendors/BOOKSTORE/theme`
|
||||
|
||||
## Example Output
|
||||
|
||||
```
|
||||
╔════════════════════════════════════════════════════════════════════╗
|
||||
║ WIZAMART DATABASE SEEDER ║
|
||||
╚════════════════════════════════════════════════════════════════════╝
|
||||
|
||||
STEP 1: Verifying database...
|
||||
✓ Database tables verified
|
||||
|
||||
════════════════════════════════════════════════════════════════════════
|
||||
COMPREHENSIVE SEEDING
|
||||
════════════════════════════════════════════════════════════════════════
|
||||
|
||||
STEP 1: Creating users...
|
||||
✓ Admin user created (ID: 1)
|
||||
✓ User 'vendor1' created (ID: 2)
|
||||
✓ User 'vendor2' created (ID: 3)
|
||||
✓ User 'customer1' created (ID: 4)
|
||||
|
||||
STEP 2: Creating vendors...
|
||||
✓ WIZAMART created (ID: 1)
|
||||
✓ Theme 'modern' applied
|
||||
✓ Custom domain 'wizamart.shop' added
|
||||
✓ FASHIONHUB created (ID: 2)
|
||||
✓ Theme 'vibrant' applied
|
||||
✓ Custom domain 'fashionhub.store' added
|
||||
✓ BOOKSTORE created (ID: 3)
|
||||
✓ Theme 'classic' applied
|
||||
|
||||
...
|
||||
|
||||
════════════════════════════════════════════════════════════════════════
|
||||
SEEDING SUMMARY
|
||||
════════════════════════════════════════════════════════════════════════
|
||||
|
||||
📊 Database Statistics:
|
||||
Users: 4
|
||||
Vendors: 3
|
||||
Vendor Themes: 3
|
||||
Custom Domains: 2
|
||||
Marketplace Products: 5
|
||||
Vendor Products: 10
|
||||
Customers: 4
|
||||
Addresses: 8
|
||||
Orders: 2
|
||||
Order Items: 4
|
||||
Import Jobs: 2
|
||||
Admin Settings: 4
|
||||
Platform Alerts: 2
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
You can customize the seeder by editing the configuration constants at the top of the file:
|
||||
|
||||
```python
|
||||
# Admin credentials
|
||||
DEFAULT_ADMIN_EMAIL = "admin@wizamart.com"
|
||||
DEFAULT_ADMIN_USERNAME = "admin"
|
||||
DEFAULT_ADMIN_PASSWORD = "admin123"
|
||||
|
||||
# Vendor configurations
|
||||
VENDOR_CONFIGS = [...]
|
||||
|
||||
# Test users
|
||||
TEST_USERS = [...]
|
||||
|
||||
# Sample products
|
||||
SAMPLE_PRODUCTS = [...]
|
||||
|
||||
# Theme presets
|
||||
THEME_PRESETS = {...}
|
||||
```
|
||||
|
||||
## Migration Path from Old Seeder
|
||||
|
||||
If you're currently using the old seed_database.py:
|
||||
|
||||
1. Backup your current seeder:
|
||||
```bash
|
||||
mv scripts/seed_database.py scripts/seed_database.old.py
|
||||
```
|
||||
|
||||
2. Install new seeder:
|
||||
```bash
|
||||
cp seed_database.py scripts/seed_database.py
|
||||
```
|
||||
|
||||
3. Run with minimal flag first to test:
|
||||
```bash
|
||||
make seed-minimal
|
||||
```
|
||||
|
||||
4. If satisfied, run full seeding:
|
||||
```bash
|
||||
make seed
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Error: "Database not ready"
|
||||
**Solution**: Run migrations first
|
||||
```bash
|
||||
make migrate-up
|
||||
```
|
||||
|
||||
### Error: "Table doesn't exist"
|
||||
**Solution**: Make sure all migrations are applied
|
||||
```bash
|
||||
alembic upgrade head
|
||||
```
|
||||
|
||||
### Error: "Foreign key constraint failed"
|
||||
**Solution**: Reset database and try again
|
||||
```bash
|
||||
make seed-reset
|
||||
```
|
||||
|
||||
### Error: Import errors
|
||||
**Solution**: Make sure you're in the project root and virtual environment is activated
|
||||
```bash
|
||||
source venv/bin/activate # Linux/Mac
|
||||
venv\Scripts\activate # Windows
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Development**: Use `make seed` for full test data
|
||||
2. **Quick Testing**: Use `make seed-minimal` for fast setup
|
||||
3. **Fresh Start**: Use `make seed-reset` when testing migrations
|
||||
4. **Production**: Never run the seeder in production!
|
||||
|
||||
## Security Notes
|
||||
|
||||
⚠️ **IMPORTANT**:
|
||||
- Default passwords are simple for development convenience
|
||||
- Change ALL default passwords in production environments
|
||||
- The admin password is intentionally weak for local development
|
||||
- Never commit sensitive credentials to version control
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
Consider adding:
|
||||
- More diverse product categories
|
||||
- Different vendor statuses (pending, suspended)
|
||||
- Customer order history variations
|
||||
- Failed import jobs
|
||||
- More complex inventory scenarios
|
||||
- Payment transactions
|
||||
- Vendor subscriptions
|
||||
- Product reviews and ratings
|
||||
198
docs/__REVAMPING/DATABASE_SEEDER/MAKEFILE_DATABASE_SEEDER.md
Normal file
198
docs/__REVAMPING/DATABASE_SEEDER/MAKEFILE_DATABASE_SEEDER.md
Normal file
@@ -0,0 +1,198 @@
|
||||
# Makefile Changes - Quick Reference
|
||||
|
||||
## Location 1: DATABASE Section (After line 78 - after backup-db)
|
||||
|
||||
Add these targets:
|
||||
|
||||
```makefile
|
||||
seed:
|
||||
@echo Seeding database with comprehensive test data...
|
||||
$(PYTHON) scripts/seed_database.py
|
||||
@echo Seeding completed successfully
|
||||
|
||||
seed-minimal:
|
||||
@echo Seeding database with minimal data (admin + 1 vendor)...
|
||||
$(PYTHON) scripts/seed_database.py --minimal
|
||||
@echo Minimal seeding completed
|
||||
|
||||
seed-reset:
|
||||
@echo WARNING: This will DELETE ALL existing data!
|
||||
$(PYTHON) scripts/seed_database.py --reset
|
||||
@echo Database reset and seeded
|
||||
|
||||
# Complete database setup (migrate + seed)
|
||||
db-setup: migrate-up seed
|
||||
@echo Database setup complete!
|
||||
@echo Run 'make dev' to start development server
|
||||
|
||||
db-reset: migrate-down migrate-up seed-reset
|
||||
@echo Database completely reset!
|
||||
```
|
||||
|
||||
## Location 2: HELP Section (Around line 270 - in DATABASE help)
|
||||
|
||||
Replace:
|
||||
```makefile
|
||||
@echo === DATABASE ===
|
||||
@echo migrate-create message="msg" - Create new migration
|
||||
@echo migrate-up - Apply pending migrations
|
||||
@echo migrate-down - Rollback last migration
|
||||
@echo migrate-status - Show migration status
|
||||
@echo backup-db - Backup database
|
||||
@echo.
|
||||
```
|
||||
|
||||
With:
|
||||
```makefile
|
||||
@echo === DATABASE ===
|
||||
@echo migrate-create message="msg" - Create new migration
|
||||
@echo migrate-up - Apply pending migrations
|
||||
@echo migrate-down - Rollback last migration
|
||||
@echo migrate-status - Show migration status
|
||||
@echo backup-db - Backup database
|
||||
@echo seed - Seed database with comprehensive test data
|
||||
@echo seed-minimal - Seed minimal data (admin + 1 vendor)
|
||||
@echo seed-reset - Reset and seed database (destructive!)
|
||||
@echo db-setup - Complete database setup (migrate + seed)
|
||||
@echo db-reset - Complete database reset
|
||||
@echo.
|
||||
```
|
||||
|
||||
## Location 3: DAILY WORKFLOW Help Section (Around line 305)
|
||||
|
||||
Replace:
|
||||
```makefile
|
||||
@echo === DAILY WORKFLOW ===
|
||||
@echo make dev # Start development
|
||||
@echo make migrate-create message="feature" # Create migration
|
||||
@echo make migrate-up # Apply migration
|
||||
@echo make test # Run tests
|
||||
```
|
||||
|
||||
With:
|
||||
```makefile
|
||||
@echo === DAILY WORKFLOW ===
|
||||
@echo make db-setup # Setup database with data
|
||||
@echo make dev # Start development
|
||||
@echo make migrate-create message="feature" # Create migration
|
||||
@echo make migrate-up # Apply migration
|
||||
@echo make seed # Seed test data
|
||||
@echo make test # Run tests
|
||||
```
|
||||
|
||||
## Location 4: help-db target (Around line 310)
|
||||
|
||||
Replace:
|
||||
```makefile
|
||||
help-db:
|
||||
@echo === DATABASE COMMANDS ===
|
||||
@echo migrate-create message="description" - Create auto-generated migration
|
||||
@echo migrate-create-manual message="desc" - Create empty migration template
|
||||
@echo migrate-up - Apply all pending migrations
|
||||
@echo migrate-down - Rollback last migration
|
||||
@echo migrate-status - Show current status and history
|
||||
@echo backup-db - Create database backup
|
||||
@echo.
|
||||
@echo TYPICAL WORKFLOW:
|
||||
@echo 1. Edit your SQLAlchemy models
|
||||
@echo 2. make migrate-create message="add_new_feature"
|
||||
@echo 3. Review the generated migration file
|
||||
@echo 4. make migrate-up
|
||||
```
|
||||
|
||||
With:
|
||||
```makefile
|
||||
help-db:
|
||||
@echo === DATABASE COMMANDS ===
|
||||
@echo migrate-create message="description" - Create auto-generated migration
|
||||
@echo migrate-create-manual message="desc" - Create empty migration template
|
||||
@echo migrate-up - Apply all pending migrations
|
||||
@echo migrate-down - Rollback last migration
|
||||
@echo migrate-status - Show current status and history
|
||||
@echo backup-db - Create database backup
|
||||
@echo seed - Seed comprehensive test data
|
||||
@echo seed-minimal - Seed minimal data (admin + 1 vendor)
|
||||
@echo seed-reset - Reset and seed (destructive!)
|
||||
@echo db-setup - Complete database setup
|
||||
@echo db-reset - Complete database reset
|
||||
@echo.
|
||||
@echo TYPICAL WORKFLOW:
|
||||
@echo 1. make db-setup # First time setup
|
||||
@echo 2. Edit your SQLAlchemy models
|
||||
@echo 3. make migrate-create message="add_new_feature"
|
||||
@echo 4. Review the generated migration file
|
||||
@echo 5. make migrate-up
|
||||
@echo 6. make seed # Refresh test data if needed
|
||||
```
|
||||
|
||||
## Update .PHONY targets (Around line 2)
|
||||
|
||||
Replace:
|
||||
```makefile
|
||||
.PHONY: install install-dev install-docs install-all dev test test-coverage lint format check docker-build docker-up docker-down clean help
|
||||
```
|
||||
|
||||
With:
|
||||
```makefile
|
||||
.PHONY: install install-dev install-docs install-all dev test test-coverage lint format check docker-build docker-up docker-down clean help seed seed-minimal seed-reset db-setup db-reset
|
||||
```
|
||||
|
||||
## Quick Copy-Paste Version
|
||||
|
||||
If you want to add all commands at once, here's the complete DATABASE section replacement:
|
||||
|
||||
```makefile
|
||||
# =============================================================================
|
||||
# DATABASE MIGRATIONS
|
||||
# =============================================================================
|
||||
|
||||
migrate-create:
|
||||
@if "$(message)"=="" (echo Error: Please provide a message. Usage: make migrate-create message="your_description") else ($(PYTHON) -m alembic revision --autogenerate -m "$(message)")
|
||||
|
||||
migrate-create-manual:
|
||||
@if "$(message)"=="" (echo Error: Please provide a message. Usage: make migrate-create-manual message="your_description") else ($(PYTHON) -m alembic revision -m "$(message)")
|
||||
|
||||
migrate-up:
|
||||
@echo Running database migrations...
|
||||
$(PYTHON) -m alembic upgrade head
|
||||
@echo Migrations completed successfully
|
||||
|
||||
migrate-down:
|
||||
@echo Rolling back last migration...
|
||||
$(PYTHON) -m alembic downgrade -1
|
||||
@echo Rollback completed
|
||||
|
||||
migrate-status:
|
||||
@echo Current migration status:
|
||||
$(PYTHON) -m alembic current
|
||||
@echo.
|
||||
@echo Migration history:
|
||||
$(PYTHON) -m alembic history --verbose
|
||||
|
||||
backup-db:
|
||||
@echo Creating database backup...
|
||||
@$(PYTHON) scripts/backup_database.py
|
||||
|
||||
seed:
|
||||
@echo Seeding database with comprehensive test data...
|
||||
$(PYTHON) scripts/seed_database.py
|
||||
@echo Seeding completed successfully
|
||||
|
||||
seed-minimal:
|
||||
@echo Seeding database with minimal data (admin + 1 vendor)...
|
||||
$(PYTHON) scripts/seed_database.py --minimal
|
||||
@echo Minimal seeding completed
|
||||
|
||||
seed-reset:
|
||||
@echo WARNING: This will DELETE ALL existing data!
|
||||
$(PYTHON) scripts/seed_database.py --reset
|
||||
@echo Database reset and seeded
|
||||
|
||||
# Complete database setup (migrate + seed)
|
||||
db-setup: migrate-up seed
|
||||
@echo Database setup complete!
|
||||
@echo Run 'make dev' to start development server
|
||||
|
||||
db-reset: migrate-down migrate-up seed-reset
|
||||
@echo Database completely reset!
|
||||
```
|
||||
@@ -0,0 +1,287 @@
|
||||
╔══════════════════════════════════════════════════════════════════════════════╗
|
||||
║ WIZAMART MULTI-TENANT ARCHITECTURE ║
|
||||
╚══════════════════════════════════════════════════════════════════════════════╝
|
||||
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ PRODUCTION DEPLOYMENT │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
Internet
|
||||
│
|
||||
│ HTTPS
|
||||
┌──────────────┼──────────────┐
|
||||
│ │ │
|
||||
┌───────────▼─────────┐ │ ┌────────▼────────────┐
|
||||
│ │ │ │ │
|
||||
│ acme.wizamart.com │ │ │ store.acme-corp.com │
|
||||
│ (Subdomain Mode) │ │ │ (Custom Domain) │
|
||||
│ │ │ │ │
|
||||
└───────────┬─────────┘ │ └────────┬────────────┘
|
||||
│ │ │
|
||||
└──────────────┼────────────┘
|
||||
│
|
||||
│ HTTP Request
|
||||
▼
|
||||
┌──────────────────────────┐
|
||||
│ FastAPI Application │
|
||||
│ (main.py) │
|
||||
└──────────────────────────┘
|
||||
│
|
||||
┌──────────────────┼──────────────────┐
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌────────────────┐ ┌──────────────┐ ┌──────────────────┐
|
||||
│ Middleware │ │ Middleware │ │ Middleware │
|
||||
│ Chain │ │ Chain │ │ Chain │
|
||||
├────────────────┤ ├──────────────┤ ├──────────────────┤
|
||||
│1. CORS │ │1. CORS │ │1. CORS │
|
||||
│2. Vendor │ │2. Vendor │ │2. Vendor │
|
||||
│ Detection │ │ Detection │ │ Detection │
|
||||
│3. Context │ │3. Context │ │3. Context │
|
||||
│ Detection │ │ Detection │ │ Detection │
|
||||
│4. Theme │ │4. Theme │ │4. Theme │
|
||||
│ Loading │ │ Loading │ │ Loading │
|
||||
│5. Logging │ │5. Logging │ │5. Logging │
|
||||
└────────────────┘ └──────────────┘ └──────────────────┘
|
||||
│ │ │
|
||||
│ request.state: │ request.state: │ request.state:
|
||||
│ vendor=1 │ vendor=1 │ vendor=2
|
||||
│ theme={...} │ theme={...} │ theme={...}
|
||||
│ context=SHOP │ context=SHOP │ context=SHOP
|
||||
│ │ │
|
||||
└──────────────────┼──────────────────┘
|
||||
│
|
||||
┌──────────▼──────────┐
|
||||
│ Route Matching │
|
||||
│ /shop/products │
|
||||
└──────────┬──────────┘
|
||||
│
|
||||
┌──────────▼──────────────────┐
|
||||
│ Handler Execution │
|
||||
│ (shop_pages.py) │
|
||||
└──────────┬──────────────────┘
|
||||
│
|
||||
┌──────────▼──────────────────┐
|
||||
│ Template Rendering │
|
||||
│ (shop/products.html) │
|
||||
│ + Vendor Theme │
|
||||
│ + Vendor Name │
|
||||
└──────────┬──────────────────┘
|
||||
│
|
||||
┌──────────────▼──────────────┐
|
||||
│ Jinja2 Template │
|
||||
│ ├─ ACME's theme colors │
|
||||
│ ├─ ACME's logo │
|
||||
│ ├─ ACME's fonts │
|
||||
│ └─ Data placeholder │
|
||||
└──────────┬────────────────┘
|
||||
│
|
||||
┌──────▼──────┐
|
||||
│ HTML Output │
|
||||
│ + Alpine.js │
|
||||
└──────┬──────┘
|
||||
│
|
||||
▼
|
||||
Browser renders
|
||||
JavaScript runs
|
||||
API calls:
|
||||
/api/v1/public/vendors/1/products
|
||||
│
|
||||
┌──────▼──────────────┐
|
||||
│ Backend API │
|
||||
│ ├─ products table │
|
||||
│ │ (vendor_id=1) │
|
||||
│ └─ returns JSON │
|
||||
└──────┬──────────────┘
|
||||
│
|
||||
┌──────▼──────────────┐
|
||||
│ Products rendered │
|
||||
│ with ACME's theme │
|
||||
│ in browser │
|
||||
└────────────────────┘
|
||||
|
||||
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ DEVELOPMENT DEPLOYMENT (Path-Based Routing) │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
Browser
|
||||
│
|
||||
│ http://localhost:8000/vendors/acme/shop/products
|
||||
│
|
||||
▼
|
||||
┌───────────────────────────┐
|
||||
│ FastAPI on Localhost │
|
||||
│ (main.py) │
|
||||
└──────────┬────────────────┘
|
||||
│
|
||||
│ Middleware detects:
|
||||
│ ├─ Path: /vendors/acme/...
|
||||
│ ├─ Extraction: vendor="acme"
|
||||
│ ├─ Query: vendors WHERE subdomain='acme'
|
||||
│ └─ Sets: request.state.vendor=Vendor(1)
|
||||
│
|
||||
┌──────▼──────────────────┐
|
||||
│ Remaining middleware │
|
||||
│ (context, theme, etc.) │
|
||||
└──────┬──────────────────┘
|
||||
│
|
||||
┌──────▼──────────────────┐
|
||||
│ Route: /shop/products │
|
||||
│ (depends on path rewrite│
|
||||
│ middleware) │
|
||||
└──────┬──────────────────┘
|
||||
│
|
||||
┌──────▼──────────────────┐
|
||||
│ Same HTML rendering │
|
||||
│ Same API calls │
|
||||
│ Same result │
|
||||
└────────────────────────┘
|
||||
|
||||
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ VENDOR DETECTION DECISION TREE │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
Incoming Request
|
||||
│
|
||||
▼
|
||||
Extract Host
|
||||
│
|
||||
┌──────────────────┼──────────────────┐
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
Is custom Is subdomain? Is path-based?
|
||||
domain? (not www, not (/vendors/...?)
|
||||
(not *. admin, not api)
|
||||
platform.com)
|
||||
│ │ │
|
||||
YES YES YES
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌─────────┐ ┌─────────┐ ┌─────────┐
|
||||
│Lookup │ │Extract │ │Extract │
|
||||
│vendor_ │ │subdomain│ │subdomain│
|
||||
│domains │ │from host│ │from path│
|
||||
│table │ └────┬────┘ └────┬────┘
|
||||
└────┬────┘ │ │
|
||||
│ ┌───────▼─────────┐ ┌─────▼──────┐
|
||||
│ │Query vendors │ │Query vendors│
|
||||
│ │WHERE subdomain= │ │WHERE subdom│
|
||||
│ │'{subdomain}' │ │='{code}' │
|
||||
│ └───────┬─────────┘ └─────┬──────┘
|
||||
│ │ │
|
||||
└──────────────┼───────────────────┘
|
||||
│
|
||||
┌───────▼────────┐
|
||||
│ Vendor Found? │
|
||||
└───┬────────┬───┘
|
||||
YES NO
|
||||
│ │
|
||||
▼ ▼
|
||||
┌───────────┐ ┌──────────┐
|
||||
│Set │ │Return 404│
|
||||
│request. │ │(Vendor │
|
||||
│state. │ │not found)│
|
||||
│vendor │ └──────────┘
|
||||
└───────────┘
|
||||
|
||||
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ DATABASE SCHEMA (Multi-Tenant Isolation) │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
vendors
|
||||
├─ id (PK)
|
||||
├─ name
|
||||
├─ subdomain (unique)
|
||||
├─ is_active
|
||||
└─ ... (other fields)
|
||||
|
||||
vendor_domains
|
||||
├─ id (PK)
|
||||
├─ vendor_id (FK → vendors.id)
|
||||
├─ domain (unique)
|
||||
├─ is_active
|
||||
├─ is_verified
|
||||
└─ ... (other fields)
|
||||
|
||||
vendor_themes
|
||||
├─ id (PK)
|
||||
├─ vendor_id (FK → vendors.id)
|
||||
├─ theme_name
|
||||
├─ colors (JSON)
|
||||
├─ branding (JSON)
|
||||
├─ fonts (JSON)
|
||||
├─ is_active
|
||||
└─ ... (other fields)
|
||||
|
||||
products
|
||||
├─ id (PK)
|
||||
├─ vendor_id (FK → vendors.id) ← MULTI-TENANT KEY
|
||||
├─ name
|
||||
├─ description
|
||||
├─ price
|
||||
├─ is_active
|
||||
└─ ... (other fields)
|
||||
|
||||
orders
|
||||
├─ id (PK)
|
||||
├─ vendor_id (FK → vendors.id) ← MULTI-TENANT KEY
|
||||
├─ customer_id
|
||||
├─ total
|
||||
└─ ... (other fields)
|
||||
|
||||
... Every table has vendor_id to enforce isolation
|
||||
|
||||
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ REQUEST CONTEXT DETECTION │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
Path Context Type Handler
|
||||
──────────────────────────────── ───────────────── ──────────────────────
|
||||
/api/v1/admin/users → RequestContext.API (JSON endpoints)
|
||||
/api/v1/vendor/products → RequestContext.API
|
||||
/api/v1/public/products → RequestContext.API
|
||||
|
||||
/admin/dashboard → RequestContext.ADMIN (HTML templates)
|
||||
/admin/users → RequestContext.ADMIN
|
||||
|
||||
/vendor/dashboard → RequestContext.VENDOR_DASHBOARD
|
||||
/vendor/products → RequestContext.VENDOR_DASHBOARD
|
||||
|
||||
/shop/products → RequestContext.SHOP (with vendor context)
|
||||
/shop/products/123 → RequestContext.SHOP
|
||||
|
||||
/ → RequestContext.FALLBACK
|
||||
|
||||
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ AUTHORIZATION LAYERS │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
Layer 1: URL Detection
|
||||
↓
|
||||
Vendor identified from host/domain/path
|
||||
|
||||
Layer 2: Middleware
|
||||
↓
|
||||
request.state.vendor = Vendor(...)
|
||||
|
||||
Layer 3: Database Queries
|
||||
↓
|
||||
SELECT * FROM products WHERE vendor_id = request.state.vendor.id
|
||||
|
||||
Layer 4: API Authorization
|
||||
↓
|
||||
if endpoint_vendor_id != request.state.vendor.id:
|
||||
raise UnauthorizedShopAccessException()
|
||||
|
||||
Result: Complete vendor isolation at all levels
|
||||
No way to cross-access vendor data
|
||||
|
||||
|
||||
╔══════════════════════════════════════════════════════════════════════════════╗
|
||||
║ END OF DIAGRAM ║
|
||||
╚══════════════════════════════════════════════════════════════════════════════╝
|
||||
@@ -0,0 +1,477 @@
|
||||
╔══════════════════════════════════════════════════════════════════════════════╗
|
||||
║ WIZAMART MULTI-TENANT URL ROUTING - COMPLETE SUMMARY ║
|
||||
╚══════════════════════════════════════════════════════════════════════════════╝
|
||||
|
||||
TL;DR
|
||||
═════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
Q: What's the URL for a customer to connect to a vendor's shop in Wizamart?
|
||||
|
||||
A: It depends on the deployment mode:
|
||||
|
||||
1. PRODUCTION (Subdomain): https://vendor-name.wizamart.com/shop/products
|
||||
2. PRODUCTION (Custom Domain): https://vendor-domain.com/shop/products
|
||||
3. DEVELOPMENT (Path-Based): http://localhost:8000/vendors/vendor-code/shop/products
|
||||
|
||||
|
||||
THE COMPLETE PICTURE
|
||||
═════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
DEPLOYMENT MODES
|
||||
────────────────
|
||||
|
||||
┌─ Mode 1: SUBDOMAIN ─────────────────────────────┐
|
||||
│ │
|
||||
│ URL: https://acme.wizamart.com/shop/products │
|
||||
│ │
|
||||
│ How it works: │
|
||||
│ 1. Customer visits acme.wizamart.com │
|
||||
│ 2. Middleware detects subdomain "acme" │
|
||||
│ 3. Queries: vendors WHERE subdomain = 'acme' │
|
||||
│ 4. Finds Vendor(id=1, name="ACME Store") │
|
||||
│ 5. Loads ACME's theme │
|
||||
│ 6. Renders HTML with ACME's branding │
|
||||
│ │
|
||||
│ Best for: Production, standard vendors │
|
||||
│ SSL: *.wizamart.com wildcard cert │
|
||||
│ DNS: Add subdomains as needed │
|
||||
└─────────────────────────────────────────────────┘
|
||||
|
||||
┌─ Mode 2: CUSTOM DOMAIN ─────────────────────────┐
|
||||
│ │
|
||||
│ URL: https://store.acmecorp.com/shop/products │
|
||||
│ │
|
||||
│ How it works: │
|
||||
│ 1. Customer visits store.acmecorp.com │
|
||||
│ 2. Middleware detects custom domain │
|
||||
│ 3. Queries: vendor_domains WHERE domain = ... │
|
||||
│ 4. Finds VendorDomain(vendor_id=1) │
|
||||
│ 5. Joins to Vendor(id=1) │
|
||||
│ 6. Loads ACME's theme │
|
||||
│ 7. Renders HTML with ACME's branding │
|
||||
│ │
|
||||
│ Best for: Production, premium vendors │
|
||||
│ SSL: Vendor's own SSL certificate │
|
||||
│ DNS: Vendor configures domain │
|
||||
└─────────────────────────────────────────────────┘
|
||||
|
||||
┌─ Mode 3: PATH-BASED ────────────────────────────┐
|
||||
│ │
|
||||
│ URL: http://localhost:8000/vendor/acme/shop... │
|
||||
│ │
|
||||
│ How it works: │
|
||||
│ 1. Developer visits localhost:8000/vendor/.. │
|
||||
│ 2. Middleware detects path-based routing │
|
||||
│ 3. Extracts "acme" from path │
|
||||
│ 4. Queries: vendors WHERE subdomain = 'acme' │
|
||||
│ 5. Finds Vendor(id=1) │
|
||||
│ 6. Loads ACME's theme │
|
||||
│ 7. Renders HTML with ACME's branding │
|
||||
│ │
|
||||
│ Best for: Local development & testing │
|
||||
│ SSL: None needed (localhost) │
|
||||
│ DNS: None needed │
|
||||
└─────────────────────────────────────────────────┘
|
||||
|
||||
|
||||
REQUEST PROCESSING FLOW
|
||||
═══════════════════════
|
||||
|
||||
Customer Request
|
||||
↓
|
||||
├─ vendor_context_middleware
|
||||
│ ├─ Detects vendor from host/domain/path
|
||||
│ └─ Sets request.state.vendor = Vendor(...)
|
||||
├─ context_middleware
|
||||
│ ├─ Detects RequestContext.SHOP
|
||||
│ └─ Sets request.state.context_type
|
||||
├─ theme_context_middleware
|
||||
│ ├─ Loads vendor theme
|
||||
│ └─ Sets request.state.theme
|
||||
↓
|
||||
├─ Route Matching → /shop/products
|
||||
├─ Handler Execution → shop_products_page()
|
||||
├─ Template Rendering → shop/products.html
|
||||
│ ├─ Uses vendor.name → "ACME Store"
|
||||
│ ├─ Uses theme.colors → "#FF6B6B"
|
||||
│ └─ Uses theme.logo → "acme-logo.png"
|
||||
├─ HTML Sent to Browser
|
||||
├─ JavaScript Runs
|
||||
├─ API Call → /api/v1/public/vendors/1/products
|
||||
├─ Backend Returns Products
|
||||
├─ JavaScript Renders with Theme
|
||||
↓
|
||||
Response: Fully Branded Shop
|
||||
|
||||
|
||||
VENDOR ISOLATION: 4 LAYERS
|
||||
════════════════════════════
|
||||
|
||||
Layer 1: URL DETECTION
|
||||
│
|
||||
├─ Subdomain → extract from host
|
||||
├─ Custom Domain → lookup VendorDomain table
|
||||
└─ Path → extract from URL path
|
||||
↓
|
||||
Result: Vendor identified
|
||||
|
||||
Layer 2: MIDDLEWARE
|
||||
│
|
||||
└─ request.state.vendor = Vendor(...)
|
||||
↓
|
||||
Result: Vendor accessible throughout request
|
||||
|
||||
Layer 3: DATABASE QUERIES
|
||||
│
|
||||
└─ WHERE vendor_id = request.state.vendor.id
|
||||
↓
|
||||
Result: ONLY that vendor's data returned
|
||||
|
||||
Layer 4: API AUTHORIZATION
|
||||
│
|
||||
└─ if vendor_id != request.state.vendor.id: raise Exception
|
||||
↓
|
||||
Result: Cross-vendor access impossible
|
||||
|
||||
|
||||
ALL SHOP ROUTES
|
||||
════════════════
|
||||
|
||||
PUBLIC ROUTES (No authentication required)
|
||||
───────────────────────────────────────────
|
||||
/shop/ → Homepage / product catalog
|
||||
/shop/products → Product catalog
|
||||
/shop/products/{id} → Product detail page
|
||||
/shop/categories/{slug} → Category products
|
||||
/shop/cart → Shopping cart
|
||||
/shop/checkout → Checkout process
|
||||
/shop/search → Search results
|
||||
/shop/about → About us page
|
||||
/shop/contact → Contact us page
|
||||
/shop/faq → FAQ page
|
||||
/shop/privacy → Privacy policy
|
||||
/shop/terms → Terms of service
|
||||
|
||||
AUTHENTICATION ROUTES
|
||||
──────────────────────
|
||||
/shop/account/register → Customer registration
|
||||
/shop/account/login → Customer login
|
||||
/shop/account/forgot-password → Password reset
|
||||
|
||||
PROTECTED ROUTES (Authentication required)
|
||||
───────────────────────────────────────────
|
||||
/shop/account/ → Account home
|
||||
/shop/account/dashboard → Account dashboard
|
||||
/shop/account/orders → Order history
|
||||
/shop/account/orders/{id} → Order details
|
||||
/shop/account/profile → Profile page
|
||||
/shop/account/addresses → Address management
|
||||
/shop/account/wishlist → Wishlist
|
||||
/shop/account/settings → Account settings
|
||||
|
||||
|
||||
COMPLETE REQUEST LIFECYCLE EXAMPLE
|
||||
═══════════════════════════════════
|
||||
|
||||
Scenario: Customer visits https://acme.wizamart.com/shop/products
|
||||
|
||||
Step 1: Browser Request
|
||||
GET https://acme.wizamart.com/shop/products
|
||||
Headers: Host: acme.wizamart.com
|
||||
|
||||
Step 2: vendor_context_middleware
|
||||
Input: host = "acme.wizamart.com"
|
||||
Processing:
|
||||
1. Check if custom domain? NO
|
||||
2. Check if subdomain? YES
|
||||
3. Extract "acme"
|
||||
4. Query: vendors WHERE subdomain = 'acme'
|
||||
Output: request.state.vendor = Vendor(id=1, name="ACME Store")
|
||||
|
||||
Step 3: context_middleware
|
||||
Input: path = "/shop/products", request.state.vendor exists
|
||||
Processing:
|
||||
1. Is /api/*? NO
|
||||
2. Is /admin/*? NO
|
||||
3. Has vendor? YES
|
||||
Output: request.state.context_type = RequestContext.SHOP
|
||||
|
||||
Step 4: theme_context_middleware
|
||||
Input: request.state.vendor.id = 1
|
||||
Processing:
|
||||
1. Query: vendor_themes WHERE vendor_id = 1
|
||||
Output: request.state.theme = {colors, branding, fonts}
|
||||
|
||||
Step 5: Route Matching
|
||||
Path: /shop/products
|
||||
Matches: GET /shop/products
|
||||
Handler: shop_products_page(request)
|
||||
|
||||
Step 6: Template Rendering (shop/products.html)
|
||||
Variables available:
|
||||
- request.state.vendor.name → "ACME Store"
|
||||
- request.state.theme.colors.primary → "#FF6B6B"
|
||||
- request.state.theme.branding.logo → "acme-logo.png"
|
||||
|
||||
Step 7: HTML Response
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
:root {
|
||||
--color-primary: #FF6B6B;
|
||||
--color-secondary: #FF8787;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<img src="acme-logo.png" />
|
||||
<h1>ACME Store</h1>
|
||||
<div id="products"></div>
|
||||
<script>
|
||||
fetch('/api/v1/public/vendors/1/products')
|
||||
.then(r => r.json())
|
||||
.then(data => renderProducts(data))
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Step 8: Browser Renders
|
||||
- JavaScript fetches products via API
|
||||
- Products rendered with theme colors
|
||||
- User sees fully branded ACME store
|
||||
|
||||
|
||||
KEY DIFFERENCES BETWEEN MODES
|
||||
═══════════════════════════════
|
||||
|
||||
SUBDOMAIN:
|
||||
✓ Easy to manage
|
||||
✓ Single SSL certificate
|
||||
✓ Customers need no setup
|
||||
✗ All on same domain
|
||||
✗ Less professional branding
|
||||
|
||||
CUSTOM DOMAIN:
|
||||
✓ Professional branding
|
||||
✓ Vendor's own domain
|
||||
✓ Better for premium vendors
|
||||
✗ Each vendor needs SSL
|
||||
✗ Vendor must configure domain
|
||||
|
||||
PATH-BASED:
|
||||
✓ Perfect for development
|
||||
✓ No DNS setup needed
|
||||
✓ Test multiple vendors
|
||||
✗ Not suitable for production
|
||||
✗ All vendors share localhost
|
||||
|
||||
|
||||
AUTHENTICATION
|
||||
════════════════
|
||||
|
||||
Customer Authentication:
|
||||
- JWT token in Authorization header (API calls)
|
||||
- customer_token cookie (page navigation)
|
||||
- Cookie path scoped to /shop (vendor isolation)
|
||||
- HttpOnly flag prevents JavaScript access
|
||||
|
||||
Example:
|
||||
Set-Cookie: customer_token=eyJ...; Path=/shop; HttpOnly; SameSite=Lax
|
||||
|
||||
|
||||
API ENDPOINTS
|
||||
════════════════
|
||||
|
||||
All public endpoints follow this pattern:
|
||||
|
||||
GET /api/v1/public/vendors/{vendor_id}/products
|
||||
GET /api/v1/public/vendors/{vendor_id}/products/{product_id}
|
||||
GET /api/v1/public/vendors/{vendor_id}/products/search
|
||||
POST /api/v1/public/vendors/{vendor_id}/cart
|
||||
...
|
||||
|
||||
These work from ANY domain because vendor_id is explicit in the URL.
|
||||
|
||||
|
||||
DATABASE MULTI-TENANT KEYS
|
||||
════════════════════════════
|
||||
|
||||
Every business table has vendor_id:
|
||||
|
||||
products
|
||||
├─ id
|
||||
├─ vendor_id ← Required
|
||||
├─ name
|
||||
└─ ...
|
||||
|
||||
orders
|
||||
├─ id
|
||||
├─ vendor_id ← Required
|
||||
├─ customer_id
|
||||
└─ ...
|
||||
|
||||
reviews
|
||||
├─ id
|
||||
├─ vendor_id ← Required
|
||||
├─ product_id
|
||||
└─ ...
|
||||
|
||||
This ensures query filtering at database level.
|
||||
|
||||
|
||||
MIDDLEWARE DETAILS
|
||||
═══════════════════
|
||||
|
||||
File: middleware/vendor_context.py
|
||||
Purpose: Detect vendor from host/domain/path
|
||||
|
||||
Methods:
|
||||
- detect_vendor_context() → Identifies vendor
|
||||
- get_vendor_from_context() → Looks up vendor
|
||||
- extract_clean_path() → Extracts path (dev mode)
|
||||
- is_admin_request() → Checks if admin
|
||||
- is_api_request() → Checks if API
|
||||
- is_static_file_request() → Checks if static
|
||||
|
||||
File: middleware/theme_context.py
|
||||
Purpose: Load vendor-specific theme
|
||||
|
||||
Methods:
|
||||
- get_vendor_theme() → Loads theme from DB
|
||||
- get_default_theme() → Returns fallback theme
|
||||
|
||||
File: middleware/context_middleware.py
|
||||
Purpose: Determine request context type
|
||||
|
||||
Types:
|
||||
- RequestContext.API → /api/* endpoints
|
||||
- RequestContext.ADMIN → /admin/* endpoints
|
||||
- RequestContext.VENDOR_DASHBOARD → /vendor/* endpoints
|
||||
- RequestContext.SHOP → Customer shop
|
||||
- RequestContext.FALLBACK → Unknown
|
||||
|
||||
File: middleware/logging_middleware.py
|
||||
Purpose: Log all requests and responses
|
||||
|
||||
|
||||
DEBUGGING TIPS
|
||||
═════════════
|
||||
|
||||
Check Vendor Detection:
|
||||
{{ request.state.vendor.name }}
|
||||
{{ request.state.vendor.subdomain }}
|
||||
|
||||
Check Theme Loading:
|
||||
{{ request.state.theme.colors.primary }}
|
||||
{{ request.state.theme.branding.logo }}
|
||||
|
||||
Check Context Type:
|
||||
{{ request.state.context_type }}
|
||||
|
||||
Server Logs:
|
||||
[OK] Vendor found via subdomain: acme -> ACME Store
|
||||
[WARNING] Vendor not found for context: {...}
|
||||
|
||||
Test with curl:
|
||||
curl -H "Host: acme.wizamart.com" http://localhost:8000/shop/products
|
||||
|
||||
|
||||
POTENTIAL ISSUES
|
||||
═════════════════
|
||||
|
||||
Issue 1: Path-Based Routing Not Working
|
||||
Problem: /vendor/acme/shop/products → 404
|
||||
Cause: clean_path set but not used for routing
|
||||
Solution: Add path rewriting middleware or dual route registration
|
||||
|
||||
Issue 2: Theme Not Loading
|
||||
Problem: Products show without vendor theme
|
||||
Cause: VendorTheme not created for vendor
|
||||
Solution: Create VendorTheme in database or create default
|
||||
|
||||
Issue 3: Cross-Vendor Data Leakage
|
||||
Problem: Customers can see other vendors' products
|
||||
Cause: Missing vendor_id WHERE clause
|
||||
Solution: Ensure all queries include vendor_id filter
|
||||
|
||||
Issue 4: Authentication Confusion
|
||||
Problem: Tokens work across vendors
|
||||
Cause: No cookie path isolation
|
||||
Solution: Ensure cookie path is /shop per deployment
|
||||
|
||||
|
||||
SECURITY BEST PRACTICES
|
||||
═════════════════════════
|
||||
|
||||
✓ DO:
|
||||
- Filter every database query by vendor_id
|
||||
- Validate vendor_id in all API endpoints
|
||||
- Use vendor-scoped authentication cookies
|
||||
- Implement multi-layer authorization checks
|
||||
- Log all vendor context switches
|
||||
|
||||
✗ DON'T:
|
||||
- Trust vendor_id from request body alone
|
||||
- Skip vendor checks in nested calls
|
||||
- Mix vendors in single query
|
||||
- Cache data without vendor isolation
|
||||
- Assume middleware always runs
|
||||
|
||||
|
||||
DEPLOYMENT CHECKLIST
|
||||
════════════════════
|
||||
|
||||
For Production:
|
||||
☐ Set up DNS with wildcard subdomain (*.wizamart.com)
|
||||
☐ Or allow vendors to add custom domains
|
||||
☐ Configure SSL certificates
|
||||
☐ Set platform_domain in config
|
||||
☐ Create vendors with unique subdomains
|
||||
☐ Create VendorTheme for each vendor
|
||||
☐ Test multi-vendor setup
|
||||
☐ Verify no data leakage
|
||||
☐ Enable logging
|
||||
|
||||
For Development:
|
||||
☐ Use localhost:8000 with path-based routing
|
||||
☐ Create test vendors
|
||||
☐ Create test themes
|
||||
☐ Test with multiple vendors
|
||||
☐ Verify middleware detection
|
||||
☐ Check database filters
|
||||
|
||||
|
||||
METRICS TO MONITOR
|
||||
═══════════════════
|
||||
|
||||
- Request/vendor distribution
|
||||
- API latency by vendor
|
||||
- Database query performance
|
||||
- Middleware execution time
|
||||
- Theme loading time
|
||||
- Error rates by vendor
|
||||
- Authentication failures
|
||||
- Cross-vendor access attempts
|
||||
|
||||
|
||||
SCALING CONSIDERATIONS
|
||||
═══════════════════════
|
||||
|
||||
- Database: Partition by vendor_id if > 10k vendors
|
||||
- Cache: Cache themes by vendor_id
|
||||
- CDN: Serve static assets globally
|
||||
- API Gateway: Route by vendor subdomain
|
||||
- Monitoring: Alert on cross-vendor attempts
|
||||
- Rate limiting: Per-vendor rate limits
|
||||
|
||||
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
For more details, see:
|
||||
- WIZAMART_MULTITENANT_URL_GUIDE.md (Complete guide)
|
||||
- QUICK_REFERENCE.md (Quick reference)
|
||||
- ARCHITECTURE_DIAGRAMS.txt (Visual diagrams)
|
||||
|
||||
Generated: November 7, 2025
|
||||
Version: Current Development
|
||||
@@ -0,0 +1,283 @@
|
||||
╔══════════════════════════════════════════════════════════════════════════════╗
|
||||
║ DOCUMENTATION GENERATION COMPLETE ✅ ║
|
||||
║ ║
|
||||
║ Wizamart Multi-Tenant URL Routing - Complete Guide ║
|
||||
╚══════════════════════════════════════════════════════════════════════════════╝
|
||||
|
||||
|
||||
📊 ANSWER TO YOUR QUESTION
|
||||
═════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
Q: What's the URL for a customer to connect to a vendor's shop in Wizamart?
|
||||
|
||||
A: It depends on the deployment mode:
|
||||
|
||||
1️⃣ PRODUCTION (Subdomain)
|
||||
https://acme.wizamart.com/shop/products
|
||||
https://techpro.wizamart.com/shop/categories/electronics
|
||||
https://fashion.wizamart.com/shop/cart
|
||||
|
||||
2️⃣ PRODUCTION (Custom Domain - Premium)
|
||||
https://store.acme-corp.com/shop/products
|
||||
https://shop.techpro.io/shop/checkout
|
||||
https://mybrand.com/shop/account/dashboard
|
||||
|
||||
3️⃣ DEVELOPMENT (Path-Based)
|
||||
http://localhost:8000/vendor/acme/shop/products
|
||||
http://localhost:8000/vendor/techpro/shop/cart
|
||||
http://localhost:8000/vendor/fashion/shop/checkout
|
||||
|
||||
|
||||
📚 COMPLETE DOCUMENTATION GENERATED
|
||||
═════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
✅ 5 Comprehensive Documentation Files Created:
|
||||
|
||||
┌─ INDEX.md ──────────────────────────────────────────────────────┐
|
||||
│ │
|
||||
│ 📍 START HERE - Navigation guide to all documentation │
|
||||
│ ├─ Quick navigation by use case │
|
||||
│ ├─ Answers to common questions │
|
||||
│ ├─ Debugging checklist │
|
||||
│ └─ Quick reference card │
|
||||
│ │
|
||||
│ Size: 12 KB │
|
||||
│ Read Time: 5-10 minutes │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─ COMPLETE_SUMMARY.txt ───────────────────────────────────────────┐
|
||||
│ │
|
||||
│ 🎯 THE COMPLETE PICTURE │
|
||||
│ ├─ TL;DR answer │
|
||||
│ ├─ All three deployment modes │
|
||||
│ ├─ Request processing flow │
|
||||
│ ├─ 4-layer vendor isolation explanation │
|
||||
│ ├─ Complete request lifecycle (step-by-step) │
|
||||
│ ├─ All shop routes │
|
||||
│ ├─ Debugging tips │
|
||||
│ ├─ Security best practices │
|
||||
│ ├─ Deployment checklist │
|
||||
│ └─ Potential issues & solutions │
|
||||
│ │
|
||||
│ Size: 17 KB │
|
||||
│ Read Time: 20-30 minutes │
|
||||
│ Best For: Complete technical overview │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─ WIZAMART_MULTITENANT_URL_GUIDE.md ──────────────────────────────┐
|
||||
│ │
|
||||
│ 📖 DETAILED TECHNICAL GUIDE │
|
||||
│ ├─ Subdomain mode (production standard) │
|
||||
│ ├─ Custom domain mode (production premium) │
|
||||
│ ├─ Path-based mode (development) │
|
||||
│ ├─ URL breakdown & examples │
|
||||
│ ├─ Database perspective │
|
||||
│ ├─ Real-world customer URLs │
|
||||
│ ├─ API endpoints (vendor-independent) │
|
||||
│ ├─ Theme integration │
|
||||
│ ├─ Multi-layer vendor isolation │
|
||||
│ └─ Potential issues & solutions │
|
||||
│ │
|
||||
│ Size: 16 KB │
|
||||
│ Read Time: 25-35 minutes │
|
||||
│ Best For: Deep technical understanding │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─ QUICK_REFERENCE.md ─────────────────────────────────────────────┐
|
||||
│ │
|
||||
│ ⚡ QUICK LOOKUP GUIDE │
|
||||
│ ├─ URL breakdown diagrams │
|
||||
│ ├─ Request flow (in order) │
|
||||
│ ├─ Detection priority │
|
||||
│ ├─ All shop routes table │
|
||||
│ ├─ Request state contents │
|
||||
│ ├─ Common scenarios │
|
||||
│ ├─ Debugging tips │
|
||||
│ ├─ Quick links to code files │
|
||||
│ └─ Environment configuration │
|
||||
│ │
|
||||
│ Size: 9.8 KB │
|
||||
│ Read Time: 5-15 minutes │
|
||||
│ Best For: Quick lookups while coding │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─ ARCHITECTURE_DIAGRAMS.txt ──────────────────────────────────────┐
|
||||
│ │
|
||||
│ 🏗️ VISUAL ARCHITECTURE REFERENCE │
|
||||
│ ├─ Production deployment diagram │
|
||||
│ ├─ Development deployment diagram │
|
||||
│ ├─ Vendor detection decision tree │
|
||||
│ ├─ Database schema (multi-tenant) │
|
||||
│ ├─ Request context detection table │
|
||||
│ ├─ Authorization layers diagram │
|
||||
│ └─ Complete request lifecycle visualization │
|
||||
│ │
|
||||
│ Size: 18 KB │
|
||||
│ Read Time: 10-15 minutes │
|
||||
│ Best For: Visual learners │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
|
||||
|
||||
🎯 QUICK START GUIDE
|
||||
═════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
For Developers (30 minutes):
|
||||
1. Read: INDEX.md (5 min) → Get oriented
|
||||
2. Read: COMPLETE_SUMMARY.txt top section (5 min) → Understand modes
|
||||
3. View: ARCHITECTURE_DIAGRAMS.txt (5 min) → See the flow
|
||||
4. Browse: QUICK_REFERENCE.md (10 min) → Learn details
|
||||
5. Start coding! ✅
|
||||
|
||||
For DevOps (20 minutes):
|
||||
1. Read: COMPLETE_SUMMARY.txt (10 min) → Understand all modes
|
||||
2. Read: QUICK_REFERENCE.md → Deployment Checklist (5 min)
|
||||
3. Follow the checklist for your chosen mode (5 min)
|
||||
|
||||
For Architects (15 minutes):
|
||||
1. View: ARCHITECTURE_DIAGRAMS.txt (5 min) → See the design
|
||||
2. Read: INDEX.md (5 min) → Understand the options
|
||||
3. Decide on deployment mode ✅
|
||||
|
||||
For Quick Reference:
|
||||
→ Use: QUICK_REFERENCE.md → Find what you need
|
||||
|
||||
|
||||
⚙️ HOW THE SYSTEM WORKS
|
||||
═════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
Customer visits: https://acme.wizamart.com/shop/products
|
||||
|
||||
↓
|
||||
┌──────────────────────────────────────────────────────┐
|
||||
│ FastAPI Middleware Chain │
|
||||
├──────────────────────────────────────────────────────┤
|
||||
│ 1. vendor_context_middleware │
|
||||
│ ├─ Detects host: "acme.wizamart.com" │
|
||||
│ ├─ Extracts subdomain: "acme" │
|
||||
│ ├─ Queries: vendors WHERE subdomain = 'acme' │
|
||||
│ └─ Sets: request.state.vendor = Vendor(ACME) │
|
||||
│ │
|
||||
│ 2. context_middleware │
|
||||
│ └─ Sets: request.state.context_type = SHOP │
|
||||
│ │
|
||||
│ 3. theme_context_middleware │
|
||||
│ ├─ Queries: vendor_themes WHERE vendor_id = 1 │
|
||||
│ └─ Sets: request.state.theme = {...ACME theme...}│
|
||||
└──────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌──────────────────────────────────────────────────────┐
|
||||
│ Route Handler │
|
||||
│ shop_products_page(request) │
|
||||
│ → Returns: shop/products.html │
|
||||
└──────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌──────────────────────────────────────────────────────┐
|
||||
│ Template Rendering (Jinja2) │
|
||||
│ ├─ {{ request.state.vendor.name }} │
|
||||
│ │ → "ACME Store" │
|
||||
│ ├─ {{ request.state.theme.colors.primary }} │
|
||||
│ │ → "#FF6B6B" │
|
||||
│ └─ {{ request.state.theme.branding.logo }} │
|
||||
│ → "acme-logo.png" │
|
||||
└──────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌──────────────────────────────────────────────────────┐
|
||||
│ HTML Response + JavaScript │
|
||||
│ fetch('/api/v1/public/vendors/1/products') │
|
||||
│ → Renders products with ACME's theme │
|
||||
└──────────────────────────────────────────────────────┘
|
||||
↓
|
||||
✅ Customer sees fully branded ACME shop
|
||||
|
||||
|
||||
📁 FILES SAVED TO: /mnt/user-data/outputs/
|
||||
═════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
📄 INDEX.md (12 KB)
|
||||
↓ START HERE - Navigation & common Q&A
|
||||
|
||||
📄 COMPLETE_SUMMARY.txt (17 KB)
|
||||
↓ Complete technical overview with examples
|
||||
|
||||
📄 WIZAMART_MULTITENANT_URL_GUIDE.md (16 KB)
|
||||
↓ Detailed technical guide for all modes
|
||||
|
||||
📄 QUICK_REFERENCE.md (9.8 KB)
|
||||
↓ Quick lookup while coding
|
||||
|
||||
📄 ARCHITECTURE_DIAGRAMS.txt (18 KB)
|
||||
↓ Visual diagrams & flowcharts
|
||||
|
||||
|
||||
💡 KEY TAKEAWAYS
|
||||
═════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
1. THREE DEPLOYMENT MODES:
|
||||
✓ Subdomain (production) → https://vendor.platform.com/shop
|
||||
✓ Custom Domain (premium) → https://vendor-domain.com/shop
|
||||
✓ Path-Based (development) → http://localhost:8000/vendor/v/shop
|
||||
|
||||
2. VENDOR DETECTION (4 Layers):
|
||||
✓ URL detection from host/domain/path
|
||||
✓ Middleware sets request.state.vendor
|
||||
✓ Database queries filtered by vendor_id
|
||||
✓ API authorization validates vendor ownership
|
||||
|
||||
3. CUSTOMER EXPERIENCE:
|
||||
✓ No multi-tenant awareness needed
|
||||
✓ Completely separate branded shops
|
||||
✓ Each vendor has their own theme
|
||||
✓ No data leakage between vendors
|
||||
|
||||
4. DEVELOPMENT WORKFLOW:
|
||||
✓ Use path-based mode locally (/vendor/acme/...)
|
||||
✓ Use subdomain mode in production
|
||||
✓ Use custom domain for premium vendors
|
||||
|
||||
|
||||
🔐 SECURITY
|
||||
═════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
✓ Multi-layer vendor isolation prevents cross-vendor access
|
||||
✓ Database queries always filtered by vendor_id
|
||||
✓ API endpoints validate vendor ownership
|
||||
✓ Authentication cookies scoped to /shop path
|
||||
✓ No way for customers to access other vendors' data
|
||||
|
||||
|
||||
📞 NEED HELP?
|
||||
═════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
1. Check INDEX.md → "Most Common Questions" section
|
||||
2. Check QUICK_REFERENCE.md → "Debugging Tips" section
|
||||
3. Check COMPLETE_SUMMARY.txt → "Potential Issues" section
|
||||
4. Review ARCHITECTURE_DIAGRAMS.txt → Visual reference
|
||||
|
||||
|
||||
✨ NEXT STEPS
|
||||
═════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
1. Read INDEX.md (5 min) to get oriented
|
||||
2. Choose a documentation file based on your needs
|
||||
3. Learn the three deployment modes
|
||||
4. Understand the middleware chain
|
||||
5. Review the vendor isolation layers
|
||||
6. Check the debugging tips
|
||||
7. Start developing! 🚀
|
||||
|
||||
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
📊 DOCUMENTATION STATS:
|
||||
Total Files: 5
|
||||
Total Size: ~73 KB
|
||||
Total Content: ~18,000 words
|
||||
Topics Covered: 50+
|
||||
Diagrams: 8+
|
||||
Code Examples: 20+
|
||||
Routes Documented: 25+
|
||||
|
||||
Generated: November 7, 2025
|
||||
Version: Complete & Ready to Use ✅
|
||||
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
@@ -0,0 +1,412 @@
|
||||
# Wizamart Multi-Tenant URL Routing - Documentation Index
|
||||
|
||||
**Generated:** November 7, 2025
|
||||
**Project:** Wizamart Multi-Tenant E-Commerce Platform
|
||||
**Topic:** Understanding how customers access vendor shops via URLs
|
||||
|
||||
---
|
||||
|
||||
## 📚 Documentation Files
|
||||
|
||||
### 1. **COMPLETE_SUMMARY.txt** ⭐ START HERE
|
||||
**Best for:** Getting the complete picture in one file
|
||||
|
||||
Contains:
|
||||
- TL;DR answer to the main question
|
||||
- All three deployment modes explained
|
||||
- Request processing flow
|
||||
- Vendor isolation layers
|
||||
- All shop routes
|
||||
- Complete request lifecycle example
|
||||
- Debugging tips & troubleshooting
|
||||
- Security best practices
|
||||
|
||||
**Read this when:** You need everything in one place
|
||||
|
||||
---
|
||||
|
||||
### 2. **WIZAMART_MULTITENANT_URL_GUIDE.md**
|
||||
**Best for:** Deep dive into multi-tenant architecture
|
||||
|
||||
Contains:
|
||||
- Detailed explanation of all three deployment modes
|
||||
- Subdomain mode with real-world example
|
||||
- Custom domain mode with setup steps
|
||||
- Path-based mode for development
|
||||
- Complete route examples
|
||||
- How vendor isolation works
|
||||
- Request routing decision tree
|
||||
- Theme integration
|
||||
- Database perspective
|
||||
- Potential issues and solutions
|
||||
|
||||
**Read this when:** You want comprehensive documentation
|
||||
|
||||
---
|
||||
|
||||
### 3. **QUICK_REFERENCE.md**
|
||||
**Best for:** Quick lookups while coding
|
||||
|
||||
Contains:
|
||||
- URL breakdown diagrams
|
||||
- Request flow (in order)
|
||||
- Detection priority (highest to lowest)
|
||||
- All shop routes table
|
||||
- API endpoints pattern
|
||||
- Request state contents
|
||||
- Vendor isolation multi-layer view
|
||||
- Common scenarios
|
||||
- Middleware files location
|
||||
- Environment configuration
|
||||
- Debugging tips
|
||||
- Quick links to code files
|
||||
|
||||
**Read this when:** You need a specific answer quickly
|
||||
|
||||
---
|
||||
|
||||
### 4. **ARCHITECTURE_DIAGRAMS.txt**
|
||||
**Best for:** Visual understanding of the system
|
||||
|
||||
Contains:
|
||||
- Production deployment diagram
|
||||
- Development deployment diagram
|
||||
- Vendor detection decision tree
|
||||
- Database schema (multi-tenant)
|
||||
- Request context detection table
|
||||
- Authorization layers
|
||||
|
||||
**Read this when:** You prefer visual representations
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Quick Navigation by Use Case
|
||||
|
||||
### "I need to understand the basic URLs"
|
||||
→ Read: **COMPLETE_SUMMARY.txt** (Top section)
|
||||
→ Time: 5 minutes
|
||||
|
||||
### "I need to set up production deployment"
|
||||
→ Read: **QUICK_REFERENCE.md** (Deployment Checklist section)
|
||||
→ Then: **WIZAMART_MULTITENANT_URL_GUIDE.md** (Mode 1 & 2 sections)
|
||||
→ Time: 15 minutes
|
||||
|
||||
### "I'm debugging a routing issue"
|
||||
→ Read: **QUICK_REFERENCE.md** (Debugging Tips section)
|
||||
→ Then: **COMPLETE_SUMMARY.txt** (Potential Issues section)
|
||||
→ Time: 10 minutes
|
||||
|
||||
### "I need to understand vendor isolation"
|
||||
→ Read: **COMPLETE_SUMMARY.txt** (Vendor Isolation section)
|
||||
→ Then: **WIZAMART_MULTITENANT_URL_GUIDE.md** (How Vendor Isolation Works)
|
||||
→ Time: 15 minutes
|
||||
|
||||
### "I want a complete technical overview"
|
||||
→ Read: **WIZAMART_MULTITENANT_URL_GUIDE.md** (entire file)
|
||||
→ Then: **ARCHITECTURE_DIAGRAMS.txt** (visual reference)
|
||||
→ Time: 30 minutes
|
||||
|
||||
### "I need to learn the middleware flow"
|
||||
→ Read: **ARCHITECTURE_DIAGRAMS.txt** (Production Deployment diagram)
|
||||
→ Then: **COMPLETE_SUMMARY.txt** (Request Processing Flow)
|
||||
→ Time: 10 minutes
|
||||
|
||||
---
|
||||
|
||||
## 📖 The Three Deployment Modes
|
||||
|
||||
### Mode 1: Subdomain (Production Standard)
|
||||
```
|
||||
URL: https://acme.wizamart.com/shop/products
|
||||
Best for: Most vendors
|
||||
SSL: Single wildcard certificate
|
||||
DNS: Add subdomains as needed
|
||||
```
|
||||
📖 Details: WIZAMART_MULTITENANT_URL_GUIDE.md → "1. SUBDOMAIN MODE"
|
||||
|
||||
### Mode 2: Custom Domain (Production Premium)
|
||||
```
|
||||
URL: https://store.acme-corp.com/shop/products
|
||||
Best for: Premium vendors
|
||||
SSL: Vendor's own certificate
|
||||
DNS: Vendor configures domain
|
||||
```
|
||||
📖 Details: WIZAMART_MULTITENANT_URL_GUIDE.md → "2. CUSTOM DOMAIN MODE"
|
||||
|
||||
### Mode 3: Path-Based (Development)
|
||||
```
|
||||
URL: http://localhost:8000/vendor/acme/shop/products
|
||||
Best for: Development & testing
|
||||
SSL: None needed
|
||||
DNS: None needed
|
||||
```
|
||||
📖 Details: WIZAMART_MULTITENANT_URL_GUIDE.md → "3. PATH-BASED MODE"
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Request Flow Overview
|
||||
|
||||
```
|
||||
Customer Request
|
||||
↓
|
||||
vendor_context_middleware ← Detects which vendor
|
||||
↓
|
||||
context_middleware ← Determines context type (SHOP)
|
||||
↓
|
||||
theme_context_middleware ← Loads vendor's theme
|
||||
↓
|
||||
Route Matching ← /shop/products
|
||||
↓
|
||||
Handler Execution ← Renders template
|
||||
↓
|
||||
Template with Theme ← Uses vendor's colors, logo, fonts
|
||||
↓
|
||||
HTML Response ← Fully branded shop
|
||||
↓
|
||||
JavaScript API Call ← Fetches products
|
||||
↓
|
||||
Customer sees Shop ← With vendor's theme
|
||||
```
|
||||
|
||||
📖 Full details: COMPLETE_SUMMARY.txt → "REQUEST PROCESSING FLOW"
|
||||
|
||||
---
|
||||
|
||||
## 🛡️ Vendor Isolation (4 Layers)
|
||||
|
||||
```
|
||||
Layer 1: URL Detection ← Identifies vendor from host/domain/path
|
||||
↓
|
||||
Layer 2: Middleware ← Sets request.state.vendor
|
||||
↓
|
||||
Layer 3: Database Queries ← WHERE vendor_id = ?
|
||||
↓
|
||||
Layer 4: API Authorization ← Validates vendor_id
|
||||
↓
|
||||
Result: No cross-vendor access possible
|
||||
```
|
||||
|
||||
📖 Full details: COMPLETE_SUMMARY.txt → "VENDOR ISOLATION: 4 LAYERS"
|
||||
|
||||
---
|
||||
|
||||
## 🗂️ All Shop Routes
|
||||
|
||||
| Category | Routes |
|
||||
|----------|--------|
|
||||
| **Public** | `/shop/`, `/shop/products`, `/shop/categories/{slug}`, `/shop/search`, etc. |
|
||||
| **Auth** | `/shop/account/register`, `/shop/account/login`, `/shop/account/forgot-password` |
|
||||
| **Protected** | `/shop/account/dashboard`, `/shop/account/orders`, `/shop/account/profile`, etc. |
|
||||
|
||||
📖 Full table: QUICK_REFERENCE.md → "ALL SHOP ROUTES"
|
||||
|
||||
---
|
||||
|
||||
## 🔑 Key Files to Know
|
||||
|
||||
### Middleware Files
|
||||
- `middleware/vendor_context.py` - Detects vendor from host/domain/path
|
||||
- `middleware/context_middleware.py` - Determines request context type
|
||||
- `middleware/theme_context.py` - Loads vendor-specific theme
|
||||
- `middleware/logging_middleware.py` - Logs all requests
|
||||
|
||||
### Route Files
|
||||
- `app/routes/shop_pages.py` - Customer shop pages
|
||||
- `app/routes/vendor_pages.py` - Vendor dashboard pages
|
||||
- `app/routes/admin_pages.py` - Admin pages
|
||||
|
||||
### Main Files
|
||||
- `main.py` - FastAPI app setup & middleware chain
|
||||
- `app/core/config.py` - Configuration including platform_domain
|
||||
- `app/api/main.py` - API router configuration
|
||||
|
||||
---
|
||||
|
||||
## ⚡ Most Common Questions
|
||||
|
||||
### Q: What URL do I give to a customer?
|
||||
|
||||
**A:** Depends on deployment mode:
|
||||
- Subdomain: `https://vendor-name.wizamart.com/shop/products`
|
||||
- Custom Domain: `https://vendor-domain.com/shop/products`
|
||||
- Development: `http://localhost:8000/vendor/vendor-code/shop/products`
|
||||
|
||||
📖 Reference: COMPLETE_SUMMARY.txt → "TL;DR"
|
||||
|
||||
---
|
||||
|
||||
### Q: How does the system know which vendor to show?
|
||||
|
||||
**A:** The `vendor_context_middleware` detects the vendor from:
|
||||
1. Custom domain lookup (highest priority)
|
||||
2. Subdomain extraction
|
||||
3. URL path extraction (development)
|
||||
|
||||
📖 Reference: ARCHITECTURE_DIAGRAMS.txt → "VENDOR DETECTION DECISION TREE"
|
||||
|
||||
---
|
||||
|
||||
### Q: How is vendor data isolated?
|
||||
|
||||
**A:** Multi-layer isolation:
|
||||
- Middleware sets request.state.vendor
|
||||
- Database queries filtered by vendor_id
|
||||
- API endpoints validate vendor ownership
|
||||
- Cookies scoped to /shop path
|
||||
|
||||
📖 Reference: COMPLETE_SUMMARY.txt → "VENDOR ISOLATION: 4 LAYERS"
|
||||
|
||||
---
|
||||
|
||||
### Q: How are themes applied per vendor?
|
||||
|
||||
**A:** The `theme_context_middleware`:
|
||||
1. Gets the vendor from middleware chain
|
||||
2. Queries VendorTheme table by vendor_id
|
||||
3. Sets request.state.theme with colors, fonts, logo
|
||||
4. Template uses {{ request.state.theme.* }} to render
|
||||
|
||||
📖 Reference: WIZAMART_MULTITENANT_URL_GUIDE.md → "Theme Integration"
|
||||
|
||||
---
|
||||
|
||||
### Q: What happens if I visit vendor1.wizamart.com but the vendor doesn't exist?
|
||||
|
||||
**A:** The `vendor_context_middleware` returns None, and the request continues. The error handling depends on the route - shop routes will likely return a 404.
|
||||
|
||||
📖 Reference: QUICK_REFERENCE.md → "Debugging Tips"
|
||||
|
||||
---
|
||||
|
||||
### Q: Can customers access multiple vendors' shops?
|
||||
|
||||
**A:** Yes! They just visit different subdomains or custom domains:
|
||||
- `https://acme.wizamart.com/shop/products` → ACME's store
|
||||
- `https://techpro.wizamart.com/shop/products` → TechPro's store
|
||||
|
||||
Each is a completely separate session with different theme, products, auth token.
|
||||
|
||||
📖 Reference: COMPLETE_SUMMARY.txt → "KEY DIFFERENCES BETWEEN MODES"
|
||||
|
||||
---
|
||||
|
||||
## 🐛 Debugging Checklist
|
||||
|
||||
- [ ] Check `request.state.vendor.name` to see detected vendor
|
||||
- [ ] Check `request.state.context_type` to see detected context
|
||||
- [ ] Check server logs for "[OK] Vendor found via..." messages
|
||||
- [ ] Test with curl: `curl -H "Host: acme.wizamart.com" http://localhost:8000/shop`
|
||||
- [ ] Verify vendor exists in database
|
||||
- [ ] Verify VendorTheme exists for vendor
|
||||
- [ ] Check all database queries have `WHERE vendor_id = ?`
|
||||
|
||||
📖 Full debugging guide: QUICK_REFERENCE.md → "Debugging Tips"
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Getting Started
|
||||
|
||||
### For Developers
|
||||
1. Read: **COMPLETE_SUMMARY.txt** (5 min)
|
||||
2. Read: **QUICK_REFERENCE.md** (5 min)
|
||||
3. Look at: **ARCHITECTURE_DIAGRAMS.txt** (5 min)
|
||||
4. Start coding!
|
||||
|
||||
### For DevOps/Deployment
|
||||
1. Read: **WIZARD_MULTITENANT_URL_GUIDE.md** → Deployment sections
|
||||
2. Read: **QUICK_REFERENCE.md** → Deployment Checklist
|
||||
3. Follow checklist for your mode (Subdomain/Custom Domain)
|
||||
|
||||
### For Architects/Decision Makers
|
||||
1. Read: **COMPLETE_SUMMARY.txt** → The Complete Picture
|
||||
2. Read: **ARCHITECTURE_DIAGRAMS.txt** → All diagrams
|
||||
3. Consider: Subdomain vs Custom Domain vs Path-Based
|
||||
|
||||
---
|
||||
|
||||
## 📝 Document Maintenance
|
||||
|
||||
This documentation covers:
|
||||
- **FastAPI** - Backend framework
|
||||
- **SQLAlchemy** - Database ORM
|
||||
- **Jinja2** - Template engine
|
||||
- **Alpine.js** - Frontend reactivity
|
||||
- **Tailwind CSS** - Styling
|
||||
|
||||
Last Updated: November 7, 2025
|
||||
Version: Current Development
|
||||
Status: Complete & Tested
|
||||
|
||||
---
|
||||
|
||||
## 🔗 Related Code References
|
||||
|
||||
### Vendor Detection Logic
|
||||
```python
|
||||
# middleware/vendor_context.py:VendorContextManager.detect_vendor_context()
|
||||
```
|
||||
|
||||
### Theme Loading Logic
|
||||
```python
|
||||
# middleware/theme_context.py:ThemeContextManager.get_vendor_theme()
|
||||
```
|
||||
|
||||
### Context Detection Logic
|
||||
```python
|
||||
# middleware/context_middleware.py:ContextManager.detect_context()
|
||||
```
|
||||
|
||||
### Shop Routes
|
||||
```python
|
||||
# app/routes/shop_pages.py
|
||||
```
|
||||
|
||||
### Main App Setup
|
||||
```python
|
||||
# main.py - See middleware registration order
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💡 Tips
|
||||
|
||||
- **Always test with multiple vendors** to ensure proper isolation
|
||||
- **Check request.state.vendor** in templates to debug vendor detection
|
||||
- **Monitor cross-vendor access attempts** for security
|
||||
- **Use path-based routing** for local development
|
||||
- **Use subdomain routing** for most production scenarios
|
||||
- **Use custom domain** only when vendor pays for premium branding
|
||||
|
||||
---
|
||||
|
||||
## 📞 Quick Reference Card
|
||||
|
||||
```
|
||||
DEPLOYMENT MODES:
|
||||
Subdomain: https://vendor.platform.com/shop/products
|
||||
Custom Domain: https://vendor-domain.com/shop/products
|
||||
Path-Based: http://localhost:8000/vendor/vendor-code/shop/products
|
||||
|
||||
MIDDLEWARE CHAIN:
|
||||
1. vendor_context_middleware → request.state.vendor
|
||||
2. context_middleware → request.state.context_type
|
||||
3. theme_context_middleware → request.state.theme
|
||||
|
||||
VENDOR ISOLATION:
|
||||
URL Detection → Middleware → Database Queries → API Authorization
|
||||
|
||||
AUTHENTICATION:
|
||||
JWT in header + customer_token cookie (path=/shop)
|
||||
|
||||
SHOP ROUTES:
|
||||
Public: /shop/*, /shop/products, /shop/categories/{slug}
|
||||
Auth: /shop/account/register, /shop/account/login
|
||||
Protected: /shop/account/dashboard, /shop/account/orders
|
||||
|
||||
DATABASE:
|
||||
Every table has vendor_id for multi-tenant isolation
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Need help?** Check the relevant documentation file above, or search for your specific question using the "Most Common Questions" section.
|
||||
@@ -0,0 +1,357 @@
|
||||
# Wizamart Multi-Tenant URL Routing - QUICK REFERENCE
|
||||
|
||||
## TL;DR - Customer Shop URLs
|
||||
|
||||
### Production
|
||||
```
|
||||
Subdomain: https://acme.wizamart.com/shop/products
|
||||
Custom Domain: https://store.acme-corp.com/shop/products
|
||||
```
|
||||
|
||||
### Development
|
||||
```
|
||||
Path-Based: http://localhost:8000/vendor/acme/shop/products
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## URL Breakdown
|
||||
|
||||
### Subdomain Mode
|
||||
```
|
||||
https://acme.wizamart.com/shop/products
|
||||
↑ ↑ ↑ ↑
|
||||
| | | └─ Route: /shop/products
|
||||
| | └────── Context: /shop (shop frontend)
|
||||
| └─────────────────────── Platform domain
|
||||
└──────────────────────────── Vendor subdomain (from vendors.subdomain)
|
||||
```
|
||||
|
||||
### Custom Domain Mode
|
||||
```
|
||||
https://store.acme-corp.com/shop/products
|
||||
↑ ↑ ↑
|
||||
| | └─ Route: /shop/products
|
||||
| └────── Context: /shop (shop frontend)
|
||||
└──────────────────────── Custom domain (from vendor_domains.domain)
|
||||
```
|
||||
|
||||
### Path-Based Mode (Dev)
|
||||
```
|
||||
http://localhost:8000/vendor/acme/shop/products
|
||||
↑ ↑ ↑ ↑ ↑
|
||||
| | | | └─ Route: /shop/products
|
||||
| | | └────── Context: /shop
|
||||
| | └─────────── Vendor code: acme
|
||||
| └────────────────── Development prefix: /vendor
|
||||
└─────────────────────────────── Localhost
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Request Flow (In Order)
|
||||
|
||||
```
|
||||
1. Request arrives
|
||||
↓
|
||||
2. vendor_context_middleware
|
||||
→ Detects vendor from host/path
|
||||
→ Sets request.state.vendor = Vendor(...)
|
||||
↓
|
||||
3. context_middleware
|
||||
→ Determines RequestContext.SHOP
|
||||
→ Sets request.state.context_type
|
||||
↓
|
||||
4. theme_context_middleware
|
||||
→ Loads vendor theme
|
||||
→ Sets request.state.theme = {...}
|
||||
↓
|
||||
5. FastAPI route matching
|
||||
→ Matches /shop/products
|
||||
↓
|
||||
6. Handler executes
|
||||
→ Returns HTML template
|
||||
↓
|
||||
7. Template renders
|
||||
→ Uses vendor theme
|
||||
→ Uses vendor name
|
||||
↓
|
||||
8. JavaScript API call
|
||||
→ fetch(/api/v1/public/vendors/{vendor_id}/products)
|
||||
↓
|
||||
9. Response sent to browser
|
||||
→ Fully branded shop
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Detection Priority (Highest to Lowest)
|
||||
|
||||
```
|
||||
1️⃣ Custom Domain (e.g., store.acme-corp.com)
|
||||
└─ Queries: vendor_domains WHERE domain = 'store.acme-corp.com'
|
||||
|
||||
2️⃣ Subdomain (e.g., acme.wizamart.com)
|
||||
└─ Queries: vendors WHERE subdomain = 'acme'
|
||||
|
||||
3️⃣ Path-Based (e.g., /vendor/acme/...)
|
||||
└─ Queries: vendors WHERE subdomain = 'acme'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## All Shop Routes
|
||||
|
||||
| Path | Handler | Auth Required | Purpose |
|
||||
|------|---------|---|---------|
|
||||
| `/shop/` | `shop_products_page()` | ❌ | Homepage |
|
||||
| `/shop/products` | `shop_products_page()` | ❌ | Product catalog |
|
||||
| `/shop/products/{id}` | `shop_product_detail_page()` | ❌ | Product detail |
|
||||
| `/shop/categories/{slug}` | `shop_category_page()` | ❌ | Category products |
|
||||
| `/shop/cart` | `shop_cart_page()` | ❌ | Shopping cart |
|
||||
| `/shop/checkout` | `shop_checkout_page()` | ❌ | Checkout |
|
||||
| `/shop/search` | `shop_search_page()` | ❌ | Search results |
|
||||
| `/shop/account/register` | `shop_register_page()` | ❌ | Customer registration |
|
||||
| `/shop/account/login` | `shop_login_page()` | ❌ | Customer login |
|
||||
| `/shop/account/dashboard` | `shop_account_dashboard_page()` | ✅ | Account dashboard |
|
||||
| `/shop/account/orders` | `shop_orders_page()` | ✅ | Order history |
|
||||
| `/shop/account/orders/{id}` | `shop_order_detail_page()` | ✅ | Order details |
|
||||
| `/shop/account/profile` | `shop_profile_page()` | ✅ | Profile |
|
||||
| `/shop/account/addresses` | `shop_addresses_page()` | ✅ | Address management |
|
||||
| `/shop/account/wishlist` | `shop_wishlist_page()` | ✅ | Wishlist |
|
||||
| `/shop/account/settings` | `shop_settings_page()` | ✅ | Account settings |
|
||||
| `/shop/about` | `shop_about_page()` | ❌ | About us |
|
||||
| `/shop/contact` | `shop_contact_page()` | ❌ | Contact |
|
||||
| `/shop/privacy` | `shop_privacy_page()` | ❌ | Privacy policy |
|
||||
| `/shop/terms` | `shop_terms_page()` | ❌ | Terms of service |
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints (Vendor-Scoped)
|
||||
|
||||
All public API endpoints follow this pattern:
|
||||
|
||||
```
|
||||
GET /api/v1/public/vendors/{vendor_id}/products
|
||||
GET /api/v1/public/vendors/{vendor_id}/products/{product_id}
|
||||
GET /api/v1/public/vendors/{vendor_id}/products/search
|
||||
POST /api/v1/public/vendors/{vendor_id}/cart
|
||||
```
|
||||
|
||||
These work from **ANY** domain/subdomain because `vendor_id` is explicit.
|
||||
|
||||
---
|
||||
|
||||
## Request State Contents
|
||||
|
||||
After middleware chain, handler receives:
|
||||
|
||||
```python
|
||||
request.state.vendor = Vendor(
|
||||
id=1,
|
||||
name="ACME Store",
|
||||
subdomain="acme",
|
||||
is_active=True,
|
||||
# ... other fields
|
||||
)
|
||||
|
||||
request.state.vendor_context = {
|
||||
"detection_method": "subdomain", # or "custom_domain" or "path"
|
||||
"subdomain": "acme", # if subdomain/path mode
|
||||
"domain": "store.acme-corp.com", # if custom_domain mode
|
||||
"host": "acme.wizamart.com" # original host
|
||||
}
|
||||
|
||||
request.state.context_type = RequestContext.SHOP # or ADMIN, VENDOR_DASHBOARD, etc.
|
||||
|
||||
request.state.theme = {
|
||||
"theme_name": "modern",
|
||||
"colors": {
|
||||
"primary": "#FF6B6B",
|
||||
"secondary": "#FF8787",
|
||||
# ...
|
||||
},
|
||||
"branding": {
|
||||
"logo": "acme-logo.png",
|
||||
"favicon": "acme-favicon.ico",
|
||||
# ...
|
||||
},
|
||||
"fonts": {
|
||||
"heading": "Poppins, sans-serif",
|
||||
"body": "Inter, sans-serif"
|
||||
}
|
||||
}
|
||||
|
||||
request.state.clean_path = "/shop/products" # For path-based dev mode
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Vendor Isolation (Multi-Layer)
|
||||
|
||||
```
|
||||
Layer 1: URL Detection
|
||||
├─ Subdomain: vendor from host
|
||||
├─ Custom domain: vendor from VendorDomain table
|
||||
└─ Path: vendor from URL path
|
||||
|
||||
Layer 2: Middleware
|
||||
└─ request.state.vendor set to correct Vendor object
|
||||
|
||||
Layer 3: Database Queries
|
||||
└─ WHERE vendor_id = ?
|
||||
(EVERY query includes vendor filter)
|
||||
|
||||
Layer 4: API Authorization
|
||||
└─ Verify vendor matches request context
|
||||
(No cross-vendor access possible)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Scenarios
|
||||
|
||||
### Scenario 1: Customer on ACME's subdomain
|
||||
```
|
||||
URL: https://acme.wizamart.com/shop/products
|
||||
Vendor detected: Vendor(id=1, subdomain="acme")
|
||||
Database query: SELECT products WHERE vendor_id = 1 AND is_active = true
|
||||
Result: ACME's products
|
||||
```
|
||||
|
||||
### Scenario 2: Customer on TechPro's custom domain
|
||||
```
|
||||
URL: https://shop.techpro.io/shop/products
|
||||
Vendor detected: Via VendorDomain(domain="shop.techpro.io", vendor_id=2)
|
||||
Database query: SELECT products WHERE vendor_id = 2 AND is_active = true
|
||||
Result: TechPro's products
|
||||
```
|
||||
|
||||
### Scenario 3: Developer testing locally
|
||||
```
|
||||
URL: http://localhost:8000/vendor/acme/shop/products
|
||||
Vendor detected: Vendor(id=1, subdomain="acme") via /vendor/acme/
|
||||
Database query: SELECT products WHERE vendor_id = 1
|
||||
Result: ACME's products
|
||||
```
|
||||
|
||||
### Scenario 4: Cross-vendor attack attempt
|
||||
```
|
||||
Attack: Customer on acme.wizamart.com tries to access TechPro's API
|
||||
URL: https://acme.wizamart.com/api/v1/public/vendors/2/products
|
||||
Backend check: Is vendor_id in request (2) same as request.state.vendor.id (1)?
|
||||
Result: ❌ UnauthorizedShopAccessException
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Middleware Files
|
||||
|
||||
```
|
||||
middleware/
|
||||
├─ vendor_context.py ← Detects vendor from host/path
|
||||
├─ theme_context.py ← Loads vendor theme
|
||||
├─ context_middleware.py ← Determines request context type
|
||||
└─ logging_middleware.py ← Logs all requests
|
||||
```
|
||||
|
||||
## Route Files
|
||||
|
||||
```
|
||||
app/routes/
|
||||
├─ shop_pages.py ← Customer shop pages
|
||||
├─ vendor_pages.py ← Vendor dashboard pages
|
||||
└─ admin_pages.py ← Admin pages
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Environment Configuration
|
||||
|
||||
```python
|
||||
# app/core/config.py
|
||||
|
||||
platform_domain = "wizamart.com" # Platform domain for subdomains
|
||||
|
||||
# Allows all *.wizamart.com subdomains
|
||||
allowed_hosts = ["*"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Development vs Production
|
||||
|
||||
### Development
|
||||
```
|
||||
HTTP (not HTTPS - no SSL)
|
||||
localhost:8000
|
||||
Path-based routing: /vendor/vendor_code/...
|
||||
No DNS setup needed
|
||||
All vendors share same localhost
|
||||
```
|
||||
|
||||
### Production
|
||||
```
|
||||
HTTPS (required - SSL certs)
|
||||
Subdomain: *.wizamart.com (wildcard SSL cert)
|
||||
Custom: vendor.domain.com (vendor's SSL cert)
|
||||
DNS setup needed for subdomains/custom domains
|
||||
Vendors have separate endpoints
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Debugging Tips
|
||||
|
||||
### Check which vendor was detected
|
||||
```python
|
||||
# In template or handler:
|
||||
{{ request.state.vendor.name }} # "ACME Store"
|
||||
{{ request.state.vendor.subdomain }} # "acme"
|
||||
{{ request.state.context_type }} # "shop"
|
||||
```
|
||||
|
||||
### Check which theme was loaded
|
||||
```python
|
||||
# In template:
|
||||
{{ request.state.theme.colors.primary }} # "#FF6B6B"
|
||||
{{ request.state.theme.branding.logo }} # "acme-logo.png"
|
||||
```
|
||||
|
||||
### Check vendor detection logs
|
||||
```bash
|
||||
# In server logs, look for:
|
||||
[OK] Vendor found via subdomain: acme -> ACME Store
|
||||
[WARNING] Vendor not found for context: {...}
|
||||
```
|
||||
|
||||
### Test with curl
|
||||
```bash
|
||||
# Subdomain mode
|
||||
curl -H "Host: acme.wizamart.com" http://localhost:8000/shop/products
|
||||
|
||||
# Path-based mode
|
||||
curl http://localhost:8000/vendor/acme/shop/products
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Links in Code
|
||||
|
||||
- Vendor detection: `middleware/vendor_context.py:VendorContextManager.detect_vendor_context()`
|
||||
- Theme loading: `middleware/theme_context.py:ThemeContextManager.get_vendor_theme()`
|
||||
- Context detection: `middleware/context_middleware.py:ContextManager.detect_context()`
|
||||
- Shop routes: `app/routes/shop_pages.py`
|
||||
- Main app setup: `main.py`
|
||||
|
||||
---
|
||||
|
||||
## Reference
|
||||
|
||||
- Platform: Wizamart
|
||||
- Mode: Multi-tenant SaaS e-commerce
|
||||
- Framework: FastAPI + SQLAlchemy + Jinja2
|
||||
- Frontend: Alpine.js + Tailwind CSS
|
||||
- Auth: JWT + Vendor-scoped cookies
|
||||
|
||||
Generated: November 7, 2025
|
||||
@@ -0,0 +1,419 @@
|
||||
# Wizamart Multi-Tenant URL Routing Guide
|
||||
|
||||
## Quick Answer
|
||||
|
||||
**How do customers access a vendor's shop in Wizamart?**
|
||||
|
||||
There are three ways depending on the deployment mode:
|
||||
|
||||
### 1. **SUBDOMAIN MODE** (Production - Recommended)
|
||||
```
|
||||
https://VENDOR_SUBDOMAIN.platform.com/shop/products
|
||||
|
||||
Example:
|
||||
https://acme.wizamart.com/shop/products
|
||||
https://techpro.wizamart.com/shop/categories/electronics
|
||||
```
|
||||
|
||||
### 2. **CUSTOM DOMAIN MODE** (Production - Premium)
|
||||
```
|
||||
https://VENDOR_CUSTOM_DOMAIN/shop/products
|
||||
|
||||
Example:
|
||||
https://store.acmecorp.com/shop/products
|
||||
https://shop.techpro.io/shop/cart
|
||||
```
|
||||
|
||||
### 3. **PATH-BASED MODE** (Development Only)
|
||||
```
|
||||
http://localhost:PORT/vendor/VENDOR_CODE/shop/products
|
||||
|
||||
Example:
|
||||
http://localhost:8000/vendor/acme/shop/products
|
||||
http://localhost:8000/vendor/techpro/shop/checkout
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Three Deployment Modes Explained
|
||||
|
||||
### 1. SUBDOMAIN MODE (Production - Recommended)
|
||||
|
||||
**URL Pattern:** `https://VENDOR_SUBDOMAIN.platform.com/shop/...`
|
||||
|
||||
**Example:**
|
||||
- Vendor subdomain: `acme`
|
||||
- Platform domain: `wizamart.com`
|
||||
- Customer Shop URL: `https://acme.wizamart.com/shop/products`
|
||||
- Product Detail: `https://acme.wizamart.com/shop/products/123`
|
||||
|
||||
**How It Works:**
|
||||
1. Customer visits `https://acme.wizamart.com/shop/products`
|
||||
2. `vendor_context_middleware` detects subdomain `"acme"`
|
||||
3. Queries: `SELECT * FROM vendors WHERE subdomain = 'acme'`
|
||||
4. Finds Vendor with ID=1 (ACME Store)
|
||||
5. Sets `request.state.vendor = Vendor(ACME Store)`
|
||||
6. `context_middleware` detects it's a SHOP request
|
||||
7. `theme_context_middleware` loads ACME's theme
|
||||
8. Routes to `shop_pages.py` → `shop_products_page()`
|
||||
9. Renders template with ACME's colors, logo, and products
|
||||
|
||||
**Advantages:**
|
||||
- Single SSL certificate for all vendors (*.wizamart.com)
|
||||
- Easy to manage DNS (just add subdomains)
|
||||
- Customers don't need to bring their own domain
|
||||
|
||||
---
|
||||
|
||||
### 2. CUSTOM DOMAIN MODE (Production - Premium)
|
||||
|
||||
**URL Pattern:** `https://CUSTOM_DOMAIN/shop/...`
|
||||
|
||||
**Example:**
|
||||
- Vendor name: "ACME Store"
|
||||
- Custom domain: `store.acme-corp.com`
|
||||
- Customer Shop URL: `https://store.acme-corp.com/shop/products`
|
||||
|
||||
**Database Setup:**
|
||||
```sql
|
||||
-- vendors table
|
||||
id | name | subdomain
|
||||
1 | ACME Store | acme
|
||||
|
||||
-- vendor_domains table (links custom domains to vendors)
|
||||
id | vendor_id | domain | is_active | is_verified
|
||||
1 | 1 | store.acme-corp.com | true | true
|
||||
```
|
||||
|
||||
**How It Works:**
|
||||
1. Customer visits `https://store.acme-corp.com/shop/products`
|
||||
2. `vendor_context_middleware` detects custom domain (not *.wizamart.com, not localhost)
|
||||
3. Normalizes domain to `"store.acme-corp.com"`
|
||||
4. Queries: `SELECT * FROM vendor_domains WHERE domain = 'store.acme-corp.com'`
|
||||
5. Finds `VendorDomain` with `vendor_id = 1`
|
||||
6. Joins to get `Vendor(ACME Store)`
|
||||
7. Rest is same as subdomain mode...
|
||||
|
||||
**Advantages:**
|
||||
- Professional branding with vendor's own domain
|
||||
- Better for premium vendors
|
||||
- Vendor controls the domain
|
||||
|
||||
**Considerations:**
|
||||
- Each vendor needs their own SSL certificate
|
||||
- Vendor must own and configure the domain
|
||||
|
||||
---
|
||||
|
||||
### 3. PATH-BASED MODE (Development Only)
|
||||
|
||||
**URL Pattern:** `http://localhost:PORT/vendor/VENDOR_CODE/shop/...`
|
||||
|
||||
**Example:**
|
||||
- Development: `http://localhost:8000/vendor/acme/shop/products`
|
||||
- With port: `http://localhost:8000/vendor/acme/shop/products/123`
|
||||
|
||||
**How It Works:**
|
||||
1. Developer visits `http://localhost:8000/vendor/acme/shop/products`
|
||||
2. `vendor_context_middleware` detects path-based routing pattern `/vendor/acme/...`
|
||||
3. Extracts vendor code `"acme"` from the path
|
||||
4. Looks up Vendor: `SELECT * FROM vendors WHERE subdomain = 'acme'`
|
||||
5. Sets `request.state.vendor = Vendor(acme)`
|
||||
6. Routes to shop pages
|
||||
|
||||
**Advantages:**
|
||||
- Perfect for local development
|
||||
- No need to configure DNS/domains
|
||||
- Test multiple vendors easily without domain setup
|
||||
|
||||
**Limitations:**
|
||||
- Only for development (not production-ready)
|
||||
- All vendors share same localhost address
|
||||
|
||||
---
|
||||
|
||||
## Complete Route Examples
|
||||
|
||||
### Subdomain/Custom Domain (PRODUCTION)
|
||||
```
|
||||
https://acme.wizamart.com/shop/ → Homepage
|
||||
https://acme.wizamart.com/shop/products → Product Catalog
|
||||
https://acme.wizamart.com/shop/products/123 → Product Detail
|
||||
https://acme.wizamart.com/shop/categories/electronics → Category Page
|
||||
https://acme.wizamart.com/shop/cart → Shopping Cart
|
||||
https://acme.wizamart.com/shop/checkout → Checkout
|
||||
https://acme.wizamart.com/shop/search?q=laptop → Search Results
|
||||
https://acme.wizamart.com/shop/account/login → Customer Login
|
||||
https://acme.wizamart.com/shop/account/dashboard → Account Dashboard (Auth Required)
|
||||
https://acme.wizamart.com/shop/account/orders → Order History (Auth Required)
|
||||
https://acme.wizamart.com/shop/account/profile → Profile (Auth Required)
|
||||
```
|
||||
|
||||
### Path-Based (DEVELOPMENT)
|
||||
```
|
||||
http://localhost:8000/vendor/acme/shop/ → Homepage
|
||||
http://localhost:8000/vendor/acme/shop/products → Products
|
||||
http://localhost:8000/vendor/acme/shop/products/123 → Product Detail
|
||||
http://localhost:8000/vendor/acme/shop/cart → Cart
|
||||
http://localhost:8000/vendor/acme/shop/checkout → Checkout
|
||||
http://localhost:8000/vendor/acme/shop/account/login → Login
|
||||
```
|
||||
|
||||
### API Endpoints (Same for All Modes)
|
||||
```
|
||||
GET /api/v1/public/vendors/1/products → Get vendor products
|
||||
GET /api/v1/public/vendors/1/products/123 → Get product details
|
||||
POST /api/v1/public/vendors/1/products/{id}/reviews → Add product review
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## How Vendor Isolation Works
|
||||
|
||||
### Multi-Layer Enforcement
|
||||
|
||||
**Layer 1: URL Routing**
|
||||
- Vendor is detected from subdomain, custom domain, or path
|
||||
- Each vendor gets their own request context
|
||||
|
||||
**Layer 2: Middleware**
|
||||
- `request.state.vendor` is set to the detected Vendor object
|
||||
- All downstream code can access the vendor
|
||||
|
||||
**Layer 3: Database Queries**
|
||||
- All queries must include `WHERE vendor_id = ?`
|
||||
- Product queries: `SELECT * FROM products WHERE vendor_id = 1`
|
||||
- Order queries: `SELECT * FROM orders WHERE vendor_id = 1`
|
||||
|
||||
**Layer 4: API Authorization**
|
||||
- Endpoints verify the vendor matches the request vendor
|
||||
- Customers can only see their own vendor's products
|
||||
|
||||
### Example: No Cross-Vendor Leakage
|
||||
```python
|
||||
# Customer on acme.wizamart.com tries to access TechPro's products
|
||||
# They make API call to /api/v1/public/vendors/2/products
|
||||
|
||||
# Backend checks:
|
||||
vendor = get_vendor_from_request(request) # Returns Vendor(id=1, name="ACME")
|
||||
if vendor.id != requested_vendor_id: # if 1 != 2
|
||||
raise UnauthorizedShopAccessException()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Request Lifecycle: Complete Flow
|
||||
|
||||
### Scenario: Customer visits `https://acme.wizamart.com/shop/products`
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 1. REQUEST ARRIVES │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
method: GET
|
||||
host: acme.wizamart.com
|
||||
path: /shop/products
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 2. MIDDLEWARE CHAIN │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
|
||||
A) vendor_context_middleware
|
||||
├─ Detects host: "acme.wizamart.com"
|
||||
├─ Extracts subdomain: "acme"
|
||||
├─ Queries: SELECT * FROM vendors WHERE subdomain = 'acme'
|
||||
└─ Sets: request.state.vendor = Vendor(ACME Store)
|
||||
|
||||
B) context_middleware
|
||||
├─ Checks path: "/shop/products"
|
||||
├─ Has request.state.vendor? YES
|
||||
└─ Sets: request.state.context_type = RequestContext.SHOP
|
||||
|
||||
C) theme_context_middleware
|
||||
├─ Queries: SELECT * FROM vendor_themes WHERE vendor_id = 1
|
||||
└─ Sets: request.state.theme = {...ACME's theme...}
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 3. ROUTE MATCHING │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
Path: /shop/products
|
||||
Matches: @router.get("/shop/products")
|
||||
Handler: shop_products_page(request)
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 4. HANDLER EXECUTES │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
@router.get("/shop/products", response_class=HTMLResponse)
|
||||
async def shop_products_page(request: Request):
|
||||
return templates.TemplateResponse(
|
||||
"shop/products.html",
|
||||
{"request": request}
|
||||
)
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 5. TEMPLATE RENDERS │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
Template accesses:
|
||||
├─ request.state.vendor.name → "ACME Store"
|
||||
├─ request.state.theme.colors.primary → "#FF6B6B"
|
||||
├─ request.state.theme.branding.logo → "acme-logo.png"
|
||||
└─ Products will load via JavaScript API call
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 6. JAVASCRIPT LOADS PRODUCTS (Client-Side) │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
fetch(`/api/v1/public/vendors/1/products`)
|
||||
.then(data => renderProducts(data.products, {theme}))
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 7. RESPONSE SENT │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
HTML with ACME's colors, logo, and products
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Theme Integration
|
||||
|
||||
Each vendor's shop is fully branded with their custom theme:
|
||||
|
||||
```python
|
||||
# Theme loaded for https://acme.wizamart.com
|
||||
request.state.theme = {
|
||||
"theme_name": "modern",
|
||||
"colors": {
|
||||
"primary": "#FF6B6B",
|
||||
"secondary": "#FF8787",
|
||||
"accent": "#FF5252",
|
||||
"background": "#ffffff",
|
||||
"text": "#1f2937"
|
||||
},
|
||||
"branding": {
|
||||
"logo": "acme-logo.png",
|
||||
"favicon": "acme-favicon.ico",
|
||||
"banner": "acme-banner.jpg"
|
||||
},
|
||||
"fonts": {
|
||||
"heading": "Poppins, sans-serif",
|
||||
"body": "Inter, sans-serif"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In Jinja2 template:
|
||||
```html
|
||||
<style>
|
||||
:root {
|
||||
--color-primary: {{ request.state.theme.colors.primary }};
|
||||
--color-secondary: {{ request.state.theme.colors.secondary }};
|
||||
}
|
||||
</style>
|
||||
|
||||
<img src="{{ request.state.theme.branding.logo }}" alt="{{ request.state.vendor.name }}" />
|
||||
<h1 style="font-family: {{ request.state.theme.fonts.heading }}">
|
||||
Welcome to {{ request.state.vendor.name }}
|
||||
</h1>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Key Points for Understanding
|
||||
|
||||
### 1. Customer Perspective
|
||||
- Customers just visit a URL (like any normal e-commerce site)
|
||||
- They have no awareness it's a multi-tenant platform
|
||||
- Each store looks completely separate and branded
|
||||
|
||||
### 2. Vendor Perspective
|
||||
- Vendors can use a subdomain (free/standard): `acme.wizamart.com`
|
||||
- Or their own custom domain (premium): `store.acme-corp.com`
|
||||
- Both routes go to the exact same backend code
|
||||
|
||||
### 3. Developer Perspective
|
||||
- The middleware layer detects which vendor is being accessed
|
||||
- All business logic remains vendor-unaware
|
||||
- Database queries automatically filtered by vendor
|
||||
- No risk of data leakage because of multi-layer isolation
|
||||
|
||||
### 4. Tech Stack
|
||||
- **Frontend:** Jinja2 templates + Alpine.js + Tailwind CSS
|
||||
- **Backend:** FastAPI + SQLAlchemy
|
||||
- **Auth:** JWT with vendor-scoped cookies
|
||||
- **Database:** All tables have `vendor_id` foreign key
|
||||
|
||||
---
|
||||
|
||||
## Potential Issue: Path-Based Development Mode
|
||||
|
||||
⚠️ **Current Implementation Gap:**
|
||||
|
||||
The `vendor_context_middleware` sets `clean_path` for path-based URLs, but this isn't used for FastAPI routing.
|
||||
|
||||
**Problem:**
|
||||
- Incoming: `GET http://localhost:8000/vendor/acme/shop/products`
|
||||
- Routes registered: `@router.get("/shop/products")`
|
||||
- FastAPI tries to match `/vendor/acme/shop/products` against `/shop/products`
|
||||
- Result: ❌ 404 Not Found
|
||||
|
||||
**Solution (Recommended):**
|
||||
|
||||
Add a path rewriting middleware in `main.py`:
|
||||
|
||||
```python
|
||||
async def path_rewrite_middleware(request: Request, call_next):
|
||||
"""Rewrite path for path-based vendor routing in development mode."""
|
||||
if hasattr(request.state, 'clean_path'):
|
||||
# Replace request path for FastAPI routing
|
||||
request._url = request._url.replace(path=request.state.clean_path)
|
||||
return await call_next(request)
|
||||
|
||||
# In main.py, add after vendor_context_middleware:
|
||||
app.middleware("http")(path_rewrite_middleware)
|
||||
```
|
||||
|
||||
Or alternatively, mount the router twice:
|
||||
```python
|
||||
app.include_router(shop_pages.router, prefix="/shop")
|
||||
app.include_router(shop_pages.router, prefix="/vendor") # Allows /vendor/* paths
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Authentication in Multi-Tenant Shop
|
||||
|
||||
Customer authentication uses vendor-scoped cookies:
|
||||
|
||||
```python
|
||||
# Login sets cookie scoped to vendor's shop
|
||||
Set-Cookie: customer_token=eyJ...; Path=/shop; HttpOnly; SameSite=Lax
|
||||
|
||||
# This prevents:
|
||||
# - Tokens leaking across vendors
|
||||
# - Cross-site request forgery
|
||||
# - Cookie scope confusion in multi-tenant setup
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Summary Table
|
||||
|
||||
| Mode | URL | Use Case | SSL | DNS |
|
||||
|------|-----|----------|-----|-----|
|
||||
| Subdomain | `vendor.platform.com/shop` | Production (standard) | *.platform.com | Add subdomains |
|
||||
| Custom Domain | `vendor-domain.com/shop` | Production (premium) | Per vendor | Vendor configures |
|
||||
| Path-Based | `localhost:8000/vendor/v/shop` | Development only | None | None |
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **For Production:** Use subdomain or custom domain mode
|
||||
2. **For Development:** Use path-based mode locally
|
||||
3. **For Deployment:** Configure DNS for subdomains or custom domains
|
||||
4. **For Testing:** Create test vendors with different themes
|
||||
5. **For Scaling:** Consider CDN for vendor-specific assets
|
||||
|
||||
---
|
||||
|
||||
Generated: November 7, 2025
|
||||
Wizamart Version: Current Development
|
||||
Reference in New Issue
Block a user