This commit adds major features for sandbox isolation, public API access, and platform support ticketing. ## Sandbox Mode - Add sandbox mode toggle for businesses to test features without affecting live data - Implement schema-based isolation for tenant data (appointments, resources, services) - Add is_sandbox field filtering for shared models (customers, staff, tickets) - Create sandbox middleware to detect and set sandbox mode from cookies - Add sandbox context and hooks for React frontend - Display sandbox banner when in test mode - Auto-reload page when switching between live/test modes - Prevent platform support tickets from being created in sandbox mode ## Public API System - Full REST API for external integrations with businesses - API token management with sandbox/live token separation - Test tokens (ss_test_*) show full plaintext for easy testing - Live tokens (ss_live_*) are hashed and secure - Security validation prevents live token plaintext storage - Comprehensive test suite for token security - Rate limiting and throttling per token - Webhook support for real-time event notifications - Scoped permissions system (read/write per resource type) - API documentation page with interactive examples - Token revocation with confirmation modal ## Platform Support - Dedicated support page for businesses to contact SmoothSchedule - View all platform support tickets in one place - Create new support tickets with simplified interface - Reply to existing tickets with conversation history - Platform tickets have no admin controls (no priority/category/assignee/status) - Internal notes hidden for platform tickets (business can't see them) - Quick help section with links to guides and API docs - Sandbox warning prevents ticket creation in test mode - Business ticketing retains full admin controls (priority, assignment, internal notes) ## UI/UX Improvements - Add notification dropdown with real-time updates - Staff permissions UI for ticket access control - Help dropdown in sidebar with Platform Guide, Ticketing Help, API Docs, and Support - Update sidebar "Contact Support" to "Support" with message icon - Fix navigation links to use React Router instead of anchor tags - Remove unused language translations (Japanese, Portuguese, Chinese) ## Technical Details - Sandbox middleware sets request.sandbox_mode from cookies - ViewSets filter data by is_sandbox field - API authentication via custom token auth class - WebSocket support for real-time ticket updates - Migration for sandbox fields on User, Tenant, and Ticket models - Comprehensive documentation in SANDBOX_MODE_IMPLEMENTATION.md 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
6.0 KiB
Sandbox Mode Implementation Summary
Overview
Sandbox/Test mode provides complete data isolation for testing. Users can toggle between Live and Test modes via a switch in the header. Each mode has its own:
- Database schema (for tenant-specific data like appointments, resources, services)
- Customer records (filtered by
is_sandboxflag on User model)
Architecture
Backend Components
-
Tenant Model (
core/models.py)sandbox_schema_name: PostgreSQL schema for sandbox data (e.g.,demo_sandbox)sandbox_enabled: Boolean to enable/disable sandbox for tenant- Auto-generates sandbox schema name on save
-
SandboxModeMiddleware (
core/middleware.py:16-118)- Switches database schema based on:
- API token prefix (
ss_test_*= sandbox,ss_live_*= live) X-Sandbox-Mode: trueheader- Session value
sandbox_mode
- API token prefix (
- Sets
request.sandbox_mode = True/Falsefor views to use - MUST run AFTER
SessionMiddlewarein middleware order
- Switches database schema based on:
-
User Model (
smoothschedule/users/models.py)is_sandbox: Boolean field to mark sandbox customers- Live customers have
is_sandbox=False, test customers haveis_sandbox=True
-
API Endpoints (
schedule/api_views.py)GET /api/sandbox/status/- Get current sandbox statePOST /api/sandbox/toggle/- Toggle sandbox mode (sets session)
-
CustomerViewSet (
schedule/views.py:199-249)- Filters customers by
request.sandbox_mode perform_createsetsis_sandboxbased on current mode
- Filters customers by
-
StaffViewSet (
schedule/views.py:302-366)- Filters staff by
request.sandbox_mode - Staff created via invitations inherit sandbox mode from request
- Filters staff by
-
TicketViewSet (
tickets/views.py:65-167)- Filters tickets by
request.sandbox_mode(except PLATFORM tickets) perform_createsetsis_sandboxbased on current mode- PLATFORM tickets are always created in live mode
- Filters tickets by
-
PublicCustomerViewSet (
public_api/views.py:888-968)- Also filters by sandbox mode for API customers
-
APIToken Model (
public_api/models.py)is_sandbox: Boolean for token type- Key prefixes:
ss_test_*(sandbox) orss_live_*(live)
Frontend Components
-
SandboxContext (
contexts/SandboxContext.tsx)- Provides
isSandbox,sandboxEnabled,toggleSandbox,isToggling - Syncs state to localStorage for API client
- Provides
-
SandboxToggle (
components/SandboxToggle.tsx)- Toggle switch component with Live/Test labels
-
SandboxBanner (
components/SandboxBanner.tsx)- Orange warning banner shown in test mode
-
API Client (
api/client.ts:23-51)- Reads
localStorage.getItem('sandbox_mode') - Adds
X-Sandbox-Mode: trueheader when in sandbox
- Reads
-
BusinessLayout (
layouts/BusinessLayout.tsx)- Wrapped with
SandboxProvider - Shows
SandboxBannerwhen in test mode
- Wrapped with
-
TopBar (
components/TopBar.tsx)- Includes
SandboxTogglecomponent
- Includes
Configuration
-
CORS (
config/settings/local.py:75-78)x-sandbox-modeadded toCORS_ALLOW_HEADERS
-
Middleware Order (
config/settings/multitenancy.py:89-122)- SandboxModeMiddleware MUST come AFTER SessionMiddleware
Database Schemas
Each tenant has two schemas:
{tenant_name}- Live data (e.g.,demo){tenant_name}_sandbox- Test data (e.g.,demo_sandbox)
Schemas created via: python manage.py create_sandbox_schemas
What's Isolated
| Data Type | Isolation Method |
|---|---|
| Appointments/Events | Schema switching (automatic) |
| Resources | Schema switching (automatic) |
| Services | Schema switching (automatic) |
| Payments | Schema switching (automatic) |
| Notifications | Schema switching (automatic) |
| Communication | Schema switching (automatic) |
| Customers | is_sandbox field on User model |
| Staff Members | is_sandbox field on User model |
| Tickets (CUSTOMER/STAFF_REQUEST/INTERNAL) | is_sandbox field on Ticket model |
| Tickets (PLATFORM) | NOT isolated (always live - platform support) |
| Business Settings (Tenant) | NOT isolated (shared between modes) |
Key Files Modified
Backend
core/models.py- Tenant sandbox fieldscore/middleware.py- SandboxModeMiddlewaresmoothschedule/users/models.py- User.is_sandbox fieldsmoothschedule/users/api_views.py- accept_invitation_view sets is_sandboxschedule/views.py- CustomerViewSet and StaffViewSet sandbox filteringschedule/api_views.py- sandbox_status_view, sandbox_toggle_viewtickets/models.py- Ticket.is_sandbox fieldtickets/views.py- TicketViewSet sandbox filteringpublic_api/models.py- APIToken.is_sandboxpublic_api/views.py- PublicCustomerViewSet sandbox filteringconfig/settings/local.py- CORS headersconfig/settings/multitenancy.py- Middleware order, tickets in SHARED_APPS
Frontend
src/api/sandbox.ts- API functionssrc/api/client.ts- X-Sandbox-Mode headersrc/hooks/useSandbox.ts- React Query hookssrc/contexts/SandboxContext.tsx- Context providersrc/components/SandboxToggle.tsx- Toggle UIsrc/components/SandboxBanner.tsx- Warning bannersrc/components/TopBar.tsx- Added togglesrc/layouts/BusinessLayout.tsx- Provider + bannersrc/i18n/locales/en.json- Translations
Migrations
# Migrations for User.is_sandbox and Ticket.is_sandbox fields
cd /home/poduck/Desktop/smoothschedule2/smoothschedule
docker compose -f docker-compose.local.yml exec django python manage.py migrate
Current State
- ✅ Sandbox mode toggle works
- ✅ CORS configured for X-Sandbox-Mode header
- ✅ Customer isolation by is_sandbox field implemented
- ✅ Staff isolation by is_sandbox field implemented
- ✅ Ticket isolation by is_sandbox field implemented (except PLATFORM tickets)
- ✅ Appointments/Events/Resources/Services automatically isolated via schema switching
- ✅ Existing users are
is_sandbox=False(live) - ✅ Existing tickets are
is_sandbox=False(live) - ✅ Test mode shows empty data (clean sandbox)