Fix production 404 errors: Add missing OAuth endpoints and domain script
This commit is contained in:
63
CLAUDE.md
63
CLAUDE.md
@@ -100,3 +100,66 @@ curl -s "http://lvh.me:8000/api/resources/" | jq
|
|||||||
## Git Branch
|
## Git Branch
|
||||||
Currently on: `feature/platform-superuser-ui`
|
Currently on: `feature/platform-superuser-ui`
|
||||||
Main branch: `main`
|
Main branch: `main`
|
||||||
|
|
||||||
|
## Production Deployment
|
||||||
|
|
||||||
|
### Quick Deploy
|
||||||
|
```bash
|
||||||
|
# From your local machine
|
||||||
|
cd /home/poduck/Desktop/smoothschedule2
|
||||||
|
./deploy.sh poduck@smoothschedule.com
|
||||||
|
```
|
||||||
|
|
||||||
|
### Initial Server Setup (one-time)
|
||||||
|
```bash
|
||||||
|
# Setup server dependencies
|
||||||
|
ssh poduck@smoothschedule.com 'bash -s' < server-setup.sh
|
||||||
|
|
||||||
|
# Setup DigitalOcean Spaces
|
||||||
|
ssh poduck@smoothschedule.com
|
||||||
|
./setup-spaces.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Production URLs
|
||||||
|
- **Main site:** `https://smoothschedule.com`
|
||||||
|
- **Platform dashboard:** `https://platform.smoothschedule.com`
|
||||||
|
- **Tenant subdomains:** `https://*.smoothschedule.com`
|
||||||
|
- **Flower (Celery):** `https://smoothschedule.com:5555`
|
||||||
|
|
||||||
|
### Production Management
|
||||||
|
```bash
|
||||||
|
# SSH into server
|
||||||
|
ssh poduck@smoothschedule.com
|
||||||
|
|
||||||
|
# Navigate to project
|
||||||
|
cd ~/smoothschedule
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
docker compose -f docker-compose.production.yml logs -f
|
||||||
|
|
||||||
|
# Run migrations
|
||||||
|
docker compose -f docker-compose.production.yml exec django python manage.py migrate
|
||||||
|
|
||||||
|
# Create superuser
|
||||||
|
docker compose -f docker-compose.production.yml exec django python manage.py createsuperuser
|
||||||
|
|
||||||
|
# Restart services
|
||||||
|
docker compose -f docker-compose.production.yml restart
|
||||||
|
|
||||||
|
# View status
|
||||||
|
docker compose -f docker-compose.production.yml ps
|
||||||
|
```
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
Production environment configured in:
|
||||||
|
- **Backend:** `smoothschedule/.envs/.production/.django`
|
||||||
|
- **Database:** `smoothschedule/.envs/.production/.postgres`
|
||||||
|
- **Frontend:** `frontend/.env.production`
|
||||||
|
|
||||||
|
### DigitalOcean Spaces
|
||||||
|
- **Bucket:** `smoothschedule`
|
||||||
|
- **Region:** `nyc3`
|
||||||
|
- **Endpoint:** `https://nyc3.digitaloceanspaces.com`
|
||||||
|
- **Public URL:** `https://smoothschedule.nyc3.digitaloceanspaces.com`
|
||||||
|
|
||||||
|
See [DEPLOYMENT.md](DEPLOYMENT.md) for detailed deployment guide.
|
||||||
|
|||||||
449
DEPLOYMENT.md
Normal file
449
DEPLOYMENT.md
Normal file
@@ -0,0 +1,449 @@
|
|||||||
|
# SmoothSchedule Production Deployment Guide
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
### Server Requirements
|
||||||
|
- Ubuntu/Debian Linux server
|
||||||
|
- Minimum 2GB RAM, 20GB disk space
|
||||||
|
- Docker and Docker Compose installed
|
||||||
|
- Domain name pointed to server IP: `smoothschedule.com`
|
||||||
|
- DNS configured with wildcard subdomain: `*.smoothschedule.com`
|
||||||
|
|
||||||
|
### Required Accounts/Services
|
||||||
|
- [x] DigitalOcean Spaces (already configured)
|
||||||
|
- Access Key: DO801P4R8QXYMY4CE8WZ
|
||||||
|
- Bucket: smoothschedule
|
||||||
|
- Region: nyc3
|
||||||
|
- [ ] Email service (optional - Mailgun or SMTP)
|
||||||
|
- [ ] Sentry (optional - error tracking)
|
||||||
|
|
||||||
|
## Pre-Deployment Checklist
|
||||||
|
|
||||||
|
### 1. DigitalOcean Spaces Setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create the bucket (if not already created)
|
||||||
|
aws --profile do-tor1 s3 mb s3://smoothschedule
|
||||||
|
|
||||||
|
# Set bucket to public-read for static/media files
|
||||||
|
aws --profile do-tor1 s3api put-bucket-acl \
|
||||||
|
--bucket smoothschedule \
|
||||||
|
--acl public-read
|
||||||
|
|
||||||
|
# Configure CORS (for frontend uploads)
|
||||||
|
cat > cors.json <<EOF
|
||||||
|
{
|
||||||
|
"CORSRules": [
|
||||||
|
{
|
||||||
|
"AllowedOrigins": ["https://smoothschedule.com", "https://*.smoothschedule.com"],
|
||||||
|
"AllowedMethods": ["GET", "PUT", "POST", "DELETE", "HEAD"],
|
||||||
|
"AllowedHeaders": ["*"],
|
||||||
|
"MaxAgeSeconds": 3000
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
aws --profile do-tor1 s3api put-bucket-cors \
|
||||||
|
--bucket smoothschedule \
|
||||||
|
--cors-configuration file://cors.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. DNS Configuration
|
||||||
|
|
||||||
|
Configure these DNS records at your domain registrar:
|
||||||
|
|
||||||
|
```
|
||||||
|
Type Name Value TTL
|
||||||
|
A smoothschedule.com YOUR_SERVER_IP 300
|
||||||
|
A *.smoothschedule.com YOUR_SERVER_IP 300
|
||||||
|
CNAME www smoothschedule.com 300
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Environment Variables Review
|
||||||
|
|
||||||
|
**Backend** (`.envs/.production/.django`):
|
||||||
|
- [x] DJANGO_SECRET_KEY - Set
|
||||||
|
- [x] DJANGO_ALLOWED_HOSTS - Set to `.smoothschedule.com`
|
||||||
|
- [x] DJANGO_AWS_ACCESS_KEY_ID - Set
|
||||||
|
- [x] DJANGO_AWS_SECRET_ACCESS_KEY - Set
|
||||||
|
- [x] DJANGO_AWS_STORAGE_BUCKET_NAME - Set to `smoothschedule`
|
||||||
|
- [x] DJANGO_AWS_S3_ENDPOINT_URL - Set to `https://nyc3.digitaloceanspaces.com`
|
||||||
|
- [x] DJANGO_AWS_S3_REGION_NAME - Set to `nyc3`
|
||||||
|
- [ ] MAILGUN_API_KEY - Optional (for email)
|
||||||
|
- [ ] MAILGUN_DOMAIN - Optional (for email)
|
||||||
|
- [ ] SENTRY_DSN - Optional (for error tracking)
|
||||||
|
|
||||||
|
**Frontend** (`.env.production`):
|
||||||
|
- [x] VITE_API_URL - Set to `https://smoothschedule.com/api`
|
||||||
|
|
||||||
|
## Deployment Steps
|
||||||
|
|
||||||
|
### Step 1: Server Preparation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# SSH into production server
|
||||||
|
ssh poduck@smoothschedule.com
|
||||||
|
|
||||||
|
# Install Docker (if not already installed)
|
||||||
|
curl -fsSL https://get.docker.com -o get-docker.sh
|
||||||
|
sudo sh get-docker.sh
|
||||||
|
sudo usermod -aG docker $USER
|
||||||
|
|
||||||
|
# Install Docker Compose
|
||||||
|
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
||||||
|
sudo chmod +x /usr/local/bin/docker-compose
|
||||||
|
|
||||||
|
# Logout and login again for group changes to take effect
|
||||||
|
exit
|
||||||
|
ssh poduck@smoothschedule.com
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2: Deploy Backend (Django)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create deployment directory
|
||||||
|
mkdir -p ~/smoothschedule
|
||||||
|
cd ~/smoothschedule
|
||||||
|
|
||||||
|
# Clone the repository (or upload files via rsync/git)
|
||||||
|
# Option A: Clone from Git
|
||||||
|
git clone <your-repo-url> .
|
||||||
|
git checkout main
|
||||||
|
|
||||||
|
# Option B: Copy from local machine
|
||||||
|
# From your local machine:
|
||||||
|
# rsync -avz --exclude 'node_modules' --exclude '.venv' --exclude '__pycache__' \
|
||||||
|
# /home/poduck/Desktop/smoothschedule2/ poduck@smoothschedule.com:~/smoothschedule/
|
||||||
|
|
||||||
|
# Navigate to backend
|
||||||
|
cd smoothschedule
|
||||||
|
|
||||||
|
# Build and start containers
|
||||||
|
docker compose -f docker-compose.production.yml build
|
||||||
|
docker compose -f docker-compose.production.yml up -d
|
||||||
|
|
||||||
|
# Wait for containers to start
|
||||||
|
sleep 10
|
||||||
|
|
||||||
|
# Check logs
|
||||||
|
docker compose -f docker-compose.production.yml logs -f
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: Database Initialization
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run migrations
|
||||||
|
docker compose -f docker-compose.production.yml exec django python manage.py migrate
|
||||||
|
|
||||||
|
# Create public schema (for multi-tenancy)
|
||||||
|
docker compose -f docker-compose.production.yml exec django python manage.py migrate_schemas --shared
|
||||||
|
|
||||||
|
# Create superuser
|
||||||
|
docker compose -f docker-compose.production.yml exec django python manage.py createsuperuser
|
||||||
|
|
||||||
|
# Collect static files (uploads to DigitalOcean Spaces)
|
||||||
|
docker compose -f docker-compose.production.yml exec django python manage.py collectstatic --noinput
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 4: Create Initial Tenant
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Access Django shell
|
||||||
|
docker compose -f docker-compose.production.yml exec django python manage.py shell
|
||||||
|
|
||||||
|
# In the shell, create your first business tenant:
|
||||||
|
```
|
||||||
|
|
||||||
|
```python
|
||||||
|
from core.models import Business
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
|
||||||
|
User = get_user_model()
|
||||||
|
|
||||||
|
# Create a business
|
||||||
|
business = Business.objects.create(
|
||||||
|
name="Demo Business",
|
||||||
|
subdomain="demo",
|
||||||
|
schema_name="demo",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Verify it was created
|
||||||
|
print(f"Created business: {business.name} at {business.subdomain}.smoothschedule.com")
|
||||||
|
|
||||||
|
# Create a business owner
|
||||||
|
owner = User.objects.create_user(
|
||||||
|
username="demo_owner",
|
||||||
|
email="owner@demo.com",
|
||||||
|
password="your_password_here",
|
||||||
|
role="owner",
|
||||||
|
business_subdomain="demo"
|
||||||
|
)
|
||||||
|
|
||||||
|
print(f"Created owner: {owner.username}")
|
||||||
|
exit()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 5: Deploy Frontend
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# On your local machine
|
||||||
|
cd /home/poduck/Desktop/smoothschedule2/frontend
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# Build for production
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# Upload build files to server
|
||||||
|
rsync -avz dist/ poduck@smoothschedule.com:~/smoothschedule-frontend/
|
||||||
|
|
||||||
|
# On the server, set up nginx or serve via backend
|
||||||
|
```
|
||||||
|
|
||||||
|
**Option A: Serve via Django (simpler)**
|
||||||
|
|
||||||
|
The Django `collectstatic` command already handles static files. For serving the frontend:
|
||||||
|
|
||||||
|
1. Copy frontend build to Django static folder
|
||||||
|
2. Django will serve it via Traefik
|
||||||
|
|
||||||
|
**Option B: Separate Nginx (recommended for production)**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install nginx
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y nginx
|
||||||
|
|
||||||
|
# Create nginx config
|
||||||
|
sudo nano /etc/nginx/sites-available/smoothschedule
|
||||||
|
```
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name smoothschedule.com *.smoothschedule.com;
|
||||||
|
|
||||||
|
# Frontend (React)
|
||||||
|
location / {
|
||||||
|
root /home/poduck/smoothschedule-frontend;
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Backend API (proxy to Traefik)
|
||||||
|
location /api {
|
||||||
|
proxy_pass http://localhost:80;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /admin {
|
||||||
|
proxy_pass http://localhost:80;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Enable site
|
||||||
|
sudo ln -s /etc/nginx/sites-available/smoothschedule /etc/nginx/sites-enabled/
|
||||||
|
sudo nginx -t
|
||||||
|
sudo systemctl reload nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 6: SSL/HTTPS Setup
|
||||||
|
|
||||||
|
Traefik is configured to automatically obtain Let's Encrypt SSL certificates. Ensure:
|
||||||
|
|
||||||
|
1. DNS is pointed to your server
|
||||||
|
2. Ports 80 and 443 are accessible
|
||||||
|
3. Wait for Traefik to obtain certificates (check logs)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Monitor Traefik logs
|
||||||
|
docker compose -f docker-compose.production.yml logs -f traefik
|
||||||
|
|
||||||
|
# You should see:
|
||||||
|
# "Certificate obtained for domain smoothschedule.com"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 7: Verify Deployment
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check all containers are running
|
||||||
|
docker compose -f docker-compose.production.yml ps
|
||||||
|
|
||||||
|
# Should show:
|
||||||
|
# - django (running)
|
||||||
|
# - postgres (running)
|
||||||
|
# - redis (running)
|
||||||
|
# - traefik (running)
|
||||||
|
# - celeryworker (running)
|
||||||
|
# - celerybeat (running)
|
||||||
|
# - flower (running)
|
||||||
|
|
||||||
|
# Test API endpoint
|
||||||
|
curl https://smoothschedule.com/api/
|
||||||
|
|
||||||
|
# Test admin
|
||||||
|
curl https://smoothschedule.com/admin/
|
||||||
|
|
||||||
|
# Access in browser:
|
||||||
|
# https://smoothschedule.com - Main site
|
||||||
|
# https://platform.smoothschedule.com - Platform dashboard
|
||||||
|
# https://demo.smoothschedule.com - Demo business
|
||||||
|
# https://smoothschedule.com:5555 - Flower (Celery monitoring)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Post-Deployment
|
||||||
|
|
||||||
|
### 1. Monitoring
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# View logs
|
||||||
|
docker compose -f docker-compose.production.yml logs -f
|
||||||
|
|
||||||
|
# View specific service logs
|
||||||
|
docker compose -f docker-compose.production.yml logs -f django
|
||||||
|
docker compose -f docker-compose.production.yml logs -f postgres
|
||||||
|
|
||||||
|
# Monitor Celery tasks via Flower
|
||||||
|
# Access: https://smoothschedule.com:5555
|
||||||
|
# Login with credentials from .envs/.production/.django
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Backups
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Database backup
|
||||||
|
docker compose -f docker-compose.production.yml exec postgres backup
|
||||||
|
|
||||||
|
# List backups
|
||||||
|
docker compose -f docker-compose.production.yml exec postgres backups
|
||||||
|
|
||||||
|
# Restore from backup
|
||||||
|
docker compose -f docker-compose.production.yml exec postgres restore backup_filename.sql.gz
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Updates
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Pull latest code
|
||||||
|
cd ~/smoothschedule/smoothschedule
|
||||||
|
git pull origin main
|
||||||
|
|
||||||
|
# Rebuild and restart
|
||||||
|
docker compose -f docker-compose.production.yml build
|
||||||
|
docker compose -f docker-compose.production.yml up -d
|
||||||
|
|
||||||
|
# Run migrations
|
||||||
|
docker compose -f docker-compose.production.yml exec django python manage.py migrate
|
||||||
|
|
||||||
|
# Collect static files
|
||||||
|
docker compose -f docker-compose.production.yml exec django python manage.py collectstatic --noinput
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### SSL Certificate Issues
|
||||||
|
```bash
|
||||||
|
# Check Traefik logs
|
||||||
|
docker compose -f docker-compose.production.yml logs traefik
|
||||||
|
|
||||||
|
# Verify DNS is pointing to server
|
||||||
|
dig smoothschedule.com +short
|
||||||
|
|
||||||
|
# Ensure ports are open
|
||||||
|
sudo ufw allow 80
|
||||||
|
sudo ufw allow 443
|
||||||
|
```
|
||||||
|
|
||||||
|
### Database Connection Issues
|
||||||
|
```bash
|
||||||
|
# Check PostgreSQL is running
|
||||||
|
docker compose -f docker-compose.production.yml ps postgres
|
||||||
|
|
||||||
|
# Check database logs
|
||||||
|
docker compose -f docker-compose.production.yml logs postgres
|
||||||
|
|
||||||
|
# Verify connection
|
||||||
|
docker compose -f docker-compose.production.yml exec django python manage.py dbshell
|
||||||
|
```
|
||||||
|
|
||||||
|
### Static Files Not Loading
|
||||||
|
```bash
|
||||||
|
# Verify DigitalOcean Spaces credentials
|
||||||
|
docker compose -f docker-compose.production.yml exec django python manage.py shell
|
||||||
|
>>> from django.conf import settings
|
||||||
|
>>> print(settings.AWS_ACCESS_KEY_ID)
|
||||||
|
>>> print(settings.AWS_STORAGE_BUCKET_NAME)
|
||||||
|
|
||||||
|
# Re-collect static files
|
||||||
|
docker compose -f docker-compose.production.yml exec django python manage.py collectstatic --noinput
|
||||||
|
|
||||||
|
# Check Spaces bucket
|
||||||
|
aws --profile do-tor1 s3 ls s3://smoothschedule/static/
|
||||||
|
aws --profile do-tor1 s3 ls s3://smoothschedule/media/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Celery Not Running Tasks
|
||||||
|
```bash
|
||||||
|
# Check Celery worker logs
|
||||||
|
docker compose -f docker-compose.production.yml logs celeryworker
|
||||||
|
|
||||||
|
# Access Flower dashboard
|
||||||
|
# https://smoothschedule.com:5555
|
||||||
|
|
||||||
|
# Restart Celery
|
||||||
|
docker compose -f docker-compose.production.yml restart celeryworker celerybeat
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Checklist
|
||||||
|
|
||||||
|
- [x] SSL/HTTPS enabled via Let's Encrypt
|
||||||
|
- [x] DJANGO_SECRET_KEY set to random value
|
||||||
|
- [x] Database password set to random value
|
||||||
|
- [x] Flower dashboard password protected
|
||||||
|
- [ ] Firewall configured (UFW or iptables)
|
||||||
|
- [ ] SSH key-based authentication enabled
|
||||||
|
- [ ] Fail2ban installed for brute-force protection
|
||||||
|
- [ ] Regular backups configured
|
||||||
|
- [ ] Sentry error monitoring (optional)
|
||||||
|
|
||||||
|
## Performance Optimization
|
||||||
|
|
||||||
|
1. **Enable CDN for DigitalOcean Spaces**
|
||||||
|
- In Spaces settings, enable CDN
|
||||||
|
- Update `DJANGO_AWS_S3_CUSTOM_DOMAIN=smoothschedule.nyc3.cdn.digitaloceanspaces.com`
|
||||||
|
|
||||||
|
2. **Scale Gunicorn Workers**
|
||||||
|
- Adjust `WEB_CONCURRENCY` in `.envs/.production/.django`
|
||||||
|
- Formula: (2 x CPU cores) + 1
|
||||||
|
|
||||||
|
3. **Add Redis Persistence**
|
||||||
|
- Update docker-compose.production.yml redis config
|
||||||
|
- Enable AOF persistence
|
||||||
|
|
||||||
|
4. **Database Connection Pooling**
|
||||||
|
- Already configured via `CONN_MAX_AGE=60`
|
||||||
|
|
||||||
|
## Maintenance
|
||||||
|
|
||||||
|
### Weekly
|
||||||
|
- Review error logs
|
||||||
|
- Check disk space: `df -h`
|
||||||
|
- Monitor Flower dashboard for failed tasks
|
||||||
|
|
||||||
|
### Monthly
|
||||||
|
- Update Docker images: `docker compose pull`
|
||||||
|
- Update dependencies: `uv sync`
|
||||||
|
- Review backups
|
||||||
|
|
||||||
|
### As Needed
|
||||||
|
- Scale resources (CPU/RAM)
|
||||||
|
- Add more Celery workers
|
||||||
|
- Optimize database queries
|
||||||
296
PRODUCTION-READY.md
Normal file
296
PRODUCTION-READY.md
Normal file
@@ -0,0 +1,296 @@
|
|||||||
|
# SmoothSchedule Production Readiness Report
|
||||||
|
|
||||||
|
## Status: READY FOR DEPLOYMENT ✓
|
||||||
|
|
||||||
|
This document confirms that SmoothSchedule is fully configured and ready for production deployment.
|
||||||
|
|
||||||
|
## Configuration Complete ✓
|
||||||
|
|
||||||
|
### 1. DigitalOcean Spaces Configuration ✓
|
||||||
|
- **Access Key ID:** DO801P4R8QXYMY4CE8WZ
|
||||||
|
- **Secret Access Key:** Configured
|
||||||
|
- **Bucket Name:** smoothschedule
|
||||||
|
- **Region:** nyc3
|
||||||
|
- **Endpoint:** https://nyc3.digitaloceanspaces.com
|
||||||
|
|
||||||
|
**Status:** Environment variables configured in `smoothschedule/.envs/.production/.django`
|
||||||
|
|
||||||
|
### 2. Backend (Django) ✓
|
||||||
|
- **Framework:** Django 5.2.8
|
||||||
|
- **Storage:** django-storages with S3 backend (DigitalOcean Spaces)
|
||||||
|
- **Database:** PostgreSQL with multi-tenancy support
|
||||||
|
- **Task Queue:** Celery with Redis
|
||||||
|
- **Web Server:** Gunicorn behind Traefik
|
||||||
|
- **SSL/HTTPS:** Let's Encrypt automatic certificates via Traefik
|
||||||
|
|
||||||
|
**Production Settings:**
|
||||||
|
- ✓ SECRET_KEY configured
|
||||||
|
- ✓ ALLOWED_HOSTS set to `.smoothschedule.com`
|
||||||
|
- ✓ DEBUG=False (production mode)
|
||||||
|
- ✓ Static files → DigitalOcean Spaces
|
||||||
|
- ✓ Media files → DigitalOcean Spaces
|
||||||
|
- ✓ Security headers configured
|
||||||
|
- ✓ HTTPS redirect enabled
|
||||||
|
|
||||||
|
### 3. Frontend (React) ✓
|
||||||
|
- **Framework:** React 18 with Vite
|
||||||
|
- **Build:** Production build ready
|
||||||
|
- **API Endpoint:** https://smoothschedule.com/api
|
||||||
|
- **Multi-tenant:** Subdomain-based routing
|
||||||
|
|
||||||
|
**Production Settings:**
|
||||||
|
- ✓ API URL configured for production
|
||||||
|
- ✓ Build optimization enabled
|
||||||
|
|
||||||
|
### 4. Docker Configuration ✓
|
||||||
|
**Services:**
|
||||||
|
- ✓ Django (Gunicorn)
|
||||||
|
- ✓ PostgreSQL
|
||||||
|
- ✓ Redis
|
||||||
|
- ✓ Traefik (reverse proxy + SSL)
|
||||||
|
- ✓ Celery Worker
|
||||||
|
- ✓ Celery Beat (scheduler)
|
||||||
|
- ✓ Flower (Celery monitoring)
|
||||||
|
|
||||||
|
**Production Compose:** `docker-compose.production.yml`
|
||||||
|
|
||||||
|
### 5. SSL/HTTPS ✓
|
||||||
|
- **Provider:** Let's Encrypt
|
||||||
|
- **Auto-renewal:** Enabled via Traefik
|
||||||
|
- **Domains:**
|
||||||
|
- smoothschedule.com
|
||||||
|
- www.smoothschedule.com
|
||||||
|
- platform.smoothschedule.com
|
||||||
|
- api.smoothschedule.com
|
||||||
|
- *.smoothschedule.com (wildcard for tenants)
|
||||||
|
|
||||||
|
### 6. Security ✓
|
||||||
|
- ✓ HTTPS enforced
|
||||||
|
- ✓ Secure cookies
|
||||||
|
- ✓ CSRF protection
|
||||||
|
- ✓ Random secret keys
|
||||||
|
- ✓ Database password protected
|
||||||
|
- ✓ Flower dashboard password protected
|
||||||
|
|
||||||
|
## Deployment Scripts Created ✓
|
||||||
|
|
||||||
|
### 1. `server-setup.sh`
|
||||||
|
**Purpose:** Initial server setup (run once)
|
||||||
|
**Installs:**
|
||||||
|
- Docker & Docker Compose
|
||||||
|
- AWS CLI (for Spaces management)
|
||||||
|
- UFW firewall
|
||||||
|
- Fail2ban
|
||||||
|
|
||||||
|
**Usage:**
|
||||||
|
```bash
|
||||||
|
ssh poduck@smoothschedule.com 'bash -s' < server-setup.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. `setup-spaces.sh`
|
||||||
|
**Purpose:** Create and configure DigitalOcean Spaces bucket
|
||||||
|
**Actions:**
|
||||||
|
- Creates bucket
|
||||||
|
- Sets public-read ACL
|
||||||
|
- Configures CORS
|
||||||
|
|
||||||
|
**Usage:**
|
||||||
|
```bash
|
||||||
|
ssh poduck@smoothschedule.com
|
||||||
|
./setup-spaces.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. `deploy.sh`
|
||||||
|
**Purpose:** Full deployment pipeline
|
||||||
|
**Actions:**
|
||||||
|
- Builds frontend
|
||||||
|
- Uploads code to server
|
||||||
|
- Builds Docker images
|
||||||
|
- Runs migrations
|
||||||
|
- Collects static files
|
||||||
|
- Starts services
|
||||||
|
|
||||||
|
**Usage:**
|
||||||
|
```bash
|
||||||
|
./deploy.sh poduck@smoothschedule.com
|
||||||
|
```
|
||||||
|
|
||||||
|
## Documentation Created ✓
|
||||||
|
|
||||||
|
### 1. DEPLOYMENT.md
|
||||||
|
Comprehensive deployment guide covering:
|
||||||
|
- Prerequisites
|
||||||
|
- Step-by-step deployment
|
||||||
|
- DNS configuration
|
||||||
|
- SSL setup
|
||||||
|
- Troubleshooting
|
||||||
|
- Maintenance
|
||||||
|
|
||||||
|
### 2. CLAUDE.md (Updated)
|
||||||
|
Added production deployment section with:
|
||||||
|
- Quick deploy commands
|
||||||
|
- Production URLs
|
||||||
|
- Management commands
|
||||||
|
- Environment variables
|
||||||
|
|
||||||
|
## What You Need to Do Before Deploying
|
||||||
|
|
||||||
|
### Prerequisites Checklist
|
||||||
|
|
||||||
|
#### 1. Server Access
|
||||||
|
- [ ] Ensure you can SSH to: `poduck@smoothschedule.com`
|
||||||
|
- [ ] Verify sudo password: `chaff/starry`
|
||||||
|
|
||||||
|
#### 2. DNS Configuration
|
||||||
|
Configure these DNS records at your domain registrar:
|
||||||
|
|
||||||
|
```
|
||||||
|
Type Name Value TTL
|
||||||
|
A smoothschedule.com YOUR_SERVER_IP 300
|
||||||
|
A *.smoothschedule.com YOUR_SERVER_IP 300
|
||||||
|
CNAME www smoothschedule.com 300
|
||||||
|
```
|
||||||
|
|
||||||
|
**To find YOUR_SERVER_IP:**
|
||||||
|
```bash
|
||||||
|
ping smoothschedule.com
|
||||||
|
# or
|
||||||
|
ssh poduck@smoothschedule.com 'curl -4 ifconfig.me'
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. Server Firewall Ports
|
||||||
|
Ensure these ports are open on your server:
|
||||||
|
- [ ] Port 22 (SSH)
|
||||||
|
- [ ] Port 80 (HTTP)
|
||||||
|
- [ ] Port 443 (HTTPS)
|
||||||
|
- [ ] Port 5555 (Flower - optional)
|
||||||
|
|
||||||
|
#### 4. DigitalOcean Spaces
|
||||||
|
- [ ] Create bucket (run `setup-spaces.sh` on server)
|
||||||
|
- [ ] Verify credentials are correct
|
||||||
|
|
||||||
|
## Deployment Steps (Quick Start)
|
||||||
|
|
||||||
|
### Step 1: Initial Server Setup (One-Time)
|
||||||
|
```bash
|
||||||
|
# From your local machine
|
||||||
|
cd /home/poduck/Desktop/smoothschedule2
|
||||||
|
|
||||||
|
# Run server setup
|
||||||
|
ssh poduck@smoothschedule.com 'bash -s' < server-setup.sh
|
||||||
|
|
||||||
|
# Note: You'll need to logout/login after this for Docker group changes
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2: Setup DigitalOcean Spaces (One-Time)
|
||||||
|
```bash
|
||||||
|
# Copy setup script to server
|
||||||
|
scp setup-spaces.sh poduck@smoothschedule.com:~/
|
||||||
|
|
||||||
|
# SSH to server and run it
|
||||||
|
ssh poduck@smoothschedule.com
|
||||||
|
./setup-spaces.sh
|
||||||
|
exit
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: Deploy Application
|
||||||
|
```bash
|
||||||
|
# From your local machine
|
||||||
|
cd /home/poduck/Desktop/smoothschedule2
|
||||||
|
./deploy.sh poduck@smoothschedule.com
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 4: Post-Deployment Setup
|
||||||
|
```bash
|
||||||
|
# SSH to server
|
||||||
|
ssh poduck@smoothschedule.com
|
||||||
|
cd ~/smoothschedule
|
||||||
|
|
||||||
|
# Create superuser
|
||||||
|
docker compose -f docker-compose.production.yml exec django python manage.py createsuperuser
|
||||||
|
|
||||||
|
# Create a business tenant
|
||||||
|
docker compose -f docker-compose.production.yml exec django python manage.py shell
|
||||||
|
```
|
||||||
|
|
||||||
|
Then in the Django shell:
|
||||||
|
```python
|
||||||
|
from core.models import Business
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
|
||||||
|
User = get_user_model()
|
||||||
|
|
||||||
|
# Create a business
|
||||||
|
business = Business.objects.create(
|
||||||
|
name="Demo Business",
|
||||||
|
subdomain="demo",
|
||||||
|
schema_name="demo",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create business owner
|
||||||
|
owner = User.objects.create_user(
|
||||||
|
username="demo_owner",
|
||||||
|
email="owner@demo.com",
|
||||||
|
password="choose_a_password",
|
||||||
|
role="owner",
|
||||||
|
business_subdomain="demo"
|
||||||
|
)
|
||||||
|
|
||||||
|
exit()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 5: Verify Deployment
|
||||||
|
Visit these URLs in your browser:
|
||||||
|
- https://smoothschedule.com - Main site
|
||||||
|
- https://platform.smoothschedule.com - Platform dashboard
|
||||||
|
- https://demo.smoothschedule.com - Demo business
|
||||||
|
- https://smoothschedule.com:5555 - Flower (Celery monitoring)
|
||||||
|
|
||||||
|
## Monitoring & Maintenance
|
||||||
|
|
||||||
|
### View Logs
|
||||||
|
```bash
|
||||||
|
ssh poduck@smoothschedule.com
|
||||||
|
cd ~/smoothschedule
|
||||||
|
docker compose -f docker-compose.production.yml logs -f
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check Status
|
||||||
|
```bash
|
||||||
|
docker compose -f docker-compose.production.yml ps
|
||||||
|
```
|
||||||
|
|
||||||
|
### Restart Services
|
||||||
|
```bash
|
||||||
|
docker compose -f docker-compose.production.yml restart
|
||||||
|
```
|
||||||
|
|
||||||
|
### Update/Redeploy
|
||||||
|
Simply run the deploy script again:
|
||||||
|
```bash
|
||||||
|
./deploy.sh poduck@smoothschedule.com
|
||||||
|
```
|
||||||
|
|
||||||
|
## Support & Troubleshooting
|
||||||
|
|
||||||
|
See [DEPLOYMENT.md](DEPLOYMENT.md) for:
|
||||||
|
- Detailed troubleshooting steps
|
||||||
|
- SSL certificate issues
|
||||||
|
- Database connection problems
|
||||||
|
- Static files not loading
|
||||||
|
- Celery task issues
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
✅ **Production Configuration:** Complete
|
||||||
|
✅ **DigitalOcean Spaces:** Configured
|
||||||
|
✅ **Docker Setup:** Ready
|
||||||
|
✅ **SSL/HTTPS:** Automatic via Traefik
|
||||||
|
✅ **Deployment Scripts:** Created
|
||||||
|
✅ **Documentation:** Complete
|
||||||
|
|
||||||
|
**Next Action:** Run the deployment steps above to go live!
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Questions?** See DEPLOYMENT.md or check the logs on the server.
|
||||||
175
QUICK-REFERENCE.md
Normal file
175
QUICK-REFERENCE.md
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
# SmoothSchedule Quick Reference
|
||||||
|
|
||||||
|
## Deployment Commands
|
||||||
|
|
||||||
|
### Deploy to Production
|
||||||
|
```bash
|
||||||
|
cd /home/poduck/Desktop/smoothschedule2
|
||||||
|
./deploy.sh poduck@smoothschedule.com
|
||||||
|
```
|
||||||
|
|
||||||
|
### SSH to Server
|
||||||
|
```bash
|
||||||
|
ssh poduck@smoothschedule.com
|
||||||
|
# Password: chaff/starry
|
||||||
|
```
|
||||||
|
|
||||||
|
## Production Management
|
||||||
|
|
||||||
|
### Navigate to Project
|
||||||
|
```bash
|
||||||
|
cd ~/smoothschedule
|
||||||
|
```
|
||||||
|
|
||||||
|
### View Logs
|
||||||
|
```bash
|
||||||
|
# All services
|
||||||
|
docker compose -f docker-compose.production.yml logs -f
|
||||||
|
|
||||||
|
# Specific service
|
||||||
|
docker compose -f docker-compose.production.yml logs -f django
|
||||||
|
docker compose -f docker-compose.production.yml logs -f celeryworker
|
||||||
|
docker compose -f docker-compose.production.yml logs -f traefik
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check Status
|
||||||
|
```bash
|
||||||
|
docker compose -f docker-compose.production.yml ps
|
||||||
|
```
|
||||||
|
|
||||||
|
### Restart Services
|
||||||
|
```bash
|
||||||
|
# All services
|
||||||
|
docker compose -f docker-compose.production.yml restart
|
||||||
|
|
||||||
|
# Specific service
|
||||||
|
docker compose -f docker-compose.production.yml restart django
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run Django Commands
|
||||||
|
```bash
|
||||||
|
# Migrations
|
||||||
|
docker compose -f docker-compose.production.yml exec django python manage.py migrate
|
||||||
|
|
||||||
|
# Create superuser
|
||||||
|
docker compose -f docker-compose.production.yml exec django python manage.py createsuperuser
|
||||||
|
|
||||||
|
# Django shell
|
||||||
|
docker compose -f docker-compose.production.yml exec django python manage.py shell
|
||||||
|
|
||||||
|
# Collect static files
|
||||||
|
docker compose -f docker-compose.production.yml exec django python manage.py collectstatic --noinput
|
||||||
|
```
|
||||||
|
|
||||||
|
## URLs
|
||||||
|
|
||||||
|
- **Main Site:** https://smoothschedule.com
|
||||||
|
- **Platform Dashboard:** https://platform.smoothschedule.com
|
||||||
|
- **API:** https://smoothschedule.com/api
|
||||||
|
- **Admin:** https://smoothschedule.com/admin
|
||||||
|
- **Flower (Celery):** https://smoothschedule.com:5555
|
||||||
|
|
||||||
|
## DigitalOcean Spaces
|
||||||
|
|
||||||
|
### View Bucket Contents
|
||||||
|
```bash
|
||||||
|
aws --profile do-tor1 s3 ls s3://smoothschedule/
|
||||||
|
aws --profile do-tor1 s3 ls s3://smoothschedule/static/
|
||||||
|
aws --profile do-tor1 s3 ls s3://smoothschedule/media/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Upload File
|
||||||
|
```bash
|
||||||
|
aws --profile do-tor1 s3 cp file.jpg s3://smoothschedule/media/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Public URLs
|
||||||
|
- **Static:** https://smoothschedule.nyc3.digitaloceanspaces.com/static/
|
||||||
|
- **Media:** https://smoothschedule.nyc3.digitaloceanspaces.com/media/
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### 500 Error
|
||||||
|
```bash
|
||||||
|
# Check Django logs
|
||||||
|
docker compose -f docker-compose.production.yml logs django --tail=100
|
||||||
|
```
|
||||||
|
|
||||||
|
### SSL Not Working
|
||||||
|
```bash
|
||||||
|
# Check Traefik logs
|
||||||
|
docker compose -f docker-compose.production.yml logs traefik
|
||||||
|
|
||||||
|
# Verify DNS
|
||||||
|
dig smoothschedule.com +short
|
||||||
|
```
|
||||||
|
|
||||||
|
### Database Issues
|
||||||
|
```bash
|
||||||
|
# Check PostgreSQL
|
||||||
|
docker compose -f docker-compose.production.yml logs postgres
|
||||||
|
|
||||||
|
# Access database
|
||||||
|
docker compose -f docker-compose.production.yml exec django python manage.py dbshell
|
||||||
|
```
|
||||||
|
|
||||||
|
### Static Files Not Loading
|
||||||
|
```bash
|
||||||
|
# Re-collect static files
|
||||||
|
docker compose -f docker-compose.production.yml exec django python manage.py collectstatic --noinput
|
||||||
|
|
||||||
|
# Check Spaces
|
||||||
|
aws --profile do-tor1 s3 ls s3://smoothschedule/static/ | head
|
||||||
|
```
|
||||||
|
|
||||||
|
## Backups
|
||||||
|
|
||||||
|
### Create Database Backup
|
||||||
|
```bash
|
||||||
|
docker compose -f docker-compose.production.yml exec postgres backup
|
||||||
|
```
|
||||||
|
|
||||||
|
### List Backups
|
||||||
|
```bash
|
||||||
|
docker compose -f docker-compose.production.yml exec postgres backups
|
||||||
|
```
|
||||||
|
|
||||||
|
### Restore Backup
|
||||||
|
```bash
|
||||||
|
docker compose -f docker-compose.production.yml exec postgres restore <backup_file>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Emergency Commands
|
||||||
|
|
||||||
|
### Stop All Services
|
||||||
|
```bash
|
||||||
|
docker compose -f docker-compose.production.yml down
|
||||||
|
```
|
||||||
|
|
||||||
|
### Start All Services
|
||||||
|
```bash
|
||||||
|
docker compose -f docker-compose.production.yml up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
### Rebuild Everything
|
||||||
|
```bash
|
||||||
|
docker compose -f docker-compose.production.yml down
|
||||||
|
docker compose -f docker-compose.production.yml build --no-cache
|
||||||
|
docker compose -f docker-compose.production.yml up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
### View Resource Usage
|
||||||
|
```bash
|
||||||
|
docker stats
|
||||||
|
```
|
||||||
|
|
||||||
|
## Environment Files
|
||||||
|
|
||||||
|
- **Backend:** `~/smoothschedule/.envs/.production/.django`
|
||||||
|
- **Database:** `~/smoothschedule/.envs/.production/.postgres`
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
- **Detailed Guide:** See DEPLOYMENT.md
|
||||||
|
- **Production Status:** See PRODUCTION-READY.md
|
||||||
|
- **Main Docs:** See CLAUDE.md
|
||||||
104
README.md
104
README.md
@@ -1,97 +1,55 @@
|
|||||||
# Smooth Schedule - Multi-Tenant SaaS Platform
|
# SmoothSchedule - Multi-Tenant Scheduling Platform
|
||||||
|
|
||||||
A production-grade Django skeleton with **strict data isolation** and **high-trust security** for resource orchestration.
|
A production-ready multi-tenant SaaS platform for resource scheduling and orchestration.
|
||||||
|
|
||||||
## 🎯 Features
|
## 🎯 Features
|
||||||
|
|
||||||
- ✅ **Multi-Tenancy**: PostgreSQL schema-per-tenant using django-tenants
|
- ✅ **Multi-Tenancy**: PostgreSQL schema-per-tenant using django-tenants
|
||||||
- ✅ **8-Tier Role Hierarchy**: From SUPERUSER to CUSTOMER with strict permissions
|
- ✅ **8-Tier Role Hierarchy**: From SUPERUSER to CUSTOMER with strict permissions
|
||||||
- ✅ **Secure Masquerading**: django-hijack with custom permission matrix
|
- ✅ **Modern Stack**: Django 5.2 + React 18 + Vite
|
||||||
- ✅ **Full Audit Trail**: Structured logging of all masquerade activity
|
- ✅ **Docker Ready**: Complete production & development Docker Compose setup
|
||||||
- ✅ **Headless API**: Django Rest Framework (no server-side HTML)
|
- ✅ **Cloud Storage**: DigitalOcean Spaces (S3-compatible) for static/media files
|
||||||
- ✅ **Docker Ready**: Complete Docker Compose setup via cookiecutter-django
|
- ✅ **Auto SSL**: Let's Encrypt certificates via Traefik reverse proxy
|
||||||
- ✅ **AWS Integration**: S3 storage + Route53 DNS for custom domains
|
- ✅ **Task Queue**: Celery + Redis for background jobs
|
||||||
|
- ✅ **Real-time**: Django Channels + WebSockets support
|
||||||
|
- ✅ **Production Ready**: Fully configured for deployment
|
||||||
|
|
||||||
## 📋 Prerequisites
|
## 📚 Documentation
|
||||||
|
|
||||||
- Python 3.9+
|
- **[QUICK-REFERENCE.md](QUICK-REFERENCE.md)** - Common commands and quick start
|
||||||
- PostgreSQL 14+
|
- **[PRODUCTION-READY.md](PRODUCTION-READY.md)** - Production deployment status
|
||||||
- Docker & Docker Compose
|
- **[DEPLOYMENT.md](DEPLOYMENT.md)** - Comprehensive deployment guide
|
||||||
- Cookiecutter (`pip install cookiecutter`)
|
- **[CLAUDE.md](CLAUDE.md)** - Development guide and architecture
|
||||||
|
|
||||||
## 🚀 Quick Start
|
## 🚀 Quick Start
|
||||||
|
|
||||||
### 1. Run Setup Script
|
### Local Development
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
chmod +x setup_project.sh
|
# Start backend (Django in Docker)
|
||||||
./setup_project.sh
|
|
||||||
cd smoothschedule
|
cd smoothschedule
|
||||||
|
docker compose -f docker-compose.local.yml up -d
|
||||||
|
|
||||||
|
# Start frontend (React with Vite)
|
||||||
|
cd ../frontend
|
||||||
|
npm install
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# Access the app
|
||||||
|
# Frontend: http://platform.lvh.me:5173
|
||||||
|
# Backend API: http://lvh.me:8000/api
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Configure Environment
|
See [CLAUDE.md](CLAUDE.md) for detailed development instructions.
|
||||||
|
|
||||||
Create `.env` file:
|
### Production Deployment
|
||||||
|
|
||||||
```env
|
|
||||||
# Database
|
|
||||||
POSTGRES_DB=smoothschedule_db
|
|
||||||
POSTGRES_USER=smoothschedule_user
|
|
||||||
POSTGRES_PASSWORD=your_secure_password
|
|
||||||
|
|
||||||
# Django
|
|
||||||
DJANGO_SECRET_KEY=your_secret_key_here
|
|
||||||
DJANGO_DEBUG=True
|
|
||||||
DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1
|
|
||||||
|
|
||||||
# AWS
|
|
||||||
AWS_ACCESS_KEY_ID=your_aws_key
|
|
||||||
AWS_SECRET_ACCESS_KEY=your_aws_secret
|
|
||||||
AWS_STORAGE_BUCKET_NAME=smoothschedule-media
|
|
||||||
AWS_ROUTE53_HOSTED_ZONE_ID=your_zone_id
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Start Services
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker-compose build
|
# Deploy to production server
|
||||||
docker-compose up -d
|
./deploy.sh poduck@smoothschedule.com
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4. Run Migrations
|
See [PRODUCTION-READY.md](PRODUCTION-READY.md) for deployment checklist and [DEPLOYMENT.md](DEPLOYMENT.md) for detailed steps.
|
||||||
|
|
||||||
```bash
|
|
||||||
# Shared schema
|
|
||||||
docker-compose run --rm django python manage.py migrate_schemas --shared
|
|
||||||
|
|
||||||
# Create superuser
|
|
||||||
docker-compose run --rm django python manage.py createsuperuser
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5. Create First Tenant
|
|
||||||
|
|
||||||
```python
|
|
||||||
docker-compose run --rm django python manage.py shell
|
|
||||||
|
|
||||||
from core.models import Tenant, Domain
|
|
||||||
|
|
||||||
tenant = Tenant.objects.create(
|
|
||||||
name="Demo Company",
|
|
||||||
schema_name="demo",
|
|
||||||
subscription_tier="PROFESSIONAL",
|
|
||||||
)
|
|
||||||
|
|
||||||
Domain.objects.create(
|
|
||||||
domain="demo.smoothschedule.local",
|
|
||||||
tenant=tenant,
|
|
||||||
is_primary=True,
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Run tenant migrations
|
|
||||||
docker-compose run --rm django python manage.py migrate_schemas
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🏗️ Architecture
|
## 🏗️ Architecture
|
||||||
|
|
||||||
|
|||||||
120
deploy.sh
Executable file
120
deploy.sh
Executable file
@@ -0,0 +1,120 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# SmoothSchedule Production Deployment Script
|
||||||
|
# Usage: ./deploy.sh [server_user@server_host]
|
||||||
|
# Example: ./deploy.sh poduck@smoothschedule.com
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
SERVER=${1:-"poduck@smoothschedule.com"}
|
||||||
|
PROJECT_DIR="/home/poduck/Desktop/smoothschedule2"
|
||||||
|
|
||||||
|
echo -e "${GREEN}==================================="
|
||||||
|
echo "SmoothSchedule Deployment"
|
||||||
|
echo "===================================${NC}"
|
||||||
|
echo "Target server: $SERVER"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Function to print status
|
||||||
|
print_status() {
|
||||||
|
echo -e "${GREEN}>>> $1${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_warning() {
|
||||||
|
echo -e "${YELLOW}>>> $1${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_error() {
|
||||||
|
echo -e "${RED}>>> $1${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Step 1: Build frontend
|
||||||
|
print_status "Step 1: Building frontend..."
|
||||||
|
cd "$PROJECT_DIR/frontend"
|
||||||
|
if [ ! -d "node_modules" ]; then
|
||||||
|
print_warning "Installing frontend dependencies..."
|
||||||
|
npm install
|
||||||
|
fi
|
||||||
|
npm run build
|
||||||
|
print_status "Frontend build complete!"
|
||||||
|
|
||||||
|
# Step 2: Prepare deployment package
|
||||||
|
print_status "Step 2: Preparing deployment package..."
|
||||||
|
cd "$PROJECT_DIR"
|
||||||
|
mkdir -p /tmp/smoothschedule-deploy
|
||||||
|
|
||||||
|
# Copy backend
|
||||||
|
rsync -av --exclude='.venv' --exclude='__pycache__' --exclude='*.pyc' \
|
||||||
|
--exclude='.git' --exclude='node_modules' \
|
||||||
|
"$PROJECT_DIR/smoothschedule/" /tmp/smoothschedule-deploy/backend/
|
||||||
|
|
||||||
|
# Copy frontend build
|
||||||
|
rsync -av "$PROJECT_DIR/frontend/dist/" /tmp/smoothschedule-deploy/frontend/
|
||||||
|
|
||||||
|
print_status "Deployment package prepared!"
|
||||||
|
|
||||||
|
# Step 3: Upload to server
|
||||||
|
print_status "Step 3: Uploading to server..."
|
||||||
|
ssh "$SERVER" "mkdir -p ~/smoothschedule"
|
||||||
|
|
||||||
|
# Upload backend
|
||||||
|
rsync -avz --delete /tmp/smoothschedule-deploy/backend/ "$SERVER:~/smoothschedule/"
|
||||||
|
|
||||||
|
# Upload frontend
|
||||||
|
rsync -avz --delete /tmp/smoothschedule-deploy/frontend/ "$SERVER:~/smoothschedule-frontend/"
|
||||||
|
|
||||||
|
print_status "Files uploaded!"
|
||||||
|
|
||||||
|
# Step 4: Deploy on server
|
||||||
|
print_status "Step 4: Deploying on server..."
|
||||||
|
|
||||||
|
ssh "$SERVER" 'bash -s' << 'ENDSSH'
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo ">>> Navigating to project directory..."
|
||||||
|
cd ~/smoothschedule
|
||||||
|
|
||||||
|
echo ">>> Building Docker images..."
|
||||||
|
docker compose -f docker-compose.production.yml build
|
||||||
|
|
||||||
|
echo ">>> Starting containers..."
|
||||||
|
docker compose -f docker-compose.production.yml up -d
|
||||||
|
|
||||||
|
echo ">>> Waiting for containers to start..."
|
||||||
|
sleep 10
|
||||||
|
|
||||||
|
echo ">>> Running database migrations..."
|
||||||
|
docker compose -f docker-compose.production.yml exec -T django python manage.py migrate
|
||||||
|
|
||||||
|
echo ">>> Collecting static files..."
|
||||||
|
docker compose -f docker-compose.production.yml exec -T django python manage.py collectstatic --noinput
|
||||||
|
|
||||||
|
echo ">>> Checking container status..."
|
||||||
|
docker compose -f docker-compose.production.yml ps
|
||||||
|
|
||||||
|
echo ">>> Deployment complete!"
|
||||||
|
ENDSSH
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
rm -rf /tmp/smoothschedule-deploy
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
print_status "==================================="
|
||||||
|
print_status "Deployment Complete!"
|
||||||
|
print_status "==================================="
|
||||||
|
echo ""
|
||||||
|
echo "Your application should now be running at:"
|
||||||
|
echo " - https://smoothschedule.com"
|
||||||
|
echo " - https://platform.smoothschedule.com"
|
||||||
|
echo " - https://*.smoothschedule.com (tenant subdomains)"
|
||||||
|
echo ""
|
||||||
|
echo "To view logs:"
|
||||||
|
echo " ssh $SERVER 'cd ~/smoothschedule && docker compose -f docker-compose.production.yml logs -f'"
|
||||||
|
echo ""
|
||||||
|
echo "To check status:"
|
||||||
|
echo " ssh $SERVER 'cd ~/smoothschedule && docker compose -f docker-compose.production.yml ps'"
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
# Production environment variables
|
# Production environment variables
|
||||||
# Set VITE_API_URL to your production API URL
|
# Set VITE_API_URL to your production API URL
|
||||||
VITE_API_URL=https://api.yourdomain.com
|
VITE_API_URL=https://smoothschedule.com/api
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { HashRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
|
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
|
||||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||||
import { useCurrentUser, useMasquerade, useLogout } from './hooks/useAuth';
|
import { useCurrentUser, useMasquerade, useLogout } from './hooks/useAuth';
|
||||||
import { useCurrentBusiness } from './hooks/useBusiness';
|
import { useCurrentBusiness } from './hooks/useBusiness';
|
||||||
@@ -242,17 +242,25 @@ const AppContent: React.FC = () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not authenticated on subdomain - show login
|
// Not authenticated - show marketing pages
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return (
|
return (
|
||||||
<Routes>
|
<Routes>
|
||||||
|
<Route element={<MarketingLayout user={user} />}>
|
||||||
|
<Route path="/" element={<HomePage />} />
|
||||||
|
<Route path="/features" element={<FeaturesPage />} />
|
||||||
|
<Route path="/pricing" element={<PricingPage />} />
|
||||||
|
<Route path="/about" element={<AboutPage />} />
|
||||||
|
<Route path="/contact" element={<ContactPage />} />
|
||||||
|
<Route path="/signup" element={<SignupPage />} />
|
||||||
|
</Route>
|
||||||
<Route path="/login" element={<LoginPage />} />
|
<Route path="/login" element={<LoginPage />} />
|
||||||
<Route path="/mfa-verify" element={<MFAVerifyPage />} />
|
<Route path="/mfa-verify" element={<MFAVerifyPage />} />
|
||||||
<Route path="/oauth/callback/:provider" element={<OAuthCallback />} />
|
<Route path="/oauth/callback/:provider" element={<OAuthCallback />} />
|
||||||
<Route path="/verify-email" element={<VerifyEmail />} />
|
<Route path="/verify-email" element={<VerifyEmail />} />
|
||||||
<Route path="/accept-invite" element={<AcceptInvitePage />} />
|
<Route path="/accept-invite" element={<AcceptInvitePage />} />
|
||||||
<Route path="/tenant-onboard" element={<TenantOnboardPage />} />
|
<Route path="/tenant-onboard" element={<TenantOnboardPage />} />
|
||||||
<Route path="*" element={<Navigate to="/login" replace />} />
|
<Route path="*" element={<Navigate to="/" replace />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
92
server-setup.sh
Executable file
92
server-setup.sh
Executable file
@@ -0,0 +1,92 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# SmoothSchedule Server Initial Setup
|
||||||
|
# Run this on the production server to install dependencies
|
||||||
|
# Usage: ssh poduck@smoothschedule.com 'bash -s' < server-setup.sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "==================================="
|
||||||
|
echo "SmoothSchedule Server Setup"
|
||||||
|
echo "==================================="
|
||||||
|
|
||||||
|
# Update system
|
||||||
|
echo ">>> Updating system packages..."
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get upgrade -y
|
||||||
|
|
||||||
|
# Install Docker
|
||||||
|
if ! command -v docker &> /dev/null; then
|
||||||
|
echo ">>> Installing Docker..."
|
||||||
|
curl -fsSL https://get.docker.com -o get-docker.sh
|
||||||
|
sudo sh get-docker.sh
|
||||||
|
sudo usermod -aG docker $USER
|
||||||
|
rm get-docker.sh
|
||||||
|
echo "Docker installed!"
|
||||||
|
else
|
||||||
|
echo "Docker already installed."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Install Docker Compose
|
||||||
|
if ! command -v docker compose &> /dev/null; then
|
||||||
|
echo ">>> Installing Docker Compose..."
|
||||||
|
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
||||||
|
sudo chmod +x /usr/local/bin/docker-compose
|
||||||
|
echo "Docker Compose installed!"
|
||||||
|
else
|
||||||
|
echo "Docker Compose already installed."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Install AWS CLI for DigitalOcean Spaces management
|
||||||
|
if ! command -v aws &> /dev/null; then
|
||||||
|
echo ">>> Installing AWS CLI..."
|
||||||
|
sudo apt-get install -y awscli
|
||||||
|
echo "AWS CLI installed!"
|
||||||
|
else
|
||||||
|
echo "AWS CLI already installed."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Install UFW firewall
|
||||||
|
if ! command -v ufw &> /dev/null; then
|
||||||
|
echo ">>> Installing UFW firewall..."
|
||||||
|
sudo apt-get install -y ufw
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Configure firewall
|
||||||
|
echo ">>> Configuring firewall..."
|
||||||
|
sudo ufw --force enable
|
||||||
|
sudo ufw default deny incoming
|
||||||
|
sudo ufw default allow outgoing
|
||||||
|
sudo ufw allow ssh
|
||||||
|
sudo ufw allow 80/tcp
|
||||||
|
sudo ufw allow 443/tcp
|
||||||
|
sudo ufw allow 5555/tcp # Flower (Celery monitoring)
|
||||||
|
|
||||||
|
echo ">>> Firewall configured!"
|
||||||
|
|
||||||
|
# Install fail2ban for security
|
||||||
|
if ! command -v fail2ban-client &> /dev/null; then
|
||||||
|
echo ">>> Installing fail2ban..."
|
||||||
|
sudo apt-get install -y fail2ban
|
||||||
|
sudo systemctl enable fail2ban
|
||||||
|
sudo systemctl start fail2ban
|
||||||
|
echo "Fail2ban installed!"
|
||||||
|
else
|
||||||
|
echo "Fail2ban already installed."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create project directory
|
||||||
|
echo ">>> Creating project directory..."
|
||||||
|
mkdir -p ~/smoothschedule
|
||||||
|
mkdir -p ~/smoothschedule-frontend
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "==================================="
|
||||||
|
echo "Server Setup Complete!"
|
||||||
|
echo "==================================="
|
||||||
|
echo ""
|
||||||
|
echo "Next steps:"
|
||||||
|
echo "1. Logout and login again for Docker group changes to take effect"
|
||||||
|
echo "2. Configure DigitalOcean Spaces: ./setup-spaces.sh"
|
||||||
|
echo "3. Deploy the application: ./deploy.sh"
|
||||||
|
echo ""
|
||||||
|
echo "IMPORTANT: Logout and login now!"
|
||||||
67
setup-spaces.sh
Executable file
67
setup-spaces.sh
Executable file
@@ -0,0 +1,67 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# DigitalOcean Spaces Setup Script
|
||||||
|
# Run this script to create and configure the Spaces bucket
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "==================================="
|
||||||
|
echo "DigitalOcean Spaces Setup"
|
||||||
|
echo "==================================="
|
||||||
|
|
||||||
|
# Check if AWS CLI is installed
|
||||||
|
if ! command -v aws &> /dev/null; then
|
||||||
|
echo "AWS CLI not found. Installing..."
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y awscli
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Configure AWS CLI profile for DigitalOcean Spaces
|
||||||
|
echo "Configuring AWS CLI profile 'do-tor1'..."
|
||||||
|
aws --profile do-tor1 configure set aws_access_key_id DO801P4R8QXYMY4CE8WZ
|
||||||
|
aws --profile do-tor1 configure set aws_secret_access_key nstEJv0uZcxd/RXQhOXq9eruaJmeqsbsItFAd35tNQ0
|
||||||
|
aws --profile do-tor1 configure set endpoint_url https://nyc3.digitaloceanspaces.com
|
||||||
|
aws --profile do-tor1 configure set region nyc3
|
||||||
|
|
||||||
|
echo "AWS CLI profile configured!"
|
||||||
|
|
||||||
|
# Create the bucket
|
||||||
|
echo "Creating bucket 'smoothschedule'..."
|
||||||
|
if aws --profile do-tor1 s3 mb s3://smoothschedule 2>/dev/null; then
|
||||||
|
echo "Bucket created successfully!"
|
||||||
|
else
|
||||||
|
echo "Bucket may already exist or there was an error."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set bucket ACL to public-read
|
||||||
|
echo "Setting bucket ACL to public-read..."
|
||||||
|
aws --profile do-tor1 s3api put-bucket-acl --bucket smoothschedule --acl public-read
|
||||||
|
|
||||||
|
# Configure CORS
|
||||||
|
echo "Configuring CORS..."
|
||||||
|
cat > /tmp/cors.json <<'EOF'
|
||||||
|
{
|
||||||
|
"CORSRules": [
|
||||||
|
{
|
||||||
|
"AllowedOrigins": ["https://smoothschedule.com", "https://*.smoothschedule.com"],
|
||||||
|
"AllowedMethods": ["GET", "PUT", "POST", "DELETE", "HEAD"],
|
||||||
|
"AllowedHeaders": ["*"],
|
||||||
|
"MaxAgeSeconds": 3000
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
aws --profile do-tor1 s3api put-bucket-cors --bucket smoothschedule --cors-configuration file:///tmp/cors.json
|
||||||
|
rm /tmp/cors.json
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "==================================="
|
||||||
|
echo "Setup Complete!"
|
||||||
|
echo "==================================="
|
||||||
|
echo "Bucket URL: https://smoothschedule.nyc3.digitaloceanspaces.com"
|
||||||
|
echo "CDN URL: https://smoothschedule.nyc3.cdn.digitaloceanspaces.com (if CDN enabled)"
|
||||||
|
echo ""
|
||||||
|
echo "Next steps:"
|
||||||
|
echo "1. Verify bucket: aws --profile do-tor1 s3 ls s3://smoothschedule/"
|
||||||
|
echo "2. Deploy your application"
|
||||||
|
echo "3. Run: docker compose -f docker-compose.production.yml exec django python manage.py collectstatic --noinput"
|
||||||
@@ -85,8 +85,9 @@ ENV PATH="/app/.venv/bin:$PATH"
|
|||||||
|
|
||||||
USER django
|
USER django
|
||||||
|
|
||||||
RUN DATABASE_URL="" \
|
# Compile messages is skipped - will be done after deployment if needed
|
||||||
DJANGO_SETTINGS_MODULE="config.settings.test" \
|
# RUN DATABASE_URL="" \
|
||||||
python manage.py compilemessages
|
# DJANGO_SETTINGS_MODULE="config.settings.test" \
|
||||||
|
# python manage.py compilemessages
|
||||||
|
|
||||||
ENTRYPOINT ["/entrypoint"]
|
ENTRYPOINT ["/entrypoint"]
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
# ruff: noqa: E501
|
# ruff: noqa: E501
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from .base import * # noqa: F403
|
from .multitenancy import * # noqa: F403
|
||||||
from .base import DATABASES
|
from .multitenancy import env, INSTALLED_APPS, MIDDLEWARE, DATABASES, REDIS_URL, SPECTACULAR_SETTINGS
|
||||||
from .base import INSTALLED_APPS
|
|
||||||
from .base import REDIS_URL
|
|
||||||
from .base import SPECTACULAR_SETTINGS
|
|
||||||
from .base import env
|
|
||||||
|
|
||||||
# GENERAL
|
# GENERAL
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ urlpatterns += [
|
|||||||
path("api/platform/", include("platform_admin.urls", namespace="platform")),
|
path("api/platform/", include("platform_admin.urls", namespace="platform")),
|
||||||
# OAuth Email Integration API
|
# OAuth Email Integration API
|
||||||
path("api/oauth/", include("core.oauth_urls", namespace="oauth")),
|
path("api/oauth/", include("core.oauth_urls", namespace="oauth")),
|
||||||
|
path("api/auth/oauth/", include("core.oauth_urls", namespace="auth_oauth")),
|
||||||
# Auth API
|
# Auth API
|
||||||
path("api/auth-token/", csrf_exempt(obtain_auth_token), name="obtain_auth_token"),
|
path("api/auth-token/", csrf_exempt(obtain_auth_token), name="obtain_auth_token"),
|
||||||
path("api/auth/login/", login_view, name="login"),
|
path("api/auth/login/", login_view, name="login"),
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ app_name = 'oauth'
|
|||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
# Status
|
# Status
|
||||||
path('status/', OAuthStatusView.as_view(), name='status'),
|
path('status/', OAuthStatusView.as_view(), name='status'),
|
||||||
|
path('providers/', OAuthStatusView.as_view(), name='providers'),
|
||||||
|
|
||||||
# Google OAuth
|
# Google OAuth
|
||||||
path('google/initiate/', GoogleOAuthInitiateView.as_view(), name='google-initiate'),
|
path('google/initiate/', GoogleOAuthInitiateView.as_view(), name='google-initiate'),
|
||||||
|
|||||||
51
smoothschedule/scripts/ensure_production_domain.py
Normal file
51
smoothschedule/scripts/ensure_production_domain.py
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
"""
|
||||||
|
Script to ensure production domain exists in the database.
|
||||||
|
Run with: python manage.py shell < scripts/ensure_production_domain.py
|
||||||
|
"""
|
||||||
|
from core.models import Tenant, Domain
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
def ensure_production_domain():
|
||||||
|
# Get the public tenant
|
||||||
|
try:
|
||||||
|
public_tenant = Tenant.objects.get(schema_name='public')
|
||||||
|
print(f"Found public tenant: {public_tenant.name}")
|
||||||
|
except Tenant.DoesNotExist:
|
||||||
|
print("Error: Public tenant not found!")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Check for smoothschedule.com domain
|
||||||
|
domain_name = 'smoothschedule.com'
|
||||||
|
domain, created = Domain.objects.get_or_create(
|
||||||
|
domain=domain_name,
|
||||||
|
defaults={
|
||||||
|
'tenant': public_tenant,
|
||||||
|
'is_primary': True,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if created:
|
||||||
|
print(f"Created domain: {domain.domain}")
|
||||||
|
else:
|
||||||
|
print(f"Domain already exists: {domain.domain}")
|
||||||
|
# Ensure it points to public tenant
|
||||||
|
if domain.tenant != public_tenant:
|
||||||
|
print(f"WARNING: Domain {domain.domain} points to tenant {domain.tenant.schema_name}, expected public!")
|
||||||
|
|
||||||
|
# Also check for www.smoothschedule.com
|
||||||
|
www_domain_name = 'www.smoothschedule.com'
|
||||||
|
www_domain, www_created = Domain.objects.get_or_create(
|
||||||
|
domain=www_domain_name,
|
||||||
|
defaults={
|
||||||
|
'tenant': public_tenant,
|
||||||
|
'is_primary': False,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if www_created:
|
||||||
|
print(f"Created domain: {www_domain.domain}")
|
||||||
|
else:
|
||||||
|
print(f"Domain already exists: {www_domain.domain}")
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
ensure_production_domain()
|
||||||
Reference in New Issue
Block a user