feat: handle invalid_grant errors with reconnection emails#1169
feat: handle invalid_grant errors with reconnection emails#1169
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
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. 📝 WalkthroughWalkthroughAdds Account.disconnectedAt, sets it when OAuth tokens are invalidated, clears user error messages when tokens/accounts are updated, sends reconnection emails for watched accounts, and short-circuits webhook/watch flows for disconnected accounts. Changes
Sequence Diagram(s)sequenceDiagram
participant Provider as OAuth Provider
participant System as App Server
participant DB as Database
participant Email as Email Service
participant User as End User
rect rgb(245,248,255)
Note over Provider,System: Token refresh fails (invalid_grant / insufficient_permissions)
Provider->>System: refresh error
System->>System: cleanupInvalidTokens()
end
rect rgb(255,245,240)
System->>DB: fetch emailAccount (includes account.disconnectedAt)
DB-->>System: emailAccount + account.disconnectedAt
alt account.disconnectedAt already set
System->>System: log & short-circuit (no further action)
else account not disconnected
System->>DB: update account set disconnectedAt = now()
DB-->>System: updated account
end
end
rect rgb(240,255,245)
alt reason == invalid_grant and account is watched
System->>System: createUnsubscribeToken()
System->>Email: sendReconnectionEmail(to, props, unsubscribeToken)
Email-->>User: reconnection email with CTA
else watched == false
System->>System: log (skip reconnection email)
end
end
rect rgb(255,250,240)
System->>DB: addUserErrorMessage(userId, ACCOUNT_DISCONNECTED, message)
DB-->>System: stored
System->>System: clearUserErrorMessages(userId) [async]
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
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 |
Handle invalid_grant by marking accounts disconnected, clearing tokens, and sending reconnection emails in
|
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (5)
apps/web/prisma/schema.prisma (1)
28-28: LGTM! Well-designed field addition.The
disconnectedAtfield is appropriately nullable and well-documented. The naming and positioning are logical.For future optimization, consider adding a partial index if the volume of disconnected accounts grows:
@@index([disconnectedAt], map: "idx_account_connected", where: "disconnectedAt IS NULL")This would optimize the filter in
watch-manager.ts(line 40:account: { disconnectedAt: null }). However, given that most accounts remain connected, this optimization can be deferred until performance metrics indicate it's needed.apps/web/utils/email/watch-manager.ts (1)
53-53: Field selected but not used in this file.The
disconnectedAtfield is included in the select statement but isn't referenced in the downstream functions (watchEmailAccounts,watchEmailAccount). This might be for future use or consistency with other queries.If there's no immediate plan to use this field in this file, consider removing it from the select to keep the query lean:
account: { select: { provider: true, access_token: true, refresh_token: true, expires_at: true, - disconnectedAt: true, }, },However, if this is being included for logging, debugging, or upcoming features, it's fine to keep as-is.
apps/web/utils/auth.ts (1)
449-531: Consider harmonizing return values across both code paths.The
emailAccountIdpath (lines 499-505) returnsvoid, while theproviderAccountIdpath (lines 517-530) returns the updated account. For consistency and potential future use cases, consider returning the account (or relevant data) from both paths.🔎 Proposed refactor to return consistent values
if (emailAccountId) { // Encrypt tokens in data directly // Usually we do this in prisma-extensions.ts but we need to do it here because we're updating the account via the emailAccount // We could also edit prisma-extensions.ts to handle this case but this is easier for now if (data.access_token) data.access_token = encryptToken(data.access_token) || undefined; if (data.refresh_token) data.refresh_token = encryptToken(data.refresh_token) || ""; const emailAccount = await prisma.emailAccount.update({ where: { id: emailAccountId }, data: { account: { update: data } }, - select: { userId: true }, + select: { userId: true, account: true }, }); await clearUserErrorMessages({ userId: emailAccount.userId }); + + return emailAccount.account; } else {apps/web/utils/auth.test.ts (1)
116-179: Good test coverage for the happy path.The tests verify the core behavior of
saveTokensfor both code paths (emailAccountId and providerAccountId), ensuring thatdisconnectedAtis cleared and user error messages are properly cleaned up.Consider adding test cases for edge scenarios:
- Behavior when
refresh_tokenis null (should return early per line 475-481 in auth.ts)- Error handling if
clearUserErrorMessagesthrows- Error handling if the database update fails
These additional tests would improve robustness and catch potential regressions.
packages/resend/emails/reconnection.tsx (1)
107-139: Consider using email-safe component instead of div.The Footer component includes a
divat line 120 which may not render consistently across all email clients. Consider using aTextorSectioncomponent instead.🔎 Proposed refactor
- <div className="mt-2"> + <Text className="mt-2"> <Link href={`${baseUrl}/api/unsubscribe?token=${unsubscribeToken}`} className="text-gray-500 underline mr-4" > Unsubscribe </Link> <Link href={`${baseUrl}/support`} className="text-gray-500 underline mr-4" > Support </Link> <Link href={`${baseUrl}/privacy`} className="text-gray-500 underline"> Privacy Policy </Link> - </div> + </Text>
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (14)
apps/web/prisma/migrations/20260101221942_account_disconnected_at/migration.sqlapps/web/prisma/schema.prismaapps/web/utils/auth.test.tsapps/web/utils/auth.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/auth/cleanup-invalid-tokens.tsapps/web/utils/email/reply-all.test.tsapps/web/utils/email/reply-all.tsapps/web/utils/email/watch-manager.tsapps/web/utils/error-messages/index.tsapps/web/utils/webhook/validate-webhook-account.test.tsapps/web/utils/webhook/validate-webhook-account.tspackages/resend/emails/reconnection.tsxpackages/resend/src/send.tsx
🧰 Additional context used
📓 Path-based instructions (20)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/data-fetching.mdc)
**/*.{ts,tsx}: For API GET requests to server, use theswrpackage
Useresult?.serverErrorwithtoastErrorfrom@/components/Toastfor error handling in async operations
**/*.{ts,tsx}: Use wrapper functions for Gmail message operations (get, list, batch, etc.) from @/utils/gmail/message.ts instead of direct API calls
Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls
Use wrapper functions for Gmail label operations from @/utils/gmail/label.ts instead of direct API calls
**/*.{ts,tsx}: For early access feature flags, create hooks using the naming conventionuse[FeatureName]Enabledthat return a boolean fromuseFeatureFlagEnabled("flag-key")
For A/B test variant flags, create hooks using the naming conventionuse[FeatureName]Variantthat define variant types, useuseFeatureFlagVariantKey()with type casting, and provide a default "control" fallback
Use kebab-case for PostHog feature flag keys (e.g.,inbox-cleaner,pricing-options-2)
Always define types for A/B test variant flags (e.g.,type PricingVariant = "control" | "variant-a" | "variant-b") and provide type safety through type casting
**/*.{ts,tsx}: Don't use primitive type aliases or misleading types
Don't use empty type parameters in type aliases and interfaces
Don't use this and super in static contexts
Don't use any or unknown as type constraints
Don't use the TypeScript directive @ts-ignore
Don't use TypeScript enums
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 TypeScript namespaces
Don't use non-null assertions with the!postfix operator
Don't use parameter properties in class constructors
Don't use user-defined types
Useas constinstead of literal types and type annotations
Use eitherT[]orArray<T>consistently
Initialize each enum member value explicitly
Useexport typefor types
Use `impo...
Files:
apps/web/utils/email/watch-manager.tsapps/web/utils/error-messages/index.tsapps/web/utils/auth.tsapps/web/utils/auth/cleanup-invalid-tokens.tsapps/web/utils/webhook/validate-webhook-account.tspackages/resend/src/send.tsxapps/web/utils/email/reply-all.tsapps/web/utils/email/reply-all.test.tspackages/resend/emails/reconnection.tsxapps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/webhook/validate-webhook-account.test.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/prisma-enum-imports.mdc)
Always import Prisma enums from
@/generated/prisma/enumsinstead of@/generated/prisma/clientto avoid Next.js bundling errors in client componentsImport Prisma using the project's centralized utility:
import prisma from '@/utils/prisma'
Files:
apps/web/utils/email/watch-manager.tsapps/web/utils/error-messages/index.tsapps/web/utils/auth.tsapps/web/utils/auth/cleanup-invalid-tokens.tsapps/web/utils/webhook/validate-webhook-account.tspackages/resend/src/send.tsxapps/web/utils/email/reply-all.tsapps/web/utils/email/reply-all.test.tspackages/resend/emails/reconnection.tsxapps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/webhook/validate-webhook-account.test.ts
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Import specific lodash functions rather than entire lodash library to minimize bundle size (e.g.,
import groupBy from 'lodash/groupBy')
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Do not export types/interfaces that are only used within the same file. Export later if needed
Files:
apps/web/utils/email/watch-manager.tsapps/web/utils/error-messages/index.tsapps/web/utils/auth.tsapps/web/utils/auth/cleanup-invalid-tokens.tsapps/web/utils/webhook/validate-webhook-account.tsapps/web/utils/email/reply-all.tsapps/web/utils/email/reply-all.test.tsapps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/webhook/validate-webhook-account.test.ts
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/security.mdc)
**/*.ts: ALL database queries MUST be scoped to the authenticated user/account by including user/account filtering in WHERE clauses to prevent unauthorized data access
Always validate that resources belong to the authenticated user before performing operations, using ownership checks in WHERE clauses or relationships
Always validate all input parameters for type, format, and length before using them in database queries
Use SafeError for error responses to prevent information disclosure. Generic error messages should not reveal internal IDs, logic, or resource ownership details
Only return necessary fields in API responses using Prisma'sselectoption. Never expose sensitive data such as password hashes, private keys, or system flags
Prevent Insecure Direct Object References (IDOR) by validating resource ownership before operations. AllfindUnique/findFirstcalls MUST include ownership filters
Prevent mass assignment vulnerabilities by explicitly whitelisting allowed fields in update operations instead of accepting all user-provided data
Prevent privilege escalation by never allowing users to modify system fields, ownership fields, or admin-only attributes through user input
AllfindManyqueries MUST be scoped to the user's data by including appropriate WHERE filters to prevent returning data from other users
Use Prisma relationships for access control by leveraging nested where clauses (e.g.,emailAccount: { id: emailAccountId }) to validate ownership
Files:
apps/web/utils/email/watch-manager.tsapps/web/utils/error-messages/index.tsapps/web/utils/auth.tsapps/web/utils/auth/cleanup-invalid-tokens.tsapps/web/utils/webhook/validate-webhook-account.tsapps/web/utils/email/reply-all.tsapps/web/utils/email/reply-all.test.tsapps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/webhook/validate-webhook-account.test.ts
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
**/*.{tsx,ts}: Use Shadcn UI and Tailwind for components and styling
Usenext/imagepackage for images
For API GET requests to server, use theswrpackage with hooks likeuseSWRto fetch data
For text inputs, use theInputcomponent withregisterPropsfor form integration and error handling
Files:
apps/web/utils/email/watch-manager.tsapps/web/utils/error-messages/index.tsapps/web/utils/auth.tsapps/web/utils/auth/cleanup-invalid-tokens.tsapps/web/utils/webhook/validate-webhook-account.tspackages/resend/src/send.tsxapps/web/utils/email/reply-all.tsapps/web/utils/email/reply-all.test.tspackages/resend/emails/reconnection.tsxapps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/webhook/validate-webhook-account.test.ts
**/*.{tsx,ts,css}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
Implement responsive design with Tailwind CSS using a mobile-first approach
Files:
apps/web/utils/email/watch-manager.tsapps/web/utils/error-messages/index.tsapps/web/utils/auth.tsapps/web/utils/auth/cleanup-invalid-tokens.tsapps/web/utils/webhook/validate-webhook-account.tspackages/resend/src/send.tsxapps/web/utils/email/reply-all.tsapps/web/utils/email/reply-all.test.tspackages/resend/emails/reconnection.tsxapps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/webhook/validate-webhook-account.test.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useaccessKeyattribute on any HTML element
Don't setaria-hidden="true"on focusable elements
Don't add ARIA roles, states, and properties to elements that don't support them
Don't use distracting elements like<marquee>or<blink>
Only use thescopeprop on<th>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 assigntabIndexto non-interactive HTML elements
Don't use positive integers fortabIndexproperty
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 atitleelement 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
AssigntabIndexto non-interactive HTML elements witharia-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 atypeattribute for button elements
Make elements with interactive roles and handlers focusable
Give heading elements content that's accessible to screen readers (not hidden witharia-hidden)
Always include alangattribute on the html element
Always include atitleattribute for iframe elements
AccompanyonClickwith at least one of:onKeyUp,onKeyDown, oronKeyPress
AccompanyonMouseOver/onMouseOutwithonFocus/onBlur
Include caption tracks for audio and video elements
Use semantic elements instead of role attributes in JSX
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 AR...
Files:
apps/web/utils/email/watch-manager.tsapps/web/utils/error-messages/index.tsapps/web/utils/auth.tsapps/web/utils/auth/cleanup-invalid-tokens.tsapps/web/utils/webhook/validate-webhook-account.tspackages/resend/src/send.tsxapps/web/utils/email/reply-all.tsapps/web/utils/email/reply-all.test.tspackages/resend/emails/reconnection.tsxapps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/webhook/validate-webhook-account.test.ts
!(pages/_document).{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
Don't use the next/head module in pages/_document.js on Next.js projects
Files:
apps/web/utils/email/watch-manager.tsapps/web/utils/error-messages/index.tsapps/web/utils/auth.tsapps/web/prisma/schema.prismaapps/web/utils/auth/cleanup-invalid-tokens.tsapps/web/utils/webhook/validate-webhook-account.tspackages/resend/src/send.tsxapps/web/utils/email/reply-all.tsapps/web/utils/email/reply-all.test.tspackages/resend/emails/reconnection.tsxapps/web/utils/auth.test.tsapps/web/prisma/migrations/20260101221942_account_disconnected_at/migration.sqlapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/webhook/validate-webhook-account.test.ts
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/utilities.mdc)
**/*.{js,ts,jsx,tsx}: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size (e.g.,import groupBy from 'lodash/groupBy')
Files:
apps/web/utils/email/watch-manager.tsapps/web/utils/error-messages/index.tsapps/web/utils/auth.tsapps/web/utils/auth/cleanup-invalid-tokens.tsapps/web/utils/webhook/validate-webhook-account.tspackages/resend/src/send.tsxapps/web/utils/email/reply-all.tsapps/web/utils/email/reply-all.test.tspackages/resend/emails/reconnection.tsxapps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/webhook/validate-webhook-account.test.ts
**/{utils,helpers,lib}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
Logger should be passed as a parameter to helper functions instead of creating their own logger instances
Files:
apps/web/utils/email/watch-manager.tsapps/web/utils/error-messages/index.tsapps/web/utils/auth.tsapps/web/utils/auth/cleanup-invalid-tokens.tsapps/web/utils/webhook/validate-webhook-account.tsapps/web/utils/email/reply-all.tsapps/web/utils/email/reply-all.test.tsapps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/webhook/validate-webhook-account.test.ts
apps/web/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx,js,jsx}: Use@/path aliases for imports from project root
Prefer self-documenting code over comments; use descriptive variable and function names instead of explaining intent with comments
Add helper functions to the bottom of files, not the top
All imports go at the top of files, no mid-file dynamic imports
Files:
apps/web/utils/email/watch-manager.tsapps/web/utils/error-messages/index.tsapps/web/utils/auth.tsapps/web/utils/auth/cleanup-invalid-tokens.tsapps/web/utils/webhook/validate-webhook-account.tsapps/web/utils/email/reply-all.tsapps/web/utils/email/reply-all.test.tsapps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/webhook/validate-webhook-account.test.ts
apps/web/**/*.{ts,tsx,js,jsx,json,css}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Format code with Prettier
Files:
apps/web/utils/email/watch-manager.tsapps/web/utils/error-messages/index.tsapps/web/utils/auth.tsapps/web/utils/auth/cleanup-invalid-tokens.tsapps/web/utils/webhook/validate-webhook-account.tsapps/web/utils/email/reply-all.tsapps/web/utils/email/reply-all.test.tsapps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/webhook/validate-webhook-account.test.ts
apps/web/**/*.{example,ts,json}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Add environment variables to
.env.example,env.ts, andturbo.json
Files:
apps/web/utils/email/watch-manager.tsapps/web/utils/error-messages/index.tsapps/web/utils/auth.tsapps/web/utils/auth/cleanup-invalid-tokens.tsapps/web/utils/webhook/validate-webhook-account.tsapps/web/utils/email/reply-all.tsapps/web/utils/email/reply-all.test.tsapps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/webhook/validate-webhook-account.test.ts
**/prisma/schema.prisma
📄 CodeRabbit inference engine (.cursor/rules/prisma.mdc)
Use PostgreSQL as the database system with Prisma
Files:
apps/web/prisma/schema.prisma
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
**/*.tsx: Use theLoadingContentcomponent to handle loading states instead of manual loading state management
For text areas, use theInputcomponent withtype='text',autosizeTextareaprop set to true, andregisterPropsfor form integration
Files:
packages/resend/src/send.tsxpackages/resend/emails/reconnection.tsx
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{jsx,tsx}: Don't use unnecessary fragments
Don't pass children as props
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 forget key props in iterators and collection literals
Don't define React components inside other components
Don't use event handlers on non-interactive elements
Don't assign to React component props
Don't use bothchildrenanddangerouslySetInnerHTMLprops on the same element
Don't use dangerous JSX props
Don't use Array index in keys
Don't insert comments as text nodes
Don't assign JSX properties multiple times
Don't add extra closing tags for components without children
Use<>...</>instead of<Fragment>...</Fragment>
Watch out for possible "wrong" semicolons inside JSX elements
Make sure void (self-closing) elements don't have children
Don't usetarget="_blank"withoutrel="noopener"
Don't use<img>elements in Next.js projects
Don't use<head>elements in Next.js projects
Files:
packages/resend/src/send.tsxpackages/resend/emails/reconnection.tsx
**/*.{test,spec}.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{test,spec}.{js,jsx,ts,tsx}: Don't nest describe() blocks too deeply in test files
Don't use callbacks in asynchronous tests and hooks
Don't have duplicate hooks in describe blocks
Don't use export or module.exports in test files
Don't use focused tests
Make sure the assertion function, like expect, is placed inside an it() function call
Don't use disabled tests
Files:
apps/web/utils/email/reply-all.test.tsapps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/webhook/validate-webhook-account.test.ts
apps/web/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Co-locate test files next to source files (e.g.,
utils/example.test.ts). Only E2E and AI tests go in__tests__/
Files:
apps/web/utils/email/reply-all.test.tsapps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/webhook/validate-webhook-account.test.ts
**/*.test.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/notes.mdc)
Co-locate test files next to source files (e.g.,
utils/example.test.ts). Only E2E and AI tests go in__tests__/
Files:
apps/web/utils/email/reply-all.test.tsapps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/webhook/validate-webhook-account.test.ts
**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/testing.mdc)
**/*.test.{ts,tsx}: Usevitestas the testing framework
Colocate test files next to the tested file with.test.tsor.test.tsxnaming convention (e.g.,dir/format.tsanddir/format.test.ts)
Mockserver-onlyusingvi.mock("server-only", () => ({}))
Mock Prisma usingvi.mock("@/utils/prisma")and the provided mock from@/utils/__mocks__/prisma
Use test helper functionsgetEmail,getEmailAccount, andgetRulefrom@/__tests__/helpersfor creating mock data
Clear all mocks between tests usingbeforeEach(() => { vi.clearAllMocks(); })
Use descriptive test names that clearly indicate what is being tested
Do not mock the Logger in tests
Files:
apps/web/utils/email/reply-all.test.tsapps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/webhook/validate-webhook-account.test.ts
🧠 Learnings (40)
📚 Learning: 2025-11-25T14:39:27.909Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:27.909Z
Learning: Applies to **/app/api/**/*.ts : Use `withEmailAccount` middleware for operations scoped to a specific email account, including reading/writing emails, rules, schedules, or any operation using `emailAccountId`
Applied to files:
apps/web/utils/email/watch-manager.tsapps/web/utils/auth.tsapps/web/utils/auth/cleanup-invalid-tokens.tsapps/web/utils/webhook/validate-webhook-account.tsapps/web/utils/auth/cleanup-invalid-tokens.test.ts
📚 Learning: 2025-11-25T14:39:08.150Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-11-25T14:39:08.150Z
Learning: Applies to apps/web/app/api/**/*.{ts,tsx} : All database queries must include user scoping with `emailAccountId` or `userId` filtering in WHERE clauses
Applied to files:
apps/web/utils/email/watch-manager.tsapps/web/utils/auth.tsapps/web/utils/auth/cleanup-invalid-tokens.tsapps/web/utils/webhook/validate-webhook-account.ts
📚 Learning: 2025-11-25T14:39:23.326Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:23.326Z
Learning: Applies to app/api/**/*.ts : Use `withEmailAccount` middleware for operations scoped to a specific email account (reading/writing emails, rules, schedules, etc.) - provides `emailAccountId`, `userId`, and `email` in `request.auth`
Applied to files:
apps/web/utils/email/watch-manager.tsapps/web/utils/auth.tsapps/web/utils/auth/cleanup-invalid-tokens.tsapps/web/utils/webhook/validate-webhook-account.tsapps/web/utils/auth.test.ts
📚 Learning: 2025-11-25T14:39:23.326Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:23.326Z
Learning: Applies to **/*.ts : ALL database queries MUST be scoped to the authenticated user/account - include user/account filtering in `where` clauses (e.g., `emailAccountId`, `userId`) to ensure users only access their own resources
Applied to files:
apps/web/utils/email/watch-manager.ts
📚 Learning: 2025-11-25T14:39:04.892Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-11-25T14:39:04.892Z
Learning: Applies to apps/web/app/api/**/route.ts : All database queries must include user/account filtering with `emailAccountId` or `userId` in WHERE clauses to prevent IDOR vulnerabilities
Applied to files:
apps/web/utils/email/watch-manager.tsapps/web/utils/auth/cleanup-invalid-tokens.ts
📚 Learning: 2026-01-01T10:42:29.767Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.767Z
Learning: Applies to **/*.test.{ts,tsx} : Use test helper functions `getEmail`, `getEmailAccount`, and `getRule` from `@/__tests__/helpers` for creating mock data
Applied to files:
apps/web/utils/email/watch-manager.tspackages/resend/src/send.tsxapps/web/utils/email/reply-all.test.tsapps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/webhook/validate-webhook-account.test.ts
📚 Learning: 2025-11-25T14:37:56.430Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Prefer using existing helpers from `@/__tests__/helpers.ts` (`getEmailAccount`, `getEmail`, `getRule`, `getMockMessage`, `getMockExecutedRule`) instead of creating custom test data helpers
Applied to files:
apps/web/utils/email/watch-manager.tsapps/web/utils/email/reply-all.test.tsapps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/webhook/validate-webhook-account.test.ts
📚 Learning: 2025-11-25T14:39:23.326Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:23.326Z
Learning: Applies to app/api/**/*.ts : ALL API routes that handle user data MUST use appropriate middleware: `withEmailAccount` for email-scoped operations, `withAuth` for user-scoped operations, or `withError` with proper validation for public/cron endpoints
Applied to files:
apps/web/utils/auth.tsapps/web/utils/auth/cleanup-invalid-tokens.ts
📚 Learning: 2025-11-25T14:39:27.909Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:27.909Z
Learning: Applies to **/app/api/**/*.ts : ALL API routes that handle user data MUST use appropriate middleware: use `withEmailAccount` for email-scoped operations, use `withAuth` for user-scoped operations, or use `withError` with proper validation for public/custom auth endpoints
Applied to files:
apps/web/utils/auth.tsapps/web/utils/auth/cleanup-invalid-tokens.ts
📚 Learning: 2025-11-25T14:39:49.448Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-11-25T14:39:49.448Z
Learning: Applies to apps/web/utils/actions/*.ts : Use `actionClient` when both authenticated user context and a specific emailAccountId are needed, with emailAccountId bound when calling from the client
Applied to files:
apps/web/utils/auth.tsapps/web/utils/auth/cleanup-invalid-tokens.ts
📚 Learning: 2025-11-25T14:39:27.909Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:27.909Z
Learning: Applies to **/app/api/**/*.ts : Use `withAuth` middleware for user-level operations such as user settings, API keys, and referrals that use only `userId`
Applied to files:
apps/web/utils/auth.tsapps/web/utils/auth.test.ts
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : LLM feature functions must import from `zod` for schema validation, use `createScopedLogger` from `@/utils/logger`, `chatCompletionObject` and `createGenerateObject` from `@/utils/llms`, and import `EmailAccountWithAI` type from `@/utils/llms/types`
Applied to files:
apps/web/utils/auth.tsapps/web/utils/email/reply-all.test.tsapps/web/utils/auth.test.ts
📚 Learning: 2025-11-25T14:37:22.822Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-11-25T14:37:22.822Z
Learning: Applies to **/app/**/route.ts : Do not use try/catch blocks in GET API route handlers when using `withAuth` or `withEmailAccount` middleware, as the middleware handles error handling
Applied to files:
apps/web/utils/auth.ts
📚 Learning: 2025-11-25T14:39:23.326Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:23.326Z
Learning: Applies to **/*.ts : Always validate that resources belong to the authenticated user before any operation - use ownership checks in queries (e.g., `emailAccount: { id: emailAccountId }`) and throw `SafeError` if validation fails
Applied to files:
apps/web/utils/auth/cleanup-invalid-tokens.ts
📚 Learning: 2025-11-25T14:39:23.326Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:23.326Z
Learning: Applies to **/*.ts : Prevent Insecure Direct Object References (IDOR) by validating resource ownership in all queries - always include ownership filters (e.g., `emailAccount: { id: emailAccountId }`) when accessing user-specific resources
Applied to files:
apps/web/utils/auth/cleanup-invalid-tokens.ts
📚 Learning: 2025-07-17T04:19:57.099Z
Learnt from: edulelis
Repo: elie222/inbox-zero PR: 576
File: packages/resend/emails/digest.tsx:78-83
Timestamp: 2025-07-17T04:19:57.099Z
Learning: In packages/resend/emails/digest.tsx, the DigestEmailProps type uses `[key: string]: DigestItem[] | undefined | string | Date | undefined` instead of intersection types like `& Record<string, DigestItem[] | undefined>` due to implementation constraints. This was the initial implementation approach and cannot be changed to more restrictive typing.
Applied to files:
packages/resend/src/send.tsxapps/web/utils/email/reply-all.tspackages/resend/emails/reconnection.tsx
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls
Applied to files:
apps/web/utils/email/reply-all.tsapps/web/utils/email/reply-all.test.ts
📚 Learning: 2025-11-25T14:37:56.430Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Use vitest imports (`describe`, `expect`, `test`, `vi`, `beforeEach`) in LLM test files
Applied to files:
apps/web/utils/email/reply-all.test.tsapps/web/utils/auth.test.tsapps/web/utils/webhook/validate-webhook-account.test.ts
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to apps/web/utils/gmail/**/*.{ts,tsx} : Keep Gmail provider-specific implementation details isolated within the apps/web/utils/gmail/ directory
Applied to files:
apps/web/utils/email/reply-all.test.ts
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail message operations (get, list, batch, etc.) from @/utils/gmail/message.ts instead of direct API calls
Applied to files:
apps/web/utils/email/reply-all.test.ts
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/**/*.test.{ts,tsx} : Co-locate test files next to source files (e.g., `utils/example.test.ts`). Only E2E and AI tests go in `__tests__/`
Applied to files:
apps/web/utils/email/reply-all.test.ts
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail label operations from @/utils/gmail/label.ts instead of direct API calls
Applied to files:
apps/web/utils/email/reply-all.test.ts
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/{utils/ai,utils/llms,__tests__}/**/*.ts : LLM-related code must be organized in specific directories: `apps/web/utils/ai/` for main implementations, `apps/web/utils/llms/` for core utilities and configurations, and `apps/web/__tests__/` for LLM-specific tests
Applied to files:
apps/web/utils/email/reply-all.test.ts
📚 Learning: 2025-11-25T14:40:13.649Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-11-25T14:40:13.649Z
Learning: Applies to **/*.{tsx,ts,jsx,js} : For text inputs in forms, use the `Input` component with `type='email'`, `name`, `label`, `registerProps` from react-hook-form, and `error` props
Applied to files:
packages/resend/emails/reconnection.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/app/api/**/*.ts : Create GET API routes wrapped with `withAuth` or `withEmailAccount` middleware for fetching data
Applied to files:
apps/web/utils/auth.test.ts
📚 Learning: 2025-11-25T14:39:23.326Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:23.326Z
Learning: Applies to app/api/**/*.ts : Use `withAuth` middleware for user-level operations (user settings, API keys, referrals) - provides only `userId` in `request.auth`
Applied to files:
apps/web/utils/auth.test.ts
📚 Learning: 2025-11-25T14:39:23.326Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:23.326Z
Learning: Applies to **/*.test.ts : Include security tests in test suites to verify: authentication is required, IDOR protection works (other users cannot access resources), parameter validation rejects invalid inputs, and error messages don't leak information
Applied to files:
apps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/webhook/validate-webhook-account.test.ts
📚 Learning: 2025-11-25T14:37:09.306Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-11-25T14:37:09.306Z
Learning: Applies to apps/web/utils/actions/*.ts : Server actions should use 'use server' directive and automatically receive authentication context (`emailAccountId`) from the `actionClient`
Applied to files:
apps/web/utils/auth.test.ts
📚 Learning: 2026-01-01T10:42:29.767Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.767Z
Learning: Applies to **/*.test.{ts,tsx} : Mock Prisma using `vi.mock("@/utils/prisma")` and the provided mock from `@/utils/__mocks__/prisma`
Applied to files:
apps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/webhook/validate-webhook-account.test.ts
📚 Learning: 2025-11-25T14:42:11.919Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-11-25T14:42:11.919Z
Learning: Applies to utils/**/*.{js,ts,jsx,tsx} : The `utils` folder contains core app logic such as Next.js Server Actions and Gmail API requests
Applied to files:
apps/web/utils/auth.test.ts
📚 Learning: 2025-11-25T14:42:16.602Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-11-25T14:42:16.602Z
Learning: The `utils` folder contains core app logic such as Next.js Server Actions and Gmail API requests
Applied to files:
apps/web/utils/auth.test.ts
📚 Learning: 2025-11-25T14:37:11.434Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-11-25T14:37:11.434Z
Learning: Applies to **/app/**/route.ts : Always wrap GET API route handlers with `withAuth` or `withEmailAccount` middleware for consistent error handling and authentication in Next.js App Router
Applied to files:
apps/web/utils/auth.test.ts
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/{pages,routes,components}/**/*.{ts,tsx} : Never call Gmail API directly from routes or components - always use wrapper functions from the utils folder
Applied to files:
apps/web/utils/auth.test.ts
📚 Learning: 2025-11-25T14:37:56.430Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Mock 'server-only' module with empty object in LLM test files: `vi.mock("server-only", () => ({}))`
Applied to files:
apps/web/utils/auth.test.tsapps/web/utils/webhook/validate-webhook-account.test.ts
📚 Learning: 2026-01-01T10:42:29.767Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.767Z
Learning: Applies to **/*.test.{ts,tsx} : Clear all mocks between tests using `beforeEach(() => { vi.clearAllMocks(); })`
Applied to files:
apps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/webhook/validate-webhook-account.test.ts
📚 Learning: 2026-01-01T10:42:29.767Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.767Z
Learning: Applies to **/*.test.{ts,tsx} : Use descriptive test names that clearly indicate what is being tested
Applied to files:
apps/web/utils/auth/cleanup-invalid-tokens.test.ts
📚 Learning: 2026-01-01T10:42:29.767Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.767Z
Learning: Applies to **/*.test.{ts,tsx} : Mock `server-only` using `vi.mock("server-only", () => ({}))`
Applied to files:
apps/web/utils/webhook/validate-webhook-account.test.ts
📚 Learning: 2026-01-01T10:42:29.767Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.767Z
Learning: Applies to **/*.test.{ts,tsx} : Do not mock the Logger in tests
Applied to files:
apps/web/utils/webhook/validate-webhook-account.test.ts
📚 Learning: 2025-12-18T16:37:47.972Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/logging.mdc:0-0
Timestamp: 2025-12-18T16:37:47.972Z
Learning: Applies to **/{scripts,tests,__tests__}/**/*.{ts,tsx} : Use createScopedLogger only for code that doesn't run within a middleware chain, such as standalone scripts or tests
Applied to files:
apps/web/utils/webhook/validate-webhook-account.test.ts
📚 Learning: 2026-01-01T10:42:29.767Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.767Z
Learning: Mock external dependencies in tests
Applied to files:
apps/web/utils/webhook/validate-webhook-account.test.ts
🧬 Code graph analysis (5)
apps/web/utils/auth.ts (1)
apps/web/utils/error-messages/index.ts (1)
clearUserErrorMessages(52-61)
apps/web/utils/auth/cleanup-invalid-tokens.ts (4)
apps/web/utils/unsubscribe.ts (1)
createUnsubscribeToken(5-21)packages/resend/src/send.tsx (1)
sendReconnectionEmail(182-207)apps/web/env.ts (1)
env(17-258)apps/web/utils/error-messages/index.ts (2)
addUserErrorMessage(25-50)ErrorType(63-70)
packages/resend/src/send.tsx (1)
packages/resend/emails/reconnection.tsx (1)
ReconnectionEmailProps(16-20)
apps/web/utils/email/reply-all.ts (1)
apps/web/utils/email.ts (1)
extractEmailAddress(32-65)
apps/web/utils/webhook/validate-webhook-account.test.ts (1)
apps/web/utils/webhook/validate-webhook-account.ts (1)
validateWebhookAccount(118-200)
⏰ 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). (5)
- GitHub Check: Baz Reviewer
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: cubic · AI code reviewer
- GitHub Check: test
- GitHub Check: Macroscope - Correctness Check
🔇 Additional comments (24)
apps/web/utils/email/reply-all.ts (1)
99-101: LGTM! Formatting improvement.The multi-line format for the Set construction improves readability without changing behavior.
apps/web/utils/email/reply-all.test.ts (1)
2-6: LGTM! Formatting improvement.The multi-line import format improves maintainability without changing test behavior.
apps/web/prisma/migrations/20260101221942_account_disconnected_at/migration.sql (1)
1-2: LGTM! Clean migration.The migration correctly adds the nullable
disconnectedAtcolumn with appropriate precision (TIMESTAMP(3) for milliseconds). Existing accounts will have NULL values, correctly indicating they are connected.apps/web/utils/email/watch-manager.ts (1)
40-40: LGTM! Essential filter for disconnected accounts.Correctly excludes accounts with revoked OAuth tokens from the watch operation. This prevents unnecessary API calls and auth errors for disconnected accounts.
apps/web/utils/error-messages/index.ts (1)
69-69: LGTM!The new error type constant follows the established pattern and provides a clear, user-facing message for account disconnection scenarios.
apps/web/utils/auth.ts (1)
406-421: Well-implemented atomic operation.Using a transaction to atomically upsert the email account and clear the disconnection state, followed by clearing user error messages, ensures consistency and proper cleanup when an account is reconnected.
apps/web/utils/webhook/validate-webhook-account.ts (2)
32-32: Necessary field addition for disconnection tracking.Adding
disconnectedAtto the select clause enables the validation logic to check account disconnection state.
127-130: Proper early exit for disconnected accounts.The guard correctly skips webhook processing for disconnected accounts, preventing operations on accounts with revoked OAuth tokens. The pattern is consistent with other validation bailouts (non-premium, no AI access, etc.).
apps/web/utils/webhook/validate-webhook-account.test.ts (3)
16-16: Good cleanup of placeholder comment.Replacing the commented note with actual imports improves code clarity.
99-118: Comprehensive test coverage for disconnected accounts.The test verifies that disconnected accounts are properly handled with an early exit, ensuring webhook processing is skipped for accounts with revoked OAuth tokens. The test follows the established pattern and properly validates the response structure.
49-49: Proper test data normalization.Adding
disconnectedAt: nullto existing mock account objects ensures consistency across all test cases and makes the disconnection state explicit.Also applies to: 204-204, 228-228
packages/resend/src/send.tsx (2)
13-15: LGTM!Import statements are properly structured.
182-207: LGTM!The
sendReconnectionEmailfunction follows the established pattern used by other email sender functions in this file. The implementation correctly uses theReconnectionEmailPropstype, includes the email address in the subject line, properly passes the unsubscribe token, and categorizes the email appropriately.apps/web/utils/auth/cleanup-invalid-tokens.ts (5)
3-6: LGTM!Imports are correctly structured for the new functionality.
27-37: LGTM!The expanded select query correctly retrieves all necessary fields for the disconnection flow, including the new
disconnectedAtfield from the related account.
45-48: LGTM!Good defensive programming - the early return prevents duplicate processing and unnecessary operations when an account is already marked as disconnected.
50-58: LGTM!The account update correctly sets
disconnectedAtalong with clearing the OAuth tokens. This properly marks the account as disconnected in the database.
60-96: LGTM!The invalid_grant handling logic is well-structured:
- Conditionally sends reconnection email only for watched accounts
- Properly handles errors during email sending without blocking the flow
- Always adds a user error message to surface the disconnection in-app
NEXT_PUBLIC_BASE_URLis properly configured and available in the server-side context. TheNEXT_PUBLIC_*prefix is specifically designed to expose variables to both server and client in Next.js, and the env configuration correctly maps it throughexperimental__runtimeEnv.packages/resend/emails/reconnection.tsx (4)
16-20: LGTM!The
ReconnectionEmailPropstype is correctly defined with all necessary fields for the reconnection email flow.
22-30: LGTM!The component type definition and default props follow the pattern established in other email templates in this codebase.
31-103: LGTM!The email template is well-structured with clear sections (header, main content, CTA, footer) and uses React Email components appropriately. The reconnect URL construction and button styling are correct.
141-145: LGTM!Preview props are correctly defined for development and testing purposes.
apps/web/utils/auth/cleanup-invalid-tokens.test.ts (2)
1-20: LGTM!Test setup follows the coding guidelines correctly:
- Uses vitest as the testing framework
- Mocks Prisma using the provided mock
- Properly mocks external dependencies
- Does not mock the Logger (as per guidelines)
22-104: LGTM!The test suite provides comprehensive coverage:
- Properly clears mocks between tests using
beforeEach- Uses descriptive test names that clearly indicate what is being tested
- Covers all critical paths: watched/unwatched accounts, already disconnected, and different reasons
- Verifies both positive cases (email sent) and negative cases (email not sent)
- Properly validates database interactions and side effects
The test implementation aligns with all coding guidelines and best practices.
There was a problem hiding this comment.
5 issues found across 14 files
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="apps/web/utils/auth.ts">
<violation number="1" location="apps/web/utils/auth.ts:421">
P2: `clearUserErrorMessages` is non-critical, but if it throws, the entire `handleLinkAccount` fails after the account was already successfully linked. This could cause the user to see an error even though their account was linked correctly. Consider making this call best-effort by catching and logging any errors.</violation>
</file>
<file name="packages/resend/emails/reconnection.tsx">
<violation number="1" location="packages/resend/emails/reconnection.tsx:122">
P2: The `unsubscribeToken` should be URL-encoded with `encodeURIComponent()` to prevent broken URLs if the token contains special characters. This matches the pattern used in `summary.tsx`.</violation>
</file>
<file name="apps/web/utils/auth/cleanup-invalid-tokens.ts">
<violation number="1" location="apps/web/utils/auth/cleanup-invalid-tokens.ts:45">
P1: The disconnected guard is non-atomic; concurrent calls can both pass the check and send duplicate reconnection emails/notifications. Make the disconnect update conditional (e.g., updateMany with `disconnectedAt: null`) and bail if no rows were updated.</violation>
<violation number="2" location="apps/web/utils/auth/cleanup-invalid-tokens.ts:61">
P2: `isWatched` should account for expiration, not just non-null. Otherwise expired watch dates can still trigger reconnection emails.</violation>
<violation number="3" location="apps/web/utils/auth/cleanup-invalid-tokens.ts:91">
P2: `addUserErrorMessage` receives `emailAccount.email`, but this function looks up users by their email. The connected email account's email may differ from the user's primary email, causing the error message to be dropped or misattributed. Consider fetching the owning user's email or updating `addUserErrorMessage` to accept `userId` directly.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/web/utils/meeting-briefs/recipient-context.test.ts (1)
53-60: Remove the logger mock.The coding guidelines explicitly state: "Do not mock the Logger in tests". Pass a real logger instance instead of creating a mock.
As per coding guidelines: "Do not mock the Logger in tests"
♻️ Duplicate comments (1)
apps/web/utils/auth/cleanup-invalid-tokens.ts (1)
62-62: Verify watch expiration, not just non-null status.The
isWatchedcheck should verify that the watch hasn't expired, not just that the field is non-null. An expired watch means the account is no longer actively monitored.🔎 Proposed fix
- const isWatched = !!emailAccount.watchEmailsExpirationDate; + const isWatched = + !!emailAccount.watchEmailsExpirationDate && + emailAccount.watchEmailsExpirationDate > new Date();
🧹 Nitpick comments (2)
apps/web/utils/meeting-briefs/recipient-context.test.ts (1)
98-98: Remove console.log statements from tests.Multiple
console.logstatements are present in the test file. Per coding guidelines, console usage should be avoided. These debugging statements should be removed or replaced with proper test assertions if the output needs to be verified.Remove console.log statements
- // Readable prompt text: - console.log("Prompt:", result); -Apply this pattern to remove all console.log statements at lines 98, 126, 157, 189, 211, and 229.
As per coding guidelines: "Don't use console"
Also applies to: 126-126, 157-157, 189-189, 211-211, 229-229
apps/web/utils/auth/cleanup-invalid-tokens.ts (1)
46-59: Consider atomic update to prevent race conditions.The current guard-then-update pattern could allow concurrent calls to both pass the check and send duplicate reconnection emails. Consider using an atomic update with a conditional where clause as suggested in past reviews:
const updated = await prisma.account.updateMany({ where: { id: emailAccount.accountId, disconnectedAt: null }, data: { access_token: null, refresh_token: null, expires_at: null, disconnectedAt: new Date(), }, }); if (updated.count === 0) { logger.info("Account already marked as disconnected"); return; }This ensures only one concurrent call proceeds to send reconnection emails.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
apps/web/utils/ai/meeting-briefs/generate-briefing.test.tsapps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/auth/cleanup-invalid-tokens.tsapps/web/utils/error-messages/index.tsapps/web/utils/llms/index.tsapps/web/utils/meeting-briefs/recipient-context.test.tspackages/resend/emails/reconnection.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/resend/emails/reconnection.tsx
🧰 Additional context used
📓 Path-based instructions (20)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/data-fetching.mdc)
**/*.{ts,tsx}: For API GET requests to server, use theswrpackage
Useresult?.serverErrorwithtoastErrorfrom@/components/Toastfor error handling in async operations
**/*.{ts,tsx}: Use wrapper functions for Gmail message operations (get, list, batch, etc.) from @/utils/gmail/message.ts instead of direct API calls
Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls
Use wrapper functions for Gmail label operations from @/utils/gmail/label.ts instead of direct API calls
**/*.{ts,tsx}: For early access feature flags, create hooks using the naming conventionuse[FeatureName]Enabledthat return a boolean fromuseFeatureFlagEnabled("flag-key")
For A/B test variant flags, create hooks using the naming conventionuse[FeatureName]Variantthat define variant types, useuseFeatureFlagVariantKey()with type casting, and provide a default "control" fallback
Use kebab-case for PostHog feature flag keys (e.g.,inbox-cleaner,pricing-options-2)
Always define types for A/B test variant flags (e.g.,type PricingVariant = "control" | "variant-a" | "variant-b") and provide type safety through type casting
**/*.{ts,tsx}: Don't use primitive type aliases or misleading types
Don't use empty type parameters in type aliases and interfaces
Don't use this and super in static contexts
Don't use any or unknown as type constraints
Don't use the TypeScript directive @ts-ignore
Don't use TypeScript enums
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 TypeScript namespaces
Don't use non-null assertions with the!postfix operator
Don't use parameter properties in class constructors
Don't use user-defined types
Useas constinstead of literal types and type annotations
Use eitherT[]orArray<T>consistently
Initialize each enum member value explicitly
Useexport typefor types
Use `impo...
Files:
apps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/ai/meeting-briefs/generate-briefing.test.tsapps/web/utils/llms/index.tsapps/web/utils/meeting-briefs/recipient-context.test.tsapps/web/utils/error-messages/index.tsapps/web/utils/auth/cleanup-invalid-tokens.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/prisma-enum-imports.mdc)
Always import Prisma enums from
@/generated/prisma/enumsinstead of@/generated/prisma/clientto avoid Next.js bundling errors in client componentsImport Prisma using the project's centralized utility:
import prisma from '@/utils/prisma'
Files:
apps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/ai/meeting-briefs/generate-briefing.test.tsapps/web/utils/llms/index.tsapps/web/utils/meeting-briefs/recipient-context.test.tsapps/web/utils/error-messages/index.tsapps/web/utils/auth/cleanup-invalid-tokens.ts
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Import specific lodash functions rather than entire lodash library to minimize bundle size (e.g.,
import groupBy from 'lodash/groupBy')
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Do not export types/interfaces that are only used within the same file. Export later if needed
Files:
apps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/ai/meeting-briefs/generate-briefing.test.tsapps/web/utils/llms/index.tsapps/web/utils/meeting-briefs/recipient-context.test.tsapps/web/utils/error-messages/index.tsapps/web/utils/auth/cleanup-invalid-tokens.ts
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/security.mdc)
**/*.ts: ALL database queries MUST be scoped to the authenticated user/account by including user/account filtering in WHERE clauses to prevent unauthorized data access
Always validate that resources belong to the authenticated user before performing operations, using ownership checks in WHERE clauses or relationships
Always validate all input parameters for type, format, and length before using them in database queries
Use SafeError for error responses to prevent information disclosure. Generic error messages should not reveal internal IDs, logic, or resource ownership details
Only return necessary fields in API responses using Prisma'sselectoption. Never expose sensitive data such as password hashes, private keys, or system flags
Prevent Insecure Direct Object References (IDOR) by validating resource ownership before operations. AllfindUnique/findFirstcalls MUST include ownership filters
Prevent mass assignment vulnerabilities by explicitly whitelisting allowed fields in update operations instead of accepting all user-provided data
Prevent privilege escalation by never allowing users to modify system fields, ownership fields, or admin-only attributes through user input
AllfindManyqueries MUST be scoped to the user's data by including appropriate WHERE filters to prevent returning data from other users
Use Prisma relationships for access control by leveraging nested where clauses (e.g.,emailAccount: { id: emailAccountId }) to validate ownership
Files:
apps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/ai/meeting-briefs/generate-briefing.test.tsapps/web/utils/llms/index.tsapps/web/utils/meeting-briefs/recipient-context.test.tsapps/web/utils/error-messages/index.tsapps/web/utils/auth/cleanup-invalid-tokens.ts
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
**/*.{tsx,ts}: Use Shadcn UI and Tailwind for components and styling
Usenext/imagepackage for images
For API GET requests to server, use theswrpackage with hooks likeuseSWRto fetch data
For text inputs, use theInputcomponent withregisterPropsfor form integration and error handling
Files:
apps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/ai/meeting-briefs/generate-briefing.test.tsapps/web/utils/llms/index.tsapps/web/utils/meeting-briefs/recipient-context.test.tsapps/web/utils/error-messages/index.tsapps/web/utils/auth/cleanup-invalid-tokens.ts
**/*.{tsx,ts,css}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
Implement responsive design with Tailwind CSS using a mobile-first approach
Files:
apps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/ai/meeting-briefs/generate-briefing.test.tsapps/web/utils/llms/index.tsapps/web/utils/meeting-briefs/recipient-context.test.tsapps/web/utils/error-messages/index.tsapps/web/utils/auth/cleanup-invalid-tokens.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useaccessKeyattribute on any HTML element
Don't setaria-hidden="true"on focusable elements
Don't add ARIA roles, states, and properties to elements that don't support them
Don't use distracting elements like<marquee>or<blink>
Only use thescopeprop on<th>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 assigntabIndexto non-interactive HTML elements
Don't use positive integers fortabIndexproperty
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 atitleelement 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
AssigntabIndexto non-interactive HTML elements witharia-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 atypeattribute for button elements
Make elements with interactive roles and handlers focusable
Give heading elements content that's accessible to screen readers (not hidden witharia-hidden)
Always include alangattribute on the html element
Always include atitleattribute for iframe elements
AccompanyonClickwith at least one of:onKeyUp,onKeyDown, oronKeyPress
AccompanyonMouseOver/onMouseOutwithonFocus/onBlur
Include caption tracks for audio and video elements
Use semantic elements instead of role attributes in JSX
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 AR...
Files:
apps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/ai/meeting-briefs/generate-briefing.test.tsapps/web/utils/llms/index.tsapps/web/utils/meeting-briefs/recipient-context.test.tsapps/web/utils/error-messages/index.tsapps/web/utils/auth/cleanup-invalid-tokens.ts
**/*.{test,spec}.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{test,spec}.{js,jsx,ts,tsx}: Don't nest describe() blocks too deeply in test files
Don't use callbacks in asynchronous tests and hooks
Don't have duplicate hooks in describe blocks
Don't use export or module.exports in test files
Don't use focused tests
Make sure the assertion function, like expect, is placed inside an it() function call
Don't use disabled tests
Files:
apps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/ai/meeting-briefs/generate-briefing.test.tsapps/web/utils/meeting-briefs/recipient-context.test.ts
!(pages/_document).{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
Don't use the next/head module in pages/_document.js on Next.js projects
Files:
apps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/ai/meeting-briefs/generate-briefing.test.tsapps/web/utils/llms/index.tsapps/web/utils/meeting-briefs/recipient-context.test.tsapps/web/utils/error-messages/index.tsapps/web/utils/auth/cleanup-invalid-tokens.ts
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/utilities.mdc)
**/*.{js,ts,jsx,tsx}: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size (e.g.,import groupBy from 'lodash/groupBy')
Files:
apps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/ai/meeting-briefs/generate-briefing.test.tsapps/web/utils/llms/index.tsapps/web/utils/meeting-briefs/recipient-context.test.tsapps/web/utils/error-messages/index.tsapps/web/utils/auth/cleanup-invalid-tokens.ts
**/{utils,helpers,lib}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
Logger should be passed as a parameter to helper functions instead of creating their own logger instances
Files:
apps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/ai/meeting-briefs/generate-briefing.test.tsapps/web/utils/llms/index.tsapps/web/utils/meeting-briefs/recipient-context.test.tsapps/web/utils/error-messages/index.tsapps/web/utils/auth/cleanup-invalid-tokens.ts
apps/web/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx,js,jsx}: Use@/path aliases for imports from project root
Prefer self-documenting code over comments; use descriptive variable and function names instead of explaining intent with comments
Add helper functions to the bottom of files, not the top
All imports go at the top of files, no mid-file dynamic imports
Files:
apps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/ai/meeting-briefs/generate-briefing.test.tsapps/web/utils/llms/index.tsapps/web/utils/meeting-briefs/recipient-context.test.tsapps/web/utils/error-messages/index.tsapps/web/utils/auth/cleanup-invalid-tokens.ts
apps/web/**/*.{ts,tsx,js,jsx,json,css}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Format code with Prettier
Files:
apps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/ai/meeting-briefs/generate-briefing.test.tsapps/web/utils/llms/index.tsapps/web/utils/meeting-briefs/recipient-context.test.tsapps/web/utils/error-messages/index.tsapps/web/utils/auth/cleanup-invalid-tokens.ts
apps/web/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Co-locate test files next to source files (e.g.,
utils/example.test.ts). Only E2E and AI tests go in__tests__/
Files:
apps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/ai/meeting-briefs/generate-briefing.test.tsapps/web/utils/meeting-briefs/recipient-context.test.ts
apps/web/**/*.{example,ts,json}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Add environment variables to
.env.example,env.ts, andturbo.json
Files:
apps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/ai/meeting-briefs/generate-briefing.test.tsapps/web/utils/llms/index.tsapps/web/utils/meeting-briefs/recipient-context.test.tsapps/web/utils/error-messages/index.tsapps/web/utils/auth/cleanup-invalid-tokens.ts
**/*.test.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/notes.mdc)
Co-locate test files next to source files (e.g.,
utils/example.test.ts). Only E2E and AI tests go in__tests__/
Files:
apps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/ai/meeting-briefs/generate-briefing.test.tsapps/web/utils/meeting-briefs/recipient-context.test.ts
**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/testing.mdc)
**/*.test.{ts,tsx}: Usevitestas the testing framework
Colocate test files next to the tested file with.test.tsor.test.tsxnaming convention (e.g.,dir/format.tsanddir/format.test.ts)
Mockserver-onlyusingvi.mock("server-only", () => ({}))
Mock Prisma usingvi.mock("@/utils/prisma")and the provided mock from@/utils/__mocks__/prisma
Use test helper functionsgetEmail,getEmailAccount, andgetRulefrom@/__tests__/helpersfor creating mock data
Clear all mocks between tests usingbeforeEach(() => { vi.clearAllMocks(); })
Use descriptive test names that clearly indicate what is being tested
Do not mock the Logger in tests
Files:
apps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/ai/meeting-briefs/generate-briefing.test.tsapps/web/utils/meeting-briefs/recipient-context.test.ts
apps/web/{utils/ai,utils/llms,__tests__}/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/llm.mdc)
LLM-related code must be organized in specific directories:
apps/web/utils/ai/for main implementations,apps/web/utils/llms/for core utilities and configurations, andapps/web/__tests__/for LLM-specific tests
Files:
apps/web/utils/ai/meeting-briefs/generate-briefing.test.tsapps/web/utils/llms/index.ts
apps/web/utils/ai/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/llm.mdc)
apps/web/utils/ai/**/*.ts: LLM feature functions must import fromzodfor schema validation, usecreateScopedLoggerfrom@/utils/logger,chatCompletionObjectandcreateGenerateObjectfrom@/utils/llms, and importEmailAccountWithAItype from@/utils/llms/types
LLM feature functions must follow a standard structure: accept options withinputDataandemailAccountparameters, implement input validation with early returns, define separate system and user prompts, create a Zod schema for response validation, and usecreateGenerateObjectto execute the LLM call
System prompts must define the LLM's role and task specifications
User prompts must contain the actual data and context, and should be kept separate from system prompts
Always define a Zod schema for LLM response validation and make schemas as specific as possible to guide the LLM output
Use descriptive scoped loggers for each LLM feature, log inputs and outputs with appropriate log levels, and include relevant context in log messages
Implement early returns for invalid LLM inputs, use proper error types and logging, implement fallbacks for AI failures, and add retry logic for transient failures usingwithRetry
Use XML-like tags to structure data in prompts, remove excessive whitespace and truncate long inputs, and format data consistently across similar LLM functions
Use TypeScript types for all LLM function parameters and return values, and define clear interfaces for complex input/output structures
Keep related AI functions in the same file or directory, extract common patterns into utility functions, and document complex AI logic with clear comments
Files:
apps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
apps/web/utils/llms/{index,model}.ts
📄 CodeRabbit inference engine (.cursor/rules/llm.mdc)
Core LLM functionality must be defined in
utils/llms/index.ts, model definitions and configurations inutils/llms/model.ts, and usage tracking inutils/usage.ts
Files:
apps/web/utils/llms/index.ts
🧠 Learnings (47)
📚 Learning: 2026-01-01T10:42:29.767Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.767Z
Learning: Applies to **/*.test.{ts,tsx} : Use test helper functions `getEmail`, `getEmailAccount`, and `getRule` from `@/__tests__/helpers` for creating mock data
Applied to files:
apps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/ai/meeting-briefs/generate-briefing.test.tsapps/web/utils/meeting-briefs/recipient-context.test.ts
📚 Learning: 2025-11-25T14:37:56.430Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Prefer using existing helpers from `@/__tests__/helpers.ts` (`getEmailAccount`, `getEmail`, `getRule`, `getMockMessage`, `getMockExecutedRule`) instead of creating custom test data helpers
Applied to files:
apps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/ai/meeting-briefs/generate-briefing.test.tsapps/web/utils/meeting-briefs/recipient-context.test.ts
📚 Learning: 2025-11-25T14:39:23.326Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:23.326Z
Learning: Applies to **/*.test.ts : Include security tests in test suites to verify: authentication is required, IDOR protection works (other users cannot access resources), parameter validation rejects invalid inputs, and error messages don't leak information
Applied to files:
apps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.ts
📚 Learning: 2025-11-25T14:39:23.326Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:23.326Z
Learning: Applies to app/api/**/*.ts : Use `withEmailAccount` middleware for operations scoped to a specific email account (reading/writing emails, rules, schedules, etc.) - provides `emailAccountId`, `userId`, and `email` in `request.auth`
Applied to files:
apps/web/utils/auth.test.tsapps/web/utils/llms/index.tsapps/web/utils/auth/cleanup-invalid-tokens.ts
📚 Learning: 2025-11-25T14:39:27.909Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:27.909Z
Learning: Applies to **/app/api/**/*.ts : Use `withAuth` middleware for user-level operations such as user settings, API keys, and referrals that use only `userId`
Applied to files:
apps/web/utils/auth.test.ts
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/app/api/**/*.ts : Create GET API routes wrapped with `withAuth` or `withEmailAccount` middleware for fetching data
Applied to files:
apps/web/utils/auth.test.ts
📚 Learning: 2026-01-01T10:42:29.767Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.767Z
Learning: Applies to **/*.test.{ts,tsx} : Mock Prisma using `vi.mock("@/utils/prisma")` and the provided mock from `@/utils/__mocks__/prisma`
Applied to files:
apps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/ai/meeting-briefs/generate-briefing.test.tsapps/web/utils/meeting-briefs/recipient-context.test.tsapps/web/utils/error-messages/index.ts
📚 Learning: 2025-11-25T14:42:11.919Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-11-25T14:42:11.919Z
Learning: Applies to utils/**/*.{js,ts,jsx,tsx} : The `utils` folder contains core app logic such as Next.js Server Actions and Gmail API requests
Applied to files:
apps/web/utils/auth.test.ts
📚 Learning: 2025-11-25T14:42:16.602Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-11-25T14:42:16.602Z
Learning: The `utils` folder contains core app logic such as Next.js Server Actions and Gmail API requests
Applied to files:
apps/web/utils/auth.test.ts
📚 Learning: 2025-11-25T14:37:11.434Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-11-25T14:37:11.434Z
Learning: Applies to **/app/**/route.ts : Always wrap GET API route handlers with `withAuth` or `withEmailAccount` middleware for consistent error handling and authentication in Next.js App Router
Applied to files:
apps/web/utils/auth.test.ts
📚 Learning: 2025-11-25T14:39:23.326Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:23.326Z
Learning: Applies to app/api/**/*.ts : Use `withAuth` middleware for user-level operations (user settings, API keys, referrals) - provides only `userId` in `request.auth`
Applied to files:
apps/web/utils/auth.test.ts
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/{pages,routes,components}/**/*.{ts,tsx} : Never call Gmail API directly from routes or components - always use wrapper functions from the utils folder
Applied to files:
apps/web/utils/auth.test.ts
📚 Learning: 2025-11-25T14:37:56.430Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Use vitest imports (`describe`, `expect`, `test`, `vi`, `beforeEach`) in LLM test files
Applied to files:
apps/web/utils/auth.test.tsapps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
📚 Learning: 2025-11-25T14:37:56.430Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Mock 'server-only' module with empty object in LLM test files: `vi.mock("server-only", () => ({}))`
Applied to files:
apps/web/utils/auth.test.tsapps/web/utils/ai/meeting-briefs/generate-briefing.test.tsapps/web/utils/meeting-briefs/recipient-context.test.ts
📚 Learning: 2026-01-01T10:42:29.767Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.767Z
Learning: Applies to **/*.test.{ts,tsx} : Clear all mocks between tests using `beforeEach(() => { vi.clearAllMocks(); })`
Applied to files:
apps/web/utils/auth.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/ai/meeting-briefs/generate-briefing.test.tsapps/web/utils/meeting-briefs/recipient-context.test.ts
📚 Learning: 2026-01-01T10:42:29.767Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.767Z
Learning: Applies to **/*.test.{ts,tsx} : Use descriptive test names that clearly indicate what is being tested
Applied to files:
apps/web/utils/auth/cleanup-invalid-tokens.test.ts
📚 Learning: 2025-11-25T14:37:56.430Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Place all LLM-related tests in `apps/web/__tests__/` directory
Applied to files:
apps/web/utils/auth/cleanup-invalid-tokens.test.ts
📚 Learning: 2025-11-25T14:37:56.430Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Use `console.debug()` for outputting generated LLM content in tests, e.g., `console.debug("Generated content:\n", result.content);`
Applied to files:
apps/web/utils/auth/cleanup-invalid-tokens.test.ts
📚 Learning: 2025-11-25T14:37:56.430Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Set timeout constant `const TIMEOUT = 15_000;` for LLM tests
Applied to files:
apps/web/utils/auth/cleanup-invalid-tokens.test.ts
📚 Learning: 2025-11-25T14:37:56.430Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Use `describe.runIf(isAiTest)` with environment variable `RUN_AI_TESTS === "true"` to conditionally run LLM tests
Applied to files:
apps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
📚 Learning: 2025-11-25T14:36:45.807Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/environment-variables.mdc:0-0
Timestamp: 2025-11-25T14:36:45.807Z
Learning: Applies to apps/web/env.ts : Add client-side environment variables to `apps/web/env.ts` under the `experimental__runtimeEnv` object to enable runtime access
Applied to files:
apps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
📚 Learning: 2026-01-01T10:42:29.767Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.767Z
Learning: Applies to **/*.test.{ts,tsx} : Mock `server-only` using `vi.mock("server-only", () => ({}))`
Applied to files:
apps/web/utils/ai/meeting-briefs/generate-briefing.test.tsapps/web/utils/meeting-briefs/recipient-context.test.ts
📚 Learning: 2026-01-01T10:42:29.767Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.767Z
Learning: Applies to **/__tests__/**/*.{ts,tsx} : Place AI tests in the `__tests__` directory and do not run them by default as they use a real LLM
Applied to files:
apps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : LLM feature functions must import from `zod` for schema validation, use `createScopedLogger` from `@/utils/logger`, `chatCompletionObject` and `createGenerateObject` from `@/utils/llms`, and import `EmailAccountWithAI` type from `@/utils/llms/types`
Applied to files:
apps/web/utils/ai/meeting-briefs/generate-briefing.test.tsapps/web/utils/llms/index.tsapps/web/utils/error-messages/index.ts
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : LLM feature functions must follow a standard structure: accept options with `inputData` and `emailAccount` parameters, implement input validation with early returns, define separate system and user prompts, create a Zod schema for response validation, and use `createGenerateObject` to execute the LLM call
Applied to files:
apps/web/utils/llms/index.ts
📚 Learning: 2025-11-25T14:39:08.150Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-11-25T14:39:08.150Z
Learning: Applies to apps/web/app/api/**/*.{ts,tsx} : All database queries must include user scoping with `emailAccountId` or `userId` filtering in WHERE clauses
Applied to files:
apps/web/utils/llms/index.tsapps/web/utils/error-messages/index.tsapps/web/utils/auth/cleanup-invalid-tokens.ts
📚 Learning: 2025-11-25T14:39:27.909Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:27.909Z
Learning: Applies to **/app/api/**/*.ts : Use `withEmailAccount` middleware for operations scoped to a specific email account, including reading/writing emails, rules, schedules, or any operation using `emailAccountId`
Applied to files:
apps/web/utils/llms/index.tsapps/web/utils/auth/cleanup-invalid-tokens.ts
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : Implement early returns for invalid LLM inputs, use proper error types and logging, implement fallbacks for AI failures, and add retry logic for transient failures using `withRetry`
Applied to files:
apps/web/utils/llms/index.tsapps/web/utils/error-messages/index.tsapps/web/utils/auth/cleanup-invalid-tokens.ts
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : Use TypeScript types for all LLM function parameters and return values, and define clear interfaces for complex input/output structures
Applied to files:
apps/web/utils/llms/index.ts
📚 Learning: 2025-11-25T14:39:23.326Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:23.326Z
Learning: Applies to app/api/**/*.ts : ALL API routes that handle user data MUST use appropriate middleware: `withEmailAccount` for email-scoped operations, `withAuth` for user-scoped operations, or `withError` with proper validation for public/cron endpoints
Applied to files:
apps/web/utils/llms/index.tsapps/web/utils/auth/cleanup-invalid-tokens.ts
📚 Learning: 2025-11-25T14:39:04.892Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-11-25T14:39:04.892Z
Learning: Applies to apps/web/app/api/**/route.ts : All database queries must include user/account filtering with `emailAccountId` or `userId` in WHERE clauses to prevent IDOR vulnerabilities
Applied to files:
apps/web/utils/llms/index.tsapps/web/utils/auth/cleanup-invalid-tokens.ts
📚 Learning: 2025-11-25T14:39:49.448Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-11-25T14:39:49.448Z
Learning: Applies to apps/web/utils/actions/*.ts : Use `actionClient` when both authenticated user context and a specific emailAccountId are needed, with emailAccountId bound when calling from the client
Applied to files:
apps/web/utils/llms/index.tsapps/web/utils/auth/cleanup-invalid-tokens.ts
📚 Learning: 2025-07-08T13:14:07.449Z
Learnt from: elie222
Repo: elie222/inbox-zero PR: 537
File: apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx:30-34
Timestamp: 2025-07-08T13:14:07.449Z
Learning: The clean onboarding page in apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx is intentionally Gmail-specific and should show an error for non-Google email accounts rather than attempting to support multiple providers.
Applied to files:
apps/web/utils/llms/index.tsapps/web/utils/error-messages/index.tsapps/web/utils/auth/cleanup-invalid-tokens.ts
📚 Learning: 2025-11-25T14:39:08.150Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-11-25T14:39:08.150Z
Learning: Applies to apps/web/app/api/**/*.{ts,tsx} : Use generic error messages instead of revealing internal details; throw `SafeError` instead of exposing user IDs, resource IDs, or system information
Applied to files:
apps/web/utils/error-messages/index.ts
📚 Learning: 2025-11-25T14:39:23.326Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:23.326Z
Learning: Applies to app/api/**/*.ts : Use `SafeError` for error responses to prevent information disclosure - provide generic messages (e.g., 'Rule not found' not 'Rule {id} does not exist for user {userId}') without revealing internal IDs or ownership details
Applied to files:
apps/web/utils/error-messages/index.ts
📚 Learning: 2025-11-25T14:39:27.909Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:27.909Z
Learning: Applies to **/app/api/**/*.ts : Maintain consistent error response format across all API routes to avoid information disclosure while providing meaningful error feedback
Applied to files:
apps/web/utils/error-messages/index.ts
📚 Learning: 2025-11-25T14:39:27.909Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:27.909Z
Learning: Applies to **/*.ts : Use SafeError for error responses to prevent information disclosure. Generic error messages should not reveal internal IDs, logic, or resource ownership details
Applied to files:
apps/web/utils/error-messages/index.ts
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/utils/actions/**/*.ts : Use proper error handling with try/catch blocks
Applied to files:
apps/web/utils/error-messages/index.ts
📚 Learning: 2025-11-25T14:38:42.022Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/prisma.mdc:0-0
Timestamp: 2025-11-25T14:38:42.022Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Import Prisma using the project's centralized utility: `import prisma from '@/utils/prisma'`
Applied to files:
apps/web/utils/error-messages/index.ts
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Make sure to pass a message value when creating a built-in error
Applied to files:
apps/web/utils/error-messages/index.ts
📚 Learning: 2025-11-25T14:39:23.326Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:23.326Z
Learning: Applies to app/api/**/*.ts : Use `withError` middleware only for public endpoints or custom authentication logic - cron endpoints MUST validate with `hasCronSecret(request)` or `hasPostCronSecret(request)` and capture unauthorized attempts with `captureException`
Applied to files:
apps/web/utils/error-messages/index.ts
📚 Learning: 2025-12-18T16:37:47.972Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/logging.mdc:0-0
Timestamp: 2025-12-18T16:37:47.972Z
Learning: Applies to **/{scripts,tests,__tests__}/**/*.{ts,tsx} : Use createScopedLogger only for code that doesn't run within a middleware chain, such as standalone scripts or tests
Applied to files:
apps/web/utils/error-messages/index.ts
📚 Learning: 2025-11-25T14:39:23.326Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:23.326Z
Learning: Applies to **/*.ts : Prevent Insecure Direct Object References (IDOR) by validating resource ownership in all queries - always include ownership filters (e.g., `emailAccount: { id: emailAccountId }`) when accessing user-specific resources
Applied to files:
apps/web/utils/auth/cleanup-invalid-tokens.ts
📚 Learning: 2025-11-25T14:39:23.326Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:23.326Z
Learning: Applies to **/*.ts : Always validate that resources belong to the authenticated user before any operation - use ownership checks in queries (e.g., `emailAccount: { id: emailAccountId }`) and throw `SafeError` if validation fails
Applied to files:
apps/web/utils/auth/cleanup-invalid-tokens.ts
📚 Learning: 2025-11-25T14:39:27.909Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:27.909Z
Learning: Applies to **/app/api/**/*.ts : ALL API routes that handle user data MUST use appropriate middleware: use `withEmailAccount` for email-scoped operations, use `withAuth` for user-scoped operations, or use `withError` with proper validation for public/custom auth endpoints
Applied to files:
apps/web/utils/auth/cleanup-invalid-tokens.ts
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : User prompts must contain the actual data and context, and should be kept separate from system prompts
Applied to files:
apps/web/utils/auth/cleanup-invalid-tokens.ts
📚 Learning: 2025-11-25T14:37:09.306Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-11-25T14:37:09.306Z
Learning: Applies to apps/web/utils/actions/*.ts : Server actions should use 'use server' directive and automatically receive authentication context (`emailAccountId`) from the `actionClient`
Applied to files:
apps/web/utils/auth/cleanup-invalid-tokens.ts
🧬 Code graph analysis (5)
apps/web/utils/auth.test.ts (2)
apps/web/utils/auth.ts (1)
saveTokens(449-531)apps/web/utils/error-messages/index.ts (1)
clearUserErrorMessages(53-70)
apps/web/utils/auth/cleanup-invalid-tokens.test.ts (4)
apps/web/utils/logger.ts (1)
createScopedLogger(18-82)apps/web/utils/auth/cleanup-invalid-tokens.ts (1)
cleanupInvalidTokens(14-100)packages/resend/src/send.tsx (1)
sendReconnectionEmail(182-207)apps/web/utils/error-messages/index.ts (1)
addUserErrorMessage(26-51)
apps/web/utils/llms/index.ts (2)
apps/web/utils/llms/types.ts (1)
EmailAccountWithAI(10-32)apps/web/utils/error-messages/index.ts (2)
ErrorType(72-79)addUserErrorMessage(26-51)
apps/web/utils/error-messages/index.ts (1)
apps/web/utils/error.ts (1)
captureException(46-76)
apps/web/utils/auth/cleanup-invalid-tokens.ts (4)
apps/web/utils/unsubscribe.ts (1)
createUnsubscribeToken(5-21)packages/resend/src/send.tsx (1)
sendReconnectionEmail(182-207)apps/web/env.ts (1)
env(17-258)apps/web/utils/error-messages/index.ts (2)
addUserErrorMessage(26-51)ErrorType(72-79)
⏰ 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). (5)
- GitHub Check: Baz Reviewer
- GitHub Check: cubic · AI code reviewer
- GitHub Check: test
- GitHub Check: Macroscope - Correctness Check
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (21)
apps/web/utils/meeting-briefs/recipient-context.test.ts (1)
15-15: LGTM! UTC conversion improves test reliability.The changes to use UTC-based date methods (
getUTCDay(),getUTCMonth(), etc.) make the tests timezone-independent and deterministic. The updated test expectations correctly reflect UTC times for the given ISO date strings. This ensures tests produce consistent results regardless of the system's timezone configuration.Also applies to: 40-44, 103-103, 105-105, 131-131, 163-163, 168-168, 234-234
apps/web/utils/ai/meeting-briefs/generate-briefing.test.ts (1)
31-148: Well-structured tests with clear documentation.The test cases are well-written with descriptive names and helpful comments explaining the timezone bug fix. Using
toMatchInlineSnapshotis appropriate for regression testing the prompt output. The tests correctly focus on unit testing the prompt building logic without calling real LLMs.apps/web/utils/error-messages/index.ts (4)
3-3: LGTM!The import of
captureExceptionsupports the enhanced error handling inclearUserErrorMessages.
26-35: LGTM! Correctly resolves past review concerns.The signature change from
userEmailtouserIdensures the error message is correctly attributed to the owning user. This addresses the concern raised in past reviews thatemailAccount.emailmay differ fromuser.email.
58-69: LGTM!The try/catch wrapper provides appropriate error handling and logging for potential Prisma update failures, ensuring the function degrades gracefully.
78-78: LGTM!The
ACCOUNT_DISCONNECTEDerror type appropriately supports the new OAuth disconnection handling flow.apps/web/utils/llms/index.ts (3)
51-51: LGTM!The type extension to include
userIdinemailAccountcorrectly supports the userId-based error handling flow.Also applies to: 146-146
116-124: LGTM!The
handleErrorsignature and all call sites have been consistently updated to propagateuserIdthrough the error handling pipeline.Also applies to: 128-136, 209-217, 299-314
318-355: LGTM!All
addUserErrorMessagecalls have been correctly updated to passuserIdinstead ofuserEmail, consistent with the signature change in the error-messages module.apps/web/utils/auth.test.ts (3)
5-28: LGTM!The expanded imports and mock setup correctly support the new
saveTokenstests, following vitest testing patterns and project conventions.
121-148: LGTM!The test correctly verifies that saving tokens via
emailAccountIdclearsdisconnectedAtand error messages, ensuring proper reconnection flow.
150-179: LGTM!The test correctly verifies that saving tokens via
providerAccountIdclearsdisconnectedAtand error messages, covering the alternative reconnection path.apps/web/utils/auth/cleanup-invalid-tokens.ts (4)
3-6: LGTM!The new imports appropriately support the reconnection email flow and error messaging functionality.
25-39: LGTM!The extended select appropriately fetches all fields required for the reconnection flow and disconnection state management.
64-90: LGTM!The reconnection email flow is well-structured with proper error handling, logging, and graceful degradation if email sending fails.
92-96: LGTM! Correctly uses userId.The
addUserErrorMessagecall now correctly usesuserIdinstead ofuserEmail, resolving the concern from past reviews about potential email mismatch.apps/web/utils/auth/cleanup-invalid-tokens.test.ts (5)
1-27: LGTM!The test setup follows project testing conventions with proper mocking and cleanup between tests.
38-61: LGTM!The test correctly verifies the full reconnection flow for watched accounts, including account disconnection, email sending, and error message creation.
63-82: LGTM!The test correctly verifies that unwatched accounts skip email sending but still get marked as disconnected with an error message.
84-98: LGTM!The test correctly verifies the idempotency guard that prevents repeated processing of already disconnected accounts.
100-113: LGTM!The test correctly verifies that
insufficient_permissionsreason triggers disconnection without sending reconnection emails or error messages.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
apps/web/utils/auth/cleanup-invalid-tokens.ts (2)
88-93: Consider removing email addresses from logs to reduce PII exposure.Logging
emailAccountIdis already available in the logging context and can be used for debugging without exposing PII.🔎 Suggested change
- logger.info("Reconnection email sent", { email: emailAccount.email }); + logger.info("Reconnection email sent"); } catch (error) { logger.error("Failed to send reconnection email", { - email: emailAccount.email, error, });
101-108: Consider wrappingaddUserErrorMessagein try-catch for consistency.The reconnection email is wrapped in try-catch to prevent notification failures from blocking the flow. For consistency, consider applying the same pattern to
addUserErrorMessageso that a database error doesn't prevent the final log from executing.🔎 Suggested change
- await addUserErrorMessage( - emailAccount.userId, - ErrorType.ACCOUNT_DISCONNECTED, - `The connection for ${emailAccount.email} was disconnected. Please reconnect your account to resume automation.`, - ); + try { + await addUserErrorMessage( + emailAccount.userId, + ErrorType.ACCOUNT_DISCONNECTED, + `The connection for ${emailAccount.email} was disconnected. Please reconnect your account to resume automation.`, + ); + } catch (error) { + logger.error("Failed to add user error message", { error }); + } }
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
apps/web/utils/ai/meeting-briefs/generate-briefing.test.tsapps/web/utils/auth/cleanup-invalid-tokens.test.tsapps/web/utils/auth/cleanup-invalid-tokens.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/web/utils/auth/cleanup-invalid-tokens.test.ts
🧰 Additional context used
📓 Path-based instructions (19)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/data-fetching.mdc)
**/*.{ts,tsx}: For API GET requests to server, use theswrpackage
Useresult?.serverErrorwithtoastErrorfrom@/components/Toastfor error handling in async operations
**/*.{ts,tsx}: Use wrapper functions for Gmail message operations (get, list, batch, etc.) from @/utils/gmail/message.ts instead of direct API calls
Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls
Use wrapper functions for Gmail label operations from @/utils/gmail/label.ts instead of direct API calls
**/*.{ts,tsx}: For early access feature flags, create hooks using the naming conventionuse[FeatureName]Enabledthat return a boolean fromuseFeatureFlagEnabled("flag-key")
For A/B test variant flags, create hooks using the naming conventionuse[FeatureName]Variantthat define variant types, useuseFeatureFlagVariantKey()with type casting, and provide a default "control" fallback
Use kebab-case for PostHog feature flag keys (e.g.,inbox-cleaner,pricing-options-2)
Always define types for A/B test variant flags (e.g.,type PricingVariant = "control" | "variant-a" | "variant-b") and provide type safety through type casting
**/*.{ts,tsx}: Don't use primitive type aliases or misleading types
Don't use empty type parameters in type aliases and interfaces
Don't use this and super in static contexts
Don't use any or unknown as type constraints
Don't use the TypeScript directive @ts-ignore
Don't use TypeScript enums
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 TypeScript namespaces
Don't use non-null assertions with the!postfix operator
Don't use parameter properties in class constructors
Don't use user-defined types
Useas constinstead of literal types and type annotations
Use eitherT[]orArray<T>consistently
Initialize each enum member value explicitly
Useexport typefor types
Use `impo...
Files:
apps/web/utils/auth/cleanup-invalid-tokens.tsapps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/prisma-enum-imports.mdc)
Always import Prisma enums from
@/generated/prisma/enumsinstead of@/generated/prisma/clientto avoid Next.js bundling errors in client componentsImport Prisma using the project's centralized utility:
import prisma from '@/utils/prisma'
Files:
apps/web/utils/auth/cleanup-invalid-tokens.tsapps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Import specific lodash functions rather than entire lodash library to minimize bundle size (e.g.,
import groupBy from 'lodash/groupBy')
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Do not export types/interfaces that are only used within the same file. Export later if needed
Files:
apps/web/utils/auth/cleanup-invalid-tokens.tsapps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/security.mdc)
**/*.ts: ALL database queries MUST be scoped to the authenticated user/account by including user/account filtering in WHERE clauses to prevent unauthorized data access
Always validate that resources belong to the authenticated user before performing operations, using ownership checks in WHERE clauses or relationships
Always validate all input parameters for type, format, and length before using them in database queries
Use SafeError for error responses to prevent information disclosure. Generic error messages should not reveal internal IDs, logic, or resource ownership details
Only return necessary fields in API responses using Prisma'sselectoption. Never expose sensitive data such as password hashes, private keys, or system flags
Prevent Insecure Direct Object References (IDOR) by validating resource ownership before operations. AllfindUnique/findFirstcalls MUST include ownership filters
Prevent mass assignment vulnerabilities by explicitly whitelisting allowed fields in update operations instead of accepting all user-provided data
Prevent privilege escalation by never allowing users to modify system fields, ownership fields, or admin-only attributes through user input
AllfindManyqueries MUST be scoped to the user's data by including appropriate WHERE filters to prevent returning data from other users
Use Prisma relationships for access control by leveraging nested where clauses (e.g.,emailAccount: { id: emailAccountId }) to validate ownership
Files:
apps/web/utils/auth/cleanup-invalid-tokens.tsapps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
**/*.{tsx,ts}: Use Shadcn UI and Tailwind for components and styling
Usenext/imagepackage for images
For API GET requests to server, use theswrpackage with hooks likeuseSWRto fetch data
For text inputs, use theInputcomponent withregisterPropsfor form integration and error handling
Files:
apps/web/utils/auth/cleanup-invalid-tokens.tsapps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
**/*.{tsx,ts,css}
📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)
Implement responsive design with Tailwind CSS using a mobile-first approach
Files:
apps/web/utils/auth/cleanup-invalid-tokens.tsapps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useaccessKeyattribute on any HTML element
Don't setaria-hidden="true"on focusable elements
Don't add ARIA roles, states, and properties to elements that don't support them
Don't use distracting elements like<marquee>or<blink>
Only use thescopeprop on<th>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 assigntabIndexto non-interactive HTML elements
Don't use positive integers fortabIndexproperty
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 atitleelement 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
AssigntabIndexto non-interactive HTML elements witharia-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 atypeattribute for button elements
Make elements with interactive roles and handlers focusable
Give heading elements content that's accessible to screen readers (not hidden witharia-hidden)
Always include alangattribute on the html element
Always include atitleattribute for iframe elements
AccompanyonClickwith at least one of:onKeyUp,onKeyDown, oronKeyPress
AccompanyonMouseOver/onMouseOutwithonFocus/onBlur
Include caption tracks for audio and video elements
Use semantic elements instead of role attributes in JSX
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 AR...
Files:
apps/web/utils/auth/cleanup-invalid-tokens.tsapps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
!(pages/_document).{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
Don't use the next/head module in pages/_document.js on Next.js projects
Files:
apps/web/utils/auth/cleanup-invalid-tokens.tsapps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/utilities.mdc)
**/*.{js,ts,jsx,tsx}: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size (e.g.,import groupBy from 'lodash/groupBy')
Files:
apps/web/utils/auth/cleanup-invalid-tokens.tsapps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
**/{utils,helpers,lib}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
Logger should be passed as a parameter to helper functions instead of creating their own logger instances
Files:
apps/web/utils/auth/cleanup-invalid-tokens.tsapps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
apps/web/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx,js,jsx}: Use@/path aliases for imports from project root
Prefer self-documenting code over comments; use descriptive variable and function names instead of explaining intent with comments
Add helper functions to the bottom of files, not the top
All imports go at the top of files, no mid-file dynamic imports
Files:
apps/web/utils/auth/cleanup-invalid-tokens.tsapps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
apps/web/**/*.{ts,tsx,js,jsx,json,css}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Format code with Prettier
Files:
apps/web/utils/auth/cleanup-invalid-tokens.tsapps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
apps/web/**/*.{example,ts,json}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Add environment variables to
.env.example,env.ts, andturbo.json
Files:
apps/web/utils/auth/cleanup-invalid-tokens.tsapps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
apps/web/{utils/ai,utils/llms,__tests__}/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/llm.mdc)
LLM-related code must be organized in specific directories:
apps/web/utils/ai/for main implementations,apps/web/utils/llms/for core utilities and configurations, andapps/web/__tests__/for LLM-specific tests
Files:
apps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
apps/web/utils/ai/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/llm.mdc)
apps/web/utils/ai/**/*.ts: LLM feature functions must import fromzodfor schema validation, usecreateScopedLoggerfrom@/utils/logger,chatCompletionObjectandcreateGenerateObjectfrom@/utils/llms, and importEmailAccountWithAItype from@/utils/llms/types
LLM feature functions must follow a standard structure: accept options withinputDataandemailAccountparameters, implement input validation with early returns, define separate system and user prompts, create a Zod schema for response validation, and usecreateGenerateObjectto execute the LLM call
System prompts must define the LLM's role and task specifications
User prompts must contain the actual data and context, and should be kept separate from system prompts
Always define a Zod schema for LLM response validation and make schemas as specific as possible to guide the LLM output
Use descriptive scoped loggers for each LLM feature, log inputs and outputs with appropriate log levels, and include relevant context in log messages
Implement early returns for invalid LLM inputs, use proper error types and logging, implement fallbacks for AI failures, and add retry logic for transient failures usingwithRetry
Use XML-like tags to structure data in prompts, remove excessive whitespace and truncate long inputs, and format data consistently across similar LLM functions
Use TypeScript types for all LLM function parameters and return values, and define clear interfaces for complex input/output structures
Keep related AI functions in the same file or directory, extract common patterns into utility functions, and document complex AI logic with clear comments
Files:
apps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
**/*.{test,spec}.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{test,spec}.{js,jsx,ts,tsx}: Don't nest describe() blocks too deeply in test files
Don't use callbacks in asynchronous tests and hooks
Don't have duplicate hooks in describe blocks
Don't use export or module.exports in test files
Don't use focused tests
Make sure the assertion function, like expect, is placed inside an it() function call
Don't use disabled tests
Files:
apps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
apps/web/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Co-locate test files next to source files (e.g.,
utils/example.test.ts). Only E2E and AI tests go in__tests__/
Files:
apps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
**/*.test.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/notes.mdc)
Co-locate test files next to source files (e.g.,
utils/example.test.ts). Only E2E and AI tests go in__tests__/
Files:
apps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/testing.mdc)
**/*.test.{ts,tsx}: Usevitestas the testing framework
Colocate test files next to the tested file with.test.tsor.test.tsxnaming convention (e.g.,dir/format.tsanddir/format.test.ts)
Mockserver-onlyusingvi.mock("server-only", () => ({}))
Mock Prisma usingvi.mock("@/utils/prisma")and the provided mock from@/utils/__mocks__/prisma
Use test helper functionsgetEmail,getEmailAccount, andgetRulefrom@/__tests__/helpersfor creating mock data
Clear all mocks between tests usingbeforeEach(() => { vi.clearAllMocks(); })
Use descriptive test names that clearly indicate what is being tested
Do not mock the Logger in tests
Files:
apps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
🧠 Learnings (28)
📚 Learning: 2025-11-25T14:39:23.326Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:23.326Z
Learning: Applies to app/api/**/*.ts : Use `withEmailAccount` middleware for operations scoped to a specific email account (reading/writing emails, rules, schedules, etc.) - provides `emailAccountId`, `userId`, and `email` in `request.auth`
Applied to files:
apps/web/utils/auth/cleanup-invalid-tokens.ts
📚 Learning: 2025-11-25T14:39:27.909Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:27.909Z
Learning: Applies to **/app/api/**/*.ts : Use `withEmailAccount` middleware for operations scoped to a specific email account, including reading/writing emails, rules, schedules, or any operation using `emailAccountId`
Applied to files:
apps/web/utils/auth/cleanup-invalid-tokens.ts
📚 Learning: 2025-11-25T14:39:08.150Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-11-25T14:39:08.150Z
Learning: Applies to apps/web/app/api/**/*.{ts,tsx} : All database queries must include user scoping with `emailAccountId` or `userId` filtering in WHERE clauses
Applied to files:
apps/web/utils/auth/cleanup-invalid-tokens.ts
📚 Learning: 2025-11-25T14:39:23.326Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:23.326Z
Learning: Applies to app/api/**/*.ts : ALL API routes that handle user data MUST use appropriate middleware: `withEmailAccount` for email-scoped operations, `withAuth` for user-scoped operations, or `withError` with proper validation for public/cron endpoints
Applied to files:
apps/web/utils/auth/cleanup-invalid-tokens.ts
📚 Learning: 2025-11-25T14:39:23.326Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:23.326Z
Learning: Applies to **/*.ts : Always validate that resources belong to the authenticated user before any operation - use ownership checks in queries (e.g., `emailAccount: { id: emailAccountId }`) and throw `SafeError` if validation fails
Applied to files:
apps/web/utils/auth/cleanup-invalid-tokens.ts
📚 Learning: 2025-11-25T14:39:04.892Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-11-25T14:39:04.892Z
Learning: Applies to apps/web/app/api/**/route.ts : All database queries must include user/account filtering with `emailAccountId` or `userId` in WHERE clauses to prevent IDOR vulnerabilities
Applied to files:
apps/web/utils/auth/cleanup-invalid-tokens.ts
📚 Learning: 2025-11-25T14:39:27.909Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:27.909Z
Learning: Applies to **/app/api/**/*.ts : ALL API routes that handle user data MUST use appropriate middleware: use `withEmailAccount` for email-scoped operations, use `withAuth` for user-scoped operations, or use `withError` with proper validation for public/custom auth endpoints
Applied to files:
apps/web/utils/auth/cleanup-invalid-tokens.ts
📚 Learning: 2025-11-25T14:39:49.448Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-11-25T14:39:49.448Z
Learning: Applies to apps/web/utils/actions/*.ts : Use `actionClient` when both authenticated user context and a specific emailAccountId are needed, with emailAccountId bound when calling from the client
Applied to files:
apps/web/utils/auth/cleanup-invalid-tokens.ts
📚 Learning: 2025-11-25T14:39:23.326Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:23.326Z
Learning: Applies to **/*.ts : Prevent Insecure Direct Object References (IDOR) by validating resource ownership in all queries - always include ownership filters (e.g., `emailAccount: { id: emailAccountId }`) when accessing user-specific resources
Applied to files:
apps/web/utils/auth/cleanup-invalid-tokens.ts
📚 Learning: 2025-07-08T13:14:07.449Z
Learnt from: elie222
Repo: elie222/inbox-zero PR: 537
File: apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx:30-34
Timestamp: 2025-07-08T13:14:07.449Z
Learning: The clean onboarding page in apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx is intentionally Gmail-specific and should show an error for non-Google email accounts rather than attempting to support multiple providers.
Applied to files:
apps/web/utils/auth/cleanup-invalid-tokens.ts
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : Implement early returns for invalid LLM inputs, use proper error types and logging, implement fallbacks for AI failures, and add retry logic for transient failures using `withRetry`
Applied to files:
apps/web/utils/auth/cleanup-invalid-tokens.ts
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : User prompts must contain the actual data and context, and should be kept separate from system prompts
Applied to files:
apps/web/utils/auth/cleanup-invalid-tokens.ts
📚 Learning: 2025-11-25T14:37:56.430Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Use vitest imports (`describe`, `expect`, `test`, `vi`, `beforeEach`) in LLM test files
Applied to files:
apps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
📚 Learning: 2026-01-01T10:42:29.767Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.767Z
Learning: Applies to **/*.test.{ts,tsx} : Clear all mocks between tests using `beforeEach(() => { vi.clearAllMocks(); })`
Applied to files:
apps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
📚 Learning: 2025-11-25T14:37:56.430Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Mock 'server-only' module with empty object in LLM test files: `vi.mock("server-only", () => ({}))`
Applied to files:
apps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
📚 Learning: 2026-01-01T10:42:29.767Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.767Z
Learning: Applies to **/*.test.{ts,tsx} : Use test helper functions `getEmail`, `getEmailAccount`, and `getRule` from `@/__tests__/helpers` for creating mock data
Applied to files:
apps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
📚 Learning: 2025-11-25T14:37:56.430Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Use `describe.runIf(isAiTest)` with environment variable `RUN_AI_TESTS === "true"` to conditionally run LLM tests
Applied to files:
apps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
📚 Learning: 2025-11-25T14:37:56.430Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Prefer using existing helpers from `@/__tests__/helpers.ts` (`getEmailAccount`, `getEmail`, `getRule`, `getMockMessage`, `getMockExecutedRule`) instead of creating custom test data helpers
Applied to files:
apps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
📚 Learning: 2026-01-01T10:42:29.767Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.767Z
Learning: Applies to **/*.test.{ts,tsx} : Mock Prisma using `vi.mock("@/utils/prisma")` and the provided mock from `@/utils/__mocks__/prisma`
Applied to files:
apps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
📚 Learning: 2026-01-01T10:42:29.767Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.767Z
Learning: Applies to **/*.test.{ts,tsx} : Mock `server-only` using `vi.mock("server-only", () => ({}))`
Applied to files:
apps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
📚 Learning: 2026-01-01T10:42:29.767Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.767Z
Learning: Applies to **/__tests__/**/*.{ts,tsx} : Place AI tests in the `__tests__` directory and do not run them by default as they use a real LLM
Applied to files:
apps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : LLM feature functions must import from `zod` for schema validation, use `createScopedLogger` from `@/utils/logger`, `chatCompletionObject` and `createGenerateObject` from `@/utils/llms`, and import `EmailAccountWithAI` type from `@/utils/llms/types`
Applied to files:
apps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
📚 Learning: 2026-01-01T10:42:29.767Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.767Z
Learning: Applies to **/*.test.{ts,tsx} : Use descriptive test names that clearly indicate what is being tested
Applied to files:
apps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
📚 Learning: 2026-01-01T10:42:29.767Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.767Z
Learning: Mock external dependencies in tests
Applied to files:
apps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
📚 Learning: 2026-01-01T10:42:29.767Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.767Z
Learning: Each test should be independent with no shared state between tests
Applied to files:
apps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{test,spec}.{js,jsx,ts,tsx} : Don't have duplicate hooks in describe blocks
Applied to files:
apps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
📚 Learning: 2026-01-01T10:42:29.767Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.767Z
Learning: Applies to **/*.test.{ts,tsx} : Use `vitest` as the testing framework
Applied to files:
apps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{test,spec}.{js,jsx,ts,tsx} : Make sure the assertion function, like expect, is placed inside an it() function call
Applied to files:
apps/web/utils/ai/meeting-briefs/generate-briefing.test.ts
🧬 Code graph analysis (1)
apps/web/utils/auth/cleanup-invalid-tokens.ts (4)
apps/web/utils/unsubscribe.ts (1)
createUnsubscribeToken(5-21)packages/resend/src/send.tsx (1)
sendReconnectionEmail(182-207)apps/web/env.ts (1)
env(17-258)apps/web/utils/error-messages/index.ts (2)
addUserErrorMessage(26-51)ErrorType(72-79)
⏰ 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: cubic · AI code reviewer
- GitHub Check: test
- GitHub Check: Macroscope - Correctness Check
🔇 Additional comments (8)
apps/web/utils/ai/meeting-briefs/generate-briefing.test.ts (3)
1-29: LGTM! Mock setup follows testing guidelines.The import and mock setup is well-structured:
- Vitest imports including
beforeEachare correct- The
server-onlymock follows the required pattern- The new
@/envmock provides deterministic test values for API keys and LLM provider- Explicit unmocking of date utilities is appropriate for testing timezone formatting
31-33: LGTM! Previous feedback addressed.The
beforeEachblock properly clears all mocks between tests, preventing state leakage. This addresses the previous review comment and follows the testing guidelines.
35-151: LGTM! Well-structured test cases.Both test cases are well-designed:
- Descriptive test names clearly indicate the scenarios being tested
- The first test documents the timezone bug fix with helpful comments
- Inline snapshot assertions are appropriate for verifying formatted output
- Test data is constructed inline (appropriate since no helper exists for
MeetingBriefingData)- Tests are independent with no shared state
apps/web/utils/auth/cleanup-invalid-tokens.ts (5)
1-6: LGTM!Imports are well-organized, using the centralized Prisma utility and proper type-only import for Logger. All imports are placed at the top of the file as per coding guidelines.
25-44: LGTM!The Prisma query properly uses
selectto return only necessary fields (following security guidelines) and is correctly scoped byemailAccountId. The logger is appropriately passed as a parameter per coding guidelines for helper functions.
46-66: LGTM!The atomic update pattern correctly addresses the concurrency concern raised in past reviews. Using
updateManywithdisconnectedAt: nullin the WHERE clause ensures only one caller proceeds with notifications, preventing duplicate emails. The early guard at lines 46-49 serves as a useful optimization to avoid unnecessary update calls.
101-106: LGTM!The
addUserErrorMessagecorrectly usesuserIdinstead of
68-99: LGTM on the reconnection flow!The
isWatchedlogic correctly validates both presence and future expiration ofwatchEmailsExpirationDate(addressing past review feedback). The conditional notification approach—sending emails only for actively watched accounts—is a sensible design that avoids unnecessary emails to inactive users.
User description
auth: implement robust handling for Gmail invalid_grant errors
Sends reconnection emails, marks accounts as disconnected, and adds in-app notifications when OAuth tokens are revoked or expired.
Summary by CodeRabbit
New Features
Improvements
Tests
✏️ Tip: You can customize this high-level summary in your review settings.
Generated description
Below is a concise technical summary of the changes proposed in this PR:
graph LR subgraph "inbox-zero-ai" ["inbox-zero-ai"] cleanupInvalidTokens_("cleanupInvalidTokens"):::modified PRISMA_("PRISMA"):::modified RESEND_("RESEND"):::added addUserErrorMessage_("addUserErrorMessage"):::modified handleLinkAccount_("handleLinkAccount"):::modified clearUserErrorMessages_("clearUserErrorMessages"):::modified saveTokens_("saveTokens"):::modified handleError_("handleError"):::modified cleanupInvalidTokens_ -- "Now reads disconnectedAt and metadata before cleanup." --> PRISMA_ cleanupInvalidTokens_ -- "Also sets disconnectedAt when clearing OAuth tokens." --> PRISMA_ cleanupInvalidTokens_ -- "Sends reconnection email to watched account's email." --> RESEND_ cleanupInvalidTokens_ -- "Records ACCOUNT_DISCONNECTED error for affected user." --> addUserErrorMessage_ handleLinkAccount_ -- "Upserts emailAccount transactionally, preserving OAuth data." --> PRISMA_ handleLinkAccount_ -- "Clears disconnectedAt marking account active again." --> PRISMA_ handleLinkAccount_ -- "Clears user's error messages upon successful linking." --> clearUserErrorMessages_ saveTokens_ -- "Resets disconnectedAt when saving refreshed OAuth tokens." --> PRISMA_ saveTokens_ -- "Updates account tokens and returns userId after update." --> PRISMA_ saveTokens_ -- "Clears user's error messages after token persistence." --> clearUserErrorMessages_ handleError_ -- "Now reports LLM errors tied to userId." --> addUserErrorMessage_ addUserErrorMessage_ -- "Now looks up user by id before adding error." --> PRISMA_ clearUserErrorMessages_ -- "Clears errorMessages with error handling and exception capture." --> PRISMA_ classDef added stroke:#15AA7A classDef removed stroke:#CD5270 classDef modified stroke:#EDAC4C linkStyle default stroke:#CBD5E1,font-size:13px end subgraph "@inboxzero/resend" ["@inboxzero/resend"] sendReconnectionEmail_("sendReconnectionEmail"):::added ReconnectionEmail_("ReconnectionEmail"):::added EMAIL_SERVICE_("EMAIL_SERVICE"):::modified Footer_("Footer"):::added GETINBOXZERO_("GETINBOXZERO"):::added sendReconnectionEmail_ -- "Renders ReconnectionEmail with email/unsubscribeToken; sends reconnection subject." --> ReconnectionEmail_ sendReconnectionEmail_ -- "Sends rendered email including unsubscribeToken and reconnection category tag." --> EMAIL_SERVICE_ ReconnectionEmail_ -- "Appends Footer adding unsubscribe link with encoded unsubscribeToken." --> Footer_ ReconnectionEmail_ -- "Uses baseUrl for branding, CTA and navigation links." --> GETINBOXZERO_ Footer_ -- "Generates unsubscribe/support/privacy links using baseUrl and token." --> GETINBOXZERO_ classDef added stroke:#15AA7A classDef removed stroke:#CD5270 classDef modified stroke:#EDAC4C linkStyle default stroke:#CBD5E1,font-size:13px endImplement robust handling for
invalid_grantOAuth errors by introducing adisconnectedAtfield in theAccountmodel to track disconnections, sending targeted reconnection emails to users, and displaying in-app notifications. Prevent disconnected accounts from being processed by thewatch-managerand webhooks, and automatically clear the disconnected status and associated error messages upon successful re-authentication.watch-managerand webhook validation to filter out accounts with adisconnectedAttimestamp. Additionally, it implements logic withinsaveTokensandhandleLinkAccountto clear thedisconnectedAttimestamp and any associated user error messages when an account successfully re-authenticates or re-links, ensuring a clean reconnection process. Minor test infrastructure updates are also included.Modified files (9)
Latest Contributors(2)
disconnectedAtfield to theAccountmodel, updating thecleanupInvalidTokensutility to mark accounts as disconnected, sending aReconnectionEmailto prompt user action, and integrating an in-app error message system to inform users of the disconnection. It also updates LLM-related error handling to useuserIdfor message attribution.Modified files (8)
Latest Contributors(2)