Skip to main content

Overview

apps/calendar and the Calendar experience inside apps/web are paired Calendar hosts. They must stay 1:1 for product features, data behavior, mutations, permissions, and user-facing workflow updates.
  • Calendar UI and logic should live in shared packages, primarily @tuturuuu/ui/calendar-app/*, with app route files acting as thin auth and workspace-context wrappers.
  • apps/calendar owns its standalone workspace shell, local /login, and local /verify-token completion route.
  • apps/web /{wsId}/calendar renders the same shared Calendar product surface inside the normal platform dashboard shell.
  • Dashboard navigation in apps/web should link to the local /{wsId}/calendar route, not to the standalone Calendar origin.

Auth Model

Calendar does not create local Supabase Auth sessions. It uses central apps/web login plus a Calendar app-session cookie:
  1. /login in apps/calendar normalizes a safe next path.
  2. If both the Calendar app-session cookie and Web-issued app-session cookie are already present, /login redirects to that local next path immediately.
  3. Otherwise /login redirects to apps/web /login with a return URL pointing back to Calendar /verify-token?nextUrl=....
  4. /verify-token posts the handoff token to the Calendar-local verifier, which validates through central Web and sets host-local app-session cookies.
Calendar proxy responses should clear stale sb-*-auth-token cookies. A valid Calendar session is represented by Tuturuuu app-session cookies, not by a local Supabase browser session.

API Ownership

Protected Calendar data APIs remain in apps/web. apps/calendar rewrites fallback /api/* requests to the central Web app, and routes consumed by Calendar must opt into app-session auth explicitly when used by the standalone host.
  • Calendar-only routes use targetApp: 'calendar'.
  • Task and board helper routes that Calendar legitimately calls for quick task creation, task sidebar scheduling, and time tracker task creation use targetApp: ['calendar', 'tasks'].
  • Do not accept generic satellite app-session tokens for Calendar APIs.
  • Keep workspace membership checks and Calendar/task permissions on the central route before admin-backed reads or writes.

OAuth Token Safety

Calendar OAuth credentials live in calendar_auth_tokens. RLS restricts selects to user_id = auth.uid(), but admin/service-role reads bypass RLS. Calendar server pages must therefore:
  • Check manage_calendar before loading calendar integration state.
  • Scope token reads to both ws_id and the authenticated user_id.
  • Never pass access_token or refresh_token into client components. SSR props should use the shared fetchUserWorkspaceCalendarGoogleTokenForClient() helper from @tuturuuu/utils/calendar-auth-token, which projects only connection metadata (id, account email/name, provider, active state, expiry).
  • Keep token refresh and Google API calls on central apps/web API routes.
When adding a new Calendar host route, reuse the helper instead of select('*') on calendar_auth_tokens. When adding new Calendar UI that needs protected data, update the shared Calendar component or helper first, then wire both host wrappers in the same change. Prefer a central apps/web API route and a packages/internal-api helper instead of host-local product API forks or direct client Supabase reads.

Calendar OAuth And Connections

Google Calendar OAuth is Web-owned even when the user starts from the standalone Calendar app. The /api/v1/calendar/auth route in apps/web must build its Google callback URL from a browser-safe Web origin and ignore wildcard listener addresses such as 0.0.0.0 or ::. If no safe configured Web origin is available, the fallback origin is https://tuturuuu.com. After Google returns to the callback, the flow always sends the user to the central Web Calendar page at https://tuturuuu.com/{wsId}/calendar?provider=google&connected=true. Valid local development callback URLs such as http://localhost:7803/api/v1/calendar/auth/callback may still be configured through GOOGLE_REDIRECT_URI, but wildcard listener URLs must never be emitted as Google redirect_uri values or browser redirects. Normal Google Calendar connect and reconnect URLs request access_type=offline and include_granted_scopes=true, but they must not force prompt=consent by default. Google may return a refresh token only during the first authorization, so the callback must preserve an existing stored refresh_token when a reconnect returns only a new access token. If neither the callback nor the existing token row has a refresh token, the flow must fail before saving an active Google connection because provider sync cannot refresh offline. Standalone apps/calendar also exposes the shared Calendar connections manager inside its settings dialog under Calendar -> Integrations. It should fetch the initial connection state through @tuturuuu/internal-api/calendar and render the shared Calendar connections UI inside CalendarSyncProvider rather than forking host-local connection controls.

CI And Deployment

Calendar deploys through dedicated GitHub Actions workflows, not Vercel-owned GitHub auto-builds:
  • Preview deployments use .github/workflows/vercel-preview-calendar.yaml.
  • Production deployments use .github/workflows/vercel-production-calendar.yaml.
  • The Vercel project id is provided through the environment-scoped VERCEL_CALENDAR_PROJECT_ID secret.
  • apps/calendar/vercel.json must keep git.deploymentEnabled and github.enabled set to false so GitHub Actions builds with vercel build and deploys prebuilt artifacts with vercel deploy --prebuilt.
Keep Calendar cron definitions in apps/calendar/vercel.json; disabling Vercel GitHub builds must not remove the app-owned cron schedules.

Provider Sync

Calendar provider sync is scheduled by apps/web cron, not Trigger.dev. The shared source of truth is apps/web/cron.config.json; calendar-provider-sync must stay at */15 * * * * and apps/web/vercel.json must be regenerated from that file with node scripts/sync-web-crons.js. The cron wrapper at /api/cron/calendar/provider-sync must keep calling /api/v1/workspaces/:wsId/calendar/sync through INTERNAL_WEB_API_ORIGIN when available. The workspace sync route owns Google and Microsoft fan-out, running locks, cron cooldown behavior, and calendar_sync_dashboard audit rows. Do not add provider-specific fetch logic or Trigger.dev scheduled calendar jobs outside that route family.

Two-Way Sync Controls

Calendar sync has two layers of user control:
  • Workspace-user preferences in private.calendar_user_workspace_preferences enable or disable inbound imports, outbound Tuturuuu-to-provider mirroring, the default outbound provider calendar, and the conflict policy.
  • calendar_connections controls each external calendar’s import, outbound write, and provider-delete behavior.
Outbound mirroring is opt-in. When enabled, native Tuturuuu event creates and edits mirror to the selected writable Google or Microsoft connection, and the local event stores provider identity so future updates and deletes propagate externally. Manual or cron sync with direction: "outbound" or "both" may also catch up local-only or previously failed Tuturuuu events in the active sync window. Inbound provider deletes are controlled per connection through sync_delete_enabled. User-initiated deletion of a synced external event still deletes the provider event directly because it is explicit CRUD, not passive provider import cleanup. After adding or changing two-way sync columns, prepare migrations in apps/database; do not run production Supabase push commands from agent sessions. Apply locally with bun sb:up when feasible, then regenerate database types only after the local schema reflects the migration.