Files
smoothschedule/smoothschedule/config/urls.py
poduck a9719a5fd2 feat: Add comprehensive sandbox mode, public API system, and platform support
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>
2025-11-28 16:44:06 -05:00

120 lines
5.4 KiB
Python

from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from django.urls import include
from django.urls import path
from django.views import defaults as default_views
from django.views.decorators.csrf import csrf_exempt
from django.views.generic import TemplateView
from drf_spectacular.views import SpectacularAPIView
from drf_spectacular.views import SpectacularSwaggerView
from rest_framework.authtoken.views import obtain_auth_token
from smoothschedule.users.api_views import (
current_user_view, logout_view, send_verification_email, verify_email,
hijack_acquire_view, hijack_release_view,
staff_invitations_view, cancel_invitation_view, resend_invitation_view,
invitation_details_view, accept_invitation_view, decline_invitation_view
)
from schedule.api_views import (
current_business_view, update_business_view,
oauth_settings_view, oauth_credentials_view,
custom_domains_view, custom_domain_detail_view,
custom_domain_verify_view, custom_domain_set_primary_view,
sandbox_status_view, sandbox_toggle_view, sandbox_reset_view
)
urlpatterns = [
# Django Admin, use {% url 'admin:index' %}
path(settings.ADMIN_URL, admin.site.urls),
# User management
path("users/", include("smoothschedule.users.urls", namespace="users")),
path("accounts/", include("allauth.urls")),
# Django Hijack (masquerade) - for admin interface
path("hijack/", include("hijack.urls")),
# Your stuff: custom urls includes go here
# ...
# Media files
*static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT),
]
# API URLS
urlpatterns += [
# Public API v1 (for third-party integrations)
path("api/v1/", include("smoothschedule.public_api.urls", namespace="public_api")),
# Schedule API (internal)
path("api/", include("schedule.urls")),
# Payments API
path("api/payments/", include("payments.urls")),
# Tickets API
path("api/tickets/", include("tickets.urls")),
# Notifications API
path("api/notifications/", include("notifications.urls")),
# Platform API
path("api/platform/", include("platform_admin.urls", namespace="platform")),
# Auth API
path("api/auth-token/", csrf_exempt(obtain_auth_token), name="obtain_auth_token"),
path("api/auth/me/", current_user_view, name="current_user"),
path("api/auth/logout/", logout_view, name="logout"),
path("api/auth/email/verify/send/", send_verification_email, name="send_verification_email"),
path("api/auth/email/verify/", verify_email, name="verify_email"),
# Hijack (masquerade) API
path("api/auth/hijack/acquire/", hijack_acquire_view, name="hijack_acquire"),
path("api/auth/hijack/release/", hijack_release_view, name="hijack_release"),
# Staff Invitations API
path("api/staff/invitations/", staff_invitations_view, name="staff_invitations"),
path("api/staff/invitations/<int:invitation_id>/", cancel_invitation_view, name="cancel_invitation"),
path("api/staff/invitations/<int:invitation_id>/resend/", resend_invitation_view, name="resend_invitation"),
path("api/staff/invitations/token/<str:token>/", invitation_details_view, name="invitation_details"),
path("api/staff/invitations/token/<str:token>/accept/", accept_invitation_view, name="accept_invitation"),
path("api/staff/invitations/token/<str:token>/decline/", decline_invitation_view, name="decline_invitation"),
# Business API
path("api/business/current/", current_business_view, name="current_business"),
path("api/business/current/update/", update_business_view, name="update_business"),
path("api/business/oauth-settings/", oauth_settings_view, name="oauth_settings"),
path("api/business/oauth-credentials/", oauth_credentials_view, name="oauth_credentials"),
# Custom Domains API
path("api/business/domains/", custom_domains_view, name="custom_domains"),
path("api/business/domains/<int:domain_id>/", custom_domain_detail_view, name="custom_domain_detail"),
path("api/business/domains/<int:domain_id>/verify/", custom_domain_verify_view, name="custom_domain_verify"),
path("api/business/domains/<int:domain_id>/set-primary/", custom_domain_set_primary_view, name="custom_domain_set_primary"),
# Sandbox Mode API
path("api/sandbox/status/", sandbox_status_view, name="sandbox_status"),
path("api/sandbox/toggle/", sandbox_toggle_view, name="sandbox_toggle"),
path("api/sandbox/reset/", sandbox_reset_view, name="sandbox_reset"),
# API Docs
path("api/schema/", SpectacularAPIView.as_view(), name="api-schema"),
path(
"api/docs/",
SpectacularSwaggerView.as_view(url_name="api-schema"),
name="api-docs",
),
]
if settings.DEBUG:
urlpatterns += [
path(
"400/",
default_views.bad_request,
kwargs={"exception": Exception("Bad Request!")},
),
path(
"403/",
default_views.permission_denied,
kwargs={"exception": Exception("Permission Denied")},
),
path(
"404/",
default_views.page_not_found,
kwargs={"exception": Exception("Page not Found")},
),
path("500/", default_views.server_error),
]
if "debug_toolbar" in settings.INSTALLED_APPS:
import debug_toolbar
urlpatterns = [
path("__debug__/", include(debug_toolbar.urls)),
*urlpatterns,
]