Files
smoothschedule/smoothschedule/config/urls.py
poduck f8d8419622 Improve deployment process and add login redirect logic
Deployment improvements:
- Add template env files (.envs.example/) for documentation
- Create init-production.sh for one-time server setup
- Create build-activepieces.sh for building/deploying AP image
- Update deploy.sh with --deploy-ap flag
- Make custom-pieces-metadata.sql idempotent
- Update DEPLOYMENT.md with comprehensive instructions

Frontend:
- Redirect logged-in business owners from root domain to tenant dashboard
- Redirect logged-in users from /login to /dashboard on their tenant
- Log out customers on wrong subdomain instead of redirecting

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-20 23:13:56 -05:00

224 lines
11 KiB
Python

from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from django.http import FileResponse, Http404
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
import os
def serve_static_image(request, filename):
"""Serve static images with CORS headers for Activepieces integration."""
allowed_files = {
'logo-branding.png': 'image/png',
'python-logo.svg': 'image/svg+xml',
'ruby-logo.svg': 'image/svg+xml',
}
if filename not in allowed_files:
raise Http404("Image not found")
# Try to find the file in static directories
for static_dir in settings.STATICFILES_DIRS:
file_path = os.path.join(static_dir, 'images', filename)
if os.path.exists(file_path):
response = FileResponse(open(file_path, 'rb'), content_type=allowed_files[filename])
response['Access-Control-Allow-Origin'] = '*'
response['Cache-Control'] = 'public, max-age=86400'
return response
raise Http404("Image not found")
from smoothschedule.identity.users.api_views import (
login_view, 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,
check_subdomain_view, signup_view, signup_setup_intent, send_customer_verification, verify_and_register_customer
)
from smoothschedule.identity.users.mfa_api_views import (
mfa_status, send_phone_verification, verify_phone, enable_sms_mfa,
setup_totp, verify_totp_setup, generate_backup_codes, backup_codes_status,
disable_mfa, mfa_login_send_code, mfa_login_verify,
list_trusted_devices, revoke_trusted_device, revoke_all_trusted_devices
)
from smoothschedule.scheduling.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
)
from smoothschedule.identity.core.email_autoconfig import (
MozillaAutoconfigView,
MicrosoftAutodiscoverView,
AppleConfigProfileView,
WellKnownAutoconfigView,
)
from smoothschedule.identity.core.api_views import (
quota_status_view,
quota_resources_view,
quota_archive_view,
quota_unarchive_view,
quota_overage_detail_view,
)
urlpatterns = [
# Static images with CORS (for Activepieces integration)
path("images/<str:filename>", serve_static_image, name="serve_static_image"),
# Django Admin, use {% url 'admin:index' %}
path(settings.ADMIN_URL, admin.site.urls),
# User management
path("users/", include("smoothschedule.identity.users.urls", namespace="users")),
path("accounts/", include("allauth.urls")),
# Django Hijack (masquerade) - for admin interface
path("hijack/", include("hijack.urls")),
# Email Autoconfiguration (for email clients)
path("mail/config-v1.1.xml", MozillaAutoconfigView.as_view(), name="autoconfig"),
path(".well-known/autoconfig/mail/config-v1.1.xml", WellKnownAutoconfigView.as_view(), name="autoconfig-wellknown"),
path("autodiscover/autodiscover.xml", MicrosoftAutodiscoverView.as_view(), name="autodiscover"),
path("Autodiscover/Autodiscover.xml", MicrosoftAutodiscoverView.as_view(), name="autodiscover-caps"),
path("email/apple-profile.mobileconfig", AppleConfigProfileView.as_view(), name="apple-config"),
# Your stuff: custom urls includes go here
# ...
# Media files
*static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT),
# Static files (for development)
*static(settings.STATIC_URL, document_root=settings.STATICFILES_DIRS[0] if settings.STATICFILES_DIRS else None),
]
# API URLS
urlpatterns += [
# Staff Invitations API - MUST come before schedule.urls to avoid conflict with /staff/ viewset
path("staff/invitations/", staff_invitations_view, name="staff_invitations"),
path("staff/invitations/<int:invitation_id>/", cancel_invitation_view, name="cancel_invitation"),
path("staff/invitations/<int:invitation_id>/resend/", resend_invitation_view, name="resend_invitation"),
path("staff/invitations/token/<str:token>/", invitation_details_view, name="invitation_details"),
path("staff/invitations/token/<str:token>/accept/", accept_invitation_view, name="accept_invitation"),
path("staff/invitations/token/<str:token>/decline/", decline_invitation_view, name="decline_invitation"),
# Stripe Webhooks (dj-stripe built-in handler)
path("stripe/", include("djstripe.urls", namespace="djstripe")),
# Public API v1 (for third-party integrations)
path("v1/", include("smoothschedule.platform.api.urls", namespace="public_api")),
# Tenant Sites API (Site Builder & Public Page)
path("", include("smoothschedule.platform.tenant_sites.urls")),
# Schedule API (internal)
path("", include("smoothschedule.scheduling.schedule.urls")),
# Activepieces Integration API
path("activepieces/", include("smoothschedule.integrations.activepieces.urls", namespace="activepieces")),
# Analytics API
path("", include("smoothschedule.scheduling.analytics.urls")),
# Payments API
path("payments/", include("smoothschedule.commerce.payments.urls")),
# Contracts API
path("contracts/", include("smoothschedule.scheduling.contracts.urls")),
# Communication Credits API
path("communication-credits/", include("smoothschedule.communication.credits.urls", namespace="comms_credits")),
# Field Mobile API (for field employee mobile app)
path("mobile/", include("smoothschedule.communication.mobile.urls", namespace="field_mobile")),
# Tickets API
path("tickets/", include("smoothschedule.commerce.tickets.urls")),
# Notifications API
path("notifications/", include("smoothschedule.communication.notifications.urls")),
# Messaging API (broadcast messages)
path("messages/", include("smoothschedule.communication.messaging.urls")),
# Billing API
path("", include("smoothschedule.billing.api.urls", namespace="billing")),
# Platform API
path("platform/", include("smoothschedule.platform.admin.urls", namespace="platform")),
# OAuth Email Integration API
path("oauth/", include("smoothschedule.identity.core.oauth_urls", namespace="oauth")),
path("auth/oauth/", include("smoothschedule.identity.core.oauth_urls", namespace="auth_oauth")),
# Auth API
path("auth-token/", csrf_exempt(obtain_auth_token), name="obtain_auth_token"),
path("auth/signup/check-subdomain/", check_subdomain_view, name="check_subdomain"),
path("auth/signup/setup-intent/", signup_setup_intent, name="signup_setup_intent"),
path("auth/signup/", signup_view, name="signup"),
path("auth/login/", login_view, name="login"),
path("auth/me/", current_user_view, name="current_user"),
path("auth/logout/", logout_view, name="logout"),
path("auth/email/verify/send/", send_verification_email, name="send_verification_email"),
path("auth/email/verify/", verify_email, name="verify_email"),
# Customer verification for booking flow
path("auth/send-verification/", send_customer_verification, name="send_customer_verification"),
path("auth/verify-and-register/", verify_and_register_customer, name="verify_and_register_customer"),
# Hijack (masquerade) API
path("auth/hijack/acquire/", hijack_acquire_view, name="hijack_acquire"),
path("auth/hijack/release/", hijack_release_view, name="hijack_release"),
# Business API
path("business/current/", current_business_view, name="current_business"),
path("business/current/update/", update_business_view, name="update_business"),
path("business/oauth-settings/", oauth_settings_view, name="oauth_settings"),
path("business/oauth-credentials/", oauth_credentials_view, name="oauth_credentials"),
# Custom Domains API
path("business/domains/", custom_domains_view, name="custom_domains"),
path("business/domains/<int:domain_id>/", custom_domain_detail_view, name="custom_domain_detail"),
path("business/domains/<int:domain_id>/verify/", custom_domain_verify_view, name="custom_domain_verify"),
path("business/domains/<int:domain_id>/set-primary/", custom_domain_set_primary_view, name="custom_domain_set_primary"),
# Sandbox Mode API
path("sandbox/status/", sandbox_status_view, name="sandbox_status"),
path("sandbox/toggle/", sandbox_toggle_view, name="sandbox_toggle"),
path("sandbox/reset/", sandbox_reset_view, name="sandbox_reset"),
# Quota Management API
path("quota/status/", quota_status_view, name="quota_status"),
path("quota/resources/<str:quota_type>/", quota_resources_view, name="quota_resources"),
path("quota/archive/", quota_archive_view, name="quota_archive"),
path("quota/unarchive/", quota_unarchive_view, name="quota_unarchive"),
path("quota/overages/<int:overage_id>/", quota_overage_detail_view, name="quota_overage_detail"),
# MFA (Two-Factor Authentication) API
path("auth/mfa/status/", mfa_status, name="mfa_status"),
path("auth/mfa/phone/send/", send_phone_verification, name="mfa_phone_send"),
path("auth/mfa/phone/verify/", verify_phone, name="mfa_phone_verify"),
path("auth/mfa/sms/enable/", enable_sms_mfa, name="mfa_sms_enable"),
path("auth/mfa/totp/setup/", setup_totp, name="mfa_totp_setup"),
path("auth/mfa/totp/verify/", verify_totp_setup, name="mfa_totp_verify"),
path("auth/mfa/backup-codes/", generate_backup_codes, name="mfa_backup_codes"),
path("auth/mfa/backup-codes/status/", backup_codes_status, name="mfa_backup_codes_status"),
path("auth/mfa/disable/", disable_mfa, name="mfa_disable"),
path("auth/mfa/login/send/", mfa_login_send_code, name="mfa_login_send"),
path("auth/mfa/login/verify/", mfa_login_verify, name="mfa_login_verify"),
path("auth/mfa/devices/", list_trusted_devices, name="mfa_devices_list"),
path("auth/mfa/devices/<int:device_id>/", revoke_trusted_device, name="mfa_device_revoke"),
path("auth/mfa/devices/revoke-all/", revoke_all_trusted_devices, name="mfa_devices_revoke_all"),
# API Docs
path("schema/", SpectacularAPIView.as_view(), name="api-schema"),
path(
"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,
]