Skip to content

Conversation

edulelis
Copy link
Collaborator

@edulelis edulelis commented Sep 23, 2025

Summary by CodeRabbit

  • New Features

    • Recall integration: create/delete Recall calendars, automatic bot scheduling/rescheduling for meetings, transcript creation/ingestion, UI managers and settings pages.
  • API

    • Secured Recall webhook for calendar updates, recordings, and transcripts; new endpoint listing user Recall calendar connections.
  • Settings

    • New env vars to configure Recall API and webhook secrets.
  • Database

    • Meeting model and meeting status support (scheduling lifecycle).
  • Bug Fixes

    • Improved disconnect flow, optimistic UI, and enhanced error/toast feedback.

@vercel
Copy link

vercel bot commented Sep 23, 2025

@edulelis is attempting to deploy a commit to the Inbox Zero OSS Program Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 23, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds Recall.ai integration: HTTP client, request helpers, calendar CRUD and event fetch, webhook endpoint and types, bot scheduling/rescheduling and transcript fetching, server actions and UI for managing Recall calendars, env vars, Prisma Meeting model/migrations, and Google OAuth callback updates to create/link Recall calendars.

Changes

Cohort / File(s) Summary
Recall HTTP client & utilities
apps/web/utils/recall/request.ts, apps/web/utils/recall/calendar.ts, apps/web/utils/recall/transcript.ts, apps/web/utils/recall/verify-svix-webhook.ts
New recallRequest wrapper (auth, params, error handling); Recall calendar CRUD and paginated event fetch; transcript create/metadata/content fetch; Svix webhook signature verification utility.
Recall bot orchestration
apps/web/utils/recall/bot.ts
New bot lifecycle: deduplication key generation, add/remove bot endpoints, schedule bot (upsert Meeting), and reschedule bots for updated events with DB updates and logging.
Recall webhook & types
apps/web/app/api/recall/webhook/route.ts, apps/web/app/api/recall/webhook/types.ts
New POST webhook with Zod schemas and optional Svix verification; dispatches calendar.sync_events, calendar.update, recording.done, transcript.done to handlers that fetch events, reschedule bots, manage Meeting records, and update transcripts; added types.
Google OAuth callback integration
apps/web/app/api/google/calendar/callback/route.ts
Callback extended to create and link a Recall calendar when OAuth refresh token exists and recallCalendarId is missing; errors are logged but existing flow preserved.
Server actions: create/delete Recall calendar
apps/web/utils/actions/recall-calendar.ts
Actions to create a Recall calendar using a refresh token and to delete linked Recall calendars across connections; update DB and revalidate /calendar.
Calendar disconnect action
apps/web/utils/actions/calendar.ts
disconnectCalendarAction now receives logger and attempts to delete linked Recall calendar (logs failures but proceeds to delete connection).
User API, hook & UI
apps/web/app/api/user/recall-calendar/route.ts, apps/web/hooks/useRecallCalendar.ts, apps/web/components/RecallCalendarManager.tsx
New authenticated GET API listing connected calendars and Recall linkage; SWR hook useRecallCalendar; React component to create/delete Recall calendars and display statuses.
Transcript settings & UI
apps/web/utils/actions/transcript-settings.ts, apps/web/app/(app)/[emailAccountId]/transcript-settings/*
Action to bulk-update transcriptEnabled; TranscriptSettingsManager UI and page to toggle transcripts, invoking recall create/delete actions when enabling/disabling.
Calendar connections UI & UX
apps/web/app/(app)/[emailAccountId]/calendars/*, apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx, apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx, apps/web/app/(app)/[emailAccountId]/calendars/page.tsx
Improved connect/disconnect flows, optimistic UI updates, confirm dialog for disconnect, connect button flow using emailAccountId, and simplified page composition.
Prisma schema & migrations
apps/web/prisma/schema.prisma, apps/web/prisma/migrations/.../migration.sql, apps/web/prisma/migrations/.../migration.sql
Adds Meeting model and MeetingStatus enum (and later enum rename migration), adds unique recallCalendarId to CalendarConnection, adds transcriptEnabled to Calendar, plus indexes and constraints.
Env / config
apps/web/env.ts
Adds RECALL_WEBHOOK_SECRET (optional), RECALL_API_KEY (optional), and RECALL_API_BASE_URL (default "https://us-west-2.recall.ai").
Misc small edits
apps/web/utils/scheduled-actions/scheduler.ts
Typing change: use SchedulingStatus.SCHEDULED / SchedulingStatus.FAILED enum values instead of string literals.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant WebApp as Web App
  participant GC as Google Callback
  participant DB as Database
  participant RecallAPI as Recall API

  User->>WebApp: Initiate Google connect
  WebApp->>GC: /api/google/calendar/callback
  GC->>DB: Upsert CalendarConnection
  alt refresh_token present && recallCalendarId missing
    GC->>RecallAPI: POST /api/v2/calendars (oauth_refresh_token)
    RecallAPI-->>GC: { id, status, platform }
    GC->>DB: Update CalendarConnection.recallCalendarId
  end
  GC-->>User: Callback complete
Loading
sequenceDiagram
  autonumber
  participant Recall as Recall Webhook
  participant API as /api/recall/webhook (POST)
  participant Verifier as Svix Verify
  participant DB as Database
  participant Bot as Bot Orchestrator
  participant RecallAPI as Recall API

  Recall->>API: payload + Svix headers
  API->>Verifier: verify(signature, timestamp)
  Verifier-->>API: ok / invalid
  alt invalid
    API-->>Recall: 403/400
  else valid
    alt calendar.sync_events
      API->>DB: Find connection by recallCalendarId
      API->>RecallAPI: GET /api/v2/calendar-events
      RecallAPI-->>API: events
      API->>Bot: rescheduleBotsForUpdatedEvents(...)
      Bot->>RecallAPI: DELETE/POST bot endpoints
      Bot->>DB: Update Meeting records/statuses
      API-->>Recall: 200
    else calendar.update
      API->>DB: Load connection by recallCalendarId
      alt disconnected
        API->>DB: Clear recallCalendarId, cancel meetings
      else connected
        API->>RecallAPI: GET updated events
        API->>Bot: rescheduleBotsForUpdatedEvents(...)
      end
      API-->>Recall: 200
    else transcript.done
      API->>RecallAPI: GET transcript metadata
      RecallAPI-->>API: { download_url }
      API->>RecallAPI: GET download_url
      RecallAPI-->>API: transcript content
      API->>DB: Update Meeting.transcript + status COMPLETED
      API-->>Recall: 200
    end
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • Add calendar connections #775 — Overlaps Google Calendar OAuth callback and calendar-connection flows; likely related to recall-calendar linking changes.

Suggested reviewers

  • anakarentorosserrano-star

Poem

I hopped through dates where meetings bloom,
I stitched a bot to tidy every room,
A whisker guards the webhook gate,
Calendars linked — transcripts await,
Hooray — the recall bells go boom! 🐇📅🎙️

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly captures the primary change of integrating Recall features throughout the application and aligns with the substantial additions related to recall calendar, webhooks, and related tooling.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

7 issues found across 12 files

Prompt for AI agents (all 7 issues)

Understand the root cause of the following 7 issues and fix them.


<file name="apps/web/app/api/recall/webhook/route.ts">

<violation number="1" location="apps/web/app/api/recall/webhook/route.ts:98">
POST handler ignores processRecallWebhook&#39;s NextResponse, causing unsupported events to return 200 instead of 400.</violation>
</file>

<file name="apps/web/utils/recall/bot.ts">

<violation number="1" location="apps/web/utils/recall/bot.ts:76">
Missing persistence of deduplicationKey increases risk of duplicate records; consider storing it or using an upsert.</violation>

<violation number="2" location="apps/web/utils/recall/bot.ts:131">
Unconditional CANCELLED status despite potential external delete failure can create inconsistent state.</violation>
</file>

<file name="apps/web/utils/recall/calendar.ts">

<violation number="1" location="apps/web/utils/recall/calendar.ts:105">
DELETE call may fail due to recallRequest always calling response.json() on empty responses; handle 204/empty body.</violation>
</file>

<file name="apps/web/utils/recall/verify-svix-webhook.ts">

<violation number="1" location="apps/web/utils/recall/verify-svix-webhook.ts:46">
svixSignature is likely versioned (e.g., v1=...) and may include multiple values; decoding the whole header as base64 can cause false negatives. Extract the v1 signature before decoding.</violation>
</file>

<file name="apps/web/utils/recall/transcript.ts">

<violation number="1" location="apps/web/utils/recall/transcript.ts:20">
Inconsistent API versioning (v1 here vs v2 elsewhere in recall utils). Verify endpoint version to ensure consistency and avoid subtle bugs.</violation>

<violation number="2" location="apps/web/utils/recall/transcript.ts:71">
Fallback will fail: response.json() consumes body; subsequent response.text() throws. Use response.clone().json() for safe fallback.</violation>
</file>

React with 👍 or 👎 to teach cubic. Mention @cubic-dev-ai to give feedback, ask questions, or re-run the review.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (8)
apps/web/app/api/recall/webhook/types.ts (3)

35-56: Align bot nullability with handler expectations

The handler for transcript.done guards for missing botId, but this type makes bot non-nullable. Either guarantee via Zod that bot is always present, or make this nullable and update the handler to use optional chaining.


28-31: Prefer Record<string, unknown> over object for metadata fields

Using object is too loose and hampers type safety. Recommend tightening to Record<string, unknown> across these metadata fields.

-      metadata: object;
+      metadata: Record<string, unknown>;

Also applies to: 44-46, 48-50, 52-54


1-140: Project guideline: prefer export type over interface

The codebase guideline asks to use export type for types. Consider converting these interfaces to type aliases (non-blocking).

apps/web/utils/recall/calendar.ts (1)

85-95: 404 detection via string matching is brittle

Checking error.message.includes("404") is fragile. Prefer propagating an error type with status code from recallRequest and branching on error.status === 404.

Would you like a small refactor to make recallRequest throw a typed HTTPError with status/code so callers can branch safely?

apps/web/utils/recall/bot.ts (2)

11-13: Stabilize deduplication key by normalizing meeting_url

Meeting URLs often vary by query/hash. Normalize to reduce accidental duplicates.

-export function generateDeduplicationKey(event: CalendarEvent): string {
-  return `${event.start_time}-${event.meeting_url}`;
-}
+export function generateDeduplicationKey(event: CalendarEvent): string {
+  const urlBase =
+    event.meeting_url?.split(/[?#]/)[0] ?? "";
+  return `${event.start_time}-${urlBase}`;
+}

76-85: Store the actual bot join time if returned

If the API returns bot.start_time, prefer persisting that over event.start_time.

-      await prisma.recallMeeting.create({
+      await prisma.recallMeeting.create({
         data: {
           botId: bot.bot_id,
           eventId: event.id,
           emailAccountId: emailAccountId,
           meetingUrl: event.meeting_url || "",
-          botWillJoinAt: new Date(event.start_time),
+          botWillJoinAt: new Date((bot as any).start_time ?? event.start_time),
           status: "SCHEDULED",
         },
       });
apps/web/app/api/recall/webhook/route.ts (2)

243-251: Remove empty else block

Empty else introduces noise.

-    if (updatedEvents.length > 0) {
+    if (updatedEvents.length > 0) {
       await rescheduleBotsForUpdatedEvents(
         connection.emailAccountId,
         updatedEvents,
         calendarId,
       );
-    } else {
-    }
+    }

382-392: Safer access for bot id

If Recall ever omits bot in transcript.done, current code throws before the guard. Use optional chaining to avoid hard failure; Zod can also enforce presence if guaranteed.

-  const botId = payload.data.bot.id;
+  const botId = payload.data.bot?.id;
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7e4fe99 and bc742f1.

📒 Files selected for processing (12)
  • apps/web/app/api/google/calendar/callback/route.ts (3 hunks)
  • apps/web/app/api/recall/calendar/route.ts (1 hunks)
  • apps/web/app/api/recall/webhook/route.ts (1 hunks)
  • apps/web/app/api/recall/webhook/types.ts (1 hunks)
  • apps/web/env.ts (1 hunks)
  • apps/web/prisma/schema.prisma (5 hunks)
  • apps/web/utils/actions/calendar.ts (2 hunks)
  • apps/web/utils/recall/bot.ts (1 hunks)
  • apps/web/utils/recall/calendar.ts (1 hunks)
  • apps/web/utils/recall/request.ts (1 hunks)
  • apps/web/utils/recall/transcript.ts (1 hunks)
  • apps/web/utils/recall/verify-svix-webhook.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (18)
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use @/ for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX

Files:

  • apps/web/utils/recall/request.ts
  • apps/web/utils/recall/verify-svix-webhook.ts
  • apps/web/app/api/google/calendar/callback/route.ts
  • apps/web/env.ts
  • apps/web/utils/recall/transcript.ts
  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/utils/recall/bot.ts
  • apps/web/app/api/recall/webhook/types.ts
  • apps/web/utils/actions/calendar.ts
  • apps/web/utils/recall/calendar.ts
  • apps/web/app/api/recall/calendar/route.ts
!{.cursor/rules/*.mdc}

📄 CodeRabbit inference engine (.cursor/rules/cursor-rules.mdc)

Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location

Files:

  • apps/web/utils/recall/request.ts
  • apps/web/utils/recall/verify-svix-webhook.ts
  • apps/web/app/api/google/calendar/callback/route.ts
  • apps/web/env.ts
  • apps/web/utils/recall/transcript.ts
  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/utils/recall/bot.ts
  • apps/web/app/api/recall/webhook/types.ts
  • apps/web/utils/actions/calendar.ts
  • apps/web/utils/recall/calendar.ts
  • apps/web/prisma/schema.prisma
  • apps/web/app/api/recall/calendar/route.ts
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)

**/*.ts: The same validation should be done in the server action too
Define validation schemas using Zod

Files:

  • apps/web/utils/recall/request.ts
  • apps/web/utils/recall/verify-svix-webhook.ts
  • apps/web/app/api/google/calendar/callback/route.ts
  • apps/web/env.ts
  • apps/web/utils/recall/transcript.ts
  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/utils/recall/bot.ts
  • apps/web/app/api/recall/webhook/types.ts
  • apps/web/utils/actions/calendar.ts
  • apps/web/utils/recall/calendar.ts
  • apps/web/app/api/recall/calendar/route.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)

**/*.{ts,tsx}: Use createScopedLogger for logging in backend TypeScript files
Typically add the logger initialization at the top of the file when using createScopedLogger
Only use .with() on a logger instance within a specific function, not for a global logger

Import Prisma in the project using import prisma from "@/utils/prisma";

**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.

Files:

  • apps/web/utils/recall/request.ts
  • apps/web/utils/recall/verify-svix-webhook.ts
  • apps/web/app/api/google/calendar/callback/route.ts
  • apps/web/env.ts
  • apps/web/utils/recall/transcript.ts
  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/utils/recall/bot.ts
  • apps/web/app/api/recall/webhook/types.ts
  • apps/web/utils/actions/calendar.ts
  • apps/web/utils/recall/calendar.ts
  • apps/web/app/api/recall/calendar/route.ts
apps/web/utils/**

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

Create utility functions in utils/ folder for reusable logic

Files:

  • apps/web/utils/recall/request.ts
  • apps/web/utils/recall/verify-svix-webhook.ts
  • apps/web/utils/recall/transcript.ts
  • apps/web/utils/recall/bot.ts
  • apps/web/utils/actions/calendar.ts
  • apps/web/utils/recall/calendar.ts
apps/web/utils/**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

apps/web/utils/**/*.ts: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size

Files:

  • apps/web/utils/recall/request.ts
  • apps/web/utils/recall/verify-svix-webhook.ts
  • apps/web/utils/recall/transcript.ts
  • apps/web/utils/recall/bot.ts
  • apps/web/utils/actions/calendar.ts
  • apps/web/utils/recall/calendar.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{js,jsx,ts,tsx}: Don't use elements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...

Files:

  • apps/web/utils/recall/request.ts
  • apps/web/utils/recall/verify-svix-webhook.ts
  • apps/web/app/api/google/calendar/callback/route.ts
  • apps/web/env.ts
  • apps/web/utils/recall/transcript.ts
  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/utils/recall/bot.ts
  • apps/web/app/api/recall/webhook/types.ts
  • apps/web/utils/actions/calendar.ts
  • apps/web/utils/recall/calendar.ts
  • apps/web/app/api/recall/calendar/route.ts
!pages/_document.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.

Files:

  • apps/web/utils/recall/request.ts
  • apps/web/utils/recall/verify-svix-webhook.ts
  • apps/web/app/api/google/calendar/callback/route.ts
  • apps/web/env.ts
  • apps/web/utils/recall/transcript.ts
  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/utils/recall/bot.ts
  • apps/web/app/api/recall/webhook/types.ts
  • apps/web/utils/actions/calendar.ts
  • apps/web/utils/recall/calendar.ts
  • apps/web/prisma/schema.prisma
  • apps/web/app/api/recall/calendar/route.ts
apps/web/app/**

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

NextJS app router structure with (app) directory

Files:

  • apps/web/app/api/google/calendar/callback/route.ts
  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/app/api/recall/webhook/types.ts
  • apps/web/app/api/recall/calendar/route.ts
apps/web/app/api/**/route.ts

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

apps/web/app/api/**/route.ts: Use withAuth for user-level operations
Use withEmailAccount for email-account-level operations
Do NOT use POST API routes for mutations - use server actions instead
No need for try/catch in GET routes when using middleware
Export response types from GET routes

apps/web/app/api/**/route.ts: Wrap all GET API route handlers with withAuth or withEmailAccount middleware for authentication and authorization.
Export response types from GET API routes for type-safe client usage.
Do not use try/catch in GET API routes when using authentication middleware; rely on centralized error handling.

Files:

  • apps/web/app/api/google/calendar/callback/route.ts
  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/app/api/recall/calendar/route.ts
**/api/**/route.ts

📄 CodeRabbit inference engine (.cursor/rules/security.mdc)

**/api/**/route.ts: ALL API routes that handle user data MUST use appropriate authentication and authorization middleware (withAuth or withEmailAccount).
ALL database queries in API routes MUST be scoped to the authenticated user/account (e.g., include userId or emailAccountId in query filters).
Always validate that resources belong to the authenticated user before performing operations (resource ownership validation).
Use withEmailAccount middleware for API routes that operate on a specific email account (i.e., use or require emailAccountId).
Use withAuth middleware for API routes that operate at the user level (i.e., use or require only userId).
Use withError middleware (with proper validation) for public endpoints, custom authentication, or cron endpoints.
Cron endpoints MUST use withError middleware and validate the cron secret using hasCronSecret(request) or hasPostCronSecret(request).
Cron endpoints MUST capture unauthorized attempts with captureException and return a 401 status for unauthorized requests.
All parameters in API routes MUST be validated for type, format, and length before use.
Request bodies in API routes MUST be validated using Zod schemas before use.
All Prisma queries in API routes MUST only return necessary fields and never expose sensitive data.
Error messages in API routes MUST not leak internal information or sensitive data; use generic error messages and SafeError where appropriate.
API routes MUST use a consistent error response format, returning JSON with an error message and status code.
All findUnique and findFirst Prisma calls in API routes MUST include ownership filters (e.g., userId or emailAccountId).
All findMany Prisma calls in API routes MUST be scoped to the authenticated user's data.
Never use direct object references in API routes without ownership checks (prevent IDOR vulnerabilities).
Prevent mass assignment vulnerabilities by only allowing explicitly whitelisted fields in update operations in AP...

Files:

  • apps/web/app/api/google/calendar/callback/route.ts
  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/app/api/recall/calendar/route.ts
apps/web/app/api/**/*.{ts,js}

📄 CodeRabbit inference engine (.cursor/rules/security-audit.mdc)

apps/web/app/api/**/*.{ts,js}: All API route handlers in 'apps/web/app/api/' must use authentication middleware: withAuth, withEmailAccount, or withError (with custom authentication logic).
All Prisma queries in API routes must include user/account filtering (e.g., emailAccountId or userId in WHERE clauses) to prevent unauthorized data access.
All parameters used in API routes must be validated before use; do not use parameters from 'params' or request bodies directly in queries without validation.
Request bodies in API routes should use Zod schemas for validation.
API routes should only return necessary fields using Prisma's 'select' and must not include sensitive data in error messages.
Error messages in API routes must not reveal internal details; use generic errors and SafeError for user-facing errors.
All QStash endpoints (API routes called via publishToQstash or publishToQstashQueue) must use verifySignatureAppRouter to verify request authenticity.
All cron endpoints in API routes must use hasCronSecret or hasPostCronSecret for authentication.
Do not hardcode weak or plaintext secrets in API route files; secrets must not be directly assigned as string literals.
Review all new withError usage in API routes to ensure custom authentication is implemented where required.

Files:

  • apps/web/app/api/google/calendar/callback/route.ts
  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/app/api/recall/webhook/types.ts
  • apps/web/app/api/recall/calendar/route.ts
apps/web/**/{.env.example,env.ts,turbo.json}

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

Add environment variables to .env.example, env.ts, and turbo.json

Files:

  • apps/web/env.ts
apps/web/**/{.env.example,env.ts}

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

Client-side environment variables: Prefix with NEXT_PUBLIC_

Files:

  • apps/web/env.ts
apps/web/env.ts

📄 CodeRabbit inference engine (.cursor/rules/environment-variables.mdc)

apps/web/env.ts: When adding a new environment variable, add it to apps/web/env.ts in the appropriate section: use server for server-only variables, and for client-side variables, use the client section and also add to experimental__runtimeEnv.
Client-side environment variables must be prefixed with NEXT_PUBLIC_ and added to both the client and experimental__runtimeEnv sections in apps/web/env.ts.

Files:

  • apps/web/env.ts
apps/web/utils/actions/**/*.ts

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

apps/web/utils/actions/**/*.ts: Use server actions for all mutations (create/update/delete operations)
next-safe-action provides centralized error handling
Use Zod schemas for validation on both client and server
Use revalidatePath in server actions for cache invalidation

apps/web/utils/actions/**/*.ts: Use server actions (with next-safe-action) for all mutations (create/update/delete operations); do NOT use POST API routes for mutations.
Use revalidatePath in server actions to invalidate cache after mutations.

Files:

  • apps/web/utils/actions/calendar.ts
apps/web/utils/actions/*.ts

📄 CodeRabbit inference engine (.cursor/rules/server-actions.mdc)

apps/web/utils/actions/*.ts: Implement all server actions using the next-safe-action library for type safety, input validation, context management, and error handling. Refer to apps/web/utils/actions/safe-action.ts for client definitions (actionClient, actionClientUser, adminActionClient).
Use actionClientUser when only authenticated user context (userId) is needed.
Use actionClient when both authenticated user context and a specific emailAccountId are needed. The emailAccountId must be bound when calling the action from the client.
Use adminActionClient for actions restricted to admin users.
Access necessary context (like userId, emailAccountId, etc.) provided by the safe action client via the ctx object in the .action() handler.
Server Actions are strictly for mutations (operations that change data, e.g., creating, updating, deleting). Do NOT use Server Actions for data fetching (GET operations). For data fetching, use dedicated GET API Routes combined with SWR Hooks.
Use SafeError for expected/handled errors within actions if needed. next-safe-action provides centralized error handling.
Use the .metadata({ name: "actionName" }) method to provide a meaningful name for monitoring. Sentry instrumentation is automatically applied via withServerActionInstrumentation within the safe action clients.
If an action modifies data displayed elsewhere, use revalidatePath or revalidateTag from next/cache within the action handler as needed.

Server action files must start with use server

Files:

  • apps/web/utils/actions/calendar.ts
apps/web/prisma/schema.prisma

📄 CodeRabbit inference engine (.cursor/rules/prisma.mdc)

The Prisma schema file must be located at apps/web/prisma/schema.prisma

Files:

  • apps/web/prisma/schema.prisma
🧠 Learnings (5)
📚 Learning: 2025-07-18T15:04:50.520Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/environment-variables.mdc:0-0
Timestamp: 2025-07-18T15:04:50.520Z
Learning: Applies to apps/web/env.ts : When adding a new environment variable, add it to `apps/web/env.ts` in the appropriate section: use `server` for server-only variables, and for client-side variables, use the `client` section and also add to `experimental__runtimeEnv`.

Applied to files:

  • apps/web/env.ts
📚 Learning: 2025-07-18T15:04:30.467Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/**/{.env.example,env.ts,turbo.json} : Add environment variables to `.env.example`, `env.ts`, and `turbo.json`

Applied to files:

  • apps/web/env.ts
📚 Learning: 2025-07-18T15:04:50.520Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/environment-variables.mdc:0-0
Timestamp: 2025-07-18T15:04:50.520Z
Learning: Applies to apps/web/env.ts : Client-side environment variables must be prefixed with `NEXT_PUBLIC_` and added to both the `client` and `experimental__runtimeEnv` sections in `apps/web/env.ts`.

Applied to files:

  • apps/web/env.ts
📚 Learning: 2025-07-18T15:04:30.467Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/app/api/**/route.ts : Use `withEmailAccount` for email-account-level operations

Applied to files:

  • apps/web/app/api/recall/calendar/route.ts
📚 Learning: 2025-07-18T17:27:46.389Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-07-18T17:27:46.389Z
Learning: Applies to **/api/**/route.ts : Use `withEmailAccount` middleware for API routes that operate on a specific email account (i.e., use or require `emailAccountId`).

Applied to files:

  • apps/web/app/api/recall/calendar/route.ts
🧬 Code graph analysis (9)
apps/web/utils/recall/request.ts (2)
apps/web/utils/logger.ts (1)
  • createScopedLogger (17-72)
apps/web/env.ts (1)
  • env (16-247)
apps/web/utils/recall/verify-svix-webhook.ts (2)
apps/web/app/api/outlook/webhook/logger.ts (1)
  • logger (3-3)
apps/web/utils/logger.ts (1)
  • createScopedLogger (17-72)
apps/web/app/api/google/calendar/callback/route.ts (2)
apps/web/utils/recall/calendar.ts (1)
  • createRecallCalendar (33-65)
apps/web/env.ts (1)
  • env (16-247)
apps/web/utils/recall/transcript.ts (3)
apps/web/utils/logger.ts (1)
  • createScopedLogger (17-72)
apps/web/utils/recall/request.ts (1)
  • recallRequest (10-52)
apps/web/app/api/recall/webhook/types.ts (2)
  • TranscriptMetadataResponse (91-95)
  • TranscriptContent (108-115)
apps/web/app/api/recall/webhook/route.ts (6)
apps/web/utils/logger.ts (1)
  • createScopedLogger (17-72)
apps/web/utils/recall/verify-svix-webhook.ts (1)
  • verifySvixWebhook (16-61)
apps/web/app/api/recall/webhook/types.ts (6)
  • RecallWebhookPayload (58-62)
  • CalendarSyncEventsEvent (8-14)
  • CalendarUpdateEvent (1-6)
  • RecordingDoneEvent (16-33)
  • TranscriptDoneEvent (35-56)
  • CalendarEvent (117-124)
apps/web/utils/recall/calendar.ts (3)
  • fetchCalendarEvents (121-134)
  • getRecallCalendar (70-96)
  • deleteRecallCalendar (101-119)
apps/web/utils/recall/bot.ts (1)
  • rescheduleBotsForUpdatedEvents (105-150)
apps/web/utils/recall/transcript.ts (3)
  • createAsyncTranscript (10-37)
  • getTranscriptMetadata (39-53)
  • fetchTranscriptContent (55-82)
apps/web/utils/recall/bot.ts (3)
apps/web/utils/logger.ts (1)
  • createScopedLogger (17-72)
apps/web/app/api/recall/webhook/types.ts (2)
  • CalendarEvent (117-124)
  • RecallCalendarEventResponse (136-139)
apps/web/utils/recall/request.ts (1)
  • recallRequest (10-52)
apps/web/utils/actions/calendar.ts (2)
apps/web/utils/logger.ts (1)
  • createScopedLogger (17-72)
apps/web/utils/recall/calendar.ts (1)
  • deleteRecallCalendar (101-119)
apps/web/utils/recall/calendar.ts (3)
apps/web/utils/logger.ts (1)
  • createScopedLogger (17-72)
apps/web/utils/recall/request.ts (1)
  • recallRequest (10-52)
apps/web/app/api/recall/webhook/types.ts (1)
  • RecallCalendarEventsListResponse (87-89)
apps/web/app/api/recall/calendar/route.ts (3)
apps/web/utils/logger.ts (1)
  • createScopedLogger (17-72)
apps/web/utils/recall/calendar.ts (4)
  • CreateRecallCalendarResponse (22-28)
  • getRecallCalendar (70-96)
  • createRecallCalendar (33-65)
  • deleteRecallCalendar (101-119)
apps/web/utils/middleware.ts (1)
  • withEmailAccount (251-255)
🔇 Additional comments (21)
apps/web/app/api/google/calendar/callback/route.ts (3)

15-15: LGTM! Clean import addition.

The import follows the established pattern for recall utilities.


138-166: Excellent integration of recall calendar creation for existing connections.

The logic properly handles the case where a connection already exists but doesn't have a Recall calendar ID, creating one asynchronously and updating the connection. Good error handling ensures the flow continues even if Recall calendar creation fails.


184-204: LGTM! Proper recall calendar integration for new connections.

The implementation correctly creates a Recall calendar after establishing a new Google Calendar connection and updates the connection with the recall calendar ID. Error handling is appropriate and doesn't interfere with the main connection flow.

apps/web/prisma/schema.prisma (5)

851-851: Good addition of recallCalendarId field.

The optional recallCalendarId field properly links calendar connections to Recall calendars.


859-859: Appropriate unique constraint on recallCalendarId.

The unique constraint ensures each Recall calendar is linked to only one connection, preventing data integrity issues.


908-926: Well-designed RecallMeeting model.

The model captures all essential meeting data:

  • Proper foreign key relationship to EmailAccount
  • Comprehensive bot lifecycle tracking with status enum
  • Appropriate indexes for common query patterns
  • Unique constraint on botId to prevent duplicates

1064-1070: Complete RecallBotStatus enum.

The enum covers all expected bot lifecycle states from SCHEDULED through FAILED, providing comprehensive status tracking.


163-163: Proper relation from EmailAccount to RecallMeeting.

The one-to-many relationship is correctly established.

apps/web/utils/actions/calendar.ts (2)

10-13: Good logging setup and clean imports.

The scoped logger and recall utility imports are properly structured.


31-45: Robust recall calendar cleanup logic.

The implementation properly:

  • Checks for recall calendar existence before attempting deletion
  • Continues with database deletion even if Recall deletion fails
  • Logs both success and failure cases with appropriate detail

This prevents orphaned resources while ensuring the disconnection always succeeds.

apps/web/utils/recall/verify-svix-webhook.ts (1)

1-61: Well-implemented Svix webhook verification.

The implementation correctly follows Svix's webhook verification protocol:

  • Proper timestamp validation with 5-minute tolerance
  • Correct secret handling (removes prefix, base64 decodes)
  • Secure signature comparison using timing-safe equality
  • Comprehensive error handling and logging

The inline documentation clearly explains the verification steps.

apps/web/utils/recall/request.ts (2)

18-19: Fix URL construction to prevent malformed endpoints.

This creates malformed URLs by concatenating a base URL ending with /api/v2 with endpoints starting with api/v2, resulting in duplicated paths like https://us-west-2.recall.ai/api/v2api/v2/calendars.

Based on the web search results, the Recall.ai API base URL should be structured as https://{region}.recall.ai/ without the /api/v2 suffix, since endpoints already include the full path.

-  const urlObj = new URL(`${base}${endpoint}`);
+  const urlObj = new URL(endpoint, base);

This uses the proper URL constructor that handles path joining correctly.


10-52: LGTM on the overall request function structure.

The function properly:

  • Validates API key presence
  • Handles query parameters correctly
  • Sets appropriate headers including authorization
  • Provides comprehensive error handling and logging
  • Returns properly typed responses

The implementation follows good practices for API client utilities.

apps/web/utils/recall/transcript.ts (3)

10-37: LGTM! Well-structured transcript creation function.

The function properly:

  • Provides sensible defaults (English language, diarization enabled)
  • Constructs the correct API payload structure
  • Uses proper endpoint for transcript creation

39-53: Good error handling in metadata retrieval.

The function properly catches errors, logs them with context, and re-throws to preserve the original error chain.


55-82: Robust content fetching with fallback handling.

The implementation correctly:

  • Validates HTTP response status
  • Handles both JSON and text content types
  • Provides graceful fallback when JSON parsing fails
  • Includes appropriate logging for debugging
apps/web/app/api/recall/calendar/route.ts (4)

17-22: LGTM! Proper middleware usage and clean handler structure.

The POST handler correctly uses withEmailAccount middleware for email-account-level operations, following the coding guidelines.


24-29: LGTM! Consistent structure for DELETE handler.

The DELETE handler follows the same pattern as POST with proper middleware usage.


31-79: Robust logic for recall calendar creation.

The implementation handles multiple edge cases properly:

  • Validates connection exists and is connected
  • Ensures primary calendar exists and is enabled
  • Checks for existing recall calendar and cleans up stale references
  • Proper error messages for missing requirements

127-179: Excellent batch deletion logic with detailed results.

The implementation properly:

  • Finds all connections with recall calendars
  • Processes each connection individually with error isolation
  • Provides detailed success/failure results
  • Continues processing even if individual deletions fail
  • Logs outcomes for debugging
apps/web/env.ts (1)

73-77: Add Recall env vars to .env.example (file missing)

apps/web/env.ts defines RECALL_WEBHOOK_SECRET, RECALL_API_KEY and RECALL_API_BASE_URL, but there is no .env.example in the repo — create or update .env.example at the repository root with:
RECALL_WEBHOOK_SECRET=
RECALL_API_KEY=
RECALL_API_BASE_URL=https://us-west-2.recall.ai/api/v2

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (6)
apps/web/prisma/schema.prisma (1)

908-926: Index for scheduling + clarify dedup semantics

  • Add an index on botWillJoinAt to support “upcoming bot joins” queries.
  • If deduplicationKey is used for idempotency, consider making it unique (or composite with emailAccountId) to enforce it at the DB layer.

Apply this diff to add a scheduling index:

 model RecallMeeting {
   id               String   @id @default(cuid())
   createdAt        DateTime @default(now())
   updatedAt        DateTime @updatedAt  
   botId            String   @unique
   eventId          String
   emailAccountId   String
   meetingUrl       String
   botWillJoinAt    DateTime
   transcript       String?
   status           RecallBotStatus @default(SCHEDULED)   // Bot status (active, completed, failed, etc.)
   deduplicationKey String?

   emailAccount  EmailAccount @relation(fields: [emailAccountId], references: [id], onDelete: Cascade)
   
   @@index([emailAccountId, eventId, status])
   @@index([deduplicationKey])
+  @@index([botWillJoinAt])
 }
apps/web/app/api/recall/webhook/route.ts (5)

28-93: Good Zod validation coverage; consider centralizing schemas

Schemas look correct and cover all event types. To avoid duplication and ensure reuse across modules/tests, consider moving them into a shared schema module (e.g., ./types.schema.ts) and importing here.


99-114: Add a sanity check for svix-timestamp format

Reject non-numeric timestamps early to avoid verifying with malformed inputs.

Apply this diff:

   if (!svixId || !svixTimestamp || !svixSignature) {
     logger.error("Missing required Svix headers", {
       hasSvixId: !!svixId,
       hasSvixTimestamp: !!svixTimestamp,
       hasSvixSignature: !!svixSignature,
     });
     return NextResponse.json(
       { message: "Missing required headers" },
       { status: 400 },
     );
   }
 
+  // svix-timestamp should be a unix-seconds integer
+  if (!/^\d+$/.test(svixTimestamp)) {
+    logger.warn("Invalid svix-timestamp format", { svixTimestamp });
+    return NextResponse.json({ message: "Invalid signature" }, { status: 403 });
+  }

170-182: Avoid returning detailed Zod errors to the caller

Public webhook should return a generic error to prevent information disclosure.

Apply this diff:

-    return NextResponse.json(
-      {
-        message: "Invalid payload structure",
-        errors: validationResult.error.errors,
-      },
-      { status: 400 },
-    );
+    return NextResponse.json(
+      { message: "Invalid payload" },
+      { status: 400 },
+    );

339-347: Remove empty else block

No-op else can be dropped.

Apply this diff:

-    if (updatedEvents.length > 0) {
+    if (updatedEvents.length > 0) {
       await rescheduleBotsForUpdatedEvents(
         connection.emailAccountId,
         updatedEvents,
         calendarId,
       );
-    } else {
-    }
+    }

214-232: Let TS narrow the union; drop redundant casts

Discriminated union on payload.event already narrows types; the explicit casts aren’t needed.

Apply this diff:

-    case "calendar.sync_events":
-      await handleCalendarSyncEvents(payload as CalendarSyncEventsEvent);
+    case "calendar.sync_events":
+      await handleCalendarSyncEvents(payload);
       return NextResponse.json({
         message: "Calendar sync events processed successfully",
       });
-    case "calendar.update":
-      await handleCalendarUpdate(payload as CalendarUpdateEvent);
+    case "calendar.update":
+      await handleCalendarUpdate(payload);
       return NextResponse.json({
         message: "Calendar update processed successfully",
       });
-    case "recording.done":
-      await handleRecordingDone(payload as RecordingDoneEvent);
+    case "recording.done":
+      await handleRecordingDone(payload);
       return NextResponse.json({
         message: "Recording done processed successfully",
       });
-    case "transcript.done":
-      await handleTranscriptDone(payload as TranscriptDoneEvent);
+    case "transcript.done":
+      await handleTranscriptDone(payload);
       return NextResponse.json({
         message: "Transcript done processed successfully",
       });
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bc742f1 and 9ba3970.

📒 Files selected for processing (10)
  • apps/web/app/api/recall/calendar/route.ts (1 hunks)
  • apps/web/app/api/recall/webhook/route.ts (1 hunks)
  • apps/web/app/api/recall/webhook/types.ts (1 hunks)
  • apps/web/env.ts (1 hunks)
  • apps/web/prisma/schema.prisma (5 hunks)
  • apps/web/utils/recall/bot.ts (1 hunks)
  • apps/web/utils/recall/calendar.ts (1 hunks)
  • apps/web/utils/recall/request.ts (1 hunks)
  • apps/web/utils/recall/transcript.ts (1 hunks)
  • apps/web/utils/recall/verify-svix-webhook.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (8)
  • apps/web/env.ts
  • apps/web/utils/recall/calendar.ts
  • apps/web/utils/recall/verify-svix-webhook.ts
  • apps/web/utils/recall/bot.ts
  • apps/web/utils/recall/transcript.ts
  • apps/web/utils/recall/request.ts
  • apps/web/app/api/recall/calendar/route.ts
  • apps/web/app/api/recall/webhook/types.ts
🧰 Additional context used
📓 Path-based instructions (11)
!{.cursor/rules/*.mdc}

📄 CodeRabbit inference engine (.cursor/rules/cursor-rules.mdc)

Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location

Files:

  • apps/web/prisma/schema.prisma
  • apps/web/app/api/recall/webhook/route.ts
apps/web/prisma/schema.prisma

📄 CodeRabbit inference engine (.cursor/rules/prisma.mdc)

The Prisma schema file must be located at apps/web/prisma/schema.prisma

Files:

  • apps/web/prisma/schema.prisma
!pages/_document.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.

Files:

  • apps/web/prisma/schema.prisma
  • apps/web/app/api/recall/webhook/route.ts
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use @/ for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX

Files:

  • apps/web/app/api/recall/webhook/route.ts
apps/web/app/**

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

NextJS app router structure with (app) directory

Files:

  • apps/web/app/api/recall/webhook/route.ts
apps/web/app/api/**/route.ts

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

apps/web/app/api/**/route.ts: Use withAuth for user-level operations
Use withEmailAccount for email-account-level operations
Do NOT use POST API routes for mutations - use server actions instead
No need for try/catch in GET routes when using middleware
Export response types from GET routes

apps/web/app/api/**/route.ts: Wrap all GET API route handlers with withAuth or withEmailAccount middleware for authentication and authorization.
Export response types from GET API routes for type-safe client usage.
Do not use try/catch in GET API routes when using authentication middleware; rely on centralized error handling.

Files:

  • apps/web/app/api/recall/webhook/route.ts
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)

**/*.ts: The same validation should be done in the server action too
Define validation schemas using Zod

Files:

  • apps/web/app/api/recall/webhook/route.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)

**/*.{ts,tsx}: Use createScopedLogger for logging in backend TypeScript files
Typically add the logger initialization at the top of the file when using createScopedLogger
Only use .with() on a logger instance within a specific function, not for a global logger

Import Prisma in the project using import prisma from "@/utils/prisma";

**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.

Files:

  • apps/web/app/api/recall/webhook/route.ts
**/api/**/route.ts

📄 CodeRabbit inference engine (.cursor/rules/security.mdc)

**/api/**/route.ts: ALL API routes that handle user data MUST use appropriate authentication and authorization middleware (withAuth or withEmailAccount).
ALL database queries in API routes MUST be scoped to the authenticated user/account (e.g., include userId or emailAccountId in query filters).
Always validate that resources belong to the authenticated user before performing operations (resource ownership validation).
Use withEmailAccount middleware for API routes that operate on a specific email account (i.e., use or require emailAccountId).
Use withAuth middleware for API routes that operate at the user level (i.e., use or require only userId).
Use withError middleware (with proper validation) for public endpoints, custom authentication, or cron endpoints.
Cron endpoints MUST use withError middleware and validate the cron secret using hasCronSecret(request) or hasPostCronSecret(request).
Cron endpoints MUST capture unauthorized attempts with captureException and return a 401 status for unauthorized requests.
All parameters in API routes MUST be validated for type, format, and length before use.
Request bodies in API routes MUST be validated using Zod schemas before use.
All Prisma queries in API routes MUST only return necessary fields and never expose sensitive data.
Error messages in API routes MUST not leak internal information or sensitive data; use generic error messages and SafeError where appropriate.
API routes MUST use a consistent error response format, returning JSON with an error message and status code.
All findUnique and findFirst Prisma calls in API routes MUST include ownership filters (e.g., userId or emailAccountId).
All findMany Prisma calls in API routes MUST be scoped to the authenticated user's data.
Never use direct object references in API routes without ownership checks (prevent IDOR vulnerabilities).
Prevent mass assignment vulnerabilities by only allowing explicitly whitelisted fields in update operations in AP...

Files:

  • apps/web/app/api/recall/webhook/route.ts
apps/web/app/api/**/*.{ts,js}

📄 CodeRabbit inference engine (.cursor/rules/security-audit.mdc)

apps/web/app/api/**/*.{ts,js}: All API route handlers in 'apps/web/app/api/' must use authentication middleware: withAuth, withEmailAccount, or withError (with custom authentication logic).
All Prisma queries in API routes must include user/account filtering (e.g., emailAccountId or userId in WHERE clauses) to prevent unauthorized data access.
All parameters used in API routes must be validated before use; do not use parameters from 'params' or request bodies directly in queries without validation.
Request bodies in API routes should use Zod schemas for validation.
API routes should only return necessary fields using Prisma's 'select' and must not include sensitive data in error messages.
Error messages in API routes must not reveal internal details; use generic errors and SafeError for user-facing errors.
All QStash endpoints (API routes called via publishToQstash or publishToQstashQueue) must use verifySignatureAppRouter to verify request authenticity.
All cron endpoints in API routes must use hasCronSecret or hasPostCronSecret for authentication.
Do not hardcode weak or plaintext secrets in API route files; secrets must not be directly assigned as string literals.
Review all new withError usage in API routes to ensure custom authentication is implemented where required.

Files:

  • apps/web/app/api/recall/webhook/route.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{js,jsx,ts,tsx}: Don't use elements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...

Files:

  • apps/web/app/api/recall/webhook/route.ts
🧠 Learnings (6)
📚 Learning: 2025-07-20T09:00:41.968Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-07-20T09:00:41.968Z
Learning: Applies to apps/web/app/api/**/*.{ts,js} : Request bodies in API routes should use Zod schemas for validation.

Applied to files:

  • apps/web/app/api/recall/webhook/route.ts
📚 Learning: 2025-07-18T17:27:46.389Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-07-18T17:27:46.389Z
Learning: Applies to **/api/**/route.ts : Request bodies in API routes MUST be validated using Zod schemas before use.

Applied to files:

  • apps/web/app/api/recall/webhook/route.ts
📚 Learning: 2025-09-17T22:05:28.616Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-09-17T22:05:28.616Z
Learning: Applies to apps/web/utils/ai/**/*.{ts,tsx} : Always define a Zod schema for response validation

Applied to files:

  • apps/web/app/api/recall/webhook/route.ts
📚 Learning: 2025-07-18T15:04:30.467Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/utils/actions/**/*.ts : Use Zod schemas for validation on both client and server

Applied to files:

  • apps/web/app/api/recall/webhook/route.ts
📚 Learning: 2025-07-18T15:05:16.146Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.146Z
Learning: Applies to apps/web/utils/actions/*.validation.ts : Define Zod schemas for validation in dedicated files and use them for both client and server validation.

Applied to files:

  • apps/web/app/api/recall/webhook/route.ts
📚 Learning: 2025-09-17T22:05:28.616Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-09-17T22:05:28.616Z
Learning: Applies to apps/web/utils/ai/**/*.{ts,tsx} : Make Zod schemas as specific as possible to guide LLM output

Applied to files:

  • apps/web/app/api/recall/webhook/route.ts
🧬 Code graph analysis (1)
apps/web/app/api/recall/webhook/route.ts (8)
apps/web/utils/logger.ts (1)
  • createScopedLogger (17-72)
apps/web/utils/middleware.ts (1)
  • withError (243-245)
apps/web/env.ts (1)
  • env (16-245)
apps/web/utils/recall/verify-svix-webhook.ts (1)
  • verifySvixWebhook (16-65)
apps/web/app/api/recall/webhook/types.ts (6)
  • RecallWebhookPayload (58-62)
  • CalendarSyncEventsEvent (8-14)
  • CalendarUpdateEvent (1-6)
  • RecordingDoneEvent (16-33)
  • TranscriptDoneEvent (35-56)
  • CalendarEvent (119-126)
apps/web/utils/recall/calendar.ts (3)
  • fetchCalendarEvents (124-165)
  • getRecallCalendar (73-99)
  • deleteRecallCalendar (104-122)
apps/web/utils/recall/bot.ts (1)
  • rescheduleBotsForUpdatedEvents (115-160)
apps/web/utils/recall/transcript.ts (3)
  • createAsyncTranscript (10-37)
  • getTranscriptMetadata (39-53)
  • fetchTranscriptContent (55-81)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Static Code Analysis Js
  • GitHub Check: Vercel Agent Review
  • GitHub Check: Jit Security
🔇 Additional comments (4)
apps/web/prisma/schema.prisma (3)

162-164: Add relation: looks good

EmailAccount → RecallMeeting relation is well-defined.


851-860: Verify uniqueness semantics for nullable recallCalendarId

PostgreSQL allows multiple NULLs under a unique constraint. If you rely on uniqueness only for non-null values (typical), this is fine. If you intended to allow at most one NULL, that won’t hold.


1063-1069: Enum addition: looks good

Statuses align with usage across the handlers.

apps/web/app/api/recall/webhook/route.ts (1)

188-205: Propagating processRecallWebhook response fixes prior 200-on-unsupported bug

Returning the NextResponse from processRecallWebhook resolves the earlier issue noted in prior review.

Also applies to: 207-240

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (8)
apps/web/utils/recall/calendar.ts (3)

66-72: Security: error logging may leak OAuth secrets.

Line 68 logs the raw error object, which may contain sensitive fields (e.g., oauth_client_secret, oauth_refresh_token) echoed by the Recall API in error responses. Sanitize the error before logging to avoid leaking secrets.

Apply this diff to log only safe error metadata:

   } catch (error) {
     logger.error("Failed to create calendar in Recall", {
-      error,
+      error: error instanceof Error ? error.message : "Unknown error",
       platform: "google_calendar",
     });
     throw error;
   }

93-103: Security: error logging may leak sensitive data.

Line 99 logs the raw error object. Apply the same sanitization as suggested for createRecallCalendar.

Apply this diff:

     logger.error("Failed to get calendar from Recall", {
-      error,
+      error: error instanceof Error ? error.message : "Unknown error",
       recallCalendarId,
     });

120-126: Security: error logging may leak sensitive data.

Line 122 logs the raw error object. Apply the same sanitization.

Apply this diff:

     logger.error("Failed to delete calendar from Recall", {
-      error,
+      error: error instanceof Error ? error.message : "Unknown error",
       recallCalendarId,
     });
apps/web/utils/recall/bot.ts (1)

137-151: Consider: Conditional status update based on API success

The meeting is marked CANCELLED even when removeBotFromCalendarEvent fails, potentially creating inconsistent state where the database reflects a cancelled bot that's still active in Recall. While the current "best effort" approach may be acceptable if bots auto-cleanup, consider tracking the API failure state.

If strict consistency is required, apply this diff:

+      let botRemoved = false;
       if (existingMeeting) {
         try {
           await removeBotFromCalendarEvent(event.id);
+          botRemoved = true;
         } catch (error) {
           logger.warn("Failed to remove existing bot via Recall API", {
             botId: existingMeeting.botId,
             eventId: event.id,
             error: error instanceof Error ? error.message : error,
           });
         }

-        await prisma.recallMeeting.update({
-          where: { id: existingMeeting.id },
-          data: { status: "CANCELLED" },
-        });
+        if (botRemoved) {
+          await prisma.recallMeeting.update({
+            where: { id: existingMeeting.id },
+            data: { status: "CANCELLED" },
+          });
+        } else {
+          // Optionally: mark as ERROR or log for manual review
+          logger.error("Cannot mark meeting as CANCELLED due to API failure", {
+            meetingId: existingMeeting.id,
+            eventId: event.id,
+          });
+        }
       }

Alternatively, if the "best effort" approach is intentional and Recall bots self-cleanup, document this behavior in a comment.

apps/web/app/api/recall/webhook/route.ts (4)

94-95: Add Zod validation for webhook payload

The request body is parsed without validation. Project guidelines require Zod schemas for all API request bodies to ensure type safety and prevent malformed payloads.

Define validation schemas after imports:

import { z } from "zod";

const CalendarUpdateEventSchema = z.object({
  event: z.literal("calendar.update"),
  data: z.object({ calendar_id: z.string().min(1) }),
});

const CalendarSyncEventsEventSchema = z.object({
  event: z.literal("calendar.sync_events"),
  data: z.object({
    calendar_id: z.string().min(1),
    last_updated_ts: z.string().min(1),
  }),
});

const RecordingDoneEventSchema = z.object({
  event: z.literal("recording.done"),
  data: z.object({
    recording: z.object({ id: z.string().min(1) }),
    bot: z.object({ id: z.string().min(1) }).nullable(),
  }),
});

const TranscriptDoneEventSchema = z.object({
  event: z.literal("transcript.done"),
  data: z.object({
    bot: z.object({ id: z.string().min(1) }),
    transcript: z.object({ id: z.string().min(1) }),
  }),
});

const RecallWebhookPayloadSchema = z.discriminatedUnion("event", [
  CalendarUpdateEventSchema,
  CalendarSyncEventsEventSchema,
  RecordingDoneEventSchema,
  TranscriptDoneEventSchema,
]);

Then validate before processing:

- const body = JSON.parse(rawBody) as RecallWebhookPayload;
+ const parsedJson = JSON.parse(rawBody);
+ const parseResult = RecallWebhookPayloadSchema.safeParse(parsedJson);
+ if (!parseResult.success) {
+   logger.error("Invalid webhook payload", {
+     issues: parseResult.error.flatten(),
+   });
+   return NextResponse.json({ message: "Invalid payload" }, { status: 400 });
+ }
+ const body = parseResult.data;
  const eventType = body.event;

Based on coding guidelines


120-143: Bug: Unsupported events still return 200 due to ignored NextResponse

The default case returns a 400 NextResponse for unsupported events, but processRecallWebhook is declared as returning Promise<void> and the caller at line 98 doesn't check for a response. This means unsupported events will incorrectly receive a 200 OK.

Change the function signature to return the response:

-async function processRecallWebhook(payload: RecallWebhookPayload) {
+async function processRecallWebhook(
+  payload: RecallWebhookPayload,
+): Promise<NextResponse | void> {
   const eventType = payload.event;

   switch (eventType) {

Update the caller to propagate the response:

   try {
-    await processRecallWebhook(body);
-
-    return NextResponse.json({
-      message: "Webhook processed successfully",
-    });
+    const response = await processRecallWebhook(body);
+    if (response) return response;
+    
+    return NextResponse.json({
+      message: "Webhook processed successfully",
+    });
   } catch (error) {

192-200: Restrict Prisma query to necessary fields only

Including the full emailAccount relation exposes potentially sensitive fields (tokens, credentials). Project guidelines require scoping queries to minimum necessary fields.

Apply this diff:

   const connection = await prisma.calendarConnection.findUnique({
     where: { recallCalendarId: calendarId },
-    include: {
-      emailAccount: true,
-      calendars: {
-        where: { isEnabled: true },
-      },
-    },
+    select: {
+      id: true,
+      emailAccountId: true,
+      calendars: {
+        where: { isEnabled: true },
+        select: { id: true },
+      },
+    },
   });

Based on coding guidelines


314-322: Restrict Prisma query to necessary fields only

Same issue as handleCalendarSyncEvents: including full emailAccount exposes sensitive fields.

Apply this diff:

   const connection = await prisma.calendarConnection.findUnique({
     where: { recallCalendarId: calendarId },
-    include: {
-      emailAccount: true,
-      calendars: {
-        where: { isEnabled: true },
-      },
-    },
+    select: {
+      id: true,
+      emailAccountId: true,
+      calendars: {
+        where: { isEnabled: true },
+        select: { id: true },
+      },
+    },
   });

Based on coding guidelines

🧹 Nitpick comments (2)
apps/web/app/api/google/calendar/callback/route.ts (1)

145-146: Consider encapsulating token access within createRecallCalendar.

As noted in the past review comment, passing oauth_refresh_token as a parameter requires the caller to know about and access the token. Consider refactoring createRecallCalendar to accept a connectionId and fetch the refresh token internally, improving encapsulation and reducing coupling.

Example refactor in apps/web/utils/recall/calendar.ts:

export async function createRecallCalendar(
  connectionId: string,
): Promise<RecallCalendar> {
  const connection = await prisma.calendarConnection.findUnique({
    where: { id: connectionId },
    select: { refreshToken: true },
  });

  if (!connection?.refreshToken) {
    throw new Error("No refresh token available for calendar connection");
  }

  // ... rest of implementation using connection.refreshToken
}
apps/web/app/api/recall/webhook/route.ts (1)

382-466: Good: Robust transcript format handling

The handler appropriately handles multiple transcript response formats and safely falls back to JSON stringification for unexpected formats.

Optional optimization: The findUnique at line 440 could scope to only { id: true } since only the ID is needed for the subsequent update.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7bdd809 and a0c4fad.

📒 Files selected for processing (12)
  • apps/web/app/api/google/calendar/callback/route.ts (3 hunks)
  • apps/web/app/api/recall/webhook/route.ts (1 hunks)
  • apps/web/app/api/user/recall-calendar/route.ts (1 hunks)
  • apps/web/components/RecallCalendarManager.tsx (1 hunks)
  • apps/web/hooks/useRecallCalendar.ts (1 hunks)
  • apps/web/prisma/migrations/20250930170850_add_recall_meeting_support/migration.sql (1 hunks)
  • apps/web/prisma/schema.prisma (4 hunks)
  • apps/web/utils/actions/calendar.ts (2 hunks)
  • apps/web/utils/actions/recall-calendar.ts (1 hunks)
  • apps/web/utils/recall/bot.ts (1 hunks)
  • apps/web/utils/recall/calendar.ts (1 hunks)
  • apps/web/utils/recall/verify-svix-webhook.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (24)
!{.cursor/rules/*.mdc}

📄 CodeRabbit inference engine (.cursor/rules/cursor-rules.mdc)

Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location

Files:

  • apps/web/prisma/schema.prisma
  • apps/web/hooks/useRecallCalendar.ts
  • apps/web/components/RecallCalendarManager.tsx
  • apps/web/utils/recall/bot.ts
  • apps/web/app/api/user/recall-calendar/route.ts
  • apps/web/prisma/migrations/20250930170850_add_recall_meeting_support/migration.sql
  • apps/web/utils/actions/recall-calendar.ts
  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/app/api/google/calendar/callback/route.ts
  • apps/web/utils/actions/calendar.ts
  • apps/web/utils/recall/calendar.ts
  • apps/web/utils/recall/verify-svix-webhook.ts
apps/web/prisma/schema.prisma

📄 CodeRabbit inference engine (.cursor/rules/prisma.mdc)

The Prisma schema file must be located at apps/web/prisma/schema.prisma

Files:

  • apps/web/prisma/schema.prisma
!pages/_document.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.

Files:

  • apps/web/prisma/schema.prisma
  • apps/web/hooks/useRecallCalendar.ts
  • apps/web/components/RecallCalendarManager.tsx
  • apps/web/utils/recall/bot.ts
  • apps/web/app/api/user/recall-calendar/route.ts
  • apps/web/prisma/migrations/20250930170850_add_recall_meeting_support/migration.sql
  • apps/web/utils/actions/recall-calendar.ts
  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/app/api/google/calendar/callback/route.ts
  • apps/web/utils/actions/calendar.ts
  • apps/web/utils/recall/calendar.ts
  • apps/web/utils/recall/verify-svix-webhook.ts
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use @/ for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX

Files:

  • apps/web/hooks/useRecallCalendar.ts
  • apps/web/components/RecallCalendarManager.tsx
  • apps/web/utils/recall/bot.ts
  • apps/web/app/api/user/recall-calendar/route.ts
  • apps/web/utils/actions/recall-calendar.ts
  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/app/api/google/calendar/callback/route.ts
  • apps/web/utils/actions/calendar.ts
  • apps/web/utils/recall/calendar.ts
  • apps/web/utils/recall/verify-svix-webhook.ts
apps/web/hooks/**/*.ts

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

Use SWR for efficient data fetching and caching

apps/web/hooks/**/*.ts: Use SWR for client-side data fetching and caching.
Call mutate() after successful mutations to refresh SWR data on the client.

Files:

  • apps/web/hooks/useRecallCalendar.ts
apps/web/hooks/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

Call mutate() after successful mutations to refresh data

Files:

  • apps/web/hooks/useRecallCalendar.ts
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)

**/*.ts: The same validation should be done in the server action too
Define validation schemas using Zod

Files:

  • apps/web/hooks/useRecallCalendar.ts
  • apps/web/utils/recall/bot.ts
  • apps/web/app/api/user/recall-calendar/route.ts
  • apps/web/utils/actions/recall-calendar.ts
  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/app/api/google/calendar/callback/route.ts
  • apps/web/utils/actions/calendar.ts
  • apps/web/utils/recall/calendar.ts
  • apps/web/utils/recall/verify-svix-webhook.ts
apps/web/hooks/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/hooks.mdc)

Place custom hooks in the apps/web/hooks/ directory.

Files:

  • apps/web/hooks/useRecallCalendar.ts
apps/web/hooks/use*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/hooks.mdc)

apps/web/hooks/use*.{js,jsx,ts,tsx}: Name custom hooks with the use prefix (e.g., useAccounts.ts).
For fetching data from API endpoints in custom hooks, prefer using useSWR.
Create dedicated hooks for specific data types (e.g., useAccounts, useLabels).
Custom hooks should encapsulate reusable stateful logic, especially for data fetching or complex UI interactions.
Keep custom hooks focused on a single responsibility.

Files:

  • apps/web/hooks/useRecallCalendar.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)

**/*.{ts,tsx}: Use createScopedLogger for logging in backend TypeScript files
Typically add the logger initialization at the top of the file when using createScopedLogger
Only use .with() on a logger instance within a specific function, not for a global logger

Import Prisma in the project using import prisma from "@/utils/prisma";

**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.

Files:

  • apps/web/hooks/useRecallCalendar.ts
  • apps/web/components/RecallCalendarManager.tsx
  • apps/web/utils/recall/bot.ts
  • apps/web/app/api/user/recall-calendar/route.ts
  • apps/web/utils/actions/recall-calendar.ts
  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/app/api/google/calendar/callback/route.ts
  • apps/web/utils/actions/calendar.ts
  • apps/web/utils/recall/calendar.ts
  • apps/web/utils/recall/verify-svix-webhook.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{js,jsx,ts,tsx}: Don't use elements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...

Files:

  • apps/web/hooks/useRecallCalendar.ts
  • apps/web/components/RecallCalendarManager.tsx
  • apps/web/utils/recall/bot.ts
  • apps/web/app/api/user/recall-calendar/route.ts
  • apps/web/utils/actions/recall-calendar.ts
  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/app/api/google/calendar/callback/route.ts
  • apps/web/utils/actions/calendar.ts
  • apps/web/utils/recall/calendar.ts
  • apps/web/utils/recall/verify-svix-webhook.ts
apps/web/**/*.tsx

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

apps/web/**/*.tsx: Follow tailwindcss patterns with prettier-plugin-tailwindcss
Prefer functional components with hooks
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach
Follow consistent naming conventions (PascalCase for components)
Use LoadingContent component for async data
Use result?.serverError with toastError and toastSuccess
Use LoadingContent component to handle loading and error states consistently
Pass loading, error, and children props to LoadingContent

Files:

  • apps/web/components/RecallCalendarManager.tsx
apps/web/components/**/*.tsx

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

Use React Hook Form with Zod validation for form handling

Use the LoadingContent component to handle loading and error states consistently in data-fetching components.

Use PascalCase for components (e.g. components/Button.tsx)

Files:

  • apps/web/components/RecallCalendarManager.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)

**/*.tsx: Use React Hook Form with Zod for validation
Validate form inputs before submission
Show validation errors inline next to form fields

Files:

  • apps/web/components/RecallCalendarManager.tsx
**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{jsx,tsx}: Don't destructure props inside JSX components in Solid projects.
Don't use both children and dangerouslySetInnerHTML props on the same element.
Don't use Array index in keys.
Don't assign to React component props.
Don't define React components inside other components.
Don't use event handlers on non-interactive elements.
Don't assign JSX properties multiple times.
Don't add extra closing tags for components without children.
Use <>...</> instead of ....
Don't insert comments as text nodes.
Don't use the return value of React.render.
Make sure all dependencies are correctly specified in React hooks.
Make sure all React hooks are called from the top level of component functions.
Don't use unnecessary fragments.
Don't pass children as props.
Use semantic elements instead of role attributes in JSX.

Files:

  • apps/web/components/RecallCalendarManager.tsx
**/*.{html,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{html,jsx,tsx}: Don't use or elements.
Don't use accessKey attribute on any HTML element.
Don't set aria-hidden="true" on focusable elements.
Don't add ARIA roles, states, and properties to elements that don't support them.
Only use the scope prop on elements.
Don't assign non-interactive ARIA roles to interactive HTML elements.
Make sure label elements have text content and are associated with an input.
Don't assign interactive ARIA roles to non-interactive HTML elements.
Don't assign tabIndex to non-interactive HTML elements.
Don't use positive integers for tabIndex property.
Don't include "image", "picture", or "photo" in img alt prop.
Don't use explicit role property that's the same as the implicit/default role.
Make static elements with click handlers use a valid role attribute.
Always include a title element for SVG elements.
Give all elements requiring alt text meaningful information for screen readers.
Make sure anchors have content that's accessible to screen readers.
Assign tabIndex to non-interactive HTML elements with aria-activedescendant.
Include all required ARIA attributes for elements with ARIA roles.
Make sure ARIA properties are valid for the element's supported roles.
Always include a type attribute for button elements.
Make elements with interactive roles and handlers focusable.
Give heading elements content that's accessible to screen readers (not hidden with aria-hidden).
Always include a lang attribute on the html element.
Always include a title attribute for iframe elements.
Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress.
Accompany onMouseOver/onMouseOut with onFocus/onBlur.
Include caption tracks for audio and video elements.
Make sure all anchors are valid and navigable.
Ensure all ARIA properties (aria-*) are valid.
Use valid, non-abstract ARIA roles for elements with ARIA roles.
Use valid ARIA state and property values.
Use valid values for the autocomplete attribute on input eleme...

Files:

  • apps/web/components/RecallCalendarManager.tsx
apps/web/utils/**

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

Create utility functions in utils/ folder for reusable logic

Files:

  • apps/web/utils/recall/bot.ts
  • apps/web/utils/actions/recall-calendar.ts
  • apps/web/utils/actions/calendar.ts
  • apps/web/utils/recall/calendar.ts
  • apps/web/utils/recall/verify-svix-webhook.ts
apps/web/utils/**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

apps/web/utils/**/*.ts: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size

Files:

  • apps/web/utils/recall/bot.ts
  • apps/web/utils/actions/recall-calendar.ts
  • apps/web/utils/actions/calendar.ts
  • apps/web/utils/recall/calendar.ts
  • apps/web/utils/recall/verify-svix-webhook.ts
apps/web/app/**

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

NextJS app router structure with (app) directory

Files:

  • apps/web/app/api/user/recall-calendar/route.ts
  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/app/api/google/calendar/callback/route.ts
apps/web/app/api/**/route.ts

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

apps/web/app/api/**/route.ts: Use withAuth for user-level operations
Use withEmailAccount for email-account-level operations
Do NOT use POST API routes for mutations - use server actions instead
No need for try/catch in GET routes when using middleware
Export response types from GET routes

apps/web/app/api/**/route.ts: Wrap all GET API route handlers with withAuth or withEmailAccount middleware for authentication and authorization.
Export response types from GET API routes for type-safe client usage.
Do not use try/catch in GET API routes when using authentication middleware; rely on centralized error handling.

Files:

  • apps/web/app/api/user/recall-calendar/route.ts
  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/app/api/google/calendar/callback/route.ts
**/api/**/route.ts

📄 CodeRabbit inference engine (.cursor/rules/security.mdc)

**/api/**/route.ts: ALL API routes that handle user data MUST use appropriate authentication and authorization middleware (withAuth or withEmailAccount).
ALL database queries in API routes MUST be scoped to the authenticated user/account (e.g., include userId or emailAccountId in query filters).
Always validate that resources belong to the authenticated user before performing operations (resource ownership validation).
Use withEmailAccount middleware for API routes that operate on a specific email account (i.e., use or require emailAccountId).
Use withAuth middleware for API routes that operate at the user level (i.e., use or require only userId).
Use withError middleware (with proper validation) for public endpoints, custom authentication, or cron endpoints.
Cron endpoints MUST use withError middleware and validate the cron secret using hasCronSecret(request) or hasPostCronSecret(request).
Cron endpoints MUST capture unauthorized attempts with captureException and return a 401 status for unauthorized requests.
All parameters in API routes MUST be validated for type, format, and length before use.
Request bodies in API routes MUST be validated using Zod schemas before use.
All Prisma queries in API routes MUST only return necessary fields and never expose sensitive data.
Error messages in API routes MUST not leak internal information or sensitive data; use generic error messages and SafeError where appropriate.
API routes MUST use a consistent error response format, returning JSON with an error message and status code.
All findUnique and findFirst Prisma calls in API routes MUST include ownership filters (e.g., userId or emailAccountId).
All findMany Prisma calls in API routes MUST be scoped to the authenticated user's data.
Never use direct object references in API routes without ownership checks (prevent IDOR vulnerabilities).
Prevent mass assignment vulnerabilities by only allowing explicitly whitelisted fields in update operations in AP...

Files:

  • apps/web/app/api/user/recall-calendar/route.ts
  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/app/api/google/calendar/callback/route.ts
apps/web/app/api/**/*.{ts,js}

📄 CodeRabbit inference engine (.cursor/rules/security-audit.mdc)

apps/web/app/api/**/*.{ts,js}: All API route handlers in 'apps/web/app/api/' must use authentication middleware: withAuth, withEmailAccount, or withError (with custom authentication logic).
All Prisma queries in API routes must include user/account filtering (e.g., emailAccountId or userId in WHERE clauses) to prevent unauthorized data access.
All parameters used in API routes must be validated before use; do not use parameters from 'params' or request bodies directly in queries without validation.
Request bodies in API routes should use Zod schemas for validation.
API routes should only return necessary fields using Prisma's 'select' and must not include sensitive data in error messages.
Error messages in API routes must not reveal internal details; use generic errors and SafeError for user-facing errors.
All QStash endpoints (API routes called via publishToQstash or publishToQstashQueue) must use verifySignatureAppRouter to verify request authenticity.
All cron endpoints in API routes must use hasCronSecret or hasPostCronSecret for authentication.
Do not hardcode weak or plaintext secrets in API route files; secrets must not be directly assigned as string literals.
Review all new withError usage in API routes to ensure custom authentication is implemented where required.

Files:

  • apps/web/app/api/user/recall-calendar/route.ts
  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/app/api/google/calendar/callback/route.ts
apps/web/utils/actions/**/*.ts

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

apps/web/utils/actions/**/*.ts: Use server actions for all mutations (create/update/delete operations)
next-safe-action provides centralized error handling
Use Zod schemas for validation on both client and server
Use revalidatePath in server actions for cache invalidation

apps/web/utils/actions/**/*.ts: Use server actions (with next-safe-action) for all mutations (create/update/delete operations); do NOT use POST API routes for mutations.
Use revalidatePath in server actions to invalidate cache after mutations.

Files:

  • apps/web/utils/actions/recall-calendar.ts
  • apps/web/utils/actions/calendar.ts
apps/web/utils/actions/*.ts

📄 CodeRabbit inference engine (.cursor/rules/server-actions.mdc)

apps/web/utils/actions/*.ts: Implement all server actions using the next-safe-action library for type safety, input validation, context management, and error handling. Refer to apps/web/utils/actions/safe-action.ts for client definitions (actionClient, actionClientUser, adminActionClient).
Use actionClientUser when only authenticated user context (userId) is needed.
Use actionClient when both authenticated user context and a specific emailAccountId are needed. The emailAccountId must be bound when calling the action from the client.
Use adminActionClient for actions restricted to admin users.
Access necessary context (like userId, emailAccountId, etc.) provided by the safe action client via the ctx object in the .action() handler.
Server Actions are strictly for mutations (operations that change data, e.g., creating, updating, deleting). Do NOT use Server Actions for data fetching (GET operations). For data fetching, use dedicated GET API Routes combined with SWR Hooks.
Use SafeError for expected/handled errors within actions if needed. next-safe-action provides centralized error handling.
Use the .metadata({ name: "actionName" }) method to provide a meaningful name for monitoring. Sentry instrumentation is automatically applied via withServerActionInstrumentation within the safe action clients.
If an action modifies data displayed elsewhere, use revalidatePath or revalidateTag from next/cache within the action handler as needed.

Server action files must start with use server

Files:

  • apps/web/utils/actions/recall-calendar.ts
  • apps/web/utils/actions/calendar.ts
🧠 Learnings (6)
📚 Learning: 2025-07-18T15:05:41.705Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/hooks.mdc:0-0
Timestamp: 2025-07-18T15:05:41.705Z
Learning: Applies to apps/web/hooks/use*.{js,jsx,ts,tsx} : For fetching data from API endpoints in custom hooks, prefer using `useSWR`.

Applied to files:

  • apps/web/hooks/useRecallCalendar.ts
📚 Learning: 2025-07-18T15:04:30.467Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/hooks/**/*.ts : Use SWR for efficient data fetching and caching

Applied to files:

  • apps/web/hooks/useRecallCalendar.ts
📚 Learning: 2025-07-18T15:05:16.146Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.146Z
Learning: Applies to apps/web/app/api/**/route.ts : Wrap all GET API route handlers with `withAuth` or `withEmailAccount` middleware for authentication and authorization.

Applied to files:

  • apps/web/app/api/user/recall-calendar/route.ts
📚 Learning: 2025-07-20T09:00:41.968Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-07-20T09:00:41.968Z
Learning: Applies to apps/web/app/api/**/*.{ts,js} : Request bodies in API routes should use Zod schemas for validation.

Applied to files:

  • apps/web/app/api/recall/webhook/route.ts
📚 Learning: 2025-07-18T17:27:46.389Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-07-18T17:27:46.389Z
Learning: Applies to **/api/**/route.ts : Request bodies in API routes MUST be validated using Zod schemas before use.

Applied to files:

  • apps/web/app/api/recall/webhook/route.ts
📚 Learning: 2025-07-20T09:00:41.968Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-07-20T09:00:41.968Z
Learning: Applies to apps/web/app/api/**/*.{ts,js} : API routes should only return necessary fields using Prisma's 'select' and must not include sensitive data in error messages.

Applied to files:

  • apps/web/app/api/recall/webhook/route.ts
🧬 Code graph analysis (10)
apps/web/hooks/useRecallCalendar.ts (1)
apps/web/app/api/user/recall-calendar/route.ts (1)
  • GetRecallCalendarResponse (6-6)
apps/web/components/RecallCalendarManager.tsx (4)
apps/web/hooks/useRecallCalendar.ts (1)
  • useRecallCalendar (4-6)
apps/web/providers/EmailAccountProvider.tsx (1)
  • useAccount (72-82)
apps/web/utils/actions/recall-calendar.ts (2)
  • createRecallCalendarAction (14-71)
  • deleteRecallCalendarAction (73-137)
apps/web/components/LoadingContent.tsx (1)
  • LoadingContent (13-27)
apps/web/utils/recall/bot.ts (3)
apps/web/utils/logger.ts (1)
  • createScopedLogger (17-72)
apps/web/app/api/recall/webhook/types.ts (2)
  • CalendarEvent (119-126)
  • RecallCalendarEventResponse (138-141)
apps/web/utils/recall/request.ts (1)
  • recallRequest (10-57)
apps/web/app/api/user/recall-calendar/route.ts (2)
apps/web/utils/middleware.ts (1)
  • withEmailAccount (294-299)
apps/web/utils/recall/calendar.ts (1)
  • getRecallCalendar (78-104)
apps/web/utils/actions/recall-calendar.ts (3)
apps/web/utils/logger.ts (1)
  • createScopedLogger (17-72)
apps/web/utils/actions/safe-action.ts (1)
  • actionClient (62-113)
apps/web/utils/recall/calendar.ts (2)
  • createRecallCalendar (34-73)
  • deleteRecallCalendar (109-127)
apps/web/app/api/recall/webhook/route.ts (8)
apps/web/utils/logger.ts (1)
  • createScopedLogger (17-72)
apps/web/utils/middleware.ts (1)
  • withError (283-288)
apps/web/env.ts (1)
  • env (16-245)
apps/web/utils/recall/verify-svix-webhook.ts (1)
  • verifySvixWebhook (16-63)
apps/web/app/api/recall/webhook/types.ts (6)
  • RecallWebhookPayload (58-62)
  • CalendarSyncEventsEvent (8-14)
  • CalendarUpdateEvent (1-6)
  • RecordingDoneEvent (16-33)
  • TranscriptDoneEvent (35-56)
  • CalendarEvent (119-126)
apps/web/utils/recall/calendar.ts (3)
  • fetchCalendarEvents (129-170)
  • getRecallCalendar (78-104)
  • deleteRecallCalendar (109-127)
apps/web/utils/recall/bot.ts (1)
  • rescheduleBotsForUpdatedEvents (122-167)
apps/web/utils/recall/transcript.ts (3)
  • createAsyncTranscript (10-37)
  • getTranscriptMetadata (39-53)
  • fetchTranscriptContent (55-81)
apps/web/app/api/google/calendar/callback/route.ts (1)
apps/web/utils/recall/calendar.ts (1)
  • createRecallCalendar (34-73)
apps/web/utils/actions/calendar.ts (2)
apps/web/utils/actions/safe-action.ts (1)
  • actionClient (62-113)
apps/web/utils/recall/calendar.ts (1)
  • deleteRecallCalendar (109-127)
apps/web/utils/recall/calendar.ts (4)
apps/web/utils/logger.ts (1)
  • createScopedLogger (17-72)
apps/web/env.ts (1)
  • env (16-245)
apps/web/utils/recall/request.ts (1)
  • recallRequest (10-57)
apps/web/app/api/recall/webhook/types.ts (2)
  • RecallCalendarEventsListResponse (87-91)
  • RecallCalendarEvent (71-85)
apps/web/utils/recall/verify-svix-webhook.ts (1)
apps/web/utils/logger.ts (1)
  • createScopedLogger (17-72)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Static Code Analysis Js
  • GitHub Check: Vercel Agent Review
  • GitHub Check: Jit Security
🔇 Additional comments (28)
apps/web/utils/recall/verify-svix-webhook.ts (1)

1-4: LGTM!

Correct import structure and logger initialization following the project's logging guidelines.

apps/web/hooks/useRecallCalendar.ts (1)

1-6: LGTM!

The hook follows best practices:

  • Uses useSWR for data fetching as per coding guidelines
  • Properly typed with GetRecallCalendarResponse
  • Uses import type for type imports
  • Follows naming convention with use prefix
  • Simple and focused on a single responsibility

Based on learnings and coding guidelines.

apps/web/prisma/migrations/20250930170850_add_recall_meeting_support/migration.sql (2)

22-23: LGTM - Proper unique constraint on botId.

The unique constraint on botId ensures one-to-one mapping between bots and meetings, which is correct for this integration.


28-29: LGTM - Efficient composite index for queries.

The composite index on (emailAccountId, eventId, status) will efficiently support queries filtering by account and event with status, which are likely common access patterns.

apps/web/components/RecallCalendarManager.tsx (3)

46-109: LGTM - Well-structured UI with proper state handling.

The component correctly:

  • Uses LoadingContent to handle loading and error states
  • Shows appropriate buttons based on state (hasRecallCalendars)
  • Disables the create button when no calendars are connected
  • Displays calendar status with clear visual indicators
  • Calls mutate() after successful mutations to refresh SWR data

As per coding guidelines.


32-44: Remove parameter from action invocation.

The action deleteRecallCalendarAction uses ctx.emailAccountId from the action context and doesn't accept parameters. Passing emailAccountId as an argument (line 33) is incorrect.

Apply this diff:

 const handleDeleteCalendar = useCallback(async () => {
-  const result = await deleteRecallCalendarAction(emailAccountId);
+  const result = await deleteRecallCalendarAction();

   if (result?.serverError) {
     toastError({
       title: "Error deleting Recall calendar",
       description: result.serverError,
     });
   } else {
     toastSuccess({ description: "Recall calendar deleted successfully!" });
     mutate();
   }
-}, [emailAccountId, mutate]);
+}, [mutate]);
⛔ Skipped due to learnings
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-07-18T17:27:58.249Z
Learning: Applies to apps/web/utils/actions/*.ts : Use `actionClient` when both authenticated user context and a specific `emailAccountId` are needed. The `emailAccountId` must be bound when calling the action from the client.

18-30: Remove parameter from action invocation.

The action createRecallCalendarAction uses ctx.emailAccountId from the action context and doesn't accept parameters. Passing emailAccountId as an argument (line 19) is incorrect.

Apply this diff:

 const handleCreateCalendar = useCallback(async () => {
-  const result = await createRecallCalendarAction(emailAccountId);
+  const result = await createRecallCalendarAction();

   if (result?.serverError) {
     toastError({
       title: "Error creating Recall calendar",
       description: result.serverError,
     });
   } else {
     toastSuccess({ description: "Recall calendar created successfully!" });
     mutate();
   }
-}, [emailAccountId, mutate]);
+}, [mutate]);
⛔ Skipped due to learnings
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-07-18T17:27:58.249Z
Learning: Applies to apps/web/utils/actions/*.ts : Use `actionClient` when both authenticated user context and a specific `emailAccountId` are needed. The `emailAccountId` must be bound when calling the action from the client.
apps/web/utils/actions/calendar.ts (3)

10-10: LGTM!

The import is correctly placed and aligns with the new Recall integration.


16-19: LGTM!

Correctly uses logger from the action context, which automatically includes contextual metadata like emailAccountId.


31-45: LGTM!

The resilient error handling ensures the local CalendarConnection is deleted even if the Recall API call fails. The error serialization and logging are appropriate.

apps/web/utils/actions/recall-calendar.ts (2)

73-137: LGTM (after applying logger refactor).

The resilient per-connection error handling and result aggregation are well implemented. Ensure you apply the logger refactor from the earlier comment.


9-12: Remove the module-level logger and use ctx.logger instead.

Server actions receive logger in the ctx parameter, which automatically includes contextual metadata (e.g., emailAccountId, userId). Remove the createScopedLogger import and the module-level logger initialization, and destructure logger from ctx in both actions.

Based on learnings and past review comments.

Apply this diff:

-import { createScopedLogger } from "@/utils/logger";
 import { revalidatePath } from "next/cache";
 
-const logger = createScopedLogger("actions/recall-calendar");
-
 export const createRecallCalendarAction = actionClient
   .metadata({ name: "createRecallCalendar" })
-  .action(async ({ ctx: { emailAccountId } }) => {
+  .action(async ({ ctx: { emailAccountId, logger } }) => {

And similarly for deleteRecallCalendarAction:

 export const deleteRecallCalendarAction = actionClient
   .metadata({ name: "deleteRecallCalendar" })
-  .action(async ({ ctx: { emailAccountId } }) => {
+  .action(async ({ ctx: { emailAccountId, logger } }) => {

Likely an incorrect or invalid review comment.

apps/web/app/api/user/recall-calendar/route.ts (2)

1-13: LGTM!

The route correctly uses withEmailAccount for authentication and exports the response type for type-safe client usage.

As per coding guidelines.


15-59: LGTM!

The query is correctly scoped to emailAccountId, and the per-connection error handling gracefully degrades on fetch failures. The parallel fetching with Promise.all is efficient.

apps/web/prisma/schema.prisma (4)

850-858: LGTM!

The optional recallCalendarId with a unique constraint correctly enforces a one-to-one mapping between calendar connections and Recall calendars.


907-924: LGTM!

The Meeting model is well-designed with appropriate indexes for query patterns and a unique botId for one-to-one mapping. The deduplicationKey index will help prevent duplicate meeting entries.


157-161: LGTM!

The meetings relation is correctly defined and aligns with the new Meeting model.


1062-1068: LGTM!

The RecallBotStatus enum values appropriately cover the bot lifecycle states.

apps/web/utils/recall/calendar.ts (2)

1-29: LGTM!

The imports and type definitions are clear and appropriate for the Recall calendar integration.


129-170: LGTM!

The pagination implementation correctly handles the Recall API's paginated responses by iterating through all pages and aggregating results.

apps/web/app/api/recall/webhook/route.ts (3)

1-28: LGTM: Clean imports and logger setup

Imports follow project conventions and the scoped logger is correctly initialized at module level.


145-186: LGTM: Robust event fetching with proper filtering

The helper correctly filters deleted events, validates required fields, and handles errors gracefully.


353-380: LGTM: Clean async transcript creation

The handler appropriately creates an async transcript job with proper error handling and logging.

apps/web/utils/recall/bot.ts (5)

1-10: LGTM: Clean imports and logger initialization

Imports and scoped logger follow project conventions.


11-13: LGTM: Simple and effective deduplication key

Combining start_time and meeting_url provides a reasonable uniqueness guarantee for bot scheduling.


15-35: LGTM: Clean API wrapper

The function appropriately wraps the Recall bot creation API call.


37-43: LGTM: Simple deletion wrapper

Appropriately delegates to recallRequest for error handling.


45-120: LGTM: Bot scheduling with proper validation and persistence

The function validates timing, uses upsert to prevent duplicates, and includes bot customization (avatar and background color) as requested in previous feedback.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (4)
apps/web/app/api/recall/webhook/route.ts (4)

92-94: Validate payload with Zod and handle parse errors before use

Per guidelines, API request bodies must be validated with Zod. Also guard JSON.parse errors and avoid using the unvalidated payload.

Based on learnings

+import { z } from "zod";
@@
-  const body = JSON.parse(rawBody) as RecallWebhookPayload;
-  const eventType = body.event;
+  // Parse JSON safely
+  let parsedJson: unknown;
+  try {
+    parsedJson = JSON.parse(rawBody);
+  } catch {
+    logger.error("Invalid JSON in webhook body");
+    return NextResponse.json({ message: "Invalid JSON" }, { status: 400 });
+  }
+
+  // Minimal Zod schemas for supported events
+  const CalendarUpdateSchema = z.object({
+    event: z.literal("calendar.update"),
+    data: z.object({ calendar_id: z.string().min(1) }),
+  });
+  const CalendarSyncEventsSchema = z.object({
+    event: z.literal("calendar.sync_events"),
+    data: z.object({
+      calendar_id: z.string().min(1),
+      last_updated_ts: z.string().min(1),
+    }),
+  });
+  const TranscriptDoneSchema = z.object({
+    event: z.literal("transcript.done"),
+    data: z.object({
+      bot: z.object({ id: z.string().min(1) }),
+      transcript: z.object({ id: z.string().min(1) }),
+    }),
+  });
+  const RecallWebhookSchema = z.union([
+    CalendarUpdateSchema,
+    CalendarSyncEventsSchema,
+    TranscriptDoneSchema,
+  ]);
+
+  const parsed = RecallWebhookSchema.safeParse(parsedJson);
+  if (!parsed.success) {
+    logger.error("Invalid webhook payload", { issues: parsed.error.flatten() });
+    return NextResponse.json({ message: "Invalid payload" }, { status: 400 });
+  }
+  const body = parsed.data as RecallWebhookPayload;
+  const eventType = body.event;

95-101: Propagate processRecallWebhook response to avoid 200 on unsupported events

The response returned inside processRecallWebhook (e.g., 400 for unsupported events) is ignored, so the route always returns 200.

-  try {
-    await processRecallWebhook(body);
-
-    return NextResponse.json({
-      message: "Webhook processed successfully",
-    });
-  } catch (error) {
+  try {
+    const maybeResponse = await processRecallWebhook(body);
+    if (maybeResponse) return maybeResponse;
+    return NextResponse.json({ message: "Webhook processed successfully" });
+  } catch (error) {

Also update the signature to reflect the returned Response:

-async function processRecallWebhook(payload: RecallWebhookPayload) {
+async function processRecallWebhook(
+  payload: RecallWebhookPayload,
+): Promise<Response | void> {

187-195: Restrict Prisma selection to minimum required fields

Avoid include for full related records in API routes. Select only needed fields to reduce exposure risk.

Based on learnings

-  const connection = await prisma.calendarConnection.findUnique({
-    where: { recallCalendarId: calendarId },
-    include: {
-      emailAccount: true,
-      calendars: {
-        where: { isEnabled: true },
-      },
-    },
-  });
+  const connection = await prisma.calendarConnection.findUnique({
+    where: { recallCalendarId: calendarId },
+    select: {
+      id: true,
+      emailAccountId: true,
+      calendars: {
+        where: { isEnabled: true },
+        select: { id: true },
+      },
+    },
+  });

309-317: Repeat: restrict Prisma selection (avoid loading secrets)

Same as above—replace include with minimal select.

Based on learnings

-  const connection = await prisma.calendarConnection.findUnique({
-    where: { recallCalendarId: calendarId },
-    include: {
-      emailAccount: true,
-      calendars: {
-        where: { isEnabled: true },
-      },
-    },
-  });
+  const connection = await prisma.calendarConnection.findUnique({
+    where: { recallCalendarId: calendarId },
+    select: {
+      id: true,
+      emailAccountId: true,
+      calendars: {
+        where: { isEnabled: true },
+        select: { id: true },
+      },
+    },
+  });
🧹 Nitpick comments (10)
apps/web/app/api/recall/webhook/types.ts (3)

24-36: Replace unsafe object with Record<string, unknown>

metadata: object is too loose and blocks safe property access. Prefer Record<string, unknown> for future-proofing and stricter typing.

-    bot: {
-      id: string;
-      metadata: object;
-    };
+    bot: {
+      id: string;
+      metadata: Record<string, unknown>;
+    };
@@
-    transcript: {
-      id: string;
-      metadata: object;
-    };
+    transcript: {
+      id: string;
+      metadata: Record<string, unknown>;
+    };
@@
-    recording: {
-      id: string;
-      metadata: object;
-    };
+    recording: {
+      id: string;
+      metadata: Record<string, unknown>;
+    };

90-97: Loosen index signature to accept non-string values

The current index signature only allows string/null/undefined values; webhook payloads often include nested objects/arrays. Use Record<string, unknown> and keep explicit string fields.

 export type TranscriptContent =
   | string
   | TranscriptSegment[]
   | {
-      transcript?: string;
-      content?: string;
-      [key: string]: string | null | undefined;
+      transcript?: string;
+      content?: string;
+      [key: string]: unknown;
     };

60-61: Narrow platform to known values (if applicable)

If Recall only returns known platform strings (e.g., "google_calendar" | "microsoft_outlook"), narrow the union to improve type safety and autocomplete.

-  platform?: string;
+  platform?: "google_calendar" | "microsoft_outlook";
apps/web/app/api/recall/webhook/route.ts (3)

244-246: Remove empty else block

The else {} after the length check is empty—drop it.

-    if (updatedEvents.length > 0) {
+    if (updatedEvents.length > 0) {
       await rescheduleBotsForUpdatedEvents(
         connection.emailAccountId,
         updatedEvents,
         calendarId,
       );
-    } else {
-    }
+    }

377-401: De-duplicate inline transcript types

The inline type for segment duplicates structures from types.ts. Import and reuse TranscriptSegment/TranscriptWord to prevent drift.

-        .map(
-          (segment: {
-            participant?: {
-              name?: string;
-              id?: number;
-              is_host?: boolean;
-              platform?: string;
-            };
-            words?: Array<{
-              text: string;
-              start_timestamp?: { relative?: number; absolute?: string };
-              end_timestamp?: { relative?: number; absolute?: string };
-            }>;
-          }) => {
+        .map((segment: TranscriptSegment) => {

27-116: Optional: allow bypassing Svix header requirement when secret is unset

You already bypass verification when RECALL_WEBHOOK_SECRET is unset; consider not failing on missing Svix headers in that scenario to ease local/dev testing.

apps/web/utils/recall/bot.ts (4)

4-7: Invert dependency: utils should not import types from app route

utils/recall/bot.ts depends on app/api/recall/webhook/types.ts. Move shared types to a utils/shared module (e.g., apps/web/utils/recall/types.ts) and import from there to keep layering clean.

-import type {
-  CalendarEvent,
-  RecallCalendarEventResponse,
-} from "@/app/api/recall/webhook/types";
+import type { CalendarEvent, RecallCalendarEventResponse } from "@/utils/recall/types";

11-13: Strengthen deduplication key

Consider including event.id to guard against events with identical start time/URL across calendars or reschedules.

-export function generateDeduplicationKey(event: CalendarEvent): string {
-  return `${event.start_time}-${event.meeting_url}`;
-}
+export function generateDeduplicationKey(event: CalendarEvent): string {
+  return `${event.id}-${event.start_time}-${event.meeting_url ?? ""}`;
+}

68-75: Permit small lead-time window instead of strict <= now

Bots often need a buffer. Consider allowing a 1–2 minute window to still schedule close-to-now meetings.

-  if (eventStart <= now) {
+  const leewayMs = 2 * 60 * 1000; // 2 minutes
+  if (eventStart.getTime() <= now.getTime() + leewayMs) {

155-158: Avoid unconditional CANCELLED state when bot removal fails

If Recall API deletion fails, immediately marking the meeting CANCELLED can desync state. Either:

  • only mark CANCELLED on successful removal, or
  • mark as CANCELLED with an error flag/reason for follow-up reconciliation.
-      await prisma.recallMeeting.update({
-        where: { id: existingMeeting.id },
-        data: { status: "CANCELLED" },
-      });
+      // Option A: only cancel on successful removal
+      await prisma.recallMeeting.update({
+        where: { id: existingMeeting.id },
+        data: { status: "CANCELLED" },
+      });
+      // If removal failed above, consider scheduling a retry job instead.
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a0c4fad and 61377aa.

📒 Files selected for processing (4)
  • apps/web/app/api/recall/webhook/route.ts (1 hunks)
  • apps/web/app/api/recall/webhook/types.ts (1 hunks)
  • apps/web/utils/recall/bot.ts (1 hunks)
  • apps/web/utils/recall/transcript.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/utils/recall/transcript.ts
🧰 Additional context used
📓 Path-based instructions (12)
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use @/ for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX

Files:

  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/app/api/recall/webhook/types.ts
  • apps/web/utils/recall/bot.ts
apps/web/app/**

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

NextJS app router structure with (app) directory

Files:

  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/app/api/recall/webhook/types.ts
apps/web/app/api/**/route.ts

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

apps/web/app/api/**/route.ts: Use withAuth for user-level operations
Use withEmailAccount for email-account-level operations
Do NOT use POST API routes for mutations - use server actions instead
No need for try/catch in GET routes when using middleware
Export response types from GET routes

apps/web/app/api/**/route.ts: Wrap all GET API route handlers with withAuth or withEmailAccount middleware for authentication and authorization.
Export response types from GET API routes for type-safe client usage.
Do not use try/catch in GET API routes when using authentication middleware; rely on centralized error handling.

Files:

  • apps/web/app/api/recall/webhook/route.ts
!{.cursor/rules/*.mdc}

📄 CodeRabbit inference engine (.cursor/rules/cursor-rules.mdc)

Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location

Files:

  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/app/api/recall/webhook/types.ts
  • apps/web/utils/recall/bot.ts
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)

**/*.ts: The same validation should be done in the server action too
Define validation schemas using Zod

Files:

  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/app/api/recall/webhook/types.ts
  • apps/web/utils/recall/bot.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)

**/*.{ts,tsx}: Use createScopedLogger for logging in backend TypeScript files
Typically add the logger initialization at the top of the file when using createScopedLogger
Only use .with() on a logger instance within a specific function, not for a global logger

Import Prisma in the project using import prisma from "@/utils/prisma";

**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.

Files:

  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/app/api/recall/webhook/types.ts
  • apps/web/utils/recall/bot.ts
**/api/**/route.ts

📄 CodeRabbit inference engine (.cursor/rules/security.mdc)

**/api/**/route.ts: ALL API routes that handle user data MUST use appropriate authentication and authorization middleware (withAuth or withEmailAccount).
ALL database queries in API routes MUST be scoped to the authenticated user/account (e.g., include userId or emailAccountId in query filters).
Always validate that resources belong to the authenticated user before performing operations (resource ownership validation).
Use withEmailAccount middleware for API routes that operate on a specific email account (i.e., use or require emailAccountId).
Use withAuth middleware for API routes that operate at the user level (i.e., use or require only userId).
Use withError middleware (with proper validation) for public endpoints, custom authentication, or cron endpoints.
Cron endpoints MUST use withError middleware and validate the cron secret using hasCronSecret(request) or hasPostCronSecret(request).
Cron endpoints MUST capture unauthorized attempts with captureException and return a 401 status for unauthorized requests.
All parameters in API routes MUST be validated for type, format, and length before use.
Request bodies in API routes MUST be validated using Zod schemas before use.
All Prisma queries in API routes MUST only return necessary fields and never expose sensitive data.
Error messages in API routes MUST not leak internal information or sensitive data; use generic error messages and SafeError where appropriate.
API routes MUST use a consistent error response format, returning JSON with an error message and status code.
All findUnique and findFirst Prisma calls in API routes MUST include ownership filters (e.g., userId or emailAccountId).
All findMany Prisma calls in API routes MUST be scoped to the authenticated user's data.
Never use direct object references in API routes without ownership checks (prevent IDOR vulnerabilities).
Prevent mass assignment vulnerabilities by only allowing explicitly whitelisted fields in update operations in AP...

Files:

  • apps/web/app/api/recall/webhook/route.ts
apps/web/app/api/**/*.{ts,js}

📄 CodeRabbit inference engine (.cursor/rules/security-audit.mdc)

apps/web/app/api/**/*.{ts,js}: All API route handlers in 'apps/web/app/api/' must use authentication middleware: withAuth, withEmailAccount, or withError (with custom authentication logic).
All Prisma queries in API routes must include user/account filtering (e.g., emailAccountId or userId in WHERE clauses) to prevent unauthorized data access.
All parameters used in API routes must be validated before use; do not use parameters from 'params' or request bodies directly in queries without validation.
Request bodies in API routes should use Zod schemas for validation.
API routes should only return necessary fields using Prisma's 'select' and must not include sensitive data in error messages.
Error messages in API routes must not reveal internal details; use generic errors and SafeError for user-facing errors.
All QStash endpoints (API routes called via publishToQstash or publishToQstashQueue) must use verifySignatureAppRouter to verify request authenticity.
All cron endpoints in API routes must use hasCronSecret or hasPostCronSecret for authentication.
Do not hardcode weak or plaintext secrets in API route files; secrets must not be directly assigned as string literals.
Review all new withError usage in API routes to ensure custom authentication is implemented where required.

Files:

  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/app/api/recall/webhook/types.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{js,jsx,ts,tsx}: Don't use elements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...

Files:

  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/app/api/recall/webhook/types.ts
  • apps/web/utils/recall/bot.ts
!pages/_document.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.

Files:

  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/app/api/recall/webhook/types.ts
  • apps/web/utils/recall/bot.ts
apps/web/utils/**

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

Create utility functions in utils/ folder for reusable logic

Files:

  • apps/web/utils/recall/bot.ts
apps/web/utils/**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

apps/web/utils/**/*.ts: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size

Files:

  • apps/web/utils/recall/bot.ts
🧠 Learnings (3)
📚 Learning: 2025-07-20T09:00:41.968Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-07-20T09:00:41.968Z
Learning: Applies to apps/web/app/api/**/*.{ts,js} : Request bodies in API routes should use Zod schemas for validation.

Applied to files:

  • apps/web/app/api/recall/webhook/route.ts
📚 Learning: 2025-07-18T17:27:46.389Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-07-18T17:27:46.389Z
Learning: Applies to **/api/**/route.ts : Request bodies in API routes MUST be validated using Zod schemas before use.

Applied to files:

  • apps/web/app/api/recall/webhook/route.ts
📚 Learning: 2025-07-20T09:00:41.968Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-07-20T09:00:41.968Z
Learning: Applies to apps/web/app/api/**/*.{ts,js} : API routes should only return necessary fields using Prisma's 'select' and must not include sensitive data in error messages.

Applied to files:

  • apps/web/app/api/recall/webhook/route.ts
🧬 Code graph analysis (2)
apps/web/app/api/recall/webhook/route.ts (9)
apps/web/app/api/outlook/webhook/logger.ts (1)
  • logger (3-3)
apps/web/utils/logger.ts (1)
  • createScopedLogger (17-72)
apps/web/utils/middleware.ts (1)
  • withError (283-288)
apps/web/env.ts (1)
  • env (16-245)
apps/web/utils/recall/verify-svix-webhook.ts (1)
  • verifySvixWebhook (16-63)
apps/web/app/api/recall/webhook/types.ts (5)
  • RecallWebhookPayload (39-42)
  • CalendarSyncEventsEvent (8-14)
  • CalendarUpdateEvent (1-6)
  • TranscriptDoneEvent (16-37)
  • CalendarEvent (99-106)
apps/web/utils/recall/calendar.ts (3)
  • fetchCalendarEvents (129-170)
  • getRecallCalendar (78-104)
  • deleteRecallCalendar (109-127)
apps/web/utils/recall/bot.ts (1)
  • rescheduleBotsForUpdatedEvents (129-174)
apps/web/utils/recall/transcript.ts (2)
  • getTranscriptMetadata (10-24)
  • fetchTranscriptContent (26-52)
apps/web/utils/recall/bot.ts (3)
apps/web/utils/logger.ts (1)
  • createScopedLogger (17-72)
apps/web/app/api/recall/webhook/types.ts (2)
  • CalendarEvent (99-106)
  • RecallCalendarEventResponse (118-121)
apps/web/utils/recall/request.ts (1)
  • recallRequest (10-57)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Static Code Analysis Js
  • GitHub Check: Vercel Agent Review
  • GitHub Check: Jit Security

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (2)
apps/web/app/api/recall/webhook/route.ts (1)

187-195: Limit Prisma selection to required fields.

Both queries fetch emailAccount: true, which loads the entire related record (tokens, secrets, etc.) even though none of those fields are used. This violates the guideline to return only necessary fields from API routes. Switch to an explicit select with just the ids (and any other actually-used columns). As per coding guidelines

Also applies to: 310-318

apps/web/utils/recall/bot.ts (1)

144-158: State inconsistency risk when external bot deletion fails.

If the Recall API call to remove the bot fails (line 146), the meeting is still marked as CANCELLED (line 157). This creates an inconsistent state where the bot remains active in Recall's system but is marked as cancelled in the local database.

This is the same concern raised in the previous review comment.

Consider making the status update conditional on successful deletion or using a different status to indicate partial failure:

       if (existingMeeting) {
+        let removalSucceeded = false;
         try {
           await removeBotFromCalendarEvent(event.id);
+          removalSucceeded = true;
         } catch (error) {
           logger.warn("Failed to remove existing bot via Recall API", {
             botId: existingMeeting.botId,
             eventId: event.id,
             error: error instanceof Error ? error.message : error,
           });
         }
 
         await prisma.meeting.update({
           where: { id: existingMeeting.id },
-          data: { status: "CANCELLED" },
+          data: { status: removalSucceeded ? "CANCELLED" : "CANCELLATION_FAILED" },
         });
       }

Note: This requires adding CANCELLATION_FAILED to the RecallBotStatus enum in your Prisma schema.

🧹 Nitpick comments (1)
apps/web/utils/recall/bot.ts (1)

52-127: Redundant empty string fallback on meetingUrl.

Lines 94 and 102 use event.meeting_url || "" as a fallback, but line 57 already validates that meeting_url exists and returns null if it's missing. The empty string fallback is unreachable.

Apply this diff to remove the redundant fallback:

       update: {
         botWillJoinAt: new Date(event.start_time),
-        meetingUrl: event.meeting_url || "",
+        meetingUrl: event.meeting_url,
         status: "SCHEDULED",
         deduplicationKey,
       },
       create: {
         botId: bot.bot_id,
         eventId: event.id,
         emailAccountId: emailAccountId,
-        meetingUrl: event.meeting_url || "",
+        meetingUrl: event.meeting_url,
         botWillJoinAt: new Date(event.start_time),
         status: "SCHEDULED",
         deduplicationKey,
       },
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 61377aa and d8c5172.

📒 Files selected for processing (6)
  • apps/web/app/api/google/calendar/callback/route.ts (3 hunks)
  • apps/web/app/api/recall/webhook/route.ts (1 hunks)
  • apps/web/prisma/migrations/20250930170850_add_recall_meeting_support/migration.sql (1 hunks)
  • apps/web/prisma/schema.prisma (4 hunks)
  • apps/web/utils/actions/recall-calendar.ts (1 hunks)
  • apps/web/utils/recall/bot.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (15)
!{.cursor/rules/*.mdc}

📄 CodeRabbit inference engine (.cursor/rules/cursor-rules.mdc)

Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location

Files:

  • apps/web/prisma/schema.prisma
  • apps/web/app/api/google/calendar/callback/route.ts
  • apps/web/utils/actions/recall-calendar.ts
  • apps/web/utils/recall/bot.ts
  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/prisma/migrations/20250930170850_add_recall_meeting_support/migration.sql
apps/web/prisma/schema.prisma

📄 CodeRabbit inference engine (.cursor/rules/prisma.mdc)

The Prisma schema file must be located at apps/web/prisma/schema.prisma

Files:

  • apps/web/prisma/schema.prisma
!pages/_document.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.

Files:

  • apps/web/prisma/schema.prisma
  • apps/web/app/api/google/calendar/callback/route.ts
  • apps/web/utils/actions/recall-calendar.ts
  • apps/web/utils/recall/bot.ts
  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/prisma/migrations/20250930170850_add_recall_meeting_support/migration.sql
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use @/ for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX

Files:

  • apps/web/app/api/google/calendar/callback/route.ts
  • apps/web/utils/actions/recall-calendar.ts
  • apps/web/utils/recall/bot.ts
  • apps/web/app/api/recall/webhook/route.ts
apps/web/app/**

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

NextJS app router structure with (app) directory

Files:

  • apps/web/app/api/google/calendar/callback/route.ts
  • apps/web/app/api/recall/webhook/route.ts
apps/web/app/api/**/route.ts

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

apps/web/app/api/**/route.ts: Use withAuth for user-level operations
Use withEmailAccount for email-account-level operations
Do NOT use POST API routes for mutations - use server actions instead
No need for try/catch in GET routes when using middleware
Export response types from GET routes

apps/web/app/api/**/route.ts: Wrap all GET API route handlers with withAuth or withEmailAccount middleware for authentication and authorization.
Export response types from GET API routes for type-safe client usage.
Do not use try/catch in GET API routes when using authentication middleware; rely on centralized error handling.

Files:

  • apps/web/app/api/google/calendar/callback/route.ts
  • apps/web/app/api/recall/webhook/route.ts
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)

**/*.ts: The same validation should be done in the server action too
Define validation schemas using Zod

Files:

  • apps/web/app/api/google/calendar/callback/route.ts
  • apps/web/utils/actions/recall-calendar.ts
  • apps/web/utils/recall/bot.ts
  • apps/web/app/api/recall/webhook/route.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)

**/*.{ts,tsx}: Use createScopedLogger for logging in backend TypeScript files
Typically add the logger initialization at the top of the file when using createScopedLogger
Only use .with() on a logger instance within a specific function, not for a global logger

Import Prisma in the project using import prisma from "@/utils/prisma";

**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.

Files:

  • apps/web/app/api/google/calendar/callback/route.ts
  • apps/web/utils/actions/recall-calendar.ts
  • apps/web/utils/recall/bot.ts
  • apps/web/app/api/recall/webhook/route.ts
**/api/**/route.ts

📄 CodeRabbit inference engine (.cursor/rules/security.mdc)

**/api/**/route.ts: ALL API routes that handle user data MUST use appropriate authentication and authorization middleware (withAuth or withEmailAccount).
ALL database queries in API routes MUST be scoped to the authenticated user/account (e.g., include userId or emailAccountId in query filters).
Always validate that resources belong to the authenticated user before performing operations (resource ownership validation).
Use withEmailAccount middleware for API routes that operate on a specific email account (i.e., use or require emailAccountId).
Use withAuth middleware for API routes that operate at the user level (i.e., use or require only userId).
Use withError middleware (with proper validation) for public endpoints, custom authentication, or cron endpoints.
Cron endpoints MUST use withError middleware and validate the cron secret using hasCronSecret(request) or hasPostCronSecret(request).
Cron endpoints MUST capture unauthorized attempts with captureException and return a 401 status for unauthorized requests.
All parameters in API routes MUST be validated for type, format, and length before use.
Request bodies in API routes MUST be validated using Zod schemas before use.
All Prisma queries in API routes MUST only return necessary fields and never expose sensitive data.
Error messages in API routes MUST not leak internal information or sensitive data; use generic error messages and SafeError where appropriate.
API routes MUST use a consistent error response format, returning JSON with an error message and status code.
All findUnique and findFirst Prisma calls in API routes MUST include ownership filters (e.g., userId or emailAccountId).
All findMany Prisma calls in API routes MUST be scoped to the authenticated user's data.
Never use direct object references in API routes without ownership checks (prevent IDOR vulnerabilities).
Prevent mass assignment vulnerabilities by only allowing explicitly whitelisted fields in update operations in AP...

Files:

  • apps/web/app/api/google/calendar/callback/route.ts
  • apps/web/app/api/recall/webhook/route.ts
apps/web/app/api/**/*.{ts,js}

📄 CodeRabbit inference engine (.cursor/rules/security-audit.mdc)

apps/web/app/api/**/*.{ts,js}: All API route handlers in 'apps/web/app/api/' must use authentication middleware: withAuth, withEmailAccount, or withError (with custom authentication logic).
All Prisma queries in API routes must include user/account filtering (e.g., emailAccountId or userId in WHERE clauses) to prevent unauthorized data access.
All parameters used in API routes must be validated before use; do not use parameters from 'params' or request bodies directly in queries without validation.
Request bodies in API routes should use Zod schemas for validation.
API routes should only return necessary fields using Prisma's 'select' and must not include sensitive data in error messages.
Error messages in API routes must not reveal internal details; use generic errors and SafeError for user-facing errors.
All QStash endpoints (API routes called via publishToQstash or publishToQstashQueue) must use verifySignatureAppRouter to verify request authenticity.
All cron endpoints in API routes must use hasCronSecret or hasPostCronSecret for authentication.
Do not hardcode weak or plaintext secrets in API route files; secrets must not be directly assigned as string literals.
Review all new withError usage in API routes to ensure custom authentication is implemented where required.

Files:

  • apps/web/app/api/google/calendar/callback/route.ts
  • apps/web/app/api/recall/webhook/route.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{js,jsx,ts,tsx}: Don't use elements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...

Files:

  • apps/web/app/api/google/calendar/callback/route.ts
  • apps/web/utils/actions/recall-calendar.ts
  • apps/web/utils/recall/bot.ts
  • apps/web/app/api/recall/webhook/route.ts
apps/web/utils/actions/**/*.ts

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

apps/web/utils/actions/**/*.ts: Use server actions for all mutations (create/update/delete operations)
next-safe-action provides centralized error handling
Use Zod schemas for validation on both client and server
Use revalidatePath in server actions for cache invalidation

apps/web/utils/actions/**/*.ts: Use server actions (with next-safe-action) for all mutations (create/update/delete operations); do NOT use POST API routes for mutations.
Use revalidatePath in server actions to invalidate cache after mutations.

Files:

  • apps/web/utils/actions/recall-calendar.ts
apps/web/utils/actions/*.ts

📄 CodeRabbit inference engine (.cursor/rules/server-actions.mdc)

apps/web/utils/actions/*.ts: Implement all server actions using the next-safe-action library for type safety, input validation, context management, and error handling. Refer to apps/web/utils/actions/safe-action.ts for client definitions (actionClient, actionClientUser, adminActionClient).
Use actionClientUser when only authenticated user context (userId) is needed.
Use actionClient when both authenticated user context and a specific emailAccountId are needed. The emailAccountId must be bound when calling the action from the client.
Use adminActionClient for actions restricted to admin users.
Access necessary context (like userId, emailAccountId, etc.) provided by the safe action client via the ctx object in the .action() handler.
Server Actions are strictly for mutations (operations that change data, e.g., creating, updating, deleting). Do NOT use Server Actions for data fetching (GET operations). For data fetching, use dedicated GET API Routes combined with SWR Hooks.
Use SafeError for expected/handled errors within actions if needed. next-safe-action provides centralized error handling.
Use the .metadata({ name: "actionName" }) method to provide a meaningful name for monitoring. Sentry instrumentation is automatically applied via withServerActionInstrumentation within the safe action clients.
If an action modifies data displayed elsewhere, use revalidatePath or revalidateTag from next/cache within the action handler as needed.

Server action files must start with use server

Files:

  • apps/web/utils/actions/recall-calendar.ts
apps/web/utils/**

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

Create utility functions in utils/ folder for reusable logic

Files:

  • apps/web/utils/actions/recall-calendar.ts
  • apps/web/utils/recall/bot.ts
apps/web/utils/**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

apps/web/utils/**/*.ts: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size

Files:

  • apps/web/utils/actions/recall-calendar.ts
  • apps/web/utils/recall/bot.ts
🧠 Learnings (4)
📚 Learning: 2025-07-18T17:27:58.249Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-07-18T17:27:58.249Z
Learning: Applies to apps/web/utils/actions/*.ts : Use `SafeError` for expected/handled errors within actions if needed. `next-safe-action` provides centralized error handling.

Applied to files:

  • apps/web/utils/actions/recall-calendar.ts
📚 Learning: 2025-07-20T09:00:41.968Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-07-20T09:00:41.968Z
Learning: Applies to apps/web/app/api/**/*.{ts,js} : Request bodies in API routes should use Zod schemas for validation.

Applied to files:

  • apps/web/app/api/recall/webhook/route.ts
📚 Learning: 2025-07-18T17:27:46.389Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-07-18T17:27:46.389Z
Learning: Applies to **/api/**/route.ts : Request bodies in API routes MUST be validated using Zod schemas before use.

Applied to files:

  • apps/web/app/api/recall/webhook/route.ts
📚 Learning: 2025-07-20T09:00:41.968Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-07-20T09:00:41.968Z
Learning: Applies to apps/web/app/api/**/*.{ts,js} : API routes should only return necessary fields using Prisma's 'select' and must not include sensitive data in error messages.

Applied to files:

  • apps/web/app/api/recall/webhook/route.ts
🧬 Code graph analysis (4)
apps/web/app/api/google/calendar/callback/route.ts (3)
apps/web/app/api/outlook/webhook/logger.ts (1)
  • logger (3-3)
apps/web/utils/logger.ts (1)
  • createScopedLogger (17-72)
apps/web/utils/recall/calendar.ts (1)
  • createRecallCalendar (34-73)
apps/web/utils/actions/recall-calendar.ts (4)
apps/web/utils/logger.ts (1)
  • createScopedLogger (17-72)
apps/web/utils/actions/safe-action.ts (1)
  • actionClient (62-113)
apps/web/utils/error.ts (1)
  • SafeError (86-96)
apps/web/utils/recall/calendar.ts (2)
  • createRecallCalendar (34-73)
  • deleteRecallCalendar (109-127)
apps/web/utils/recall/bot.ts (3)
apps/web/utils/logger.ts (1)
  • createScopedLogger (17-72)
apps/web/app/api/recall/webhook/types.ts (2)
  • CalendarEvent (99-106)
  • RecallCalendarEventResponse (118-121)
apps/web/utils/recall/request.ts (1)
  • recallRequest (10-57)
apps/web/app/api/recall/webhook/route.ts (8)
apps/web/utils/logger.ts (1)
  • createScopedLogger (17-72)
apps/web/utils/middleware.ts (1)
  • withError (283-288)
apps/web/env.ts (1)
  • env (16-245)
apps/web/utils/recall/verify-svix-webhook.ts (1)
  • verifySvixWebhook (16-63)
apps/web/app/api/recall/webhook/types.ts (5)
  • RecallWebhookPayload (39-42)
  • CalendarSyncEventsEvent (8-14)
  • CalendarUpdateEvent (1-6)
  • TranscriptDoneEvent (16-37)
  • CalendarEvent (99-106)
apps/web/utils/recall/calendar.ts (3)
  • fetchCalendarEvents (129-170)
  • getRecallCalendar (78-104)
  • deleteRecallCalendar (109-127)
apps/web/utils/recall/bot.ts (1)
  • rescheduleBotsForUpdatedEvents (129-174)
apps/web/utils/recall/transcript.ts (2)
  • getTranscriptMetadata (10-24)
  • fetchTranscriptContent (26-52)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Vercel Agent Review
🔇 Additional comments (3)
apps/web/utils/recall/bot.ts (3)

1-9: LGTM! Logger properly initialized.

The logger initialization follows coding guidelines by using createScopedLogger at the top of the file with an appropriate scope.


15-42: LGTM! Bot configuration properly structured.

The function correctly constructs the Recall API request with bot configuration and recording settings.


44-50: LGTM!

@edulelis edulelis requested a review from elie222 September 30, 2025 22:43
@edulelis edulelis changed the title WIP: Recall integration Recall integration Sep 30, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
apps/web/prisma/schema.prisma (1)

1048-1065: Consider adding index on emailAccountId for foreign key performance.

While the composite index @@index([emailAccountId, eventId, status]) exists, consider adding a standalone index on emailAccountId to optimize the foreign key relation queries from EmailAccount to Meeting.

Apply this diff to add the index:

   @@index([emailAccountId, eventId, status])
+  @@index([emailAccountId])
   @@unique([deduplicationKey])
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d8c5172 and c902a82.

📒 Files selected for processing (1)
  • apps/web/prisma/schema.prisma (7 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
!{.cursor/rules/*.mdc}

📄 CodeRabbit inference engine (.cursor/rules/cursor-rules.mdc)

Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location

Files:

  • apps/web/prisma/schema.prisma
apps/web/prisma/schema.prisma

📄 CodeRabbit inference engine (.cursor/rules/prisma.mdc)

The Prisma schema file must be located at apps/web/prisma/schema.prisma

Files:

  • apps/web/prisma/schema.prisma
!pages/_document.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.

Files:

  • apps/web/prisma/schema.prisma
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Static Code Analysis Js
  • GitHub Check: Vercel Agent Review
  • GitHub Check: Jit Security
🔇 Additional comments (8)
apps/web/prisma/schema.prisma (8)

100-103: LGTM! Organization relationships correctly moved to User model.

The refactoring to move organization relationships (members, invitations, ssoproviders) from EmailAccount to User is architecturally sound, as organizations logically relate to users rather than email accounts.


162-162: LGTM! Meetings relation added for Recall integration.

The addition of the meetings relation to EmailAccount is appropriate for tracking Recall bot meetings associated with each email account.


184-191: LGTM! Member model correctly refactored to reference User.

The refactoring from emailAccountId to userId is consistent with the architectural change to move organization relationships to the User model. The unique constraint and relation have been properly updated.


203-203: LGTM! Invitation inviter relation correctly updated.

The inviter relation has been properly updated to reference User instead of EmailAccount, consistent with the organization relationships migration.


224-226: LGTM! SsoProvider correctly refactored to relate to User.

The refactoring to use userId and User relation instead of emailAccountId is consistent with the organization relationships migration. The nullable fields are appropriate given the optional nature of the relationship.


849-849: LGTM! Recall calendar linkage field added appropriately.

The recallCalendarId field with unique constraint is correctly added to link CalendarConnection to Recall calendars, with appropriate nullability for connections that don't use Recall.


1040-1046: LGTM! RecallBotStatus enum appropriately defined.

The enum values (SCHEDULED, ACTIVE, COMPLETED, CANCELLED, FAILED) comprehensively cover the bot lifecycle states needed for tracking Recall bot status.


1048-1065: Model name and code references are consistent. All prisma.recallMeeting references have been removed and the code uniformly uses prisma.meeting.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
apps/web/prisma/schema.prisma (1)

161-162: Model name mismatch: relation points to Meeting[], but code likely uses prisma.recallMeeting

This will generate prisma.meeting, not prisma.recallMeeting. Align schema and client usage to prevent runtime/TS errors.

Apply this diff:

-  meetings     Meeting[]
+  meetings     RecallMeeting[]

Run to verify client usage across repo:

#!/bin/bash
rg -nP -C2 '\bprisma\.(recallMeeting|meeting)\b' --type=ts --type=tsx --type=js
🧹 Nitpick comments (1)
apps/web/prisma/schema.prisma (1)

906-927: Rename model to RecallMeeting and map to existing table

-// Meeting-related models for recall integration
-model Meeting {
+// Meeting-related models for recall integration
+model RecallMeeting {
   id        String   @id @default(cuid())
   createdAt DateTime @default(now())
   updatedAt DateTime @updatedAt

   emailAccountId String
   eventId        String
   botId          String
   deduplicationKey String
   status         RecallBotStatus @default(SCHEDULED)
   transcript     String?
   botWillJoinAt  DateTime
   meetingUrl     String

   emailAccount EmailAccount @relation(fields: [emailAccountId], references: [id], onDelete: Cascade)

   @@unique([botId])
   @@unique([deduplicationKey])
-  @@index([emailAccountId, eventId, status])
+  @@index([emailAccountId, eventId, status])
+  @@map("Meeting")
 }

Optionally add:

-  @@index([emailAccountId, eventId, status])
+  @@index([emailAccountId, eventId, status])
+  @@index([status, botWillJoinAt])

After updating, run in apps/web:

pnpm prisma generate && pnpm exec tsc --noEmit --skipLibCheck

to confirm no errors.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c902a82 and 11586ad.

📒 Files selected for processing (1)
  • apps/web/prisma/schema.prisma (5 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
!{.cursor/rules/*.mdc}

📄 CodeRabbit inference engine (.cursor/rules/cursor-rules.mdc)

Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location

Files:

  • apps/web/prisma/schema.prisma
apps/web/prisma/schema.prisma

📄 CodeRabbit inference engine (.cursor/rules/prisma.mdc)

The Prisma schema file must be located at apps/web/prisma/schema.prisma

Files:

  • apps/web/prisma/schema.prisma
!pages/_document.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.

Files:

  • apps/web/prisma/schema.prisma
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Static Code Analysis Js
  • GitHub Check: Jit Security
🔇 Additional comments (2)
apps/web/prisma/schema.prisma (2)

850-851: LGTM: unique recallCalendarId

Unique nullable field is fine on Postgres (multiple NULLs allowed). Ensures a single mapping per Recall calendar.

Confirm intended cardinality: one Recall calendar per CalendarConnection globally. If it should be per emailAccount only, switch to a composite unique on [emailAccountId, recallCalendarId].


1063-1069: LGTM: RecallBotStatus enum

Enum values cover lifecycle states used by the Recall bot.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (6)
apps/web/app/api/recall/webhook/route.ts (4)

92-93: Add Zod validation for webhook payload.

Per coding guidelines, all API request bodies must be validated using Zod schemas. The current implementation parses the JSON and trusts its shape, allowing malformed or malicious payloads through.

As per coding guidelines

Define validation schemas after the imports:

import { z } from "zod";

const CalendarUpdateSchema = z.object({
  event: z.literal("calendar.update"),
  data: z.object({ 
    calendar_id: z.string().min(1) 
  }),
});

const CalendarSyncEventsSchema = z.object({
  event: z.literal("calendar.sync_events"),
  data: z.object({
    calendar_id: z.string().min(1),
    last_updated_ts: z.string().min(1),
  }),
});

const TranscriptDoneSchema = z.object({
  event: z.literal("transcript.done"),
  data: z.object({
    bot: z.object({ id: z.string().min(1) }),
    transcript: z.object({ id: z.string().min(1) }),
  }),
});

const RecallWebhookSchema = z.discriminatedUnion("event", [
  CalendarUpdateSchema,
  CalendarSyncEventsSchema,
  TranscriptDoneSchema,
]);

Then validate before processing:

-  const body = JSON.parse(rawBody) as RecallWebhookPayload;
-  const eventType = body.event;
+  const parsedJson = JSON.parse(rawBody);
+  const parsed = RecallWebhookSchema.safeParse(parsedJson);
+  
+  if (!parsed.success) {
+    logger.error("Invalid webhook payload", {
+      issues: parsed.error.flatten(),
+    });
+    return NextResponse.json(
+      { message: "Invalid payload" },
+      { status: 400 }
+    );
+  }
+  
+  const body = parsed.data;
+  const eventType = body.event;

95-137: Propagate unsupported event responses.

The processRecallWebhook function returns a 400 response for unsupported events (line 133), but the POST handler ignores this return value and always returns 200 OK (line 98). This means unsupported event types will never surface the intended error response.

Modify processRecallWebhook to return NextResponse | void and update the handler:

-async function processRecallWebhook(payload: RecallWebhookPayload) {
+async function processRecallWebhook(
+  payload: RecallWebhookPayload,
+): Promise<NextResponse | void> {
   const eventType = payload.event;

   switch (eventType) {
     // ... cases ...
     default:
       logger.warn("Unsupported event type", { eventType });
       return NextResponse.json(
         { message: "Unsupported event type" },
         { status: 400 },
       );
   }
 }

Update the handler to propagate the response:

   try {
-    await processRecallWebhook(body);
-
-    return NextResponse.json({
-      message: "Webhook processed successfully",
-    });
+    const response = await processRecallWebhook(body);
+    if (response) return response;
+    
+    return NextResponse.json({
+      message: "Webhook processed successfully",
+    });
   } catch (error) {

187-195: Restrict Prisma query to required fields.

The query uses include to fetch the entire EmailAccount object, which contains sensitive fields like API keys, tokens, and secrets. Per coding guidelines, only select the fields actually used to prevent exposing sensitive data.

As per coding guidelines

Apply this diff:

   const connection = await prisma.calendarConnection.findUnique({
     where: { recallCalendarId: calendarId },
-    include: {
-      emailAccount: true,
-      calendars: {
-        where: { isEnabled: true },
-      },
-    },
+    select: {
+      id: true,
+      emailAccountId: true,
+      calendars: {
+        where: { isEnabled: true },
+        select: { id: true, calendarId: true },
+      },
+    },
   });

309-317: Restrict Prisma query to required fields.

Same issue as line 187—the query includes the entire EmailAccount object with sensitive fields.

As per coding guidelines

Apply this diff:

   const connection = await prisma.calendarConnection.findUnique({
     where: { recallCalendarId: calendarId },
-    include: {
-      emailAccount: true,
-      calendars: {
-        where: { isEnabled: true },
-      },
-    },
+    select: {
+      id: true,
+      emailAccountId: true,
+      calendars: {
+        where: { isEnabled: true },
+        select: { id: true, calendarId: true },
+      },
+    },
   });
apps/web/prisma/schema.prisma (1)

248-250: Revert DigestItem.updatedAt to automatic update behavior.

Changing updatedAt from @updatedAt to @default(now()) prevents the timestamp from being automatically refreshed on updates, which will break any queries that rely on freshness or ordering by update time. This was flagged in a past review and appears to be unintentional.

Apply this diff:

 model DigestItem {
   id          String          @id @default(cuid())
   createdAt   DateTime        @default(now())
-  updatedAt   DateTime        @default(now())
+  updatedAt   DateTime        @updatedAt
apps/web/utils/recall/bot.ts (1)

154-167: Potential state inconsistency when bot removal fails.

If the Recall API call to remove the bot fails (line 155), the code logs a warning but still marks the meeting as CANCELLED in the database (line 164-167). This creates an inconsistent state where the database says the bot is cancelled, but it may still be active in Recall's system.

Consider propagating the error or checking the removal result before updating the status:

       if (existingMeeting) {
+        let botRemoved = false;
         try {
           await removeBotFromCalendarEvent(event.id);
+          botRemoved = true;
         } catch (error) {
           logger.warn("Failed to remove existing bot via Recall API", {
             botId: existingMeeting.botId,
             eventId: event.id,
             error: error instanceof Error ? error.message : error,
           });
         }

-        await prisma.meeting.update({
-          where: { id: existingMeeting.id },
-          data: { status: "CANCELLED" },
-        });
+        if (botRemoved) {
+          await prisma.meeting.update({
+            where: { id: existingMeeting.id },
+            data: { status: "CANCELLED" },
+          });
+        } else {
+          logger.error("Cannot cancel meeting - bot removal failed", {
+            meetingId: existingMeeting.id,
+            eventId: event.id,
+          });
+          continue;
+        }
       }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 11586ad and c115c60.

📒 Files selected for processing (7)
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx (1 hunks)
  • apps/web/app/api/recall/webhook/route.ts (1 hunks)
  • apps/web/prisma/migrations/20250930170850_add_recall_meeting_support/migration.sql (1 hunks)
  • apps/web/prisma/schema.prisma (5 hunks)
  • apps/web/utils/actions/calendar.ts (2 hunks)
  • apps/web/utils/recall/bot.ts (1 hunks)
  • apps/web/utils/recall/transcript.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/utils/recall/transcript.ts
🧰 Additional context used
📓 Path-based instructions (23)
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use @/ for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX

Files:

  • apps/web/utils/actions/calendar.ts
  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
  • apps/web/utils/recall/bot.ts
apps/web/utils/actions/**/*.ts

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

apps/web/utils/actions/**/*.ts: Use server actions for all mutations (create/update/delete operations)
next-safe-action provides centralized error handling
Use Zod schemas for validation on both client and server
Use revalidatePath in server actions for cache invalidation

apps/web/utils/actions/**/*.ts: Use server actions (with next-safe-action) for all mutations (create/update/delete operations); do NOT use POST API routes for mutations.
Use revalidatePath in server actions to invalidate cache after mutations.

Files:

  • apps/web/utils/actions/calendar.ts
!{.cursor/rules/*.mdc}

📄 CodeRabbit inference engine (.cursor/rules/cursor-rules.mdc)

Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location

Files:

  • apps/web/utils/actions/calendar.ts
  • apps/web/prisma/schema.prisma
  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
  • apps/web/prisma/migrations/20250930170850_add_recall_meeting_support/migration.sql
  • apps/web/utils/recall/bot.ts
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)

**/*.ts: The same validation should be done in the server action too
Define validation schemas using Zod

Files:

  • apps/web/utils/actions/calendar.ts
  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/utils/recall/bot.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)

**/*.{ts,tsx}: Use createScopedLogger for logging in backend TypeScript files
Typically add the logger initialization at the top of the file when using createScopedLogger
Only use .with() on a logger instance within a specific function, not for a global logger

Import Prisma in the project using import prisma from "@/utils/prisma";

**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.

Files:

  • apps/web/utils/actions/calendar.ts
  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
  • apps/web/utils/recall/bot.ts
apps/web/utils/actions/*.ts

📄 CodeRabbit inference engine (.cursor/rules/server-actions.mdc)

apps/web/utils/actions/*.ts: Implement all server actions using the next-safe-action library for type safety, input validation, context management, and error handling. Refer to apps/web/utils/actions/safe-action.ts for client definitions (actionClient, actionClientUser, adminActionClient).
Use actionClientUser when only authenticated user context (userId) is needed.
Use actionClient when both authenticated user context and a specific emailAccountId are needed. The emailAccountId must be bound when calling the action from the client.
Use adminActionClient for actions restricted to admin users.
Access necessary context (like userId, emailAccountId, etc.) provided by the safe action client via the ctx object in the .action() handler.
Server Actions are strictly for mutations (operations that change data, e.g., creating, updating, deleting). Do NOT use Server Actions for data fetching (GET operations). For data fetching, use dedicated GET API Routes combined with SWR Hooks.
Use SafeError for expected/handled errors within actions if needed. next-safe-action provides centralized error handling.
Use the .metadata({ name: "actionName" }) method to provide a meaningful name for monitoring. Sentry instrumentation is automatically applied via withServerActionInstrumentation within the safe action clients.
If an action modifies data displayed elsewhere, use revalidatePath or revalidateTag from next/cache within the action handler as needed.

Server action files must start with use server

Files:

  • apps/web/utils/actions/calendar.ts
apps/web/utils/**

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

Create utility functions in utils/ folder for reusable logic

Files:

  • apps/web/utils/actions/calendar.ts
  • apps/web/utils/recall/bot.ts
apps/web/utils/**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

apps/web/utils/**/*.ts: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size

Files:

  • apps/web/utils/actions/calendar.ts
  • apps/web/utils/recall/bot.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{js,jsx,ts,tsx}: Don't use elements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...

Files:

  • apps/web/utils/actions/calendar.ts
  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
  • apps/web/utils/recall/bot.ts
!pages/_document.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.

Files:

  • apps/web/utils/actions/calendar.ts
  • apps/web/prisma/schema.prisma
  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
  • apps/web/prisma/migrations/20250930170850_add_recall_meeting_support/migration.sql
  • apps/web/utils/recall/bot.ts
apps/web/prisma/schema.prisma

📄 CodeRabbit inference engine (.cursor/rules/prisma.mdc)

The Prisma schema file must be located at apps/web/prisma/schema.prisma

Files:

  • apps/web/prisma/schema.prisma
apps/web/app/**

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

NextJS app router structure with (app) directory

Files:

  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
apps/web/app/api/**/route.ts

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

apps/web/app/api/**/route.ts: Use withAuth for user-level operations
Use withEmailAccount for email-account-level operations
Do NOT use POST API routes for mutations - use server actions instead
No need for try/catch in GET routes when using middleware
Export response types from GET routes

apps/web/app/api/**/route.ts: Wrap all GET API route handlers with withAuth or withEmailAccount middleware for authentication and authorization.
Export response types from GET API routes for type-safe client usage.
Do not use try/catch in GET API routes when using authentication middleware; rely on centralized error handling.

Files:

  • apps/web/app/api/recall/webhook/route.ts
**/api/**/route.ts

📄 CodeRabbit inference engine (.cursor/rules/security.mdc)

**/api/**/route.ts: ALL API routes that handle user data MUST use appropriate authentication and authorization middleware (withAuth or withEmailAccount).
ALL database queries in API routes MUST be scoped to the authenticated user/account (e.g., include userId or emailAccountId in query filters).
Always validate that resources belong to the authenticated user before performing operations (resource ownership validation).
Use withEmailAccount middleware for API routes that operate on a specific email account (i.e., use or require emailAccountId).
Use withAuth middleware for API routes that operate at the user level (i.e., use or require only userId).
Use withError middleware (with proper validation) for public endpoints, custom authentication, or cron endpoints.
Cron endpoints MUST use withError middleware and validate the cron secret using hasCronSecret(request) or hasPostCronSecret(request).
Cron endpoints MUST capture unauthorized attempts with captureException and return a 401 status for unauthorized requests.
All parameters in API routes MUST be validated for type, format, and length before use.
Request bodies in API routes MUST be validated using Zod schemas before use.
All Prisma queries in API routes MUST only return necessary fields and never expose sensitive data.
Error messages in API routes MUST not leak internal information or sensitive data; use generic error messages and SafeError where appropriate.
API routes MUST use a consistent error response format, returning JSON with an error message and status code.
All findUnique and findFirst Prisma calls in API routes MUST include ownership filters (e.g., userId or emailAccountId).
All findMany Prisma calls in API routes MUST be scoped to the authenticated user's data.
Never use direct object references in API routes without ownership checks (prevent IDOR vulnerabilities).
Prevent mass assignment vulnerabilities by only allowing explicitly whitelisted fields in update operations in AP...

Files:

  • apps/web/app/api/recall/webhook/route.ts
apps/web/app/api/**/*.{ts,js}

📄 CodeRabbit inference engine (.cursor/rules/security-audit.mdc)

apps/web/app/api/**/*.{ts,js}: All API route handlers in 'apps/web/app/api/' must use authentication middleware: withAuth, withEmailAccount, or withError (with custom authentication logic).
All Prisma queries in API routes must include user/account filtering (e.g., emailAccountId or userId in WHERE clauses) to prevent unauthorized data access.
All parameters used in API routes must be validated before use; do not use parameters from 'params' or request bodies directly in queries without validation.
Request bodies in API routes should use Zod schemas for validation.
API routes should only return necessary fields using Prisma's 'select' and must not include sensitive data in error messages.
Error messages in API routes must not reveal internal details; use generic errors and SafeError for user-facing errors.
All QStash endpoints (API routes called via publishToQstash or publishToQstashQueue) must use verifySignatureAppRouter to verify request authenticity.
All cron endpoints in API routes must use hasCronSecret or hasPostCronSecret for authentication.
Do not hardcode weak or plaintext secrets in API route files; secrets must not be directly assigned as string literals.
Review all new withError usage in API routes to ensure custom authentication is implemented where required.

Files:

  • apps/web/app/api/recall/webhook/route.ts
apps/web/**/*.tsx

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

apps/web/**/*.tsx: Follow tailwindcss patterns with prettier-plugin-tailwindcss
Prefer functional components with hooks
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach
Follow consistent naming conventions (PascalCase for components)
Use LoadingContent component for async data
Use result?.serverError with toastError and toastSuccess
Use LoadingContent component to handle loading and error states consistently
Pass loading, error, and children props to LoadingContent

Files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)

**/*.tsx: Use React Hook Form with Zod for validation
Validate form inputs before submission
Show validation errors inline next to form fields

Files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
apps/web/app/(app)/*/**

📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)

Components for the page are either put in page.tsx, or in the apps/web/app/(app)/PAGE_NAME folder

Files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
apps/web/app/(app)/*/**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)

If you need to use onClick in a component, that component is a client component and file must start with 'use client'

Files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
apps/web/app/(app)/*/**/**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)

If we're in a deeply nested component we will use swr to fetch via API

Files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
apps/web/app/**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

Components with onClick must be client components with use client directive

Files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{jsx,tsx}: Don't destructure props inside JSX components in Solid projects.
Don't use both children and dangerouslySetInnerHTML props on the same element.
Don't use Array index in keys.
Don't assign to React component props.
Don't define React components inside other components.
Don't use event handlers on non-interactive elements.
Don't assign JSX properties multiple times.
Don't add extra closing tags for components without children.
Use <>...</> instead of ....
Don't insert comments as text nodes.
Don't use the return value of React.render.
Make sure all dependencies are correctly specified in React hooks.
Make sure all React hooks are called from the top level of component functions.
Don't use unnecessary fragments.
Don't pass children as props.
Use semantic elements instead of role attributes in JSX.

Files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
**/*.{html,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{html,jsx,tsx}: Don't use or elements.
Don't use accessKey attribute on any HTML element.
Don't set aria-hidden="true" on focusable elements.
Don't add ARIA roles, states, and properties to elements that don't support them.
Only use the scope prop on elements.
Don't assign non-interactive ARIA roles to interactive HTML elements.
Make sure label elements have text content and are associated with an input.
Don't assign interactive ARIA roles to non-interactive HTML elements.
Don't assign tabIndex to non-interactive HTML elements.
Don't use positive integers for tabIndex property.
Don't include "image", "picture", or "photo" in img alt prop.
Don't use explicit role property that's the same as the implicit/default role.
Make static elements with click handlers use a valid role attribute.
Always include a title element for SVG elements.
Give all elements requiring alt text meaningful information for screen readers.
Make sure anchors have content that's accessible to screen readers.
Assign tabIndex to non-interactive HTML elements with aria-activedescendant.
Include all required ARIA attributes for elements with ARIA roles.
Make sure ARIA properties are valid for the element's supported roles.
Always include a type attribute for button elements.
Make elements with interactive roles and handlers focusable.
Give heading elements content that's accessible to screen readers (not hidden with aria-hidden).
Always include a lang attribute on the html element.
Always include a title attribute for iframe elements.
Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress.
Accompany onMouseOver/onMouseOut with onFocus/onBlur.
Include caption tracks for audio and video elements.
Make sure all anchors are valid and navigable.
Ensure all ARIA properties (aria-*) are valid.
Use valid, non-abstract ARIA roles for elements with ARIA roles.
Use valid ARIA state and property values.
Use valid values for the autocomplete attribute on input eleme...

Files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
🧠 Learnings (3)
📚 Learning: 2025-07-20T09:00:41.968Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-07-20T09:00:41.968Z
Learning: Applies to apps/web/app/api/**/*.{ts,js} : Request bodies in API routes should use Zod schemas for validation.

Applied to files:

  • apps/web/app/api/recall/webhook/route.ts
📚 Learning: 2025-07-18T17:27:46.389Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-07-18T17:27:46.389Z
Learning: Applies to **/api/**/route.ts : Request bodies in API routes MUST be validated using Zod schemas before use.

Applied to files:

  • apps/web/app/api/recall/webhook/route.ts
📚 Learning: 2025-07-20T09:00:41.968Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-07-20T09:00:41.968Z
Learning: Applies to apps/web/app/api/**/*.{ts,js} : API routes should only return necessary fields using Prisma's 'select' and must not include sensitive data in error messages.

Applied to files:

  • apps/web/app/api/recall/webhook/route.ts
🧬 Code graph analysis (3)
apps/web/utils/actions/calendar.ts (3)
apps/web/utils/actions/safe-action.ts (1)
  • actionClient (62-113)
apps/web/utils/actions/calendar.validation.ts (1)
  • disconnectCalendarBody (3-5)
apps/web/utils/recall/calendar.ts (1)
  • deleteRecallCalendar (109-127)
apps/web/app/api/recall/webhook/route.ts (8)
apps/web/utils/logger.ts (1)
  • createScopedLogger (17-72)
apps/web/utils/middleware.ts (1)
  • withError (283-288)
apps/web/env.ts (1)
  • env (16-245)
apps/web/utils/recall/verify-svix-webhook.ts (1)
  • verifySvixWebhook (16-63)
apps/web/app/api/recall/webhook/types.ts (5)
  • RecallWebhookPayload (39-42)
  • CalendarSyncEventsEvent (8-14)
  • CalendarUpdateEvent (1-6)
  • TranscriptDoneEvent (16-37)
  • CalendarEvent (99-106)
apps/web/utils/recall/calendar.ts (3)
  • fetchCalendarEvents (129-170)
  • getRecallCalendar (78-104)
  • deleteRecallCalendar (109-127)
apps/web/utils/recall/bot.ts (1)
  • rescheduleBotsForUpdatedEvents (138-183)
apps/web/utils/recall/transcript.ts (2)
  • getTranscriptMetadata (10-24)
  • fetchTranscriptContent (26-53)
apps/web/utils/recall/bot.ts (3)
apps/web/utils/logger.ts (1)
  • createScopedLogger (17-72)
apps/web/app/api/recall/webhook/types.ts (3)
  • CalendarEvent (99-106)
  • RecallBot (108-116)
  • RecallCalendarEventResponse (118-121)
apps/web/utils/recall/request.ts (1)
  • recallRequest (10-57)
🔇 Additional comments (5)
apps/web/prisma/migrations/20250930170850_add_recall_meeting_support/migration.sql (1)

1-38: LGTM!

The migration is well-structured with proper constraints, indexes, and foreign key relationships. All previously flagged issues have been addressed.

apps/web/prisma/schema.prisma (2)

906-925: LGTM!

The Meeting model is well-designed with appropriate fields, constraints, and indexes. The unique constraints on botId and deduplicationKey will prevent duplicates, and the composite index on (emailAccountId, eventId, status) will optimize queries.


1062-1068: LGTM!

The RecallBotStatus enum clearly defines all possible bot states, supporting the bot lifecycle management.

apps/web/utils/recall/bot.ts (2)

13-15: LGTM!

The deduplication key generation is straightforward. Since scheduleBotForEvent validates meeting_url before calling this function (line 48), there's no risk of generating keys with "undefined" strings.


43-136: LGTM!

The bot scheduling logic is well-structured with proper validation, error handling, and database persistence. The upsert pattern ensures idempotency, and the logging provides good observability.

@elie222
Copy link
Owner

elie222 commented Oct 2, 2025

Fix conflict

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is being reviewed by Cursor Bugbot

Details

You are on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (3)
apps/web/app/api/recall/webhook/route.ts (2)

280-288: Restrict Prisma selection to minimum required fields.

The query includes full related objects. emailAccount contains sensitive data like tokens and secrets that shouldn't be loaded unnecessarily. Only select the fields actually used.

Apply this diff:

 const connection = await prisma.calendarConnection.findUnique({
   where: { recallCalendarId: calendarId },
-  include: {
-    emailAccount: true,
-    calendars: {
-      where: { isEnabled: true },
-    },
-  },
+  select: {
+    id: true,
+    emailAccountId: true,
+    calendars: {
+      where: { isEnabled: true },
+      select: { id: true },
+    },
+  },
 });

As per coding guidelines


402-410: Restrict Prisma selection to minimum required fields.

Same issue as in handleCalendarSyncEvents—avoid including entire related objects.

Apply this diff:

 const connection = await prisma.calendarConnection.findUnique({
   where: { recallCalendarId: calendarId },
-  include: {
-    emailAccount: true,
-    calendars: {
-      where: { isEnabled: true },
-    },
-  },
+  select: {
+    id: true,
+    emailAccountId: true,
+    calendars: {
+      where: { isEnabled: true },
+      select: { id: true },
+    },
+  },
 });

As per coding guidelines

apps/web/utils/recall/bot.ts (1)

13-15: Handle undefined meeting_url in deduplication key.

When event.meeting_url is undefined, the key becomes "timestamp-undefined", potentially causing collisions between different events without meeting URLs.

Apply this diff to use event ID as fallback:

 export function generateDeduplicationKey(event: CalendarEvent): string {
-  return `${event.start_time}-${event.meeting_url}`;
+  return `${event.start_time}-${event.meeting_url || event.id}`;
 }
🧹 Nitpick comments (2)
apps/web/utils/actions/transcript-settings.ts (1)

36-50: Consider optimizing the update pattern.

The current implementation loops through connections, counts calendars, and performs individual updateMany calls. This could be more efficient:

  1. The initial query includes calendars only for counting, which adds overhead
  2. Multiple database round-trips (one updateMany per connection)

Consider this more efficient approach:

-      let totalCalendars = 0;
-      for (const connection of connections) {
-        if (connection.calendars.length > 0) {
-          await prisma.calendar.updateMany({
-            where: {
-              connectionId: connection.id,
-              isEnabled: true,
-            },
-            data: {
-              transcriptEnabled,
-            },
-          });
-          totalCalendars += connection.calendars.length;
-        }
-      }
+      // Single update for all enabled calendars across all connections
+      const updateResult = await prisma.calendar.updateMany({
+        where: {
+          connectionId: { in: connections.map(c => c.id) },
+          isEnabled: true,
+        },
+        data: {
+          transcriptEnabled,
+        },
+      });
+      const totalCalendars = updateResult.count;

This reduces the query to a single database round-trip and simplifies the logic.

apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx (1)

21-44: Code duplication with ConnectCalendarButton.

The handleConnectCalendar function duplicates the exact same logic found in ConnectCalendarButton.tsx (lines 12-38). This creates maintenance burden and increases the risk of inconsistencies.

Consider extracting the connection logic into a reusable hook:

// hooks/useConnectCalendar.ts
export function useConnectCalendar() {
  const { emailAccountId } = useAccount();
  const [isConnecting, setIsConnecting] = useState(false);

  const connect = async () => {
    setIsConnecting(true);
    try {
      const response = await fetchWithAccount({
        url: "/api/google/calendar/auth-url",
        emailAccountId,
        init: { headers: { "Content-Type": "application/json" } },
      });

      if (!response.ok) {
        throw new Error("Failed to initiate calendar connection");
      }

      const data: GetCalendarAuthUrlResponse = await response.json();
      window.location.href = data.url;
    } catch (error) {
      toastError({
        title: "Error initiating calendar connection",
        description: "Please try again or contact support",
      });
      setIsConnecting(false);
    }
  };

  return { connect, isConnecting };
}

Then use it in both components:

const { connect, isConnecting } = useConnectCalendar();
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 29d2939 and 86b196f.

📒 Files selected for processing (16)
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx (4 hunks)
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx (2 hunks)
  • apps/web/app/(app)/[emailAccountId]/calendars/page.tsx (1 hunks)
  • apps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsx (1 hunks)
  • apps/web/app/(app)/[emailAccountId]/transcript-settings/page.tsx (1 hunks)
  • apps/web/app/api/google/calendar/callback/route.ts (2 hunks)
  • apps/web/app/api/recall/webhook/route.ts (1 hunks)
  • apps/web/app/api/recall/webhook/types.ts (1 hunks)
  • apps/web/app/api/user/calendars/route.ts (2 hunks)
  • apps/web/prisma/migrations/20250930170850_add_recall_meeting_support/migration.sql (1 hunks)
  • apps/web/prisma/migrations/20251003004957_meeting_status/migration.sql (1 hunks)
  • apps/web/prisma/schema.prisma (5 hunks)
  • apps/web/utils/actions/transcript-settings.ts (1 hunks)
  • apps/web/utils/recall/bot.ts (1 hunks)
  • apps/web/utils/recall/transcript.ts (1 hunks)
  • apps/web/utils/scheduled-actions/scheduler.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/app/api/google/calendar/callback/route.ts
🧰 Additional context used
📓 Path-based instructions (23)
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use @/ for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX

Files:

  • apps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/page.tsx
  • apps/web/app/(app)/[emailAccountId]/transcript-settings/page.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
  • apps/web/utils/actions/transcript-settings.ts
  • apps/web/app/api/recall/webhook/types.ts
  • apps/web/utils/scheduled-actions/scheduler.ts
  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/utils/recall/transcript.ts
  • apps/web/app/api/user/calendars/route.ts
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
  • apps/web/utils/recall/bot.ts
apps/web/app/**

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

NextJS app router structure with (app) directory

Files:

  • apps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/page.tsx
  • apps/web/app/(app)/[emailAccountId]/transcript-settings/page.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
  • apps/web/app/api/recall/webhook/types.ts
  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/app/api/user/calendars/route.ts
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
apps/web/**/*.tsx

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

apps/web/**/*.tsx: Follow tailwindcss patterns with prettier-plugin-tailwindcss
Prefer functional components with hooks
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach
Follow consistent naming conventions (PascalCase for components)
Use LoadingContent component for async data
Use result?.serverError with toastError and toastSuccess
Use LoadingContent component to handle loading and error states consistently
Pass loading, error, and children props to LoadingContent

Files:

  • apps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/page.tsx
  • apps/web/app/(app)/[emailAccountId]/transcript-settings/page.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
!{.cursor/rules/*.mdc}

📄 CodeRabbit inference engine (.cursor/rules/cursor-rules.mdc)

Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location

Files:

  • apps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/page.tsx
  • apps/web/app/(app)/[emailAccountId]/transcript-settings/page.tsx
  • apps/web/prisma/schema.prisma
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
  • apps/web/utils/actions/transcript-settings.ts
  • apps/web/prisma/migrations/20251003004957_meeting_status/migration.sql
  • apps/web/app/api/recall/webhook/types.ts
  • apps/web/utils/scheduled-actions/scheduler.ts
  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/utils/recall/transcript.ts
  • apps/web/app/api/user/calendars/route.ts
  • apps/web/prisma/migrations/20250930170850_add_recall_meeting_support/migration.sql
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
  • apps/web/utils/recall/bot.ts
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)

**/*.tsx: Use React Hook Form with Zod for validation
Validate form inputs before submission
Show validation errors inline next to form fields

Files:

  • apps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/page.tsx
  • apps/web/app/(app)/[emailAccountId]/transcript-settings/page.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)

**/*.{ts,tsx}: Use createScopedLogger for logging in backend TypeScript files
Typically add the logger initialization at the top of the file when using createScopedLogger
Only use .with() on a logger instance within a specific function, not for a global logger

Import Prisma in the project using import prisma from "@/utils/prisma";

**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.

Files:

  • apps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/page.tsx
  • apps/web/app/(app)/[emailAccountId]/transcript-settings/page.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
  • apps/web/utils/actions/transcript-settings.ts
  • apps/web/app/api/recall/webhook/types.ts
  • apps/web/utils/scheduled-actions/scheduler.ts
  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/utils/recall/transcript.ts
  • apps/web/app/api/user/calendars/route.ts
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
  • apps/web/utils/recall/bot.ts
apps/web/app/(app)/*/**

📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)

Components for the page are either put in page.tsx, or in the apps/web/app/(app)/PAGE_NAME folder

Files:

  • apps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/page.tsx
  • apps/web/app/(app)/[emailAccountId]/transcript-settings/page.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
apps/web/app/(app)/*/**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)

If you need to use onClick in a component, that component is a client component and file must start with 'use client'

Files:

  • apps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/page.tsx
  • apps/web/app/(app)/[emailAccountId]/transcript-settings/page.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
apps/web/app/(app)/*/**/**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)

If we're in a deeply nested component we will use swr to fetch via API

Files:

  • apps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/page.tsx
  • apps/web/app/(app)/[emailAccountId]/transcript-settings/page.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
apps/web/app/**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

Components with onClick must be client components with use client directive

Files:

  • apps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/page.tsx
  • apps/web/app/(app)/[emailAccountId]/transcript-settings/page.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{js,jsx,ts,tsx}: Don't use elements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...

Files:

  • apps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/page.tsx
  • apps/web/app/(app)/[emailAccountId]/transcript-settings/page.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
  • apps/web/utils/actions/transcript-settings.ts
  • apps/web/app/api/recall/webhook/types.ts
  • apps/web/utils/scheduled-actions/scheduler.ts
  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/utils/recall/transcript.ts
  • apps/web/app/api/user/calendars/route.ts
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
  • apps/web/utils/recall/bot.ts
!pages/_document.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.

Files:

  • apps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/page.tsx
  • apps/web/app/(app)/[emailAccountId]/transcript-settings/page.tsx
  • apps/web/prisma/schema.prisma
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
  • apps/web/utils/actions/transcript-settings.ts
  • apps/web/prisma/migrations/20251003004957_meeting_status/migration.sql
  • apps/web/app/api/recall/webhook/types.ts
  • apps/web/utils/scheduled-actions/scheduler.ts
  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/utils/recall/transcript.ts
  • apps/web/app/api/user/calendars/route.ts
  • apps/web/prisma/migrations/20250930170850_add_recall_meeting_support/migration.sql
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
  • apps/web/utils/recall/bot.ts
**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{jsx,tsx}: Don't destructure props inside JSX components in Solid projects.
Don't use both children and dangerouslySetInnerHTML props on the same element.
Don't use Array index in keys.
Don't assign to React component props.
Don't define React components inside other components.
Don't use event handlers on non-interactive elements.
Don't assign JSX properties multiple times.
Don't add extra closing tags for components without children.
Use <>...</> instead of ....
Don't insert comments as text nodes.
Don't use the return value of React.render.
Make sure all dependencies are correctly specified in React hooks.
Make sure all React hooks are called from the top level of component functions.
Don't use unnecessary fragments.
Don't pass children as props.
Use semantic elements instead of role attributes in JSX.

Files:

  • apps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/page.tsx
  • apps/web/app/(app)/[emailAccountId]/transcript-settings/page.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
**/*.{html,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{html,jsx,tsx}: Don't use or elements.
Don't use accessKey attribute on any HTML element.
Don't set aria-hidden="true" on focusable elements.
Don't add ARIA roles, states, and properties to elements that don't support them.
Only use the scope prop on elements.
Don't assign non-interactive ARIA roles to interactive HTML elements.
Make sure label elements have text content and are associated with an input.
Don't assign interactive ARIA roles to non-interactive HTML elements.
Don't assign tabIndex to non-interactive HTML elements.
Don't use positive integers for tabIndex property.
Don't include "image", "picture", or "photo" in img alt prop.
Don't use explicit role property that's the same as the implicit/default role.
Make static elements with click handlers use a valid role attribute.
Always include a title element for SVG elements.
Give all elements requiring alt text meaningful information for screen readers.
Make sure anchors have content that's accessible to screen readers.
Assign tabIndex to non-interactive HTML elements with aria-activedescendant.
Include all required ARIA attributes for elements with ARIA roles.
Make sure ARIA properties are valid for the element's supported roles.
Always include a type attribute for button elements.
Make elements with interactive roles and handlers focusable.
Give heading elements content that's accessible to screen readers (not hidden with aria-hidden).
Always include a lang attribute on the html element.
Always include a title attribute for iframe elements.
Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress.
Accompany onMouseOver/onMouseOut with onFocus/onBlur.
Include caption tracks for audio and video elements.
Make sure all anchors are valid and navigable.
Ensure all ARIA properties (aria-*) are valid.
Use valid, non-abstract ARIA roles for elements with ARIA roles.
Use valid ARIA state and property values.
Use valid values for the autocomplete attribute on input eleme...

Files:

  • apps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/page.tsx
  • apps/web/app/(app)/[emailAccountId]/transcript-settings/page.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
apps/web/prisma/schema.prisma

📄 CodeRabbit inference engine (.cursor/rules/prisma.mdc)

The Prisma schema file must be located at apps/web/prisma/schema.prisma

Files:

  • apps/web/prisma/schema.prisma
apps/web/utils/actions/**/*.ts

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

apps/web/utils/actions/**/*.ts: Use server actions for all mutations (create/update/delete operations)
next-safe-action provides centralized error handling
Use Zod schemas for validation on both client and server
Use revalidatePath in server actions for cache invalidation

apps/web/utils/actions/**/*.ts: Use server actions (with next-safe-action) for all mutations (create/update/delete operations); do NOT use POST API routes for mutations.
Use revalidatePath in server actions to invalidate cache after mutations.

Files:

  • apps/web/utils/actions/transcript-settings.ts
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)

**/*.ts: The same validation should be done in the server action too
Define validation schemas using Zod

Files:

  • apps/web/utils/actions/transcript-settings.ts
  • apps/web/app/api/recall/webhook/types.ts
  • apps/web/utils/scheduled-actions/scheduler.ts
  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/utils/recall/transcript.ts
  • apps/web/app/api/user/calendars/route.ts
  • apps/web/utils/recall/bot.ts
apps/web/utils/actions/*.ts

📄 CodeRabbit inference engine (.cursor/rules/server-actions.mdc)

apps/web/utils/actions/*.ts: Implement all server actions using the next-safe-action library for type safety, input validation, context management, and error handling. Refer to apps/web/utils/actions/safe-action.ts for client definitions (actionClient, actionClientUser, adminActionClient).
Use actionClientUser when only authenticated user context (userId) is needed.
Use actionClient when both authenticated user context and a specific emailAccountId are needed. The emailAccountId must be bound when calling the action from the client.
Use adminActionClient for actions restricted to admin users.
Access necessary context (like userId, emailAccountId, etc.) provided by the safe action client via the ctx object in the .action() handler.
Server Actions are strictly for mutations (operations that change data, e.g., creating, updating, deleting). Do NOT use Server Actions for data fetching (GET operations). For data fetching, use dedicated GET API Routes combined with SWR Hooks.
Use SafeError for expected/handled errors within actions if needed. next-safe-action provides centralized error handling.
Use the .metadata({ name: "actionName" }) method to provide a meaningful name for monitoring. Sentry instrumentation is automatically applied via withServerActionInstrumentation within the safe action clients.
If an action modifies data displayed elsewhere, use revalidatePath or revalidateTag from next/cache within the action handler as needed.

Server action files must start with use server

Files:

  • apps/web/utils/actions/transcript-settings.ts
apps/web/utils/**

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

Create utility functions in utils/ folder for reusable logic

Files:

  • apps/web/utils/actions/transcript-settings.ts
  • apps/web/utils/scheduled-actions/scheduler.ts
  • apps/web/utils/recall/transcript.ts
  • apps/web/utils/recall/bot.ts
apps/web/utils/**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

apps/web/utils/**/*.ts: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size

Files:

  • apps/web/utils/actions/transcript-settings.ts
  • apps/web/utils/scheduled-actions/scheduler.ts
  • apps/web/utils/recall/transcript.ts
  • apps/web/utils/recall/bot.ts
apps/web/app/api/**/*.{ts,js}

📄 CodeRabbit inference engine (.cursor/rules/security-audit.mdc)

apps/web/app/api/**/*.{ts,js}: All API route handlers in 'apps/web/app/api/' must use authentication middleware: withAuth, withEmailAccount, or withError (with custom authentication logic).
All Prisma queries in API routes must include user/account filtering (e.g., emailAccountId or userId in WHERE clauses) to prevent unauthorized data access.
All parameters used in API routes must be validated before use; do not use parameters from 'params' or request bodies directly in queries without validation.
Request bodies in API routes should use Zod schemas for validation.
API routes should only return necessary fields using Prisma's 'select' and must not include sensitive data in error messages.
Error messages in API routes must not reveal internal details; use generic errors and SafeError for user-facing errors.
All QStash endpoints (API routes called via publishToQstash or publishToQstashQueue) must use verifySignatureAppRouter to verify request authenticity.
All cron endpoints in API routes must use hasCronSecret or hasPostCronSecret for authentication.
Do not hardcode weak or plaintext secrets in API route files; secrets must not be directly assigned as string literals.
Review all new withError usage in API routes to ensure custom authentication is implemented where required.

Files:

  • apps/web/app/api/recall/webhook/types.ts
  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/app/api/user/calendars/route.ts
apps/web/app/api/**/route.ts

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

apps/web/app/api/**/route.ts: Use withAuth for user-level operations
Use withEmailAccount for email-account-level operations
Do NOT use POST API routes for mutations - use server actions instead
No need for try/catch in GET routes when using middleware
Export response types from GET routes

apps/web/app/api/**/route.ts: Wrap all GET API route handlers with withAuth or withEmailAccount middleware for authentication and authorization.
Export response types from GET API routes for type-safe client usage.
Do not use try/catch in GET API routes when using authentication middleware; rely on centralized error handling.

Files:

  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/app/api/user/calendars/route.ts
**/api/**/route.ts

📄 CodeRabbit inference engine (.cursor/rules/security.mdc)

**/api/**/route.ts: ALL API routes that handle user data MUST use appropriate authentication and authorization middleware (withAuth or withEmailAccount).
ALL database queries in API routes MUST be scoped to the authenticated user/account (e.g., include userId or emailAccountId in query filters).
Always validate that resources belong to the authenticated user before performing operations (resource ownership validation).
Use withEmailAccount middleware for API routes that operate on a specific email account (i.e., use or require emailAccountId).
Use withAuth middleware for API routes that operate at the user level (i.e., use or require only userId).
Use withError middleware (with proper validation) for public endpoints, custom authentication, or cron endpoints.
Cron endpoints MUST use withError middleware and validate the cron secret using hasCronSecret(request) or hasPostCronSecret(request).
Cron endpoints MUST capture unauthorized attempts with captureException and return a 401 status for unauthorized requests.
All parameters in API routes MUST be validated for type, format, and length before use.
Request bodies in API routes MUST be validated using Zod schemas before use.
All Prisma queries in API routes MUST only return necessary fields and never expose sensitive data.
Error messages in API routes MUST not leak internal information or sensitive data; use generic error messages and SafeError where appropriate.
API routes MUST use a consistent error response format, returning JSON with an error message and status code.
All findUnique and findFirst Prisma calls in API routes MUST include ownership filters (e.g., userId or emailAccountId).
All findMany Prisma calls in API routes MUST be scoped to the authenticated user's data.
Never use direct object references in API routes without ownership checks (prevent IDOR vulnerabilities).
Prevent mass assignment vulnerabilities by only allowing explicitly whitelisted fields in update operations in AP...

Files:

  • apps/web/app/api/recall/webhook/route.ts
  • apps/web/app/api/user/calendars/route.ts
🧠 Learnings (11)
📚 Learning: 2025-07-20T09:00:16.505Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-07-20T09:00:16.505Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Create new pages at: `apps/web/app/(app)/PAGE_NAME/page.tsx`

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/transcript-settings/page.tsx
📚 Learning: 2025-07-18T15:07:00.269Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-07-18T15:07:00.269Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Create new pages at: apps/web/app/(app)/PAGE_NAME/page.tsx

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/transcript-settings/page.tsx
📚 Learning: 2025-07-18T15:04:30.467Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/**/*.tsx : Use LoadingContent component for async data

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
📚 Learning: 2025-07-19T17:50:22.078Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-19T17:50:22.078Z
Learning: Applies to components/**/*.tsx : Use the `LoadingContent` component to handle loading states

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
📚 Learning: 2025-07-18T15:04:30.467Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/**/*.tsx : Use `LoadingContent` component to handle loading and error states consistently

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
📚 Learning: 2025-07-18T15:05:16.146Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.146Z
Learning: Applies to apps/web/components/**/*.tsx : Use the `LoadingContent` component to handle loading and error states consistently in data-fetching components.

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
📚 Learning: 2025-07-18T15:04:30.467Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/**/*.tsx : Pass `loading`, `error`, and children props to `LoadingContent`

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
📚 Learning: 2025-07-20T09:00:41.968Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-07-20T09:00:41.968Z
Learning: Applies to apps/web/app/api/**/*.{ts,js} : Request bodies in API routes should use Zod schemas for validation.

Applied to files:

  • apps/web/app/api/recall/webhook/route.ts
📚 Learning: 2025-07-18T17:27:46.389Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-07-18T17:27:46.389Z
Learning: Applies to **/api/**/route.ts : Request bodies in API routes MUST be validated using Zod schemas before use.

Applied to files:

  • apps/web/app/api/recall/webhook/route.ts
📚 Learning: 2025-07-20T09:00:41.968Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-07-20T09:00:41.968Z
Learning: Applies to apps/web/app/api/**/*.{ts,js} : API routes should only return necessary fields using Prisma's 'select' and must not include sensitive data in error messages.

Applied to files:

  • apps/web/app/api/recall/webhook/route.ts
📚 Learning: 2025-09-17T22:05:28.646Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-09-17T22:05:28.646Z
Learning: Applies to apps/web/utils/ai/**/*.{ts,tsx} : Implement fallbacks for AI failures

Applied to files:

  • apps/web/utils/recall/transcript.ts
🧬 Code graph analysis (9)
apps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsx (6)
apps/web/providers/EmailAccountProvider.tsx (1)
  • useAccount (72-82)
apps/web/hooks/useCalendars.ts (1)
  • useCalendars (4-6)
apps/web/utils/actions/transcript-settings.ts (1)
  • updateTranscriptsForAllCalendarsAction (11-63)
apps/web/utils/actions/recall-calendar.ts (2)
  • createRecallCalendarAction (15-72)
  • deleteRecallCalendarAction (74-138)
apps/web/components/LoadingContent.tsx (1)
  • LoadingContent (13-27)
apps/web/components/SettingCard.tsx (1)
  • SettingCard (3-26)
apps/web/app/(app)/[emailAccountId]/calendars/page.tsx (1)
apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx (1)
  • CalendarConnections (15-84)
apps/web/app/(app)/[emailAccountId]/transcript-settings/page.tsx (2)
apps/web/components/PageWrapper.tsx (1)
  • PageWrapper (3-20)
apps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsx (1)
  • TranscriptSettingsManager (17-144)
apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx (6)
apps/web/hooks/useCalendars.ts (1)
  • useCalendars (4-6)
apps/web/providers/EmailAccountProvider.tsx (1)
  • useAccount (72-82)
apps/web/utils/fetch.ts (1)
  • fetchWithAccount (7-25)
apps/web/app/api/google/calendar/auth-url/route.ts (1)
  • GetCalendarAuthUrlResponse (11-11)
apps/web/components/LoadingContent.tsx (1)
  • LoadingContent (13-27)
apps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendarButton.tsx (1)
  • ConnectCalendarButton (11-50)
apps/web/utils/actions/transcript-settings.ts (3)
apps/web/utils/logger.ts (1)
  • createScopedLogger (17-80)
apps/web/utils/actions/safe-action.ts (1)
  • actionClient (62-113)
apps/web/utils/error.ts (1)
  • SafeError (86-96)
apps/web/app/api/recall/webhook/route.ts (8)
apps/web/utils/logger.ts (1)
  • createScopedLogger (17-80)
apps/web/utils/middleware.ts (1)
  • withError (282-287)
apps/web/env.ts (1)
  • env (16-245)
apps/web/utils/recall/verify-svix-webhook.ts (1)
  • verifySvixWebhook (16-63)
apps/web/app/api/recall/webhook/types.ts (6)
  • RecallWebhookPayload (58-62)
  • CalendarSyncEventsEvent (8-14)
  • CalendarUpdateEvent (1-6)
  • RecordingDoneEvent (16-33)
  • TranscriptDoneEvent (35-56)
  • CalendarEvent (119-126)
apps/web/utils/recall/calendar.ts (3)
  • fetchCalendarEvents (129-170)
  • getRecallCalendar (78-104)
  • deleteRecallCalendar (109-127)
apps/web/utils/recall/bot.ts (1)
  • rescheduleBotsForUpdatedEvents (144-189)
apps/web/utils/recall/transcript.ts (3)
  • createAsyncTranscript (26-45)
  • getTranscriptMetadata (47-61)
  • fetchTranscriptContent (63-90)
apps/web/utils/recall/transcript.ts (3)
apps/web/utils/logger.ts (1)
  • createScopedLogger (17-80)
apps/web/utils/recall/request.ts (1)
  • recallRequest (10-57)
apps/web/app/api/recall/webhook/types.ts (2)
  • TranscriptMetadataResponse (93-97)
  • TranscriptContent (110-117)
apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx (2)
apps/web/utils/actions/calendar.ts (2)
  • disconnectCalendarAction (13-54)
  • toggleCalendarAction (56-80)
apps/web/app/(app)/[emailAccountId]/calendars/CalendarList.tsx (1)
  • CalendarList (16-63)
apps/web/utils/recall/bot.ts (3)
apps/web/utils/logger.ts (1)
  • createScopedLogger (17-80)
apps/web/app/api/recall/webhook/types.ts (2)
  • CalendarEvent (119-126)
  • RecallCalendarEventResponse (138-141)
apps/web/utils/recall/request.ts (1)
  • recallRequest (10-57)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Static Code Analysis Js
  • GitHub Check: Jit Security
🔇 Additional comments (17)
apps/web/app/(app)/[emailAccountId]/calendars/page.tsx (1)

5-15: LGTM!

The layout simplification is clean and improves readability. The conditional rendering logic has been moved into CalendarConnections itself, which is a better separation of concerns.

apps/web/utils/actions/transcript-settings.ts (1)

1-9: LGTM!

Correct usage of "use server" directive, actionClient, scoped logger, and proper imports.

apps/web/prisma/migrations/20251003004957_meeting_status/migration.sql (1)

1-2: LGTM!

The enum rename migration is correctly structured and aligns with the Prisma schema changes introducing MeetingStatus.

apps/web/app/(app)/[emailAccountId]/transcript-settings/page.tsx (1)

1-10: LGTM!

The page structure follows the established pattern for pages in the (app) directory. Based on learnings.

apps/web/app/api/user/calendars/route.ts (1)

22-22: LGTM!

The additional fields (recallCalendarId and transcriptEnabled) correctly expand the API response to support the Recall calendar integration features.

Also applies to: 31-31

apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx (2)

34-108: LGTM!

The refactor to direct action binding with local loading states, optimistic updates, and toast notifications is well-implemented. The error handling properly calls mutate() to refresh data, and the code correctly avoids console statements (addressing previous feedback).


116-202: LGTM!

The UI improvements enhance the visual hierarchy and user experience. The ConfirmDialog provides clear feedback for destructive actions, and the styling updates improve consistency across the calendar management interface.

apps/web/app/api/recall/webhook/route.ts (6)

32-94: LGTM! Zod schemas properly address previous review feedback.

The webhook payload schemas comprehensively validate all event types. The use of z.unknown() for metadata fields is appropriate since these are pass-through values.


187-192: Verify the intent of returning 200 for "invalid_grant" errors.

Returning a 200 status for OAuth errors like "invalid_grant" is unconventional and can mask authentication issues. Typically, authentication/authorization failures should return appropriate error codes (401/403).

Is this intentional to prevent webhook retries for expired tokens, or should this return an error status?


198-231: LGTM! Response propagation properly addresses previous review feedback.

The function now consistently returns NextResponse from all branches, ensuring unsupported event types receive the intended 400 status.


233-274: LGTM! Good error handling and validation.

The function properly filters invalid events and handles errors gracefully by returning an empty array while logging the error.


441-497: LGTM! Proper validation and error handling.

The function validates botId presence, updates meeting status appropriately, and creates transcripts with good logging throughout.


499-574: LGTM! Robust transcript processing.

The function handles various transcript data formats gracefully, with proper validation and error handling. The normalization logic for stringified JSON is a good defensive measure.

apps/web/utils/recall/bot.ts (4)

17-53: LGTM! Well-structured bot creation.

The function provides sensible defaults for bot configuration and properly structures the API request.


55-61: LGTM! Clean bot removal.

The function is straightforward and delegates error handling appropriately to callers.


63-142: LGTM! Robust scheduling logic.

The function properly validates timing constraints and uses upsert to handle idempotent bot scheduling. The 5-minute buffer prevents scheduling bots for events that have already started.

Minor note: Lines 110 and 118 use event.meeting_url || "" as a fallback, but meeting_url is already validated as present at line 68, making the fallback redundant (though harmless).


144-189: LGTM! Comprehensive rescheduling logic.

The function handles the full rescheduling lifecycle: removing old bots, updating status, and creating new bots when needed. The error handling around removeBotFromCalendarEvent (lines 160-168) is pragmatic—it logs failures but continues with rescheduling, which is reasonable given that duplicate bots are prevented via deduplication keys.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
apps/web/app/api/recall/webhook/route.ts (2)

280-288: Repeat: Scope Prisma query to required fields.

This Prisma query loads the full emailAccount object (including secrets like access tokens) and all calendar fields. As previously noted, restrict to only the fields actually used.

Apply this diff to scope the selection:

-  const connection = await prisma.calendarConnection.findUnique({
-    where: { recallCalendarId: calendarId },
-    include: {
-      emailAccount: true,
-      calendars: {
-        where: { isEnabled: true },
-      },
-    },
-  });
+  const connection = await prisma.calendarConnection.findUnique({
+    where: { recallCalendarId: calendarId },
+    select: {
+      id: true,
+      emailAccountId: true,
+      calendars: {
+        where: { isEnabled: true },
+        select: { id: true },
+      },
+    },
+  });

402-410: Repeat: Scope Prisma query to required fields.

Same issue as above—full emailAccount and calendar objects are loaded unnecessarily. This has been flagged multiple times in previous reviews.

Apply this diff to scope the selection:

-  const connection = await prisma.calendarConnection.findUnique({
-    where: { recallCalendarId: calendarId },
-    include: {
-      emailAccount: true,
-      calendars: {
-        where: { isEnabled: true },
-      },
-    },
-  });
+  const connection = await prisma.calendarConnection.findUnique({
+    where: { recallCalendarId: calendarId },
+    select: {
+      id: true,
+      emailAccountId: true,
+      calendars: {
+        where: { isEnabled: true },
+        select: { id: true },
+      },
+    },
+  });
🧹 Nitpick comments (3)
apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx (1)

20-42: Extract duplicated calendar connection logic into a shared hook.

The handleConnectCalendar function duplicates the logic in ConnectCalendarButton (which is used on line 67). Both implement identical state management, API calls, error handling, and redirect flows, violating the DRY principle.

Consider extracting this logic into a custom hook:

// hooks/useConnectCalendar.ts
import { useState } from "react";
import { fetchWithAccount } from "@/utils/fetch";
import { toastError } from "@/components/Toast";
import type { GetCalendarAuthUrlResponse } from "@/app/api/google/calendar/auth-url/route";

export function useConnectCalendar(emailAccountId: string | null) {
  const [isConnecting, setIsConnecting] = useState(false);

  const connect = async () => {
    setIsConnecting(true);
    try {
      const response = await fetchWithAccount({
        url: "/api/google/calendar/auth-url",
        emailAccountId,
        init: { headers: { "Content-Type": "application/json" } },
      });

      if (!response.ok) {
        throw new Error("Failed to initiate calendar connection");
      }

      const data: GetCalendarAuthUrlResponse = await response.json();
      window.location.href = data.url;
    } catch (error) {
      toastError({
        title: "Error initiating calendar connection",
        description: "Please try again or contact support",
      });
      setIsConnecting(false);
    }
  };

  return { connect, isConnecting };
}

Then update both CalendarConnections.tsx and ConnectCalendarButton.tsx to use this hook:

 export function CalendarConnections() {
   const { data, isLoading, error } = useCalendars();
   const { emailAccountId } = useAccount();
+  const { connect: handleConnectCalendar, isConnecting } = useConnectCalendar(emailAccountId);
   const connections = data?.connections || [];
-  const [isConnecting, setIsConnecting] = useState(false);
-
-  const handleConnectCalendar = async () => {
-    setIsConnecting(true);
-    try {
-      const response = await fetchWithAccount({
-        url: "/api/google/calendar/auth-url",
-        emailAccountId,
-        init: { headers: { "Content-Type": "application/json" } },
-      });
-
-      if (!response.ok) {
-        throw new Error("Failed to initiate calendar connection");
-      }
-
-      const data: GetCalendarAuthUrlResponse = await response.json();
-      window.location.href = data.url;
-    } catch (error) {
-      toastError({
-        title: "Error initiating calendar connection",
-        description: "Please try again or contact support",
-      });
-      setIsConnecting(false);
-    }
-  };
apps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsx (1)

113-115: Consider more robust state derivation.

The current logic assumes the first connected calendar's first enabled calendar is representative of all transcript settings. While this works when updateTranscriptsForAllCalendarsAction keeps all calendars in sync, edge cases (partial failures, manual DB changes) could lead to inconsistent state being displayed.

Consider deriving transcriptEnabled from all enabled calendars:

 const enabledCalendars =
-  connectedCalendars[0]?.calendars.filter((cal) => cal.isEnabled) || [];
-const transcriptEnabled = enabledCalendars[0]?.transcriptEnabled ?? false;
+  connectedCalendars.flatMap((conn) =>
+    conn.calendars.filter((cal) => cal.isEnabled)
+  );
+const transcriptEnabled = enabledCalendars.length > 0 
+  ? enabledCalendars.every((cal) => cal.transcriptEnabled)
+  : false;

This shows the toggle as enabled only if ALL enabled calendars have transcripts enabled, providing a more accurate representation of the system state.

apps/web/app/api/recall/webhook/route.ts (1)

337-338: Remove unnecessary else block.

The empty else block adds no value and can be removed.

Apply this diff:

     if (updatedEvents.length > 0) {
       await rescheduleBotsForUpdatedEvents(
         connection.emailAccountId,
         updatedEvents,
         calendarId,
       );
-    } else {
     }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 86b196f and 8c58bd8.

📒 Files selected for processing (4)
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx (2 hunks)
  • apps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsx (1 hunks)
  • apps/web/app/api/recall/webhook/route.ts (1 hunks)
  • apps/web/utils/scheduled-actions/scheduler.ts (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/utils/scheduled-actions/scheduler.ts
🧰 Additional context used
📓 Path-based instructions (18)
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use @/ for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX

Files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
  • apps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsx
  • apps/web/app/api/recall/webhook/route.ts
apps/web/app/**

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

NextJS app router structure with (app) directory

Files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
  • apps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsx
  • apps/web/app/api/recall/webhook/route.ts
apps/web/**/*.tsx

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

apps/web/**/*.tsx: Follow tailwindcss patterns with prettier-plugin-tailwindcss
Prefer functional components with hooks
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach
Follow consistent naming conventions (PascalCase for components)
Use LoadingContent component for async data
Use result?.serverError with toastError and toastSuccess
Use LoadingContent component to handle loading and error states consistently
Pass loading, error, and children props to LoadingContent

Files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
  • apps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsx
!{.cursor/rules/*.mdc}

📄 CodeRabbit inference engine (.cursor/rules/cursor-rules.mdc)

Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location

Files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
  • apps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsx
  • apps/web/app/api/recall/webhook/route.ts
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)

**/*.tsx: Use React Hook Form with Zod for validation
Validate form inputs before submission
Show validation errors inline next to form fields

Files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
  • apps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)

**/*.{ts,tsx}: Use createScopedLogger for logging in backend TypeScript files
Typically add the logger initialization at the top of the file when using createScopedLogger
Only use .with() on a logger instance within a specific function, not for a global logger

Import Prisma in the project using import prisma from "@/utils/prisma";

**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.

Files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
  • apps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsx
  • apps/web/app/api/recall/webhook/route.ts
apps/web/app/(app)/*/**

📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)

Components for the page are either put in page.tsx, or in the apps/web/app/(app)/PAGE_NAME folder

Files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
  • apps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsx
apps/web/app/(app)/*/**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)

If you need to use onClick in a component, that component is a client component and file must start with 'use client'

Files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
  • apps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsx
apps/web/app/(app)/*/**/**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)

If we're in a deeply nested component we will use swr to fetch via API

Files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
  • apps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsx
apps/web/app/**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

Components with onClick must be client components with use client directive

Files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
  • apps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{js,jsx,ts,tsx}: Don't use elements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...

Files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
  • apps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsx
  • apps/web/app/api/recall/webhook/route.ts
!pages/_document.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.

Files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
  • apps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsx
  • apps/web/app/api/recall/webhook/route.ts
**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{jsx,tsx}: Don't destructure props inside JSX components in Solid projects.
Don't use both children and dangerouslySetInnerHTML props on the same element.
Don't use Array index in keys.
Don't assign to React component props.
Don't define React components inside other components.
Don't use event handlers on non-interactive elements.
Don't assign JSX properties multiple times.
Don't add extra closing tags for components without children.
Use <>...</> instead of ....
Don't insert comments as text nodes.
Don't use the return value of React.render.
Make sure all dependencies are correctly specified in React hooks.
Make sure all React hooks are called from the top level of component functions.
Don't use unnecessary fragments.
Don't pass children as props.
Use semantic elements instead of role attributes in JSX.

Files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
  • apps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsx
**/*.{html,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)

**/*.{html,jsx,tsx}: Don't use or elements.
Don't use accessKey attribute on any HTML element.
Don't set aria-hidden="true" on focusable elements.
Don't add ARIA roles, states, and properties to elements that don't support them.
Only use the scope prop on elements.
Don't assign non-interactive ARIA roles to interactive HTML elements.
Make sure label elements have text content and are associated with an input.
Don't assign interactive ARIA roles to non-interactive HTML elements.
Don't assign tabIndex to non-interactive HTML elements.
Don't use positive integers for tabIndex property.
Don't include "image", "picture", or "photo" in img alt prop.
Don't use explicit role property that's the same as the implicit/default role.
Make static elements with click handlers use a valid role attribute.
Always include a title element for SVG elements.
Give all elements requiring alt text meaningful information for screen readers.
Make sure anchors have content that's accessible to screen readers.
Assign tabIndex to non-interactive HTML elements with aria-activedescendant.
Include all required ARIA attributes for elements with ARIA roles.
Make sure ARIA properties are valid for the element's supported roles.
Always include a type attribute for button elements.
Make elements with interactive roles and handlers focusable.
Give heading elements content that's accessible to screen readers (not hidden with aria-hidden).
Always include a lang attribute on the html element.
Always include a title attribute for iframe elements.
Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress.
Accompany onMouseOver/onMouseOut with onFocus/onBlur.
Include caption tracks for audio and video elements.
Make sure all anchors are valid and navigable.
Ensure all ARIA properties (aria-*) are valid.
Use valid, non-abstract ARIA roles for elements with ARIA roles.
Use valid ARIA state and property values.
Use valid values for the autocomplete attribute on input eleme...

Files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
  • apps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsx
apps/web/app/api/**/route.ts

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

apps/web/app/api/**/route.ts: Use withAuth for user-level operations
Use withEmailAccount for email-account-level operations
Do NOT use POST API routes for mutations - use server actions instead
No need for try/catch in GET routes when using middleware
Export response types from GET routes

apps/web/app/api/**/route.ts: Wrap all GET API route handlers with withAuth or withEmailAccount middleware for authentication and authorization.
Export response types from GET API routes for type-safe client usage.
Do not use try/catch in GET API routes when using authentication middleware; rely on centralized error handling.

Files:

  • apps/web/app/api/recall/webhook/route.ts
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)

**/*.ts: The same validation should be done in the server action too
Define validation schemas using Zod

Files:

  • apps/web/app/api/recall/webhook/route.ts
**/api/**/route.ts

📄 CodeRabbit inference engine (.cursor/rules/security.mdc)

**/api/**/route.ts: ALL API routes that handle user data MUST use appropriate authentication and authorization middleware (withAuth or withEmailAccount).
ALL database queries in API routes MUST be scoped to the authenticated user/account (e.g., include userId or emailAccountId in query filters).
Always validate that resources belong to the authenticated user before performing operations (resource ownership validation).
Use withEmailAccount middleware for API routes that operate on a specific email account (i.e., use or require emailAccountId).
Use withAuth middleware for API routes that operate at the user level (i.e., use or require only userId).
Use withError middleware (with proper validation) for public endpoints, custom authentication, or cron endpoints.
Cron endpoints MUST use withError middleware and validate the cron secret using hasCronSecret(request) or hasPostCronSecret(request).
Cron endpoints MUST capture unauthorized attempts with captureException and return a 401 status for unauthorized requests.
All parameters in API routes MUST be validated for type, format, and length before use.
Request bodies in API routes MUST be validated using Zod schemas before use.
All Prisma queries in API routes MUST only return necessary fields and never expose sensitive data.
Error messages in API routes MUST not leak internal information or sensitive data; use generic error messages and SafeError where appropriate.
API routes MUST use a consistent error response format, returning JSON with an error message and status code.
All findUnique and findFirst Prisma calls in API routes MUST include ownership filters (e.g., userId or emailAccountId).
All findMany Prisma calls in API routes MUST be scoped to the authenticated user's data.
Never use direct object references in API routes without ownership checks (prevent IDOR vulnerabilities).
Prevent mass assignment vulnerabilities by only allowing explicitly whitelisted fields in update operations in AP...

Files:

  • apps/web/app/api/recall/webhook/route.ts
apps/web/app/api/**/*.{ts,js}

📄 CodeRabbit inference engine (.cursor/rules/security-audit.mdc)

apps/web/app/api/**/*.{ts,js}: All API route handlers in 'apps/web/app/api/' must use authentication middleware: withAuth, withEmailAccount, or withError (with custom authentication logic).
All Prisma queries in API routes must include user/account filtering (e.g., emailAccountId or userId in WHERE clauses) to prevent unauthorized data access.
All parameters used in API routes must be validated before use; do not use parameters from 'params' or request bodies directly in queries without validation.
Request bodies in API routes should use Zod schemas for validation.
API routes should only return necessary fields using Prisma's 'select' and must not include sensitive data in error messages.
Error messages in API routes must not reveal internal details; use generic errors and SafeError for user-facing errors.
All QStash endpoints (API routes called via publishToQstash or publishToQstashQueue) must use verifySignatureAppRouter to verify request authenticity.
All cron endpoints in API routes must use hasCronSecret or hasPostCronSecret for authentication.
Do not hardcode weak or plaintext secrets in API route files; secrets must not be directly assigned as string literals.
Review all new withError usage in API routes to ensure custom authentication is implemented where required.

Files:

  • apps/web/app/api/recall/webhook/route.ts
🧠 Learnings (8)
📚 Learning: 2025-07-18T15:04:30.467Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/**/*.tsx : Use LoadingContent component for async data

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
📚 Learning: 2025-07-19T17:50:22.078Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-19T17:50:22.078Z
Learning: Applies to components/**/*.tsx : Use the `LoadingContent` component to handle loading states

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
📚 Learning: 2025-07-18T15:04:30.467Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/**/*.tsx : Use `LoadingContent` component to handle loading and error states consistently

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
📚 Learning: 2025-07-18T15:05:16.146Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.146Z
Learning: Applies to apps/web/components/**/*.tsx : Use the `LoadingContent` component to handle loading and error states consistently in data-fetching components.

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
📚 Learning: 2025-07-18T15:04:30.467Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/**/*.tsx : Pass `loading`, `error`, and children props to `LoadingContent`

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
📚 Learning: 2025-07-20T09:00:41.968Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-07-20T09:00:41.968Z
Learning: Applies to apps/web/app/api/**/*.{ts,js} : Request bodies in API routes should use Zod schemas for validation.

Applied to files:

  • apps/web/app/api/recall/webhook/route.ts
📚 Learning: 2025-07-18T17:27:46.389Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-07-18T17:27:46.389Z
Learning: Applies to **/api/**/route.ts : Request bodies in API routes MUST be validated using Zod schemas before use.

Applied to files:

  • apps/web/app/api/recall/webhook/route.ts
📚 Learning: 2025-07-20T09:00:41.968Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-07-20T09:00:41.968Z
Learning: Applies to apps/web/app/api/**/*.{ts,js} : API routes should only return necessary fields using Prisma's 'select' and must not include sensitive data in error messages.

Applied to files:

  • apps/web/app/api/recall/webhook/route.ts
🧬 Code graph analysis (3)
apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx (5)
apps/web/hooks/useCalendars.ts (1)
  • useCalendars (4-6)
apps/web/providers/EmailAccountProvider.tsx (1)
  • useAccount (72-82)
apps/web/utils/fetch.ts (1)
  • fetchWithAccount (7-25)
apps/web/app/api/google/calendar/auth-url/route.ts (1)
  • GetCalendarAuthUrlResponse (11-11)
apps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendarButton.tsx (1)
  • ConnectCalendarButton (11-50)
apps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsx (6)
apps/web/providers/EmailAccountProvider.tsx (1)
  • useAccount (72-82)
apps/web/hooks/useCalendars.ts (1)
  • useCalendars (4-6)
apps/web/utils/actions/transcript-settings.ts (1)
  • updateTranscriptsForAllCalendarsAction (11-63)
apps/web/utils/actions/recall-calendar.ts (2)
  • createRecallCalendarAction (15-72)
  • deleteRecallCalendarAction (74-138)
apps/web/components/LoadingContent.tsx (1)
  • LoadingContent (13-27)
apps/web/components/SettingCard.tsx (1)
  • SettingCard (3-26)
apps/web/app/api/recall/webhook/route.ts (6)
apps/web/utils/logger.ts (1)
  • createScopedLogger (17-80)
apps/web/utils/recall/verify-svix-webhook.ts (1)
  • verifySvixWebhook (16-63)
apps/web/app/api/recall/webhook/types.ts (6)
  • RecallWebhookPayload (58-62)
  • CalendarSyncEventsEvent (8-14)
  • CalendarUpdateEvent (1-6)
  • RecordingDoneEvent (16-33)
  • TranscriptDoneEvent (35-56)
  • CalendarEvent (119-126)
apps/web/utils/recall/calendar.ts (3)
  • fetchCalendarEvents (129-170)
  • getRecallCalendar (78-104)
  • deleteRecallCalendar (109-127)
apps/web/utils/recall/bot.ts (1)
  • rescheduleBotsForUpdatedEvents (144-189)
apps/web/utils/recall/transcript.ts (3)
  • createAsyncTranscript (26-45)
  • getTranscriptMetadata (47-61)
  • fetchTranscriptContent (63-90)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Jit Security
🔇 Additional comments (15)
apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx (1)

44-81: Clean UI structure with proper state management.

The component correctly uses LoadingContent for async state handling and provides clear conditional rendering for empty vs. populated states. The use of EnableFeatureCard for onboarding and the header layout for existing connections follows good UX patterns.

Based on learnings

apps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsx (6)

1-15: Imports look good.

All imports follow the project's path alias conventions and coding guidelines.


17-39: State management and action binding are correct.

The direct binding of server actions with emailAccountId is appropriate for next-safe-action's actionClient pattern. The derived state correctly filters connected calendars and identifies the recall connection.


72-75: Bug fix confirmed: transcript flag now always updates when disabling.

The previously flagged critical bug has been resolved. The code now correctly calls executeUpdate({ transcriptEnabled: false }) unconditionally after optionally deleting the recall calendar, ensuring the database flag is always updated.


89-97: useCallback dependencies are complete.

The dependency array correctly includes all external values referenced within the callback.


99-111: Early return for no calendars is appropriate.

Shows a clear call-to-action card directing users to connect their calendar first, which is good UX.


117-142: Main render logic looks good.

The component properly uses LoadingContent for loading/error states and SettingCard to present the transcript toggle in a clean, consistent layout.

apps/web/app/api/recall/webhook/route.ts (8)

1-30: LGTM: Proper setup for webhook endpoint.

Imports, middleware, and logger initialization follow project conventions. Using withError is appropriate for this public webhook endpoint that relies on signature verification rather than user authentication.


32-94: LGTM: Comprehensive Zod validation.

The schemas properly validate all expected webhook event types. Using z.unknown() for metadata fields is pragmatic since external webhook payloads may evolve.


96-196: LGTM: Robust webhook verification and validation.

The handler properly validates headers, verifies signatures when configured, and validates the payload with Zod before processing. The special handling for invalid_grant errors (returning 200) prevents webhook retry storms when OAuth tokens expire.


198-231: LGTM: Proper response handling.

The function correctly returns NextResponse from all branches, ensuring unsupported events return the intended 400 status. This properly addresses earlier feedback about response propagation.


233-274: LGTM: Defensive event fetching.

The function properly filters deleted events, validates required fields, and returns an empty array on errors to prevent webhook processing failures. The logging provides good visibility into skipped events.


348-400: LGTM: Proper disconnect handling.

The calendar disconnect scenario is handled correctly: cleans up the Recall calendar, updates connection state, and cancels active/scheduled meetings. The error handling for calendar deletion is appropriately defensive (warns but continues).


441-497: LGTM: Recording completion handling.

The function properly validates the bot ID, updates the meeting status to ACTIVE, and initiates async transcript creation. The error handling is comprehensive with appropriate logging.


499-574: LGTM: Comprehensive transcript processing.

The function handles transcript completion properly with thorough validation. The defensive JSON parsing (lines 526-539) is a pragmatic workaround for potential API inconsistencies where transcript data might be double-encoded.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants