# Custom Domain Implementation Guide

## Overview

This guide explains how to implement custom domain support for your multi-tenant e-commerce platform, allowing vendors to use their own domains (e.g., `customdomain1.com`) instead of subdomains (`vendor1.platform.com`).

## Architecture Summary

### Current Routing (What You Have)
```
Development: localhost:8000/vendor/vendor1/ → Vendor 1
Production:  vendor1.platform.com → Vendor 1
Admin:       admin.platform.com → Admin Panel
```

### New Routing (What You're Adding)
```
Custom Domain: customdomain1.com → Vendor 1
Custom Domain: shop.mybrand.com → Vendor 2
Subdomain:     vendor1.platform.com → Vendor 1 (still works!)
Path-based:    localhost:8000/vendor/vendor1/ → Vendor 1 (still works!)
```

## How It Works

### 1. Domain Mapping Flow

```
Customer visits customdomain1.com
         ↓
DNS routes to your server
         ↓
Middleware reads Host header: "customdomain1.com"
         ↓
Queries vendor_domains table: WHERE domain = 'customdomain1.com'
         ↓
Finds VendorDomain record → vendor_id = 1
         ↓
Loads Vendor 1 data
         ↓
Sets request.state.vendor = Vendor 1
         ↓
All queries automatically scoped to Vendor 1
         ↓
Customer sees Vendor 1's shop
```

### 2. Priority Order

The middleware checks in this order:

1. **Custom Domain** (highest priority)
   - Checks if host is NOT platform.com/localhost
   - Queries `vendor_domains` table
   
2. **Subdomain**
   - Checks if host matches `*.platform.com`
   - Queries `vendors.subdomain` field
   
3. **Path-based** (lowest priority - dev only)
   - Checks if path starts with `/vendor/`
   - Queries `vendors.subdomain` field

## Implementation Steps

### Step 1: Add Database Table

Create `vendor_domains` table to store custom domain mappings:

```sql
CREATE TABLE vendor_domains (
    id SERIAL PRIMARY KEY,
    vendor_id INTEGER NOT NULL REFERENCES vendors(id) ON DELETE CASCADE,
    domain VARCHAR(255) NOT NULL UNIQUE,
    is_primary BOOLEAN DEFAULT FALSE,
    is_active BOOLEAN DEFAULT TRUE,
    is_verified BOOLEAN DEFAULT FALSE,
    verification_token VARCHAR(100) UNIQUE,
    verified_at TIMESTAMP WITH TIME ZONE,
    ssl_status VARCHAR(50) DEFAULT 'pending',
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

CREATE INDEX idx_domain_active ON vendor_domains(domain, is_active);
```

**Files to create:**
- `models/database/vendor_domain.py` - Model definition
- `alembic/versions/XXX_add_vendor_domains.py` - Migration

### Step 2: Update Vendor Model

Add relationship to domains:

```python
# models/database/vendor.py
class Vendor(Base):
    # ... existing fields ...
    
    domains = relationship(
        "VendorDomain",
        back_populates="vendor",
        cascade="all, delete-orphan"
    )
```

### Step 3: Update Middleware

The middleware needs to check custom domains BEFORE subdomains.

**Key changes in `middleware/vendor_context.py`:**

```python
def detect_vendor_context(request: Request):
    host = request.headers.get("host", "")
    
    # NEW: Check if it's a custom domain
    if is_custom_domain(host):
        return {
            "domain": normalize_domain(host),
            "detection_method": "custom_domain"
        }
    
    # EXISTING: Check subdomain
    if is_subdomain(host):
        return {
            "subdomain": extract_subdomain(host),
            "detection_method": "subdomain"
        }
    
    # EXISTING: Check path
    if is_path_based(request.path):
        return {
            "subdomain": extract_from_path(request.path),
            "detection_method": "path"
        }
```

### Step 4: Create Admin Interface

Create API endpoints for managing domains:

```python
POST   /api/v1/admin/vendors/{vendor_id}/domains  - Add domain
GET    /api/v1/admin/vendors/{vendor_id}/domains  - List domains
PUT    /api/v1/admin/domains/{domain_id}          - Update domain
DELETE /api/v1/admin/domains/{domain_id}          - Remove domain
POST   /api/v1/admin/domains/{domain_id}/verify   - Verify ownership
```

**Files to create:**
- `app/api/v1/admin/vendor_domains.py` - API endpoints
- `app/templates/admin/vendor_domains.html` - HTML interface

### Step 5: DNS Verification

To prevent domain hijacking, verify the vendor owns the domain:

**Verification Flow:**
1. Vendor adds domain in admin panel
2. System generates verification token: `abc123xyz`
3. Vendor adds DNS TXT record:
   ```
   Name:  _wizamart-verify.customdomain1.com
   Type:  TXT
   Value: abc123xyz
   ```
4. Admin clicks "Verify" button
5. System queries DNS, checks for token
6. If found, marks domain as verified

**Code:**
```python
@router.post("/domains/{domain_id}/verify")
def verify_domain(domain_id: int, db: Session):
    domain = db.query(VendorDomain).get(domain_id)
    
    # Query DNS for TXT record
    txt_records = dns.resolver.resolve(
        f"_wizamart-verify.{domain.domain}", 
        'TXT'
    )
    
    # Check if token matches
    for txt in txt_records:
        if txt.to_text() == domain.verification_token:
            domain.is_verified = True
            db.commit()
            return {"message": "Verified!"}
    
    raise HTTPException(400, "Token not found")
```

### Step 6: DNS Configuration

Vendor must configure their domain's DNS:

**Option A: Point to Platform (Simple)**
```
Type: A
Name: @
Value: 123.45.67.89  (your server IP)

Type: A  
Name: www
Value: 123.45.67.89
```

**Option B: CNAME to Platform (Better)**
```
Type: CNAME
Name: @
Value: platform.com

Type: CNAME
Name: www  
Value: platform.com
```

**Option C: Cloudflare Proxy (Best)**
```
Enable Cloudflare proxy → Automatic SSL + CDN
```

### Step 7: SSL/TLS Certificates

For HTTPS on custom domains, you need SSL certificates.

**Option A: Wildcard Certificate (Simplest for subdomains)**
```bash
# Only works for *.platform.com
certbot certonly --dns-cloudflare \
  -d "*.platform.com" \
  -d "platform.com"
```

**Option B: Let's Encrypt with Certbot (Per-domain)**
```bash
# For each custom domain
certbot certonly --webroot \
  -w /var/www/html \
  -d customdomain1.com \
  -d www.customdomain1.com
```

**Option C: Cloudflare (Recommended)**
- Vendor uses Cloudflare for their domain
- Cloudflare provides SSL automatically
- No server-side certificate management needed

**Option D: AWS Certificate Manager**
- Use with AWS ALB/CloudFront
- Automatic certificate provisioning
- Free SSL certificates

### Step 8: Web Server Configuration

Configure Nginx/Apache to handle multiple domains:

**Nginx Configuration:**
```nginx
# /etc/nginx/sites-available/platform

server {
    listen 80;
    listen 443 ssl http2;
    
    # Accept ANY domain
    server_name _;
    
    # SSL configuration
    ssl_certificate /etc/letsencrypt/live/platform.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/platform.com/privkey.pem;
    
    # Pass Host header to FastAPI
    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
```

**Key Points:**
- `server_name _;` accepts ALL domains
- `proxy_set_header Host $host;` passes domain to FastAPI
- FastAPI middleware reads the Host header to identify vendor

## Testing Guide

### Test 1: Subdomain (Existing)
```bash
# Development
curl -H "Host: vendor1.localhost:8000" http://localhost:8000/

# Production
curl https://vendor1.platform.com/
```

### Test 2: Custom Domain (New)
```bash
# Add to /etc/hosts for testing:
127.0.0.1 customdomain1.com

# Test locally
curl -H "Host: customdomain1.com" http://localhost:8000/

# Production
curl https://customdomain1.com/
```

### Test 3: Path-based (Development)
```bash
curl http://localhost:8000/vendor/vendor1/
```

## Data Flow Example

### Example: Customer visits customdomain1.com

**Step 1: DNS Resolution**
```
customdomain1.com → 123.45.67.89 (your server)
```

**Step 2: HTTP Request**
```
GET / HTTP/1.1
Host: customdomain1.com
```

**Step 3: Nginx Proxy**
```
Nginx receives request
  ↓
Passes to FastAPI with Host header intact
  ↓
FastAPI receives: Host = "customdomain1.com"
```

**Step 4: Middleware Processing**
```python
# vendor_context_middleware runs
host = "customdomain1.com"

# Detect it's a custom domain
is_custom = not host.endswith("platform.com")  # True

# Query database
vendor_domain = db.query(VendorDomain).filter(
    VendorDomain.domain == "customdomain1.com",
    VendorDomain.is_active == True,
    VendorDomain.is_verified == True
).first()

# vendor_domain.vendor_id = 5
# Load vendor
vendor = db.query(Vendor).get(5)

# Set in request state
request.state.vendor = vendor  # Vendor 5
```

**Step 5: Route Handler**
```python
@router.get("/")
def shop_home(request: Request):
    vendor = request.state.vendor  # Vendor 5
    
    # All queries automatically filtered to Vendor 5
    products = db.query(Product).filter(
        Product.vendor_id == vendor.id
    ).all()
    
    return templates.TemplateResponse("shop/home.html", {
        "request": request,
        "vendor": vendor,
        "products": products
    })
```

**Step 6: Response**
```
Customer sees Vendor 5's shop at customdomain1.com
```

## Security Considerations

### 1. Domain Verification
- ALWAYS verify domain ownership via DNS TXT records
- Never allow unverified domains to go live
- Prevents domain hijacking

### 2. SSL/TLS
- Require HTTPS for custom domains
- Validate SSL certificates
- Monitor certificate expiration

### 3. Vendor Isolation
- Double-check vendor_id in all queries
- Never trust user input for vendor selection
- Always use `request.state.vendor`

### 4. Rate Limiting
- Limit domain additions per vendor
- Prevent DNS verification spam
- Monitor failed verification attempts

### 5. Reserved Domains
- Block platform.com and subdomains
- Block reserved names (admin, api, www, mail)
- Validate domain format

## Common Issues & Solutions

### Issue 1: "Domain not found"
**Cause:** DNS not pointing to your server
**Solution:** Check DNS A/CNAME records

### Issue 2: SSL certificate error
**Cause:** No certificate for custom domain
**Solution:** Use Cloudflare or provision certificate

### Issue 3: Vendor not detected
**Cause:** Domain not in vendor_domains table
**Solution:** Add domain via admin panel and verify

### Issue 4: Wrong vendor loaded
**Cause:** Multiple vendors with same domain
**Solution:** Enforce unique constraint on domain column

### Issue 5: Verification fails
**Cause:** DNS TXT record not found
**Solution:** Wait for DNS propagation (5-15 minutes)

## Deployment Checklist

- [ ] Database migration applied
- [ ] Vendor model updated with domains relationship
- [ ] Middleware updated to check custom domains
- [ ] Admin API endpoints created
- [ ] Admin UI created for domain management
- [ ] DNS verification implemented
- [ ] Web server configured to accept all domains
- [ ] SSL certificate strategy decided
- [ ] Testing completed (subdomain + custom domain)
- [ ] Documentation updated
- [ ] Monitoring configured
- [ ] Rate limiting added

## Future Enhancements

### 1. Automatic SSL Provisioning
Use certbot or ACME protocol to automatically provision SSL certificates when vendor adds domain.

### 2. Domain Status Dashboard
Show vendors their domain status:
- DNS configuration status
- SSL certificate status
- Traffic analytics per domain

### 3. Multiple Domains per Vendor
Allow vendors to have multiple custom domains pointing to same shop.

### 4. Domain Transfer
Allow transferring domain from one vendor to another.

### 5. Subdomain Customization
Let vendors choose their subdomain: `mybrand.platform.com`

## Summary

**What you're adding:**
1. Database table to store domain → vendor mappings
2. Middleware logic to detect custom domains
3. Admin interface to manage domains
4. DNS verification system
5. Web server configuration for multi-domain

**What stays the same:**
- Existing subdomain routing still works
- Path-based routing still works for development
- Vendor isolation and security
- All existing functionality

**Result:**
Vendors can use their own domains while you maintain a single codebase with multi-tenant architecture!
