Initial commit: SmoothSchedule multi-tenant scheduling platform

This commit includes:
- Django backend with multi-tenancy (django-tenants)
- React + TypeScript frontend with Vite
- Platform administration API with role-based access control
- Authentication system with token-based auth
- Quick login dev tools for testing different user roles
- CORS and CSRF configuration for local development
- Docker development environment setup

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
poduck
2025-11-27 01:43:20 -05:00
commit 2e111364a2
567 changed files with 96410 additions and 0 deletions

View File

@@ -0,0 +1,213 @@
"""
Smooth Schedule - Multi-Tenancy Settings Override
This file extends base.py and adds django-tenants configuration
"""
from .base import * # noqa
from .base import INSTALLED_APPS, MIDDLEWARE, DATABASES, LOGGING, env
# =============================================================================
# MULTI-TENANCY CONFIGURATION (django-tenants)
# =============================================================================
# Shared apps - Available to all tenants (stored in 'public' schema)
SHARED_APPS = [
'django_tenants', # Must be first
'core', # Core models (Tenant, Domain, PermissionGrant)
# Django built-ins (must be in shared
'django.contrib.contenttypes',
'django.contrib.auth',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.admin',
# Users app (shared across tenants)
'smoothschedule.users',
# Third-party apps that should be shared
'rest_framework',
'rest_framework.authtoken',
'corsheaders',
'drf_spectacular',
'allauth',
'allauth.account',
'allauth.mfa',
'allauth.socialaccount',
'django_celery_beat',
'hijack',
'hijack.contrib.admin',
'crispy_forms',
'crispy_bootstrap5',
]
# Tenant-specific apps - Each tenant gets isolated data in their own schema
TENANT_APPS = [
'django.contrib.contenttypes', # Needed for tenant schemas
'schedule', # Resource scheduling with configurable concurrency
'payments', # Stripe Connect payments bridge
'communication', # Twilio masked communications
# Add your tenant-scoped business logic apps here:
# 'appointments',
# 'customers',
# 'analytics',
]
# Override INSTALLED_APPS to include all unique apps
INSTALLED_APPS = list(dict.fromkeys(SHARED_APPS + TENANT_APPS))
# Tenant model configuration
TENANT_MODEL = "core.Tenant"
TENANT_DOMAIN_MODEL = "core.Domain"
PUBLIC_SCHEMA_NAME = 'public'
# =============================================================================
# DATABASE CONFIGURATION (Multi-schema)
# =============================================================================
# Override database engine for django-tenants
DATABASES['default']['ENGINE'] ='django_tenants.postgresql_backend'
# Database routers for tenant isolation
DATABASE_ROUTERS = [
'django_tenants.routers.TenantSyncRouter',
]
# =============================================================================
# MIDDLEWARE CONFIGURATION
# =============================================================================
# CRITICAL: Order matters!
MIDDLEWARE = [
# 1. MUST BE FIRST: Tenant resolution
'django_tenants.middleware.main.TenantMainMiddleware',
# 2. Security middleware
'django.middleware.security.SecurityMiddleware',
'corsheaders.middleware.CorsMiddleware', # Moved up for better CORS handling
'whitenoise.middleware.WhiteNoiseMiddleware',
# 3. Session & CSRF
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
# 4. Authentication
'django.contrib.auth.middleware.AuthenticationMiddleware',
# 5. Hijack (Masquerading) - MUST come before our audit middleware
'hijack.middleware.HijackUserMiddleware',
# 6. MASQUERADE AUDIT - MUST come AFTER HijackUserMiddleware
'core.middleware.MasqueradeAuditMiddleware',
# 7. Messages, Clickjacking, and Allauth
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'allauth.account.middleware.AccountMiddleware',
]
# =============================================================================
# TEMPLATE CONTEXT PROCESSORS (for admin)
# =============================================================================
# Ensure required context processors are present
from .base import TEMPLATES # noqa
if TEMPLATES and len(TEMPLATES) > 0:
context_processors = TEMPLATES[0]['OPTIONS']['context_processors']
if 'django.contrib.auth.context_processors.auth' not in context_processors:
context_processors.insert(0, 'django.contrib.auth.context_processors.auth')
if 'django.contrib.messages.context_processors.messages' not in context_processors:
context_processors.append('django.contrib.messages.context_processors.messages')
# =============================================================================
# PASSWORD VALIDATION
# =============================================================================
# Password hashers
PASSWORD_HASHERS = [
'django.contrib.auth.hashers.Argon2PasswordHasher',
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
]
# Password validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
'OPTIONS': {
'min_length': 10,
}
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# =============================================================================
# HIJACK (MASQUERADING) CONFIGURATION
# =============================================================================
HIJACK_AUTHORIZATION_CHECK = 'core.permissions.can_hijack'
HIJACK_DISPLAY_ADMIN_BUTTON = True
HIJACK_USE_BOOTSTRAP = True
HIJACK_ALLOW_GET_REQUESTS = False # Security: require POST
HIJACK_INSERT_BEFORE = True
# =============================================================================
# ENHANCED LOGGING FOR SECURITY AUDIT
# =============================================================================
# Extend existing logging configuration
LOGGING['formatters']['json'] = {
'()': 'django.utils.log.ServerFormatter',
'format': '[{server_time}] {message}',
'style': '{',
}
LOGGING['handlers']['security_file'] = {
'class': 'logging.handlers.RotatingFileHandler',
'filename': str(BASE_DIR / 'logs' / 'security.log'),
'maxBytes': 1024 * 1024 * 10, # 10 MB
'backupCount': 5,
'formatter': 'json',
}
LOGGING['handlers']['masquerade_file'] = {
'class': 'logging.handlers.RotatingFileHandler',
'filename': str(BASE_DIR / 'logs' / 'masquerade.log'),
'maxBytes': 1024 * 1024 * 10, # 10 MB
'backupCount': 5,
'formatter': 'json',
}
# Ensure 'loggers' key exists in LOGGING
if 'loggers' not in LOGGING:
LOGGING['loggers'] = {}
LOGGING['loggers']['smoothschedule.security'] = {
'handlers': ['console', 'security_file'],
'level': 'INFO',
'propagate': False,
}
LOGGING['loggers']['smoothschedule.security.masquerade'] = {
'handlers': ['console', 'masquerade_file'],
'level': 'INFO',
'propagate': False,
}
# Create logs directory if it doesn't exist
import os
os.makedirs(BASE_DIR / 'logs', exist_ok=True)