The Timeline component was using a raw axios instance with hardcoded URLs, causing it to bypass authentication and tenant context headers. This resulted in empty or failed data fetches. Updated it to use the configured 'apiClient', ensuring that the authentication token and 'X-Business-Subdomain' headers are correctly sent, allowing the backend to return the appropriate tenant-specific resources and appointments.
SmoothSchedule - Multi-Tenant Scheduling Platform
A production-ready multi-tenant SaaS platform for resource scheduling and orchestration.
🎯 Features
- ✅ Multi-Tenancy: PostgreSQL schema-per-tenant using django-tenants
- ✅ 8-Tier Role Hierarchy: From SUPERUSER to CUSTOMER with strict permissions
- ✅ Modern Stack: Django 5.2 + React 18 + Vite
- ✅ Docker Ready: Complete production & development Docker Compose setup
- ✅ Cloud Storage: DigitalOcean Spaces (S3-compatible) for static/media files
- ✅ Auto SSL: Let's Encrypt certificates via Traefik reverse proxy
- ✅ Task Queue: Celery + Redis for background jobs
- ✅ Real-time: Django Channels + WebSockets support
- ✅ Production Ready: Fully configured for deployment
📚 Documentation
- PRODUCTION_DEPLOYMENT.md - Manual step-by-step production deployment (start here for fresh deployments)
- QUICK-REFERENCE.md - Common commands and quick start
- PRODUCTION-READY.md - Production deployment status
- DEPLOYMENT.md - Comprehensive deployment guide
- CLAUDE.md - Development guide and architecture
🚀 Quick Start
Local Development
# Start backend (Django in Docker)
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
See CLAUDE.md for detailed development instructions.
Production Deployment
For fresh deployments or complete reset, follow PRODUCTION_DEPLOYMENT.md for manual step-by-step instructions.
For routine updates, use the automated script:
# Deploy to production server (code changes only)
./deploy.sh poduck@smoothschedule.com
See PRODUCTION-READY.md for deployment checklist and DEPLOYMENT.md for detailed steps.
🏗️ Architecture
Multi-Tenancy Model
┌─────────────────────────────────────────┐
│ PostgreSQL Database │
├─────────────────────────────────────────┤
│ public (shared schema) │
│ ├─ Tenants │
│ ├─ Domains │
│ ├─ Users │
│ └─ PermissionGrants │
├─────────────────────────────────────────┤
│ tenant_demo (schema for Demo Company) │
│ ├─ Appointments │
│ ├─ Resources │
│ └─ Customers │
├─────────────────────────────────────────┤
│ tenant_acme (schema for Acme Corp) │
│ ├─ Appointments │
│ ├─ Resources │
│ └─ Customers │
└─────────────────────────────────────────┘
Role Hierarchy
| Role | Level | Access Scope |
|---|---|---|
| SUPERUSER | Platform | All tenants (god mode) |
| PLATFORM_MANAGER | Platform | All tenants |
| PLATFORM_SALES | Platform | Demo accounts only |
| PLATFORM_SUPPORT | Platform | Tenant users |
| TENANT_OWNER | Tenant | Own tenant (full access) |
| TENANT_MANAGER | Tenant | Own tenant |
| TENANT_STAFF | Tenant | Own tenant (limited) |
| CUSTOMER | Tenant | Own data only |
Masquerading Matrix
| Hijacker Role | Can Masquerade As |
|---|---|
| SUPERUSER | Anyone |
| PLATFORM_SUPPORT | Tenant users |
| PLATFORM_SALES | Demo accounts (is_temporary=True) |
| TENANT_OWNER | Staff in same tenant |
| Others | No one |
Security Rules:
- Cannot hijack yourself
- Cannot hijack SUPERUSERs (except by other SUPERUSERs)
- Maximum depth: 1 (no hijack chains)
- All attempts logged to
logs/masquerade.log
📁 Project Structure
smoothschedule/
├── config/
│ └── settings.py # Multi-tenancy & security config
├── core/
│ ├── models.py # Tenant, Domain, PermissionGrant
│ ├── permissions.py # Hijack permission matrix
│ ├── middleware.py # Masquerade audit logging
│ └── admin.py # Django admin for core models
├── users/
│ ├── models.py # Custom User with 8-tier roles
│ └── admin.py # User admin with hijack button
├── logs/
│ ├── security.log # General security events
│ └── masquerade.log # Hijack activity (JSON)
└── setup_project.sh # Automated setup script
🔐 Security Features
Audit Logging
All masquerade activity is logged in JSON format:
{
"timestamp": "2024-01-15T10:30:00Z",
"action": "HIJACK_START",
"hijacker_email": "support@smoothschedule.com",
"hijacked_email": "customer@demo.com",
"ip_address": "192.168.1.1",
"session_key": "abc123..."
}
Permission Grants (30-Minute Window)
Time-limited elevated permissions:
from core.models import PermissionGrant
grant = PermissionGrant.create_grant(
grantor=admin_user,
grantee=support_user,
action="view_billing",
reason="Customer requested billing support",
duration_minutes=30,
)
# Check if active
if grant.is_active():
# Perform privileged action
pass
🧪 Testing Masquerading
- Access Django Admin:
http://localhost:8000/admin/ - Create test users with different roles
- Click "Hijack" button next to a user
- Verify audit logs:
docker-compose exec django cat logs/masquerade.log
📊 Admin Interface
- Tenant Management: View tenants, domains, subscription tiers
- User Management: Color-coded roles, masquerade buttons
- Permission Grants: Active/expired/revoked status, bulk revoke
- Domain Verification: AWS Route53 integration status
🛠️ Development
Adding Tenant Apps
Edit config/settings.py:
TENANT_APPS = [
'django.contrib.contenttypes',
'appointments', # Your app
'resources', # Your app
'billing', # Your app
]
Custom Domain Setup
domain = Domain.objects.create(
domain="app.customdomain.com",
tenant=tenant,
is_custom_domain=True,
route53_zone_id="Z1234567890ABC",
)
Then configure Route53 CNAME: app.customdomain.com → smoothschedule.yourhost.com
📖 Key Files Reference
| File | Purpose |
|---|---|
setup_project.sh |
Automated project initialization |
config/settings.py |
Multi-tenancy, middleware, security config |
core/models.py |
Tenant, Domain, PermissionGrant models |
core/permissions.py |
Masquerading permission matrix |
core/middleware.py |
Audit logging for masquerading |
users/models.py |
Custom User with 8-tier roles |
📝 Important Notes
- Django Admin: The ONLY HTML interface (everything else is API)
- Middleware Order:
TenantMainMiddlewaremust be first,MasqueradeAuditMiddlewareafterHijackUserMiddleware - Tenant Isolation: Each tenant's data is in a separate PostgreSQL schema
- Production: Update
SECRET_KEY, database credentials, and AWS keys via environment variables
🐛 Troubleshooting
Cannot create tenant users:
- Error: "Users with role TENANT_STAFF must be assigned to a tenant"
- Solution: Set
user.tenant = tenant_instancebefore saving
Hijack button doesn't appear:
- Check
HIJACK_AUTHORIZATION_CHECKin settings - Verify
HijackUserAdminMixininusers/admin.py - Ensure user has permission per matrix rules
Migrations fail:
- Run shared migrations first:
migrate_schemas --shared - Then run tenant migrations:
migrate_schemas
📄 License
MIT
🤝 Contributing
This is a production skeleton. Extend TENANT_APPS with your business logic.
Built with ❤️ for multi-tenant SaaS perfection