-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Recall integration #815
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Recall integration #815
Conversation
|
@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. |
|
Note Other AI code review bot(s) detectedCodeRabbit 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. WalkthroughAdds 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
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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this 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'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.
There was a problem hiding this 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 expectationsThe 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 fieldsUsing 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: preferexport typeoverinterfaceThe 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 brittleChecking 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_urlMeeting 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 returnedIf 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 blockEmpty else introduces noise.
- if (updatedEvents.length > 0) { + if (updatedEvents.length > 0) { await rescheduleBotsForUpdatedEvents( connection.emailAccountId, updatedEvents, calendarId, ); - } else { - } + }
382-392: Safer access for bot idIf 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
📒 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.tsapps/web/utils/recall/verify-svix-webhook.tsapps/web/app/api/google/calendar/callback/route.tsapps/web/env.tsapps/web/utils/recall/transcript.tsapps/web/app/api/recall/webhook/route.tsapps/web/utils/recall/bot.tsapps/web/app/api/recall/webhook/types.tsapps/web/utils/actions/calendar.tsapps/web/utils/recall/calendar.tsapps/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.tsapps/web/utils/recall/verify-svix-webhook.tsapps/web/app/api/google/calendar/callback/route.tsapps/web/env.tsapps/web/utils/recall/transcript.tsapps/web/app/api/recall/webhook/route.tsapps/web/utils/recall/bot.tsapps/web/app/api/recall/webhook/types.tsapps/web/utils/actions/calendar.tsapps/web/utils/recall/calendar.tsapps/web/prisma/schema.prismaapps/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.tsapps/web/utils/recall/verify-svix-webhook.tsapps/web/app/api/google/calendar/callback/route.tsapps/web/env.tsapps/web/utils/recall/transcript.tsapps/web/app/api/recall/webhook/route.tsapps/web/utils/recall/bot.tsapps/web/app/api/recall/webhook/types.tsapps/web/utils/actions/calendar.tsapps/web/utils/recall/calendar.tsapps/web/app/api/recall/calendar/route.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}: UsecreateScopedLoggerfor logging in backend TypeScript files
Typically add the logger initialization at the top of the file when usingcreateScopedLogger
Only use.with()on a logger instance within a specific function, not for a global loggerImport 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.tsapps/web/utils/recall/verify-svix-webhook.tsapps/web/app/api/google/calendar/callback/route.tsapps/web/env.tsapps/web/utils/recall/transcript.tsapps/web/app/api/recall/webhook/route.tsapps/web/utils/recall/bot.tsapps/web/app/api/recall/webhook/types.tsapps/web/utils/actions/calendar.tsapps/web/utils/recall/calendar.tsapps/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.tsapps/web/utils/recall/verify-svix-webhook.tsapps/web/utils/recall/transcript.tsapps/web/utils/recall/bot.tsapps/web/utils/actions/calendar.tsapps/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.tsapps/web/utils/recall/verify-svix-webhook.tsapps/web/utils/recall/transcript.tsapps/web/utils/recall/bot.tsapps/web/utils/actions/calendar.tsapps/web/utils/recall/calendar.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useelements 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.tsapps/web/utils/recall/verify-svix-webhook.tsapps/web/app/api/google/calendar/callback/route.tsapps/web/env.tsapps/web/utils/recall/transcript.tsapps/web/app/api/recall/webhook/route.tsapps/web/utils/recall/bot.tsapps/web/app/api/recall/webhook/types.tsapps/web/utils/actions/calendar.tsapps/web/utils/recall/calendar.tsapps/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.tsapps/web/utils/recall/verify-svix-webhook.tsapps/web/app/api/google/calendar/callback/route.tsapps/web/env.tsapps/web/utils/recall/transcript.tsapps/web/app/api/recall/webhook/route.tsapps/web/utils/recall/bot.tsapps/web/app/api/recall/webhook/types.tsapps/web/utils/actions/calendar.tsapps/web/utils/recall/calendar.tsapps/web/prisma/schema.prismaapps/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.tsapps/web/app/api/recall/webhook/route.tsapps/web/app/api/recall/webhook/types.tsapps/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: UsewithAuthfor user-level operations
UsewithEmailAccountfor 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 withwithAuthorwithEmailAccountmiddleware 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.tsapps/web/app/api/recall/webhook/route.tsapps/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).
UsewithEmailAccountmiddleware for API routes that operate on a specific email account (i.e., use or requireemailAccountId).
UsewithAuthmiddleware for API routes that operate at the user level (i.e., use or require onlyuserId).
UsewithErrormiddleware (with proper validation) for public endpoints, custom authentication, or cron endpoints.
Cron endpoints MUST usewithErrormiddleware and validate the cron secret usinghasCronSecret(request)orhasPostCronSecret(request).
Cron endpoints MUST capture unauthorized attempts withcaptureExceptionand 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.
AllfindUniqueandfindFirstPrisma calls in API routes MUST include ownership filters (e.g., userId or emailAccountId).
AllfindManyPrisma 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.tsapps/web/app/api/recall/webhook/route.tsapps/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.tsapps/web/app/api/recall/webhook/route.tsapps/web/app/api/recall/webhook/types.tsapps/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, andturbo.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 toapps/web/env.tsin the appropriate section: useserverfor server-only variables, and for client-side variables, use theclientsection and also add toexperimental__runtimeEnv.
Client-side environment variables must be prefixed withNEXT_PUBLIC_and added to both theclientandexperimental__runtimeEnvsections inapps/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-actionprovides centralized error handling
Use Zod schemas for validation on both client and server
UserevalidatePathin server actions for cache invalidation
apps/web/utils/actions/**/*.ts: Use server actions (withnext-safe-action) for all mutations (create/update/delete operations); do NOT use POST API routes for mutations.
UserevalidatePathin 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 thenext-safe-actionlibrary for type safety, input validation, context management, and error handling. Refer toapps/web/utils/actions/safe-action.tsfor client definitions (actionClient,actionClientUser,adminActionClient).
UseactionClientUserwhen only authenticated user context (userId) is needed.
UseactionClientwhen both authenticated user context and a specificemailAccountIdare needed. TheemailAccountIdmust be bound when calling the action from the client.
UseadminActionClientfor actions restricted to admin users.
Access necessary context (likeuserId,emailAccountId, etc.) provided by the safe action client via thectxobject 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.
UseSafeErrorfor expected/handled errors within actions if needed.next-safe-actionprovides centralized error handling.
Use the.metadata({ name: "actionName" })method to provide a meaningful name for monitoring. Sentry instrumentation is automatically applied viawithServerActionInstrumentationwithin the safe action clients.
If an action modifies data displayed elsewhere, userevalidatePathorrevalidateTagfromnext/cachewithin 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
recallCalendarIdfield 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/v2with endpoints starting withapi/v2, resulting in duplicated paths likehttps://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/v2suffix, 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
withEmailAccountmiddleware 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
There was a problem hiding this 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 schemasSchemas 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 formatReject 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 callerPublic 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 blockNo-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 castsDiscriminated 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
📒 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.prismaapps/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.prismaapps/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: UsewithAuthfor user-level operations
UsewithEmailAccountfor 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 withwithAuthorwithEmailAccountmiddleware 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}: UsecreateScopedLoggerfor logging in backend TypeScript files
Typically add the logger initialization at the top of the file when usingcreateScopedLogger
Only use.with()on a logger instance within a specific function, not for a global loggerImport 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).
UsewithEmailAccountmiddleware for API routes that operate on a specific email account (i.e., use or requireemailAccountId).
UsewithAuthmiddleware for API routes that operate at the user level (i.e., use or require onlyuserId).
UsewithErrormiddleware (with proper validation) for public endpoints, custom authentication, or cron endpoints.
Cron endpoints MUST usewithErrormiddleware and validate the cron secret usinghasCronSecret(request)orhasPostCronSecret(request).
Cron endpoints MUST capture unauthorized attempts withcaptureExceptionand 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.
AllfindUniqueandfindFirstPrisma calls in API routes MUST include ownership filters (e.g., userId or emailAccountId).
AllfindManyPrisma 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 useelements 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 goodEmailAccount → RecallMeeting relation is well-defined.
851-860: Verify uniqueness semantics for nullable recallCalendarIdPostgreSQL 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 goodStatuses 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 bugReturning the NextResponse from processRecallWebhook resolves the earlier issue noted in prior review.
Also applies to: 207-240
There was a problem hiding this 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
errorobject, 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
errorobject. Apply the same sanitization as suggested forcreateRecallCalendar.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
errorobject. 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 successThe meeting is marked
CANCELLEDeven whenremoveBotFromCalendarEventfails, 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 payloadThe 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 NextResponseThe default case returns a 400 NextResponse for unsupported events, but
processRecallWebhookis declared as returningPromise<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 onlyIncluding the full
emailAccountrelation 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 onlySame issue as
handleCalendarSyncEvents: including fullemailAccountexposes 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_tokenas a parameter requires the caller to know about and access the token. Consider refactoringcreateRecallCalendarto accept aconnectionIdand 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 handlingThe handler appropriately handles multiple transcript response formats and safely falls back to JSON stringification for unexpected formats.
Optional optimization: The
findUniqueat 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
📒 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.prismaapps/web/hooks/useRecallCalendar.tsapps/web/components/RecallCalendarManager.tsxapps/web/utils/recall/bot.tsapps/web/app/api/user/recall-calendar/route.tsapps/web/prisma/migrations/20250930170850_add_recall_meeting_support/migration.sqlapps/web/utils/actions/recall-calendar.tsapps/web/app/api/recall/webhook/route.tsapps/web/app/api/google/calendar/callback/route.tsapps/web/utils/actions/calendar.tsapps/web/utils/recall/calendar.tsapps/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.prismaapps/web/hooks/useRecallCalendar.tsapps/web/components/RecallCalendarManager.tsxapps/web/utils/recall/bot.tsapps/web/app/api/user/recall-calendar/route.tsapps/web/prisma/migrations/20250930170850_add_recall_meeting_support/migration.sqlapps/web/utils/actions/recall-calendar.tsapps/web/app/api/recall/webhook/route.tsapps/web/app/api/google/calendar/callback/route.tsapps/web/utils/actions/calendar.tsapps/web/utils/recall/calendar.tsapps/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.tsapps/web/components/RecallCalendarManager.tsxapps/web/utils/recall/bot.tsapps/web/app/api/user/recall-calendar/route.tsapps/web/utils/actions/recall-calendar.tsapps/web/app/api/recall/webhook/route.tsapps/web/app/api/google/calendar/callback/route.tsapps/web/utils/actions/calendar.tsapps/web/utils/recall/calendar.tsapps/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.
Callmutate()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.tsapps/web/utils/recall/bot.tsapps/web/app/api/user/recall-calendar/route.tsapps/web/utils/actions/recall-calendar.tsapps/web/app/api/recall/webhook/route.tsapps/web/app/api/google/calendar/callback/route.tsapps/web/utils/actions/calendar.tsapps/web/utils/recall/calendar.tsapps/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 theuseprefix (e.g.,useAccounts.ts).
For fetching data from API endpoints in custom hooks, prefer usinguseSWR.
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}: UsecreateScopedLoggerfor logging in backend TypeScript files
Typically add the logger initialization at the top of the file when usingcreateScopedLogger
Only use.with()on a logger instance within a specific function, not for a global loggerImport 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.tsapps/web/components/RecallCalendarManager.tsxapps/web/utils/recall/bot.tsapps/web/app/api/user/recall-calendar/route.tsapps/web/utils/actions/recall-calendar.tsapps/web/app/api/recall/webhook/route.tsapps/web/app/api/google/calendar/callback/route.tsapps/web/utils/actions/calendar.tsapps/web/utils/recall/calendar.tsapps/web/utils/recall/verify-svix-webhook.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useelements 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.tsapps/web/components/RecallCalendarManager.tsxapps/web/utils/recall/bot.tsapps/web/app/api/user/recall-calendar/route.tsapps/web/utils/actions/recall-calendar.tsapps/web/app/api/recall/webhook/route.tsapps/web/app/api/google/calendar/callback/route.tsapps/web/utils/actions/calendar.tsapps/web/utils/recall/calendar.tsapps/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
Useresult?.serverErrorwithtoastErrorandtoastSuccess
UseLoadingContentcomponent to handle loading and error states consistently
Passloading,error, and children props toLoadingContent
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
LoadingContentcomponent 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.tsxapps/web/utils/**
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Create utility functions in
utils/folder for reusable logicFiles:
apps/web/utils/recall/bot.tsapps/web/utils/actions/recall-calendar.tsapps/web/utils/actions/calendar.tsapps/web/utils/recall/calendar.tsapps/web/utils/recall/verify-svix-webhook.tsapps/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 sizeFiles:
apps/web/utils/recall/bot.tsapps/web/utils/actions/recall-calendar.tsapps/web/utils/actions/calendar.tsapps/web/utils/recall/calendar.tsapps/web/utils/recall/verify-svix-webhook.tsapps/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.tsapps/web/app/api/recall/webhook/route.tsapps/web/app/api/google/calendar/callback/route.tsapps/web/app/api/**/route.ts
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/app/api/**/route.ts: UsewithAuthfor user-level operations
UsewithEmailAccountfor 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 withwithAuthorwithEmailAccountmiddleware 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.tsapps/web/app/api/recall/webhook/route.tsapps/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).
UsewithEmailAccountmiddleware for API routes that operate on a specific email account (i.e., use or requireemailAccountId).
UsewithAuthmiddleware for API routes that operate at the user level (i.e., use or require onlyuserId).
UsewithErrormiddleware (with proper validation) for public endpoints, custom authentication, or cron endpoints.
Cron endpoints MUST usewithErrormiddleware and validate the cron secret usinghasCronSecret(request)orhasPostCronSecret(request).
Cron endpoints MUST capture unauthorized attempts withcaptureExceptionand 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.
AllfindUniqueandfindFirstPrisma calls in API routes MUST include ownership filters (e.g., userId or emailAccountId).
AllfindManyPrisma 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.tsapps/web/app/api/recall/webhook/route.tsapps/web/app/api/google/calendar/callback/route.tsapps/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.tsapps/web/app/api/recall/webhook/route.tsapps/web/app/api/google/calendar/callback/route.tsapps/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-actionprovides centralized error handling
Use Zod schemas for validation on both client and server
UserevalidatePathin server actions for cache invalidation
apps/web/utils/actions/**/*.ts: Use server actions (withnext-safe-action) for all mutations (create/update/delete operations); do NOT use POST API routes for mutations.
UserevalidatePathin server actions to invalidate cache after mutations.Files:
apps/web/utils/actions/recall-calendar.tsapps/web/utils/actions/calendar.tsapps/web/utils/actions/*.ts
📄 CodeRabbit inference engine (.cursor/rules/server-actions.mdc)
apps/web/utils/actions/*.ts: Implement all server actions using thenext-safe-actionlibrary for type safety, input validation, context management, and error handling. Refer toapps/web/utils/actions/safe-action.tsfor client definitions (actionClient,actionClientUser,adminActionClient).
UseactionClientUserwhen only authenticated user context (userId) is needed.
UseactionClientwhen both authenticated user context and a specificemailAccountIdare needed. TheemailAccountIdmust be bound when calling the action from the client.
UseadminActionClientfor actions restricted to admin users.
Access necessary context (likeuserId,emailAccountId, etc.) provided by the safe action client via thectxobject 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.
UseSafeErrorfor expected/handled errors within actions if needed.next-safe-actionprovides centralized error handling.
Use the.metadata({ name: "actionName" })method to provide a meaningful name for monitoring. Sentry instrumentation is automatically applied viawithServerActionInstrumentationwithin the safe action clients.
If an action modifies data displayed elsewhere, userevalidatePathorrevalidateTagfromnext/cachewithin the action handler as needed.Server action files must start with
use serverFiles:
apps/web/utils/actions/recall-calendar.tsapps/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 cachingApplied 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
useSWRfor data fetching as per coding guidelines- Properly typed with
GetRecallCalendarResponse- Uses
import typefor type imports- Follows naming convention with
useprefix- 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
botIdensures 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
LoadingContentto 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 dataAs per coding guidelines.
32-44: Remove parameter from action invocation.The action
deleteRecallCalendarActionusesctx.emailAccountIdfrom the action context and doesn't accept parameters. PassingemailAccountIdas 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
createRecallCalendarActionusesctx.emailAccountIdfrom the action context and doesn't accept parameters. PassingemailAccountIdas 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
loggerfrom the action context, which automatically includes contextual metadata likeemailAccountId.
31-45: LGTM!The resilient error handling ensures the local
CalendarConnectionis 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 usectx.loggerinstead.Server actions receive
loggerin thectxparameter, which automatically includes contextual metadata (e.g.,emailAccountId,userId). Remove thecreateScopedLoggerimport and the module-levelloggerinitialization, and destructureloggerfromctxin 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
withEmailAccountfor 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 withPromise.allis efficient.apps/web/prisma/schema.prisma (4)
850-858: LGTM!The optional
recallCalendarIdwith a unique constraint correctly enforces a one-to-one mapping between calendar connections and Recall calendars.
907-924: LGTM!The
Meetingmodel is well-designed with appropriate indexes for query patterns and a uniquebotIdfor one-to-one mapping. ThededuplicationKeyindex will help prevent duplicate meeting entries.
157-161: LGTM!The
meetingsrelation is correctly defined and aligns with the newMeetingmodel.
1062-1068: LGTM!The
RecallBotStatusenum 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 setupImports follow project conventions and the scoped logger is correctly initialized at module level.
145-186: LGTM: Robust event fetching with proper filteringThe helper correctly filters deleted events, validates required fields, and handles errors gracefully.
353-380: LGTM: Clean async transcript creationThe 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 initializationImports and scoped logger follow project conventions.
11-13: LGTM: Simple and effective deduplication keyCombining
start_timeandmeeting_urlprovides a reasonable uniqueness guarantee for bot scheduling.
15-35: LGTM: Clean API wrapperThe function appropriately wraps the Recall bot creation API call.
37-43: LGTM: Simple deletion wrapperAppropriately delegates to
recallRequestfor error handling.
45-120: LGTM: Bot scheduling with proper validation and persistenceThe function validates timing, uses upsert to prevent duplicates, and includes bot customization (avatar and background color) as requested in previous feedback.
apps/web/prisma/migrations/20250930170850_add_recall_meeting_support/migration.sql
Show resolved
Hide resolved
apps/web/prisma/migrations/20250930170850_add_recall_meeting_support/migration.sql
Show resolved
Hide resolved
apps/web/prisma/migrations/20250930170850_add_recall_meeting_support/migration.sql
Show resolved
Hide resolved
There was a problem hiding this 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 usePer guidelines, API request bodies must be validated with Zod. Also guard
JSON.parseerrors 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: PropagateprocessRecallWebhookresponse to avoid 200 on unsupported eventsThe 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 fieldsAvoid
includefor 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
includewith minimalselect.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 unsafeobjectwithRecord<string, unknown>
metadata: objectis too loose and blocks safe property access. PreferRecord<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 valuesThe 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: Narrowplatformto 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 emptyelseblockThe
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 typesThe inline type for
segmentduplicates structures from types.ts. Import and reuseTranscriptSegment/TranscriptWordto 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 unsetYou already bypass verification when
RECALL_WEBHOOK_SECRETis 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.tsdepends onapp/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 keyConsider including
event.idto 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<= nowBots 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 failsIf Recall API deletion fails, immediately marking the meeting CANCELLED can desync state. Either:
- only mark CANCELLED on successful removal, or
- mark as
CANCELLEDwith 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
📒 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.tsapps/web/app/api/recall/webhook/types.tsapps/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.tsapps/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: UsewithAuthfor user-level operations
UsewithEmailAccountfor 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 withwithAuthorwithEmailAccountmiddleware 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.tsapps/web/app/api/recall/webhook/types.tsapps/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.tsapps/web/app/api/recall/webhook/types.tsapps/web/utils/recall/bot.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}: UsecreateScopedLoggerfor logging in backend TypeScript files
Typically add the logger initialization at the top of the file when usingcreateScopedLogger
Only use.with()on a logger instance within a specific function, not for a global loggerImport 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.tsapps/web/app/api/recall/webhook/types.tsapps/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).
UsewithEmailAccountmiddleware for API routes that operate on a specific email account (i.e., use or requireemailAccountId).
UsewithAuthmiddleware for API routes that operate at the user level (i.e., use or require onlyuserId).
UsewithErrormiddleware (with proper validation) for public endpoints, custom authentication, or cron endpoints.
Cron endpoints MUST usewithErrormiddleware and validate the cron secret usinghasCronSecret(request)orhasPostCronSecret(request).
Cron endpoints MUST capture unauthorized attempts withcaptureExceptionand 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.
AllfindUniqueandfindFirstPrisma calls in API routes MUST include ownership filters (e.g., userId or emailAccountId).
AllfindManyPrisma 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.tsapps/web/app/api/recall/webhook/types.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useelements 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.tsapps/web/app/api/recall/webhook/types.tsapps/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.tsapps/web/app/api/recall/webhook/types.tsapps/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
There was a problem hiding this 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 explicitselectwith just the ids (and any other actually-used columns). As per coding guidelinesAlso 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_FAILEDto theRecallBotStatusenum 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 thatmeeting_urlexists and returnsnullif 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
📒 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.prismaapps/web/app/api/google/calendar/callback/route.tsapps/web/utils/actions/recall-calendar.tsapps/web/utils/recall/bot.tsapps/web/app/api/recall/webhook/route.tsapps/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.prismaapps/web/app/api/google/calendar/callback/route.tsapps/web/utils/actions/recall-calendar.tsapps/web/utils/recall/bot.tsapps/web/app/api/recall/webhook/route.tsapps/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.tsapps/web/utils/actions/recall-calendar.tsapps/web/utils/recall/bot.tsapps/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.tsapps/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: UsewithAuthfor user-level operations
UsewithEmailAccountfor 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 withwithAuthorwithEmailAccountmiddleware 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.tsapps/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.tsapps/web/utils/actions/recall-calendar.tsapps/web/utils/recall/bot.tsapps/web/app/api/recall/webhook/route.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}: UsecreateScopedLoggerfor logging in backend TypeScript files
Typically add the logger initialization at the top of the file when usingcreateScopedLogger
Only use.with()on a logger instance within a specific function, not for a global loggerImport 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.tsapps/web/utils/actions/recall-calendar.tsapps/web/utils/recall/bot.tsapps/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).
UsewithEmailAccountmiddleware for API routes that operate on a specific email account (i.e., use or requireemailAccountId).
UsewithAuthmiddleware for API routes that operate at the user level (i.e., use or require onlyuserId).
UsewithErrormiddleware (with proper validation) for public endpoints, custom authentication, or cron endpoints.
Cron endpoints MUST usewithErrormiddleware and validate the cron secret usinghasCronSecret(request)orhasPostCronSecret(request).
Cron endpoints MUST capture unauthorized attempts withcaptureExceptionand 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.
AllfindUniqueandfindFirstPrisma calls in API routes MUST include ownership filters (e.g., userId or emailAccountId).
AllfindManyPrisma 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.tsapps/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.tsapps/web/app/api/recall/webhook/route.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useelements 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.tsapps/web/utils/actions/recall-calendar.tsapps/web/utils/recall/bot.tsapps/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-actionprovides centralized error handling
Use Zod schemas for validation on both client and server
UserevalidatePathin server actions for cache invalidation
apps/web/utils/actions/**/*.ts: Use server actions (withnext-safe-action) for all mutations (create/update/delete operations); do NOT use POST API routes for mutations.
UserevalidatePathin 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 thenext-safe-actionlibrary for type safety, input validation, context management, and error handling. Refer toapps/web/utils/actions/safe-action.tsfor client definitions (actionClient,actionClientUser,adminActionClient).
UseactionClientUserwhen only authenticated user context (userId) is needed.
UseactionClientwhen both authenticated user context and a specificemailAccountIdare needed. TheemailAccountIdmust be bound when calling the action from the client.
UseadminActionClientfor actions restricted to admin users.
Access necessary context (likeuserId,emailAccountId, etc.) provided by the safe action client via thectxobject 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.
UseSafeErrorfor expected/handled errors within actions if needed.next-safe-actionprovides centralized error handling.
Use the.metadata({ name: "actionName" })method to provide a meaningful name for monitoring. Sentry instrumentation is automatically applied viawithServerActionInstrumentationwithin the safe action clients.
If an action modifies data displayed elsewhere, userevalidatePathorrevalidateTagfromnext/cachewithin 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.tsapps/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.tsapps/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
createScopedLoggerat 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!
There was a problem hiding this 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 onemailAccountIdto 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
📒 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. Allprisma.recallMeetingreferences have been removed and the code uniformly usesprisma.meeting.
There was a problem hiding this 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 toMeeting[], but code likely usesprisma.recallMeetingThis will generate
prisma.meeting, notprisma.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 toRecallMeetingand 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 --skipLibCheckto confirm no errors.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 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: uniquerecallCalendarIdUnique 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:RecallBotStatusenumEnum values cover lifecycle states used by the Recall bot.
There was a problem hiding this 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
processRecallWebhookfunction 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
processRecallWebhookto returnNextResponse | voidand 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
includeto fetch the entireEmailAccountobject, 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
EmailAccountobject 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
updatedAtfrom@updatedAtto@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 @updatedAtapps/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
CANCELLEDin 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
📒 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.tsapps/web/app/api/recall/webhook/route.tsapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/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-actionprovides centralized error handling
Use Zod schemas for validation on both client and server
UserevalidatePathin server actions for cache invalidation
apps/web/utils/actions/**/*.ts: Use server actions (withnext-safe-action) for all mutations (create/update/delete operations); do NOT use POST API routes for mutations.
UserevalidatePathin 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.tsapps/web/prisma/schema.prismaapps/web/app/api/recall/webhook/route.tsapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/web/prisma/migrations/20250930170850_add_recall_meeting_support/migration.sqlapps/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.tsapps/web/app/api/recall/webhook/route.tsapps/web/utils/recall/bot.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}: UsecreateScopedLoggerfor logging in backend TypeScript files
Typically add the logger initialization at the top of the file when usingcreateScopedLogger
Only use.with()on a logger instance within a specific function, not for a global loggerImport 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.tsapps/web/app/api/recall/webhook/route.tsapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/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 thenext-safe-actionlibrary for type safety, input validation, context management, and error handling. Refer toapps/web/utils/actions/safe-action.tsfor client definitions (actionClient,actionClientUser,adminActionClient).
UseactionClientUserwhen only authenticated user context (userId) is needed.
UseactionClientwhen both authenticated user context and a specificemailAccountIdare needed. TheemailAccountIdmust be bound when calling the action from the client.
UseadminActionClientfor actions restricted to admin users.
Access necessary context (likeuserId,emailAccountId, etc.) provided by the safe action client via thectxobject 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.
UseSafeErrorfor expected/handled errors within actions if needed.next-safe-actionprovides centralized error handling.
Use the.metadata({ name: "actionName" })method to provide a meaningful name for monitoring. Sentry instrumentation is automatically applied viawithServerActionInstrumentationwithin the safe action clients.
If an action modifies data displayed elsewhere, userevalidatePathorrevalidateTagfromnext/cachewithin 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.tsapps/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.tsapps/web/utils/recall/bot.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useelements 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.tsapps/web/app/api/recall/webhook/route.tsapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/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.tsapps/web/prisma/schema.prismaapps/web/app/api/recall/webhook/route.tsapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/web/prisma/migrations/20250930170850_add_recall_meeting_support/migration.sqlapps/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.tsapps/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: UsewithAuthfor user-level operations
UsewithEmailAccountfor 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 withwithAuthorwithEmailAccountmiddleware 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).
UsewithEmailAccountmiddleware for API routes that operate on a specific email account (i.e., use or requireemailAccountId).
UsewithAuthmiddleware for API routes that operate at the user level (i.e., use or require onlyuserId).
UsewithErrormiddleware (with proper validation) for public endpoints, custom authentication, or cron endpoints.
Cron endpoints MUST usewithErrormiddleware and validate the cron secret usinghasCronSecret(request)orhasPostCronSecret(request).
Cron endpoints MUST capture unauthorized attempts withcaptureExceptionand 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.
AllfindUniqueandfindFirstPrisma calls in API routes MUST include ownership filters (e.g., userId or emailAccountId).
AllfindManyPrisma 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
Useresult?.serverErrorwithtoastErrorandtoastSuccess
UseLoadingContentcomponent to handle loading and error states consistently
Passloading,error, and children props toLoadingContent
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
onClickmust be client components withuse clientdirective
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
botIdanddeduplicationKeywill 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
scheduleBotForEventvalidatesmeeting_urlbefore 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.
apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
Outdated
Show resolved
Hide resolved
|
Fix conflict |
There was a problem hiding this 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.
There was a problem hiding this 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.
emailAccountcontains 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_urlis 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
updateManycalls. This could be more efficient:
- The initial query includes calendars only for counting, which adds overhead
- Multiple database round-trips (one
updateManyper 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
handleConnectCalendarfunction duplicates the exact same logic found inConnectCalendarButton.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
📒 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.tsxapps/web/app/(app)/[emailAccountId]/calendars/page.tsxapps/web/app/(app)/[emailAccountId]/transcript-settings/page.tsxapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsxapps/web/utils/actions/transcript-settings.tsapps/web/app/api/recall/webhook/types.tsapps/web/utils/scheduled-actions/scheduler.tsapps/web/app/api/recall/webhook/route.tsapps/web/utils/recall/transcript.tsapps/web/app/api/user/calendars/route.tsapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/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.tsxapps/web/app/(app)/[emailAccountId]/calendars/page.tsxapps/web/app/(app)/[emailAccountId]/transcript-settings/page.tsxapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsxapps/web/app/api/recall/webhook/types.tsapps/web/app/api/recall/webhook/route.tsapps/web/app/api/user/calendars/route.tsapps/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
Useresult?.serverErrorwithtoastErrorandtoastSuccess
UseLoadingContentcomponent to handle loading and error states consistently
Passloading,error, and children props toLoadingContent
Files:
apps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsxapps/web/app/(app)/[emailAccountId]/calendars/page.tsxapps/web/app/(app)/[emailAccountId]/transcript-settings/page.tsxapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsxapps/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.tsxapps/web/app/(app)/[emailAccountId]/calendars/page.tsxapps/web/app/(app)/[emailAccountId]/transcript-settings/page.tsxapps/web/prisma/schema.prismaapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsxapps/web/utils/actions/transcript-settings.tsapps/web/prisma/migrations/20251003004957_meeting_status/migration.sqlapps/web/app/api/recall/webhook/types.tsapps/web/utils/scheduled-actions/scheduler.tsapps/web/app/api/recall/webhook/route.tsapps/web/utils/recall/transcript.tsapps/web/app/api/user/calendars/route.tsapps/web/prisma/migrations/20250930170850_add_recall_meeting_support/migration.sqlapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/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.tsxapps/web/app/(app)/[emailAccountId]/calendars/page.tsxapps/web/app/(app)/[emailAccountId]/transcript-settings/page.tsxapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsxapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}: UsecreateScopedLoggerfor logging in backend TypeScript files
Typically add the logger initialization at the top of the file when usingcreateScopedLogger
Only use.with()on a logger instance within a specific function, not for a global loggerImport 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.tsxapps/web/app/(app)/[emailAccountId]/calendars/page.tsxapps/web/app/(app)/[emailAccountId]/transcript-settings/page.tsxapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsxapps/web/utils/actions/transcript-settings.tsapps/web/app/api/recall/webhook/types.tsapps/web/utils/scheduled-actions/scheduler.tsapps/web/app/api/recall/webhook/route.tsapps/web/utils/recall/transcript.tsapps/web/app/api/user/calendars/route.tsapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/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.tsxapps/web/app/(app)/[emailAccountId]/calendars/page.tsxapps/web/app/(app)/[emailAccountId]/transcript-settings/page.tsxapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsxapps/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.tsxapps/web/app/(app)/[emailAccountId]/calendars/page.tsxapps/web/app/(app)/[emailAccountId]/transcript-settings/page.tsxapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsxapps/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.tsxapps/web/app/(app)/[emailAccountId]/calendars/page.tsxapps/web/app/(app)/[emailAccountId]/transcript-settings/page.tsxapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsxapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
apps/web/app/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Components with
onClickmust be client components withuse clientdirective
Files:
apps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsxapps/web/app/(app)/[emailAccountId]/calendars/page.tsxapps/web/app/(app)/[emailAccountId]/transcript-settings/page.tsxapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsxapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useelements 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.tsxapps/web/app/(app)/[emailAccountId]/calendars/page.tsxapps/web/app/(app)/[emailAccountId]/transcript-settings/page.tsxapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsxapps/web/utils/actions/transcript-settings.tsapps/web/app/api/recall/webhook/types.tsapps/web/utils/scheduled-actions/scheduler.tsapps/web/app/api/recall/webhook/route.tsapps/web/utils/recall/transcript.tsapps/web/app/api/user/calendars/route.tsapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/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.tsxapps/web/app/(app)/[emailAccountId]/calendars/page.tsxapps/web/app/(app)/[emailAccountId]/transcript-settings/page.tsxapps/web/prisma/schema.prismaapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsxapps/web/utils/actions/transcript-settings.tsapps/web/prisma/migrations/20251003004957_meeting_status/migration.sqlapps/web/app/api/recall/webhook/types.tsapps/web/utils/scheduled-actions/scheduler.tsapps/web/app/api/recall/webhook/route.tsapps/web/utils/recall/transcript.tsapps/web/app/api/user/calendars/route.tsapps/web/prisma/migrations/20250930170850_add_recall_meeting_support/migration.sqlapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/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.tsxapps/web/app/(app)/[emailAccountId]/calendars/page.tsxapps/web/app/(app)/[emailAccountId]/transcript-settings/page.tsxapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsxapps/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.tsxapps/web/app/(app)/[emailAccountId]/calendars/page.tsxapps/web/app/(app)/[emailAccountId]/transcript-settings/page.tsxapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsxapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/web/prisma/schema.prisma
📄 CodeRabbit inference engine (.cursor/rules/prisma.mdc)
The Prisma schema file must be located at
apps/web/prisma/schema.prismaFiles:
apps/web/prisma/schema.prismaapps/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-actionprovides centralized error handling
Use Zod schemas for validation on both client and server
UserevalidatePathin server actions for cache invalidation
apps/web/utils/actions/**/*.ts: Use server actions (withnext-safe-action) for all mutations (create/update/delete operations); do NOT use POST API routes for mutations.
UserevalidatePathin 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 ZodFiles:
apps/web/utils/actions/transcript-settings.tsapps/web/app/api/recall/webhook/types.tsapps/web/utils/scheduled-actions/scheduler.tsapps/web/app/api/recall/webhook/route.tsapps/web/utils/recall/transcript.tsapps/web/app/api/user/calendars/route.tsapps/web/utils/recall/bot.tsapps/web/utils/actions/*.ts
📄 CodeRabbit inference engine (.cursor/rules/server-actions.mdc)
apps/web/utils/actions/*.ts: Implement all server actions using thenext-safe-actionlibrary for type safety, input validation, context management, and error handling. Refer toapps/web/utils/actions/safe-action.tsfor client definitions (actionClient,actionClientUser,adminActionClient).
UseactionClientUserwhen only authenticated user context (userId) is needed.
UseactionClientwhen both authenticated user context and a specificemailAccountIdare needed. TheemailAccountIdmust be bound when calling the action from the client.
UseadminActionClientfor actions restricted to admin users.
Access necessary context (likeuserId,emailAccountId, etc.) provided by the safe action client via thectxobject 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.
UseSafeErrorfor expected/handled errors within actions if needed.next-safe-actionprovides centralized error handling.
Use the.metadata({ name: "actionName" })method to provide a meaningful name for monitoring. Sentry instrumentation is automatically applied viawithServerActionInstrumentationwithin the safe action clients.
If an action modifies data displayed elsewhere, userevalidatePathorrevalidateTagfromnext/cachewithin the action handler as needed.Server action files must start with
use serverFiles:
apps/web/utils/actions/transcript-settings.tsapps/web/utils/**
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Create utility functions in
utils/folder for reusable logicFiles:
apps/web/utils/actions/transcript-settings.tsapps/web/utils/scheduled-actions/scheduler.tsapps/web/utils/recall/transcript.tsapps/web/utils/recall/bot.tsapps/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 sizeFiles:
apps/web/utils/actions/transcript-settings.tsapps/web/utils/scheduled-actions/scheduler.tsapps/web/utils/recall/transcript.tsapps/web/utils/recall/bot.tsapps/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.tsapps/web/app/api/recall/webhook/route.tsapps/web/app/api/user/calendars/route.tsapps/web/app/api/**/route.ts
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/app/api/**/route.ts: UsewithAuthfor user-level operations
UsewithEmailAccountfor 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 withwithAuthorwithEmailAccountmiddleware 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.tsapps/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).
UsewithEmailAccountmiddleware for API routes that operate on a specific email account (i.e., use or requireemailAccountId).
UsewithAuthmiddleware for API routes that operate at the user level (i.e., use or require onlyuserId).
UsewithErrormiddleware (with proper validation) for public endpoints, custom authentication, or cron endpoints.
Cron endpoints MUST usewithErrormiddleware and validate the cron secret usinghasCronSecret(request)orhasPostCronSecret(request).
Cron endpoints MUST capture unauthorized attempts withcaptureExceptionand 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.
AllfindUniqueandfindFirstPrisma calls in API routes MUST include ownership filters (e.g., userId or emailAccountId).
AllfindManyPrisma 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.tsapps/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.tsxApplied 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 dataApplied 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 statesApplied 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 consistentlyApplied 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 failuresApplied 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
CalendarConnectionsitself, 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 (
recallCalendarIdandtranscriptEnabled) 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 avoidsconsolestatements (addressing previous feedback).
116-202: LGTM!The UI improvements enhance the visual hierarchy and user experience. The
ConfirmDialogprovides 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
200status 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
NextResponsefrom 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
botIdpresence, 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
upsertto 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, butmeeting_urlis 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.
apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx
Outdated
Show resolved
Hide resolved
apps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsx
Show resolved
Hide resolved
There was a problem hiding this 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
emailAccountobject (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
emailAccountand 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
handleConnectCalendarfunction duplicates the logic inConnectCalendarButton(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.tsxandConnectCalendarButton.tsxto 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
updateTranscriptsForAllCalendarsActionkeeps all calendars in sync, edge cases (partial failures, manual DB changes) could lead to inconsistent state being displayed.Consider deriving
transcriptEnabledfrom 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
📒 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.tsxapps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsxapps/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.tsxapps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsxapps/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
Useresult?.serverErrorwithtoastErrorandtoastSuccess
UseLoadingContentcomponent to handle loading and error states consistently
Passloading,error, and children props toLoadingContent
Files:
apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsxapps/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.tsxapps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsxapps/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.tsxapps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}: UsecreateScopedLoggerfor logging in backend TypeScript files
Typically add the logger initialization at the top of the file when usingcreateScopedLogger
Only use.with()on a logger instance within a specific function, not for a global loggerImport 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.tsxapps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsxapps/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.tsxapps/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.tsxapps/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.tsxapps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsx
apps/web/app/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Components with
onClickmust be client components withuse clientdirective
Files:
apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsxapps/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 useelements 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.tsxapps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsxapps/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.tsxapps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsxapps/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.tsxapps/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.tsxapps/web/app/(app)/[emailAccountId]/transcript-settings/TranscriptSettingsManager.tsxapps/web/app/api/**/route.ts
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/app/api/**/route.ts: UsewithAuthfor user-level operations
UsewithEmailAccountfor 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 withwithAuthorwithEmailAccountmiddleware 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 ZodFiles:
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).
UsewithEmailAccountmiddleware for API routes that operate on a specific email account (i.e., use or requireemailAccountId).
UsewithAuthmiddleware for API routes that operate at the user level (i.e., use or require onlyuserId).
UsewithErrormiddleware (with proper validation) for public endpoints, custom authentication, or cron endpoints.
Cron endpoints MUST usewithErrormiddleware and validate the cron secret usinghasCronSecret(request)orhasPostCronSecret(request).
Cron endpoints MUST capture unauthorized attempts withcaptureExceptionand 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.
AllfindUniqueandfindFirstPrisma calls in API routes MUST include ownership filters (e.g., userId or emailAccountId).
AllfindManyPrisma 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.tsapps/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 dataApplied 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 statesApplied 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 consistentlyApplied 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
LoadingContentfor async state handling and provides clear conditional rendering for empty vs. populated states. The use ofEnableFeatureCardfor 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
emailAccountIdis appropriate for next-safe-action'sactionClientpattern. 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
LoadingContentfor loading/error states andSettingCardto 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
withErroris 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_granterrors (returning 200) prevents webhook retry storms when OAuth tokens expire.
198-231: LGTM: Proper response handling.The function correctly returns
NextResponsefrom 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.
Summary by CodeRabbit
New Features
API
Settings
Database
Bug Fixes