poduck a274d70cec feat(websocket): Resolve ticket WebSocket disconnection/reconnection issue
This commit addresses the persistent WebSocket disconnection and reconnection
problem experienced with ticket updates. The root cause was identified as the
Django backend not running as an ASGI server, which is essential for WebSocket
functionality, and incorrect WebSocket routing.

The following changes were made:

- **Frontend ():**
  - Updated to append the  from cookies to the WebSocket URL's
    query parameter for authentication, ensuring the token is sent with the
    WebSocket connection request.

- **Backend Configuration:**
  - **:** Modified to explicitly
    start the Daphne ASGI server using  instead
    of . This ensures the backend runs in ASGI
    mode, capable of handling WebSocket connections.
  - **:** Removed 'daphne' from
    . Daphne is an ASGI server, not a traditional Django
    application, and its presence in  was causing application
    startup failures.
  - **:**
    - Removed  from  as it
      conflicts with Channels' ASGI server takeover.
    - Explicitly set  to ensure
      the ASGI entry point is correctly referenced.
  - **:** Added 'channels'
    to , ensuring the Channels application is correctly loaded
    within the multi-tenant setup, enabling ASGI functionality.

- **Backend Middleware & Routing:**
  - **:** Implemented a custom
     to authenticate WebSocket connections using an
     from either a query parameter or cookies. This middleware
    ensures proper user authentication for WebSocket sessions. Debugging
    prints with  were added for better visibility.
  - **:** Adjusted WebSocket URL regexes
    to  for robustness, ensuring correct matching
    regardless of leading/trailing slashes in the path.

These changes collectively ensure that WebSocket connections are properly
initiated by the frontend, authenticated by the backend, and served by
an ASGI-compliant server, resolving the frequent disconnection/reconnection
issue.
2025-12-01 01:40:45 -05:00

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

🚀 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

  1. Access Django Admin: http://localhost:8000/admin/
  2. Create test users with different roles
  3. Click "Hijack" button next to a user
  4. 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.comsmoothschedule.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: TenantMainMiddleware must be first, MasqueradeAuditMiddleware after HijackUserMiddleware
  • 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_instance before saving

Hijack button doesn't appear:

  • Check HIJACK_AUTHORIZATION_CHECK in settings
  • Verify HijackUserAdminMixin in users/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

Description
No description provided
Readme 43 MiB
Languages
TypeScript 58.2%
Python 21.8%
HTML 18.8%
JavaScript 0.4%
Shell 0.3%
Other 0.4%