Add Fastmail support using JMAP API#1110
Conversation
|
@claude is attempting to deploy a commit to the Inbox Zero OSS Program Team on Vercel. A member of the Team first needs to authorize it. |
|
|
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughAdds Fastmail and Authelia support: new Fastmail JMAP client and EmailProvider, OAuth auth-url and callback routes, app-token linking action and modal, UI wiring and provider-driven loading in AddAccount, feature flags/env entries, and support utilities across auth and account-linking. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant UI as AddAccount UI
participant AuthURL as /api/fastmail/linking/auth-url
participant FastmailOAuth as Fastmail OAuth
participant Callback as /api/fastmail/linking/callback
participant FastmailJMAP as Fastmail JMAP
participant ServerAction as linkFastmailAppTokenAction
participant DB as Database
User->>UI: Click "Add Fastmail" (OAuth) or "Use App Token"
alt OAuth flow
UI->>AuthURL: GET auth-url
AuthURL->>AuthURL: generate state & set state cookie
AuthURL-->>UI: return { url }
UI->>FastmailOAuth: redirect user to url
FastmailOAuth-->>User: consent prompt
User->>FastmailOAuth: authorize (redirect with code,state)
FastmailOAuth->>Callback: deliver code,state
Callback->>Callback: validate state cookie
Callback->>Callback: acquire lock / check cache
Callback->>FastmailOAuth: exchange code -> tokens
Callback->>FastmailJMAP: get userinfo (sub,email,name)
Callback->>DB: lookup linked account
alt existing account
Callback->>DB: update tokens
else new account
Callback->>DB: create account + emailAccount
end
Callback->>Callback: cache result, release lock, clear state cookie
Callback-->>User: redirect to /accounts with status
else App Token flow
UI->>ServerAction: submit app token
ServerAction->>FastmailJMAP: validate token -> userinfo
ServerAction->>DB: lookup/handle create/update/merge
ServerAction-->>UI: return result (success/error)
end
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Add Fastmail email account support using the JMAP API across provider factory, OAuth/app-token linking flows, and a new
|
There was a problem hiding this comment.
Actionable comments posted: 11
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/account.ts (1)
23-36: Addawaitwhen callinggetGmailClientWithRefreshingetGmailClientForEmail.
getGmailClientWithRefreshis async, but line 29 is missingawait. The other client functions—getOutlookClientForEmail(line 60) andgetFastmailClientForEmail(line 113)—correctly await their respective*WithRefreshcalls. Withoutawait, the function returns a Promise instead of the resolved client, causing downstream errors.
🧹 Nitpick comments (9)
apps/web/utils/account-linking.ts (1)
39-42: Consider simplifying the response type union.All three response types (
GetAuthLinkUrlResponse,GetOutlookAuthLinkUrlResponse,GetFastmailAuthLinkUrlResponse) have the identical shape{ url: string }. You could simplify this to a single inline type or a shared type alias.🔎 Optional simplification:
- const data: - | GetAuthLinkUrlResponse - | GetOutlookAuthLinkUrlResponse - | GetFastmailAuthLinkUrlResponse = await response.json(); + const data: { url: string } = await response.json();Alternatively, define a shared
AuthLinkUrlResponsetype in a common location.apps/web/utils/auth.ts (1)
307-368: Consider adding explicit handling for unsupported providers.The
getProfileDatafunction implicitly returnsundefinedwhen the provider doesn't match any known type. This could lead to confusing runtime behavior if a new provider is added but this function isn't updated. Consider adding an explicit throw for unknown providers.🔎 Suggested improvement:
if (isFastmailProvider(providerId)) { try { const userInfo = await getFastmailUserInfo(accessToken); return { email: userInfo.email?.toLowerCase(), name: userInfo.name, image: undefined, // Fastmail doesn't provide profile photos via OIDC }; } catch (error) { logger.error("Error fetching Fastmail profile data", { error }); throw error; } } + + throw new Error(`Unsupported provider: ${providerId}`); }apps/web/app/(app)/accounts/AddAccount.tsx (1)
85-94: Consider using an official Fastmail brand asset.The implementation is correct, but the generic
MailIconwith a hardcoded purple color (#5c2d91) differs from Google/Microsoft which use official SVG brand icons. For visual consistency and brand accuracy, consider adding a Fastmail logo SVG to/images/fastmail.svg.apps/web/app/api/fastmail/linking/callback/route.ts (1)
88-96: Consider not including status code in error message.The error message includes the HTTP status code which could leak implementation details. While this is an internal error, consider using a more generic message.
🔎 Suggested change:
- throw new Error( - `Failed to exchange code for tokens: ${tokenResponse.status}`, - ); + throw new Error("Failed to exchange code for tokens");apps/web/utils/email/fastmail.ts (5)
2275-2283: Inefficient base64 encoding for large attachments.String concatenation in a loop is O(n²) due to string immutability. For large attachments, this could cause significant performance degradation and memory pressure.
🔎 Use Buffer for efficient encoding (Node.js environment):
const buffer = await response.arrayBuffer(); - // Convert ArrayBuffer to base64 using Web APIs - const bytes = new Uint8Array(buffer); - let binary = ""; - for (let i = 0; i < bytes.byteLength; i++) { - binary += String.fromCharCode(bytes[i]); - } - const base64 = btoa(binary); + // Use Buffer for efficient base64 encoding + const base64 = Buffer.from(buffer).toString("base64"); return { data: base64, size: buffer.byteLength, };
1682-1691: Consider batching label removals.Iterating and making separate API calls for each label is inefficient. JMAP supports multiple patches in a single update object.
🔎 Batch into single request:
async removeThreadLabels( threadId: string, labelIds: string[], ): Promise<void> { if (labelIds.length === 0) return; - for (const labelId of labelIds) { - await this.removeThreadLabel(threadId, labelId); - } + // Get all emails in the thread + const threadResponse = await this.client.request([ + [ + "Email/query", + { + accountId: this.client.accountId, + filter: { inThread: threadId }, + }, + "0", + ], + ]); + + const emailIds = getResponseData<JMAPQueryResponse>( + threadResponse.methodResponses[0], + ).ids; + + if (emailIds.length === 0) return; + + // Remove all labels from all emails in one request + const update: Record<string, Record<string, boolean>> = {}; + for (const emailId of emailIds) { + update[emailId] = {}; + for (const labelId of labelIds) { + update[emailId][`mailboxIds/${labelId}`] = false; + } + } + + await this.client.request([ + [ + "Email/set", + { + accountId: this.client.accountId, + update, + }, + "0", + ], + ]); }
950-1004: Redundant mailbox lookups inside loop.
getMailboxByRole(FastmailMailbox.INBOX)andgetMailboxByRole(FastmailMailbox.ARCHIVE)are called for each sender iteration, but the results don't change. Move these lookups outside the loop.🔎 Apply this diff:
async bulkArchiveFromSenders( fromEmails: string[], ownerEmail: string, _emailAccountId: string, ): Promise<void> { const log = this.logger.with({ action: "bulkArchiveFromSenders", sendersCount: fromEmails.length, }); + const inbox = await this.getMailboxByRole(FastmailMailbox.INBOX); + if (!inbox) { + log.warn("Inbox mailbox not found"); + return; + } + const archive = await this.getMailboxByRole(FastmailMailbox.ARCHIVE); + for (const sender of fromEmails) { - const inbox = await this.getMailboxByRole(FastmailMailbox.INBOX); - if (!inbox) continue; - // Query for all emails from this sender in inbox const response = await this.client.request([ // ... rest of the query ]); // ... rest of the code, remove the archive lookup from inside - if (emailIds.length > 0) { - // Archive all emails - const archive = await this.getMailboxByRole(FastmailMailbox.ARCHIVE);
2670-2676: Assumption: first identity is default.The code assumes
index === 0means default identity. JMAP identities don't have a standard "default" flag, so this heuristic is reasonable, but consider documenting this behavior.
326-351: Extract repeated email properties to a constant.The same list of email properties is repeated in ~15 places throughout the file. Extract to a constant for maintainability.
🔎 Add at the top of the file:
const EMAIL_PROPERTIES = [ "id", "threadId", "mailboxIds", "keywords", "from", "to", "cc", "bcc", "subject", "receivedAt", "sentAt", "preview", "hasAttachment", "messageId", "inReplyTo", "references", "replyTo", "bodyStructure", "bodyValues", "textBody", "htmlBody", "attachments", ] as const;Then use
properties: EMAIL_PROPERTIESin allEmail/getcalls.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (15)
apps/web/app/(app)/accounts/AddAccount.tsx(5 hunks)apps/web/app/api/fastmail/linking/auth-url/route.ts(1 hunks)apps/web/app/api/fastmail/linking/callback/route.ts(1 hunks)apps/web/env.ts(1 hunks)apps/web/utils/account-linking.ts(2 hunks)apps/web/utils/account.ts(2 hunks)apps/web/utils/auth.ts(2 hunks)apps/web/utils/email/fastmail.ts(1 hunks)apps/web/utils/email/provider-types.ts(1 hunks)apps/web/utils/email/provider.ts(2 hunks)apps/web/utils/email/types.ts(1 hunks)apps/web/utils/fastmail/client.ts(1 hunks)apps/web/utils/fastmail/constants.ts(1 hunks)apps/web/utils/fastmail/scopes.ts(1 hunks)apps/web/utils/oauth/account-linking.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (28)
**/*.{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/oauth/account-linking.tsapps/web/env.tsapps/web/utils/account.tsapps/web/utils/fastmail/scopes.tsapps/web/utils/email/types.tsapps/web/utils/email/provider.tsapps/web/app/api/fastmail/linking/callback/route.tsapps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/utils/email/provider-types.tsapps/web/utils/fastmail/client.tsapps/web/utils/auth.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/utils/fastmail/constants.tsapps/web/utils/account-linking.tsapps/web/utils/email/fastmail.ts
**/{server,api,actions,utils}/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
**/{server,api,actions,utils}/**/*.ts: UsecreateScopedLoggerfrom "@/utils/logger" for logging in backend code
Add thecreateScopedLoggerinstantiation at the top of the file with an appropriate scope name
Use.with()method to attach context variables only within specific functions, not on global loggers
For large functions with reused variables, usecreateScopedLogger().with()to attach context once and reuse the logger without passing variables repeatedly
Files:
apps/web/utils/oauth/account-linking.tsapps/web/utils/account.tsapps/web/utils/fastmail/scopes.tsapps/web/utils/email/types.tsapps/web/utils/email/provider.tsapps/web/app/api/fastmail/linking/callback/route.tsapps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/utils/email/provider-types.tsapps/web/utils/fastmail/client.tsapps/web/utils/auth.tsapps/web/utils/fastmail/constants.tsapps/web/utils/account-linking.tsapps/web/utils/email/fastmail.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/oauth/account-linking.tsapps/web/env.tsapps/web/utils/account.tsapps/web/utils/fastmail/scopes.tsapps/web/utils/email/types.tsapps/web/utils/email/provider.tsapps/web/app/api/fastmail/linking/callback/route.tsapps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/utils/email/provider-types.tsapps/web/utils/fastmail/client.tsapps/web/utils/auth.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/utils/fastmail/constants.tsapps/web/utils/account-linking.tsapps/web/utils/email/fastmail.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
Use@/path aliases for imports from project root
Follow tailwindcss patterns with prettier-plugin-tailwindcss class organization
Prefix client-side environment variables withNEXT_PUBLIC_
Leverage TypeScript inference for better developer experience with type exports from API routes
Files:
apps/web/utils/oauth/account-linking.tsapps/web/env.tsapps/web/utils/account.tsapps/web/utils/fastmail/scopes.tsapps/web/utils/email/types.tsapps/web/utils/email/provider.tsapps/web/app/api/fastmail/linking/callback/route.tsapps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/utils/email/provider-types.tsapps/web/utils/fastmail/client.tsapps/web/utils/auth.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/utils/fastmail/constants.tsapps/web/utils/account-linking.tsapps/web/utils/email/fastmail.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/oauth/account-linking.tsapps/web/env.tsapps/web/utils/account.tsapps/web/utils/fastmail/scopes.tsapps/web/utils/email/types.tsapps/web/utils/email/provider.tsapps/web/app/api/fastmail/linking/callback/route.tsapps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/utils/email/provider-types.tsapps/web/utils/fastmail/client.tsapps/web/utils/auth.tsapps/web/utils/fastmail/constants.tsapps/web/utils/account-linking.tsapps/web/utils/email/fastmail.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/oauth/account-linking.tsapps/web/env.tsapps/web/utils/account.tsapps/web/utils/fastmail/scopes.tsapps/web/utils/email/types.tsapps/web/utils/email/provider.tsapps/web/app/api/fastmail/linking/callback/route.tsapps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/utils/email/provider-types.tsapps/web/utils/fastmail/client.tsapps/web/utils/auth.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/utils/fastmail/constants.tsapps/web/utils/account-linking.tsapps/web/utils/email/fastmail.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/oauth/account-linking.tsapps/web/env.tsapps/web/utils/account.tsapps/web/utils/fastmail/scopes.tsapps/web/utils/email/types.tsapps/web/utils/email/provider.tsapps/web/app/api/fastmail/linking/callback/route.tsapps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/utils/email/provider-types.tsapps/web/utils/fastmail/client.tsapps/web/utils/auth.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/utils/fastmail/constants.tsapps/web/utils/account-linking.tsapps/web/utils/email/fastmail.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/oauth/account-linking.tsapps/web/env.tsapps/web/utils/account.tsapps/web/utils/fastmail/scopes.tsapps/web/utils/email/types.tsapps/web/utils/email/provider.tsapps/web/app/api/fastmail/linking/callback/route.tsapps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/utils/email/provider-types.tsapps/web/utils/fastmail/client.tsapps/web/utils/auth.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/utils/fastmail/constants.tsapps/web/utils/account-linking.tsapps/web/utils/email/fastmail.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/oauth/account-linking.tsapps/web/env.tsapps/web/utils/account.tsapps/web/utils/fastmail/scopes.tsapps/web/utils/email/types.tsapps/web/utils/email/provider.tsapps/web/app/api/fastmail/linking/callback/route.tsapps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/utils/email/provider-types.tsapps/web/utils/fastmail/client.tsapps/web/utils/auth.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/utils/fastmail/constants.tsapps/web/utils/account-linking.tsapps/web/utils/email/fastmail.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/oauth/account-linking.tsapps/web/env.tsapps/web/utils/account.tsapps/web/utils/fastmail/scopes.tsapps/web/utils/email/types.tsapps/web/utils/email/provider.tsapps/web/app/api/fastmail/linking/callback/route.tsapps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/utils/email/provider-types.tsapps/web/utils/fastmail/client.tsapps/web/utils/auth.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/utils/fastmail/constants.tsapps/web/utils/account-linking.tsapps/web/utils/email/fastmail.ts
apps/web/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx,js,jsx}: Use proper error handling with try/catch blocks
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
Files:
apps/web/utils/oauth/account-linking.tsapps/web/env.tsapps/web/utils/account.tsapps/web/utils/fastmail/scopes.tsapps/web/utils/email/types.tsapps/web/utils/email/provider.tsapps/web/app/api/fastmail/linking/callback/route.tsapps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/utils/email/provider-types.tsapps/web/utils/fastmail/client.tsapps/web/utils/auth.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/utils/fastmail/constants.tsapps/web/utils/account-linking.tsapps/web/utils/email/fastmail.ts
apps/web/**/*.{ts,tsx,js,jsx,json,css,md}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Format code with Prettier
Files:
apps/web/utils/oauth/account-linking.tsapps/web/env.tsapps/web/utils/account.tsapps/web/utils/fastmail/scopes.tsapps/web/utils/email/types.tsapps/web/utils/email/provider.tsapps/web/app/api/fastmail/linking/callback/route.tsapps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/utils/email/provider-types.tsapps/web/utils/fastmail/client.tsapps/web/utils/auth.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/utils/fastmail/constants.tsapps/web/utils/account-linking.tsapps/web/utils/email/fastmail.ts
apps/web/env.ts
📄 CodeRabbit inference engine (.cursor/rules/environment-variables.mdc)
apps/web/env.ts: Add server-only environment variables toapps/web/env.tsunder theserverobject with Zod schema validation
Add client-side environment variables toapps/web/env.tsunder theclientobject withNEXT_PUBLIC_prefix and Zod schema validation
Add client-side environment variables toapps/web/env.tsunder theexperimental__runtimeEnvobject to enable runtime access
Files:
apps/web/env.ts
{.env.example,apps/web/env.ts}
📄 CodeRabbit inference engine (.cursor/rules/environment-variables.mdc)
Client-side environment variables must be prefixed with
NEXT_PUBLIC_
Files:
apps/web/env.ts
apps/web/**/{.env.example,env.ts,turbo.json}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Add environment variables to
.env.example,env.ts, andturbo.json
Files:
apps/web/env.ts
apps/web/app/api/**/route.ts
📄 CodeRabbit inference engine (.cursor/rules/fullstack-workflow.mdc)
apps/web/app/api/**/route.ts: Create GET API routes usingwithAuthorwithEmailAccountmiddleware inapps/web/app/api/*/route.ts, export response types asGetExampleResponsetype alias for client-side type safety
Always export response types from GET routes asGet[Feature]Responseusing type inference from the data fetching function for type-safe client consumption
Do NOT use POST API routes for mutations - always use server actions withnext-safe-actioninstead
Files:
apps/web/app/api/fastmail/linking/callback/route.tsapps/web/app/api/fastmail/linking/auth-url/route.ts
**/app/**/route.ts
📄 CodeRabbit inference engine (.cursor/rules/get-api-route.mdc)
**/app/**/route.ts: Always wrap GET API route handlers withwithAuthorwithEmailAccountmiddleware for consistent error handling and authentication in Next.js App Router
Infer and export response type for GET API routes usingAwaited<ReturnType<typeof functionName>>pattern in Next.js
Use Prisma for database queries in GET API routes
Return responses usingNextResponse.json()in GET API routes
Do not use try/catch blocks in GET API route handlers when usingwithAuthorwithEmailAccountmiddleware, as the middleware handles error handling
Files:
apps/web/app/api/fastmail/linking/callback/route.tsapps/web/app/api/fastmail/linking/auth-url/route.ts
apps/web/app/**/[!.]*/route.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Use kebab-case for route directories in Next.js App Router (e.g.,
api/hello-world/route)
Files:
apps/web/app/api/fastmail/linking/callback/route.tsapps/web/app/api/fastmail/linking/auth-url/route.ts
apps/web/app/api/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/security-audit.mdc)
apps/web/app/api/**/*.{ts,tsx}: API routes must usewithAuth,withEmailAccount, orwithErrormiddleware for authentication
All database queries must include user scoping withemailAccountIdoruserIdfiltering in WHERE clauses
Request parameters must be validated before use; avoid direct parameter usage without type checking
Use generic error messages instead of revealing internal details; throwSafeErrorinstead of exposing user IDs, resource IDs, or system information
API routes should only return necessary fields usingselectin database queries to prevent unintended information disclosure
Cron endpoints must usehasCronSecretorhasPostCronSecretto validate cron requests and prevent unauthorized access
Request bodies should use Zod schemas for validation to ensure type safety and prevent injection attacks
Files:
apps/web/app/api/fastmail/linking/callback/route.tsapps/web/app/api/fastmail/linking/auth-url/route.ts
**/app/api/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/security.mdc)
**/app/api/**/*.ts: ALL API routes that handle user data MUST use appropriate middleware: usewithEmailAccountfor email-scoped operations, usewithAuthfor user-scoped operations, or usewithErrorwith proper validation for public/custom auth endpoints
UsewithEmailAccountmiddleware for operations scoped to a specific email account, including reading/writing emails, rules, schedules, or any operation usingemailAccountId
UsewithAuthmiddleware for user-level operations such as user settings, API keys, and referrals that use onlyuserId
UsewithErrormiddleware only for public endpoints, custom authentication logic, or cron endpoints. For cron endpoints, MUST usehasCronSecret()orhasPostCronSecret()validation
Cron endpoints without proper authentication can be triggered by anyone. CRITICAL: All cron endpoints MUST validate cron secret usinghasCronSecret(request)orhasPostCronSecret(request)and capture unauthorized attempts withcaptureException()
Always validate request bodies using Zod schemas to ensure type safety and prevent invalid data from reaching database operations
Maintain consistent error response format across all API routes to avoid information disclosure while providing meaningful error feedback
Files:
apps/web/app/api/fastmail/linking/callback/route.tsapps/web/app/api/fastmail/linking/auth-url/route.ts
apps/web/**/app/**
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Follow NextJS app router structure with (app) directory
Files:
apps/web/app/api/fastmail/linking/callback/route.tsapps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/(app)/accounts/AddAccount.tsx
apps/web/**/app/api/**/route.ts
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/app/api/**/route.ts: Wrap GET API routes withwithAuthorwithEmailAccountmiddleware
Export response types from GET API routes for type-safe client use (e.g.,GetExampleResponse)
Files:
apps/web/app/api/fastmail/linking/callback/route.tsapps/web/app/api/fastmail/linking/auth-url/route.ts
apps/web/**/{app/api,utils/actions}/**/*.ts
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/{app/api,utils/actions}/**/*.ts: Use server actions for all mutations (create/update/delete operations) instead of POST API routes
UsewithAuthfor user-level operations andwithEmailAccountfor email-account-level operations
Files:
apps/web/app/api/fastmail/linking/callback/route.tsapps/web/app/api/fastmail/linking/auth-url/route.ts
apps/web/app/(app)/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
apps/web/app/(app)/**/*.{ts,tsx}: Components for the page are either put inpage.tsx, or in theapps/web/app/(app)/PAGE_NAMEfolder
If we're in a deeply nested component we will useswrto fetch via API
If you need to useonClickin a component, that component is a client component and file must start withuse client
Files:
apps/web/app/(app)/accounts/AddAccount.tsx
**/*.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:
apps/web/app/(app)/accounts/AddAccount.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:
apps/web/app/(app)/accounts/AddAccount.tsx
apps/web/**/*.{tsx,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{tsx,jsx}: Prefer functional components with hooks over class components
Use shadcn/ui components when available
Follow consistent naming conventions with PascalCase for component names
Use LoadingContent component for async data with loading and error states
Useresult?.serverErrorwithtoastErrorandtoastSuccessfor mutation error handling
Files:
apps/web/app/(app)/accounts/AddAccount.tsx
apps/web/**/*.{tsx,jsx,css}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Ensure responsive design with mobile-first approach
Files:
apps/web/app/(app)/accounts/AddAccount.tsx
🧠 Learnings (41)
📓 Common learnings
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: Design Gmail wrapper functions to be provider-agnostic to support future email providers like Outlook and ProtonMail
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`
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.
📚 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/oauth/account-linking.tsapps/web/utils/account.tsapps/web/utils/fastmail/scopes.tsapps/web/utils/email/types.tsapps/web/utils/email/provider.tsapps/web/utils/email/provider-types.tsapps/web/utils/fastmail/client.tsapps/web/utils/auth.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/utils/account-linking.tsapps/web/utils/email/fastmail.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} : Always use wrapper functions from @/utils/gmail/ for Gmail API operations instead of direct provider API calls
Applied to files:
apps/web/utils/oauth/account-linking.tsapps/web/utils/account.tsapps/web/utils/email/types.tsapps/web/utils/email/provider.tsapps/web/utils/email/provider-types.tsapps/web/utils/fastmail/client.tsapps/web/utils/auth.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/utils/account-linking.tsapps/web/utils/email/fastmail.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/oauth/account-linking.tsapps/web/utils/email/provider.tsapps/web/utils/auth.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/utils/account-linking.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/oauth/account-linking.tsapps/web/utils/account.tsapps/web/utils/email/provider.tsapps/web/utils/auth.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/utils/account-linking.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/oauth/account-linking.tsapps/web/utils/account.tsapps/web/utils/email/provider.tsapps/web/app/api/fastmail/linking/callback/route.tsapps/web/utils/auth.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/utils/account-linking.tsapps/web/utils/email/fastmail.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: Design Gmail wrapper functions to be provider-agnostic to support future email providers like Outlook and ProtonMail
Applied to files:
apps/web/utils/oauth/account-linking.tsapps/web/utils/email/types.tsapps/web/utils/email/provider.tsapps/web/utils/email/provider-types.tsapps/web/utils/auth.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/utils/email/fastmail.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 server-only environment variables to `apps/web/env.ts` under the `server` object with Zod schema validation
Applied to files:
apps/web/env.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 `client` object with `NEXT_PUBLIC_` prefix and Zod schema validation
Applied to files:
apps/web/env.ts
📚 Learning: 2025-11-25T14:36:43.454Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/environment-variables.mdc:0-0
Timestamp: 2025-11-25T14:36:43.454Z
Learning: Applies to apps/web/env.ts : Define environment variables in `apps/web/env.ts` using Zod schema validation, organizing them into `server` and `client` sections
Applied to files:
apps/web/env.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/env.ts
📚 Learning: 2025-11-25T14:36:43.454Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/environment-variables.mdc:0-0
Timestamp: 2025-11-25T14:36:43.454Z
Learning: Applies to apps/web/env.ts : For client-side environment variables in `apps/web/env.ts`, prefix them with `NEXT_PUBLIC_` and add them to both the `client` and `experimental__runtimeEnv` sections
Applied to files:
apps/web/env.ts
📚 Learning: 2025-12-17T11:18:06.818Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-17T11:18:06.818Z
Learning: Applies to apps/web/**/{.env.example,env.ts,turbo.json} : Add environment variables to `.env.example`, `env.ts`, and `turbo.json`
Applied to files:
apps/web/env.ts
📚 Learning: 2025-12-17T11:18:06.818Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-17T11:18:06.818Z
Learning: Applies to apps/web/**/*.{ts,tsx} : Prefix client-side environment variables with `NEXT_PUBLIC_`
Applied to files:
apps/web/env.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 {.env.example,apps/web/env.ts} : Client-side environment variables must be prefixed with `NEXT_PUBLIC_`
Applied to files:
apps/web/env.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 turbo.json : Add new environment variables to `turbo.json` under `tasks.build.env` as a global dependency for the build task
Applied to files:
apps/web/env.ts
📚 Learning: 2025-11-25T14:36:43.454Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/environment-variables.mdc:0-0
Timestamp: 2025-11-25T14:36:43.454Z
Learning: Applies to turbo.json : Add environment variables to `turbo.json` under `tasks.build.env` array to declare build-time dependencies
Applied to files:
apps/web/env.ts
📚 Learning: 2025-12-17T11:18:06.818Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-17T11:18:06.818Z
Learning: Applies to apps/web/**/{app/api,utils/actions}/**/*.ts : Use `withAuth` for user-level operations and `withEmailAccount` for email-account-level operations
Applied to files:
apps/web/utils/account.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/account.tsapps/web/utils/email/provider.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/account.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/account.tsapps/web/utils/email/provider.tsapps/web/utils/fastmail/client.tsapps/web/utils/auth.tsapps/web/utils/account-linking.tsapps/web/utils/email/fastmail.ts
📚 Learning: 2025-11-25T14:40:00.833Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-25T14:40:00.833Z
Learning: Applies to **/*.test.{ts,tsx} : Use test helpers `getEmail`, `getEmailAccount`, and `getRule` from `@/__tests__/helpers` for mocking emails, accounts, and rules
Applied to files:
apps/web/utils/account.tsapps/web/utils/email/provider.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 thread operations from @/utils/gmail/thread.ts instead of direct API calls
Applied to files:
apps/web/utils/account.tsapps/web/utils/email/types.tsapps/web/utils/email/fastmail.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/account.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/account.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/account.ts
📚 Learning: 2025-11-25T14:38:08.183Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/logging.mdc:0-0
Timestamp: 2025-11-25T14:38:08.183Z
Learning: Applies to **/{server,api,actions,utils}/**/*.ts : Add the `createScopedLogger` instantiation at the top of the file with an appropriate scope name
Applied to files:
apps/web/utils/fastmail/scopes.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/fastmail/scopes.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/app/api/fastmail/linking/callback/route.tsapps/web/app/api/fastmail/linking/auth-url/route.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/app/api/**/route.ts : Create GET API routes using `withAuth` or `withEmailAccount` middleware in `apps/web/app/api/*/route.ts`, export response types as `GetExampleResponse` type alias for client-side type safety
Applied to files:
apps/web/app/api/fastmail/linking/callback/route.tsapps/web/app/api/fastmail/linking/auth-url/route.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/app/api/fastmail/linking/callback/route.ts
📚 Learning: 2025-12-17T11:18:06.818Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-17T11:18:06.818Z
Learning: Applies to apps/web/**/app/api/**/route.ts : Wrap GET API routes with `withAuth` or `withEmailAccount` middleware
Applied to files:
apps/web/app/api/fastmail/linking/callback/route.tsapps/web/app/api/fastmail/linking/auth-url/route.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/app/api/fastmail/linking/callback/route.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 API routes must use `withAuth`, `withEmailAccount`, or `withError` middleware for authentication
Applied to files:
apps/web/app/api/fastmail/linking/callback/route.tsapps/web/app/api/fastmail/linking/auth-url/route.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} : API routes must use `withAuth`, `withEmailAccount`, or `withError` middleware for authentication
Applied to files:
apps/web/app/api/fastmail/linking/callback/route.tsapps/web/app/api/fastmail/linking/auth-url/route.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/app/api/fastmail/linking/callback/route.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 : Infer and export the response type for GET API routes using `export type GetResponse = Awaited<ReturnType<typeof getData>>` pattern in Next.js
Applied to files:
apps/web/app/api/fastmail/linking/auth-url/route.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/app/api/**/route.ts : Always export response types from GET routes as `Get[Feature]Response` using type inference from the data fetching function for type-safe client consumption
Applied to files:
apps/web/app/api/fastmail/linking/auth-url/route.ts
📚 Learning: 2025-12-17T11:18:06.818Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-17T11:18:06.818Z
Learning: Applies to apps/web/**/app/api/**/route.ts : Export response types from GET API routes for type-safe client use (e.g., `GetExampleResponse`)
Applied to files:
apps/web/app/api/fastmail/linking/auth-url/route.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 : Infer and export response type for GET API routes using `Awaited<ReturnType<typeof functionName>>` pattern in Next.js
Applied to files:
apps/web/app/api/fastmail/linking/auth-url/route.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/fastmail/constants.tsapps/web/utils/email/fastmail.ts
🧬 Code graph analysis (6)
apps/web/utils/account.ts (1)
apps/web/utils/fastmail/client.ts (1)
getFastmailClientWithRefresh(148-213)
apps/web/app/api/fastmail/linking/auth-url/route.ts (5)
apps/web/utils/fastmail/client.ts (2)
getLinkingOAuth2Config(62-71)FASTMAIL_OAUTH_AUTHORIZE_URL(12-13)apps/web/utils/oauth/state.ts (2)
generateOAuthState(10-18)oauthStateCookieOptions(34-40)apps/web/utils/fastmail/scopes.ts (1)
SCOPES(3-10)apps/web/utils/middleware.ts (1)
withAuth(378-386)apps/web/utils/fastmail/constants.ts (1)
FASTMAIL_LINKING_STATE_COOKIE_NAME(2-2)
apps/web/utils/fastmail/client.ts (5)
apps/web/utils/logger.ts (1)
createScopedLogger(17-81)apps/web/env.ts (1)
env(17-257)apps/web/utils/fastmail/scopes.ts (1)
SCOPES(3-10)apps/web/utils/error.ts (1)
SafeError(58-68)apps/web/utils/auth.ts (1)
saveTokens(456-530)
apps/web/utils/auth.ts (1)
apps/web/utils/email/provider-types.ts (1)
isFastmailProvider(9-11)
apps/web/utils/account-linking.ts (2)
apps/web/app/api/fastmail/linking/auth-url/route.ts (1)
GetAuthLinkUrlResponse(14-14)apps/web/app/api/outlook/linking/auth-url/route.ts (1)
GetOutlookAuthLinkUrlResponse(10-10)
apps/web/utils/email/fastmail.ts (7)
apps/web/utils/fastmail/client.ts (3)
JMAPMethodResponse(52-52)FastmailClient(54-60)getAccessTokenFromClient(236-238)apps/web/utils/email/types.ts (4)
EmailThread(7-12)EmailLabel(14-25)EmailFilter(27-36)EmailSignature(38-43)apps/web/utils/types.ts (1)
ParsedMessage(51-73)apps/web/utils/fastmail/constants.ts (1)
FastmailMailbox(5-14)apps/web/utils/label.ts (1)
InboxZeroLabel(63-63)apps/web/app/api/threads/validation.ts (1)
ThreadsQuery(15-15)apps/web/utils/outlook/folders.ts (1)
OutlookFolder(12-17)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: cubic · AI code reviewer
- GitHub Check: Review for correctness
| export function getLinkingOAuth2Config() { | ||
| return { | ||
| clientId: env.FASTMAIL_CLIENT_ID || "", | ||
| clientSecret: env.FASTMAIL_CLIENT_SECRET || "", | ||
| redirectUri: `${env.NEXT_PUBLIC_BASE_URL}/api/fastmail/linking/callback`, | ||
| authorizeUrl: FASTMAIL_OAUTH_AUTHORIZE_URL, | ||
| tokenUrl: FASTMAIL_OAUTH_TOKEN_URL, | ||
| scopes: SCOPES, | ||
| }; | ||
| } |
There was a problem hiding this comment.
Missing validation for required environment variables.
getLinkingOAuth2Config silently returns empty strings when FASTMAIL_CLIENT_ID or FASTMAIL_CLIENT_SECRET are not set. This will lead to obscure OAuth failures downstream rather than a clear error at configuration time.
🔎 Consider adding validation:
export function getLinkingOAuth2Config() {
+ if (!env.FASTMAIL_CLIENT_ID || !env.FASTMAIL_CLIENT_SECRET) {
+ throw new SafeError("Fastmail OAuth credentials not configured");
+ }
+
return {
- clientId: env.FASTMAIL_CLIENT_ID || "",
- clientSecret: env.FASTMAIL_CLIENT_SECRET || "",
+ clientId: env.FASTMAIL_CLIENT_ID,
+ clientSecret: env.FASTMAIL_CLIENT_SECRET,
redirectUri: `${env.NEXT_PUBLIC_BASE_URL}/api/fastmail/linking/callback`,
authorizeUrl: FASTMAIL_OAUTH_AUTHORIZE_URL,
tokenUrl: FASTMAIL_OAUTH_TOKEN_URL,
scopes: SCOPES,
};
}🤖 Prompt for AI Agents
In apps/web/utils/fastmail/client.ts around lines 62 to 71,
getLinkingOAuth2Config currently returns empty strings when FASTMAIL_CLIENT_ID
or FASTMAIL_CLIENT_SECRET are unset; add explicit validation at the start of
this function to check env.FASTMAIL_CLIENT_ID and env.FASTMAIL_CLIENT_SECRET
(and optionally env.NEXT_PUBLIC_BASE_URL), and throw a clear Error with a
descriptive message if any required variable is missing so the app fails fast
and surfaces a helpful message during startup/configuration rather than
producing obscure OAuth failures downstream.
There was a problem hiding this comment.
4 issues found across 15 files
Prompt for AI agents (all 4 issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="apps/web/utils/fastmail/client.ts">
<violation number="1" location="apps/web/utils/fastmail/client.ts:165">
P2: Access token expiry check uses seconds value against `Date.now()` milliseconds, causing valid tokens to be treated as expired and forcing needless refreshes.</violation>
</file>
<file name="apps/web/utils/email/fastmail.ts">
<violation number="1" location="apps/web/utils/email/fastmail.ts:2555">
P1: Async callback inside `some()` will not work correctly. `Array.prototype.some()` doesn't handle Promises - async functions always return a truthy Promise object, causing this to return `true` whenever `labelIds` has any elements. The method should be made async and use a proper loop or `Promise.all` pattern.</violation>
</file>
<file name="apps/web/app/api/fastmail/linking/callback/route.ts">
<violation number="1" location="apps/web/app/api/fastmail/linking/callback/route.ts:24">
P2: Missing environment variable validation at the start of the handler. Unlike the Outlook callback which checks for `MICROSOFT_CLIENT_ID` and `MICROSOFT_CLIENT_SECRET` before proceeding, this handler doesn't validate that `FASTMAIL_CLIENT_ID` and `FASTMAIL_CLIENT_SECRET` are configured. If these aren't set, the request will fail with an unclear error from Fastmail's API.</violation>
</file>
<file name="apps/web/app/api/fastmail/linking/auth-url/route.ts">
<violation number="1" location="apps/web/app/api/fastmail/linking/auth-url/route.ts:28">
P1: `access_type: "offline"` is a Google-specific OAuth parameter. Fastmail uses OIDC where you need to add `offline_access` to the scope to get a refresh token. Without this, the token exchange may not return a refresh token.</violation>
</file>
Since this is your first cubic review, here's how it works:
- cubic automatically reviews your code and comments on bugs and improvements
- Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
- Ask questions if you need clarification on any suggestion
Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR
apps/web/utils/fastmail/client.ts
Outdated
| } | ||
|
|
||
| // Check if token is still valid | ||
| if (accessToken && expiresAt && expiresAt > Date.now()) { |
There was a problem hiding this comment.
P2: Access token expiry check uses seconds value against Date.now() milliseconds, causing valid tokens to be treated as expired and forcing needless refreshes.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/utils/fastmail/client.ts, line 165:
<comment>Access token expiry check uses seconds value against `Date.now()` milliseconds, causing valid tokens to be treated as expired and forcing needless refreshes.</comment>
<file context>
@@ -0,0 +1,238 @@
+ }
+
+ // Check if token is still valid
+ if (accessToken && expiresAt && expiresAt > Date.now()) {
+ return createFastmailClient(accessToken);
+ }
</file context>
✅ Addressed in d3bd77c
apps/web/utils/email/fastmail.ts
Outdated
| // Check if the message is in the sent mailbox | ||
| // This would need to be checked against actual mailbox IDs | ||
| return ( | ||
| message.labelIds?.some(async (id) => { |
There was a problem hiding this comment.
P1: Async callback inside some() will not work correctly. Array.prototype.some() doesn't handle Promises - async functions always return a truthy Promise object, causing this to return true whenever labelIds has any elements. The method should be made async and use a proper loop or Promise.all pattern.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/utils/email/fastmail.ts, line 2555:
<comment>Async callback inside `some()` will not work correctly. `Array.prototype.some()` doesn't handle Promises - async functions always return a truthy Promise object, causing this to return `true` whenever `labelIds` has any elements. The method should be made async and use a proper loop or `Promise.all` pattern.</comment>
<file context>
@@ -0,0 +1,2678 @@
+ // Check if the message is in the sent mailbox
+ // This would need to be checked against actual mailbox IDs
+ return (
+ message.labelIds?.some(async (id) => {
+ const mailbox = await this.getMailboxById(id);
+ return mailbox?.role === FastmailMailbox.SENT;
</file context>
✅ Addressed in d3bd77c
| import { isDuplicateError } from "@/utils/prisma-helpers"; | ||
|
|
||
| export const GET = withError("fastmail/linking/callback", async (request) => { | ||
| const logger = request.logger; |
There was a problem hiding this comment.
P2: Missing environment variable validation at the start of the handler. Unlike the Outlook callback which checks for MICROSOFT_CLIENT_ID and MICROSOFT_CLIENT_SECRET before proceeding, this handler doesn't validate that FASTMAIL_CLIENT_ID and FASTMAIL_CLIENT_SECRET are configured. If these aren't set, the request will fail with an unclear error from Fastmail's API.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/app/api/fastmail/linking/callback/route.ts, line 24:
<comment>Missing environment variable validation at the start of the handler. Unlike the Outlook callback which checks for `MICROSOFT_CLIENT_ID` and `MICROSOFT_CLIENT_SECRET` before proceeding, this handler doesn't validate that `FASTMAIL_CLIENT_ID` and `FASTMAIL_CLIENT_SECRET` are configured. If these aren't set, the request will fail with an unclear error from Fastmail's API.</comment>
<file context>
@@ -0,0 +1,303 @@
+import { isDuplicateError } from "@/utils/prisma-helpers";
+
+export const GET = withError("fastmail/linking/callback", async (request) => {
+ const logger = request.logger;
+
+ const searchParams = request.nextUrl.searchParams;
</file context>
✅ Addressed in d3bd77c
| scope: [...new Set([...SCOPES, "openid", "email", "profile"])].join(" "), | ||
| state, | ||
| // Request offline access to get refresh token | ||
| access_type: "offline", |
There was a problem hiding this comment.
P1: access_type: "offline" is a Google-specific OAuth parameter. Fastmail uses OIDC where you need to add offline_access to the scope to get a refresh token. Without this, the token exchange may not return a refresh token.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/app/api/fastmail/linking/auth-url/route.ts, line 28:
<comment>`access_type: "offline"` is a Google-specific OAuth parameter. Fastmail uses OIDC where you need to add `offline_access` to the scope to get a refresh token. Without this, the token exchange may not return a refresh token.</comment>
<file context>
@@ -0,0 +1,50 @@
+ scope: [...new Set([...SCOPES, "openid", "email", "profile"])].join(" "),
+ state,
+ // Request offline access to get refresh token
+ access_type: "offline",
+ prompt: "consent",
+ });
</file context>
✅ Addressed in d3bd77c
|
Have you tested it? I'd love to know if this works well |
|
@elie222 not yet,, but Ill have a go at deploying it either today or on the weekend. I use Fastmail myself, so very motivated to get it working 😆. |
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (3)
apps/web/utils/fastmail/client.ts (3)
216-225: Missing validation for required environment variables.
getLinkingOAuth2Configsilently returns empty strings whenFASTMAIL_CLIENT_IDorFASTMAIL_CLIENT_SECRETare not set. While the calling code in the auth-url route now validates this, it would be safer to fail fast here.
241-244: Missingawaitonresponse.json().The function returns a Promise chain rather than awaiting the JSON parsing result. This is inconsistent with the pattern used elsewhere in this file and could mask parsing errors.
🔎 Apply this diff:
- return response.json(); + return await response.json();
437-440: Missingawaitonresponse.json()ingetUserInfo.Same inconsistency as
getJMAPSession- the function should await the JSON parsing.🔎 Apply this diff:
- return response.json(); + return await response.json();
🧹 Nitpick comments (1)
apps/web/utils/email/fastmail.ts (1)
597-642: Duplicate property list that could use the shared constant.The
getMessagemethod defines its own property list instead of using theEMAIL_PROPERTIESconstant defined at the top of the file.🔎 Consider using the shared constant:
properties: [ - "id", - "threadId", - "mailboxIds", - ... - "attachments", - ], + ...EMAIL_PROPERTIES, + ],
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
apps/web/app/api/fastmail/linking/auth-url/route.ts(1 hunks)apps/web/app/api/fastmail/linking/callback/route.ts(1 hunks)apps/web/utils/email/fastmail.ts(1 hunks)apps/web/utils/fastmail/client.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (21)
**/*.{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/app/api/fastmail/linking/auth-url/route.tsapps/web/app/api/fastmail/linking/callback/route.tsapps/web/utils/fastmail/client.tsapps/web/utils/email/fastmail.ts
apps/web/app/api/**/route.ts
📄 CodeRabbit inference engine (.cursor/rules/fullstack-workflow.mdc)
apps/web/app/api/**/route.ts: Create GET API routes usingwithAuthorwithEmailAccountmiddleware inapps/web/app/api/*/route.ts, export response types asGetExampleResponsetype alias for client-side type safety
Always export response types from GET routes asGet[Feature]Responseusing type inference from the data fetching function for type-safe client consumption
Do NOT use POST API routes for mutations - always use server actions withnext-safe-actioninstead
Files:
apps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/api/fastmail/linking/callback/route.ts
**/app/**/route.ts
📄 CodeRabbit inference engine (.cursor/rules/get-api-route.mdc)
**/app/**/route.ts: Always wrap GET API route handlers withwithAuthorwithEmailAccountmiddleware for consistent error handling and authentication in Next.js App Router
Infer and export response type for GET API routes usingAwaited<ReturnType<typeof functionName>>pattern in Next.js
Use Prisma for database queries in GET API routes
Return responses usingNextResponse.json()in GET API routes
Do not use try/catch blocks in GET API route handlers when usingwithAuthorwithEmailAccountmiddleware, as the middleware handles error handling
Files:
apps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/api/fastmail/linking/callback/route.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/app/api/fastmail/linking/auth-url/route.tsapps/web/app/api/fastmail/linking/callback/route.tsapps/web/utils/fastmail/client.tsapps/web/utils/email/fastmail.ts
apps/web/app/**/[!.]*/route.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Use kebab-case for route directories in Next.js App Router (e.g.,
api/hello-world/route)
Files:
apps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/api/fastmail/linking/callback/route.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
Use@/path aliases for imports from project root
Follow tailwindcss patterns with prettier-plugin-tailwindcss class organization
Prefix client-side environment variables withNEXT_PUBLIC_
Leverage TypeScript inference for better developer experience with type exports from API routes
Files:
apps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/api/fastmail/linking/callback/route.tsapps/web/utils/fastmail/client.tsapps/web/utils/email/fastmail.ts
apps/web/app/api/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/security-audit.mdc)
apps/web/app/api/**/*.{ts,tsx}: API routes must usewithAuth,withEmailAccount, orwithErrormiddleware for authentication
All database queries must include user scoping withemailAccountIdoruserIdfiltering in WHERE clauses
Request parameters must be validated before use; avoid direct parameter usage without type checking
Use generic error messages instead of revealing internal details; throwSafeErrorinstead of exposing user IDs, resource IDs, or system information
API routes should only return necessary fields usingselectin database queries to prevent unintended information disclosure
Cron endpoints must usehasCronSecretorhasPostCronSecretto validate cron requests and prevent unauthorized access
Request bodies should use Zod schemas for validation to ensure type safety and prevent injection attacks
Files:
apps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/api/fastmail/linking/callback/route.ts
**/app/api/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/security.mdc)
**/app/api/**/*.ts: ALL API routes that handle user data MUST use appropriate middleware: usewithEmailAccountfor email-scoped operations, usewithAuthfor user-scoped operations, or usewithErrorwith proper validation for public/custom auth endpoints
UsewithEmailAccountmiddleware for operations scoped to a specific email account, including reading/writing emails, rules, schedules, or any operation usingemailAccountId
UsewithAuthmiddleware for user-level operations such as user settings, API keys, and referrals that use onlyuserId
UsewithErrormiddleware only for public endpoints, custom authentication logic, or cron endpoints. For cron endpoints, MUST usehasCronSecret()orhasPostCronSecret()validation
Cron endpoints without proper authentication can be triggered by anyone. CRITICAL: All cron endpoints MUST validate cron secret usinghasCronSecret(request)orhasPostCronSecret(request)and capture unauthorized attempts withcaptureException()
Always validate request bodies using Zod schemas to ensure type safety and prevent invalid data from reaching database operations
Maintain consistent error response format across all API routes to avoid information disclosure while providing meaningful error feedback
Files:
apps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/api/fastmail/linking/callback/route.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/app/api/fastmail/linking/auth-url/route.tsapps/web/app/api/fastmail/linking/callback/route.tsapps/web/utils/fastmail/client.tsapps/web/utils/email/fastmail.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/app/api/fastmail/linking/auth-url/route.tsapps/web/app/api/fastmail/linking/callback/route.tsapps/web/utils/fastmail/client.tsapps/web/utils/email/fastmail.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/app/api/fastmail/linking/auth-url/route.tsapps/web/app/api/fastmail/linking/callback/route.tsapps/web/utils/fastmail/client.tsapps/web/utils/email/fastmail.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/app/api/fastmail/linking/auth-url/route.tsapps/web/app/api/fastmail/linking/callback/route.tsapps/web/utils/fastmail/client.tsapps/web/utils/email/fastmail.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/app/api/fastmail/linking/auth-url/route.tsapps/web/app/api/fastmail/linking/callback/route.tsapps/web/utils/fastmail/client.tsapps/web/utils/email/fastmail.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/app/api/fastmail/linking/auth-url/route.tsapps/web/app/api/fastmail/linking/callback/route.tsapps/web/utils/fastmail/client.tsapps/web/utils/email/fastmail.ts
apps/web/**/app/**
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Follow NextJS app router structure with (app) directory
Files:
apps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/api/fastmail/linking/callback/route.ts
apps/web/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx,js,jsx}: Use proper error handling with try/catch blocks
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
Files:
apps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/api/fastmail/linking/callback/route.tsapps/web/utils/fastmail/client.tsapps/web/utils/email/fastmail.ts
apps/web/**/*.{ts,tsx,js,jsx,json,css,md}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Format code with Prettier
Files:
apps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/api/fastmail/linking/callback/route.tsapps/web/utils/fastmail/client.tsapps/web/utils/email/fastmail.ts
apps/web/**/app/api/**/route.ts
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/app/api/**/route.ts: Wrap GET API routes withwithAuthorwithEmailAccountmiddleware
Export response types from GET API routes for type-safe client use (e.g.,GetExampleResponse)
Files:
apps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/api/fastmail/linking/callback/route.ts
apps/web/**/{app/api,utils/actions}/**/*.ts
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/{app/api,utils/actions}/**/*.ts: Use server actions for all mutations (create/update/delete operations) instead of POST API routes
UsewithAuthfor user-level operations andwithEmailAccountfor email-account-level operations
Files:
apps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/api/fastmail/linking/callback/route.ts
**/{app,pages}/**/{route,+page}.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
**/{app,pages}/**/{route,+page}.{ts,tsx}: Use middleware wrappers (withError, withAuth, withEmailAccount, withEmailProvider) that automatically create loggers with request context in API routes
Enrich logger context within route handlers using logger.with() to add request-specific fields like messageId
Files:
apps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/api/fastmail/linking/callback/route.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/fastmail/client.tsapps/web/utils/email/fastmail.ts
🧠 Learnings (31)
📓 Common learnings
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: Design Gmail wrapper functions to be provider-agnostic to support future email providers like Outlook and ProtonMail
📚 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/app/api/**/route.ts : Create GET API routes using `withAuth` or `withEmailAccount` middleware in `apps/web/app/api/*/route.ts`, export response types as `GetExampleResponse` type alias for client-side type safety
Applied to files:
apps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/api/fastmail/linking/callback/route.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/app/api/fastmail/linking/auth-url/route.tsapps/web/app/api/fastmail/linking/callback/route.ts
📚 Learning: 2025-12-17T11:18:06.818Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-17T11:18:06.818Z
Learning: Applies to apps/web/**/app/api/**/route.ts : Wrap GET API routes with `withAuth` or `withEmailAccount` middleware
Applied to files:
apps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/api/fastmail/linking/callback/route.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 : Infer and export the response type for GET API routes using `export type GetResponse = Awaited<ReturnType<typeof getData>>` pattern in Next.js
Applied to files:
apps/web/app/api/fastmail/linking/auth-url/route.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/app/api/**/route.ts : Always export response types from GET routes as `Get[Feature]Response` using type inference from the data fetching function for type-safe client consumption
Applied to files:
apps/web/app/api/fastmail/linking/auth-url/route.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 : Infer and export response type for GET API routes using `Awaited<ReturnType<typeof functionName>>` pattern in Next.js
Applied to files:
apps/web/app/api/fastmail/linking/auth-url/route.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} : API routes must use `withAuth`, `withEmailAccount`, or `withError` middleware for authentication
Applied to files:
apps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/api/fastmail/linking/callback/route.ts
📚 Learning: 2025-12-17T11:18:06.818Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-17T11:18:06.818Z
Learning: Applies to apps/web/**/app/api/**/route.ts : Export response types from GET API routes for type-safe client use (e.g., `GetExampleResponse`)
Applied to files:
apps/web/app/api/fastmail/linking/auth-url/route.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 API routes must use `withAuth`, `withEmailAccount`, or `withError` middleware for authentication
Applied to files:
apps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/api/fastmail/linking/callback/route.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/app/api/fastmail/linking/callback/route.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/app/api/fastmail/linking/callback/route.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/app/api/fastmail/linking/callback/route.tsapps/web/utils/email/fastmail.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/app/api/fastmail/linking/callback/route.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/app/api/fastmail/linking/callback/route.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/fastmail/client.tsapps/web/utils/email/fastmail.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 thread operations from @/utils/gmail/thread.ts instead of direct API calls
Applied to files:
apps/web/utils/fastmail/client.tsapps/web/utils/email/fastmail.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/fastmail/client.tsapps/web/utils/email/fastmail.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/fastmail/client.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} : Always use wrapper functions from @/utils/gmail/ for Gmail API operations instead of direct provider API calls
Applied to files:
apps/web/utils/fastmail/client.tsapps/web/utils/email/fastmail.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/fastmail/client.tsapps/web/utils/email/fastmail.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 `client` object with `NEXT_PUBLIC_` prefix and Zod schema validation
Applied to files:
apps/web/utils/fastmail/client.ts
📚 Learning: 2025-11-25T14:36:43.454Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/environment-variables.mdc:0-0
Timestamp: 2025-11-25T14:36:43.454Z
Learning: Applies to apps/web/env.ts : Define environment variables in `apps/web/env.ts` using Zod schema validation, organizing them into `server` and `client` sections
Applied to files:
apps/web/utils/fastmail/client.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} : Don't hardcode sensitive data like API keys and tokens
Applied to files:
apps/web/utils/fastmail/client.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 {.env.example,apps/web/env.ts} : Client-side environment variables must be prefixed with `NEXT_PUBLIC_`
Applied to files:
apps/web/utils/fastmail/client.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/fastmail/client.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/fastmail/client.ts
📚 Learning: 2025-11-25T14:36:43.454Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/environment-variables.mdc:0-0
Timestamp: 2025-11-25T14:36:43.454Z
Learning: Applies to apps/web/env.ts : For client-side environment variables in `apps/web/env.ts`, prefix them with `NEXT_PUBLIC_` and add them to both the `client` and `experimental__runtimeEnv` sections
Applied to files:
apps/web/utils/fastmail/client.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 async functions actually use await
Applied to files:
apps/web/utils/fastmail/client.tsapps/web/utils/email/fastmail.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 Promise-like statements are handled appropriately
Applied to files:
apps/web/utils/fastmail/client.tsapps/web/utils/email/fastmail.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: Design Gmail wrapper functions to be provider-agnostic to support future email providers like Outlook and ProtonMail
Applied to files:
apps/web/utils/email/fastmail.ts
🧬 Code graph analysis (3)
apps/web/app/api/fastmail/linking/auth-url/route.ts (6)
apps/web/utils/fastmail/client.ts (2)
getLinkingOAuth2Config(216-225)FASTMAIL_OAUTH_AUTHORIZE_URL(92-93)apps/web/utils/oauth/state.ts (2)
generateOAuthState(10-18)oauthStateCookieOptions(34-40)apps/web/utils/fastmail/scopes.ts (1)
SCOPES(8-17)apps/web/app/api/fastmail/linking/callback/route.ts (1)
GET(24-323)apps/web/utils/middleware.ts (1)
withAuth(378-386)apps/web/utils/fastmail/constants.ts (1)
FASTMAIL_LINKING_STATE_COOKIE_NAME(10-10)
apps/web/utils/fastmail/client.ts (3)
apps/web/utils/error.ts (1)
SafeError(58-68)apps/web/utils/fastmail/scopes.ts (1)
SCOPES(8-17)apps/web/utils/auth.ts (1)
saveTokens(456-530)
apps/web/utils/email/fastmail.ts (8)
apps/web/utils/fastmail/client.ts (1)
FastmailClient(199-210)apps/web/utils/email/types.ts (5)
EmailProvider(45-245)EmailThread(7-12)EmailLabel(14-25)EmailFilter(27-36)EmailSignature(38-43)apps/web/utils/logger.ts (2)
Logger(5-5)createScopedLogger(17-81)apps/web/utils/types.ts (1)
ParsedMessage(51-73)apps/web/utils/fastmail/constants.ts (1)
FastmailMailbox(22-39)apps/web/utils/label.ts (1)
InboxZeroLabel(63-63)apps/web/app/api/threads/validation.ts (1)
ThreadsQuery(15-15)apps/web/utils/outlook/folders.ts (1)
OutlookFolder(12-17)
🔇 Additional comments (22)
apps/web/utils/fastmail/client.ts (4)
1-13: LGTM! Well-structured imports and module setup.The file establishes a clean foundation for the Fastmail JMAP client with appropriate dependencies for authentication, error handling, and logging.
15-80: Well-implemented retry logic with exponential backoff.The
fetchWithRetryfunction properly handles transient errors (429, 502, 503, 504), respectsRetry-Afterheaders, and uses exponential backoff. The implementation correctly distinguishes between HTTP errors and network errors.
351-363: Good handling of token expiry timestamp formats.The code properly handles both Unix timestamp (seconds) and Date timestamp (milliseconds) formats for
expiresAt, with a reasonable threshold check. This addresses the previously reported issue.
292-323: Solid client creation with capability validation.The
createFastmailClientfunction properly validates required JMAP capabilities and extracts the primary mail account ID before returning the client instance.apps/web/utils/email/fastmail.ts (9)
1-54: Well-organized imports and constants.The file properly imports types and utilities, and defines a reusable
EMAIL_PROPERTIESconstant to ensure consistency across JMAP requests.
202-228: Clean provider class initialization.The
FastmailProviderclass properly implements theEmailProviderinterface with appropriate logger initialization and instance variables for caching.
983-1004: Correct use of JSON Pointer notation for mailbox patching.The
archiveThreadmethod now correctly uses JSON Pointer notation (mailboxIds/${inbox.id}) to patch individual mailbox memberships instead of replacing the entiremailboxIdsobject. This addresses the previously reported bug.
1039-1062: Consistent JSON Pointer pattern inarchiveMessage.Same correct pattern applied for single message archiving.
1606-1649: Attachment upload now implemented correctly.The
sendEmailWithHtmlmethod properly uploads attachments via the blob upload mechanism and includes them in the email creation request. This addresses the previously reported issue about unused attachments parameter.
2754-2769: Correct synchronous implementation ofisSentMessage.The method now correctly uses the synchronous mailbox cache lookup instead of the broken async callback pattern. This addresses the previously reported critical bug.
1163-1169:bulkTrashFromSendersonly adds to trash without removing from other mailboxes.The trash operation adds messages to the Trash mailbox but doesn't remove them from other mailboxes (like Inbox). This might not match the expected "move to trash" behavior. Consider whether this is intentional (keeping in both) or if you should also remove from the source mailbox.
1218-1224: Same concern intrashThread- only adds to trash.Similar to
bulkTrashFromSenders, this only adds the trash mailbox flag without removing from other mailboxes.
2720-2733: Appropriate stub implementations for unsupported features.The
processHistory,watchEmails, andunwatchEmailsmethods correctly log warnings about unsupported functionality. JMAP uses EventSource for real-time updates, which would require a different architecture.apps/web/app/api/fastmail/linking/auth-url/route.ts (3)
1-14: Well-structured imports and type export.The file properly imports dependencies and exports the
GetAuthLinkUrlResponsetype for client-side type safety, following the coding guidelines.
16-36: Correct OAuth URL construction with OIDC-standard scopes.The code now correctly uses the
offline_accessscope (OIDC standard) instead of the Google-specificaccess_type: "offline"parameter. The use ofSetto deduplicate scopes is a nice touch.
38-60: Proper configuration validation and state management.The route correctly:
- Validates OAuth credentials before proceeding
- Uses
withAuthmiddleware as required by coding guidelines- Sets the state cookie with proper options for CSRF protection
apps/web/app/api/fastmail/linking/callback/route.ts (6)
1-22: Comprehensive imports for OAuth callback handling.The file imports all necessary utilities for OAuth validation, account linking, error handling, and race condition management.
24-36: Good: Environment variable validation added.The handler now validates
FASTMAIL_CLIENT_IDandFASTMAIL_CLIENT_SECRETat the start and returns a user-friendly error redirect if not configured. This addresses the previously reported issue.
57-80: Robust OAuth code handling with caching and locking.The implementation properly:
- Checks for cached results to avoid reprocessing
- Acquires a distributed lock to prevent race conditions
- Handles the case where another request is already processing the code
82-116: Proper token exchange with error handling.The code exchange follows OAuth best practices with proper error logging and exception capture.
164-237: Well-handled account creation with race condition recovery.The
continue_createbranch properly handles the case where a concurrent request may have already created the account, avoiding duplicate errors.
312-322: Proper cleanup in error handler.The error handler correctly clears the OAuth code before redirecting to prevent replay attacks.
There was a problem hiding this comment.
3 issues found across 17 files
Prompt for AI agents (all 3 issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="apps/web/utils/fastmail/client.ts">
<violation number="1" location="apps/web/utils/fastmail/client.ts:47">
P2: `Retry-After` header can be an HTTP-date string per RFC 7231. When it is, `Number.parseInt` returns `NaN`, causing `setTimeout` to execute immediately (0ms delay). Consider validating the parsed value or checking if it's a date string.</violation>
</file>
<file name="apps/web/app/api/fastmail/linking/callback/route.ts">
<violation number="1" location="apps/web/app/api/fastmail/linking/callback/route.ts:113">
P2: Missing validation for refresh_token allows creating/updating Fastmail accounts without a refresh token, leading to inevitable token expiry and a broken account.</violation>
</file>
<file name="apps/web/utils/email/fastmail.ts">
<violation number="1" location="apps/web/utils/email/fastmail.ts:1782">
P1: Setting `keywords: { $seen: read }` replaces the entire keywords object, which will remove other email flags like `$flagged` (starred) and `$draft`. Use JSON Pointer notation to patch only the `$seen` keyword while preserving other keywords.</violation>
</file>
Since this is your first cubic review, here's how it works:
- cubic automatically reviews your code and comments on bugs and improvements
- Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
- Ask questions if you need clarification on any suggestion
Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR
apps/web/utils/fastmail/client.ts
Outdated
| // Check for Retry-After header | ||
| const retryAfter = response.headers.get("Retry-After"); | ||
| const delay = retryAfter | ||
| ? Number.parseInt(retryAfter, 10) * 1000 |
There was a problem hiding this comment.
P2: Retry-After header can be an HTTP-date string per RFC 7231. When it is, Number.parseInt returns NaN, causing setTimeout to execute immediately (0ms delay). Consider validating the parsed value or checking if it's a date string.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/utils/fastmail/client.ts, line 47:
<comment>`Retry-After` header can be an HTTP-date string per RFC 7231. When it is, `Number.parseInt` returns `NaN`, causing `setTimeout` to execute immediately (0ms delay). Consider validating the parsed value or checking if it's a date string.</comment>
<file context>
@@ -0,0 +1,449 @@
+ // Check for Retry-After header
+ const retryAfter = response.headers.get("Retry-After");
+ const delay = retryAfter
+ ? Number.parseInt(retryAfter, 10) * 1000
+ : BASE_RETRY_DELAY * 2 ** attempt;
+
</file context>
✅ Addressed in b0819eb
|
|
||
| const tokens = await tokenResponse.json(); | ||
|
|
||
| if (!tokens.access_token) { |
There was a problem hiding this comment.
P2: Missing validation for refresh_token allows creating/updating Fastmail accounts without a refresh token, leading to inevitable token expiry and a broken account.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/app/api/fastmail/linking/callback/route.ts, line 113:
<comment>Missing validation for refresh_token allows creating/updating Fastmail accounts without a refresh token, leading to inevitable token expiry and a broken account.</comment>
<file context>
@@ -0,0 +1,323 @@
+
+ const tokens = await tokenResponse.json();
+
+ if (!tokens.access_token) {
+ throw new Error("Missing access_token from Fastmail response");
+ }
</file context>
✅ Addressed in b0819eb
apps/web/utils/email/fastmail.ts
Outdated
| if (emailIds.length === 0) return; | ||
|
|
||
| // Update $seen keyword | ||
| const update: Record<string, { keywords: Record<string, boolean> }> = {}; |
There was a problem hiding this comment.
P1: Setting keywords: { $seen: read } replaces the entire keywords object, which will remove other email flags like $flagged (starred) and $draft. Use JSON Pointer notation to patch only the $seen keyword while preserving other keywords.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/utils/email/fastmail.ts, line 1782:
<comment>Setting `keywords: { $seen: read }` replaces the entire keywords object, which will remove other email flags like `$flagged` (starred) and `$draft`. Use JSON Pointer notation to patch only the `$seen` keyword while preserving other keywords.</comment>
<file context>
@@ -0,0 +1,2887 @@
+ if (emailIds.length === 0) return;
+
+ // Update $seen keyword
+ const update: Record<string, { keywords: Record<string, boolean> }> = {};
+ for (const emailId of emailIds) {
+ update[emailId] = {
</file context>
✅ Addressed in b0819eb
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (2)
apps/web/utils/fastmail/client.ts (2)
216-225: Missing validation for required environment variables.
getLinkingOAuth2Configreturns empty strings whenFASTMAIL_CLIENT_IDorFASTMAIL_CLIENT_SECRETare not set, leading to obscure OAuth failures downstream rather than a clear error at configuration time.
44-48:Retry-Afterheader parsing may fail for HTTP-date format.Per RFC 7231,
Retry-Aftercan be either seconds or an HTTP-date string. When it's a date string,Number.parseIntreturnsNaN, causingsetTimeoutto execute immediately (0ms delay) instead of backing off.🔎 Consider validating the parsed value
const retryAfter = response.headers.get("Retry-After"); - const delay = retryAfter - ? Number.parseInt(retryAfter, 10) * 1000 - : BASE_RETRY_DELAY * 2 ** attempt; + let delay = BASE_RETRY_DELAY * 2 ** attempt; + if (retryAfter) { + const parsed = Number.parseInt(retryAfter, 10); + if (!Number.isNaN(parsed)) { + delay = parsed * 1000; + } + // Falls back to exponential backoff if Retry-After is HTTP-date or invalid + }
🧹 Nitpick comments (4)
apps/web/utils/actions/fastmail-app-token.validation.ts (1)
1-7: Schema follows project conventions.The validation file correctly exports both the Zod schema and inferred type as per coding guidelines.
Consider adding a regex pattern for Fastmail app tokens (typically
fmu1-xxxxxxxx-xxxxxxxxxxxxxxxxxxxxformat) to catch invalid tokens earlier, though the server-side validation will catch them anyway.apps/web/utils/actions/fastmail-app-token.ts (1)
98-99: Consider defining scopes as a constant.The hardcoded scope string duplicates values from
SCOPESin@/utils/fastmail/scopes.ts. Consider importing and joining the scopes array for consistency.🔎 Suggested change
+import { SCOPES } from "@/utils/fastmail/scopes"; + // In the account creation: scope: - "urn:ietf:params:jmap:core urn:ietf:params:jmap:mail urn:ietf:params:jmap:submission", + SCOPES.slice(0, 3).join(" "), // core, mail, submissionapps/web/utils/fastmail/client.ts (2)
243-243: Missingawaitonresponse.json()return statements.Lines 243 and 445 return
response.json()withoutawait. While this works because the function is async and the caller awaits, it's inconsistent with the pattern at line 278 and can mask JSON parsing errors at the call site.🔎 Apply these changes for consistency
// Line 243 - return response.json(); + return await response.json(); // Line 445 - return response.json(); + return await response.json();Also applies to: 445-445
396-418: Consider validating token response before use.If the token endpoint returns an unexpected response format,
tokens.access_tokencould be undefined, which would then be passed tocreateFastmailClient. Adding a validation check would make this more robust.🔎 Add validation
const tokens = await response.json(); const newAccessToken = tokens.access_token; + + if (!newAccessToken) { + logger.error("Token refresh response missing access_token", { emailAccountId }); + throw new SafeError("Failed to refresh Fastmail token: invalid response"); + } + const newRefreshToken = tokens.refresh_token;
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
apps/web/app/(app)/accounts/AddAccount.tsx(5 hunks)apps/web/app/(app)/accounts/FastmailAppTokenModal.tsx(1 hunks)apps/web/utils/actions/fastmail-app-token.ts(1 hunks)apps/web/utils/actions/fastmail-app-token.validation.ts(1 hunks)apps/web/utils/fastmail/client.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (25)
**/*.{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/actions/fastmail-app-token.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/app/(app)/accounts/FastmailAppTokenModal.tsxapps/web/utils/fastmail/client.tsapps/web/utils/actions/fastmail-app-token.validation.ts
apps/web/utils/actions/*.ts
📄 CodeRabbit inference engine (.cursor/rules/fullstack-workflow.mdc)
apps/web/utils/actions/*.ts: Usenext-safe-actionwith Zod schemas for all server actions (create/update/delete mutations), storing validation schemas inapps/web/utils/actions/*.validation.ts
Server actions should use 'use server' directive and automatically receive authentication context (emailAccountId) from theactionClient
apps/web/utils/actions/*.ts: Create corresponding server action implementation files using the naming conventionapps/web/utils/actions/NAME.tswith 'use server' directive
Use 'use server' directive at the top of server action implementation files
Implement all server actions using thenext-safe-actionlibrary with actionClient, actionClientUser, or adminActionClient for type safety and validation
UseactionClientUserwhen only authenticated user context (userId) is needed
UseactionClientwhen both authenticated user context and a specific emailAccountId are needed, with emailAccountId bound when calling from the client
UseadminActionClientfor actions restricted to admin users
Add metadata with a meaningful action name using.metadata({ name: "actionName" })for Sentry instrumentation and monitoring
Use.schema()method with Zod validation schemas from corresponding.validation.tsfiles in next-safe-action configuration
Access context (userId, emailAccountId, etc.) via thectxobject parameter in the.action()handler
UserevalidatePathorrevalidateTagfrom 'next/cache' within server action handlers when mutations modify data displayed elsewhere
Files:
apps/web/utils/actions/fastmail-app-token.tsapps/web/utils/actions/fastmail-app-token.validation.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/actions/fastmail-app-token.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/app/(app)/accounts/FastmailAppTokenModal.tsxapps/web/utils/fastmail/client.tsapps/web/utils/actions/fastmail-app-token.validation.ts
apps/web/utils/actions/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
apps/web/utils/actions/**/*.ts: Server actions must be located inapps/web/utils/actionsfolder
Server action files must start withuse serverdirective
Files:
apps/web/utils/actions/fastmail-app-token.tsapps/web/utils/actions/fastmail-app-token.validation.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
Use@/path aliases for imports from project root
Follow tailwindcss patterns with prettier-plugin-tailwindcss class organization
Prefix client-side environment variables withNEXT_PUBLIC_
Leverage TypeScript inference for better developer experience with type exports from API routes
Files:
apps/web/utils/actions/fastmail-app-token.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/app/(app)/accounts/FastmailAppTokenModal.tsxapps/web/utils/fastmail/client.tsapps/web/utils/actions/fastmail-app-token.validation.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/actions/fastmail-app-token.tsapps/web/utils/fastmail/client.tsapps/web/utils/actions/fastmail-app-token.validation.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/actions/fastmail-app-token.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/app/(app)/accounts/FastmailAppTokenModal.tsxapps/web/utils/fastmail/client.tsapps/web/utils/actions/fastmail-app-token.validation.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/actions/fastmail-app-token.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/app/(app)/accounts/FastmailAppTokenModal.tsxapps/web/utils/fastmail/client.tsapps/web/utils/actions/fastmail-app-token.validation.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/actions/fastmail-app-token.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/app/(app)/accounts/FastmailAppTokenModal.tsxapps/web/utils/fastmail/client.tsapps/web/utils/actions/fastmail-app-token.validation.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/actions/fastmail-app-token.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/app/(app)/accounts/FastmailAppTokenModal.tsxapps/web/utils/fastmail/client.tsapps/web/utils/actions/fastmail-app-token.validation.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/actions/fastmail-app-token.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/app/(app)/accounts/FastmailAppTokenModal.tsxapps/web/utils/fastmail/client.tsapps/web/utils/actions/fastmail-app-token.validation.ts
apps/web/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx,js,jsx}: Use proper error handling with try/catch blocks
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
Files:
apps/web/utils/actions/fastmail-app-token.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/app/(app)/accounts/FastmailAppTokenModal.tsxapps/web/utils/fastmail/client.tsapps/web/utils/actions/fastmail-app-token.validation.ts
apps/web/**/*.{ts,tsx,js,jsx,json,css,md}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Format code with Prettier
Files:
apps/web/utils/actions/fastmail-app-token.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/app/(app)/accounts/FastmailAppTokenModal.tsxapps/web/utils/fastmail/client.tsapps/web/utils/actions/fastmail-app-token.validation.ts
apps/web/**/utils/actions/*.ts
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/utils/actions/*.ts: Usenext-safe-actionwith proper Zod validation for server actions
CallrevalidatePathin server actions for cache invalidation after mutations
Files:
apps/web/utils/actions/fastmail-app-token.tsapps/web/utils/actions/fastmail-app-token.validation.ts
apps/web/**/{app/api,utils/actions}/**/*.ts
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/{app/api,utils/actions}/**/*.ts: Use server actions for all mutations (create/update/delete operations) instead of POST API routes
UsewithAuthfor user-level operations andwithEmailAccountfor email-account-level operations
Files:
apps/web/utils/actions/fastmail-app-token.tsapps/web/utils/actions/fastmail-app-token.validation.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/actions/fastmail-app-token.tsapps/web/utils/fastmail/client.tsapps/web/utils/actions/fastmail-app-token.validation.ts
apps/web/app/(app)/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
apps/web/app/(app)/**/*.{ts,tsx}: Components for the page are either put inpage.tsx, or in theapps/web/app/(app)/PAGE_NAMEfolder
If we're in a deeply nested component we will useswrto fetch via API
If you need to useonClickin a component, that component is a client component and file must start withuse client
Files:
apps/web/app/(app)/accounts/AddAccount.tsxapps/web/app/(app)/accounts/FastmailAppTokenModal.tsx
**/*.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:
apps/web/app/(app)/accounts/AddAccount.tsxapps/web/app/(app)/accounts/FastmailAppTokenModal.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:
apps/web/app/(app)/accounts/AddAccount.tsxapps/web/app/(app)/accounts/FastmailAppTokenModal.tsx
apps/web/**/app/**
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Follow NextJS app router structure with (app) directory
Files:
apps/web/app/(app)/accounts/AddAccount.tsxapps/web/app/(app)/accounts/FastmailAppTokenModal.tsx
apps/web/**/*.{tsx,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{tsx,jsx}: Prefer functional components with hooks over class components
Use shadcn/ui components when available
Follow consistent naming conventions with PascalCase for component names
Use LoadingContent component for async data with loading and error states
Useresult?.serverErrorwithtoastErrorandtoastSuccessfor mutation error handling
Files:
apps/web/app/(app)/accounts/AddAccount.tsxapps/web/app/(app)/accounts/FastmailAppTokenModal.tsx
apps/web/**/*.{tsx,jsx,css}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Ensure responsive design with mobile-first approach
Files:
apps/web/app/(app)/accounts/AddAccount.tsxapps/web/app/(app)/accounts/FastmailAppTokenModal.tsx
**/*.validation.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)
**/*.validation.{ts,tsx}: Define validation schemas using Zod
Use descriptive error messages in validation schemas
Files:
apps/web/utils/actions/fastmail-app-token.validation.ts
apps/web/utils/actions/*.validation.ts
📄 CodeRabbit inference engine (.cursor/rules/fullstack-workflow.mdc)
apps/web/utils/actions/*.validation.ts: Define Zod validation schemas in separate*.validation.tsfiles and export both the schema and inferred type (e.g.,CreateExampleBody)
Export types from Zod schemas usingz.infer<>to maintain type safety between validation and client usage
apps/web/utils/actions/*.validation.ts: Create separate validation files for server actions using the naming conventionapps/web/utils/actions/NAME.validation.tscontaining Zod schemas and inferred types
Define input validation schemas using Zod in.validation.tsfiles and export both the schema and its inferred TypeScript type
Files:
apps/web/utils/actions/fastmail-app-token.validation.ts
apps/web/**/utils/actions/*.validation.ts
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Create separate Zod validation schema files for server action inputs
Files:
apps/web/utils/actions/fastmail-app-token.validation.ts
🧠 Learnings (49)
📓 Common learnings
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: Design Gmail wrapper functions to be provider-agnostic to support future email providers like Outlook and ProtonMail
📚 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 : Implement all server actions using the `next-safe-action` library with actionClient, actionClientUser, or adminActionClient for type safety and validation
Applied to files:
apps/web/utils/actions/fastmail-app-token.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 : Use `next-safe-action` with Zod schemas for all server actions (create/update/delete mutations), storing validation schemas in `apps/web/utils/actions/*.validation.ts`
Applied to files:
apps/web/utils/actions/fastmail-app-token.ts
📚 Learning: 2025-12-17T11:18:06.818Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-17T11:18:06.818Z
Learning: Applies to apps/web/**/utils/actions/*.ts : Use `next-safe-action` with proper Zod validation for server actions
Applied to files:
apps/web/utils/actions/fastmail-app-token.tsapps/web/app/(app)/accounts/FastmailAppTokenModal.tsx
📚 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/actions/fastmail-app-token.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/*.validation.ts : Create separate validation files for server actions using the naming convention `apps/web/utils/actions/NAME.validation.ts` containing Zod schemas and inferred types
Applied to files:
apps/web/utils/actions/fastmail-app-token.tsapps/web/utils/actions/fastmail-app-token.validation.ts
📚 Learning: 2025-12-17T11:18:06.818Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-17T11:18:06.818Z
Learning: Applies to apps/web/**/utils/actions/*.validation.ts : Create separate Zod validation schema files for server action inputs
Applied to files:
apps/web/utils/actions/fastmail-app-token.tsapps/web/utils/actions/fastmail-app-token.validation.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 : Create corresponding server action implementation files using the naming convention `apps/web/utils/actions/NAME.ts` with 'use server' directive
Applied to files:
apps/web/utils/actions/fastmail-app-token.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/actions/fastmail-app-token.ts
📚 Learning: 2025-12-17T11:18:06.818Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-17T11:18:06.818Z
Learning: Applies to apps/web/**/{app/api,utils/actions}/**/*.ts : Use `withAuth` for user-level operations and `withEmailAccount` for email-account-level operations
Applied to files:
apps/web/utils/actions/fastmail-app-token.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/app/(app)/accounts/FastmailAppTokenModal.tsx
📚 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 `.schema()` method with Zod validation schemas from corresponding `.validation.ts` files in next-safe-action configuration
Applied to files:
apps/web/utils/actions/fastmail-app-token.tsapps/web/utils/actions/fastmail-app-token.validation.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 `revalidatePath` or `revalidateTag` from 'next/cache' within server action handlers when mutations modify data displayed elsewhere
Applied to files:
apps/web/utils/actions/fastmail-app-token.ts
📚 Learning: 2025-12-17T02:38:37.011Z
Learnt from: elie222
Repo: elie222/inbox-zero PR: 1103
File: apps/web/utils/actions/rule.ts:447-457
Timestamp: 2025-12-17T02:38:37.011Z
Learning: In apps/web/utils/actions/rule.ts, revalidatePath is not needed for toggleAllRulesAction because rules data is fetched client-side using SWR, not server-side. Server-side cache revalidation is only needed when using Next.js server components or server-side data fetching.
Applied to files:
apps/web/utils/actions/fastmail-app-token.ts
📚 Learning: 2025-12-17T11:18:06.818Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-17T11:18:06.818Z
Learning: Applies to apps/web/**/utils/actions/*.ts : Call `revalidatePath` in server actions for cache invalidation after mutations
Applied to files:
apps/web/utils/actions/fastmail-app-token.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 `adminActionClient` for actions restricted to admin users
Applied to files:
apps/web/utils/actions/fastmail-app-token.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/app/(app)/accounts/AddAccount.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 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/app/(app)/accounts/AddAccount.tsx
📚 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/app/(app)/accounts/AddAccount.tsx
📚 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/app/(app)/accounts/AddAccount.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: Design Gmail wrapper functions to be provider-agnostic to support future email providers like Outlook and ProtonMail
Applied to files:
apps/web/app/(app)/accounts/AddAccount.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 apps/web/utils/gmail/**/*.{ts,tsx} : Always use wrapper functions from @/utils/gmail/ for Gmail API operations instead of direct provider API calls
Applied to files:
apps/web/app/(app)/accounts/AddAccount.tsx
📚 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/components/**/*Form*.tsx : Handle form submission results using `result?.serverError` to show error toasts and `toastSuccess` to show success messages after server action completion
Applied to files:
apps/web/app/(app)/accounts/FastmailAppTokenModal.tsx
📚 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/components/**/*Form*.tsx : Use React Hook Form with Zod validation (`zodResolver`) for form handling, with form components using `register`, `handleSubmit`, and error handling from the hook
Applied to files:
apps/web/app/(app)/accounts/FastmailAppTokenModal.tsx
📚 Learning: 2025-11-25T14:36:51.389Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/form-handling.mdc:0-0
Timestamp: 2025-11-25T14:36:51.389Z
Learning: Applies to **/*Form.{ts,tsx} : Validate form inputs before submission using React Hook Form and Zod resolver
Applied to files:
apps/web/app/(app)/accounts/FastmailAppTokenModal.tsx
📚 Learning: 2025-11-25T14:36:51.389Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/form-handling.mdc:0-0
Timestamp: 2025-11-25T14:36:51.389Z
Learning: Applies to **/*Form.{ts,tsx} : Use React Hook Form with Zod for form validation
Applied to files:
apps/web/app/(app)/accounts/FastmailAppTokenModal.tsx
📚 Learning: 2025-12-17T11:18:06.818Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-17T11:18:06.818Z
Learning: Applies to apps/web/**/*{Form,Form.tsx} : Use React Hook Form with Zod validation for form handling
Applied to files:
apps/web/app/(app)/accounts/FastmailAppTokenModal.tsx
📚 Learning: 2025-11-25T14:36:53.147Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/form-handling.mdc:0-0
Timestamp: 2025-11-25T14:36:53.147Z
Learning: Applies to **/*Form.{ts,tsx} : Use React Hook Form with Zod for validation in form components
Applied to files:
apps/web/app/(app)/accounts/FastmailAppTokenModal.tsx
📚 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:
apps/web/app/(app)/accounts/FastmailAppTokenModal.tsx
📚 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/fastmail/client.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/fastmail/client.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 thread operations from @/utils/gmail/thread.ts instead of direct API calls
Applied to files:
apps/web/utils/fastmail/client.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/fastmail/client.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 `client` object with `NEXT_PUBLIC_` prefix and Zod schema validation
Applied to files:
apps/web/utils/fastmail/client.ts
📚 Learning: 2025-11-25T14:36:43.454Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/environment-variables.mdc:0-0
Timestamp: 2025-11-25T14:36:43.454Z
Learning: Applies to apps/web/env.ts : Define environment variables in `apps/web/env.ts` using Zod schema validation, organizing them into `server` and `client` sections
Applied to files:
apps/web/utils/fastmail/client.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} : Don't hardcode sensitive data like API keys and tokens
Applied to files:
apps/web/utils/fastmail/client.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/fastmail/client.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 {.env.example,apps/web/env.ts} : Client-side environment variables must be prefixed with `NEXT_PUBLIC_`
Applied to files:
apps/web/utils/fastmail/client.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/fastmail/client.ts
📚 Learning: 2025-11-25T14:36:43.454Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/environment-variables.mdc:0-0
Timestamp: 2025-11-25T14:36:43.454Z
Learning: Applies to apps/web/env.ts : For client-side environment variables in `apps/web/env.ts`, prefix them with `NEXT_PUBLIC_` and add them to both the `client` and `experimental__runtimeEnv` sections
Applied to files:
apps/web/utils/fastmail/client.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 async functions actually use await
Applied to files:
apps/web/utils/fastmail/client.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/*.validation.ts : Define input validation schemas using Zod in `.validation.ts` files and export both the schema and its inferred TypeScript type
Applied to files:
apps/web/utils/actions/fastmail-app-token.validation.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/*.validation.ts : Define Zod validation schemas in separate `*.validation.ts` files and export both the schema and inferred type (e.g., `CreateExampleBody`)
Applied to files:
apps/web/utils/actions/fastmail-app-token.validation.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/*.validation.ts : Export types from Zod schemas using `z.infer<>` to maintain type safety between validation and client usage
Applied to files:
apps/web/utils/actions/fastmail-app-token.validation.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} : Request bodies should use Zod schemas for validation to ensure type safety and prevent injection attacks
Applied to files:
apps/web/utils/actions/fastmail-app-token.validation.ts
📚 Learning: 2025-11-25T14:36:51.389Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/form-handling.mdc:0-0
Timestamp: 2025-11-25T14:36:51.389Z
Learning: Applies to **/*.validation.ts : Define validation schemas using Zod
Applied to files:
apps/web/utils/actions/fastmail-app-token.validation.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 input parameters must be validated - check for presence, type, and format before use; use Zod schemas to validate request bodies with type guards and constraints
Applied to files:
apps/web/utils/actions/fastmail-app-token.validation.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 : Always define a Zod schema for LLM response validation and make schemas as specific as possible to guide the LLM output
Applied to files:
apps/web/utils/actions/fastmail-app-token.validation.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 : Use Zod schemas for request body validation in API routes
Applied to files:
apps/web/utils/actions/fastmail-app-token.validation.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 : Always validate request bodies using Zod schemas to ensure type safety and prevent invalid data from reaching database operations
Applied to files:
apps/web/utils/actions/fastmail-app-token.validation.ts
🧬 Code graph analysis (2)
apps/web/app/(app)/accounts/AddAccount.tsx (3)
apps/web/utils/llms/config.ts (1)
Provider(3-12)apps/web/components/new-landing/common/Card.tsx (1)
CardContent(10-12)apps/web/app/(app)/accounts/FastmailAppTokenModal.tsx (1)
FastmailAppTokenModal(27-97)
apps/web/utils/fastmail/client.ts (4)
apps/web/utils/logger.ts (1)
createScopedLogger(17-81)apps/web/utils/error.ts (1)
SafeError(58-68)apps/web/utils/fastmail/scopes.ts (1)
SCOPES(8-17)apps/web/utils/auth.ts (1)
saveTokens(456-530)
🔇 Additional comments (10)
apps/web/app/(app)/accounts/AddAccount.tsx (3)
13-19: Well-structured provider abstraction.The
Providertype andPROVIDER_DISPLAY_NAMESmap cleanly support the new Fastmail provider alongside existing ones. This aligns with the project's goal of provider-agnostic design (per retrieved learnings).
27-36: Clean loading state management.The
isAnyLoadingderived flag andsetLoadingMappattern effectively prevent concurrent account linking attempts while keeping the code DRY.
87-106: Good UX with dual Fastmail linking options.Offering both OAuth flow via the main button and App Token fallback via the link provides flexibility for users whose Fastmail setup may not support standard OAuth.
apps/web/app/(app)/accounts/FastmailAppTokenModal.tsx (2)
31-44: Correct error handling pattern.The
onErrorcallback properly extractserror.error.serverErrorwith a fallback message, following the project's guidelines for mutation error handling withtoastError.
70-78: Secure token input.Using
type="password"for the app token field is appropriate since it prevents shoulder-surfing and accidental exposure of the sensitive credential.apps/web/utils/actions/fastmail-app-token.ts (2)
16-29: Proper token validation with SafeError.The action correctly validates the app token by attempting to create a JMAP client, and uses
SafeErrorto return a user-friendly message without exposing internal details. This follows security guidelines.
88-120: Robust account creation with race condition handling.The duplicate error check (lines 121-146) handles the edge case where concurrent requests might create the same account, returning success if the current user owns the newly created account. This is defensive programming.
apps/web/utils/fastmail/client.ts (3)
15-80: Well-implemented retry logic with exponential backoff.The
fetchWithRetryfunction properly handles transient errors (429, 502, 503, 504) with configurable retries and exponential backoff. The logging provides good observability for debugging retry scenarios.
357-369: Robust handling of timestamp format ambiguity.The heuristic at lines 362-365 correctly handles both Unix timestamps in seconds and milliseconds by checking if the value is less than year 3000 in seconds. The comment explains the rationale well.
292-323: Clean client factory with capability validation.The
createFastmailClientfunction properly validates required JMAP capabilities before returning the client, ensuring downstream code won't fail due to missing capabilities.
There was a problem hiding this comment.
5 issues found across 20 files
Prompt for AI agents (all 5 issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="apps/web/utils/fastmail/client.ts">
<violation number="1" location="apps/web/utils/fastmail/client.ts:47">
P2: Retry-After header parsing doesn't handle HTTP-date format. If the server returns a date string instead of seconds, `parseInt` returns NaN causing immediate retries (NaN becomes 0 in setTimeout). The existing Outlook retry utility handles both formats properly.</violation>
</file>
<file name="apps/web/utils/email/fastmail.ts">
<violation number="1" location="apps/web/utils/email/fastmail.ts:1355">
P2: After extracting the email address, the recipient's display name is discarded. The JMAP `to` field supports both `email` and `name` properties. Consider preserving the sender's name for better email formatting.</violation>
<violation number="2" location="apps/web/utils/email/fastmail.ts:1410">
P1: The `to` field uses the full "Name <email>" format string from `email.headers.from` instead of extracting just the email address. JMAP's email field expects only the email address, which will cause delivery failures when the original sender has a display name.</violation>
<violation number="3" location="apps/web/utils/email/fastmail.ts:1782">
P2: Marking a thread as read overwrites the entire keywords map, clearing flags like $flagged/$draft. Patch only $seen instead of replacing all keywords.</violation>
<violation number="4" location="apps/web/utils/email/fastmail.ts:2472">
P2: String concatenation in a loop is O(n²) and will cause severe performance issues for large attachments. Consider using `Buffer.from(buffer).toString('base64')` in Node.js 22+ for efficient base64 encoding.</violation>
</file>
Since this is your first cubic review, here's how it works:
- cubic automatically reviews your code and comments on bugs and improvements
- Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
- Ask questions if you need clarification on any suggestion
Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR
apps/web/utils/fastmail/client.ts
Outdated
| // Check for Retry-After header | ||
| const retryAfter = response.headers.get("Retry-After"); | ||
| const delay = retryAfter | ||
| ? Number.parseInt(retryAfter, 10) * 1000 |
There was a problem hiding this comment.
P2: Retry-After header parsing doesn't handle HTTP-date format. If the server returns a date string instead of seconds, parseInt returns NaN causing immediate retries (NaN becomes 0 in setTimeout). The existing Outlook retry utility handles both formats properly.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/utils/fastmail/client.ts, line 47:
<comment>Retry-After header parsing doesn't handle HTTP-date format. If the server returns a date string instead of seconds, `parseInt` returns NaN causing immediate retries (NaN becomes 0 in setTimeout). The existing Outlook retry utility handles both formats properly.</comment>
<file context>
@@ -0,0 +1,455 @@
+ // Check for Retry-After header
+ const retryAfter = response.headers.get("Retry-After");
+ const delay = retryAfter
+ ? Number.parseInt(retryAfter, 10) * 1000
+ : BASE_RETRY_DELAY * 2 ** attempt;
+
</file context>
✅ Addressed in b0819eb
apps/web/utils/email/fastmail.ts
Outdated
| if (emailIds.length === 0) return; | ||
|
|
||
| // Update $seen keyword | ||
| const update: Record<string, { keywords: Record<string, boolean> }> = {}; |
There was a problem hiding this comment.
P2: Marking a thread as read overwrites the entire keywords map, clearing flags like $flagged/$draft. Patch only $seen instead of replacing all keywords.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/utils/email/fastmail.ts, line 1782:
<comment>Marking a thread as read overwrites the entire keywords map, clearing flags like $flagged/$draft. Patch only $seen instead of replacing all keywords.</comment>
<file context>
@@ -0,0 +1,2887 @@
+ if (emailIds.length === 0) return;
+
+ // Update $seen keyword
+ const update: Record<string, { keywords: Record<string, boolean> }> = {};
+ for (const emailId of emailIds) {
+ update[emailId] = {
</file context>
✅ Addressed in b0819eb
apps/web/utils/email/fastmail.ts
Outdated
| mailboxIds: { [drafts.id]: true }, | ||
| keywords: { $draft: true }, | ||
| from: [{ email: userEmail }], | ||
| to: [{ email: to }], |
There was a problem hiding this comment.
P2: After extracting the email address, the recipient's display name is discarded. The JMAP to field supports both email and name properties. Consider preserving the sender's name for better email formatting.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/utils/email/fastmail.ts, line 1355:
<comment>After extracting the email address, the recipient's display name is discarded. The JMAP `to` field supports both `email` and `name` properties. Consider preserving the sender's name for better email formatting.</comment>
<file context>
@@ -0,0 +1,2887 @@
+ mailboxIds: { [drafts.id]: true },
+ keywords: { $draft: true },
+ from: [{ email: userEmail }],
+ to: [{ email: to }],
+ subject,
+ bodyValues: {
</file context>
✅ Addressed in 9fb10b9
apps/web/utils/email/fastmail.ts
Outdated
| const buffer = await response.arrayBuffer(); | ||
| // Convert ArrayBuffer to base64 using Web APIs | ||
| const bytes = new Uint8Array(buffer); | ||
| let binary = ""; |
There was a problem hiding this comment.
P2: String concatenation in a loop is O(n²) and will cause severe performance issues for large attachments. Consider using Buffer.from(buffer).toString('base64') in Node.js 22+ for efficient base64 encoding.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/utils/email/fastmail.ts, line 2472:
<comment>String concatenation in a loop is O(n²) and will cause severe performance issues for large attachments. Consider using `Buffer.from(buffer).toString('base64')` in Node.js 22+ for efficient base64 encoding.</comment>
<file context>
@@ -0,0 +1,2887 @@
+ const buffer = await response.arrayBuffer();
+ // Convert ArrayBuffer to base64 using Web APIs
+ const bytes = new Uint8Array(buffer);
+ let binary = "";
+ for (let i = 0; i < bytes.byteLength; i++) {
+ binary += String.fromCharCode(bytes[i]);
</file context>
✅ Addressed in 9fb10b9
apps/web/utils/email/fastmail.ts
Outdated
| throw new Error("No identity found for sending"); | ||
| } | ||
|
|
||
| const to = email.headers.from; |
There was a problem hiding this comment.
P1: The to field uses the full "Name " format string from email.headers.from instead of extracting just the email address. JMAP's email field expects only the email address, which will cause delivery failures when the original sender has a display name.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/utils/email/fastmail.ts, line 1410:
<comment>The `to` field uses the full "Name <email>" format string from `email.headers.from` instead of extracting just the email address. JMAP's email field expects only the email address, which will cause delivery failures when the original sender has a display name.</comment>
<file context>
@@ -0,0 +1,2887 @@
+ throw new Error("No identity found for sending");
+ }
+
+ const to = email.headers.from;
+
+ await this.client.request([
</file context>
✅ Addressed in 9fb10b9
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (5)
apps/web/utils/fastmail/client.ts (2)
251-254: Missingawaitonresponse.json()return.The function returns
response.json()without awaiting. While this works because the caller awaits the async function result, it's inconsistent with the pattern elsewhere and could mask JSON parsing errors at the call site rather than within this function.
453-456: Missingawaitonresponse.json()return.Same pattern issue - returning
response.json()without await.apps/web/utils/email/fastmail.ts (3)
1342-1355:draftEmailuses formatted address string in JMAPtofield.When
args.tois not provided,email.headers.from(which may be "Name " format fromparseEmailAddress) is used directly into: [{ email: to }]. JMAP's email address objects expect just the email address in the
1406-1421:replyToEmailhas the same formatted address issue.
email.headers.fromreturns "Name " format, but it's used directly into: [{ email: to }]. This could cause delivery issues when the sender has a display name.
2469-2476: Base64 encoding via string concatenation is O(n²) for large attachments.The loop
binary += String.fromCharCode(bytes[i])creates a new string on each iteration, which is O(n²) for large files. For a 10MB attachment, this could cause significant performance issues.
🧹 Nitpick comments (6)
apps/web/app/api/fastmail/linking/callback/route.ts (2)
186-193: Inconsistentexpires_atstorage format compared tosaveTokensexpectations.The callback stores
expires_atas aDateobject (new Date(Date.now() + tokens.expires_in * 1000)), butsaveTokensinauth.tsexpectsexpires_atas a Unix timestamp in seconds (it multiplies by 1000 when creating the Date). This inconsistency is handled ingetFastmailClientWithRefreshvia the heuristic check (line 372-375 in client.ts), but it would be cleaner to store consistently as Unix seconds.🔎 Consider storing as Unix timestamp in seconds for consistency:
expires_at: tokens.expires_in - ? new Date(Date.now() + tokens.expires_in * 1000) + ? Math.floor(Date.now() / 1000) + tokens.expires_in : null,
255-267: Token update path also has the sameexpires_atformat inconsistency.Same issue as the create path - storing as
Dateobject instead of Unix timestamp in seconds.apps/web/utils/fastmail/client.ts (2)
226-235:getLinkingOAuth2Configreturns empty strings for missing credentials.While the callback route now validates these credentials before use, this function still silently returns empty strings. The callback handles this correctly, but other callers might not.
Consider adding validation here to fail fast, or at minimum add a JSDoc warning that callers must validate the returned values.
406-428: Token refresh validation could be more robust.After refreshing tokens,
tokens.access_tokenis used without validation. If the token endpoint returns an error with HTTP 200 (some OAuth servers do this),newAccessTokencould be undefined.🔎 Add validation for the refreshed access token:
const tokens = await response.json(); const newAccessToken = tokens.access_token; const newRefreshToken = tokens.refresh_token; + if (!newAccessToken) { + logger.error("Token refresh response missing access_token", { emailAccountId }); + throw new SafeError("Token refresh failed: no access token in response"); + } + // Always save tokens after successful refresh...apps/web/utils/email/fastmail.ts (2)
1864-1873:removeThreadLabelsmakes sequential API calls instead of batching.Each label removal is a separate request. For efficiency, consider batching all label removals into a single request.
🔎 Batch label removals into a single request:
async removeThreadLabels( threadId: string, labelIds: string[], ): Promise<void> { if (labelIds.length === 0) return; - for (const labelId of labelIds) { - await this.removeThreadLabel(threadId, labelId); - } + // Get all emails in the thread + const threadResponse = await this.client.request([ + [ + "Email/query", + { + accountId: this.client.accountId, + filter: { inThread: threadId }, + }, + "0", + ], + ]); + + const emailIds = getResponseData<JMAPQueryResponse>( + threadResponse.methodResponses[0], + ).ids; + + if (emailIds.length === 0) return; + + // Remove all labels from all emails in one request + const update: Record<string, Record<string, boolean>> = {}; + for (const emailId of emailIds) { + update[emailId] = {}; + for (const labelId of labelIds) { + update[emailId][`mailboxIds/${labelId}`] = false; + } + } + + await this.client.request([ + [ + "Email/set", + { + accountId: this.client.accountId, + update, + }, + "0", + ], + ]); }
1064-1126: Redundant mailbox lookup inside loop inbulkArchiveFromSenders.The inbox lookup is performed inside the
forloop for each sender. While the cache makes subsequent calls fast, it's cleaner to move it outside the loop.🔎 Move inbox lookup outside the loop:
async bulkArchiveFromSenders( fromEmails: string[], ownerEmail: string, _emailAccountId: string, ): Promise<void> { const log = this.logger.with({ action: "bulkArchiveFromSenders", sendersCount: fromEmails.length, }); + const inbox = await this.getMailboxByRole(FastmailMailbox.INBOX); + if (!inbox) { + log.warn("Inbox mailbox not found"); + return; + } + const archive = await this.getMailboxByRole(FastmailMailbox.ARCHIVE); + for (const sender of fromEmails) { - const inbox = await this.getMailboxByRole(FastmailMailbox.INBOX); - if (!inbox) continue; - // Query for all emails from this sender in inbox // ... rest of the loop - const archive = await this.getMailboxByRole(FastmailMailbox.ARCHIVE);
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
apps/web/app/api/fastmail/linking/callback/route.ts(1 hunks)apps/web/utils/email/fastmail.ts(1 hunks)apps/web/utils/fastmail/client.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (21)
**/*.{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/app/api/fastmail/linking/callback/route.tsapps/web/utils/fastmail/client.tsapps/web/utils/email/fastmail.ts
apps/web/app/api/**/route.ts
📄 CodeRabbit inference engine (.cursor/rules/fullstack-workflow.mdc)
apps/web/app/api/**/route.ts: Create GET API routes usingwithAuthorwithEmailAccountmiddleware inapps/web/app/api/*/route.ts, export response types asGetExampleResponsetype alias for client-side type safety
Always export response types from GET routes asGet[Feature]Responseusing type inference from the data fetching function for type-safe client consumption
Do NOT use POST API routes for mutations - always use server actions withnext-safe-actioninstead
Files:
apps/web/app/api/fastmail/linking/callback/route.ts
**/app/**/route.ts
📄 CodeRabbit inference engine (.cursor/rules/get-api-route.mdc)
**/app/**/route.ts: Always wrap GET API route handlers withwithAuthorwithEmailAccountmiddleware for consistent error handling and authentication in Next.js App Router
Infer and export response type for GET API routes usingAwaited<ReturnType<typeof functionName>>pattern in Next.js
Use Prisma for database queries in GET API routes
Return responses usingNextResponse.json()in GET API routes
Do not use try/catch blocks in GET API route handlers when usingwithAuthorwithEmailAccountmiddleware, as the middleware handles error handling
Files:
apps/web/app/api/fastmail/linking/callback/route.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/app/api/fastmail/linking/callback/route.tsapps/web/utils/fastmail/client.tsapps/web/utils/email/fastmail.ts
apps/web/app/**/[!.]*/route.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Use kebab-case for route directories in Next.js App Router (e.g.,
api/hello-world/route)
Files:
apps/web/app/api/fastmail/linking/callback/route.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
Use@/path aliases for imports from project root
Follow tailwindcss patterns with prettier-plugin-tailwindcss class organization
Prefix client-side environment variables withNEXT_PUBLIC_
Leverage TypeScript inference for better developer experience with type exports from API routes
Files:
apps/web/app/api/fastmail/linking/callback/route.tsapps/web/utils/fastmail/client.tsapps/web/utils/email/fastmail.ts
apps/web/app/api/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/security-audit.mdc)
apps/web/app/api/**/*.{ts,tsx}: API routes must usewithAuth,withEmailAccount, orwithErrormiddleware for authentication
All database queries must include user scoping withemailAccountIdoruserIdfiltering in WHERE clauses
Request parameters must be validated before use; avoid direct parameter usage without type checking
Use generic error messages instead of revealing internal details; throwSafeErrorinstead of exposing user IDs, resource IDs, or system information
API routes should only return necessary fields usingselectin database queries to prevent unintended information disclosure
Cron endpoints must usehasCronSecretorhasPostCronSecretto validate cron requests and prevent unauthorized access
Request bodies should use Zod schemas for validation to ensure type safety and prevent injection attacks
Files:
apps/web/app/api/fastmail/linking/callback/route.ts
**/app/api/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/security.mdc)
**/app/api/**/*.ts: ALL API routes that handle user data MUST use appropriate middleware: usewithEmailAccountfor email-scoped operations, usewithAuthfor user-scoped operations, or usewithErrorwith proper validation for public/custom auth endpoints
UsewithEmailAccountmiddleware for operations scoped to a specific email account, including reading/writing emails, rules, schedules, or any operation usingemailAccountId
UsewithAuthmiddleware for user-level operations such as user settings, API keys, and referrals that use onlyuserId
UsewithErrormiddleware only for public endpoints, custom authentication logic, or cron endpoints. For cron endpoints, MUST usehasCronSecret()orhasPostCronSecret()validation
Cron endpoints without proper authentication can be triggered by anyone. CRITICAL: All cron endpoints MUST validate cron secret usinghasCronSecret(request)orhasPostCronSecret(request)and capture unauthorized attempts withcaptureException()
Always validate request bodies using Zod schemas to ensure type safety and prevent invalid data from reaching database operations
Maintain consistent error response format across all API routes to avoid information disclosure while providing meaningful error feedback
Files:
apps/web/app/api/fastmail/linking/callback/route.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/app/api/fastmail/linking/callback/route.tsapps/web/utils/fastmail/client.tsapps/web/utils/email/fastmail.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/app/api/fastmail/linking/callback/route.tsapps/web/utils/fastmail/client.tsapps/web/utils/email/fastmail.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/app/api/fastmail/linking/callback/route.tsapps/web/utils/fastmail/client.tsapps/web/utils/email/fastmail.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/app/api/fastmail/linking/callback/route.tsapps/web/utils/fastmail/client.tsapps/web/utils/email/fastmail.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/app/api/fastmail/linking/callback/route.tsapps/web/utils/fastmail/client.tsapps/web/utils/email/fastmail.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/app/api/fastmail/linking/callback/route.tsapps/web/utils/fastmail/client.tsapps/web/utils/email/fastmail.ts
apps/web/**/app/**
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Follow NextJS app router structure with (app) directory
Files:
apps/web/app/api/fastmail/linking/callback/route.ts
apps/web/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx,js,jsx}: Use proper error handling with try/catch blocks
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
Files:
apps/web/app/api/fastmail/linking/callback/route.tsapps/web/utils/fastmail/client.tsapps/web/utils/email/fastmail.ts
apps/web/**/*.{ts,tsx,js,jsx,json,css,md}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Format code with Prettier
Files:
apps/web/app/api/fastmail/linking/callback/route.tsapps/web/utils/fastmail/client.tsapps/web/utils/email/fastmail.ts
apps/web/**/app/api/**/route.ts
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/app/api/**/route.ts: Wrap GET API routes withwithAuthorwithEmailAccountmiddleware
Export response types from GET API routes for type-safe client use (e.g.,GetExampleResponse)
Files:
apps/web/app/api/fastmail/linking/callback/route.ts
apps/web/**/{app/api,utils/actions}/**/*.ts
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/{app/api,utils/actions}/**/*.ts: Use server actions for all mutations (create/update/delete operations) instead of POST API routes
UsewithAuthfor user-level operations andwithEmailAccountfor email-account-level operations
Files:
apps/web/app/api/fastmail/linking/callback/route.ts
**/{app,pages}/**/{route,+page}.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
**/{app,pages}/**/{route,+page}.{ts,tsx}: Use middleware wrappers (withError, withAuth, withEmailAccount, withEmailProvider) that automatically create loggers with request context in API routes
Enrich logger context within route handlers using logger.with() to add request-specific fields like messageId
Files:
apps/web/app/api/fastmail/linking/callback/route.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/fastmail/client.tsapps/web/utils/email/fastmail.ts
🧠 Learnings (27)
📓 Common learnings
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: Design Gmail wrapper functions to be provider-agnostic to support future email providers like Outlook and ProtonMail
📚 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/app/api/fastmail/linking/callback/route.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/app/api/**/route.ts : Create GET API routes using `withAuth` or `withEmailAccount` middleware in `apps/web/app/api/*/route.ts`, export response types as `GetExampleResponse` type alias for client-side type safety
Applied to files:
apps/web/app/api/fastmail/linking/callback/route.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/app/api/fastmail/linking/callback/route.ts
📚 Learning: 2025-12-17T11:18:06.818Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-17T11:18:06.818Z
Learning: Applies to apps/web/**/app/api/**/route.ts : Wrap GET API routes with `withAuth` or `withEmailAccount` middleware
Applied to files:
apps/web/app/api/fastmail/linking/callback/route.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/app/api/fastmail/linking/callback/route.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/app/api/fastmail/linking/callback/route.tsapps/web/utils/email/fastmail.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 API routes must use `withAuth`, `withEmailAccount`, or `withError` middleware for authentication
Applied to files:
apps/web/app/api/fastmail/linking/callback/route.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} : API routes must use `withAuth`, `withEmailAccount`, or `withError` middleware for authentication
Applied to files:
apps/web/app/api/fastmail/linking/callback/route.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/app/api/fastmail/linking/callback/route.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/app/api/fastmail/linking/callback/route.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/fastmail/client.tsapps/web/utils/email/fastmail.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 thread operations from @/utils/gmail/thread.ts instead of direct API calls
Applied to files:
apps/web/utils/fastmail/client.tsapps/web/utils/email/fastmail.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/fastmail/client.tsapps/web/utils/email/fastmail.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 `client` object with `NEXT_PUBLIC_` prefix and Zod schema validation
Applied to files:
apps/web/utils/fastmail/client.ts
📚 Learning: 2025-11-25T14:36:43.454Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/environment-variables.mdc:0-0
Timestamp: 2025-11-25T14:36:43.454Z
Learning: Applies to apps/web/env.ts : Define environment variables in `apps/web/env.ts` using Zod schema validation, organizing them into `server` and `client` sections
Applied to files:
apps/web/utils/fastmail/client.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} : Don't hardcode sensitive data like API keys and tokens
Applied to files:
apps/web/utils/fastmail/client.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 {.env.example,apps/web/env.ts} : Client-side environment variables must be prefixed with `NEXT_PUBLIC_`
Applied to files:
apps/web/utils/fastmail/client.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/fastmail/client.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/fastmail/client.ts
📚 Learning: 2025-11-25T14:36:43.454Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/environment-variables.mdc:0-0
Timestamp: 2025-11-25T14:36:43.454Z
Learning: Applies to apps/web/env.ts : For client-side environment variables in `apps/web/env.ts`, prefix them with `NEXT_PUBLIC_` and add them to both the `client` and `experimental__runtimeEnv` sections
Applied to files:
apps/web/utils/fastmail/client.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/fastmail/client.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 async functions actually use await
Applied to files:
apps/web/utils/fastmail/client.tsapps/web/utils/email/fastmail.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/fastmail.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} : Always use wrapper functions from @/utils/gmail/ for Gmail API operations instead of direct provider API calls
Applied to files:
apps/web/utils/email/fastmail.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: Design Gmail wrapper functions to be provider-agnostic to support future email providers like Outlook and ProtonMail
Applied to files:
apps/web/utils/email/fastmail.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 Promise-like statements are handled appropriately
Applied to files:
apps/web/utils/email/fastmail.ts
🧬 Code graph analysis (2)
apps/web/utils/fastmail/client.ts (5)
apps/web/utils/logger.ts (1)
createScopedLogger(17-81)apps/web/utils/error.ts (1)
SafeError(58-68)apps/web/env.ts (1)
env(17-257)apps/web/utils/fastmail/scopes.ts (1)
SCOPES(8-17)apps/web/utils/auth.ts (1)
saveTokens(456-530)
apps/web/utils/email/fastmail.ts (7)
apps/web/utils/fastmail/client.ts (2)
JMAPMethodResponse(159-159)FastmailClient(209-220)apps/web/utils/logger.ts (2)
Logger(5-5)createScopedLogger(17-81)apps/web/utils/types.ts (1)
ParsedMessage(51-73)apps/web/utils/fastmail/constants.ts (1)
FastmailMailbox(22-39)apps/web/utils/label.ts (1)
InboxZeroLabel(63-63)apps/web/app/api/threads/validation.ts (1)
ThreadsQuery(15-15)apps/web/utils/outlook/folders.ts (1)
OutlookFolder(12-17)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Socket Security: Pull Request Alerts
- GitHub Check: cubic · AI code reviewer
🔇 Additional comments (14)
apps/web/app/api/fastmail/linking/callback/route.ts (4)
1-22: LGTM - Imports and setup are well-organized.The imports are appropriately scoped, and the use of
withErrormiddleware is correct for OAuth callback routes that need custom authentication handling.
27-36: Environment variable validation is properly implemented.Good addition of environment variable checks at the start of the handler. This provides clear error messaging when Fastmail OAuth is not configured, matching the pattern used in the Outlook callback.
117-124: Refresh token validation added.Good catch on requiring the refresh_token. The error message helpfully suggests the
offline_accessscope may be missing.
285-320: Merge account flow is well-implemented.The merge flow correctly handles account reassignment, logs appropriate context, and caches the result before redirecting.
apps/web/utils/fastmail/client.ts (2)
44-58: Retry-After header parsing now handles both formats correctly.Good fix - the code now properly handles both numeric seconds and HTTP-date format per RFC 7231. The fallback to exponential backoff when parsing fails is appropriate.
367-379: Clever handling of inconsistentexpires_atstorage formats.The heuristic to detect seconds vs milliseconds based on magnitude (
< 32_503_680_000) is pragmatic but adds complexity. This would be unnecessary if storage was consistent.apps/web/utils/email/fastmail.ts (8)
1-25: Well-organized imports and type definitions.The imports are appropriate, and the file structure with constants and type definitions at the top aids readability.
26-54: Good extraction ofEMAIL_PROPERTIESconstant.This reduces duplication and ensures consistency across Email/get requests.
342-347:parseEmailAddressreturns formatted "Name " string.This is used correctly for display purposes in
ParsedMessage.headers, but is problematic when used directly for JMAP email fields (see later comments).
1781-1787: Keywords update now correctly uses JSON Pointer notation.Good fix - using
"keywords/$seen": readpatches only the$seenkeyword while preserving other keywords like$flagged.
2754-2769:isSentMessageis now correctly synchronous.Good fix - the method now uses the cached mailbox lookup synchronously instead of incorrectly using async callback in
Array.some().
983-1007: Archive operations correctly use JSON Pointer notation.The
archiveThreadmethod properly usesmailboxIds/${inbox.id}patch notation to preserve other mailbox memberships.
1961-1989: Filter operations correctly return unsupported status.Good implementation - JMAP doesn't have native filter support, and the methods appropriately log warnings and return 501 status codes.
202-219: Provider class structure is well-designed.The constructor correctly accepts an optional logger and creates a scoped logger with provider context. The
toJSONmethod aids debugging.
|
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
apps/web/app/(landing)/welcome-redirect/page.tsx (1)
21-29: LGTM! Consider optional performance optimization.The logic correctly handles users from auth-only providers (like Authelia) who don't have email accounts yet. The redirect flow is sound and the security filtering is proper.
Optional: Combine DB queries for better performance
You can reduce database round-trips from 2 to 1 by including the email account check in the initial user query:
const user = await prisma.user.findUnique({ where: { id: session.user.id }, - select: { completedOnboardingAt: true, utms: true }, + select: { + completedOnboardingAt: true, + utms: true, + emailAccounts: { take: 1, select: { id: true } } + }, }); // Session exists but user doesn't - invalid state, log out if (!user) redirect("/logout"); -// Check if user has any email accounts -// Users who logged in with auth-only providers (like Authelia) won't have one yet -const emailAccount = await prisma.emailAccount.findFirst({ - where: { userId: session.user.id }, - select: { id: true }, -}); - // No email account yet - redirect to accounts page to add one -if (!emailAccount) redirect("/accounts"); +if (user.emailAccounts.length === 0) redirect("/accounts");apps/web/app/(landing)/login/LoginForm.tsx (1)
144-156: Use the validatedenvimport instead of directprocess.envaccess.The codebase uses
@t3-oss/env-nextjsfor environment variable validation. Accessingprocess.env.NEXT_PUBLIC_AUTHELIA_ENABLEDdirectly bypasses the Zod validation and type coercion defined inenv.ts.🔎 Proposed fix
+import { env } from "@/env"; // ... existing imports - {process.env.NEXT_PUBLIC_AUTHELIA_ENABLED === "true" && ( + {env.NEXT_PUBLIC_AUTHELIA_ENABLED && (This ensures you get the properly coerced boolean value from the validated environment configuration.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
apps/web/.env.example(1 hunks)apps/web/app/(landing)/login/LoginForm.tsx(3 hunks)apps/web/app/(landing)/welcome-redirect/page.tsx(1 hunks)apps/web/env.ts(3 hunks)apps/web/utils/auth-client.ts(1 hunks)apps/web/utils/auth.ts(4 hunks)
🧰 Additional context used
📓 Path-based instructions (22)
**/*.{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/app/(landing)/welcome-redirect/page.tsxapps/web/utils/auth-client.tsapps/web/utils/auth.tsapps/web/env.tsapps/web/app/(landing)/login/LoginForm.tsx
**/*.{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/app/(landing)/welcome-redirect/page.tsxapps/web/utils/auth-client.tsapps/web/utils/auth.tsapps/web/env.tsapps/web/app/(landing)/login/LoginForm.tsx
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
Use@/path aliases for imports from project root
Follow tailwindcss patterns with prettier-plugin-tailwindcss class organization
Prefix client-side environment variables withNEXT_PUBLIC_
Leverage TypeScript inference for better developer experience with type exports from API routes
Files:
apps/web/app/(landing)/welcome-redirect/page.tsxapps/web/utils/auth-client.tsapps/web/utils/auth.tsapps/web/env.tsapps/web/app/(landing)/login/LoginForm.tsx
**/*.{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/app/(landing)/welcome-redirect/page.tsxapps/web/utils/auth-client.tsapps/web/utils/auth.tsapps/web/env.tsapps/web/app/(landing)/login/LoginForm.tsx
**/*.{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/app/(landing)/welcome-redirect/page.tsxapps/web/utils/auth-client.tsapps/web/utils/auth.tsapps/web/env.tsapps/web/app/(landing)/login/LoginForm.tsx
**/*.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:
apps/web/app/(landing)/welcome-redirect/page.tsxapps/web/app/(landing)/login/LoginForm.tsx
**/*.{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/app/(landing)/welcome-redirect/page.tsxapps/web/utils/auth-client.tsapps/web/utils/auth.tsapps/web/env.tsapps/web/app/(landing)/login/LoginForm.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:
apps/web/app/(landing)/welcome-redirect/page.tsxapps/web/app/(landing)/login/LoginForm.tsx
!(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/app/(landing)/welcome-redirect/page.tsxapps/web/utils/auth-client.tsapps/web/utils/auth.tsapps/web/.env.exampleapps/web/env.tsapps/web/app/(landing)/login/LoginForm.tsx
**/*.{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/app/(landing)/welcome-redirect/page.tsxapps/web/utils/auth-client.tsapps/web/utils/auth.tsapps/web/env.tsapps/web/app/(landing)/login/LoginForm.tsx
apps/web/**/app/**
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Follow NextJS app router structure with (app) directory
Files:
apps/web/app/(landing)/welcome-redirect/page.tsxapps/web/app/(landing)/login/LoginForm.tsx
apps/web/**/*.{tsx,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{tsx,jsx}: Prefer functional components with hooks over class components
Use shadcn/ui components when available
Follow consistent naming conventions with PascalCase for component names
Use LoadingContent component for async data with loading and error states
Useresult?.serverErrorwithtoastErrorandtoastSuccessfor mutation error handling
Files:
apps/web/app/(landing)/welcome-redirect/page.tsxapps/web/app/(landing)/login/LoginForm.tsx
apps/web/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx,js,jsx}: Use proper error handling with try/catch blocks
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
Files:
apps/web/app/(landing)/welcome-redirect/page.tsxapps/web/utils/auth-client.tsapps/web/utils/auth.tsapps/web/env.tsapps/web/app/(landing)/login/LoginForm.tsx
apps/web/**/*.{ts,tsx,js,jsx,json,css,md}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Format code with Prettier
Files:
apps/web/app/(landing)/welcome-redirect/page.tsxapps/web/utils/auth-client.tsapps/web/utils/auth.tsapps/web/env.tsapps/web/app/(landing)/login/LoginForm.tsx
apps/web/**/*.{tsx,jsx,css}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Ensure responsive design with mobile-first approach
Files:
apps/web/app/(landing)/welcome-redirect/page.tsxapps/web/app/(landing)/login/LoginForm.tsx
**/*.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-client.tsapps/web/utils/auth.tsapps/web/env.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-client.tsapps/web/utils/auth.ts
apps/web/**/{.env.example,env.ts,turbo.json}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Add environment variables to
.env.example,env.ts, andturbo.json
Files:
apps/web/.env.exampleapps/web/env.ts
apps/web/env.ts
📄 CodeRabbit inference engine (.cursor/rules/environment-variables.mdc)
apps/web/env.ts: Add server-only environment variables toapps/web/env.tsunder theserverobject with Zod schema validation
Add client-side environment variables toapps/web/env.tsunder theclientobject withNEXT_PUBLIC_prefix and Zod schema validation
Add client-side environment variables toapps/web/env.tsunder theexperimental__runtimeEnvobject to enable runtime access
Files:
apps/web/env.ts
{.env.example,apps/web/env.ts}
📄 CodeRabbit inference engine (.cursor/rules/environment-variables.mdc)
Client-side environment variables must be prefixed with
NEXT_PUBLIC_
Files:
apps/web/env.ts
**/*Form.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)
**/*Form.{ts,tsx}: Use React Hook Form with Zod for validation in form components
Validate form inputs before submission
Show validation errors inline next to form fields
Files:
apps/web/app/(landing)/login/LoginForm.tsx
apps/web/**/*{Form,Form.tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Use React Hook Form with Zod validation for form handling
Files:
apps/web/app/(landing)/login/LoginForm.tsx
🧠 Learnings (29)
📓 Common learnings
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`
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`
📚 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/app/(landing)/welcome-redirect/page.tsxapps/web/utils/auth.ts
📚 Learning: 2025-12-17T11:18:06.818Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-17T11:18:06.818Z
Learning: Applies to apps/web/**/{app/api,utils/actions}/**/*.ts : Use `withAuth` for user-level operations and `withEmailAccount` for email-account-level operations
Applied to files:
apps/web/app/(landing)/welcome-redirect/page.tsxapps/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 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/app/(landing)/welcome-redirect/page.tsxapps/web/utils/auth.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/app/(landing)/welcome-redirect/page.tsxapps/web/utils/auth.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 : Use `withAuth` middleware to get the authenticated user or `withEmailAccount` middleware to get the currently active email account in GET API routes
Applied to files:
apps/web/app/(landing)/welcome-redirect/page.tsx
📚 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/app/(landing)/welcome-redirect/page.tsxapps/web/utils/auth.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/app/(landing)/welcome-redirect/page.tsx
📚 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/app/(landing)/welcome-redirect/page.tsx
📚 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/app/(landing)/welcome-redirect/page.tsx
📚 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 : 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/app/(landing)/welcome-redirect/page.tsx
📚 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.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: Design Gmail wrapper functions to be provider-agnostic to support future email providers like Outlook and ProtonMail
Applied to files:
apps/web/utils/auth.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} : Always use wrapper functions from @/utils/gmail/ for Gmail API operations instead of direct provider API calls
Applied to files:
apps/web/utils/auth.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/auth.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/auth.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 `client` object with `NEXT_PUBLIC_` prefix and Zod schema validation
Applied to files:
apps/web/.env.exampleapps/web/env.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 .env.example : Add new environment variables to `.env.example` with example values
Applied to files:
apps/web/.env.exampleapps/web/env.ts
📚 Learning: 2025-12-17T11:18:06.818Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-17T11:18:06.818Z
Learning: Applies to apps/web/**/{.env.example,env.ts,turbo.json} : Add environment variables to `.env.example`, `env.ts`, and `turbo.json`
Applied to files:
apps/web/.env.exampleapps/web/env.ts
📚 Learning: 2025-11-25T14:36:43.454Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/environment-variables.mdc:0-0
Timestamp: 2025-11-25T14:36:43.454Z
Learning: Applies to apps/web/env.ts : For client-side environment variables in `apps/web/env.ts`, prefix them with `NEXT_PUBLIC_` and add them to both the `client` and `experimental__runtimeEnv` sections
Applied to files:
apps/web/.env.exampleapps/web/env.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 {.env.example,apps/web/env.ts} : Client-side environment variables must be prefixed with `NEXT_PUBLIC_`
Applied to files:
apps/web/.env.exampleapps/web/env.ts
📚 Learning: 2025-07-19T15:06:43.730Z
Learnt from: garnertb
Repo: elie222/inbox-zero PR: 580
File: apps/web/.env.example:4-7
Timestamp: 2025-07-19T15:06:43.730Z
Learning: In apps/web/.env.example, boolean environment variables follow an unquoted convention (e.g., LOG_ZOD_ERRORS=true, AUTH_TRUST_HOST=true). Logical grouping of related variables (like auth variables together) is preferred over strict alphabetical ordering for better developer experience.
Applied to files:
apps/web/.env.example
📚 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/.env.exampleapps/web/env.ts
📚 Learning: 2025-11-25T14:36:43.454Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/environment-variables.mdc:0-0
Timestamp: 2025-11-25T14:36:43.454Z
Learning: Applies to .env.example : Add environment variables to `.env.example` with example values in the format `VARIABLE_NAME=value_example`
Applied to files:
apps/web/.env.exampleapps/web/env.ts
📚 Learning: 2025-12-17T11:18:06.818Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-17T11:18:06.818Z
Learning: Applies to apps/web/**/*.{ts,tsx} : Prefix client-side environment variables with `NEXT_PUBLIC_`
Applied to files:
apps/web/.env.exampleapps/web/env.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 server-only environment variables to `apps/web/env.ts` under the `server` object with Zod schema validation
Applied to files:
apps/web/.env.exampleapps/web/env.ts
📚 Learning: 2025-11-25T14:36:43.454Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/environment-variables.mdc:0-0
Timestamp: 2025-11-25T14:36:43.454Z
Learning: Applies to apps/web/env.ts : Define environment variables in `apps/web/env.ts` using Zod schema validation, organizing them into `server` and `client` sections
Applied to files:
apps/web/env.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 turbo.json : Add new environment variables to `turbo.json` under `tasks.build.env` as a global dependency for the build task
Applied to files:
apps/web/env.ts
📚 Learning: 2025-11-25T14:36:43.454Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/environment-variables.mdc:0-0
Timestamp: 2025-11-25T14:36:43.454Z
Learning: Applies to turbo.json : Add environment variables to `turbo.json` under `tasks.build.env` array to declare build-time dependencies
Applied to files:
apps/web/env.ts
🧬 Code graph analysis (3)
apps/web/utils/auth-client.ts (1)
apps/web/env.ts (1)
env(17-262)
apps/web/utils/auth.ts (2)
apps/web/env.ts (1)
env(17-262)apps/web/utils/email/provider-types.ts (1)
isFastmailProvider(9-11)
apps/web/app/(landing)/login/LoginForm.tsx (1)
apps/web/utils/config.ts (1)
WELCOME_PATH(24-24)
🔇 Additional comments (9)
apps/web/utils/auth-client.ts (1)
4-12: LGTM!The
genericOAuthClientplugin is correctly imported and added to the client-side auth configuration, enabling support for generic OAuth providers like Authelia. This properly complements the server-sidegenericOAuthplugin configuration inauth.ts.apps/web/utils/auth.ts (4)
69-87: LGTM! Authelia OIDC provider configuration looks correct.The conditional spread pattern cleanly enables Authelia only when all required environment variables are present. The scopes (
openid,profile,
374-397: Authelia profile fetching implementation is correct.The error handling is appropriate, and the fallback from
nametopreferred_usernameis a good practice for OIDC providers. The lowercasing of email is consistent with other providers.
399-411: LGTM!The Fastmail profile fetching correctly delegates to the dedicated utility and maintains consistency with other provider implementations. The comment explaining the
undefinedimage is helpful.
414-423: LGTM!Correctly skipping
EmailAccountcreation for auth-only providers like Authelia. The logging provides good observability for debugging account linking flows.apps/web/env.ts (2)
29-33: LGTM!Server-side environment variables for Fastmail and Authelia are correctly defined with optional string schemas. The inline comment for
AUTHELIA_ISSUER_URLprovides helpful documentation.
198-198: LGTM!The client-side
NEXT_PUBLIC_AUTHELIA_ENABLEDvariable is correctly:
- Prefixed with
NEXT_PUBLIC_as required- Defined with
z.coerce.boolean().optional()for proper type coercion- Added to
experimental__runtimeEnvfor runtime accessAlso applies to: 260-260
apps/web/app/(landing)/login/LoginForm.tsx (2)
20-20: LGTM!The
ShieldCheckIconimport andloadingAutheliastate follow the established patterns for other sign-in providers.Also applies to: 28-28
68-85: LGTM!The
handleAutheliaSignInfunction correctly usessignIn.oauth2withproviderId: "authelia"for the generic OAuth flow. Error handling is consistent with the Google and Microsoft handlers.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
apps/web/utils/email/fastmail.ts (2)
1113-1115: Consider moving mailbox lookup outside the loop for clarity.The
inboxmailbox lookup is repeated inside the sender loop. While this is efficient due to caching (the cache makes subsequent calls O(1)), moving it outside the loop would improve code readability and make the caching optimization more explicit.🔎 Suggested refactor
+ const inbox = await this.getMailboxByRole(FastmailMailbox.INBOX); + if (!inbox) { + log.warn("Inbox mailbox not found"); + return; + } + for (const sender of fromEmails) { - const inbox = await this.getMailboxByRole(FastmailMailbox.INBOX); - if (!inbox) continue; - // Query for all emails from this sender in inbox
1903-1912: Consider batching label removal operations.The method makes sequential API calls for each label removal, requiring N round trips for N labels. Since JMAP's
Email/setsupports batching multiple updates in a single request, you could optimize this by removing all labels in one call.🔎 Suggested optimization
async removeThreadLabels( threadId: string, labelIds: string[], ): Promise<void> { if (labelIds.length === 0) return; - for (const labelId of labelIds) { - await this.removeThreadLabel(threadId, labelId); - } + // Get all emails in the thread + const threadResponse = await this.client.request([ + [ + "Email/query", + { + accountId: this.client.accountId, + filter: { inThread: threadId }, + }, + "0", + ], + ]); + + const emailIds = getResponseData<JMAPQueryResponse>( + threadResponse.methodResponses[0], + ).ids; + + if (emailIds.length === 0) return; + + // Remove all labels from all emails in one batch + const update: Record<string, Record<string, boolean>> = {}; + for (const emailId of emailIds) { + update[emailId] = {}; + for (const labelId of labelIds) { + update[emailId][`mailboxIds/${labelId}`] = false; + } + } + + await this.client.request([ + [ + "Email/set", + { + accountId: this.client.accountId, + update, + }, + "0", + ], + ]); }
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
apps/web/providers/EmailProvider.tsx(2 hunks)apps/web/utils/email/fastmail.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (16)
**/*.{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/providers/EmailProvider.tsxapps/web/utils/email/fastmail.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/providers/EmailProvider.tsxapps/web/utils/email/fastmail.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
Use@/path aliases for imports from project root
Follow tailwindcss patterns with prettier-plugin-tailwindcss class organization
Prefix client-side environment variables withNEXT_PUBLIC_
Leverage TypeScript inference for better developer experience with type exports from API routes
Files:
apps/web/providers/EmailProvider.tsxapps/web/utils/email/fastmail.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/providers/EmailProvider.tsxapps/web/utils/email/fastmail.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/providers/EmailProvider.tsxapps/web/utils/email/fastmail.ts
**/*.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:
apps/web/providers/EmailProvider.tsx
**/*.{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/providers/EmailProvider.tsxapps/web/utils/email/fastmail.ts
**/*.{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:
apps/web/providers/EmailProvider.tsx
!(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/providers/EmailProvider.tsxapps/web/utils/email/fastmail.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/providers/EmailProvider.tsxapps/web/utils/email/fastmail.ts
apps/web/**/*.{tsx,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{tsx,jsx}: Prefer functional components with hooks over class components
Use shadcn/ui components when available
Follow consistent naming conventions with PascalCase for component names
Use LoadingContent component for async data with loading and error states
Useresult?.serverErrorwithtoastErrorandtoastSuccessfor mutation error handling
Files:
apps/web/providers/EmailProvider.tsx
apps/web/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx,js,jsx}: Use proper error handling with try/catch blocks
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
Files:
apps/web/providers/EmailProvider.tsxapps/web/utils/email/fastmail.ts
apps/web/**/*.{ts,tsx,js,jsx,json,css,md}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Format code with Prettier
Files:
apps/web/providers/EmailProvider.tsxapps/web/utils/email/fastmail.ts
apps/web/**/*.{tsx,jsx,css}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Ensure responsive design with mobile-first approach
Files:
apps/web/providers/EmailProvider.tsx
**/*.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/fastmail.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/fastmail.ts
🧠 Learnings (9)
📚 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/providers/EmailProvider.tsxapps/web/utils/email/fastmail.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/providers/EmailProvider.tsxapps/web/utils/email/fastmail.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} : Always use wrapper functions from @/utils/gmail/ for Gmail API operations instead of direct provider API calls
Applied to files:
apps/web/providers/EmailProvider.tsxapps/web/utils/email/fastmail.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 thread operations from @/utils/gmail/thread.ts instead of direct API calls
Applied to files:
apps/web/providers/EmailProvider.tsxapps/web/utils/email/fastmail.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/providers/EmailProvider.tsxapps/web/utils/email/fastmail.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: Design Gmail wrapper functions to be provider-agnostic to support future email providers like Outlook and ProtonMail
Applied to files:
apps/web/providers/EmailProvider.tsxapps/web/utils/email/fastmail.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/email/fastmail.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 async functions actually use await
Applied to files:
apps/web/utils/email/fastmail.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 Promise-like statements are handled appropriately
Applied to files:
apps/web/utils/email/fastmail.ts
🧬 Code graph analysis (2)
apps/web/providers/EmailProvider.tsx (1)
apps/web/utils/email/provider-types.ts (1)
isFastmailProvider(9-11)
apps/web/utils/email/fastmail.ts (8)
apps/web/utils/fastmail/client.ts (3)
JMAPMethodResponse(159-159)FastmailClient(209-220)getAccessTokenFromClient(463-465)apps/web/utils/email/types.ts (1)
EmailProvider(45-245)apps/web/utils/logger.ts (2)
Logger(5-5)createScopedLogger(17-81)apps/web/utils/types.ts (1)
ParsedMessage(51-73)apps/web/utils/fastmail/constants.ts (1)
FastmailMailbox(22-39)apps/web/utils/label.ts (1)
InboxZeroLabel(63-63)apps/web/app/api/threads/validation.ts (1)
ThreadsQuery(15-15)apps/web/utils/outlook/folders.ts (1)
OutlookFolder(12-17)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: cubic · AI code reviewer
🔇 Additional comments (1)
apps/web/providers/EmailProvider.tsx (1)
10-10: LGTM! Fastmail label color handling is correct.The implementation correctly identifies Fastmail providers and returns
undefinedfor label colors, which aligns with JMAP's mailbox model that doesn't support color attributes. The explanatory comment is helpful.Also applies to: 56-59
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
apps/web/utils/email/fastmail.ts (2)
2560-2573: Optimize base64 encoding to avoid O(n²) string concatenation.Lines 2563-2566 use string concatenation in a loop to build the binary string, which is O(n²) due to string immutability in JavaScript. For large attachments (e.g., 10MB+), this will cause severe performance degradation and potential timeouts.
🔎 Proposed fix using Buffer (Node.js)
const buffer = await response.arrayBuffer(); -// Convert ArrayBuffer to base64 using Web APIs -const bytes = new Uint8Array(buffer); -let binary = ""; -for (let i = 0; i < bytes.byteLength; i++) { - binary += String.fromCharCode(bytes[i]); -} -const base64 = btoa(binary); +// Convert ArrayBuffer to base64 efficiently +const base64 = Buffer.from(buffer).toString('base64'); return { data: base64, size: buffer.byteLength, };
1462-1530: Extract email address from formatted string in reply recipient.Line 1488 assigns
email.headers.fromdirectly toto, which contains the full "Name " format. Line 1499 then uses this in the JMAP🔎 Proposed fix
+import { isValidEmail, extractEmailAddress } from "@/utils/email"; // ... in replyToEmail method: const to = email.headers.from; +const toEmail = extractEmailAddress(to); await this.client.request([ [ "Email/set", { accountId: this.client.accountId, create: { reply: { mailboxIds: { [sent.id]: true }, from: [{ email: identity.email, name: identity.name }], - to: [{ email: to }], + to: [{ email: toEmail }], subject: `Re: ${email.subject}`,
🧹 Nitpick comments (2)
apps/web/utils/email/fastmail.ts (2)
1-7: Remove unused type imports.
JMAPMethodCallandJMAPErrorare imported but never used in this file.🔎 Suggested fix
import type { FastmailClient, - JMAPMethodCall, JMAPMethodResponse, - JMAPError, } from "@/utils/fastmail/client";
1943-1952: Consider batching label removals for better performance.This method makes sequential API calls in a loop. For multiple labels, consider batching all updates into a single
Email/setrequest by building the update object with all label removals at once (similar to the pattern used inarchiveThreadandbulkArchiveFromSenders).
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
apps/web/utils/email.ts(1 hunks)apps/web/utils/email/fastmail.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (12)
**/*.{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.tsapps/web/utils/email/fastmail.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.tsapps/web/utils/email/fastmail.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
Use@/path aliases for imports from project root
Follow tailwindcss patterns with prettier-plugin-tailwindcss class organization
Prefix client-side environment variables withNEXT_PUBLIC_
Leverage TypeScript inference for better developer experience with type exports from API routes
Files:
apps/web/utils/email.tsapps/web/utils/email/fastmail.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.tsapps/web/utils/email/fastmail.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.tsapps/web/utils/email/fastmail.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.tsapps/web/utils/email/fastmail.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.tsapps/web/utils/email/fastmail.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.tsapps/web/utils/email/fastmail.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.tsapps/web/utils/email/fastmail.ts
apps/web/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx,js,jsx}: Use proper error handling with try/catch blocks
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
Files:
apps/web/utils/email.tsapps/web/utils/email/fastmail.ts
apps/web/**/*.{ts,tsx,js,jsx,json,css,md}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Format code with Prettier
Files:
apps/web/utils/email.tsapps/web/utils/email/fastmail.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.tsapps/web/utils/email/fastmail.ts
🧠 Learnings (9)
📚 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.tsapps/web/utils/email/fastmail.ts
📚 Learning: 2025-12-17T11:18:06.818Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-17T11:18:06.818Z
Learning: Applies to apps/web/**/{app/api,utils/actions}/**/*.ts : Use `withAuth` for user-level operations and `withEmailAccount` for email-account-level operations
Applied to files:
apps/web/utils/email.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/email.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} : Always use wrapper functions from @/utils/gmail/ for Gmail API operations instead of direct provider API calls
Applied to files:
apps/web/utils/email.tsapps/web/utils/email/fastmail.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.tsapps/web/utils/email/fastmail.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/fastmail.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/fastmail.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 async functions actually use await
Applied to files:
apps/web/utils/email/fastmail.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 Promise-like statements are handled appropriately
Applied to files:
apps/web/utils/email/fastmail.ts
🔇 Additional comments (2)
apps/web/utils/email.ts (1)
54-56: LGTM!The export addition enables the Fastmail provider to validate email addresses before bulk operations, which is a good safety practice.
apps/web/utils/email/fastmail.ts (1)
1116-1121: Excellent safety validation for bulk operations.The email validation check before processing bulk operations is a good defensive practice that prevents accidental operations with malformed data.
|
@elie222 Seems to be working now, had it running for most of the day just havent customised the rules yet. |
This implements Fastmail as a third email provider alongside Gmail and Microsoft/Outlook. Uses the JMAP (JSON Meta Application Protocol) which is Fastmail's native API. Key changes: - Add FastmailProvider class implementing the EmailProvider interface - Add JMAP client utilities for session management and API calls - Add OAuth flow routes for Fastmail account linking - Update provider factory to support Fastmail - Add Fastmail to UI account linking options - Add environment variables for FASTMAIL_CLIENT_ID and FASTMAIL_CLIENT_SECRET Features supported: - Email read/send/reply/forward - Thread management (archive, trash, mark read) - Mailbox/folder management (JMAP uses mailboxes instead of Gmail labels) - Bulk operations - Attachments Note: Some features like Gmail-style filters and Pub/Sub watching use different mechanisms in JMAP and have limited/no support initially.
- Fix JMAP mailboxIds patching to use JSON Pointer notation instead of replacing the entire object, preserving other mailbox memberships - Add FASTMAIL_CLIENT_ID and FASTMAIL_CLIENT_SECRET to .env.example - Add Fastmail env vars to turbo.json build.env - Add comprehensive JSDoc documentation to Fastmail modules
- Add uploadBlob method to upload files to Fastmail using JMAP blob upload - Update sendEmailWithHtml to upload and include attachments in emails - Previously the attachments parameter was declared but never used
The core JMAP scope (urn:ietf:params:jmap:core) is required for all JMAP operations and should be requested before other JMAP scopes.
- Fix token expiry check: expiresAt is stored in seconds but was compared against Date.now() milliseconds - now multiplies by 1000 for correct comparison - Fix isSentMessage: async callback in some() doesn't work - now uses synchronous cache lookup with proper loop - Add env var validation in callback route: check FASTMAIL_CLIENT_ID and FASTMAIL_CLIENT_SECRET before proceeding - Replace Google-specific access_type: "offline" with OIDC standard offline_access scope for getting refresh tokens
Critical fixes: - checkIfReplySent now returns false on error (was silently skipping replies) - Fixed token expiration handling for both seconds and milliseconds formats - Always save tokens after refresh to handle rotated refresh tokens - Added configuration validation in auth-url route High priority fixes: - labelMessage only retries on specific JMAP errors (invalidArguments/notFound) - Added JMAP method-level error checking (checkJMAPErrors) - Added email domain validation to prevent invalid JMAP filters - Added try-catch to ensureMailboxCache, getThreads, getThread methods - Added try-catch with logging for getUserInfo in callback Medium priority fixes: - Added exponential backoff retry logic for transient errors (429/5xx) - Added session capabilities validation for required JMAP capabilities - Parallelized attachment uploads using Promise.all - Fixed keyword handling logic for UNREAD label Low priority fixes: - Extracted EMAIL_PROPERTIES constant to reduce duplication - Added captureException for token exchange failures - Added fallback values for optional scope and token_type fields 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
apps/web/app/(app)/accounts/AddAccount.tsx (1)
89-110: Fastmail CTA + app-token modal wiring is correct; consider gating modal behind the flagThe Fastmail button and “Use App Token instead” CTA are properly gated behind
fastmailEnabledand respectisAnyLoading, andFastmailAppTokenModalis wired with simpleopen/onOpenChangestate. Functionally this looks good.If you want to avoid mounting the modal when Fastmail is entirely disabled (minor clarity/optimization), you could also guard the modal with the same flag:
Proposed optional refactor
- <FastmailAppTokenModal - open={showAppTokenModal} - onOpenChange={setShowAppTokenModal} - /> + {fastmailEnabled && ( + <FastmailAppTokenModal + open={showAppTokenModal} + onOpenChange={setShowAppTokenModal} + /> + )}As per coding guidelines, this keeps the Fastmail-specific UI fully behind its feature flag without changing behavior.
Also applies to: 117-120
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
apps/web/.env.example(1 hunks)apps/web/app/(app)/accounts/AddAccount.tsx(5 hunks)apps/web/env.ts(3 hunks)apps/web/hooks/useFeatureFlags.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/web/.env.example
- apps/web/env.ts
🧰 Additional context used
📓 Path-based instructions (21)
**/*.{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/hooks/useFeatureFlags.tsapps/web/app/(app)/accounts/AddAccount.tsx
apps/web/hooks/use*.ts
📄 CodeRabbit inference engine (.cursor/rules/fullstack-workflow.mdc)
apps/web/hooks/use*.ts: Use SWR hooks for client-side data fetching, with hooks stored inapps/web/hooks/use*.tsthat return typed responses from GET API routes
Callmutate()on SWR hooks after successful mutations to refresh cached data
apps/web/hooks/use*.ts: Use theuseprefix for custom hook filenames (e.g.,useAccounts.ts)
For data fetching in custom hooks, prefer usinguseSWRand wrap it to handle API endpoint URL, returning data, loading state, error state, and potentially themutatefunction
Create dedicated hooks for specific data types (e.g.,useAccounts,useLabels) to wrapuseSWRfor individual API endpoints
Files:
apps/web/hooks/useFeatureFlags.ts
apps/web/hooks/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/hooks.mdc)
Place custom hooks in the
apps/web/hooks/directory
Files:
apps/web/hooks/useFeatureFlags.ts
apps/web/hooks/useFeatureFlags.ts
📄 CodeRabbit inference engine (.cursor/rules/posthog-feature-flags.mdc)
Feature flag hooks should be defined in
apps/web/hooks/useFeatureFlags.tswith two patterns: boolean flags usinguseFeatureFlagEnabled("key")and variant flags usinguseFeatureFlagVariantKey("key")with type casting
Files:
apps/web/hooks/useFeatureFlags.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/hooks/useFeatureFlags.tsapps/web/app/(app)/accounts/AddAccount.tsx
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
Use@/path aliases for imports from project root
Follow tailwindcss patterns with prettier-plugin-tailwindcss class organization
Prefix client-side environment variables withNEXT_PUBLIC_
Leverage TypeScript inference for better developer experience with type exports from API routes
Files:
apps/web/hooks/useFeatureFlags.tsapps/web/app/(app)/accounts/AddAccount.tsx
**/*.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/hooks/useFeatureFlags.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/hooks/useFeatureFlags.tsapps/web/app/(app)/accounts/AddAccount.tsx
**/*.{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/hooks/useFeatureFlags.tsapps/web/app/(app)/accounts/AddAccount.tsx
**/*.{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/hooks/useFeatureFlags.tsapps/web/app/(app)/accounts/AddAccount.tsx
!(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/hooks/useFeatureFlags.tsapps/web/app/(app)/accounts/AddAccount.tsx
**/*.{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/hooks/useFeatureFlags.tsapps/web/app/(app)/accounts/AddAccount.tsx
apps/web/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx,js,jsx}: Use proper error handling with try/catch blocks
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
Files:
apps/web/hooks/useFeatureFlags.tsapps/web/app/(app)/accounts/AddAccount.tsx
apps/web/**/*.{ts,tsx,js,jsx,json,css,md}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Format code with Prettier
Files:
apps/web/hooks/useFeatureFlags.tsapps/web/app/(app)/accounts/AddAccount.tsx
apps/web/**/hooks/*.ts
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Use SWR for client-side data fetching with type-safe response types
Files:
apps/web/hooks/useFeatureFlags.ts
apps/web/app/(app)/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
apps/web/app/(app)/**/*.{ts,tsx}: Components for the page are either put inpage.tsx, or in theapps/web/app/(app)/PAGE_NAMEfolder
If we're in a deeply nested component we will useswrto fetch via API
If you need to useonClickin a component, that component is a client component and file must start withuse client
Files:
apps/web/app/(app)/accounts/AddAccount.tsx
**/*.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:
apps/web/app/(app)/accounts/AddAccount.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:
apps/web/app/(app)/accounts/AddAccount.tsx
apps/web/**/app/**
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Follow NextJS app router structure with (app) directory
Files:
apps/web/app/(app)/accounts/AddAccount.tsx
apps/web/**/*.{tsx,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{tsx,jsx}: Prefer functional components with hooks over class components
Use shadcn/ui components when available
Follow consistent naming conventions with PascalCase for component names
Use LoadingContent component for async data with loading and error states
Useresult?.serverErrorwithtoastErrorandtoastSuccessfor mutation error handling
Files:
apps/web/app/(app)/accounts/AddAccount.tsx
apps/web/**/*.{tsx,jsx,css}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Ensure responsive design with mobile-first approach
Files:
apps/web/app/(app)/accounts/AddAccount.tsx
🧠 Learnings (15)
📚 Learning: 2025-11-25T14:38:27.988Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/posthog-feature-flags.mdc:0-0
Timestamp: 2025-11-25T14:38:27.988Z
Learning: Applies to apps/web/hooks/useFeatureFlags.ts : For early access features, create boolean flag hooks using `useFeatureFlagEnabled` with the pattern `export function use[FeatureName]Enabled()`
Applied to files:
apps/web/hooks/useFeatureFlags.ts
📚 Learning: 2025-11-25T14:38:32.328Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/posthog-feature-flags.mdc:0-0
Timestamp: 2025-11-25T14:38:32.328Z
Learning: Applies to apps/web/hooks/useFeatureFlags.ts : Feature flag hooks should be defined in `apps/web/hooks/useFeatureFlags.ts` with two patterns: boolean flags using `useFeatureFlagEnabled("key")` and variant flags using `useFeatureFlagVariantKey("key")` with type casting
Applied to files:
apps/web/hooks/useFeatureFlags.ts
📚 Learning: 2025-11-25T14:38:27.988Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/posthog-feature-flags.mdc:0-0
Timestamp: 2025-11-25T14:38:27.988Z
Learning: Applies to apps/web/hooks/useFeatureFlags.ts : All feature flag hooks should be defined in `apps/web/hooks/useFeatureFlags.ts`
Applied to files:
apps/web/hooks/useFeatureFlags.ts
📚 Learning: 2025-11-25T14:38:32.328Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/posthog-feature-flags.mdc:0-0
Timestamp: 2025-11-25T14:38:32.328Z
Learning: Applies to **/*.{ts,tsx} : For early access feature flags, create hooks using the naming convention `use[FeatureName]Enabled` that return a boolean from `useFeatureFlagEnabled("flag-key")`
Applied to files:
apps/web/hooks/useFeatureFlags.ts
📚 Learning: 2025-11-25T14:38:27.988Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/posthog-feature-flags.mdc:0-0
Timestamp: 2025-11-25T14:38:27.988Z
Learning: Applies to apps/web/hooks/useFeatureFlags.ts : Use `use[FeatureName]Enabled` naming convention for boolean feature flag hooks and `use[FeatureName]Variant` for A/B test variant hooks
Applied to files:
apps/web/hooks/useFeatureFlags.ts
📚 Learning: 2025-11-25T14:38:32.328Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/posthog-feature-flags.mdc:0-0
Timestamp: 2025-11-25T14:38:32.328Z
Learning: Centralize all feature flag hooks in `apps/web/hooks/useFeatureFlags.ts` rather than scattering them across multiple files
Applied to files:
apps/web/hooks/useFeatureFlags.ts
📚 Learning: 2025-11-25T14:38:32.328Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/posthog-feature-flags.mdc:0-0
Timestamp: 2025-11-25T14:38:32.328Z
Learning: Applies to **/*.{ts,tsx} : For A/B test variant flags, create hooks using the naming convention `use[FeatureName]Variant` that define variant types, use `useFeatureFlagVariantKey()` with type casting, and provide a default "control" fallback
Applied to files:
apps/web/hooks/useFeatureFlags.ts
📚 Learning: 2025-11-25T14:38:27.988Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/posthog-feature-flags.mdc:0-0
Timestamp: 2025-11-25T14:38:27.988Z
Learning: Applies to apps/web/hooks/useFeatureFlags.ts : For A/B test variants, create hooks using `useFeatureFlagVariantKey` with the pattern `export function use[FeatureName]Variant()` and always provide a default fallback value
Applied to files:
apps/web/hooks/useFeatureFlags.ts
📚 Learning: 2025-11-25T14:38:27.988Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/posthog-feature-flags.mdc:0-0
Timestamp: 2025-11-25T14:38:27.988Z
Learning: Applies to apps/web/hooks/useFeatureFlags.ts : Always define explicit types for variant flags and provide a default/control fallback value
Applied to files:
apps/web/hooks/useFeatureFlags.ts
📚 Learning: 2025-11-25T14:38:27.988Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/posthog-feature-flags.mdc:0-0
Timestamp: 2025-11-25T14:38:27.988Z
Learning: Applies to apps/web/hooks/useFeatureFlags.ts : Use kebab-case for feature flag keys (e.g., `inbox-cleaner`, `pricing-options-2`)
Applied to files:
apps/web/hooks/useFeatureFlags.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/app/(app)/accounts/AddAccount.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 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/app/(app)/accounts/AddAccount.tsx
📚 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/app/(app)/accounts/AddAccount.tsx
📚 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/app/(app)/accounts/AddAccount.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: Design Gmail wrapper functions to be provider-agnostic to support future email providers like Outlook and ProtonMail
Applied to files:
apps/web/app/(app)/accounts/AddAccount.tsx
🧬 Code graph analysis (2)
apps/web/hooks/useFeatureFlags.ts (1)
apps/web/env.ts (1)
env(17-264)
apps/web/app/(app)/accounts/AddAccount.tsx (2)
apps/web/hooks/useFeatureFlags.ts (1)
useFastmailEnabled(19-21)apps/web/app/(app)/accounts/FastmailAppTokenModal.tsx (1)
FastmailAppTokenModal(27-97)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: cubic · AI code reviewer
🔇 Additional comments (2)
apps/web/hooks/useFeatureFlags.ts (1)
19-21: Fastmail feature-flag hook matches existing env-backed pattern
useFastmailEnabledcleanly wrapsenv.NEXT_PUBLIC_FASTMAIL_ENABLED, consistent withuseMeetingBriefsEnabled/useIntegrationsEnabledand the centralization of feature-flag hooks in this file. No changes suggested.Based on learnings, this is the intended place and pattern for new boolean feature flags.
apps/web/app/(app)/accounts/AddAccount.tsx (1)
10-20: Provider union + shared loading state refactor looks solidTyping
Provideras a union and driving both the loading setters and error titles offProvider/PROVIDER_DISPLAY_NAMESmakes the flow extensible and keepshandleAddAccountDRY. The aggregatedisAnyLoadingflag correctly prevents concurrent link starts across Google/Microsoft/Fastmail and is wired into all primary CTAs. No behavioral issues spotted.As per coding guidelines, this centralization of provider-specific behavior improves maintainability.
Also applies to: 25-38, 47-47, 63-63, 79-79
For self-hosted users who don't have OAuth client credentials, this adds the ability to connect Fastmail using an app token generated in Fastmail settings. - Add "Use App Token instead" link below Fastmail OAuth button - Create modal dialog for app token entry - Validate token by connecting to JMAP before saving - Store app token accounts with type="app_token" and no refresh token - Modify Fastmail client to skip token refresh for app token accounts 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix Retry-After header parsing: handle HTTP-date strings per RFC 7231, previously would cause immediate retry (0ms) when header was a date - Add refresh_token validation in OAuth callback: fail early if no refresh token to prevent creating broken accounts that can't refresh - Fix markReadThread keywords: use JMAP path notation "keywords/$seen" instead of replacing entire keywords object, preserving other flags like $flagged 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Two issues fixed:
1. Pagination only loading 20 emails:
- Added `calculateTotal: true` to Email/query requests (required by JMAP spec)
- Created `calculateNextPageToken()` helper with fallback for robustness
- Applied to getMessagesWithPagination, getMessagesFromSender, getThreadsWithQuery
2. Sent emails not appearing in analytics:
- Added SENT/INBOX synthetic labels in parseJMAPEmail based on mailbox roles
- Ensures compatibility with Gmail-style label checks (labelIds.includes("SENT"))
- Added ensureMailboxCache() calls before parsing to guarantee cache availability
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…nbox JMAP requires `null` to remove emails from mailboxes, not `false`. The previous code was only adding emails to archive/trash without actually removing them from the inbox. Changes: - Export isValidEmail for use in bulk operation validation - Add email validation guard to prevent accidental bulk operations - Fix all mailbox removal operations to use `null` instead of `false` - Add logging for bulk operation query results 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Hide Fastmail provider behind NEXT_PUBLIC_FASTMAIL_ENABLED env var (defaults to false) to allow controlled rollout. This follows the existing pattern used for other optional features like integrations and meeting briefs. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
d09eff6 to
bd68c01
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (3)
apps/web/utils/email/fastmail.ts (3)
1420-1433: Extract email address from formatted string for JMAP email field.The
tovariable may contain the full "Name " format fromemail.headers.from(viaparseEmailAddress), but JMAP'sargs.tois not provided and the original sender has a display name.🔎 Proposed fix
+import { extractEmailAddress } from "@/utils/email"; // ... in draftEmail method: -const to = args.to || email.headers.from; +const toRaw = args.to || email.headers.from; +const to = extractEmailAddress(toRaw) || toRaw; const subject = args.subject || `Re: ${email.subject}`;
1488-1499: Same issue: Extract email address from formatted string.Similar to
draftEmail, thetovariable contains the formatted "Name " string fromemail.headers.from, but JMAP expects only the email address in the🔎 Proposed fix
+import { extractEmailAddress } from "@/utils/email"; // ... in replyToEmail method: -const to = email.headers.from; +const to = extractEmailAddress(email.headers.from) || email.headers.from;
2560-2572: String concatenation in loop is O(n²) for large attachments.The base64 conversion uses string concatenation in a loop, which has O(n²) time complexity and will cause severe performance issues for large attachments.
🔎 Use Buffer for efficient base64 encoding
const buffer = await response.arrayBuffer(); - // Convert ArrayBuffer to base64 using Web APIs - const bytes = new Uint8Array(buffer); - let binary = ""; - for (let i = 0; i < bytes.byteLength; i++) { - binary += String.fromCharCode(bytes[i]); - } - const base64 = btoa(binary); + // Use Buffer for efficient base64 conversion (Node.js runtime) + const base64 = Buffer.from(buffer).toString("base64"); return { data: base64, size: buffer.byteLength, };
🧹 Nitpick comments (4)
apps/web/utils/email/fastmail.ts (2)
1116-1124: Move inbox lookup outside the loop for consistency.The inbox lookup is performed inside the
forloop for each sender. WhileensureMailboxCachereturns cached data after the first call, this is still redundant and inconsistent withbulkTrashFromSenders(lines 1195-1199) which correctly fetches inbox and trash outside the loop.🔎 Proposed fix
async bulkArchiveFromSenders( fromEmails: string[], ownerEmail: string, _emailAccountId: string, ): Promise<void> { const log = this.logger.with({ action: "bulkArchiveFromSenders", sendersCount: fromEmails.length, }); + const inbox = await this.getMailboxByRole(FastmailMailbox.INBOX); + if (!inbox) { + log.warn("Inbox mailbox not found"); + return; + } + const archive = await this.getMailboxByRole(FastmailMailbox.ARCHIVE); + for (const sender of fromEmails) { // Safety check: only process valid email addresses to prevent accidental bulk operations if (!isValidEmail(sender)) { log.warn("Skipping invalid sender email for bulk archive", { sender }); continue; } - const inbox = await this.getMailboxByRole(FastmailMailbox.INBOX); - if (!inbox) continue; - // Query for all emails from this sender in inbox // ... if (emailIds.length > 0) { // Archive all emails using JSON Pointer notation to patch mailbox flags - const archive = await this.getMailboxByRole(FastmailMailbox.ARCHIVE); // ... } }
1943-1952: Consider batching label removals for efficiency.The method makes sequential API calls for each label. For better performance with multiple labels, consider batching all removals into a single
Email/setrequest.🔎 Batched implementation
async removeThreadLabels( threadId: string, labelIds: string[], ): Promise<void> { if (labelIds.length === 0) return; - for (const labelId of labelIds) { - await this.removeThreadLabel(threadId, labelId); - } + // Get all emails in the thread once + const threadResponse = await this.client.request([ + [ + "Email/query", + { + accountId: this.client.accountId, + filter: { inThread: threadId }, + }, + "0", + ], + ]); + + const emailIds = getResponseData<JMAPQueryResponse>( + threadResponse.methodResponses[0], + ).ids; + + if (emailIds.length === 0) return; + + // Build update to remove all labels from all emails in one request + const update: Record<string, Record<string, boolean | null>> = {}; + for (const emailId of emailIds) { + update[emailId] = {}; + for (const labelId of labelIds) { + update[emailId][`mailboxIds/${labelId}`] = null; + } + } + + await this.client.request([ + [ + "Email/set", + { + accountId: this.client.accountId, + update, + }, + "0", + ], + ]); }apps/web/utils/fastmail/client.ts (2)
226-235: Add validation for required OAuth credentials.While the callback route now validates environment variables upfront,
getLinkingOAuth2Configshould also fail fast when credentials are missing. This function could be called from other contexts where the caller may not validate, leading to obscure OAuth failures with empty credentials.🔎 Proposed fix
export function getLinkingOAuth2Config() { + if (!env.FASTMAIL_CLIENT_ID || !env.FASTMAIL_CLIENT_SECRET) { + throw new SafeError("Fastmail OAuth credentials not configured"); + } + return { - clientId: env.FASTMAIL_CLIENT_ID || "", - clientSecret: env.FASTMAIL_CLIENT_SECRET || "", + clientId: env.FASTMAIL_CLIENT_ID, + clientSecret: env.FASTMAIL_CLIENT_SECRET, redirectUri: `${env.NEXT_PUBLIC_BASE_URL}/api/fastmail/linking/callback`, authorizeUrl: FASTMAIL_OAUTH_AUTHORIZE_URL, tokenUrl: FASTMAIL_OAUTH_TOKEN_URL, scopes: SCOPES, }; }
383-394: Consider usingfetchWithRetryfor token refresh and getUserInfo.The token refresh (line 383) and
getUserInfo(line 440) calls use plainfetchinstead offetchWithRetry, meaning they don't benefit from retry logic for transient errors (429, 503, etc.). While this might be intentional to avoid delays in authentication flows, applying the same retry pattern used elsewhere would improve resilience.Also applies to: 440-444
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (26)
apps/web/.env.example(1 hunks)apps/web/app/(app)/accounts/AddAccount.tsx(5 hunks)apps/web/app/(app)/accounts/FastmailAppTokenModal.tsx(1 hunks)apps/web/app/(landing)/login/LoginForm.tsx(3 hunks)apps/web/app/(landing)/welcome-redirect/page.tsx(1 hunks)apps/web/app/api/fastmail/linking/auth-url/route.ts(1 hunks)apps/web/app/api/fastmail/linking/callback/route.ts(1 hunks)apps/web/env.ts(3 hunks)apps/web/hooks/useFeatureFlags.ts(1 hunks)apps/web/providers/EmailProvider.tsx(2 hunks)apps/web/utils/account-linking.ts(2 hunks)apps/web/utils/account.ts(2 hunks)apps/web/utils/actions/fastmail-app-token.ts(1 hunks)apps/web/utils/actions/fastmail-app-token.validation.ts(1 hunks)apps/web/utils/auth-client.ts(1 hunks)apps/web/utils/auth.ts(4 hunks)apps/web/utils/email.ts(1 hunks)apps/web/utils/email/fastmail.ts(1 hunks)apps/web/utils/email/provider-types.ts(1 hunks)apps/web/utils/email/provider.ts(2 hunks)apps/web/utils/email/types.ts(1 hunks)apps/web/utils/fastmail/client.ts(1 hunks)apps/web/utils/fastmail/constants.ts(1 hunks)apps/web/utils/fastmail/scopes.ts(1 hunks)apps/web/utils/oauth/account-linking.ts(1 hunks)turbo.json(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (13)
- apps/web/utils/auth-client.ts
- apps/web/app/(landing)/welcome-redirect/page.tsx
- apps/web/utils/email/types.ts
- turbo.json
- apps/web/.env.example
- apps/web/utils/account.ts
- apps/web/hooks/useFeatureFlags.ts
- apps/web/utils/auth.ts
- apps/web/env.ts
- apps/web/utils/actions/fastmail-app-token.validation.ts
- apps/web/app/(app)/accounts/FastmailAppTokenModal.tsx
- apps/web/utils/fastmail/scopes.ts
- apps/web/providers/EmailProvider.tsx
🧰 Additional context used
📓 Path-based instructions (31)
**/*.{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.tsapps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/(landing)/login/LoginForm.tsxapps/web/utils/actions/fastmail-app-token.tsapps/web/utils/fastmail/constants.tsapps/web/utils/fastmail/client.tsapps/web/app/api/fastmail/linking/callback/route.tsapps/web/utils/email/provider.tsapps/web/utils/account-linking.tsapps/web/utils/email/provider-types.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/utils/oauth/account-linking.tsapps/web/utils/email/fastmail.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.tsapps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/(landing)/login/LoginForm.tsxapps/web/utils/actions/fastmail-app-token.tsapps/web/utils/fastmail/constants.tsapps/web/utils/fastmail/client.tsapps/web/app/api/fastmail/linking/callback/route.tsapps/web/utils/email/provider.tsapps/web/utils/account-linking.tsapps/web/utils/email/provider-types.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/utils/oauth/account-linking.tsapps/web/utils/email/fastmail.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
Use@/path aliases for imports from project root
Follow tailwindcss patterns with prettier-plugin-tailwindcss class organization
Prefix client-side environment variables withNEXT_PUBLIC_
Leverage TypeScript inference for better developer experience with type exports from API routes
Files:
apps/web/utils/email.tsapps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/(landing)/login/LoginForm.tsxapps/web/utils/actions/fastmail-app-token.tsapps/web/utils/fastmail/constants.tsapps/web/utils/fastmail/client.tsapps/web/app/api/fastmail/linking/callback/route.tsapps/web/utils/email/provider.tsapps/web/utils/account-linking.tsapps/web/utils/email/provider-types.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/utils/oauth/account-linking.tsapps/web/utils/email/fastmail.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.tsapps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/utils/actions/fastmail-app-token.tsapps/web/utils/fastmail/constants.tsapps/web/utils/fastmail/client.tsapps/web/app/api/fastmail/linking/callback/route.tsapps/web/utils/email/provider.tsapps/web/utils/account-linking.tsapps/web/utils/email/provider-types.tsapps/web/utils/oauth/account-linking.tsapps/web/utils/email/fastmail.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.tsapps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/(landing)/login/LoginForm.tsxapps/web/utils/actions/fastmail-app-token.tsapps/web/utils/fastmail/constants.tsapps/web/utils/fastmail/client.tsapps/web/app/api/fastmail/linking/callback/route.tsapps/web/utils/email/provider.tsapps/web/utils/account-linking.tsapps/web/utils/email/provider-types.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/utils/oauth/account-linking.tsapps/web/utils/email/fastmail.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.tsapps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/(landing)/login/LoginForm.tsxapps/web/utils/actions/fastmail-app-token.tsapps/web/utils/fastmail/constants.tsapps/web/utils/fastmail/client.tsapps/web/app/api/fastmail/linking/callback/route.tsapps/web/utils/email/provider.tsapps/web/utils/account-linking.tsapps/web/utils/email/provider-types.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/utils/oauth/account-linking.tsapps/web/utils/email/fastmail.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.tsapps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/(landing)/login/LoginForm.tsxapps/web/utils/actions/fastmail-app-token.tsapps/web/utils/fastmail/constants.tsapps/web/utils/fastmail/client.tsapps/web/app/api/fastmail/linking/callback/route.tsapps/web/utils/email/provider.tsapps/web/utils/account-linking.tsapps/web/utils/email/provider-types.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/utils/oauth/account-linking.tsapps/web/utils/email/fastmail.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.tsapps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/(landing)/login/LoginForm.tsxapps/web/utils/actions/fastmail-app-token.tsapps/web/utils/fastmail/constants.tsapps/web/utils/fastmail/client.tsapps/web/app/api/fastmail/linking/callback/route.tsapps/web/utils/email/provider.tsapps/web/utils/account-linking.tsapps/web/utils/email/provider-types.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/utils/oauth/account-linking.tsapps/web/utils/email/fastmail.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.tsapps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/(landing)/login/LoginForm.tsxapps/web/utils/actions/fastmail-app-token.tsapps/web/utils/fastmail/constants.tsapps/web/utils/fastmail/client.tsapps/web/app/api/fastmail/linking/callback/route.tsapps/web/utils/email/provider.tsapps/web/utils/account-linking.tsapps/web/utils/email/provider-types.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/utils/oauth/account-linking.tsapps/web/utils/email/fastmail.ts
apps/web/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx,js,jsx}: Use proper error handling with try/catch blocks
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
Files:
apps/web/utils/email.tsapps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/(landing)/login/LoginForm.tsxapps/web/utils/actions/fastmail-app-token.tsapps/web/utils/fastmail/constants.tsapps/web/utils/fastmail/client.tsapps/web/app/api/fastmail/linking/callback/route.tsapps/web/utils/email/provider.tsapps/web/utils/account-linking.tsapps/web/utils/email/provider-types.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/utils/oauth/account-linking.tsapps/web/utils/email/fastmail.ts
apps/web/**/*.{ts,tsx,js,jsx,json,css,md}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Format code with Prettier
Files:
apps/web/utils/email.tsapps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/(landing)/login/LoginForm.tsxapps/web/utils/actions/fastmail-app-token.tsapps/web/utils/fastmail/constants.tsapps/web/utils/fastmail/client.tsapps/web/app/api/fastmail/linking/callback/route.tsapps/web/utils/email/provider.tsapps/web/utils/account-linking.tsapps/web/utils/email/provider-types.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/utils/oauth/account-linking.tsapps/web/utils/email/fastmail.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.tsapps/web/utils/actions/fastmail-app-token.tsapps/web/utils/fastmail/constants.tsapps/web/utils/fastmail/client.tsapps/web/utils/email/provider.tsapps/web/utils/account-linking.tsapps/web/utils/email/provider-types.tsapps/web/utils/oauth/account-linking.tsapps/web/utils/email/fastmail.ts
apps/web/app/api/**/route.ts
📄 CodeRabbit inference engine (.cursor/rules/fullstack-workflow.mdc)
apps/web/app/api/**/route.ts: Create GET API routes usingwithAuthorwithEmailAccountmiddleware inapps/web/app/api/*/route.ts, export response types asGetExampleResponsetype alias for client-side type safety
Always export response types from GET routes asGet[Feature]Responseusing type inference from the data fetching function for type-safe client consumption
Do NOT use POST API routes for mutations - always use server actions withnext-safe-actioninstead
Files:
apps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/api/fastmail/linking/callback/route.ts
**/app/**/route.ts
📄 CodeRabbit inference engine (.cursor/rules/get-api-route.mdc)
**/app/**/route.ts: Always wrap GET API route handlers withwithAuthorwithEmailAccountmiddleware for consistent error handling and authentication in Next.js App Router
Infer and export response type for GET API routes usingAwaited<ReturnType<typeof functionName>>pattern in Next.js
Use Prisma for database queries in GET API routes
Return responses usingNextResponse.json()in GET API routes
Do not use try/catch blocks in GET API route handlers when usingwithAuthorwithEmailAccountmiddleware, as the middleware handles error handling
Files:
apps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/api/fastmail/linking/callback/route.ts
apps/web/app/**/[!.]*/route.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Use kebab-case for route directories in Next.js App Router (e.g.,
api/hello-world/route)
Files:
apps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/api/fastmail/linking/callback/route.ts
apps/web/app/api/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/security-audit.mdc)
apps/web/app/api/**/*.{ts,tsx}: API routes must usewithAuth,withEmailAccount, orwithErrormiddleware for authentication
All database queries must include user scoping withemailAccountIdoruserIdfiltering in WHERE clauses
Request parameters must be validated before use; avoid direct parameter usage without type checking
Use generic error messages instead of revealing internal details; throwSafeErrorinstead of exposing user IDs, resource IDs, or system information
API routes should only return necessary fields usingselectin database queries to prevent unintended information disclosure
Cron endpoints must usehasCronSecretorhasPostCronSecretto validate cron requests and prevent unauthorized access
Request bodies should use Zod schemas for validation to ensure type safety and prevent injection attacks
Files:
apps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/api/fastmail/linking/callback/route.ts
**/app/api/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/security.mdc)
**/app/api/**/*.ts: ALL API routes that handle user data MUST use appropriate middleware: usewithEmailAccountfor email-scoped operations, usewithAuthfor user-scoped operations, or usewithErrorwith proper validation for public/custom auth endpoints
UsewithEmailAccountmiddleware for operations scoped to a specific email account, including reading/writing emails, rules, schedules, or any operation usingemailAccountId
UsewithAuthmiddleware for user-level operations such as user settings, API keys, and referrals that use onlyuserId
UsewithErrormiddleware only for public endpoints, custom authentication logic, or cron endpoints. For cron endpoints, MUST usehasCronSecret()orhasPostCronSecret()validation
Cron endpoints without proper authentication can be triggered by anyone. CRITICAL: All cron endpoints MUST validate cron secret usinghasCronSecret(request)orhasPostCronSecret(request)and capture unauthorized attempts withcaptureException()
Always validate request bodies using Zod schemas to ensure type safety and prevent invalid data from reaching database operations
Maintain consistent error response format across all API routes to avoid information disclosure while providing meaningful error feedback
Files:
apps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/api/fastmail/linking/callback/route.ts
apps/web/**/app/**
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Follow NextJS app router structure with (app) directory
Files:
apps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/(landing)/login/LoginForm.tsxapps/web/app/api/fastmail/linking/callback/route.tsapps/web/app/(app)/accounts/AddAccount.tsx
apps/web/**/app/api/**/route.ts
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/app/api/**/route.ts: Wrap GET API routes withwithAuthorwithEmailAccountmiddleware
Export response types from GET API routes for type-safe client use (e.g.,GetExampleResponse)
Files:
apps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/api/fastmail/linking/callback/route.ts
apps/web/**/{app/api,utils/actions}/**/*.ts
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/{app/api,utils/actions}/**/*.ts: Use server actions for all mutations (create/update/delete operations) instead of POST API routes
UsewithAuthfor user-level operations andwithEmailAccountfor email-account-level operations
Files:
apps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/utils/actions/fastmail-app-token.tsapps/web/app/api/fastmail/linking/callback/route.ts
**/{app,pages}/**/{route,+page}.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
**/{app,pages}/**/{route,+page}.{ts,tsx}: Use middleware wrappers (withError, withAuth, withEmailAccount, withEmailProvider) that automatically create loggers with request context in API routes
Enrich logger context within route handlers using logger.with() to add request-specific fields like messageId
Files:
apps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/api/fastmail/linking/callback/route.ts
**/*Form.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)
**/*Form.{ts,tsx}: Use React Hook Form with Zod for validation in form components
Validate form inputs before submission
Show validation errors inline next to form fields
Files:
apps/web/app/(landing)/login/LoginForm.tsx
**/*.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:
apps/web/app/(landing)/login/LoginForm.tsxapps/web/app/(app)/accounts/AddAccount.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:
apps/web/app/(landing)/login/LoginForm.tsxapps/web/app/(app)/accounts/AddAccount.tsx
apps/web/**/*.{tsx,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{tsx,jsx}: Prefer functional components with hooks over class components
Use shadcn/ui components when available
Follow consistent naming conventions with PascalCase for component names
Use LoadingContent component for async data with loading and error states
Useresult?.serverErrorwithtoastErrorandtoastSuccessfor mutation error handling
Files:
apps/web/app/(landing)/login/LoginForm.tsxapps/web/app/(app)/accounts/AddAccount.tsx
apps/web/**/*.{tsx,jsx,css}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Ensure responsive design with mobile-first approach
Files:
apps/web/app/(landing)/login/LoginForm.tsxapps/web/app/(app)/accounts/AddAccount.tsx
apps/web/**/*{Form,Form.tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Use React Hook Form with Zod validation for form handling
Files:
apps/web/app/(landing)/login/LoginForm.tsx
apps/web/utils/actions/*.ts
📄 CodeRabbit inference engine (.cursor/rules/fullstack-workflow.mdc)
apps/web/utils/actions/*.ts: Usenext-safe-actionwith Zod schemas for all server actions (create/update/delete mutations), storing validation schemas inapps/web/utils/actions/*.validation.ts
Server actions should use 'use server' directive and automatically receive authentication context (emailAccountId) from theactionClient
apps/web/utils/actions/*.ts: Create corresponding server action implementation files using the naming conventionapps/web/utils/actions/NAME.tswith 'use server' directive
Use 'use server' directive at the top of server action implementation files
Implement all server actions using thenext-safe-actionlibrary with actionClient, actionClientUser, or adminActionClient for type safety and validation
UseactionClientUserwhen only authenticated user context (userId) is needed
UseactionClientwhen both authenticated user context and a specific emailAccountId are needed, with emailAccountId bound when calling from the client
UseadminActionClientfor actions restricted to admin users
Add metadata with a meaningful action name using.metadata({ name: "actionName" })for Sentry instrumentation and monitoring
Use.schema()method with Zod validation schemas from corresponding.validation.tsfiles in next-safe-action configuration
Access context (userId, emailAccountId, etc.) via thectxobject parameter in the.action()handler
UserevalidatePathorrevalidateTagfrom 'next/cache' within server action handlers when mutations modify data displayed elsewhere
Files:
apps/web/utils/actions/fastmail-app-token.ts
apps/web/utils/actions/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
apps/web/utils/actions/**/*.ts: Server actions must be located inapps/web/utils/actionsfolder
Server action files must start withuse serverdirective
Files:
apps/web/utils/actions/fastmail-app-token.ts
apps/web/**/utils/actions/*.ts
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/utils/actions/*.ts: Usenext-safe-actionwith proper Zod validation for server actions
CallrevalidatePathin server actions for cache invalidation after mutations
Files:
apps/web/utils/actions/fastmail-app-token.ts
apps/web/app/(app)/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
apps/web/app/(app)/**/*.{ts,tsx}: Components for the page are either put inpage.tsx, or in theapps/web/app/(app)/PAGE_NAMEfolder
If we're in a deeply nested component we will useswrto fetch via API
If you need to useonClickin a component, that component is a client component and file must start withuse client
Files:
apps/web/app/(app)/accounts/AddAccount.tsx
🧠 Learnings (49)
📓 Common learnings
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`
📚 Learning: 2025-12-17T11:18:06.818Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-17T11:18:06.818Z
Learning: Applies to apps/web/**/{app/api,utils/actions}/**/*.ts : Use `withAuth` for user-level operations and `withEmailAccount` for email-account-level operations
Applied to files:
apps/web/utils/email.tsapps/web/utils/actions/fastmail-app-token.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 thread operations from @/utils/gmail/thread.ts instead of direct API calls
Applied to files:
apps/web/utils/email.tsapps/web/utils/fastmail/client.tsapps/web/utils/email/fastmail.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/email.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} : Always use wrapper functions from @/utils/gmail/ for Gmail API operations instead of direct provider API calls
Applied to files:
apps/web/utils/email.tsapps/web/utils/email/provider.tsapps/web/utils/account-linking.tsapps/web/utils/email/provider-types.tsapps/web/utils/oauth/account-linking.tsapps/web/utils/email/fastmail.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/app/api/**/route.ts : Create GET API routes using `withAuth` or `withEmailAccount` middleware in `apps/web/app/api/*/route.ts`, export response types as `GetExampleResponse` type alias for client-side type safety
Applied to files:
apps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/api/fastmail/linking/callback/route.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/app/api/fastmail/linking/auth-url/route.tsapps/web/app/api/fastmail/linking/callback/route.ts
📚 Learning: 2025-12-17T11:18:06.818Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-17T11:18:06.818Z
Learning: Applies to apps/web/**/app/api/**/route.ts : Wrap GET API routes with `withAuth` or `withEmailAccount` middleware
Applied to files:
apps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/api/fastmail/linking/callback/route.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 : Infer and export the response type for GET API routes using `export type GetResponse = Awaited<ReturnType<typeof getData>>` pattern in Next.js
Applied to files:
apps/web/app/api/fastmail/linking/auth-url/route.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/app/api/**/route.ts : Always export response types from GET routes as `Get[Feature]Response` using type inference from the data fetching function for type-safe client consumption
Applied to files:
apps/web/app/api/fastmail/linking/auth-url/route.ts
📚 Learning: 2025-12-17T11:18:06.818Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-17T11:18:06.818Z
Learning: Applies to apps/web/**/app/api/**/route.ts : Export response types from GET API routes for type-safe client use (e.g., `GetExampleResponse`)
Applied to files:
apps/web/app/api/fastmail/linking/auth-url/route.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} : API routes must use `withAuth`, `withEmailAccount`, or `withError` middleware for authentication
Applied to files:
apps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/api/fastmail/linking/callback/route.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 : Infer and export response type for GET API routes using `Awaited<ReturnType<typeof functionName>>` pattern in Next.js
Applied to files:
apps/web/app/api/fastmail/linking/auth-url/route.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 API routes must use `withAuth`, `withEmailAccount`, or `withError` middleware for authentication
Applied to files:
apps/web/app/api/fastmail/linking/auth-url/route.tsapps/web/app/api/fastmail/linking/callback/route.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 : Implement all server actions using the `next-safe-action` library with actionClient, actionClientUser, or adminActionClient for type safety and validation
Applied to files:
apps/web/utils/actions/fastmail-app-token.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 : Use `next-safe-action` with Zod schemas for all server actions (create/update/delete mutations), storing validation schemas in `apps/web/utils/actions/*.validation.ts`
Applied to files:
apps/web/utils/actions/fastmail-app-token.ts
📚 Learning: 2025-12-17T11:18:06.818Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-17T11:18:06.818Z
Learning: Applies to apps/web/**/utils/actions/*.ts : Use `next-safe-action` with proper Zod validation for server actions
Applied to files:
apps/web/utils/actions/fastmail-app-token.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/actions/fastmail-app-token.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/*.validation.ts : Create separate validation files for server actions using the naming convention `apps/web/utils/actions/NAME.validation.ts` containing Zod schemas and inferred types
Applied to files:
apps/web/utils/actions/fastmail-app-token.ts
📚 Learning: 2025-12-17T11:18:06.818Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-17T11:18:06.818Z
Learning: Applies to apps/web/**/utils/actions/*.validation.ts : Create separate Zod validation schema files for server action inputs
Applied to files:
apps/web/utils/actions/fastmail-app-token.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/actions/fastmail-app-token.tsapps/web/utils/email/provider.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 : Create corresponding server action implementation files using the naming convention `apps/web/utils/actions/NAME.ts` with 'use server' directive
Applied to files:
apps/web/utils/actions/fastmail-app-token.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 `.schema()` method with Zod validation schemas from corresponding `.validation.ts` files in next-safe-action configuration
Applied to files:
apps/web/utils/actions/fastmail-app-token.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 `revalidatePath` or `revalidateTag` from 'next/cache' within server action handlers when mutations modify data displayed elsewhere
Applied to files:
apps/web/utils/actions/fastmail-app-token.ts
📚 Learning: 2025-12-17T02:38:37.011Z
Learnt from: elie222
Repo: elie222/inbox-zero PR: 1103
File: apps/web/utils/actions/rule.ts:447-457
Timestamp: 2025-12-17T02:38:37.011Z
Learning: In apps/web/utils/actions/rule.ts, revalidatePath is not needed for toggleAllRulesAction because rules data is fetched client-side using SWR, not server-side. Server-side cache revalidation is only needed when using Next.js server components or server-side data fetching.
Applied to files:
apps/web/utils/actions/fastmail-app-token.ts
📚 Learning: 2025-12-17T11:18:06.818Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-17T11:18:06.818Z
Learning: Applies to apps/web/**/utils/actions/*.ts : Call `revalidatePath` in server actions for cache invalidation after mutations
Applied to files:
apps/web/utils/actions/fastmail-app-token.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 `adminActionClient` for actions restricted to admin users
Applied to files:
apps/web/utils/actions/fastmail-app-token.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/fastmail/constants.tsapps/web/utils/fastmail/client.tsapps/web/utils/email/fastmail.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/fastmail/client.tsapps/web/utils/email/provider.tsapps/web/utils/account-linking.tsapps/web/utils/email/fastmail.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 `client` object with `NEXT_PUBLIC_` prefix and Zod schema validation
Applied to files:
apps/web/utils/fastmail/client.ts
📚 Learning: 2025-11-25T14:36:43.454Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/environment-variables.mdc:0-0
Timestamp: 2025-11-25T14:36:43.454Z
Learning: Applies to apps/web/env.ts : Define environment variables in `apps/web/env.ts` using Zod schema validation, organizing them into `server` and `client` sections
Applied to files:
apps/web/utils/fastmail/client.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} : Don't hardcode sensitive data like API keys and tokens
Applied to files:
apps/web/utils/fastmail/client.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/fastmail/client.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 {.env.example,apps/web/env.ts} : Client-side environment variables must be prefixed with `NEXT_PUBLIC_`
Applied to files:
apps/web/utils/fastmail/client.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/fastmail/client.ts
📚 Learning: 2025-11-25T14:36:43.454Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/environment-variables.mdc:0-0
Timestamp: 2025-11-25T14:36:43.454Z
Learning: Applies to apps/web/env.ts : For client-side environment variables in `apps/web/env.ts`, prefix them with `NEXT_PUBLIC_` and add them to both the `client` and `experimental__runtimeEnv` sections
Applied to files:
apps/web/utils/fastmail/client.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/fastmail/client.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 async functions actually use await
Applied to files:
apps/web/utils/fastmail/client.tsapps/web/utils/email/fastmail.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/app/api/fastmail/linking/callback/route.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/app/api/fastmail/linking/callback/route.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/app/api/fastmail/linking/callback/route.tsapps/web/utils/email/provider.tsapps/web/utils/account-linking.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/utils/oauth/account-linking.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/app/api/fastmail/linking/callback/route.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/app/api/fastmail/linking/callback/route.tsapps/web/utils/email/provider.tsapps/web/utils/account-linking.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/utils/oauth/account-linking.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/provider.tsapps/web/utils/account-linking.tsapps/web/utils/email/provider-types.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/utils/oauth/account-linking.tsapps/web/utils/email/fastmail.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: Design Gmail wrapper functions to be provider-agnostic to support future email providers like Outlook and ProtonMail
Applied to files:
apps/web/utils/email/provider.tsapps/web/utils/email/provider-types.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/utils/oauth/account-linking.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/provider.tsapps/web/utils/account-linking.tsapps/web/app/(app)/accounts/AddAccount.tsxapps/web/utils/oauth/account-linking.ts
📚 Learning: 2025-11-25T14:40:00.833Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-25T14:40:00.833Z
Learning: Applies to **/*.test.{ts,tsx} : Use test helpers `getEmail`, `getEmailAccount`, and `getRule` from `@/__tests__/helpers` for mocking emails, accounts, and rules
Applied to files:
apps/web/utils/email/provider.tsapps/web/utils/email/fastmail.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 Promise-like statements are handled appropriately
Applied to files:
apps/web/utils/email/fastmail.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/fastmail.ts
🧬 Code graph analysis (4)
apps/web/app/(landing)/login/LoginForm.tsx (1)
apps/web/utils/config.ts (1)
WELCOME_PATH(24-24)
apps/web/utils/account-linking.ts (2)
apps/web/app/api/fastmail/linking/auth-url/route.ts (1)
GetAuthLinkUrlResponse(14-14)apps/web/app/api/outlook/linking/auth-url/route.ts (1)
GetOutlookAuthLinkUrlResponse(10-10)
apps/web/app/(app)/accounts/AddAccount.tsx (2)
apps/web/hooks/useFeatureFlags.ts (1)
useFastmailEnabled(19-21)apps/web/app/(app)/accounts/FastmailAppTokenModal.tsx (1)
FastmailAppTokenModal(27-97)
apps/web/utils/email/fastmail.ts (9)
apps/web/utils/fastmail/client.ts (2)
JMAPMethodResponse(159-159)FastmailClient(209-220)apps/web/utils/email/types.ts (4)
EmailProvider(45-245)EmailThread(7-12)EmailLabel(14-25)EmailSignature(38-43)apps/web/scripts/check-enum-imports.js (1)
content(74-74)apps/web/utils/types.ts (1)
ParsedMessage(51-73)apps/web/utils/fastmail/constants.ts (1)
FastmailMailbox(22-39)apps/web/utils/email.ts (1)
isValidEmail(54-56)apps/web/utils/label.ts (1)
InboxZeroLabel(63-63)apps/web/app/api/threads/validation.ts (1)
ThreadsQuery(15-15)apps/web/utils/outlook/folders.ts (1)
OutlookFolder(10-15)
| } else if (isFastmailProvider(provider)) { | ||
| const client = await getFastmailClientForEmail({ emailAccountId }); | ||
| return new FastmailProvider(client, logger); |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Pass logger parameter to getFastmailClientForEmail for consistency.
The Google and Outlook client factories (lines 27, 30) both receive the logger parameter, but the Fastmail call on line 33 omits it. For consistency with the existing pattern and to follow the guideline that helper functions should receive logger as a parameter, pass logger to getFastmailClientForEmail.
🔎 Proposed fix
} else if (isFastmailProvider(provider)) {
- const client = await getFastmailClientForEmail({ emailAccountId });
+ const client = await getFastmailClientForEmail({ emailAccountId, logger });
return new FastmailProvider(client, logger);Based on coding guidelines: Logger should be passed as a parameter to helper functions instead of creating their own logger instances.
🤖 Prompt for AI Agents
In apps/web/utils/email/provider.ts around lines 32 to 34, the call to
getFastmailClientForEmail is missing the logger argument (unlike Google and
Outlook paths); update the call to pass the existing logger (e.g.,
getFastmailClientForEmail({ emailAccountId, logger })) and, if necessary, update
getFastmailClientForEmail’s signature to accept and use the logger parameter so
helper functions consistently receive the logger.
- Extract email address from "Name <email>" format for JMAP to field using extractEmailAddress utility to prevent delivery failures - Preserve sender display name in JMAP to field for better formatting - Fix O(n²) base64 encoding loop with efficient Buffer.from().toString() - Add Authelia env vars to turbo.json build.env for proper tracking 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (3)
apps/web/utils/email/fastmail.ts (3)
1-12: Optional: Remove unused type imports.
JMAPMethodCallandJMAPErrorare imported but not used in this file. Consider removing them to keep imports clean.🔎 Proposed cleanup
import type { FastmailClient, - JMAPMethodCall, JMAPMethodResponse, - JMAPError, } from "@/utils/fastmail/client"; import { getAccessTokenFromClient, - getJMAPError, isJMAPError, } from "@/utils/fastmail/client";
1120-1128: Move inbox lookup outside the loop.The inbox mailbox lookup on line 1127 is inside the sender iteration loop. While the mailbox cache makes subsequent calls fast, it's cleaner and more efficient to fetch it once before the loop starts.
🔎 Proposed refactor
+ const inbox = await this.getMailboxByRole(FastmailMailbox.INBOX); + if (!inbox) { + log.warn("Inbox mailbox not found"); + return; + } + for (const sender of fromEmails) { // Safety check: only process valid email addresses to prevent accidental bulk operations if (!isValidEmail(sender)) { log.warn("Skipping invalid sender email for bulk archive", { sender }); continue; } - const inbox = await this.getMailboxByRole(FastmailMailbox.INBOX); - if (!inbox) continue; - // Query for all emails from this sender in inbox
1957-1966: Consider batching label removals into a single API call.This method makes sequential API calls (one per label). For better performance when removing multiple labels, consider batching the updates into a single
Email/setrequest.🔎 Proposed refactor to batch updates
async removeThreadLabels( threadId: string, labelIds: string[], ): Promise<void> { if (labelIds.length === 0) return; - for (const labelId of labelIds) { - await this.removeThreadLabel(threadId, labelId); - } + // Get all emails in the thread + const threadResponse = await this.client.request([ + [ + "Email/query", + { + accountId: this.client.accountId, + filter: { inThread: threadId }, + }, + "0", + ], + ]); + + const emailIds = getResponseData<JMAPQueryResponse>( + threadResponse.methodResponses[0], + ).ids; + + if (emailIds.length === 0) return; + + // Remove all labels from all emails in one request + const update: Record<string, Record<string, boolean | null>> = {}; + for (const emailId of emailIds) { + const patches: Record<string, boolean | null> = {}; + for (const labelId of labelIds) { + patches[`mailboxIds/${labelId}`] = null; + } + update[emailId] = patches; + } + + await this.client.request([ + [ + "Email/set", + { + accountId: this.client.accountId, + update, + }, + "0", + ], + ]); }
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
apps/web/utils/email/fastmail.ts(1 hunks)turbo.json(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- turbo.json
🧰 Additional context used
📓 Path-based instructions (12)
**/*.{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/fastmail.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/fastmail.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
Use@/path aliases for imports from project root
Follow tailwindcss patterns with prettier-plugin-tailwindcss class organization
Prefix client-side environment variables withNEXT_PUBLIC_
Leverage TypeScript inference for better developer experience with type exports from API routes
Files:
apps/web/utils/email/fastmail.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/fastmail.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/fastmail.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/fastmail.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/fastmail.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/fastmail.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/fastmail.ts
apps/web/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx,js,jsx}: Use proper error handling with try/catch blocks
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
Files:
apps/web/utils/email/fastmail.ts
apps/web/**/*.{ts,tsx,js,jsx,json,css,md}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
Format code with Prettier
Files:
apps/web/utils/email/fastmail.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/fastmail.ts
🧠 Learnings (10)
📓 Common learnings
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`
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`
📚 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/fastmail.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 thread operations from @/utils/gmail/thread.ts instead of direct API calls
Applied to files:
apps/web/utils/email/fastmail.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/fastmail.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} : Always use wrapper functions from @/utils/gmail/ for Gmail API operations instead of direct provider API calls
Applied to files:
apps/web/utils/email/fastmail.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/fastmail.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 async functions actually use await
Applied to files:
apps/web/utils/email/fastmail.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 Promise-like statements are handled appropriately
Applied to files:
apps/web/utils/email/fastmail.ts
📚 Learning: 2025-11-25T14:40:00.833Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-25T14:40:00.833Z
Learning: Applies to **/*.test.{ts,tsx} : Use test helpers `getEmail`, `getEmailAccount`, and `getRule` from `@/__tests__/helpers` for mocking emails, accounts, and rules
Applied to files:
apps/web/utils/email/fastmail.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/fastmail.ts
🧬 Code graph analysis (1)
apps/web/utils/email/fastmail.ts (8)
apps/web/utils/fastmail/client.ts (3)
JMAPMethodResponse(159-159)FastmailClient(209-220)getAccessTokenFromClient(463-465)apps/web/utils/logger.ts (2)
Logger(5-5)createScopedLogger(17-81)apps/web/utils/types.ts (1)
ParsedMessage(51-73)apps/web/utils/fastmail/constants.ts (1)
FastmailMailbox(22-39)apps/web/utils/email.ts (3)
isValidEmail(54-56)extractEmailAddress(19-52)extractNameFromEmail(9-16)apps/web/utils/label.ts (1)
InboxZeroLabel(63-63)apps/web/app/api/threads/validation.ts (1)
ThreadsQuery(15-15)apps/web/utils/outlook/folders.ts (1)
OutlookFolder(10-15)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: cubic · AI code reviewer
|
|
||
| try { | ||
| // Exchange code for tokens | ||
| const tokenResponse = await fetch(FASTMAIL_OAUTH_TOKEN_URL, { |
There was a problem hiding this comment.
Note: Fastmail oAuth integration hasnt been tested yet, as I cant create an oAuth client myself directly, need to contact Fastmail support for that. So all changes were tested with an API KEY instead
|
Started implementing pollign and applying of filters here: marcodejongh#2. Had to create that PR in my fork so it can target this branch |

this is a first claude code pass, will have to try and deploy this tomorrow.
This implements Fastmail as a third email provider alongside Gmail and Microsoft/Outlook. Uses the JMAP (JSON Meta Application Protocol) which is Fastmail's native API.
Key changes:
Features supported:
Note: Some features like Gmail-style filters and Pub/Sub watching use different mechanisms in JMAP and have limited/no support initially.
Summary by CodeRabbit
New Features
Chores
✏️ Tip: You can customize this high-level summary in your review settings.