Improve deployment process and add login redirect logic

Deployment improvements:
- Add template env files (.envs.example/) for documentation
- Create init-production.sh for one-time server setup
- Create build-activepieces.sh for building/deploying AP image
- Update deploy.sh with --deploy-ap flag
- Make custom-pieces-metadata.sql idempotent
- Update DEPLOYMENT.md with comprehensive instructions

Frontend:
- Redirect logged-in business owners from root domain to tenant dashboard
- Redirect logged-in users from /login to /dashboard on their tenant
- Log out customers on wrong subdomain instead of redirecting

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
poduck
2025-12-20 23:13:56 -05:00
parent 2a33e4cf57
commit f8d8419622
38 changed files with 2471 additions and 396 deletions

View File

@@ -1,322 +1,381 @@
# SmoothSchedule Production Deployment Guide
This guide covers deploying SmoothSchedule to a production server.
## Table of Contents
1. [Prerequisites](#prerequisites)
2. [Quick Reference](#quick-reference)
3. [Initial Server Setup](#initial-server-setup-first-time-only)
4. [Regular Deployments](#regular-deployments)
5. [Activepieces Updates](#activepieces-updates)
6. [Troubleshooting](#troubleshooting)
7. [Maintenance](#maintenance)
## 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`
- Ubuntu 20.04+ or Debian 11+
- 4GB RAM minimum (2GB works but cannot build Activepieces image)
- 40GB disk space
- Docker and Docker Compose v2 installed
- Domain with wildcard DNS configured
### Local Requirements (for deployment)
- Git access to the repository
- SSH access to the production server
- Docker (for building Activepieces image)
### 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)
- DigitalOcean Spaces (for static/media files)
- Stripe (for payments)
- Twilio (for SMS/phone features)
- OpenAI API (optional, for Activepieces AI copilot)
## Pre-Deployment Checklist
### 1. DigitalOcean Spaces Setup
## Quick Reference
```bash
# Create the bucket (if not already created)
aws --profile do-tor1 s3 mb s3://smoothschedule
# Regular deployment (after initial setup)
./deploy.sh
# Set bucket to public-read for static/media files
aws --profile do-tor1 s3api put-bucket-acl \
--bucket smoothschedule \
--acl public-read
# Deploy with Activepieces image rebuild
./deploy.sh --deploy-ap
# 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
# Deploy specific services only
./deploy.sh django nginx
aws --profile do-tor1 s3api put-bucket-cors \
--bucket smoothschedule \
--cors-configuration file://cors.json
# Skip migrations (config changes only)
./deploy.sh --no-migrate
```
### 2. DNS Configuration
## Initial Server Setup (First Time Only)
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
### 1. Server Preparation
```bash
# SSH into production server
ssh poduck@smoothschedule.com
ssh your-user@your-server
# 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
# Logout and login again for group changes
exit
ssh poduck@smoothschedule.com
ssh your-user@your-server
```
### Step 2: Deploy Backend (Django)
### 2. Clone Repository
```bash
# Create deployment directory
mkdir -p ~/smoothschedule
git clone https://your-repo-url ~/smoothschedule
cd ~/smoothschedule/smoothschedule
```
### 3. Create Environment Files
Copy the template files and fill in your values:
```bash
mkdir -p .envs/.production
cp .envs.example/.django .envs/.production/.django
cp .envs.example/.postgres .envs/.production/.postgres
cp .envs.example/.activepieces .envs/.production/.activepieces
```
Edit each file with your production values:
```bash
nano .envs/.production/.django
nano .envs/.production/.postgres
nano .envs/.production/.activepieces
```
**Key values to configure:**
| File | Variable | Description |
|------|----------|-------------|
| `.django` | `DJANGO_SECRET_KEY` | Generate: `openssl rand -hex 32` |
| `.django` | `DJANGO_ALLOWED_HOSTS` | `.yourdomain.com` |
| `.django` | `STRIPE_*` | Your Stripe keys (live keys for production) |
| `.django` | `TWILIO_*` | Your Twilio credentials |
| `.django` | `AWS_*` | DigitalOcean Spaces credentials |
| `.postgres` | `POSTGRES_USER` | Generate random username |
| `.postgres` | `POSTGRES_PASSWORD` | Generate: `openssl rand -hex 32` |
| `.activepieces` | `AP_JWT_SECRET` | Generate: `openssl rand -hex 32` |
| `.activepieces` | `AP_ENCRYPTION_KEY` | Generate: `openssl rand -hex 16` |
| `.activepieces` | `AP_POSTGRES_USERNAME` | Generate random username |
| `.activepieces` | `AP_POSTGRES_PASSWORD` | Generate: `openssl rand -hex 32` |
**Important:** `AP_JWT_SECRET` must be copied to `.django` as well!
### 4. DNS Configuration
Configure these DNS records:
```
Type Name Value TTL
A yourdomain.com YOUR_SERVER_IP 300
A *.yourdomain.com YOUR_SERVER_IP 300
CNAME www yourdomain.com 300
```
### 5. Build Activepieces Image (on your local machine)
The production server typically cannot build this image (requires 4GB+ RAM):
```bash
# On your LOCAL machine, not the server
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
./scripts/build-activepieces.sh deploy
```
### Step 3: Database Initialization
Or manually:
```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
cd activepieces-fork
docker build -t smoothschedule_production_activepieces .
docker save smoothschedule_production_activepieces | gzip > /tmp/ap.tar.gz
scp /tmp/ap.tar.gz your-user@your-server:/tmp/
ssh your-user@your-server 'gunzip -c /tmp/ap.tar.gz | docker load'
```
### Step 4: Create Initial Tenant
### 6. Run Initialization Script
```bash
# On the server
cd ~/smoothschedule/smoothschedule
chmod +x scripts/init-production.sh
./scripts/init-production.sh
```
This script will:
1. Verify environment files
2. Generate any missing security keys
3. Start PostgreSQL and Redis
4. Create the Activepieces database
5. Start all services
6. Run Django migrations
7. Guide you through Activepieces platform setup
### 7. Complete Activepieces Platform Setup
After the init script completes:
1. Visit `https://automations.yourdomain.com`
2. Create an admin account (this creates the platform)
3. Get the platform ID:
```bash
docker compose -f docker-compose.production.yml exec postgres \
psql -U <ap_db_user> -d activepieces -c "SELECT id FROM platform"
```
4. Update `AP_PLATFORM_ID` in both:
- `.envs/.production/.activepieces`
- `.envs/.production/.django`
5. Restart services:
```bash
docker compose -f docker-compose.production.yml restart
```
### 8. Create First 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
from smoothschedule.identity.core.models import Tenant, Domain
User = get_user_model()
# Create a business
business = Business.objects.create(
# Create tenant
tenant = Tenant.objects.create(
name="Demo Business",
subdomain="demo",
schema_name="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"
# Create domain
Domain.objects.create(
tenant=tenant,
domain="demo.yourdomain.com",
is_primary=True
)
print(f"Created owner: {owner.username}")
exit()
```
### Step 5: Deploy Frontend
### 9. Provision Activepieces Connection
```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
docker compose -f docker-compose.production.yml exec django \
python manage.py provision_ap_connections --tenant demo
```
**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
### 10. 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)
# Test endpoints
curl https://yourdomain.com/api/
curl https://platform.yourdomain.com/
curl https://automations.yourdomain.com/api/v1/health
```
## Post-Deployment
## Regular Deployments
### 1. Monitoring
After initial setup, deployments are simple:
```bash
# View logs
docker compose -f docker-compose.production.yml logs -f
# From your local machine
cd ~/smoothschedule
# View specific service logs
docker compose -f docker-compose.production.yml logs -f django
docker compose -f docker-compose.production.yml logs -f postgres
# Commit and push your changes
git add .
git commit -m "Your changes"
git push
# Monitor Celery tasks via Flower
# Access: https://smoothschedule.com:5555
# Login with credentials from .envs/.production/.django
# Deploy
./deploy.sh
```
### 2. Backups
### Deployment Options
| Command | Description |
|---------|-------------|
| `./deploy.sh` | Full deployment with migrations |
| `./deploy.sh --no-migrate` | Deploy without running migrations |
| `./deploy.sh --deploy-ap` | Rebuild and deploy Activepieces image |
| `./deploy.sh django` | Rebuild only Django container |
| `./deploy.sh nginx traefik` | Rebuild specific services |
### What the Deploy Script Does
1. Checks for uncommitted changes
2. Verifies changes are pushed to remote
3. (If `--deploy-ap`) Builds and transfers Activepieces image
4. SSHs to server and pulls latest code
5. Backs up and restores `.envs` directory
6. Builds Docker images
7. Starts containers
8. Sets up Activepieces database (if needed)
9. Runs Django migrations (unless `--no-migrate`)
10. Seeds platform plugins for all tenants
## Activepieces Updates
When you modify custom pieces (in `activepieces-fork/`):
1. Make your changes to piece code
2. Commit and push
3. Deploy with the image flag:
```bash
./deploy.sh --deploy-ap
```
The Activepieces container will:
1. Start with the new image
2. Run `publish-pieces.sh` to register custom pieces
3. Insert piece metadata into the database
### Custom Pieces
Custom pieces are located in:
- `activepieces-fork/packages/pieces/community/smoothschedule/` - Main SmoothSchedule piece
- `activepieces-fork/packages/pieces/community/python-code/` - Python code execution
- `activepieces-fork/packages/pieces/community/ruby-code/` - Ruby code execution
Piece metadata is registered via:
- `activepieces-fork/custom-pieces-metadata.sql` - Database registration
- `activepieces-fork/publish-pieces.sh` - Container startup script
## Troubleshooting
### 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 activepieces
docker compose -f docker-compose.production.yml logs -f traefik
```
### Restart Services
```bash
# All services
docker compose -f docker-compose.production.yml restart
# Specific service
docker compose -f docker-compose.production.yml restart django
docker compose -f docker-compose.production.yml restart activepieces
```
### Django Shell
```bash
docker compose -f docker-compose.production.yml exec django python manage.py shell
```
### Database Access
```bash
# SmoothSchedule database
docker compose -f docker-compose.production.yml exec postgres \
psql -U <postgres_user> -d smoothschedule
# Activepieces database
docker compose -f docker-compose.production.yml exec postgres \
psql -U <ap_user> -d activepieces
```
### Common Issues
**1. Activepieces pieces not showing up**
```bash
# Check if platform exists
docker compose -f docker-compose.production.yml exec postgres \
psql -U <ap_user> -d activepieces -c "SELECT id FROM platform"
# Restart to re-run piece registration
docker compose -f docker-compose.production.yml restart activepieces
# Check logs for errors
docker compose -f docker-compose.production.yml logs activepieces | grep -i error
```
**2. 502 Bad Gateway**
- Service is still starting, wait a moment
- Check container health: `docker compose ps`
- Check logs for errors
**3. Database connection errors**
- Verify credentials in `.envs/.production/`
- Ensure PostgreSQL is running: `docker compose ps postgres`
**4. Activepieces embedding not working**
- Verify `AP_JWT_SECRET` matches in both `.django` and `.activepieces`
- Verify `AP_PLATFORM_ID` is set correctly in both files
- Check `AP_EMBEDDING_ENABLED=true` in `.activepieces`
**5. SSL certificate issues**
```bash
# Check Traefik logs
docker compose -f docker-compose.production.yml logs traefik
# Verify DNS is pointing to server
dig yourdomain.com +short
# Ensure ports 80 and 443 are open
sudo ufw allow 80
sudo ufw allow 443
```
## Maintenance
### Backups
```bash
# Database backup
@@ -329,121 +388,50 @@ docker compose -f docker-compose.production.yml exec postgres backups
docker compose -f docker-compose.production.yml exec postgres restore backup_filename.sql.gz
```
### 3. Updates
### Monitoring
```bash
# Pull latest code
cd ~/smoothschedule/smoothschedule
git pull origin main
- **Flower Dashboard**: `https://yourdomain.com:5555` - Celery task monitoring
- **Container Status**: `docker compose ps`
- **Resource Usage**: `docker stats`
# Rebuild and restart
docker compose -f docker-compose.production.yml build
docker compose -f docker-compose.production.yml up -d
### Security Checklist
# 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
- [x] SSL/HTTPS enabled via Let's Encrypt (automatic with Traefik)
- [x] All secret keys are unique random values
- [x] Database passwords are strong
- [x] Flower dashboard is password protected
- [ ] Firewall configured (UFW)
- [ ] SSH key-based authentication only
- [ ] Regular backups configured
- [ ] Sentry error monitoring (optional)
- [ ] Monitoring/alerting set up
## Performance Optimization
## File Structure
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
```
smoothschedule/
├── deploy.sh # Main deployment script
├── DEPLOYMENT.md # This file
├── scripts/
│ └── build-activepieces.sh # Activepieces image builder
├── smoothschedule/
│ ├── docker-compose.production.yml
│ ├── scripts/
│ │ └── init-production.sh # One-time initialization
│ ├── .envs/
│ │ └── .production/ # Production secrets (NOT in git)
│ │ ├── .django
│ │ ├── .postgres
│ │ └── .activepieces
│ └── .envs.example/ # Template files (in git)
│ ├── .django
│ ├── .postgres
│ └── .activepieces
└── activepieces-fork/
├── Dockerfile
├── custom-pieces-metadata.sql
├── publish-pieces.sh
└── packages/pieces/community/
├── smoothschedule/ # Main custom piece
├── python-code/
└── ruby-code/
```