-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Create stats API #607
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?
Create stats API #607
Conversation
|
WalkthroughThis change introduces four new email statistics API endpoints under Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant API_Router
participant Auth
participant Validation
participant DB
participant ExternalService
Client->>API_Router: GET /stats/by-period (with API key & query)
API_Router->>Auth: Validate API key & get user/account
Auth-->>API_Router: User & account info
API_Router->>Validation: Validate query (period, date range)
Validation-->>API_Router: Validated params
API_Router->>DB: Aggregate stats by period
DB-->>API_Router: Stats data
API_Router-->>Client: JSON response
Client->>API_Router: GET /stats/newsletters (with API key & query)
API_Router->>Auth: Validate API key & get user/account
Auth-->>API_Router: User & account info
API_Router->>Validation: Validate query (date range, limit, orderBy)
Validation-->>API_Router: Validated params
API_Router->>DB: Query newsletter stats
DB-->>API_Router: Newsletter data
API_Router-->>Client: JSON response
Client->>API_Router: GET /stats/email-actions (with API key & query)
API_Router->>Auth: Validate API key & get user/account
Auth-->>API_Router: User & account info
API_Router->>Validation: Validate date range
Validation-->>API_Router: Validated params
API_Router->>ExternalService: Fetch archive/delete counts
ExternalService-->>API_Router: Actions data
API_Router-->>Client: JSON response
Client->>API_Router: GET /stats/summary (with API key & query)
API_Router->>Auth: Validate API key & get user/account
Auth-->>API_Router: User & account info
API_Router->>Validation: Validate date range
Validation-->>API_Router: Validated params
API_Router->>DB: Aggregate summary stats
DB-->>API_Router: Summary data
API_Router-->>Client: JSON response
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
Note ⚡️ Unit Test Generation is now available in beta!Learn more here, or try it out under "Finishing Touches" below. ✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
Documentation and Community
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
Claude finished @elie222's task —— View job PR Review: Create Stats APIComprehensive review of the stats API implementation covering code quality, potential bugs, performance, and security. Todo List:
✅ Strengths
|
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: 8
🧹 Nitpick comments (2)
apps/web/app/api/v1/stats/by-period/route.ts (1)
12-12
: Remove unused import.The
format
import from date-fns is not used in the code.-import format from "date-fns/format";
apps/web/app/api/v1/stats/newsletters/route.ts (1)
45-51
: Improve type safety for date conditions.The
dateConditions
object is typed asany
, which reduces type safety. Consider using a more specific type.- const dateConditions: any = {}; + const dateConditions: { + createdAt?: { + gte?: Date; + lte?: Date; + }; + } = {};
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
apps/web/app/api/v1/openapi/route.ts
(2 hunks)apps/web/app/api/v1/stats/by-period/route.ts
(1 hunks)apps/web/app/api/v1/stats/email-actions/route.ts
(1 hunks)apps/web/app/api/v1/stats/newsletters/route.ts
(1 hunks)apps/web/app/api/v1/stats/summary/route.ts
(1 hunks)apps/web/app/api/v1/stats/validation.ts
(1 hunks)
🧰 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/v1/stats/by-period/route.ts
apps/web/app/api/v1/stats/newsletters/route.ts
apps/web/app/api/v1/stats/summary/route.ts
apps/web/app/api/v1/openapi/route.ts
apps/web/app/api/v1/stats/validation.ts
apps/web/app/api/v1/stats/email-actions/route.ts
apps/web/app/**
📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)
NextJS app router structure with (app) directory
Files:
apps/web/app/api/v1/stats/by-period/route.ts
apps/web/app/api/v1/stats/newsletters/route.ts
apps/web/app/api/v1/stats/summary/route.ts
apps/web/app/api/v1/openapi/route.ts
apps/web/app/api/v1/stats/validation.ts
apps/web/app/api/v1/stats/email-actions/route.ts
apps/web/app/api/**/route.ts
📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)
apps/web/app/api/**/route.ts
: UsewithAuth
for user-level operations
UsewithEmailAccount
for email-account-level operations
Do NOT use POST API routes for mutations - use server actions instead
No need for try/catch in GET routes when using middleware
Export response types from GET routes
apps/web/app/api/**/route.ts
: Wrap all GET API route handlers withwithAuth
orwithEmailAccount
middleware for authentication and authorization.
Export response types from GET API routes for type-safe client usage.
Do not use try/catch in GET API routes when using authentication middleware; rely on centralized error handling.
Files:
apps/web/app/api/v1/stats/by-period/route.ts
apps/web/app/api/v1/stats/newsletters/route.ts
apps/web/app/api/v1/stats/summary/route.ts
apps/web/app/api/v1/openapi/route.ts
apps/web/app/api/v1/stats/email-actions/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/v1/stats/by-period/route.ts
apps/web/app/api/v1/stats/newsletters/route.ts
apps/web/app/api/v1/stats/summary/route.ts
apps/web/app/api/v1/openapi/route.ts
apps/web/app/api/v1/stats/validation.ts
apps/web/app/api/v1/stats/email-actions/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/v1/stats/by-period/route.ts
apps/web/app/api/v1/stats/newsletters/route.ts
apps/web/app/api/v1/stats/summary/route.ts
apps/web/app/api/v1/openapi/route.ts
apps/web/app/api/v1/stats/validation.ts
apps/web/app/api/v1/stats/email-actions/route.ts
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}
: UsecreateScopedLogger
for 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/v1/stats/by-period/route.ts
apps/web/app/api/v1/stats/newsletters/route.ts
apps/web/app/api/v1/stats/summary/route.ts
apps/web/app/api/v1/openapi/route.ts
apps/web/app/api/v1/stats/validation.ts
apps/web/app/api/v1/stats/email-actions/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).
UsewithEmailAccount
middleware for API routes that operate on a specific email account (i.e., use or requireemailAccountId
).
UsewithAuth
middleware for API routes that operate at the user level (i.e., use or require onlyuserId
).
UsewithError
middleware (with proper validation) for public endpoints, custom authentication, or cron endpoints.
Cron endpoints MUST usewithError
middleware and validate the cron secret usinghasCronSecret(request)
orhasPostCronSecret(request)
.
Cron endpoints MUST capture unauthorized attempts withcaptureException
and return a 401 status for unauthorized requests.
All parameters in API routes MUST be validated for type, format, and length before use.
Request bodies in API routes MUST be validated using Zod schemas before use.
All Prisma queries in API routes MUST only return necessary fields and never expose sensitive data.
Error messages in API routes MUST not leak internal information or sensitive data; use generic error messages and SafeError where appropriate.
API routes MUST use a consistent error response format, returning JSON with an error message and status code.
AllfindUnique
andfindFirst
Prisma calls in API routes MUST include ownership filters (e.g., userId or emailAccountId).
AllfindMany
Prisma calls in API routes MUST be scoped to the authenticated user's data.
Never use direct object references in API routes without ownership checks (prevent IDOR vulnerabilities).
Prevent mass assignment vulnerabilities by only allowing explicitly whitelisted fields in update operations in AP...
Files:
apps/web/app/api/v1/stats/by-period/route.ts
apps/web/app/api/v1/stats/newsletters/route.ts
apps/web/app/api/v1/stats/summary/route.ts
apps/web/app/api/v1/openapi/route.ts
apps/web/app/api/v1/stats/email-actions/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/v1/stats/by-period/route.ts
apps/web/app/api/v1/stats/newsletters/route.ts
apps/web/app/api/v1/stats/summary/route.ts
apps/web/app/api/v1/openapi/route.ts
apps/web/app/api/v1/stats/validation.ts
apps/web/app/api/v1/stats/email-actions/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/v1/stats/by-period/route.ts
apps/web/app/api/v1/stats/newsletters/route.ts
apps/web/app/api/v1/stats/summary/route.ts
apps/web/app/api/v1/openapi/route.ts
apps/web/app/api/v1/stats/validation.ts
apps/web/app/api/v1/stats/email-actions/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/v1/stats/by-period/route.ts
apps/web/app/api/v1/stats/newsletters/route.ts
apps/web/app/api/v1/stats/summary/route.ts
apps/web/app/api/v1/openapi/route.ts
apps/web/app/api/v1/stats/validation.ts
apps/web/app/api/v1/stats/email-actions/route.ts
🧠 Learnings (7)
📓 Common learnings
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-07-20T09:00:41.968Z
Learning: Applies to apps/web/app/api/**/*.{ts,js} : Request bodies in API routes should use Zod schemas for validation.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-07-18T17:27:46.389Z
Learning: Applies to **/api/**/route.ts : Request bodies in API routes MUST be validated using Zod schemas before use.
apps/web/app/api/v1/stats/by-period/route.ts (15)
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-07-18T15:05:26.713Z
Learning: Applies to app/api/**/route.ts : Always wrap the handler with withAuth
or withEmailAccount
for consistent error handling and authentication in GET API routes.
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.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-07-18T15:05:26.713Z
Learning: Applies to app/api/**/route.ts : Do not use try/catch in GET API route handlers, as withAuth
and withEmailAccount
handle error catching.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-07-18T17:27:46.389Z
Learning: Applies to /api//route.ts : Use withEmailAccount
middleware for API routes that operate on a specific email account (i.e., use or require emailAccountId
).
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/app/api/**/route.ts : Use withEmailAccount
for email-account-level operations
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 : Export response types from GET API routes for type-safe client usage.
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/app/api/**/route.ts : Export response types from GET routes
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-07-18T15:05:26.713Z
Learning: Applies to app/api/**/route.ts : Use Prisma for database queries in GET API routes.
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 : Do not use try/catch in GET API routes when using authentication middleware; rely on centralized error handling.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-07-18T17:27:46.389Z
Learning: Applies to /api//route.ts : ALL database queries in API routes MUST be scoped to the authenticated user/account (e.g., include userId or emailAccountId in query filters).
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/index.mdc:0-0
Timestamp: 2025-07-18T15:05:56.644Z
Learning: Guidelines for implementing GET API routes in Next.js
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-07-18T15:04:44.818Z
Learning: Applies to {app,components}/**/*.{ts,tsx} : For API GET requests to server, use the swr
package
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-07-18T15:05:26.713Z
Learning: Applies to app/api/**/route.ts : Return responses using NextResponse.json()
in GET API routes.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-07-18T15:05:26.713Z
Learning: Applies to app/api/**/route.ts : Infer and export the response type for GET API routes using Awaited<ReturnType<typeof getData>>
as shown in the example.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/index.mdc:0-0
Timestamp: 2025-07-18T15:05:56.644Z
Learning: Guidelines for implementing Next.js server actions
apps/web/app/api/v1/stats/newsletters/route.ts (15)
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-07-18T15:05:26.713Z
Learning: Applies to app/api/**/route.ts : Always wrap the handler with withAuth
or withEmailAccount
for consistent error handling and authentication in GET API routes.
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.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-07-18T15:05:26.713Z
Learning: Applies to app/api/**/route.ts : Do not use try/catch in GET API route handlers, as withAuth
and withEmailAccount
handle error catching.
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 : Export response types from GET API routes for type-safe client usage.
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 : Do not use try/catch in GET API routes when using authentication middleware; rely on centralized error handling.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-07-18T17:27:46.389Z
Learning: Applies to /api//route.ts : Use withError
middleware (with proper validation) for public endpoints, custom authentication, or cron endpoints.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-07-18T15:05:26.713Z
Learning: Applies to app/api/**/route.ts : Use Prisma for database queries in GET API routes.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-07-18T17:27:46.389Z
Learning: Applies to /api//route.ts : ALL API routes that handle user data MUST use appropriate authentication and authorization middleware (withAuth or withEmailAccount).
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/app/api/**/route.ts : Export response types from GET routes
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-07-18T17:27:46.389Z
Learning: Applies to /api//route.ts : Use withEmailAccount
middleware for API routes that operate on a specific email account (i.e., use or require emailAccountId
).
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/index.mdc:0-0
Timestamp: 2025-07-18T15:05:56.644Z
Learning: Guidelines for implementing GET API routes in Next.js
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-07-18T15:05:26.713Z
Learning: Applies to app/api/**/route.ts : Return responses using NextResponse.json()
in GET API routes.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/index.mdc:0-0
Timestamp: 2025-07-18T15:05:56.644Z
Learning: Guidelines for implementing Next.js server actions
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-07-18T15:04:44.818Z
Learning: Applies to {app,components}/**/*.{ts,tsx} : For API GET requests to server, use the swr
package
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-07-19T17:50:28.270Z
Learning: The utils
folder also contains core app logic such as Next.js Server Actions and Gmail API requests.
apps/web/app/api/v1/stats/summary/route.ts (15)
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-07-18T15:05:26.713Z
Learning: Applies to app/api/**/route.ts : Always wrap the handler with withAuth
or withEmailAccount
for consistent error handling and authentication in GET API routes.
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.
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 : Export response types from GET API routes for type-safe client usage.
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/app/api/**/route.ts : Use withEmailAccount
for email-account-level operations
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-07-18T15:05:26.713Z
Learning: Applies to app/api/**/route.ts : Do not use try/catch in GET API route handlers, as withAuth
and withEmailAccount
handle error catching.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-07-18T17:27:46.389Z
Learning: Applies to /api//route.ts : Use withEmailAccount
middleware for API routes that operate on a specific email account (i.e., use or require emailAccountId
).
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/app/api/**/route.ts : Export response types from GET routes
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-07-18T15:05:26.713Z
Learning: Applies to app/api/**/route.ts : Use Prisma for database queries in GET API routes.
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 : Do not use try/catch in GET API routes when using authentication middleware; rely on centralized error handling.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-07-18T17:27:46.389Z
Learning: Applies to /api//route.ts : ALL database queries in API routes MUST be scoped to the authenticated user/account (e.g., include userId or emailAccountId in query filters).
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/index.mdc:0-0
Timestamp: 2025-07-18T15:05:56.644Z
Learning: Guidelines for implementing GET API routes in Next.js
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-07-18T15:05:26.713Z
Learning: Applies to app/api/**/route.ts : Return responses using NextResponse.json()
in GET API routes.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-07-18T15:04:44.818Z
Learning: Applies to {app,components}/**/*.{ts,tsx} : For API GET requests to server, use the swr
package
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-07-19T17:50:28.270Z
Learning: The utils
folder also contains core app logic such as Next.js Server Actions and Gmail API requests.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/index.mdc:0-0
Timestamp: 2025-07-18T15:05:56.644Z
Learning: Guidelines for implementing Next.js server actions
apps/web/app/api/v1/openapi/route.ts (21)
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.
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 : Export response types from GET API routes for type-safe client usage.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-07-20T09:00:41.968Z
Learning: Applies to apps/web/app/api/**/*.{ts,js} : Review all new withError usage in API routes to ensure custom authentication is implemented where required.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-07-18T17:27:46.389Z
Learning: Applies to /api//route.ts : ALL API routes that handle user data MUST use appropriate authentication and authorization middleware (withAuth or withEmailAccount).
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-07-18T17:27:46.389Z
Learning: Applies to /api//route.ts : Prevent mass assignment vulnerabilities by only allowing explicitly whitelisted fields in update operations in API routes.
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/app/api/**/route.ts : Use withEmailAccount
for email-account-level operations
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-07-18T15:05:26.713Z
Learning: Applies to app/api/**/route.ts : Use Prisma for database queries in GET API routes.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-07-18T17:27:46.389Z
Learning: Applies to /api//route.ts : Use withEmailAccount
middleware for API routes that operate on a specific email account (i.e., use or require emailAccountId
).
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-07-18T15:05:26.713Z
Learning: Applies to app/api/**/route.ts : Always wrap the handler with withAuth
or withEmailAccount
for consistent error handling and authentication in GET API routes.
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/app/api/**/route.ts : Export response types from GET routes
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.146Z
Learning: Applies to apps/web/utils/actions/*.validation.ts : Define Zod schemas for validation in dedicated files and use them for both client and server validation.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-07-20T09:00:41.968Z
Learning: Applies to apps/web/app/api/**/*.{ts,js} : Request bodies in API routes should use Zod schemas for validation.
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/utils/actions/**/*.ts : Use Zod schemas for validation on both client and server
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-07-18T17:27:58.249Z
Learning: Applies to apps/web/utils/actions/*.validation.ts : Define input validation schemas using Zod in the corresponding .validation.ts
file. These schemas are used by next-safe-action
(.schema()
) and can also be reused on the client for form validation.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Always define a Zod schema for response validation in LLM-related functions.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/form-handling.mdc:0-0
Timestamp: 2025-07-18T15:04:57.115Z
Learning: Applies to **/*.ts : Define validation schemas using Zod
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-07-18T17:27:46.389Z
Learning: Applies to /api//route.ts : Request bodies in API routes MUST be validated using Zod schemas before use.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Make Zod schemas as specific as possible to guide the LLM output.
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/components/**/*.tsx : Use React Hook Form with Zod validation for form handling
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/form-handling.mdc:0-0
Timestamp: 2025-07-18T15:04:57.115Z
Learning: Applies to **/*.tsx : Use React Hook Form with Zod for validation
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/index.mdc:0-0
Timestamp: 2025-07-18T15:05:56.644Z
Learning: Guidelines for implementing GET API routes in Next.js
apps/web/app/api/v1/stats/validation.ts (11)
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.146Z
Learning: Applies to apps/web/utils/actions/*.validation.ts : Define Zod schemas for validation in dedicated files and use them for both client and server validation.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-07-18T17:27:58.249Z
Learning: Applies to apps/web/utils/actions/*.validation.ts : Define input validation schemas using Zod in the corresponding .validation.ts
file. These schemas are used by next-safe-action
(.schema()
) and can also be reused on the client for form validation.
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/utils/actions/**/*.ts : Use Zod schemas for validation on both client and server
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Always define a Zod schema for response validation in LLM-related functions.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-07-20T09:00:41.968Z
Learning: Applies to apps/web/app/api/**/*.{ts,js} : Request bodies in API routes should use Zod schemas for validation.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/form-handling.mdc:0-0
Timestamp: 2025-07-18T15:04:57.115Z
Learning: Applies to **/*.ts : Define validation schemas using Zod
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Make Zod schemas as specific as possible to guide the LLM output.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-07-18T17:27:46.389Z
Learning: Applies to /api//route.ts : Request bodies in API routes MUST be validated using Zod schemas before use.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Follow the standard structure for LLM-related functions: use a scoped logger, define a Zod schema for output, validate inputs early, separate system and user prompts, log inputs and outputs, call chatCompletionObject with proper configuration, and return validated results.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Format data consistently across similar LLM-related functions.
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 : Export response types from GET API routes for type-safe client usage.
apps/web/app/api/v1/stats/email-actions/route.ts (16)
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-07-18T15:05:26.713Z
Learning: Applies to app/api/**/route.ts : Always wrap the handler with withAuth
or withEmailAccount
for consistent error handling and authentication in GET API routes.
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/app/api/**/route.ts : Use withEmailAccount
for email-account-level operations
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.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-07-18T17:27:46.389Z
Learning: Applies to /api//route.ts : Use withEmailAccount
middleware for API routes that operate on a specific email account (i.e., use or require emailAccountId
).
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-07-18T15:05:26.713Z
Learning: Applies to app/api/**/route.ts : Do not use try/catch in GET API route handlers, as withAuth
and withEmailAccount
handle error catching.
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 : Export response types from GET API routes for type-safe client usage.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-07-18T17:27:46.389Z
Learning: Applies to /api//route.ts : ALL API routes that handle user data MUST use appropriate authentication and authorization middleware (withAuth or withEmailAccount).
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 : Do not use try/catch in GET API routes when using authentication middleware; rely on centralized error handling.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-07-18T17:27:46.389Z
Learning: Applies to /api//route.ts : Use withError
middleware (with proper validation) for public endpoints, custom authentication, or cron endpoints.
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/app/api/**/route.ts : Export response types from GET routes
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/index.mdc:0-0
Timestamp: 2025-07-18T15:05:56.644Z
Learning: Guidelines for implementing GET API routes in Next.js
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/index.mdc:0-0
Timestamp: 2025-07-18T15:05:56.644Z
Learning: Guidelines for implementing Next.js server actions
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-07-19T17:50:28.270Z
Learning: The utils
folder also contains core app logic such as Next.js Server Actions and Gmail API requests.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-07-18T15:05:26.713Z
Learning: Applies to app/api/**/route.ts : Return responses using NextResponse.json()
in GET API routes.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-07-18T15:04:44.818Z
Learning: Applies to {app,components}/**/*.{ts,tsx} : For API GET requests to server, use the swr
package
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-07-18T17:27:58.249Z
Learning: Applies to apps/web/utils/actions/*.ts : Server Actions are strictly for mutations (operations that change data, e.g., creating, updating, deleting). Do NOT use Server Actions for data fetching (GET operations). For data fetching, use dedicated GET API Routes combined with SWR Hooks.
🧬 Code Graph Analysis (1)
apps/web/app/api/v1/openapi/route.ts (1)
apps/web/app/api/v1/stats/validation.ts (8)
summaryStatsQuerySchema
(16-16)summaryStatsResponseSchema
(19-24)statsByPeriodQuerySchema
(27-32)statsByPeriodResponseSchema
(44-47)newsletterStatsQuerySchema
(50-60)newsletterStatsResponseSchema
(72-75)dateRangeSchema
(4-13)emailActionsResponseSchema
(84-86)
⏰ 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: claude-review
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (6)
apps/web/app/api/v1/stats/validation.ts (1)
1-95
: Well-structured validation schemas following best practices!The validation module is well-organized with:
- Reusable
dateRangeSchema
for common parameters- Comprehensive schemas for all stats endpoints
- Proper TypeScript type exports
- Good OpenAPI descriptions
apps/web/app/api/v1/stats/email-actions/route.ts (1)
1-76
: Secure implementation with proper authentication and data scoping!The route correctly implements:
- API key authentication via
validateApiKeyAndGetGmailClient
- Query parameter validation using Zod schemas
- Data scoping to authenticated user's email account
- Proper error handling without exposing sensitive information
- Comprehensive logging for debugging
apps/web/app/api/v1/stats/summary/route.ts (1)
14-87
: Well-implemented stats endpoint with proper security!The route correctly implements:
- API key authentication
- Data scoping to authenticated user
- Efficient Prisma aggregation query
- Logical calculation of archived emails
- Comprehensive error handling
apps/web/app/api/v1/openapi/route.ts (1)
16-25
: Comprehensive OpenAPI documentation for new stats endpoints!The OpenAPI registrations are well-structured with:
- Clear endpoint descriptions
- Proper request/response schema references
- Consistent API key authentication
- Appropriate HTTP status codes
Also applies to: 85-165
apps/web/app/api/v1/stats/by-period/route.ts (1)
43-128
: Excellent implementation of period-based statistics!The route demonstrates:
- Secure SQL query construction with Prisma.sql
- Efficient date-based grouping using PostgreSQL functions
- Proper bigint handling for large numbers
- Comprehensive summary calculation
- Good error handling and logging
apps/web/app/api/v1/stats/newsletters/route.ts (1)
86-86
: Confirm the correct source forhasUnsubscribeLink
The schema’s
status: NewsletterStatus
enum (APPROVED | UNSUBSCRIBED | AUTO_ARCHIVED
) reflects how a user handled the newsletter, not whether it contains an unsubscribe link. Separately, the Prisma schema declares anunsubscribeLink String?
field—presumably the actual link if present.Please verify and update accordingly:
- Does the
Newsletter
model includeunsubscribeLink
? If so, add it to your query:and change the mapping to:select: { …, status: true, unsubscribeLink: true, },hasUnsubscribeLink: Boolean(newsletter.unsubscribeLink),- If you intended
status === "UNSUBSCRIBED"
to flag link presence, document that assumption with a clear comment.- Otherwise, consider adding or computing a dedicated field for link detection.
|
||
const logger = createScopedLogger("api/v1/stats/newsletters"); | ||
|
||
export const GET = withError(async (request) => { |
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.
Use withEmailAccount
middleware instead of withError
for email-account-level operations.
This API route operates on email account data and should use withEmailAccount
middleware for proper authentication and authorization. The withError
middleware is intended for public endpoints, custom authentication, or cron endpoints.
-export const GET = withError(async (request) => {
+export const GET = withEmailAccount(async (request, { userId, emailAccountId }) => {
You'll also need to import withEmailAccount
:
-import { withError } from "@/utils/middleware";
+import { withEmailAccount } from "@/utils/middleware";
And remove the manual authentication and email account retrieval since the middleware provides these:
- const { userId, accountId, accessToken } = await validateApiKeyAndGetGmailClient(request);
-
- // ... query validation code ...
-
- const emailAccountId = await getEmailAccountId({
- accountId,
- userId,
- });
-
- if (!emailAccountId) {
- return NextResponse.json(
- { error: "Email account not found" },
- { status: 400 },
- );
- }
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In apps/web/app/api/v1/stats/newsletters/route.ts at line 14, replace the use of
withError middleware with withEmailAccount middleware to ensure proper
authentication and authorization for email-account-level operations. Import
withEmailAccount at the top of the file, remove any manual authentication and
email account retrieval code inside the GET handler, and wrap the async function
with withEmailAccount instead of withError.
try { | ||
const { fromDate, toDate, limit = "50", orderBy = "count" } = queryResult.data; | ||
|
||
// Build date conditions | ||
const dateConditions: any = {}; | ||
if (fromDate) { | ||
dateConditions.createdAt = { ...dateConditions.createdAt, gte: new Date(fromDate) }; | ||
} | ||
if (toDate) { | ||
dateConditions.createdAt = { ...dateConditions.createdAt, lte: new Date(toDate) }; | ||
} | ||
|
||
// Determine order by clause | ||
const orderByClause = orderBy === "lastReceived" | ||
? { createdAt: "desc" as const } | ||
: orderBy === "firstReceived" | ||
? { createdAt: "asc" as const } | ||
: { count: "desc" as const }; | ||
|
||
// Get newsletter stats from database | ||
const newsletters = await prisma.newsletter.findMany({ | ||
where: { | ||
email: emailAccountId, | ||
...dateConditions, | ||
}, | ||
select: { | ||
email: true, | ||
name: true, | ||
count: true, | ||
createdAt: true, | ||
updatedAt: true, | ||
readPercentage: true, | ||
status: true, | ||
}, | ||
orderBy: orderByClause, | ||
take: parseInt(limit, 10), | ||
}); | ||
|
||
const response: NewsletterStatsResponse = { | ||
newsletters: newsletters.map((newsletter) => ({ | ||
name: newsletter.name || "Unknown", | ||
from: newsletter.email, | ||
count: newsletter.count, | ||
lastReceived: newsletter.updatedAt.toISOString(), | ||
readPercentage: newsletter.readPercentage || 0, | ||
hasUnsubscribeLink: newsletter.status === "UNSUBSCRIBED", | ||
})), | ||
total: newsletters.length, | ||
}; | ||
|
||
logger.info("Retrieved newsletter stats", { | ||
userId, | ||
emailAccountId, | ||
count: response.total, | ||
}); | ||
|
||
return NextResponse.json(response); | ||
} catch (error) { | ||
logger.error("Error retrieving newsletter stats", { | ||
userId, | ||
error, | ||
}); | ||
return NextResponse.json( | ||
{ error: "Failed to retrieve newsletter stats" }, | ||
{ status: 500 }, | ||
); | ||
} |
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.
Remove try/catch block when using authentication middleware.
The coding guidelines specify not to use try/catch in GET API routes when using authentication middleware, as the middleware handles centralized error handling.
- try {
const { fromDate, toDate, limit = "50", orderBy = "count" } = queryResult.data;
// ... rest of the logic ...
return NextResponse.json(response);
- } catch (error) {
- logger.error("Error retrieving newsletter stats", {
- userId,
- error,
- });
- return NextResponse.json(
- { error: "Failed to retrieve newsletter stats" },
- { status: 500 },
- );
- }
📝 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.
try { | |
const { fromDate, toDate, limit = "50", orderBy = "count" } = queryResult.data; | |
// Build date conditions | |
const dateConditions: any = {}; | |
if (fromDate) { | |
dateConditions.createdAt = { ...dateConditions.createdAt, gte: new Date(fromDate) }; | |
} | |
if (toDate) { | |
dateConditions.createdAt = { ...dateConditions.createdAt, lte: new Date(toDate) }; | |
} | |
// Determine order by clause | |
const orderByClause = orderBy === "lastReceived" | |
? { createdAt: "desc" as const } | |
: orderBy === "firstReceived" | |
? { createdAt: "asc" as const } | |
: { count: "desc" as const }; | |
// Get newsletter stats from database | |
const newsletters = await prisma.newsletter.findMany({ | |
where: { | |
email: emailAccountId, | |
...dateConditions, | |
}, | |
select: { | |
email: true, | |
name: true, | |
count: true, | |
createdAt: true, | |
updatedAt: true, | |
readPercentage: true, | |
status: true, | |
}, | |
orderBy: orderByClause, | |
take: parseInt(limit, 10), | |
}); | |
const response: NewsletterStatsResponse = { | |
newsletters: newsletters.map((newsletter) => ({ | |
name: newsletter.name || "Unknown", | |
from: newsletter.email, | |
count: newsletter.count, | |
lastReceived: newsletter.updatedAt.toISOString(), | |
readPercentage: newsletter.readPercentage || 0, | |
hasUnsubscribeLink: newsletter.status === "UNSUBSCRIBED", | |
})), | |
total: newsletters.length, | |
}; | |
logger.info("Retrieved newsletter stats", { | |
userId, | |
emailAccountId, | |
count: response.total, | |
}); | |
return NextResponse.json(response); | |
} catch (error) { | |
logger.error("Error retrieving newsletter stats", { | |
userId, | |
error, | |
}); | |
return NextResponse.json( | |
{ error: "Failed to retrieve newsletter stats" }, | |
{ status: 500 }, | |
); | |
} | |
const { fromDate, toDate, limit = "50", orderBy = "count" } = queryResult.data; | |
// Build date conditions | |
const dateConditions: any = {}; | |
if (fromDate) { | |
dateConditions.createdAt = { ...dateConditions.createdAt, gte: new Date(fromDate) }; | |
} | |
if (toDate) { | |
dateConditions.createdAt = { ...dateConditions.createdAt, lte: new Date(toDate) }; | |
} | |
// Determine order by clause | |
const orderByClause = orderBy === "lastReceived" | |
? { createdAt: "desc" as const } | |
: orderBy === "firstReceived" | |
? { createdAt: "asc" as const } | |
: { count: "desc" as const }; | |
// Get newsletter stats from database | |
const newsletters = await prisma.newsletter.findMany({ | |
where: { | |
email: emailAccountId, | |
...dateConditions, | |
}, | |
select: { | |
email: true, | |
name: true, | |
count: true, | |
createdAt: true, | |
updatedAt: true, | |
readPercentage: true, | |
status: true, | |
}, | |
orderBy: orderByClause, | |
take: parseInt(limit, 10), | |
}); | |
const response: NewsletterStatsResponse = { | |
newsletters: newsletters.map((newsletter) => ({ | |
name: newsletter.name || "Unknown", | |
from: newsletter.email, | |
count: newsletter.count, | |
lastReceived: newsletter.updatedAt.toISOString(), | |
readPercentage: newsletter.readPercentage || 0, | |
hasUnsubscribeLink: newsletter.status === "UNSUBSCRIBED", | |
})), | |
total: newsletters.length, | |
}; | |
logger.info("Retrieved newsletter stats", { | |
userId, | |
emailAccountId, | |
count: response.total, | |
}); | |
return NextResponse.json(response); |
🤖 Prompt for AI Agents
In apps/web/app/api/v1/stats/newsletters/route.ts between lines 41 and 107,
remove the try/catch block wrapping the main logic of the GET API route because
the authentication middleware already provides centralized error handling.
Simply keep the code inside the try block as the main function body and let any
errors propagate naturally to be handled by the middleware.
// Get newsletter stats from database | ||
const newsletters = await prisma.newsletter.findMany({ | ||
where: { | ||
email: 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.
💡 Verification agent
🧩 Analysis chain
Verify database query field name for email account filtering.
The query uses email: emailAccountId
for filtering, but this seems incorrect. Typically, email account filtering would use a field like emailAccountId
rather than email
.
🏁 Script executed:
#!/bin/bash
# Verify the Newsletter model schema to confirm the correct field name for email account filtering
ast-grep --pattern 'model Newsletter {
$$$
}'
Length of output: 49
To locate the Newsletter
model and confirm the correct field name for filtering, let’s search your Prisma schema for it:
This will show the model and its fields so we can verify whether it uses email
, emailAccountId
, or another field for filtering.
🏁 Script executed:
#!/bin/bash
# Search for the Newsletter model definition in all .prisma files
rg -n "model Newsletter" -C3 --glob '*.prisma'
Length of output: 632
Let’s pull the full Newsletter
model definition to confirm its fields:
🏁 Script executed:
#!/bin/bash
# Extract the Newsletter model block from the Prisma schema
sed -n '/model Newsletter {/,/}/p' apps/web/prisma/schema.prisma
Length of output: 679
Use the correct field for email‐account filtering
The Newsletter
model defines both email
and emailAccountId
fields, and the latter is used in relations and indexing for filtering. On line 63 of
apps/web/app/api/v1/stats/newsletters/route.ts
, you should replace:
- email: emailAccountId,
+ emailAccountId: emailAccountId,
This ensures you’re filtering by the account’s ID (matching the @@index([emailAccountId, …])
and relation) rather than comparing the wrong column.
📝 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.
email: emailAccountId, | |
emailAccountId: emailAccountId, |
🤖 Prompt for AI Agents
In apps/web/app/api/v1/stats/newsletters/route.ts at line 63, replace the use of
the `email` field with `emailAccountId` for filtering. This aligns the filter
with the correct database column used in relations and indexing, ensuring the
query filters by the account's ID rather than the incorrect email field.
name: newsletter.name || "Unknown", | ||
from: newsletter.email, | ||
count: newsletter.count, | ||
lastReceived: newsletter.updatedAt.toISOString(), |
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.
🛠️ Refactor suggestion
Use createdAt
for first received date instead of updatedAt
for last received.
The response maps newsletter.updatedAt
to lastReceived
, but based on the orderBy
logic (lines 54-58), createdAt
represents the received date. Using updatedAt
for last received might not reflect the actual last time a newsletter was received.
- lastReceived: newsletter.updatedAt.toISOString(),
+ lastReceived: newsletter.createdAt.toISOString(),
📝 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.
lastReceived: newsletter.updatedAt.toISOString(), | |
lastReceived: newsletter.createdAt.toISOString(), |
🤖 Prompt for AI Agents
In apps/web/app/api/v1/stats/newsletters/route.ts at line 84, replace
newsletter.updatedAt with newsletter.createdAt for the lastReceived field to
correctly reflect the first received date as per the orderBy logic. This ensures
the date corresponds to when the newsletter was actually received, not when it
was last updated.
{ status: 500 }, | ||
); | ||
} | ||
}); No newline at end of file |
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.
Export response type for type-safe client usage.
The coding guidelines require exporting response types from GET API routes for type-safe client usage.
Add this export at the end of the file:
});
+
+export type GetNewsletterStatsResponse = NewsletterStatsResponse;
📝 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.
}); | |
}); | |
export type GetNewsletterStatsResponse = NewsletterStatsResponse; |
🤖 Prompt for AI Agents
In apps/web/app/api/v1/stats/newsletters/route.ts at line 108, the GET API route
response type is not exported, which is required for type-safe client usage.
Define the response type for the GET handler and export it at the end of the
file to comply with coding guidelines and enable type safety in clients
consuming this API.
const dateConditions: any = { emailAccountId }; | ||
if (fromDate) { | ||
dateConditions.date = { ...dateConditions.date, gte: new Date(fromDate) }; | ||
} | ||
if (toDate) { | ||
dateConditions.date = { ...dateConditions.date, lte: new Date(toDate) }; | ||
} |
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.
Replace any
type with proper Prisma type.
Avoid using any
type. Use Prisma's generated types for type safety.
- const dateConditions: any = { emailAccountId };
+ const dateConditions: Prisma.EmailStatWhereInput = { emailAccountId };
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In apps/web/app/api/v1/stats/summary/route.ts around lines 45 to 51, replace the
use of the `any` type for the `dateConditions` variable with the appropriate
Prisma-generated type that matches the expected filter object for the query.
Import the correct Prisma type from the Prisma client and use it to type
`dateConditions` to ensure type safety and proper IntelliSense support.
export const dateRangeSchema = z.object({ | ||
fromDate: z | ||
.string() | ||
.optional() | ||
.describe("Start date in ISO format (e.g., 2024-01-01)"), | ||
toDate: z | ||
.string() | ||
.optional() | ||
.describe("End date in ISO format (e.g., 2024-12-31)"), | ||
}); |
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.
🛠️ Refactor suggestion
Add date format validation to ensure ISO format.
The schema currently accepts any string for dates. Add validation to ensure the dates are in valid ISO format as mentioned in the description.
export const dateRangeSchema = z.object({
fromDate: z
.string()
+ .regex(/^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d{3})?Z?)?$/, "Invalid ISO date format")
.optional()
.describe("Start date in ISO format (e.g., 2024-01-01)"),
toDate: z
.string()
+ .regex(/^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d{3})?Z?)?$/, "Invalid ISO date format")
.optional()
.describe("End date in ISO format (e.g., 2024-12-31)"),
});
📝 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.
export const dateRangeSchema = z.object({ | |
fromDate: z | |
.string() | |
.optional() | |
.describe("Start date in ISO format (e.g., 2024-01-01)"), | |
toDate: z | |
.string() | |
.optional() | |
.describe("End date in ISO format (e.g., 2024-12-31)"), | |
}); | |
export const dateRangeSchema = z.object({ | |
fromDate: z | |
.string() | |
.regex(/^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d{3})?Z?)?$/, "Invalid ISO date format") | |
.optional() | |
.describe("Start date in ISO format (e.g., 2024-01-01)"), | |
toDate: z | |
.string() | |
.regex(/^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d{3})?Z?)?$/, "Invalid ISO date format") | |
.optional() | |
.describe("End date in ISO format (e.g., 2024-12-31)"), | |
}); |
🤖 Prompt for AI Agents
In apps/web/app/api/v1/stats/validation.ts lines 4 to 13, the dateRangeSchema
allows any string for fromDate and toDate without validating the ISO date
format. Update the schema to include a refinement or regex check that validates
the strings conform to the ISO date format (YYYY-MM-DD) as described. This will
ensure only valid ISO date strings are accepted.
limit: z | ||
.string() | ||
.optional() | ||
.describe("Maximum number of results to return"), |
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.
Fix the limit parameter type to properly handle numeric values.
The limit
parameter should be numeric, not a string. Use Zod's coercion to handle string inputs from query parameters.
limit: z
- .string()
+ .coerce.number().positive().int()
.optional()
.describe("Maximum number of results to return"),
📝 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.
limit: z | |
.string() | |
.optional() | |
.describe("Maximum number of results to return"), | |
limit: z | |
.coerce.number().positive().int() | |
.optional() | |
.describe("Maximum number of results to return"), |
🤖 Prompt for AI Agents
In apps/web/app/api/v1/stats/validation.ts around lines 51 to 54, the limit
parameter is currently defined as an optional string, but it should be a numeric
value. Update the schema to use Zod's coercion method to convert string inputs
to numbers, ensuring the limit parameter correctly handles numeric values from
query parameters.
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.
apps/web/app/api/v1/openapi/route.ts
Summary by CodeRabbit
New Features
Bug Fixes