-
Couldn't load subscription status.
- Fork 1.1k
Add Outlook Calendar integration #841
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
@edulelis is attempting to deploy a commit to the Inbox Zero OSS Program Team on Vercel. A member of the Team first needs to authorize it. |
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughAdds Microsoft/Outlook calendar integration, provider-aware calendar UI and ConnectCalendar client, centralized OAuth callback handling and helpers, new provider implementations and availability providers for Google/Microsoft, unified availability aggregator, Outlook OAuth routes and client utilities, README updates, and a version bump. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant UI as Calendars UI
participant AuthAPI as /api/outlook/calendar/auth-url
participant MS_OAuth as Microsoft OAuth
participant Callback as /api/.../calendar/callback
participant Provider as microsoftCalendarProvider
participant Graph as Microsoft Graph
participant DB as Database
User->>UI: Click "Connect Microsoft"
UI->>AuthAPI: GET auth-url (with account context)
AuthAPI->>AuthAPI: Create OAuth state (emailAccountId,type=calendar)
AuthAPI-->>UI: { url } + Set CALENDAR_STATE cookie
UI-->>User: Redirect to Microsoft OAuth
User->>MS_OAuth: Authorize app
MS_OAuth->>Callback: Redirect with code & state
Callback->>Callback: validate state & ownership
Callback->>Provider: exchangeCodeForTokens(code)
Provider->>Graph: Token exchange & profile fetch
Provider-->>Callback: tokens + email
Callback->>DB: Create/Update calendar connection (tokens, expiresAt)
Callback->>Provider: syncCalendars(connectionId, tokens..., emailAccountId)
Provider->>Graph: Fetch calendars
Provider->>DB: Upsert calendars
Callback-->>User: Redirect to /calendars (success or error)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
apps/web/public/images/product/outlook-calendar.svgis excluded by!**/*.svg
📒 Files selected for processing (9)
apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx(3 hunks)apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsx(1 hunks)apps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsx(1 hunks)apps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendarButton.tsx(0 hunks)apps/web/app/(app)/[emailAccountId]/calendars/page.tsx(1 hunks)apps/web/app/api/outlook/calendar/auth-url/route.ts(1 hunks)apps/web/app/api/outlook/calendar/callback/route.ts(1 hunks)apps/web/utils/outlook/calendar-client.ts(1 hunks)apps/web/utils/outlook/scopes.ts(1 hunks)
💤 Files with no reviewable changes (1)
- apps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendarButton.tsx
🧰 Additional context used
📓 Path-based instructions (20)
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use@/for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX
Files:
apps/web/utils/outlook/scopes.tsapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsxapps/web/app/api/outlook/calendar/auth-url/route.tsapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsxapps/web/app/(app)/[emailAccountId]/calendars/page.tsxapps/web/app/api/outlook/calendar/callback/route.tsapps/web/utils/outlook/calendar-client.ts
!{.cursor/rules/*.mdc}
📄 CodeRabbit inference engine (.cursor/rules/cursor-rules.mdc)
Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location
Files:
apps/web/utils/outlook/scopes.tsapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsxapps/web/app/api/outlook/calendar/auth-url/route.tsapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsxapps/web/app/(app)/[emailAccountId]/calendars/page.tsxapps/web/app/api/outlook/calendar/callback/route.tsapps/web/utils/outlook/calendar-client.ts
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)
**/*.ts: The same validation should be done in the server action too
Define validation schemas using Zod
Files:
apps/web/utils/outlook/scopes.tsapps/web/app/api/outlook/calendar/auth-url/route.tsapps/web/app/api/outlook/calendar/callback/route.tsapps/web/utils/outlook/calendar-client.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}: UsecreateScopedLoggerfor logging in backend TypeScript files
Typically add the logger initialization at the top of the file when usingcreateScopedLogger
Only use.with()on a logger instance within a specific function, not for a global loggerImport Prisma in the project using
import prisma from "@/utils/prisma";
**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.
Files:
apps/web/utils/outlook/scopes.tsapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsxapps/web/app/api/outlook/calendar/auth-url/route.tsapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsxapps/web/app/(app)/[emailAccountId]/calendars/page.tsxapps/web/app/api/outlook/calendar/callback/route.tsapps/web/utils/outlook/calendar-client.ts
apps/web/utils/**
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Create utility functions in
utils/folder for reusable logic
Files:
apps/web/utils/outlook/scopes.tsapps/web/utils/outlook/calendar-client.ts
apps/web/utils/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
apps/web/utils/**/*.ts: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size
Files:
apps/web/utils/outlook/scopes.tsapps/web/utils/outlook/calendar-client.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useelements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...
Files:
apps/web/utils/outlook/scopes.tsapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsxapps/web/app/api/outlook/calendar/auth-url/route.tsapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsxapps/web/app/(app)/[emailAccountId]/calendars/page.tsxapps/web/app/api/outlook/calendar/callback/route.tsapps/web/utils/outlook/calendar-client.ts
!pages/_document.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.
Files:
apps/web/utils/outlook/scopes.tsapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsxapps/web/app/api/outlook/calendar/auth-url/route.tsapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsxapps/web/app/(app)/[emailAccountId]/calendars/page.tsxapps/web/app/api/outlook/calendar/callback/route.tsapps/web/utils/outlook/calendar-client.ts
apps/web/app/**
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
NextJS app router structure with (app) directory
Files:
apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsxapps/web/app/api/outlook/calendar/auth-url/route.tsapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsxapps/web/app/(app)/[emailAccountId]/calendars/page.tsxapps/web/app/api/outlook/calendar/callback/route.ts
apps/web/**/*.tsx
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.tsx: Follow tailwindcss patterns with prettier-plugin-tailwindcss
Prefer functional components with hooks
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach
Follow consistent naming conventions (PascalCase for components)
Use LoadingContent component for async data
Useresult?.serverErrorwithtoastErrorandtoastSuccess
UseLoadingContentcomponent to handle loading and error states consistently
Passloading,error, and children props toLoadingContent
Files:
apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsxapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsxapps/web/app/(app)/[emailAccountId]/calendars/page.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)
**/*.tsx: Use React Hook Form with Zod for validation
Validate form inputs before submission
Show validation errors inline next to form fields
Files:
apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsxapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsxapps/web/app/(app)/[emailAccountId]/calendars/page.tsx
apps/web/app/(app)/*/**
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
Components for the page are either put in page.tsx, or in the apps/web/app/(app)/PAGE_NAME folder
Files:
apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsxapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsxapps/web/app/(app)/[emailAccountId]/calendars/page.tsx
apps/web/app/(app)/*/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
If you need to use onClick in a component, that component is a client component and file must start with 'use client'
Files:
apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsxapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsxapps/web/app/(app)/[emailAccountId]/calendars/page.tsx
apps/web/app/(app)/*/**/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
If we're in a deeply nested component we will use swr to fetch via API
Files:
apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsxapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsxapps/web/app/(app)/[emailAccountId]/calendars/page.tsx
apps/web/app/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Components with
onClickmust be client components withuse clientdirective
Files:
apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsxapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsxapps/web/app/(app)/[emailAccountId]/calendars/page.tsx
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{jsx,tsx}: Don't destructure props inside JSX components in Solid projects.
Don't use both children and dangerouslySetInnerHTML props on the same element.
Don't use Array index in keys.
Don't assign to React component props.
Don't define React components inside other components.
Don't use event handlers on non-interactive elements.
Don't assign JSX properties multiple times.
Don't add extra closing tags for components without children.
Use <>...</> instead of ....
Don't insert comments as text nodes.
Don't use the return value of React.render.
Make sure all dependencies are correctly specified in React hooks.
Make sure all React hooks are called from the top level of component functions.
Don't use unnecessary fragments.
Don't pass children as props.
Use semantic elements instead of role attributes in JSX.
Files:
apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsxapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsxapps/web/app/(app)/[emailAccountId]/calendars/page.tsx
**/*.{html,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{html,jsx,tsx}: Don't use or elements.
Don't use accessKey attribute on any HTML element.
Don't set aria-hidden="true" on focusable elements.
Don't add ARIA roles, states, and properties to elements that don't support them.
Only use the scope prop on elements.
Don't assign non-interactive ARIA roles to interactive HTML elements.
Make sure label elements have text content and are associated with an input.
Don't assign interactive ARIA roles to non-interactive HTML elements.
Don't assign tabIndex to non-interactive HTML elements.
Don't use positive integers for tabIndex property.
Don't include "image", "picture", or "photo" in img alt prop.
Don't use explicit role property that's the same as the implicit/default role.
Make static elements with click handlers use a valid role attribute.
Always include a title element for SVG elements.
Give all elements requiring alt text meaningful information for screen readers.
Make sure anchors have content that's accessible to screen readers.
Assign tabIndex to non-interactive HTML elements with aria-activedescendant.
Include all required ARIA attributes for elements with ARIA roles.
Make sure ARIA properties are valid for the element's supported roles.
Always include a type attribute for button elements.
Make elements with interactive roles and handlers focusable.
Give heading elements content that's accessible to screen readers (not hidden with aria-hidden).
Always include a lang attribute on the html element.
Always include a title attribute for iframe elements.
Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress.
Accompany onMouseOver/onMouseOut with onFocus/onBlur.
Include caption tracks for audio and video elements.
Make sure all anchors are valid and navigable.
Ensure all ARIA properties (aria-*) are valid.
Use valid, non-abstract ARIA roles for elements with ARIA roles.
Use valid ARIA state and property values.
Use valid values for the autocomplete attribute on input eleme...Files:
apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnections.tsxapps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsxapps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsxapps/web/app/(app)/[emailAccountId]/calendars/page.tsxapps/web/app/api/**/route.ts
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/app/api/**/route.ts: UsewithAuthfor user-level operations
UsewithEmailAccountfor email-account-level operations
Do NOT use POST API routes for mutations - use server actions instead
No need for try/catch in GET routes when using middleware
Export response types from GET routes
apps/web/app/api/**/route.ts: Wrap all GET API route handlers withwithAuthorwithEmailAccountmiddleware for authentication and authorization.
Export response types from GET API routes for type-safe client usage.
Do not use try/catch in GET API routes when using authentication middleware; rely on centralized error handling.Files:
apps/web/app/api/outlook/calendar/auth-url/route.tsapps/web/app/api/outlook/calendar/callback/route.ts**/api/**/route.ts
📄 CodeRabbit inference engine (.cursor/rules/security.mdc)
**/api/**/route.ts: ALL API routes that handle user data MUST use appropriate authentication and authorization middleware (withAuth or withEmailAccount).
ALL database queries in API routes MUST be scoped to the authenticated user/account (e.g., include userId or emailAccountId in query filters).
Always validate that resources belong to the authenticated user before performing operations (resource ownership validation).
UsewithEmailAccountmiddleware for API routes that operate on a specific email account (i.e., use or requireemailAccountId).
UsewithAuthmiddleware for API routes that operate at the user level (i.e., use or require onlyuserId).
UsewithErrormiddleware (with proper validation) for public endpoints, custom authentication, or cron endpoints.
Cron endpoints MUST usewithErrormiddleware and validate the cron secret usinghasCronSecret(request)orhasPostCronSecret(request).
Cron endpoints MUST capture unauthorized attempts withcaptureExceptionand return a 401 status for unauthorized requests.
All parameters in API routes MUST be validated for type, format, and length before use.
Request bodies in API routes MUST be validated using Zod schemas before use.
All Prisma queries in API routes MUST only return necessary fields and never expose sensitive data.
Error messages in API routes MUST not leak internal information or sensitive data; use generic error messages and SafeError where appropriate.
API routes MUST use a consistent error response format, returning JSON with an error message and status code.
AllfindUniqueandfindFirstPrisma calls in API routes MUST include ownership filters (e.g., userId or emailAccountId).
AllfindManyPrisma calls in API routes MUST be scoped to the authenticated user's data.
Never use direct object references in API routes without ownership checks (prevent IDOR vulnerabilities).
Prevent mass assignment vulnerabilities by only allowing explicitly whitelisted fields in update operations in AP...Files:
apps/web/app/api/outlook/calendar/auth-url/route.tsapps/web/app/api/outlook/calendar/callback/route.tsapps/web/app/api/**/*.{ts,js}
📄 CodeRabbit inference engine (.cursor/rules/security-audit.mdc)
apps/web/app/api/**/*.{ts,js}: All API route handlers in 'apps/web/app/api/' must use authentication middleware: withAuth, withEmailAccount, or withError (with custom authentication logic).
All Prisma queries in API routes must include user/account filtering (e.g., emailAccountId or userId in WHERE clauses) to prevent unauthorized data access.
All parameters used in API routes must be validated before use; do not use parameters from 'params' or request bodies directly in queries without validation.
Request bodies in API routes should use Zod schemas for validation.
API routes should only return necessary fields using Prisma's 'select' and must not include sensitive data in error messages.
Error messages in API routes must not reveal internal details; use generic errors and SafeError for user-facing errors.
All QStash endpoints (API routes called via publishToQstash or publishToQstashQueue) must use verifySignatureAppRouter to verify request authenticity.
All cron endpoints in API routes must use hasCronSecret or hasPostCronSecret for authentication.
Do not hardcode weak or plaintext secrets in API route files; secrets must not be directly assigned as string literals.
Review all new withError usage in API routes to ensure custom authentication is implemented where required.Files:
apps/web/app/api/outlook/calendar/auth-url/route.tsapps/web/app/api/outlook/calendar/callback/route.ts🧠 Learnings (1)
📚 Learning: 2025-07-18T15:05:16.146Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/fullstack-workflow.mdc:0-0 Timestamp: 2025-07-18T15:05:16.146Z Learning: Applies to apps/web/app/api/**/route.ts : Wrap all GET API route handlers with `withAuth` or `withEmailAccount` middleware for authentication and authorization.Applied to files:
apps/web/app/api/outlook/calendar/auth-url/route.ts🧬 Code graph analysis (5)
apps/web/app/api/outlook/calendar/auth-url/route.ts (5)
apps/web/utils/oauth/state.ts (2)
generateOAuthState(10-18)oauthStateCookieOptions(34-40)apps/web/utils/outlook/calendar-client.ts (1)
getCalendarOAuth2Url(25-42)apps/web/app/api/outlook/calendar/callback/route.ts (1)
GET(16-176)apps/web/utils/middleware.ts (1)
withEmailAccount(293-298)apps/web/utils/calendar/constants.ts (1)
CALENDAR_STATE_COOKIE_NAME(1-1)apps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsx (3)
apps/web/providers/EmailAccountProvider.tsx (1)
useAccount(72-82)apps/web/utils/fetch.ts (1)
fetchWithAccount(7-25)apps/web/app/api/outlook/calendar/auth-url/route.ts (1)
GetCalendarAuthUrlResponse(10-10)apps/web/app/(app)/[emailAccountId]/calendars/page.tsx (3)
apps/web/components/PageWrapper.tsx (1)
PageWrapper(3-20)apps/web/components/PageHeader.tsx (1)
PageHeader(14-34)apps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsx (1)
ConnectCalendar(11-101)apps/web/app/api/outlook/calendar/callback/route.ts (5)
apps/web/utils/logger.ts (1)
createScopedLogger(17-80)apps/web/utils/calendar/constants.ts (1)
CALENDAR_STATE_COOKIE_NAME(1-1)apps/web/utils/oauth/state.ts (1)
parseOAuthState(25-29)apps/web/utils/auth.ts (1)
auth(485-486)apps/web/utils/outlook/calendar-client.ts (2)
getCalendarClientWithRefresh(44-131)fetchMicrosoftCalendars(133-149)apps/web/utils/outlook/calendar-client.ts (5)
apps/web/app/api/outlook/webhook/logger.ts (1)
logger(3-3)apps/web/utils/logger.ts (1)
createScopedLogger(17-80)apps/web/env.ts (1)
env(16-242)apps/web/utils/outlook/scopes.ts (1)
CALENDAR_SCOPES(20-28)apps/web/utils/error.ts (1)
SafeError(86-96)⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Static Code Analysis Js
- GitHub Check: cubic · AI code reviewer
- GitHub Check: Jit Security
apps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1 issue found across 10 files
Prompt for AI agents (all 1 issues)
Understand the root cause of the following 1 issues and fix them.
<file name="apps/web/utils/outlook/calendar-client.ts">
<violation number="1" location="apps/web/utils/outlook/calendar-client.ts:58">
Only return a client when a valid access token exists; otherwise fall back to the refresh flow to avoid initializing Graph with an empty bearer and causing downstream 401s.</violation>
</file>
React with 👍 or 👎 to teach cubic. Mention @cubic-dev-ai to give feedback, ask questions, or re-run the review.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (1)
apps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsx (1)
7-7: Import the Outlook auth URL response type.This component now calls
/api/outlook/calendar/auth-url, but still reuses the Google route’s response type. Pull the type from the Outlook route (or a shared module) so the typing tracks the correct contract and we don’t silently miss future divergences between providers.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
apps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsx(1 hunks)apps/web/utils/outlook/calendar-client.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (17)
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use@/for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX
Files:
apps/web/utils/outlook/calendar-client.tsapps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsx
!{.cursor/rules/*.mdc}
📄 CodeRabbit inference engine (.cursor/rules/cursor-rules.mdc)
Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location
Files:
apps/web/utils/outlook/calendar-client.tsapps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsx
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)
**/*.ts: The same validation should be done in the server action too
Define validation schemas using Zod
Files:
apps/web/utils/outlook/calendar-client.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}: UsecreateScopedLoggerfor logging in backend TypeScript files
Typically add the logger initialization at the top of the file when usingcreateScopedLogger
Only use.with()on a logger instance within a specific function, not for a global loggerImport Prisma in the project using
import prisma from "@/utils/prisma";
**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.
Files:
apps/web/utils/outlook/calendar-client.tsapps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsx
apps/web/utils/**
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Create utility functions in
utils/folder for reusable logic
Files:
apps/web/utils/outlook/calendar-client.ts
apps/web/utils/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
apps/web/utils/**/*.ts: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size
Files:
apps/web/utils/outlook/calendar-client.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useelements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...
Files:
apps/web/utils/outlook/calendar-client.tsapps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsx
!pages/_document.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.
Files:
apps/web/utils/outlook/calendar-client.tsapps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsx
apps/web/app/**
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
NextJS app router structure with (app) directory
Files:
apps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsx
apps/web/**/*.tsx
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.tsx: Follow tailwindcss patterns with prettier-plugin-tailwindcss
Prefer functional components with hooks
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach
Follow consistent naming conventions (PascalCase for components)
Use LoadingContent component for async data
Useresult?.serverErrorwithtoastErrorandtoastSuccess
UseLoadingContentcomponent to handle loading and error states consistently
Passloading,error, and children props toLoadingContent
Files:
apps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)
**/*.tsx: Use React Hook Form with Zod for validation
Validate form inputs before submission
Show validation errors inline next to form fields
Files:
apps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsx
apps/web/app/(app)/*/**
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
Components for the page are either put in page.tsx, or in the apps/web/app/(app)/PAGE_NAME folder
Files:
apps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsx
apps/web/app/(app)/*/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
If you need to use onClick in a component, that component is a client component and file must start with 'use client'
Files:
apps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsx
apps/web/app/(app)/*/**/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
If we're in a deeply nested component we will use swr to fetch via API
Files:
apps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsx
apps/web/app/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Components with
onClickmust be client components withuse clientdirective
Files:
apps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsx
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{jsx,tsx}: Don't destructure props inside JSX components in Solid projects.
Don't use both children and dangerouslySetInnerHTML props on the same element.
Don't use Array index in keys.
Don't assign to React component props.
Don't define React components inside other components.
Don't use event handlers on non-interactive elements.
Don't assign JSX properties multiple times.
Don't add extra closing tags for components without children.
Use <>...</> instead of ....
Don't insert comments as text nodes.
Don't use the return value of React.render.
Make sure all dependencies are correctly specified in React hooks.
Make sure all React hooks are called from the top level of component functions.
Don't use unnecessary fragments.
Don't pass children as props.
Use semantic elements instead of role attributes in JSX.
Files:
apps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsx
**/*.{html,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{html,jsx,tsx}: Don't use or elements.
Don't use accessKey attribute on any HTML element.
Don't set aria-hidden="true" on focusable elements.
Don't add ARIA roles, states, and properties to elements that don't support them.
Only use the scope prop on elements.
Don't assign non-interactive ARIA roles to interactive HTML elements.
Make sure label elements have text content and are associated with an input.
Don't assign interactive ARIA roles to non-interactive HTML elements.
Don't assign tabIndex to non-interactive HTML elements.
Don't use positive integers for tabIndex property.
Don't include "image", "picture", or "photo" in img alt prop.
Don't use explicit role property that's the same as the implicit/default role.
Make static elements with click handlers use a valid role attribute.
Always include a title element for SVG elements.
Give all elements requiring alt text meaningful information for screen readers.
Make sure anchors have content that's accessible to screen readers.
Assign tabIndex to non-interactive HTML elements with aria-activedescendant.
Include all required ARIA attributes for elements with ARIA roles.
Make sure ARIA properties are valid for the element's supported roles.
Always include a type attribute for button elements.
Make elements with interactive roles and handlers focusable.
Give heading elements content that's accessible to screen readers (not hidden with aria-hidden).
Always include a lang attribute on the html element.
Always include a title attribute for iframe elements.
Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress.
Accompany onMouseOver/onMouseOut with onFocus/onBlur.
Include caption tracks for audio and video elements.
Make sure all anchors are valid and navigable.
Ensure all ARIA properties (aria-*) are valid.
Use valid, non-abstract ARIA roles for elements with ARIA roles.
Use valid ARIA state and property values.
Use valid values for the autocomplete attribute on input eleme...Files:
apps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsx🧠 Learnings (1)
📚 Learning: 2025-09-17T22:05:28.646Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/llm.mdc:0-0 Timestamp: 2025-09-17T22:05:28.646Z Learning: Applies to apps/web/utils/ai/**/*.{ts,tsx} : Use proper error types and logging for failuresApplied to files:
apps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsx🧬 Code graph analysis (2)
apps/web/utils/outlook/calendar-client.ts (4)
apps/web/utils/logger.ts (1)
createScopedLogger(17-80)apps/web/env.ts (1)
env(16-242)apps/web/utils/outlook/scopes.ts (1)
CALENDAR_SCOPES(20-28)apps/web/utils/error.ts (1)
SafeError(86-96)apps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsx (4)
apps/web/providers/EmailAccountProvider.tsx (1)
useAccount(72-82)apps/web/utils/logger.ts (1)
createScopedLogger(17-80)apps/web/utils/fetch.ts (1)
fetchWithAccount(7-25)apps/web/app/api/outlook/calendar/auth-url/route.ts (1)
GetCalendarAuthUrlResponse(10-10)⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Static Code Analysis Js
- GitHub Check: Jit Security
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (3)
apps/web/utils/outlook/calendar-client.ts (3)
44-56: Consider validating for empty refresh token.An empty string
refreshTokenwould pass the truthy check on line 55 but fail during the token refresh API call. While this is a minor edge case, validating for a non-empty string would provide clearer error messaging upfront.Apply this diff to validate for non-empty strings:
- if (!refreshToken) throw new SafeError("No refresh token"); + if (!refreshToken || refreshToken.trim() === "") { + throw new SafeError("No refresh token"); + }
63-90: Consider adding type validation for the token response.The
tokensobject from the Microsoft endpoint is untyped (any). Adding Zod validation would ensure the response contains expected fields (access_token,refresh_token,expires_in) and provide better error messages if the API response structure changes.Example Zod schema:
import { z } from "zod"; const TokenResponseSchema = z.object({ access_token: z.string(), refresh_token: z.string().optional(), expires_in: z.union([z.string(), z.number()]), error: z.string().optional(), error_description: z.string().optional(), });Then validate after line 86:
const tokens = TokenResponseSchema.parse(await response.json());
153-188: Consider the silent return behavior.The function silently returns when
access_tokenis missing (lines 164-169), logging a warning. While this provides graceful handling, it means the caller has no indication that the save operation was skipped. This is acceptable for an internal function, but consider whether the caller should be notified (e.g., via a return boolean) if this pattern is used elsewhere.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
apps/web/utils/outlook/calendar-client.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use@/for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX
Files:
apps/web/utils/outlook/calendar-client.ts
!{.cursor/rules/*.mdc}
📄 CodeRabbit inference engine (.cursor/rules/cursor-rules.mdc)
Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location
Files:
apps/web/utils/outlook/calendar-client.ts
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)
**/*.ts: The same validation should be done in the server action too
Define validation schemas using Zod
Files:
apps/web/utils/outlook/calendar-client.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}: UsecreateScopedLoggerfor logging in backend TypeScript files
Typically add the logger initialization at the top of the file when usingcreateScopedLogger
Only use.with()on a logger instance within a specific function, not for a global loggerImport Prisma in the project using
import prisma from "@/utils/prisma";
**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.
Files:
apps/web/utils/outlook/calendar-client.ts
apps/web/utils/**
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Create utility functions in
utils/folder for reusable logic
Files:
apps/web/utils/outlook/calendar-client.ts
apps/web/utils/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
apps/web/utils/**/*.ts: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size
Files:
apps/web/utils/outlook/calendar-client.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useelements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...
Files:
apps/web/utils/outlook/calendar-client.ts
!pages/_document.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.
Files:
apps/web/utils/outlook/calendar-client.ts
🧬 Code graph analysis (1)
apps/web/utils/outlook/calendar-client.ts (4)
apps/web/utils/logger.ts (1)
createScopedLogger(17-80)apps/web/env.ts (1)
env(16-242)apps/web/utils/outlook/scopes.ts (1)
CALENDAR_SCOPES(20-28)apps/web/utils/error.ts (1)
SafeError(86-96)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Static Code Analysis Js
- GitHub Check: cubic · AI code reviewer
- GitHub Check: Jit Security
🔇 Additional comments (6)
apps/web/utils/outlook/calendar-client.ts (6)
1-11: LGTM! Proper imports and logger initialization.The imports follow the path alias convention, and the scoped logger is correctly initialized at the top of the file as per coding guidelines.
13-23: LGTM! Clean authentication provider implementation.The
CalendarAuthProvidercorrectly implements theAuthenticationProviderinterface from the Microsoft Graph client library.
25-42: LGTM! Correct OAuth2 URL generation.The function properly validates the client ID, constructs the authorization URL with all required parameters including state for CSRF protection, and uses the appropriate scopes.
58-61: LGTM! Token validation correctly addressed.The condition now properly checks for
accessTokenpresence in addition to expiry validation, ensuring we only use the cached token when it's both valid and present. This addresses the previous review concern.
118-132: LGTM! Appropriate error handling.The error handling properly distinguishes
invalid_granterrors (which indicate the refresh token is no longer valid) from other failures, and logs them appropriately before re-throwing.
135-151: LGTM! Clean calendar fetch implementation.The function properly queries the Graph API, handles the response, and provides appropriate error handling with logging.
| tokens: { | ||
| access_token: tokens.access_token, | ||
| refresh_token: tokens.refresh_token, | ||
| expires_at: Math.floor( | ||
| Date.now() / 1000 + Number(tokens.expires_in ?? 0), | ||
| ), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reconsider the fallback value for missing expires_in.
While the Number() coercion correctly addresses the string-to-number issue from the previous review, the fallback to 0 when expires_in is missing creates a token that's immediately expired. This could cause continuous refresh attempts or immediate failures on the next calendar operation.
Microsoft's token endpoint typically provides expires_in (usually 3600 seconds). If it's missing, this indicates an API contract violation that should fail explicitly rather than silently creating an expired token.
Apply this diff to throw an error when expires_in is missing:
- expires_at: Math.floor(
- Date.now() / 1000 + Number(tokens.expires_in ?? 0),
- ),
+ expires_at: tokens.expires_in
+ ? Math.floor(Date.now() / 1000 + Number(tokens.expires_in))
+ : null,Alternatively, if you want to maintain a numeric expires_at:
+ if (!tokens.expires_in) {
+ throw new Error("Token response missing expires_in field");
+ }
expires_at: Math.floor(
- Date.now() / 1000 + Number(tokens.expires_in ?? 0),
+ Date.now() / 1000 + Number(tokens.expires_in),
),Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In apps/web/utils/outlook/calendar-client.ts around lines 103 to 108, the code
currently falls back to 0 when tokens.expires_in is missing, producing an
immediately expired token; instead, validate that tokens.expires_in exists and
is a valid number: if missing or not a finite number, throw an explicit error
(including context such as the token response) so the failure surfaces
immediately; otherwise compute expires_at using Number(tokens.expires_in) (or
parseInt) added to Date.now()/1000.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No issues found across 2 files
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the final PR Bugbot will review for you during this billing cycle
Your free Bugbot reviews will reset on November 30
Details
You are on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle.
To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.
…dling
Fix calendar OAuth callbacks to redirect to /{emailAccountId}/calendars
and standardize error handling between Google and Microsoft providers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (1)
apps/web/app/api/outlook/calendar/callback/route.ts (1)
67-71: Good fix: redirect now scopes to emailAccountId.This addresses the earlier bug report about missing account scoping in the redirect URL.
🧹 Nitpick comments (5)
apps/web/app/api/outlook/calendar/callback/route.ts (5)
27-31: Make cookie deletion deterministic by constructing the redirect response at return-time (avoid reusing headers from an earlier NextResponse).Current pattern creates a NextResponse up-front, deletes the cookie on it, then reuses its headers for later redirects. Prefer creating a fresh NextResponse for each branch and deleting the cookie on that response to avoid subtle header/cookie propagation issues and to improve clarity.
Apply this refactor:
- // We'll set the proper redirect URL after we decode the state and get emailAccountId - let redirectUrl = new URL("/calendars", request.nextUrl.origin); - const response = NextResponse.redirect(redirectUrl); - - response.cookies.delete(CALENDAR_STATE_COOKIE_NAME); + // We'll set the proper redirect URL after we decode the state and get emailAccountId + let redirectUrl = new URL("/calendars", request.nextUrl.origin); + + const redirectWithCookie = (url: URL) => { + const res = NextResponse.redirect(url); + res.cookies.delete(CALENDAR_STATE_COOKIE_NAME); + return res; + }; @@ - return NextResponse.redirect(redirectUrl, { headers: response.headers }); + return redirectWithCookie(redirectUrl); @@ - return NextResponse.redirect(redirectUrl, { headers: response.headers }); + return redirectWithCookie(redirectUrl); @@ - return NextResponse.redirect(redirectUrl, { headers: response.headers }); + return redirectWithCookie(redirectUrl); @@ - return NextResponse.redirect(redirectUrl, { headers: response.headers }); + return redirectWithCookie(redirectUrl); @@ - return NextResponse.redirect(redirectUrl, { headers: response.headers }); + return redirectWithCookie(redirectUrl); @@ - return NextResponse.redirect(redirectUrl, { headers: response.headers }); + return redirectWithCookie(redirectUrl); @@ - return NextResponse.redirect(redirectUrl, { headers: response.headers }); + return redirectWithCookie(redirectUrl); @@ - return NextResponse.redirect(redirectUrl, { headers: response.headers }); + return redirectWithCookie(redirectUrl);Also applies to: 33-37, 44-46, 54-55, 62-63, 78-79, 95-96, 157-158, 188-189, 192-193
100-115: Add timeouts to external fetch calls (token and profile).Unbounded fetches can hang request threads. Add AbortSignal timeouts.
- const tokenResponse = await fetch( + const tokenResponse = await fetch( "https://login.microsoftonline.com/common/oauth2/v2.0/token", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", }, body: new URLSearchParams({ client_id: env.MICROSOFT_CLIENT_ID, client_secret: env.MICROSOFT_CLIENT_SECRET, code, grant_type: "authorization_code", redirect_uri: `${env.NEXT_PUBLIC_BASE_URL}/api/outlook/calendar/callback`, }), + // 10s timeout + signal: AbortSignal.timeout(10_000), }, ); @@ - const profileResponse = await fetch("https://graph.microsoft.com/v1.0/me", { + const profileResponse = await fetch("https://graph.microsoft.com/v1.0/me", { headers: { Authorization: `Bearer ${tokens.access_token}`, }, + signal: AbortSignal.timeout(10_000), });Also applies to: 126-131
174-180: Avoid immediate token refresh; pass fresh token expiry to the client.You pass
expiresAt: null, forcing an unnecessary refresh right after obtaining tokens.- await syncMicrosoftCalendars( - connection.id, - tokens.access_token, - tokens.refresh_token, - emailAccountId, - ); + await syncMicrosoftCalendars( + connection.id, + tokens.access_token, + tokens.refresh_token, + emailAccountId, + tokens.expires_in ? Date.now() + tokens.expires_in * 1000 : null, + );And update the function signature:
-async function syncMicrosoftCalendars( +async function syncMicrosoftCalendars( connectionId: string, accessToken: string, refreshToken: string, - emailAccountId: string, + emailAccountId: string, + expiresAtMs: number | null = null, ) { @@ - const calendarClient = await getCalendarClientWithRefresh({ - accessToken, - refreshToken, - expiresAt: null, - emailAccountId, - }); + const calendarClient = await getCalendarClientWithRefresh({ + accessToken, + refreshToken, + expiresAt: expiresAtMs, + emailAccountId, + });Also applies to: 196-208
210-236: Verify Graph Calendar fields: description/timeZone may be undefined.Microsoft Graph Calendar typically exposes name, (hex) color, owner, etc. Confirm that
descriptionandtimeZoneexist on/me/calendarsresponses; otherwise map to supported fields or drop them.
174-180: Don’t block the callback on full calendar sync.Running a full sync inline can slow redirects and increase error surface. Prefer deferring the sync (e.g., queue or Next.js unstable_after) and show a “syncing…” toast on the calendars page.
Also applies to: 196-245
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
apps/web/app/api/google/calendar/callback/route.ts(0 hunks)apps/web/app/api/outlook/calendar/callback/route.ts(1 hunks)
💤 Files with no reviewable changes (1)
- apps/web/app/api/google/calendar/callback/route.ts
🧰 Additional context used
📓 Path-based instructions (10)
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use@/for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX
Files:
apps/web/app/api/outlook/calendar/callback/route.ts
apps/web/app/**
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
NextJS app router structure with (app) directory
Files:
apps/web/app/api/outlook/calendar/callback/route.ts
apps/web/app/api/**/route.ts
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/app/api/**/route.ts: UsewithAuthfor user-level operations
UsewithEmailAccountfor email-account-level operations
Do NOT use POST API routes for mutations - use server actions instead
No need for try/catch in GET routes when using middleware
Export response types from GET routes
apps/web/app/api/**/route.ts: Wrap all GET API route handlers withwithAuthorwithEmailAccountmiddleware for authentication and authorization.
Export response types from GET API routes for type-safe client usage.
Do not use try/catch in GET API routes when using authentication middleware; rely on centralized error handling.
Files:
apps/web/app/api/outlook/calendar/callback/route.ts
!{.cursor/rules/*.mdc}
📄 CodeRabbit inference engine (.cursor/rules/cursor-rules.mdc)
Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location
Files:
apps/web/app/api/outlook/calendar/callback/route.ts
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)
**/*.ts: The same validation should be done in the server action too
Define validation schemas using Zod
Files:
apps/web/app/api/outlook/calendar/callback/route.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}: UsecreateScopedLoggerfor logging in backend TypeScript files
Typically add the logger initialization at the top of the file when usingcreateScopedLogger
Only use.with()on a logger instance within a specific function, not for a global loggerImport Prisma in the project using
import prisma from "@/utils/prisma";
**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.
Files:
apps/web/app/api/outlook/calendar/callback/route.ts
**/api/**/route.ts
📄 CodeRabbit inference engine (.cursor/rules/security.mdc)
**/api/**/route.ts: ALL API routes that handle user data MUST use appropriate authentication and authorization middleware (withAuth or withEmailAccount).
ALL database queries in API routes MUST be scoped to the authenticated user/account (e.g., include userId or emailAccountId in query filters).
Always validate that resources belong to the authenticated user before performing operations (resource ownership validation).
UsewithEmailAccountmiddleware for API routes that operate on a specific email account (i.e., use or requireemailAccountId).
UsewithAuthmiddleware for API routes that operate at the user level (i.e., use or require onlyuserId).
UsewithErrormiddleware (with proper validation) for public endpoints, custom authentication, or cron endpoints.
Cron endpoints MUST usewithErrormiddleware and validate the cron secret usinghasCronSecret(request)orhasPostCronSecret(request).
Cron endpoints MUST capture unauthorized attempts withcaptureExceptionand return a 401 status for unauthorized requests.
All parameters in API routes MUST be validated for type, format, and length before use.
Request bodies in API routes MUST be validated using Zod schemas before use.
All Prisma queries in API routes MUST only return necessary fields and never expose sensitive data.
Error messages in API routes MUST not leak internal information or sensitive data; use generic error messages and SafeError where appropriate.
API routes MUST use a consistent error response format, returning JSON with an error message and status code.
AllfindUniqueandfindFirstPrisma calls in API routes MUST include ownership filters (e.g., userId or emailAccountId).
AllfindManyPrisma calls in API routes MUST be scoped to the authenticated user's data.
Never use direct object references in API routes without ownership checks (prevent IDOR vulnerabilities).
Prevent mass assignment vulnerabilities by only allowing explicitly whitelisted fields in update operations in AP...
Files:
apps/web/app/api/outlook/calendar/callback/route.ts
apps/web/app/api/**/*.{ts,js}
📄 CodeRabbit inference engine (.cursor/rules/security-audit.mdc)
apps/web/app/api/**/*.{ts,js}: All API route handlers in 'apps/web/app/api/' must use authentication middleware: withAuth, withEmailAccount, or withError (with custom authentication logic).
All Prisma queries in API routes must include user/account filtering (e.g., emailAccountId or userId in WHERE clauses) to prevent unauthorized data access.
All parameters used in API routes must be validated before use; do not use parameters from 'params' or request bodies directly in queries without validation.
Request bodies in API routes should use Zod schemas for validation.
API routes should only return necessary fields using Prisma's 'select' and must not include sensitive data in error messages.
Error messages in API routes must not reveal internal details; use generic errors and SafeError for user-facing errors.
All QStash endpoints (API routes called via publishToQstash or publishToQstashQueue) must use verifySignatureAppRouter to verify request authenticity.
All cron endpoints in API routes must use hasCronSecret or hasPostCronSecret for authentication.
Do not hardcode weak or plaintext secrets in API route files; secrets must not be directly assigned as string literals.
Review all new withError usage in API routes to ensure custom authentication is implemented where required.
Files:
apps/web/app/api/outlook/calendar/callback/route.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useelements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...
Files:
apps/web/app/api/outlook/calendar/callback/route.ts
!pages/_document.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.
Files:
apps/web/app/api/outlook/calendar/callback/route.ts
🧬 Code graph analysis (1)
apps/web/app/api/outlook/calendar/callback/route.ts (5)
apps/web/utils/middleware.ts (1)
withError(283-288)apps/web/utils/calendar/constants.ts (1)
CALENDAR_STATE_COOKIE_NAME(1-1)apps/web/utils/oauth/state.ts (1)
parseOAuthState(25-29)apps/web/utils/auth.ts (1)
auth(499-500)apps/web/utils/outlook/calendar-client.ts (2)
getCalendarClientWithRefresh(44-133)fetchMicrosoftCalendars(135-151)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Static Code Analysis Js
- GitHub Check: Jit Security
- GitHub Check: test
🔇 Additional comments (2)
apps/web/app/api/outlook/calendar/callback/route.ts (2)
81-88: Ensure the authorized Microsoft account matches the email account being connected.Currently, any Microsoft identity can be connected to the provided emailAccountId after ownership check. Verify whether product requirements mandate matching the Microsoft email to the email account’s address; if so, enforce it and fail with a specific error.
If applicable, extend the
emailAccountselect to include its address and compare case-insensitively before creating the connection.Also applies to: 136-141
17-20: Middleware choice: withError vs withAuth for GET route.Guidelines say wrap GET routes with withAuth/withEmailAccount; here you use withError and perform custom auth. For OAuth callbacks this can be acceptable; please confirm this exception is intentional for consistency.
Also applies to: 73-80
| let decodedState: { emailAccountId: string; type: string; nonce: string }; | ||
| try { | ||
| decodedState = parseOAuthState(storedState); | ||
| } catch (error) { | ||
| logger.error("Failed to decode state", { error }); | ||
| redirectUrl.searchParams.set("error", "invalid_state_format"); | ||
| return NextResponse.redirect(redirectUrl, { headers: response.headers }); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Validate OAuth state and params with Zod (MUST as per guidelines).
State shape and emailAccountId format aren’t validated. Add a Zod schema to enforce type: "calendar" and a UUID/ID format for emailAccountId before use.
Suggested change:
+import { z } from "zod";
@@
- let decodedState: { emailAccountId: string; type: string; nonce: string };
+ const StateSchema = z.object({
+ nonce: z.string().min(8).max(128),
+ type: z.literal("calendar"),
+ emailAccountId: z.string().min(1).max(64), // replace with .uuid() if UUID
+ });
+ let decodedState: z.infer<typeof StateSchema>;
try {
- decodedState = parseOAuthState(storedState);
+ decodedState = StateSchema.parse(parseOAuthState(storedState));
} catch (error) {
logger.error("Failed to decode state", { error });
redirectUrl.searchParams.set("error", "invalid_state_format");
- return NextResponse.redirect(redirectUrl, { headers: response.headers });
+ return redirectWithCookie(redirectUrl);
}
@@
- if (decodedState.type !== "calendar") {
- logger.error("Invalid state type for calendar callback", {
- type: decodedState.type,
- });
- redirectUrl.searchParams.set("error", "invalid_state_type");
- return NextResponse.redirect(redirectUrl, { headers: response.headers });
- }
+ // type already validated by ZodAlso consider validating code length > 10 before use.
Also applies to: 57-66
🤖 Prompt for AI Agents
In apps/web/app/api/outlook/calendar/callback/route.ts around lines 48-55 (and
also apply same validation to lines 57-66), add a Zod schema for the OAuth state
such as an object with emailAccountId (validate as UUID or the app's ID
pattern), type (literal "calendar") and nonce (string), then replace the untyped
parseOAuthState usage with schema.safeParse; on failure log the validation error
and redirect with error=invalid_state_format. Also validate the incoming `code`
param (e.g., ensure it exists and length > 10) before using it; if invalid, log
and redirect with an appropriate error query param. Ensure all early returns
preserve response.headers like the existing redirects.
|
Hey, this isn't integrated into the drafting system. The point of adding the Goop Outlook calendar is so that the AI can then use it to draft responses. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❌ Jit has detected 1 important finding in this PR that you should review.
The finding is detailed below as a comment.
It’s highly recommended that you fix this security issue before merge.
| const storedState = request.cookies.get(CALENDAR_STATE_COOKIE_NAME)?.value; | ||
|
|
||
| const redirectUrl = new URL("/calendars", request.nextUrl.origin); | ||
| const response = NextResponse.redirect(redirectUrl); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Security control: Static Code Analysis Js
Open Redirect Vulnerability In Express Redirect() Method
Untrusted user input in redirect() can result in Open Redirect vulnerability.
Severity: HIGH
Jit Bot commands and options (e.g., ignore issue)
You can trigger Jit actions by commenting on this PR review:
#jit_ignore_fpIgnore and mark this specific single instance of finding as “False Positive”#jit_ignore_acceptIgnore and mark this specific single instance of finding as “Accept Risk”#jit_ignore_type_in_fileIgnore any finding of type "Open Redirect Vulnerability in Express redirect() Method" in apps/web/utils/calendar/oauth-callback-helpers.ts; future occurrences will also be ignored.#jit_undo_ignoreUndo ignore command
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
🧹 Nitpick comments (8)
apps/web/app/api/google/calendar/callback/route.ts (1)
3-10: Thin wrapper LGTM; export a response type for client usage.Delegating to handleCalendarCallback is clean and matches middleware guidance. Add an explicit exported response type for type‑safe client imports.
+// Optional: export response type for clients +export type GetGoogleCalendarCallbackResponse = Awaited<ReturnType<typeof GET>>;apps/web/utils/calendar/handle-calendar-callback.ts (1)
102-113: Wire calendars to the drafting system post‑connect.After successful connection, enqueue an events sync and update the drafting context (e.g., availability cache) so the AI can use Outlook/Google data when drafting.
- Publish a background job: sync last 60 days and next 60 days of events for the connection.
- Update the drafting pipeline to read from a provider‑agnostic Events table and expose “getAvailability(emailAccountId, window)” to the composer.
- Emit a “calendar_connected” domain event for downstream consumers (drafting indexer).
I can sketch the job interface and the context fetch adapter if helpful.
apps/web/utils/calendar/providers/google.ts (1)
64-87: Upsert looks solid; consider persisting color/accessRole for UX.Optional: store Google calendar color and accessRole to improve selection UI and permissions handling later.
apps/web/utils/calendar/providers/microsoft.ts (1)
21-36: Network calls lack timeouts; add AbortController for resilience.Production OAuth/Graph calls should have timeouts/retries to avoid hanging requests.
- const tokenResponse = await fetch( + const tokenAbort = new AbortController(); + const tokenTimeout = setTimeout(() => tokenAbort.abort(), 15_000); + const tokenResponse = await fetch( "https://login.microsoftonline.com/common/oauth2/v2.0/token", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", }, body: new URLSearchParams({ client_id: env.MICROSOFT_CLIENT_ID, client_secret: env.MICROSOFT_CLIENT_SECRET, code, grant_type: "authorization_code", redirect_uri: `${env.NEXT_PUBLIC_BASE_URL}/api/outlook/calendar/callback`, }), + signal: tokenAbort.signal, }, ); + clearTimeout(tokenTimeout);Apply similar timeout to the profile fetch.
apps/web/utils/calendar/oauth-types.ts (2)
35-39: Narrow the OAuth state type for stronger guarantees.Use a literal type to enforce only "calendar" at compile time (you still keep the runtime check).
export interface CalendarOAuthState { emailAccountId: string; - type: string; + type: "calendar"; nonce: string; }
10-27: Consider usingexport typealiases for consistency.Guidelines prefer export type over interfaces for shared types. Optional, non‑blocking.
apps/web/utils/calendar/oauth-callback-helpers.ts (2)
126-138: Add explicit return type annotation.The function is missing an explicit return type annotation. While TypeScript can infer it, explicit types improve code clarity and catch unintended changes.
Apply this diff to add the return type:
export async function checkExistingConnection( emailAccountId: string, provider: "google" | "microsoft", email: string, -) { +): Promise<CalendarConnection | null> { return await prisma.calendarConnection.findFirst({Additionally, consider extracting the
"google" | "microsoft"provider type to a shared type definition to avoid duplication across multiple functions in this file.
143-162: Add explicit return type and verifyisConnectedlogic.Two concerns:
- The function is missing an explicit return type annotation.
- Line 159 hardcodes
isConnected: truewithout verifying the connection is actually functional. Should this be validated before marking the connection as connected?Apply this diff to add the return type:
export async function createCalendarConnection(params: { provider: "google" | "microsoft"; email: string; emailAccountId: string; accessToken: string; refreshToken: string; expiresAt: Date | null; -}) { +}): Promise<CalendarConnection> { return await prisma.calendarConnection.create({Regarding
isConnected: true- verify whether the connection should be tested before marking it as connected, or if the presence of valid tokens is sufficient.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
apps/web/app/api/google/calendar/callback/route.ts(1 hunks)apps/web/app/api/outlook/calendar/callback/route.ts(1 hunks)apps/web/utils/calendar/handle-calendar-callback.ts(1 hunks)apps/web/utils/calendar/oauth-callback-helpers.ts(1 hunks)apps/web/utils/calendar/oauth-types.ts(1 hunks)apps/web/utils/calendar/providers/google.ts(1 hunks)apps/web/utils/calendar/providers/microsoft.ts(1 hunks)version.txt(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- version.txt
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/web/app/api/outlook/calendar/callback/route.ts
🧰 Additional context used
📓 Path-based instructions (12)
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use@/for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX
Files:
apps/web/utils/calendar/providers/google.tsapps/web/utils/calendar/oauth-types.tsapps/web/app/api/google/calendar/callback/route.tsapps/web/utils/calendar/handle-calendar-callback.tsapps/web/utils/calendar/providers/microsoft.tsapps/web/utils/calendar/oauth-callback-helpers.ts
!{.cursor/rules/*.mdc}
📄 CodeRabbit inference engine (.cursor/rules/cursor-rules.mdc)
Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location
Files:
apps/web/utils/calendar/providers/google.tsapps/web/utils/calendar/oauth-types.tsapps/web/app/api/google/calendar/callback/route.tsapps/web/utils/calendar/handle-calendar-callback.tsapps/web/utils/calendar/providers/microsoft.tsapps/web/utils/calendar/oauth-callback-helpers.ts
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)
**/*.ts: The same validation should be done in the server action too
Define validation schemas using Zod
Files:
apps/web/utils/calendar/providers/google.tsapps/web/utils/calendar/oauth-types.tsapps/web/app/api/google/calendar/callback/route.tsapps/web/utils/calendar/handle-calendar-callback.tsapps/web/utils/calendar/providers/microsoft.tsapps/web/utils/calendar/oauth-callback-helpers.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}: UsecreateScopedLoggerfor logging in backend TypeScript files
Typically add the logger initialization at the top of the file when usingcreateScopedLogger
Only use.with()on a logger instance within a specific function, not for a global loggerImport Prisma in the project using
import prisma from "@/utils/prisma";
**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.
Files:
apps/web/utils/calendar/providers/google.tsapps/web/utils/calendar/oauth-types.tsapps/web/app/api/google/calendar/callback/route.tsapps/web/utils/calendar/handle-calendar-callback.tsapps/web/utils/calendar/providers/microsoft.tsapps/web/utils/calendar/oauth-callback-helpers.ts
apps/web/utils/**
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Create utility functions in
utils/folder for reusable logic
Files:
apps/web/utils/calendar/providers/google.tsapps/web/utils/calendar/oauth-types.tsapps/web/utils/calendar/handle-calendar-callback.tsapps/web/utils/calendar/providers/microsoft.tsapps/web/utils/calendar/oauth-callback-helpers.ts
apps/web/utils/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
apps/web/utils/**/*.ts: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size
Files:
apps/web/utils/calendar/providers/google.tsapps/web/utils/calendar/oauth-types.tsapps/web/utils/calendar/handle-calendar-callback.tsapps/web/utils/calendar/providers/microsoft.tsapps/web/utils/calendar/oauth-callback-helpers.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useelements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...
Files:
apps/web/utils/calendar/providers/google.tsapps/web/utils/calendar/oauth-types.tsapps/web/app/api/google/calendar/callback/route.tsapps/web/utils/calendar/handle-calendar-callback.tsapps/web/utils/calendar/providers/microsoft.tsapps/web/utils/calendar/oauth-callback-helpers.ts
!pages/_document.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.
Files:
apps/web/utils/calendar/providers/google.tsapps/web/utils/calendar/oauth-types.tsapps/web/app/api/google/calendar/callback/route.tsapps/web/utils/calendar/handle-calendar-callback.tsapps/web/utils/calendar/providers/microsoft.tsapps/web/utils/calendar/oauth-callback-helpers.ts
apps/web/app/**
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
NextJS app router structure with (app) directory
Files:
apps/web/app/api/google/calendar/callback/route.ts
apps/web/app/api/**/route.ts
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/app/api/**/route.ts: UsewithAuthfor user-level operations
UsewithEmailAccountfor email-account-level operations
Do NOT use POST API routes for mutations - use server actions instead
No need for try/catch in GET routes when using middleware
Export response types from GET routes
apps/web/app/api/**/route.ts: Wrap all GET API route handlers withwithAuthorwithEmailAccountmiddleware for authentication and authorization.
Export response types from GET API routes for type-safe client usage.
Do not use try/catch in GET API routes when using authentication middleware; rely on centralized error handling.
Files:
apps/web/app/api/google/calendar/callback/route.ts
**/api/**/route.ts
📄 CodeRabbit inference engine (.cursor/rules/security.mdc)
**/api/**/route.ts: ALL API routes that handle user data MUST use appropriate authentication and authorization middleware (withAuth or withEmailAccount).
ALL database queries in API routes MUST be scoped to the authenticated user/account (e.g., include userId or emailAccountId in query filters).
Always validate that resources belong to the authenticated user before performing operations (resource ownership validation).
UsewithEmailAccountmiddleware for API routes that operate on a specific email account (i.e., use or requireemailAccountId).
UsewithAuthmiddleware for API routes that operate at the user level (i.e., use or require onlyuserId).
UsewithErrormiddleware (with proper validation) for public endpoints, custom authentication, or cron endpoints.
Cron endpoints MUST usewithErrormiddleware and validate the cron secret usinghasCronSecret(request)orhasPostCronSecret(request).
Cron endpoints MUST capture unauthorized attempts withcaptureExceptionand return a 401 status for unauthorized requests.
All parameters in API routes MUST be validated for type, format, and length before use.
Request bodies in API routes MUST be validated using Zod schemas before use.
All Prisma queries in API routes MUST only return necessary fields and never expose sensitive data.
Error messages in API routes MUST not leak internal information or sensitive data; use generic error messages and SafeError where appropriate.
API routes MUST use a consistent error response format, returning JSON with an error message and status code.
AllfindUniqueandfindFirstPrisma calls in API routes MUST include ownership filters (e.g., userId or emailAccountId).
AllfindManyPrisma calls in API routes MUST be scoped to the authenticated user's data.
Never use direct object references in API routes without ownership checks (prevent IDOR vulnerabilities).
Prevent mass assignment vulnerabilities by only allowing explicitly whitelisted fields in update operations in AP...
Files:
apps/web/app/api/google/calendar/callback/route.ts
apps/web/app/api/**/*.{ts,js}
📄 CodeRabbit inference engine (.cursor/rules/security-audit.mdc)
apps/web/app/api/**/*.{ts,js}: All API route handlers in 'apps/web/app/api/' must use authentication middleware: withAuth, withEmailAccount, or withError (with custom authentication logic).
All Prisma queries in API routes must include user/account filtering (e.g., emailAccountId or userId in WHERE clauses) to prevent unauthorized data access.
All parameters used in API routes must be validated before use; do not use parameters from 'params' or request bodies directly in queries without validation.
Request bodies in API routes should use Zod schemas for validation.
API routes should only return necessary fields using Prisma's 'select' and must not include sensitive data in error messages.
Error messages in API routes must not reveal internal details; use generic errors and SafeError for user-facing errors.
All QStash endpoints (API routes called via publishToQstash or publishToQstashQueue) must use verifySignatureAppRouter to verify request authenticity.
All cron endpoints in API routes must use hasCronSecret or hasPostCronSecret for authentication.
Do not hardcode weak or plaintext secrets in API route files; secrets must not be directly assigned as string literals.
Review all new withError usage in API routes to ensure custom authentication is implemented where required.
Files:
apps/web/app/api/google/calendar/callback/route.ts
🧬 Code graph analysis (5)
apps/web/utils/calendar/providers/google.ts (4)
apps/web/utils/logger.ts (1)
createScopedLogger(17-80)apps/web/utils/calendar/oauth-types.ts (2)
CalendarOAuthProvider(10-27)CalendarTokens(3-8)apps/web/utils/calendar/client.ts (2)
getCalendarOAuth2Client(31-37)fetchGoogleCalendars(114-124)apps/web/env.ts (1)
env(16-242)
apps/web/app/api/google/calendar/callback/route.ts (2)
apps/web/utils/calendar/handle-calendar-callback.ts (1)
handleCalendarCallback(19-135)apps/web/utils/calendar/providers/google.ts (1)
googleCalendarProvider(13-98)
apps/web/utils/calendar/handle-calendar-callback.ts (3)
apps/web/utils/calendar/oauth-types.ts (1)
CalendarOAuthProvider(10-27)apps/web/utils/logger.ts (1)
Logger(5-5)apps/web/utils/calendar/oauth-callback-helpers.ts (9)
validateOAuthCallback(17-47)parseAndValidateCalendarState(52-77)buildCalendarRedirectUrl(82-87)verifyEmailAccountAccess(92-121)checkExistingConnection(126-138)redirectWithMessage(167-174)createCalendarConnection(143-162)RedirectError(191-201)redirectWithError(179-186)
apps/web/utils/calendar/providers/microsoft.ts (4)
apps/web/utils/logger.ts (1)
createScopedLogger(17-80)apps/web/utils/calendar/oauth-types.ts (2)
CalendarOAuthProvider(10-27)CalendarTokens(3-8)apps/web/env.ts (1)
env(16-242)apps/web/utils/outlook/calendar-client.ts (1)
fetchMicrosoftCalendars(135-151)
apps/web/utils/calendar/oauth-callback-helpers.ts (5)
apps/web/utils/logger.ts (1)
Logger(5-5)apps/web/utils/calendar/oauth-types.ts (2)
OAuthCallbackValidation(29-33)CalendarOAuthState(35-39)apps/web/utils/calendar/constants.ts (1)
CALENDAR_STATE_COOKIE_NAME(1-1)apps/web/utils/oauth/state.ts (1)
parseOAuthState(25-29)apps/web/utils/auth.ts (1)
auth(499-500)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Static Code Analysis Js
- GitHub Check: Jit Security
- GitHub Check: test
🔇 Additional comments (3)
apps/web/utils/calendar/providers/google.ts (1)
26-28: No changes needed; guard and upstream OAuth setup are correct.Verified that the calendar auth flow at
apps/web/app/api/google/calendar/auth-url/route.tsincludes bothaccess_type: "offline"(line 22) andprompt: "consent"(line 25), which guarantees Google will issue a refresh token. The guard clause properly validates its presence.apps/web/utils/calendar/providers/microsoft.ts (2)
74-87: expiresAt: null semantics verified—behavior is correct and intentional.Both implementations handle
expiresAt: nullcorrectly. Whennullis passed:
- Outlook client checks
if (expiresAt && expiresAt > Date.now() && accessToken), which evaluates to false, triggering token refresh- Google client checks
if (expiresAt && expiresAt > Date.now()), which also evaluates to false, triggering token refreshPassing
expiresAt: nullin both the Microsoft and GooglesyncCalendarsmethods is intentional—it forces immediate token refresh, which is the desired behavior during sync operations.
20-44: No action required—offline_access and calendar scopes are already correctly included.The authorization flow already requests
offline_accessand bothCalendars.ReadandCalendars.ReadWritescopes, which are defined inapps/web/utils/outlook/scopes.tsand passed to the Microsoft authorize endpoint viagetCalendarOAuth2Url(). The scopes are properly included in both the initial authorization request and subsequent token refresh requests.
| const storedState = request.cookies.get("calendar_state")?.value; | ||
| if (!storedState) { | ||
| throw new Error("Missing stored state"); | ||
| } | ||
|
|
||
| // Step 2: Parse and validate the OAuth state | ||
| const decodedState = parseAndValidateCalendarState( | ||
| storedState, | ||
| logger, | ||
| redirectUrl, | ||
| response.headers, | ||
| ); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid hardcoded cookie name and redundant state read; decode the received state.
You already validated code/state in validateOAuthCallback. Re-reading a literal "calendar_state" cookie is brittle and unnecessary. Decode the validated state from the request query instead.
- const storedState = request.cookies.get("calendar_state")?.value;
- if (!storedState) {
- throw new Error("Missing stored state");
- }
+ // `validateOAuthCallback` already checked code/state and deleted the cookie.
+ // Decode the received state directly to avoid cookie name coupling.
+ const receivedState = request.nextUrl.searchParams.get("state")!;
// Step 2: Parse and validate the OAuth state
const decodedState = parseAndValidateCalendarState(
- storedState,
+ receivedState,
logger,
redirectUrl,
response.headers,
);Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In apps/web/utils/calendar/handle-calendar-callback.ts around lines 31 to 43,
remove the brittle hardcoded cookie read of "calendar_state" and the redundant
storedState check; instead use the already-validated OAuth state from
validateOAuthCallback (the decoded/validated state parsed from the request
query) when calling parseAndValidateCalendarState. Replace the storedState
variable usage with the validated state value passed into
parseAndValidateCalendarState, and drop the throw for missing cookie; also
remove any direct dependency on a literal cookie name so the state source is the
validated callback payload.
| // Handle all other errors | ||
| logger.error("Error in calendar callback", { error }); | ||
|
|
||
| // Try to build a redirect URL, fallback to /calendars | ||
| const errorRedirectUrl = new URL("/calendars", request.nextUrl.origin); | ||
| return redirectWithError( | ||
| errorRedirectUrl, | ||
| "connection_failed", | ||
| new Headers(), | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ensure the state cookie is deleted even on generic errors.
When errors are not RedirectError, you return with new Headers(), dropping the cookie deletion set in validateOAuthCallback. Preserve those headers.
-export async function handleCalendarCallback(
+export async function handleCalendarCallback(
request: NextRequest,
provider: CalendarOAuthProvider,
logger: Logger,
): Promise<NextResponse> {
- try {
+ // Headers to carry cookie deletions from validation through all exit paths
+ let redirectHeaders = new Headers();
+ try {
// Step 1: Validate OAuth callback parameters
const { code, redirectUrl, response } = await validateOAuthCallback(
request,
logger,
);
+ redirectHeaders = response.headers;
@@
- return redirectWithMessage(
- finalRedirectUrl,
- "calendar_already_connected",
- response.headers,
- );
+ return redirectWithMessage(finalRedirectUrl, "calendar_already_connected", redirectHeaders);
@@
- return redirectWithMessage(
- finalRedirectUrl,
- "calendar_connected",
- response.headers,
- );
+ return redirectWithMessage(finalRedirectUrl, "calendar_connected", redirectHeaders);
} catch (error) {
@@
- const errorRedirectUrl = new URL("/calendars", request.nextUrl.origin);
- return redirectWithError(
- errorRedirectUrl,
- "connection_failed",
- new Headers(),
- );
+ const errorRedirectUrl = new URL("/calendars", request.nextUrl.origin);
+ return redirectWithError(errorRedirectUrl, "connection_failed", redirectHeaders);
}
}Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In apps/web/utils/calendar/handle-calendar-callback.ts around lines 124-133, the
error path creates a new Headers() losing the cookie-deletion headers set
earlier; update this return to pass the existing headers (the headers object
populated in validateOAuthCallback or the responseHeaders variable) into
redirectWithError instead of new Headers() so the state cookie is removed on
generic errors as well.
| const calendarClient = await getCalendarClientWithRefresh({ | ||
| accessToken, | ||
| refreshToken, | ||
| expiresAt: null, | ||
| emailAccountId, | ||
| }); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Passing expiresAt: null may cause unnecessary refreshes; verify client expectations.
If getCalendarClientWithRefresh uses expiresAt to decide refresh, passing null could force immediate refresh or skip needed refresh logic.
🏁 Script executed:
#!/bin/bash
# Inspect getCalendarClientWithRefresh signature and logic
rg -n -C3 --type=ts 'function getCalendarClientWithRefresh|getCalendarClientWithRefresh\s*\(' apps/web/utilsLength of output: 1549
I need to search within the codebase for the getCalendarClientWithRefresh function definition. Let me look for imports and the function itself.
---
🏁 Script executed:
```shell
#!/bin/bash
# Search for imports of getCalendarClientWithRefresh in the google.ts file
rg -n 'import.*getCalendarClientWithRefresh|from.*getCalendarClientWithRefresh' apps/web/utils/calendar/providers/google.ts
Length of output: 0
---
🏁 Script executed:
```shell
#!/bin/bash
# Search for the function definition of getCalendarClientWithRefresh across calendar utils
rg -n 'function getCalendarClientWithRefresh|export.*getCalendarClientWithRefresh|const getCalendarClientWithRefresh' apps/web/utils/calendar --type=ts
Length of output: 89
---
🏁 Script executed:
```shell
#!/bin/bash
# View the getCalendarClientWithRefresh function implementation
cat apps/web/utils/calendar/client.ts | head -100 | tail -70
Length of output: 2125
---
🏁 Script executed:
```shell
#!/bin/bash
# View the function in google.ts that contains the getCalendarClientWithRefresh call (around line 55)
sed -n '40,70p' apps/web/utils/calendar/providers/google.ts
Length of output: 807
---
🏁 Script executed:
```shell
#!/bin/bash
# View the availability.ts function signature and context around line 81
sed -n '65,95p' apps/web/utils/calendar/availability.ts
Length of output: 822
---
🏁 Script executed:
```shell
#!/bin/bash
# Search for calendarConnection queries to see what fields are stored/retrieved
rg -n 'prisma\.calendarConnection' apps/web/utils/calendar --type=ts -A 3 -B 1 | head -50
Length of output: 2394
---
🏁 Script executed:
```shell
#!/bin/bash
# Search for saveCalendarTokens function to see what token data is stored
rg -n 'saveCalendarTokens|function saveCalendarTokens|export.*saveCalendarTokens' apps/web/utils/calendar --type=ts -A 10
Length of output: 1390
---
🏁 Script executed:
```shell
#!/bin/bash
# View the full saveCalendarTokens function implementation
sed -n '126,160p' apps/web/utils/calendar/client.ts
Length of output: 826
Pass actual token expiry from database instead of hardcoded null to avoid unnecessary refreshes.
The calendarConnection record stores expiresAt (persisted in saveCalendarTokens), but this function hardcodes expiresAt: null, bypassing the freshness check in getCalendarClientWithRefresh that returns early when expiresAt > Date.now(). Query the stored expiresAt value and pass it to prevent forcing a refresh on every call:
// Before calling getCalendarClientWithRefresh, query the connection:
const connection = await prisma.calendarConnection.findFirst({
where: { id: connectionId, provider: "google" },
select: { expiresAt: true },
});
const calendarClient = await getCalendarClientWithRefresh({
accessToken,
refreshToken,
expiresAt: connection?.expiresAt?.getTime() ?? null,
emailAccountId,
});🤖 Prompt for AI Agents
In apps/web/utils/calendar/providers/google.ts around lines 55 to 61, the call
to getCalendarClientWithRefresh passes expiresAt: null which forces unnecessary
token refreshes; query the stored calendarConnection by connectionId (selecting
expiresAt), convert the resulting Date to a timestamp (or use null if missing),
and pass that value as expiresAt to getCalendarClientWithRefresh so the function
can short-circuit when the token is still fresh; also handle the case where the
connection is not found (use null) and keep
accessToken/refreshToken/emailAccountId as before.
| return { | ||
| accessToken: tokens.access_token, | ||
| refreshToken: tokens.refresh_token, | ||
| expiresAt: tokens.expires_in | ||
| ? new Date(Date.now() + tokens.expires_in * 1000) | ||
| : null, | ||
| email: microsoftEmail, | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Validate presence of refresh_token before proceeding.
Without offline_access or if the app is misconfigured, Microsoft won’t return a refresh_token. Proceeding would create a brittle connection.
return {
- accessToken: tokens.access_token,
- refreshToken: tokens.refresh_token,
+ accessToken: tokens.access_token,
+ refreshToken: tokens.refresh_token,
expiresAt: tokens.expires_in
? new Date(Date.now() + tokens.expires_in * 1000)
: null,
email: microsoftEmail,
};Add validation:
+ if (!tokens.refresh_token) {
+ throw new Error("No refresh_token returned from Microsoft (ensure offline_access scope and correct app type)");
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| return { | |
| accessToken: tokens.access_token, | |
| refreshToken: tokens.refresh_token, | |
| expiresAt: tokens.expires_in | |
| ? new Date(Date.now() + tokens.expires_in * 1000) | |
| : null, | |
| email: microsoftEmail, | |
| }; | |
| if (!tokens.refresh_token) { | |
| throw new Error("No refresh_token returned from Microsoft (ensure offline_access scope and correct app type)"); | |
| } | |
| return { | |
| accessToken: tokens.access_token, | |
| refreshToken: tokens.refresh_token, | |
| expiresAt: tokens.expires_in | |
| ? new Date(Date.now() + tokens.expires_in * 1000) | |
| : null, | |
| email: microsoftEmail, | |
| }; |
🤖 Prompt for AI Agents
In apps/web/utils/calendar/providers/microsoft.ts around lines 64 to 71, the
code returns tokens.refresh_token without verifying it exists — add a check that
tokens.refresh_token is present and valid before returning; if it's missing,
fail fast by throwing a clear error or returning a null/Result indicating the
missing refresh token (include a message instructing to ensure offline_access
scope and correct app configuration), and update any callers to handle the
error/nullable response.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
apps/web/utils/calendar/providers/microsoft-availability.ts (1)
11-66: Consider defensive property access for API response data.The code checks
item.start && item.endat line 44, but then directly accessesitem.start.dateTimeanditem.end.dateTimeat lines 46-47 without verifying thedateTimeproperty exists. While the Microsoft Graph API contract likely guarantees these properties, defensive coding would be safer.Consider using optional chaining or explicit checks:
- if (item.status !== "free" && item.start && item.end) { + if (item.status !== "free" && item.start?.dateTime && item.end?.dateTime) { busyPeriods.push({ start: item.start.dateTime, end: item.end.dateTime, }); }
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
apps/web/utils/ai/calendar/availability.ts(3 hunks)apps/web/utils/calendar/availability-types.ts(1 hunks)apps/web/utils/calendar/availability.ts(0 hunks)apps/web/utils/calendar/providers/google-availability.ts(1 hunks)apps/web/utils/calendar/providers/microsoft-availability.ts(1 hunks)apps/web/utils/calendar/unified-availability.ts(1 hunks)
💤 Files with no reviewable changes (1)
- apps/web/utils/calendar/availability.ts
🧰 Additional context used
📓 Path-based instructions (10)
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use@/for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX
Files:
apps/web/utils/calendar/unified-availability.tsapps/web/utils/calendar/providers/google-availability.tsapps/web/utils/calendar/providers/microsoft-availability.tsapps/web/utils/ai/calendar/availability.tsapps/web/utils/calendar/availability-types.ts
!{.cursor/rules/*.mdc}
📄 CodeRabbit inference engine (.cursor/rules/cursor-rules.mdc)
Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location
Files:
apps/web/utils/calendar/unified-availability.tsapps/web/utils/calendar/providers/google-availability.tsapps/web/utils/calendar/providers/microsoft-availability.tsapps/web/utils/ai/calendar/availability.tsapps/web/utils/calendar/availability-types.ts
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)
**/*.ts: The same validation should be done in the server action too
Define validation schemas using Zod
Files:
apps/web/utils/calendar/unified-availability.tsapps/web/utils/calendar/providers/google-availability.tsapps/web/utils/calendar/providers/microsoft-availability.tsapps/web/utils/ai/calendar/availability.tsapps/web/utils/calendar/availability-types.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}: UsecreateScopedLoggerfor logging in backend TypeScript files
Typically add the logger initialization at the top of the file when usingcreateScopedLogger
Only use.with()on a logger instance within a specific function, not for a global loggerImport Prisma in the project using
import prisma from "@/utils/prisma";
**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.
Files:
apps/web/utils/calendar/unified-availability.tsapps/web/utils/calendar/providers/google-availability.tsapps/web/utils/calendar/providers/microsoft-availability.tsapps/web/utils/ai/calendar/availability.tsapps/web/utils/calendar/availability-types.ts
apps/web/utils/**
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Create utility functions in
utils/folder for reusable logic
Files:
apps/web/utils/calendar/unified-availability.tsapps/web/utils/calendar/providers/google-availability.tsapps/web/utils/calendar/providers/microsoft-availability.tsapps/web/utils/ai/calendar/availability.tsapps/web/utils/calendar/availability-types.ts
apps/web/utils/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
apps/web/utils/**/*.ts: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size
Files:
apps/web/utils/calendar/unified-availability.tsapps/web/utils/calendar/providers/google-availability.tsapps/web/utils/calendar/providers/microsoft-availability.tsapps/web/utils/ai/calendar/availability.tsapps/web/utils/calendar/availability-types.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useelements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...
Files:
apps/web/utils/calendar/unified-availability.tsapps/web/utils/calendar/providers/google-availability.tsapps/web/utils/calendar/providers/microsoft-availability.tsapps/web/utils/ai/calendar/availability.tsapps/web/utils/calendar/availability-types.ts
!pages/_document.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.
Files:
apps/web/utils/calendar/unified-availability.tsapps/web/utils/calendar/providers/google-availability.tsapps/web/utils/calendar/providers/microsoft-availability.tsapps/web/utils/ai/calendar/availability.tsapps/web/utils/calendar/availability-types.ts
apps/web/utils/ai/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/llm.mdc)
apps/web/utils/ai/**/*.{ts,tsx}: Place main LLM feature implementations under apps/web/utils/ai/
LLM feature functions should follow the provided TypeScript pattern (separate system/user prompts, use createGenerateObject, Zod schema validation, early validation, return result.object)
Keep system prompts and user prompts separate
System prompt should define the LLM's role and task specifications
User prompt should contain the actual data and context
Always define a Zod schema for response validation
Make Zod schemas as specific as possible to guide LLM output
Use descriptive scoped loggers for each feature
Log inputs and outputs with appropriate log levels and include relevant context
Implement early returns for invalid inputs
Use proper error types and logging for failures
Implement fallbacks for AI failures
Add retry logic for transient failures using withRetry
Use XML-like tags to structure data in prompts
Remove excessive whitespace and truncate long inputs in prompts
Format prompt data consistently across similar functions
Use TypeScript types for all parameters and return values in LLM features
Define clear interfaces for complex input/output structures in LLM features
Files:
apps/web/utils/ai/calendar/availability.ts
apps/web/utils/{ai,llms}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/llm.mdc)
Keep related AI functions co-located and extract common patterns into utilities; document complex AI logic with clear comments
Files:
apps/web/utils/ai/calendar/availability.ts
🧬 Code graph analysis (4)
apps/web/utils/calendar/unified-availability.ts (4)
apps/web/utils/logger.ts (1)
createScopedLogger(17-80)apps/web/utils/calendar/availability-types.ts (1)
BusyPeriod(1-4)apps/web/utils/calendar/providers/google-availability.ts (1)
googleAvailabilityProvider(63-89)apps/web/utils/calendar/providers/microsoft-availability.ts (1)
microsoftAvailabilityProvider(68-94)
apps/web/utils/calendar/providers/google-availability.ts (2)
apps/web/utils/logger.ts (1)
createScopedLogger(17-80)apps/web/utils/calendar/availability-types.ts (2)
BusyPeriod(1-4)CalendarAvailabilityProvider(6-21)
apps/web/utils/calendar/providers/microsoft-availability.ts (2)
apps/web/utils/logger.ts (1)
createScopedLogger(17-80)apps/web/utils/calendar/availability-types.ts (2)
BusyPeriod(1-4)CalendarAvailabilityProvider(6-21)
apps/web/utils/ai/calendar/availability.ts (1)
apps/web/utils/calendar/unified-availability.ts (1)
getUnifiedCalendarAvailability(14-137)
🔇 Additional comments (11)
apps/web/utils/ai/calendar/availability.ts (2)
6-6: LGTM! Clear refactoring to unified availability.The import and tool description accurately reflect the new unified calendar availability approach that aggregates across all connected Google and Microsoft calendars.
Also applies to: 112-113
126-142: Excellent error handling for calendar availability.The try-catch wrapper ensures that calendar fetch errors don't break the AI drafting flow. Returning an empty
busyPeriodsarray on error allows the AI to continue processing without calendar data, which is the right fallback behavior.apps/web/utils/calendar/providers/microsoft-availability.ts (2)
68-94: LGTM! Clean provider implementation.The Microsoft availability provider correctly implements the
CalendarAvailabilityProviderinterface and properly delegates token refresh togetCalendarClientWithRefresh. The structure mirrors the Google provider for consistency.
1-10: Proper logging setup following guidelines.Logger initialization follows the coding guidelines correctly: uses
createScopedLoggerat the top of the file with an appropriate scope name.Based on learnings
apps/web/utils/calendar/unified-availability.ts (4)
25-39: Excellent timezone handling.The use of
TZDatewithstartOfDayandendOfDayensures proper day boundary calculation in the user's timezone before converting to UTC strings for API calls. This prevents timezone-related edge cases.
42-60: Efficient database query with proper filtering.The Prisma query correctly filters for connected calendars and enabled calendars only, fetching just the needed fields. The early return when no connections exist prevents unnecessary processing.
62-122: Outstanding resilient error handling.The error handling strategy is excellent: each provider fetch has its own
.catchblock that logs the error and returns an empty array. This ensures that if one calendar connection fails (e.g., expired tokens, API issues), the others can still succeed. The use ofPromise.allfor concurrent fetching is appropriate and efficient.
124-137: Clean result aggregation.The use of
Promise.allfollowed byflat()efficiently aggregates busy periods from all providers. The final trace log with connection counts provides useful observability.apps/web/utils/calendar/availability-types.ts (1)
1-21: Clean type definitions establishing provider contract.The types are well-designed:
BusyPeriodis simple and focused, whileCalendarAvailabilityProviderprovides a clear interface for implementations. The parameter types appropriately handle the OAuth token refresh flow (optionalaccessToken, requiredrefreshToken).apps/web/utils/calendar/providers/google-availability.ts (2)
11-61: LGTM! Solid Google Calendar integration.The implementation correctly uses the Google Calendar freebusy API and properly validates that
period.startandperiod.endexist before creatingBusyPeriodobjects. The structure is consistent with the Microsoft provider for maintainability.
63-89: Well-structured provider implementation.The Google availability provider correctly implements the
CalendarAvailabilityProviderinterface with proper token refresh handling. The consistent structure with the Microsoft provider makes the codebase more maintainable.
Summary by CodeRabbit
New Features
Style
Documentation
Bug Fixes