In YAML single-quoted strings, backslashes are literal characters.
'\\.' was being interpreted as two backslashes + dot, not as an
escaped dot in the regex.
Changed from '\\.smoothschedule\\.com' to '\.smoothschedule\.com'
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The TCP router was intercepting wildcard subdomain traffic at the TCP layer
and sending it directly to nginx:80, bypassing HTTP routing entirely.
This caused 404 errors because nginx wasn't receiving proper HTTP routing.
Now relying on:
- TLS store's defaultGeneratedCert for wildcard certificate
- HTTP HostRegexp router for subdomain routing to nginx
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add support for specifying services to rebuild (e.g., ./deploy.sh traefik)
- Add --no-migrate flag to skip migrations for config-only changes
- Reduce wait time from 10s to 5s
Usage examples:
./deploy.sh # Build all, run migrations
./deploy.sh traefik --no-migrate # Only rebuild traefik, skip migrations
./deploy.sh django nginx # Build django and nginx, run migrations
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add a TCP-level router using HostSNIRegexp to match unknown subdomains
at the TLS layer and terminate TLS with wildcard certificate.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add default TLS store with wildcard certificate for unknown SNIs
- Add priority=1 to subdomain-router for catch-all behavior
- Use proper Traefik v3 HostRegexp syntax with anchors
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Separate acme.json storage for HTTP and DNS certificate resolvers
to prevent conflicts when requesting wildcard certificates.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Traefik v3 changed HostRegexp syntax from named capture groups to
standard regex. Added low priority to avoid matching specific routes.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
HostRegexp patterns don't work with HTTP challenge because Traefik
can't request certificates for dynamic subdomains. Switched to DNS
challenge using DigitalOcean provider for *.smoothschedule.com wildcard.
Requires DO_AUTH_TOKEN environment variable to be set.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The subdomain-router was incorrectly sending tenant subdomain requests
directly to Django (API server), causing 404 errors. Now routes to nginx
which serves the React SPA and proxies /api/ requests to Django.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add elapsed time counter (MM:SS)
- Spread animation steps over ~30 seconds before final step
- Final step stays spinning (doesn't complete early)
- Progress bar caps at 90% until actually done, pulses on final step
- Show "Finalizing..." and helpful message during long final step
- Clear "45-90 seconds" time estimate upfront
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add multi-step animated loading indicator during tenant creation
- Fix blank completion screen (was checking wrong step number)
- Auto-verify email for users accepting tenant invitations
- Show progress bar and step-by-step status during database setup
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The post_save signal was trying to seed plugins before the tenant's
schema migrations had completed, causing a 500 error when accepting
tenant invitations. Using transaction.on_commit() ensures the schema
and tables exist before seeding.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Platform/tenant invitations should redirect to /tenant-onboard which has
the full onboarding wizard, not /accept-invite which is for staff invitations.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Update useInvitationDetails to try platform tenant invitation first,
then fall back to staff invitation
- Update useAcceptInvitation to handle both invitation types
- Update useDeclineInvitation to handle both invitation types
- Pass invitation type from AcceptInvitePage to mutations
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Requires changes to be committed and pushed before deploying
- Clones repo on first deploy, then uses git fetch/reset
- Preserves .envs secrets which are not in git
- Better for multi-developer workflows
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Don't redirect unauthenticated users to login when accessing public paths
like /accept-invite, /verify-email, or /tenant-onboard on subdomains.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add SMTP email backend support to production settings (EMAIL_HOST, EMAIL_PORT, etc.)
- Falls back to console backend if SMTP not configured
- Fix AcceptInvitePage to support both path parameter (/accept-invite/:token) and
query parameter (?token=...) formats for invitation tokens
- Add route for /accept-invite/:token in App.tsx
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Remove volume mount for SSH keys and rely on Dockerfile COPY instruction
to bake SSH keys into the image during build. This ensures proper
permissions and ownership for the django user.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Mount SSH keys in Django container for mail server access
- Add mail server configuration environment variables
- Import mail server settings in production.py with env defaults
- Configure mail.talova.net SSH connection parameters
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Major updates including:
- Customizable dashboard with drag-and-drop widget grid layout
- Plan-based feature locking for plugins and tasks
- Comprehensive help documentation updates across all pages
- Plugin seeding in deployment process for all tenants
- Permission synchronization system for subscription plans
- QuotaOverageModal component and enhanced UX flows
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed documentation that incorrectly stated the pending appointments
sidebar appears on the right side of the scheduler when it actually
appears on the left side.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Rewrote HelpScheduler.tsx to document actual scheduler features including:
- Drag-and-drop to reschedule, change resource, or delete appointments
- Resize appointments by dragging edges (start or end)
- Pending appointments sidebar with archive zone
- Undo/Redo with Ctrl+Z/Ctrl+Y (up to 50 actions)
- Zoom controls for timeline detail
- Status colors (blue/yellow/red/green/gray)
- Filtering by status, resource, and service
- Overlapping appointment lanes
- Real-time WebSocket updates
- Month view click-to-day and drag overlay features
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replaced inline HelpButton components with a global FloatingHelpButton
that appears fixed in the top-right corner of all pages. The button:
- Automatically detects the current route and links to the appropriate help page
- Uses a consistent position across all pages (fixed, top-right)
- Is hidden on help pages themselves
- Works on both business and platform layouts
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add locked state to Plugins sidebar item with plan feature check
- Create Branding section in settings with Appearance, Email Templates, Custom Domains
- Split Domains page into Booking (URLs, redirects) and Custom Domains (BYOD, purchase)
- Add booking_return_url field to Tenant model for customer redirects
- Update SidebarItem component to support locked prop with lock icon
- Move Email Templates from main sidebar to Settings > Branding
- Add communication credits hooks and payment form updates
- Add timezone fields migration and various UI improvements
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Fix Stripe SDK v14 compatibility (bracket notation for subscription items)
- Fix subscription period dates from subscription items instead of subscription object
- Add tier-based permissions (can_accept_payments, etc.) on tenant signup
- Add stripe_customer_id field to Tenant model
- Add clickable logo on login page (navigates to /)
- Add database setup message during signup wizard
- Add dark mode support for payment settings and Connect onboarding
- Add subscription management endpoints (cancel, reactivate)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Create reusable ConfirmationModal component with variants (info, warning, danger, success)
- Replace browser confirm() dialogs with styled modal for email verification
- Update PlatformBusinesses and PlatformUsers to use the new modal
- Add translation keys for verification messages
- Unverify test@example.com for testing
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The PlatformUserViewSet restricted HTTP methods to GET and PATCH,
but the verify_email action requires POST. Added POST to allowed
methods.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add email template presets for Browse Templates tab (12 templates)
- Add bulk selection and deletion for My Templates tab
- Add communication credits system with Twilio integration
- Add payment views for credit purchases and auto-reload
- Add SMS reminder and masked calling plan permissions
- Fix appointment status mapping (frontend/backend mismatch)
- Clear masquerade stack on login/logout for session hygiene
- Update platform settings with credit configuration
- Add new migrations for Twilio and Stripe payment fields
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The Tiers & Pricing tab was crashing with "RefreshCw is not defined"
because the icon was used but not imported from lucide-react.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
PlatformEmailReceiver was setting source_email_address_id to a
PlatformEmailAddress ID, but the Ticket model expects a TicketEmailAddress
foreign key. This caused an integrity error that was rolling back the
entire transaction (due to ATOMIC_REQUESTS=True), preventing tickets
from being created.
Django's multi-tenant middleware doesn't recognize autoconfig/autodiscover
subdomains. This middleware rewrites the Host header to api.smoothschedule.com
so Django routes the request to the public schema.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Install openssh-client in production Django container for mail server management
- Copy .ssh keys into container with proper permissions
- Add explicit Traefik routes for autoconfig/autodiscover subdomains
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add PlatformEmailAddress model for managing platform-level email addresses
- Add TicketEmailAddress model for tenant-level email addresses
- Create MailServerService for IMAP integration with mail.talova.net
- Implement PlatformEmailReceiver for processing incoming platform emails
- Add email autoconfiguration for Mozilla, Microsoft, and Apple clients
- Add configurable email polling interval in platform settings
- Add "Check Emails" button on support page for manual refresh
- Add ticket counts to status tabs on support page
- Add platform email addresses management page
- Add Privacy Policy and Terms of Service pages
- Add robots.txt for SEO
- Restrict email addresses to smoothschedule.com domain only
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Removed django-debug-toolbar as it's unnecessary for API-only setup:
- Removed from INSTALLED_APPS and MIDDLEWARE in local.py
- Removed from dev dependencies in pyproject.toml
- Updated uv.lock after package removal
The debug toolbar was interfering with API documentation pages
and provides minimal value for a primarily API-based application.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added SHOW_TOOLBAR_CALLBACK to exclude the debug toolbar from
displaying on /v1/* paths (Swagger UI and ReDoc documentation).
This prevents the large debug toolbar logo from interfering with
the API documentation interface.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Updated local.py CSP directives to match multitenancy.py changes.
This allows Swagger UI assets to load in local development.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The Interactive Explorer link was using a relative URL (/v1/docs/), which caused it to open on the current subdomain instead of the API subdomain. This resulted in users being redirected to the dashboard.
Changed to use API_BASE_URL to construct the absolute URL, which will correctly point to:
- Local: http://lvh.me:8000/v1/docs/
- Production: https://api.smoothschedule.com/v1/docs/🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Update nginx build context to ../smoothschedule-frontend (matches deployment structure)
- Add VITE_API_URL build arg to pass API URL during frontend build
- Fixes login issues caused by incorrect API endpoint configuration
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Switched production start script from Gunicorn to Daphne to support WebSockets.
- Updated VITE_API_URL in frontend production env to 'https://api.smoothschedule.com', removing the '/api' prefix to align with the backend URL refactor.
- Implemented TenantHeaderMiddleware to switch the database tenant schema based on the 'X-Business-Subdomain' request header.
- This allows API requests directed to the centralized 'api' subdomain (e.g., api.lvh.me) to correctly access tenant-specific data when the header is present.
- Registered the middleware in multitenancy settings after TenantMainMiddleware.
The Timeline component was using a raw axios instance with hardcoded URLs, causing it to bypass authentication and tenant context headers. This resulted in empty or failed data fetches. Updated it to use the configured 'apiClient', ensuring that the authentication token and 'X-Business-Subdomain' headers are correctly sent, allowing the backend to return the appropriate tenant-specific resources and appointments.
The email verification link was incorrectly including a hash fragment (/#/), which caused the React Router (using BrowserRouter) to misinterpret the path as the root path, leading to a redirect to the default 'email-verification-required' page instead of the 'verify-email' page. This commit removes the hash, ensuring the link correctly points to /verify-email.
- Updated all API endpoint strings in 'frontend/src' (via sed and manual fixes) to remove the '/api/' prefix.
- Manually fixed 'Timeline.tsx' absolute URLs to use the 'api' subdomain and correct path.
- Manually fixed 'useAuth.ts' logout fetch URLs.
- Updated 'HelpApiDocs.tsx' sandbox URL.
- This change, combined with the backend URL update, fully transitions the application to use subdomain-based routing (e.g., 'http://api.lvh.me:8000/resource/') instead of path-prefix routing (e.g., 'http://api.lvh.me:8000/api/resource/').
- Removed '/api/' prefix from endpoint paths in auth.ts, notifications.ts, oauth.ts, and platform.ts to align with the backend URL reconfiguration.
- Updated 'API_BASE_URL' in config.ts to remove the '/api' suffix, ensuring that API requests are correctly routed to the root of the 'api' subdomain (e.g., http://api.lvh.me:8000/).
- Included improvements to login redirect logic in client.ts.
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.
Root cause: CorsMiddleware was positioned after TenantMainMiddleware, which
prevented CORS headers from being set. The tenant middleware processes requests
before CORS middleware could add the necessary headers.
Changes:
- Moved CorsMiddleware to first position in MIDDLEWARE stack
- Added CORS_ALLOW_ALL_ORIGINS configuration (for testing only)
- Updated production CORS regex to match both base and subdomains
- Created public tenant and registered production domains
- Re-enabled CORS_URLS_REGEX for API security
This fix ensures proper CORS headers are sent for cross-origin requests from
smoothschedule.com domains to api.smoothschedule.com.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Platform users need to send notifications to business tenants, so the
notifications app must be in SHARED_APPS (public schema) rather than
TENANT_APPS (tenant-specific schemas).
Changes:
- Moved notifications from TENANT_APPS to SHARED_APPS in multitenancy.py
- Run migrations on public schema to create notifications tables
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Added http://lvh.me:5173 and http://lvh.me:5174 to CORS_ALLOWED_ORIGINS
- Added regex pattern to allow all *.lvh.me subdomains in CORS_ALLOWED_ORIGIN_REGEXES
- This allows frontend at lvh.me:5173 to make requests to API at api.lvh.me:8000
- CORS preflight requests now return proper Access-Control-Allow-Origin headers
- Quick login and all API calls from frontend now work without CORS errors
Testing confirmed:
✓ OPTIONS request to /api/auth-token/ returns 200 OK with CORS headers
✓ Access-Control-Allow-Origin: http://lvh.me:5173
✓ Access-Control-Allow-Methods: DELETE, GET, OPTIONS, PATCH, POST, PUT
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Changed VITE_API_URL from localhost:8000 to api.lvh.me:8000
- Registered api.lvh.me domain in database pointing to public schema
- This maintains consistency between development and production where
api subdomain is used for API access
- All test users can now authenticate via quick login
The development setup now mirrors production:
- Production: api.smoothschedule.com → Django API
- Development: api.lvh.me:8000 → Django API (via docker container)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Settings Refactoring Fixes:
- Add minimal LOGGING structure to base.py for multitenancy.py to extend
- Restore LOGGING import in multitenancy.py
- Add development LOGGING configuration to local.py
- This allows multitenancy.py to extend LOGGING configuration properly
Quick Login Fix:
- Update frontend .env.development to use VITE_API_URL=http://localhost:8000
- Previous configuration tried to access api.lvh.me which failed due to
django-tenants not recognizing that hostname
- Using localhost:8000 directly bypasses subdomain routing and accesses
the public schema where auth endpoints are available
Both fixes restore full functionality:
- Django now starts without import errors in local development
- Quick login API calls now succeed and return authentication tokens
- Frontend can authenticate users for development/testing
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Move CORS_ALLOWED_ORIGINS to base.py, configurable via DJANGO_CORS_ALLOWED_ORIGINS env var
- Move CORS_ALLOWED_ORIGIN_REGEXES to base.py, configurable via DJANGO_CORS_ALLOWED_ORIGIN_REGEXES
- Move CSRF_TRUSTED_ORIGINS to base.py, configurable via DJANGO_CSRF_TRUSTED_ORIGINS
- Remove duplicate CORS/CSRF config from local.py (now inherits from base)
- Remove production-specific CORS config (now uses env vars from base)
- Allows development and production to use same settings with different .env variables
- Add CORS_ALLOWED_ORIGINS configurable via DJANGO_CORS_ALLOWED_ORIGINS env var
- Add CORS_ALLOWED_ORIGIN_REGEXES for wildcard subdomains
- Add CSRF_TRUSTED_ORIGINS for production domain
- Support custom domains via DJANGO_DOMAIN_NAME env var
- Use corsheaders.defaults for standard CORS headers
- Add custom headers: x-business-subdomain, x-sandbox-mode
This commit adds all previously untracked files and modifications needed for production deployment:
- New marketing components (BenefitsSection, CodeBlock, PluginShowcase, PricingTable)
- Platform admin components (EditPlatformEntityModal, PlatformListRow, PlatformListing, PlatformTable)
- Updated deployment configuration and scripts
- Various frontend API and component improvements
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Fixed double /api/api/ issue in production
- Updated all API files to remove /api/ prefix since baseURL already includes it
- Files fixed: platform.ts, oauth.ts, customDomains.ts, domains.ts, business.ts, sandbox.ts
- Production build will need to be rebuilt after pulling these changes
When VITE_API_URL=/api, axios baseURL is already set to /api. However, all endpoint calls included the /api/ prefix, creating double paths like /api/api/auth/login/.
Removed /api/ prefix from 81 API endpoint calls across 22 files:
- src/api/auth.ts - Fixed login, logout, me, refresh, hijack endpoints
- src/api/client.ts - Fixed token refresh endpoint
- src/api/profile.ts - Fixed all profile, email, password, MFA, sessions endpoints
- src/hooks/*.ts - Fixed all remaining API calls (users, appointments, resources, etc)
- src/pages/*.tsx - Fixed signup and email verification endpoints
This ensures API requests use the correct path: /api/auth/login/ instead of /api/api/auth/login/
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
- Add OAuthCredential model for storing Google/Microsoft OAuth tokens
- Add email provider auto-detection endpoint (Gmail, Outlook, Yahoo, etc.)
- Add EmailConfigWizard frontend component with step-by-step setup
- Add OAuth flow endpoints for Google and Microsoft XOAUTH2
- Update production settings to make AWS, Sentry, Mailgun optional
- Update Traefik config for wildcard subdomain routing
- Add logo resize utility script
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add SMTP fields to TicketEmailSettings model (host, port, TLS/SSL, credentials, from email/name)
- Update serializers with SMTP fields and is_smtp_configured flag
- Add TicketEmailTestSmtpView for testing SMTP connections
- Update frontend API types and hooks for SMTP settings
- Add collapsible IMAP and SMTP configuration sections with "Configured" badges
- Fix TypeScript errors in mockData.ts (missing required fields, type mismatches)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add GlobalEventPlugin model for auto-attaching plugins to all events
- Create signals for auto-attachment on new events and rescheduling
- Add API endpoints for global event plugins (CRUD, toggle, reapply)
- Update CreateTaskModal with "Scheduled Task" vs "Event Automation" choice
- Add option to apply to all events or future events only
- Display event automations in Tasks page alongside scheduled tasks
- Add EditEventAutomationModal for editing trigger and timing
- Handle event reschedule - update Celery task timing on time/duration changes
- Add Marketplace to Plugins menu in sidebar
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## Backend Changes:
- Enhanced PluginTemplate.save() to auto-parse template variables from plugin code
- Updated PluginInstallationSerializer to expose template metadata (description, category, version, author, logo, template_variables)
- Fixed template variable parser to handle nested {{ }} braces in default values
- Added brace-counting algorithm to properly extract variables with insertion codes
- Fixed explicit type parameter detection (textarea, text, email, etc.)
- Made scheduled_task optional on PluginInstallation model
- Added EventPlugin through model for event-plugin relationships
- Added Event.execute_plugins() method for plugin automation
## Frontend Changes:
- Created Tasks.tsx page for managing scheduled tasks
- Enhanced MyPlugins page with clickable plugin cards
- Added edit configuration modal with dynamic form generation
- Implemented escape sequence handling (convert \n, \', etc. for display)
- Added plugin logos to My Plugins page
- Updated type definitions for PluginInstallation interface
- Added insertion code documentation to Plugin Docs
## Plugin System:
- All platform plugins now have editable email templates with textarea support
- Template variables properly parsed with full default values
- Insertion codes ({{CUSTOMER_NAME}}, {{BUSINESS_NAME}}, etc.) documented
- Plugin logos displayed in marketplace and My Plugins
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changes:
1. Changed "Install" button to "View" button with Eye icon
- Removed separate "Details" button
- Single "View" button now opens the details modal
2. Fixed author mapping to use author_name from API
3. Fixed rating field to use rating_average from API
4. Set isVerified based on visibility === 'PLATFORM'
The modal now correctly displays:
- Plugin name with verified badge for platform plugins
- Author name (e.g., "Smooth Schedule")
- Version number
- Full description with formatting
- Category, ratings, and install count
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The description field is now included in marketplace listing responses,
allowing the frontend to display full plugin descriptions with formatting,
bullet points, and detailed benefits.
All 6 platform plugins now return complete information:
- Name, author (Smooth Schedule), version (1.0.0)
- Short description (one-line summary)
- Full description (multi-paragraph with bullets)
- Category, ratings, install count
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changes:
1. Added version field to PluginTemplate model (default: 1.0.0)
2. Updated platform plugins with detailed descriptions
3. Changed author_name from "SmoothSchedule Platform" to "Smooth Schedule"
4. Enhanced seed_platform_plugins command with comprehensive descriptions
Plugin descriptions now include:
- Detailed explanations of functionality
- Benefits and use cases
- Key features in bullet points
All 6 platform plugins now have:
- Version: 1.0.0
- Author: Smooth Schedule
- Rich, marketing-friendly descriptions
Migration: 0016_plugintemplate_version
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changes:
1. Updated PluginTemplateViewSet.get_queryset() to include PLATFORM
visibility plugins in marketplace view alongside approved PUBLIC plugins
2. Fixed PluginTemplateListSerializer read_only_fields from string '__all__'
to proper list reference
Platform plugins are now visible in the marketplace API:
- 6 platform-created plugins seeded via seed_platform_plugins command
- Categories: EMAIL, REPORTS, CUSTOMER, BOOKING
- All marked as visibility=PLATFORM, is_approved=True
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Users can access the marketplace via the "Browse Marketplace" button
on the My Plugins page, so the redundant sidebar link has been removed.
The Plugins dropdown now contains:
- My Plugins
- Plugin Docs
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit completes the plugin system UI by adding two key pages:
1. Plugin Marketplace (/plugins/marketplace):
- Browse and search platform-provided plugins
- Filter by category (EMAIL, REPORTS, CUSTOMER, BOOKING, etc.)
- View plugin details including ratings, install count, description
- Install plugins with one click
- Modal view for detailed plugin information
2. My Plugins (/plugins/my-plugins):
- View all installed plugins
- Manage plugin activation status
- Update plugins when new versions available
- Rate and review installed plugins
- Uninstall plugins with confirmation
- Links to create custom plugins and browse marketplace
Additional changes:
- Added plugin routes to App.tsx with owner/manager access control
- Updated HelpPluginDocs.tsx with navigation callout boxes
- Added TypeScript interfaces for PluginTemplate and PluginInstallation
- Both pages feature full dark mode support
- Professional UI with Tailwind CSS styling
- React Query integration for data fetching
The pages are accessible from the Plugins dropdown menu in the sidebar.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Added Plugins collapsible menu with 3 items:
- Plugin Marketplace (/plugins/marketplace)
- My Plugins (/plugins/my-plugins)
- Plugin Docs (/help/plugins)
- Moved Plugin Docs from Help dropdown to Plugins dropdown
- Added Plug, ShoppingBag, Package icons from lucide-react
- Added translations for plugin navigation items
- Auto-opens dropdown when on /plugins/* routes
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Backend Features:
- Created PluginTemplate and PluginInstallation models
- Built complete REST API with marketplace, my plugins, install/uninstall endpoints
- Platform plugins supported (PLATFORM visibility, no whitelisting required)
- Template variable extraction and compilation
- Plugin approval workflow for marketplace publishing
- Rating and review system
- Update detection and version management
- Install count tracking
API Endpoints:
- GET /api/plugin-templates/ - Browse marketplace (view=marketplace/my_plugins/platform)
- POST /api/plugin-templates/ - Create new plugin
- POST /api/plugin-templates/{id}/install/ - Install plugin as ScheduledTask
- POST /api/plugin-templates/{id}/publish/ - Publish to marketplace
- POST /api/plugin-templates/{id}/approve/ - Approve for marketplace (admins)
- POST /api/plugin-installations/{id}/rate/ - Rate and review
- POST /api/plugin-installations/{id}/update_to_latest/ - Update plugin
- DELETE /api/plugin-installations/{id}/ - Uninstall plugin
Platform Plugins:
- Created seed_platform_plugins management command
- 6 starter plugins ready: daily summary, no-show tracker, birthdays, revenue reports, reminders, re-engagement
- Platform plugins bypass whitelist validation
- Pre-approved and available to all businesses
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implemented static code analysis to detect and validate HTTP calls in plugins
before they are uploaded or approved for the marketplace.
**New Functions:**
1. **analyze_plugin_http_calls(script):**
- Parses plugin code using Python AST
- Detects all api.http_* method calls (GET, POST, PUT, PATCH, DELETE)
- Extracts URL from first argument if it's a string literal
- Handles dynamic URLs (f-strings, variables) with appropriate warnings
- Returns list of HTTP calls with method, URL, and line number
2. **validate_plugin_whitelist(script, scheduled_task):**
- Analyzes plugin code for HTTP calls
- Validates each detected URL against WhitelistedURL model
- Checks both platform-wide and plugin-specific whitelists
- Returns validation results with errors, warnings, and detected calls
- Provides clear error messages with line numbers
**Validation Logic:**
- **Static URLs** (string literals): Validated against whitelist, error if not found
- **Dynamic URLs** (f-strings, variables): Warning issued, runtime validation required
- **Syntax Errors**: Caught and reported as validation errors
- **Line Numbers**: All errors/warnings include line number for debugging
**Use Cases:**
1. Pre-upload validation: Check plugin before saving to database
2. Approval workflow: Platform staff can see which URLs need whitelisting
3. Marketplace submission: Reject plugins with non-whitelisted URLs
4. Security audit: Analyze existing plugins for HTTP call patterns
**Error Messages:**
- Clear, actionable messages with line numbers
- Direct users to pluginaccess@smoothschedule.com for whitelisting
- Warns about dynamic URLs that can't be statically validated
This enables proactive security enforcement before plugins are executed,
preventing runtime failures and improving user experience.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Updated all references to "Platform" in the licensing documentation to use
"SmoothSchedule" for better branding consistency:
- Changed "Platform Rights" to "SmoothSchedule Rights"
- Changed "Platform Service Rights" to "SmoothSchedule Service Rights"
- Changed "Platform Use" table header to "SmoothSchedule Use"
- Updated all inline references from "Platform" to "SmoothSchedule"
This makes the licensing terms more specific and branded while maintaining
the same legal structure and protections.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added new "Plugin Licensing" section to plugin documentation that covers:
**SmoothSchedule Community Plugin License (SCPL):**
- Custom license for marketplace plugins
- Grants platform rights to use, execute, host, and distribute code
- Grants users rights to install, use, and modify for their business
- Requires attribution to original author
- Allows users to adapt code but not republish without permission
- Permits authors to unpublish anytime (existing installs continue)
- Authors retain copyright and can license elsewhere
- Similar to MIT License + Platform Service Rights
**Private vs. Marketplace Plugins:**
- Private plugins: Full ownership, any license (or none), private to business
- Marketplace plugins: Must use SCPL, visible to all users, author credited
- Both types: Platform can execute code to provide automation service
**Key Features:**
- Comparison table showing differences between private and marketplace plugins
- Code verification and security requirements documented
- Decision guide for when to publish vs. keep private
- Important notices about warranties, compliance, and data privacy
- Platform service rights clearly explained
**Legal Protections:**
- Platform has rights to execute user code for service delivery
- No warranty disclaimer (plugins "as-is")
- User compliance responsibility for laws and regulations
- Data privacy obligations (GDPR, CCPA compliance)
The SCPL is designed to be marketplace-friendly while protecting both
the platform and plugin authors, similar to how npm/PyPI handle packages
but with added platform service rights for SaaS execution.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Backend Changes:
- Extended SafeScriptAPI to support all HTTP methods (GET, POST, PUT, PATCH, DELETE)
- Created WhitelistedURL model for per-plugin and platform-wide URL whitelisting
- Added _validate_url() method with SSRF protection and private IP blocking
- Updated SafeScriptAPI to accept scheduled_task parameter for whitelist checking
- All HTTP methods now validate against whitelist before making requests
WhitelistedURL Model:
- Supports two scopes: PLATFORM (all plugins) and PLUGIN (specific plugin)
- Stores URL patterns with wildcard support (e.g., https://api.example.com/*)
- Tracks allowed HTTP methods per URL
- Includes approval workflow (approved_by, approved_at)
- Stores original plugin code for verification
- Domain-based indexing for fast lookup
- Database constraint ensures platform-wide entries have no plugin assigned
Security Features:
- SSRF prevention: blocks localhost, loopback, and private IP ranges
- Per-plugin whitelist: each ScheduledTask can only access its whitelisted URLs
- Platform-wide whitelist: approved URLs accessible by all plugins
- HTTP method validation: URLs must explicitly allow each method
- URL pattern matching with wildcard support
Related Models:
- WhitelistedURL.scheduled_task -> ScheduledTask (plugin that owns the whitelist)
- WhitelistedURL.approved_by -> User (platform user who approved the URL)
Migration: schedule/migrations/0014_whitelistedurl.py
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>