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