# Administrator Setup Guide: Custom Domains

Quick reference for system administrators setting up the custom domain system.

## Prerequisites Checklist

- [ ] OpenLiteSpeed installed and running
- [ ] PHP 8.1+ with required extensions
- [ ] Laravel application deployed
- [ ] Database migrations run
- [ ] Server has public IP address
- [ ] Firewall allows ports 80 and 443
- [ ] (Optional) Cloudflare account for SSL

---

## Quick Setup (5 Minutes)

### 1. Run Migrations

```bash
cd /path/to/multistore
php artisan migrate
```

This creates the `seller_domains` table.

### 2. Configure OpenLiteSpeed Virtual Host

Edit: `/usr/local/lsws/conf/vhosts/[yoursite]/vhconf.conf`

```apache
vhDomain                  $VH_NAME, *
```

The wildcard `*` accepts all domains.

### 3. Configure Listener

Edit: `/usr/local/lsws/conf/httpd_config.conf`

```apache
listener HTTP {
  address                 *:80
  secure                  0
  map                     [yoursite] *
}

listener HTTPS {
  address                 *:443
  secure                  1
  keyFile                 /path/to/ssl/privkey.pem
  certFile                /path/to/ssl/fullchain.pem
  certChain               1
  map                     [yoursite] *
}
```

### 4. Restart OpenLiteSpeed

```bash
/usr/local/lsws/bin/lswsctrl restart
```

### 5. Test the System

```bash
# Check middleware is loaded
php artisan route:list | grep DetectCustomDomain

# Test in tinker
php artisan tinker
>>> $domain = new \App\Models\SellerDomain();
>>> $domain->fill(['user_id' => 1, 'domain' => 'test.com', 'is_active' => true, 'verified_at' => now()]);
>>> $domain->save();
>>> \App\Models\SellerDomain::first();
```

---

## Complete Configuration

### OpenLiteSpeed Virtual Host (Full Config)

`/usr/local/lsws/conf/vhosts/[yoursite]/vhconf.conf`:

```apache
docRoot                   $VH_ROOT/public
vhDomain                  $VH_NAME, *.yourdomain.com, *
enableGzip                1
enableBrCache             1
enableIpGeo               1

errorlog $VH_ROOT/logs/$VH_NAME.error_log {
  useServer               0
  logLevel                ERROR
  rollingSize             10M
}

accesslog $VH_ROOT/logs/$VH_NAME.access_log {
  useServer               0
  logHeaders              5
  rollingSize             10M
  keepDays                30
}

index  {
  useServer               0
  indexFiles              index.php, index.html
}

scripthandler  {
  add                     lsapi:lsphp81 php
}

rewrite  {
  enable                  1
  autoLoadHtaccess        1
  rules                   <<<END_rules
  RewriteEngine On

  # Handle Authorization Header
  RewriteCond %{HTTP:Authorization} .
  RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

  # Redirect Trailing Slashes
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteCond %{REQUEST_URI} (.+)/$
  RewriteRule ^ %1 [L,R=301]

  # Front Controller
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteRule ^ index.php [L]
  END_rules
}
```

### Environment Variables

`.env`:

```env
APP_URL=https://yourmainplatform.com

# Optional: List main domains to exclude from custom domain detection
MAIN_DOMAINS=yourmainplatform.com,localhost,127.0.0.1
```

---

## SSL Certificate Setup

### Option 1: Cloudflare (Recommended)

**Advantages:**
- Free SSL for all custom domains
- Automatic certificate management
- DDoS protection
- CDN

**Setup:**

1. Create Cloudflare account
2. Install Origin Certificate on server:

```bash
mkdir -p /etc/letsencrypt/live/yourdomain.com

# Create fullchain.pem (copy from Cloudflare)
nano /etc/letsencrypt/live/yourdomain.com/fullchain.pem

# Create privkey.pem (copy from Cloudflare)
nano /etc/letsencrypt/live/yourdomain.com/privkey.pem

chmod 644 /etc/letsencrypt/live/yourdomain.com/fullchain.pem
chmod 600 /etc/letsencrypt/live/yourdomain.com/privkey.pem
```

3. Update OpenLiteSpeed listener (see above)

4. Restart OpenLiteSpeed

**Seller instructions**: Point domain to Cloudflare nameservers, enable orange cloud proxy.

### Option 2: Let's Encrypt Wildcard

**For**: `*.yourmainplatform.com` subdomains

```bash
certbot certonly --manual --preferred-challenges=dns \
  --email admin@yourdomain.com \
  --server https://acme-v02.api.letsencrypt.org/directory \
  --agree-tos \
  -d yourdomain.com -d *.yourdomain.com
```

Follow prompts to add DNS TXT records.

**Certificate paths**:
- `/etc/letsencrypt/live/yourdomain.com/fullchain.pem`
- `/etc/letsencrypt/live/yourdomain.com/privkey.pem`

**Auto-renewal**:
```bash
# Add to crontab
0 0 1 * * certbot renew --deploy-hook "/usr/local/lsws/bin/lswsctrl restart"
```

### Option 3: Per-Domain Let's Encrypt

**Not recommended** for many domains (rate limits).

```bash
certbot certonly --webroot -w /var/www/html \
  -d customerdomain.com \
  --email admin@yourdomain.com \
  --agree-tos
```

---

## Firewall Configuration

### UFW (Ubuntu/Debian)

```bash
ufw allow 80/tcp
ufw allow 443/tcp
ufw allow 7080/tcp  # OpenLiteSpeed WebAdmin (restrict to your IP)
ufw enable
```

### FirewallD (CentOS/RHEL)

```bash
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --reload
```

### iptables

```bash
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
iptables-save > /etc/iptables/rules.v4
```

---

## Monitoring & Maintenance

### Check Active Domains

```bash
php artisan tinker
>>> \App\Models\SellerDomain::where('is_active', true)->count();
>>> \App\Models\SellerDomain::verified()->pluck('domain');
```

### Monitor OpenLiteSpeed Logs

```bash
# Real-time access log
tail -f /usr/local/lsws/logs/access.log

# Real-time error log
tail -f /usr/local/lsws/logs/error.log

# Check which domains are being accessed
awk '{print $1}' /usr/local/lsws/logs/access.log | sort | uniq -c | sort -rn
```

### Laravel Application Logs

```bash
# Monitor application errors
tail -f /path/to/multistore/storage/logs/laravel.log

# Search for domain-related errors
grep -i "domain" /path/to/multistore/storage/logs/laravel.log
```

### Performance Monitoring

```bash
# Check OpenLiteSpeed status
systemctl status lsws

# Check memory usage
free -h

# Check disk usage
df -h

# Check active connections
netstat -an | grep :80 | wc -l
netstat -an | grep :443 | wc -l
```

---

## Troubleshooting Commands

### Test Domain Resolution

```bash
# Check DNS
nslookup customerdomain.com

# Check from specific DNS server
dig @8.8.8.8 customerdomain.com

# Check HTTPS
curl -I https://customerdomain.com

# Test with custom host header
curl -H "Host: customerdomain.com" https://YOUR_SERVER_IP
```

### Check OpenLiteSpeed Configuration

```bash
# Test configuration syntax
/usr/local/lsws/bin/lswsctrl configtest

# View virtual host domains
grep -r "vhDomain" /usr/local/lsws/conf/vhosts/

# View listener mappings
grep -A 10 "listener" /usr/local/lsws/conf/httpd_config.conf
```

### Check SSL Certificates

```bash
# View certificate details
openssl x509 -in /etc/letsencrypt/live/domain.com/fullchain.pem -noout -text

# Check certificate expiry
openssl x509 -in /etc/letsencrypt/live/domain.com/fullchain.pem -noout -dates

# Test SSL connection
openssl s_client -connect customerdomain.com:443 -servername customerdomain.com
```

### Database Queries

```bash
php artisan tinker

# Find domain by name
>>> \App\Models\SellerDomain::where('domain', 'customerdomain.com')->first();

# List unverified domains
>>> \App\Models\SellerDomain::whereNull('verified_at')->get();

# List domains by user
>>> \App\Models\User::find(1)->customDomains;

# Check primary domain
>>> \App\Models\User::find(1)->primaryDomain;

# Manual verification
>>> $domain = \App\Models\SellerDomain::where('domain', 'test.com')->first();
>>> $domain->markAsVerified();
```

---

## Common Issues & Solutions

### Issue: Custom domain shows 404

**Check:**
```bash
# Verify domain in database
php artisan tinker
>>> \App\Models\SellerDomain::where('domain', 'customerdomain.com')->first();
# Should show is_active=1, verified_at not null
```

**Solution:**
- Ensure domain is verified
- Check middleware is loaded: `php artisan route:list`
- Clear cache: `php artisan cache:clear && php artisan config:clear`

### Issue: SSL errors on custom domains

**Check:**
```bash
# Test SSL
curl -v https://customerdomain.com 2>&1 | grep -i ssl
```

**Solution:**
- Verify Cloudflare SSL mode is "Full" or "Full (strict)"
- Check certificate paths in OpenLiteSpeed listener config
- Ensure orange cloud is enabled in Cloudflare

### Issue: Performance degradation with many domains

**Solutions:**

1. **Enable OPcache:**
```bash
nano /usr/local/lsws/lsphp81/etc/php/8.1/litespeed/php.ini

opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
```

2. **Laravel optimizations:**
```bash
php artisan config:cache
php artisan route:cache
php artisan view:cache
```

3. **Database indexing:**
```sql
-- Already created in migration
CREATE INDEX idx_domain ON seller_domains(domain);
CREATE INDEX idx_active ON seller_domains(is_active);
```

### Issue: Domain verification failing

**Check DNS:**
```bash
# Check A record
nslookup customerdomain.com

# Should return your server IP
# If not, DNS not propagated or wrong IP
```

**Check from application:**
```bash
php artisan tinker
>>> $host = 'customerdomain.com';
>>> $ip = gethostbyname($host);
>>> echo $ip;  // Should be your server IP
```

---

## Security Best Practices

### 1. Rate Limiting

`app/Http/Kernel.php`:
```php
protected $middlewareGroups = [
    'web' => [
        // ... existing middleware
        \Illuminate\Routing\Middleware\ThrottleRequests::class.':60,1',
    ],
];
```

### 2. Input Sanitization

Already implemented in `SellerDomainController::cleanDomainName()`.

### 3. Domain Ownership Verification

Current: Manual verification
Future: Automated via TXT record or file verification

### 4. SSL Enforcement

```bash
# Redirect HTTP to HTTPS (if not using Cloudflare)
# Add to .htaccess or OpenLiteSpeed rewrite rules:

RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
```

### 5. DDoS Protection

With Cloudflare (automatic when proxied):
- Rate limiting
- Bot protection
- Challenge pages

Without Cloudflare:
```bash
# Install and configure fail2ban
apt install fail2ban
systemctl enable fail2ban
```

---

## Backup & Recovery

### Backup Domain Data

```bash
# Export domain data
php artisan tinker
>>> $domains = \App\Models\SellerDomain::all()->toArray();
>>> file_put_contents('domains_backup.json', json_encode($domains, JSON_PRETTY_PRINT));
```

### Restore Domain Data

```bash
php artisan tinker
>>> $data = json_decode(file_get_contents('domains_backup.json'), true);
>>> foreach ($data as $domain) {
    \App\Models\SellerDomain::create($domain);
}
```

### Database Backup

```bash
# Full database backup
mysqldump -u root -p multistore_db > multistore_backup_$(date +%Y%m%d).sql

# Just seller_domains table
mysqldump -u root -p multistore_db seller_domains > domains_$(date +%Y%m%d).sql
```

---

## Performance Optimization

### 1. Query Optimization

The middleware runs on every request. Ensure fast queries:

```sql
-- Check indexes (already created in migration)
SHOW INDEXES FROM seller_domains;

-- Should have indexes on:
-- - domain (unique)
-- - is_active
-- - user_id
```

### 2. Caching Strategy

Consider caching active domains:

```php
// Example: Cache active domains for 5 minutes
$activeDomains = Cache::remember('active_seller_domains', 300, function () {
    return \App\Models\SellerDomain::where('is_active', true)
        ->whereNotNull('verified_at')
        ->pluck('user_id', 'domain');
});
```

### 3. OpenLiteSpeed Tuning

Edit: `/usr/local/lsws/conf/httpd_config.conf`

```apache
tuning {
  maxConnections            10000
  maxSSLConnections         5000
  connTimeout               300
  maxKeepAliveReq           10000
  keepAliveTimeout          5
  smartKeepAlive            1
  sndBufSize                0
  rcvBufSize                0
}
```

---

## Scaling Considerations

### Multiple Servers (Load Balancer)

If using multiple application servers behind a load balancer:

1. **Shared Database**: All servers connect to same database
2. **Shared Storage**: `storage/` directory on shared filesystem
3. **Session Management**: Use Redis for sessions
4. **Cache**: Use Redis for application cache

```env
SESSION_DRIVER=redis
CACHE_DRIVER=redis
REDIS_HOST=your-redis-server
```

### CDN Integration

With Cloudflare, CDN is automatic when proxied.

Without Cloudflare:
- Consider AWS CloudFront or similar
- Configure proper cache headers

---

## Useful Artisan Commands

```bash
# Clear all caches
php artisan cache:clear
php artisan config:clear
php artisan route:clear
php artisan view:clear

# Check routes
php artisan route:list | grep -i domain

# Database status
php artisan migrate:status

# Check queue status (if using queues)
php artisan queue:work --once

# Run tinker for testing
php artisan tinker
```

---

## Monitoring Checklist

Daily:
- [ ] Check error logs
- [ ] Monitor disk space
- [ ] Check SSL certificate expiry

Weekly:
- [ ] Review new domain verifications
- [ ] Check performance metrics
- [ ] Review access logs for anomalies

Monthly:
- [ ] Update SSL certificates (if not auto-renewed)
- [ ] Review and clean old logs
- [ ] Performance optimization review
- [ ] Security updates

---

## Support Script for Sellers

Create this as an artisan command or blade view:

```bash
php artisan tinker

# Get seller's server IP to display
>>> $serverIp = file_get_contents('https://api.ipify.org');
>>> echo "Server IP: $serverIp";

# Check seller's domain status
>>> $userId = 123;  // Replace with seller ID
>>> $domains = \App\Models\SellerDomain::where('user_id', $userId)->get();
>>> foreach ($domains as $d) {
    echo $d->domain . " - " . ($d->isVerified() ? "VERIFIED" : "PENDING") . "\n";
}
```

---

## Reference Links

- OpenLiteSpeed Docs: https://openlitespeed.org/kb/
- Laravel Docs: https://laravel.com/docs
- Cloudflare Docs: https://developers.cloudflare.com/
- Let's Encrypt: https://letsencrypt.org/docs/

---

**Last Updated**: [Current Date]
**Version**: 1.0.0
**Support**: admin@yourdomain.com
