Skip to content

Conversation

@AshishViradiya153
Copy link
Contributor

@AshishViradiya153 AshishViradiya153 commented Jun 24, 2025

Summary by CodeRabbit

  • New Features

    • Added pagination and sorting controls to the Visitors and Visitor Detail pages, allowing users to navigate and sort lists of viewers and document views.
    • Introduced loading indicators for document durations in visitor detail tables.
  • Improvements

    • Enhanced tables to support external control of pagination, sorting, and page size.
    • Updated table columns and UI for clearer display of visit statistics and durations.
    • Improved search and sorting performance for large datasets.
    • Refined API endpoints to support advanced pagination, sorting, and duration data fetching with caching.
    • Enhanced hooks to handle pagination, sorting, and duration loading states.
  • Performance

    • Optimized database queries and added new indexes to speed up filtering, sorting, and searching of viewers and views.

@AshishViradiya153 AshishViradiya153 requested a review from mfts as a code owner June 24, 2025 02:13
@vercel
Copy link

vercel bot commented Jun 24, 2025

@AshishViradiya153 is attempting to deploy a commit to the mftsio Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jun 24, 2025

Walkthrough

This set of changes introduces comprehensive support for server-side pagination and sorting to the viewers and document views features. The React components for listing viewers and their document views are refactored to be fully controlled via external props and callbacks, delegating pagination and sorting to parent components. The associated SWR hooks and API endpoints are updated to accept and process pagination and sorting parameters, returning structured metadata alongside data. Database performance is improved through new indexes and optimized SQL queries, and caching is enhanced at both the API and Redis levels.

Changes

File(s) / Area Change Summary
components/visitors/contacts-table.tsx, components/visitors/contacts-document-table.tsx Refactored tables to be fully controlled components with external pagination and sorting; updated types and handlers; replaced pagination UI; adjusted columns and loading handling; removed internal sorting and pagination state.
lib/swr/use-viewers.ts, lib/swr/use-viewer.ts Updated hooks to accept pagination and sorting parameters; changed return types to include pagination, sorting, and durations; added separate loading and error states for durations; improved caching and fetch logic.
pages/visitors/index.tsx, pages/visitors/[id]/index.tsx Enhanced pages to manage and pass pagination/sorting state; connected to new table and hook APIs; added handlers for interactive control of sorting and pagination; reset page on search or sort changes.
pages/api/teams/[teamId]/viewers/index.ts, pages/api/teams/[teamId]/viewers/[id]/index.ts Refactored API endpoints to support pagination, multi-field sorting, and optimized raw SQL queries with CTEs; added response metadata and advanced caching logic including Redis caching for durations; introduced batch fetching and sorting by total duration.
prisma/schema/schema.prisma, prisma/migrations/.../migration.sql Added indexes to the View and Viewer tables for query performance; included a trigram index for email search and composite indexes for sorting/filtering; added descending indexes and partial indexes for optimized queries.

Sequence Diagram(s)

Viewer List Pagination & Sorting (Visitors Page)

sequenceDiagram
    participant User
    participant VisitorsPage
    participant ContactsTable
    participant useViewers
    participant API
    participant DB

    User->>ContactsTable: Interacts (sorts, paginates)
    ContactsTable->>VisitorsPage: Calls onSortChange/onPageChange
    VisitorsPage->>useViewers: Updates state, calls hook with new params
    useViewers->>API: Fetch /api/teams/{teamId}/viewers with pagination/sorting
    API->>DB: Query viewers with pagination/sorting
    DB-->>API: Return viewers, stats, counts
    API-->>useViewers: Return viewers, pagination, sorting
    useViewers-->>VisitorsPage: Return data
    VisitorsPage-->>ContactsTable: Pass new data/metadata
Loading

Viewer Detail Document Views Pagination & Sorting

sequenceDiagram
    participant User
    participant VisitorDetailPage
    participant ContactsDocumentsTable
    participant useViewer
    participant API
    participant DB
    participant Redis

    User->>ContactsDocumentsTable: Interacts (sorts, paginates)
    ContactsDocumentsTable->>VisitorDetailPage: Calls onSortChange/onPageChange
    VisitorDetailPage->>useViewer: Updates state, calls hook with new params
    useViewer->>API: Fetch /api/teams/{teamId}/viewers/{id} with pagination/sorting
    API->>DB: Query views grouped by document, aggregate stats
    API->>Redis: Check/set cache for durations
    Redis-->>API: Return cached durations (if any)
    API-->>useViewer: Return viewer, views, durations, pagination, sorting
    useViewer-->>VisitorDetailPage: Return data
    VisitorDetailPage-->>ContactsDocumentsTable: Pass new data/metadata
Loading

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

npm error Exit handler never called!
npm error This is an error with npm itself. Please report this error at:
npm error https://github.com/npm/cli/issues
npm error A complete log of this run can be found in: /.npm/_logs/2025-07-03T06_10_47_899Z-debug-0.log

✨ Finishing Touches
  • 📝 Generate Docstrings

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need 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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (4)
prisma/migrations/20250624020507_indexed_view_modal/migration.sql (1)

1-2: Typo in migration filename: "modal" should be "model".

The migration filename contains "indexed_view_modal" but should be "indexed_view_model". While this doesn't affect functionality, it's important to maintain consistent naming conventions for clarity.

pages/api/teams/[teamId]/viewers/[id]/index.ts (1)

78-79: Consider extracting cache key generation into a helper function.

Cache key construction is repeated and could be centralized for consistency.

function generateCacheKey(prefix: string, params: {
  teamId: string;
  viewerId: string;
  page?: number;
  pageSize?: number;
  sortBy?: string;
  sortOrder?: string;
}): string {
  const parts = [prefix, params.teamId, params.viewerId];
  if (params.page !== undefined) parts.push(params.page.toString());
  if (params.pageSize !== undefined) parts.push(params.pageSize.toString());
  if (params.sortBy) parts.push(params.sortBy);
  if (params.sortOrder) parts.push(params.sortOrder);
  return parts.join(':');
}

Also applies to: 93-93, 183-183, 287-287

components/visitors/contacts-document-table.tsx (1)

212-213: Consider using optional chaining for consistency.

For consistency with other parts of the codebase, consider using optional chaining.

-  const duration = durations[view.documentId] ?? view.totalDuration;
+  const duration = durations?.[view.documentId] ?? view.totalDuration;
components/visitors/contacts-table.tsx (1)

84-110: Simplify the sorting logic for better readability.

The current sorting logic is complex and could be simplified to match the pattern in ContactsDocumentsTable.

  const handleSort = useCallback(
    (columnId: string) => {
      if (!onSortChange) return;

      const currentSortBy = sorting?.sortBy;
      const currentSortOrder = sorting?.sortOrder;

-      if (currentSortBy === columnId) {
-        if (columnId === "lastViewed") {
-          if (currentSortOrder === "asc") {
-            onSortChange("lastViewed", "desc");
-          } else {
-            onSortChange("lastViewed", "asc");
-          }
-        } else {
-          if (currentSortOrder === "asc") {
-            onSortChange(columnId, "desc");
-          } else if (currentSortOrder === "desc") {
-            onSortChange("lastViewed", "desc");
-          }
-        }
-      } else {
-        onSortChange(columnId, "asc");
-      }
+      let newSortOrder = "desc";
+      if (currentSortBy === columnId) {
+        if (currentSortOrder === "desc") {
+          newSortOrder = "asc";
+        } else if (currentSortOrder === "asc") {
+          // Reset to default sort
+          onSortChange("lastViewed", "desc");
+          return;
+        }
+      }
+
+      onSortChange(columnId, newSortOrder);
    },
    [onSortChange, sorting?.sortBy, sorting?.sortOrder],
  );
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c08ddcb and 6ed43cc.

📒 Files selected for processing (10)
  • components/visitors/contacts-document-table.tsx (4 hunks)
  • components/visitors/contacts-table.tsx (3 hunks)
  • lib/swr/use-viewer.ts (1 hunks)
  • lib/swr/use-viewers.ts (2 hunks)
  • pages/api/teams/[teamId]/viewers/[id]/index.ts (3 hunks)
  • pages/api/teams/[teamId]/viewers/index.ts (3 hunks)
  • pages/visitors/[id]/index.tsx (3 hunks)
  • pages/visitors/index.tsx (2 hunks)
  • prisma/migrations/20250624020507_indexed_view_modal/migration.sql (1 hunks)
  • prisma/schema/schema.prisma (1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
lib/swr/use-viewer.ts

[error] 65-65: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🔇 Additional comments (10)
prisma/migrations/20250624020507_indexed_view_modal/migration.sql (1)

1-2: Good addition of viewerId index for performance optimization.

This index will significantly improve query performance when filtering or joining on viewerId, which aligns with the refactored API queries that aggregate view statistics by viewer.

prisma/schema/schema.prisma (1)

299-299: Schema index addition is correct and consistent with migration.

The index on viewerId is properly defined and will optimize queries that filter or aggregate views by viewer.

pages/visitors/index.tsx (2)

18-22: Well-structured state management for pagination and sorting.

Good default values chosen - starting with page 1, 10 items per page, and sorting by lastViewed in descending order provides a sensible initial view.


34-43: Correct implementation of state change handlers.

Excellent practice to reset to page 1 when changing page size or sorting criteria. This prevents users from landing on non-existent pages.

pages/api/teams/[teamId]/viewers/index.ts (2)

36-40: Good input validation for sort parameters.

The validation against whitelisted values prevents invalid sort fields and orders from being used.


79-102: Excellent performance optimization with raw SQL query.

The refactored query efficiently aggregates view statistics in a single query with proper LEFT JOIN, avoiding N+1 query problems. The use of parameterized queries for teamId and search conditions is secure.

pages/visitors/[id]/index.tsx (1)

27-55: Consistent implementation of pagination and sorting.

The implementation mirrors the pattern used in the visitors list page, maintaining consistency across the application. The state management and handler logic are correctly implemented.

pages/api/teams/[teamId]/viewers/[id]/index.ts (1)

158-180: Verify SQL injection safety with Prisma parameters.

The raw SQL queries appear to be using parameterized queries correctly, which is good for SQL injection prevention. The use of ${id} and other parameters should be safe as Prisma handles the escaping.

Also applies to: 240-265

components/visitors/contacts-document-table.tsx (1)

92-112: Well-implemented sorting logic with proper state cycling.

The handleSort implementation correctly cycles through desc → asc → reset states, providing good UX.

components/visitors/contacts-table.tsx (1)

1-324: Overall implementation looks good!

The refactoring to controlled components with external pagination and sorting is well-executed. This pattern will improve performance by allowing parent components to manage state and reduce unnecessary re-renders.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (3)
lib/swr/use-viewer.ts (2)

52-57: URLSearchParams implementation addresses previous feedback.

This properly addresses the past review comment about using URLSearchParams for safer query string construction.


69-69: Optional chaining implementation needs correction.

The current implementation (viewer?.views?.length ?? 0) > 0 is unnecessarily complex. The past review comment suggested a simpler approach.

Apply this diff for cleaner optional chaining:

-  const shouldFetchDurations = (viewer?.views?.length ?? 0) > 0;
+  const shouldFetchDurations = viewer?.views?.length > 0;
pages/api/teams/[teamId]/viewers/[id]/index.ts (1)

13-60: Excellent helper function extraction that addresses previous feedback.

The fetchAndCacheDurations helper successfully eliminates code duplication as suggested in the past review. The implementation includes proper error handling, batch processing, and caching strategy.

🧹 Nitpick comments (1)
pages/api/teams/[teamId]/viewers/[id]/index.ts (1)

168-250: Optimize the totalDuration sorting approach.

The current implementation fetches all documents first, then sorts in memory. For large datasets, this could be inefficient.

Consider implementing database-level sorting for totalDuration when the dataset grows large:

// Alternative approach: Use a CTE or subquery to sort at database level
const documentsWithDurations = await prisma.$queryRaw`
  WITH document_durations AS (
    SELECT 
      v."documentId",
      -- Fetch durations via database function if possible
      get_document_duration(v."documentId", v."viewerId") as "totalDuration"
    FROM "View" v
    WHERE v."viewerId" = ${id}
    GROUP BY v."documentId"
  )
  SELECT * FROM document_durations
  ORDER BY "totalDuration" ${Prisma.raw(orderDirection)}
  LIMIT ${limit} OFFSET ${offset}
`;
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6ed43cc and 37f75ec.

📒 Files selected for processing (4)
  • lib/swr/use-viewer.ts (1 hunks)
  • lib/swr/use-viewers.ts (2 hunks)
  • pages/api/teams/[teamId]/viewers/[id]/index.ts (3 hunks)
  • pages/api/teams/[teamId]/viewers/index.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • pages/api/teams/[teamId]/viewers/index.ts
  • lib/swr/use-viewers.ts
🔇 Additional comments (8)
lib/swr/use-viewer.ts (4)

9-38: Well-structured type definition with comprehensive metadata.

The expanded ViewerWithViews type provides detailed document information and proper pagination/sorting metadata structure. This will improve type safety and developer experience.


40-45: Good default parameter choices for pagination and sorting.

The default values (page=1, pageSize=10, sortBy="lastViewed", sortOrder="desc") are sensible for a visitors table use case.


71-83: Effective separation of concerns with dedicated duration fetching.

The conditional second SWR hook for durations is well-implemented with appropriate caching settings and error handling. The withDuration=true parameter clearly separates the two concerns.


89-91: Robust loading state management.

The distinction between main loading and durations loading provides clear feedback to components about what data is being fetched.

pages/api/teams/[teamId]/viewers/[id]/index.ts (4)

73-92: Robust parameter validation and parsing.

The query parameter parsing includes proper validation with whitelisted sort fields and orders, preventing potential security issues and ensuring data integrity.


264-266: Smart caching strategy prevents cache conflicts.

The conditional caching logic (if (withDuration !== "true")) prevents caching conflicts between different request types, which is crucial for data consistency.


277-283: Graceful error handling with fallback values.

The error handling for duration processing provides meaningful fallbacks, ensuring the API remains functional even when duration data is unavailable.


224-249: ```shell
#!/bin/bash

Locate the declaration and assignments of orderClause to assess input validation

rg -n "orderClause" pages/api/teams/[teamId]/viewers/[id]/index.ts


</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
lib/swr/use-viewers.ts (1)

47-57: URLSearchParams implementation addresses previous review feedback effectively.

The implementation resolves the past review concern about manual string concatenation. The approach using append() method is consistent and includes proper type checking for the search query with typeof searchQuery === 'string'.

🧹 Nitpick comments (2)
pages/api/teams/[teamId]/viewers/[id]/index.ts (2)

153-231: Consider performance implications of in-memory sorting for totalDuration.

The current approach fetches all documents when sorting by totalDuration and then applies pagination in-memory. This could become a performance bottleneck for viewers with many documents.

Consider implementing database-level duration aggregation or caching sorted results to avoid loading all records into memory.

The conditional logic for different sorting scenarios is also quite complex. Consider extracting query-building logic into separate functions:

+async function buildViewsQuery(
+  viewerId: string, 
+  sort: string, 
+  order: string, 
+  limit: number, 
+  offset: number
+) {
+  // Query building logic here
+}

+async function getViewsWithDurationSort(viewerId: string, order: string) {
+  // Duration sorting logic here
+}

61-309: Consider breaking down the main handler for better maintainability.

The main handler function has grown quite complex with multiple responsibilities. Consider extracting logic into focused helper functions:

async function validateTeamAndViewer(teamId: string, viewerId: string, userId: string) {
  // Team and viewer validation logic
}

async function buildPaginatedViews(
  viewerId: string, 
  pagination: PaginationParams, 
  sorting: SortingParams
) {
  // Query building and execution logic
}

async function formatResponse(
  viewer: any, 
  views: any[], 
  pagination: any, 
  sorting: any
) {
  // Response formatting logic
}

This would improve code organization and make the main handler more readable and testable.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 37f75ec and 1503349.

📒 Files selected for processing (5)
  • lib/swr/use-viewers.ts (1 hunks)
  • pages/api/teams/[teamId]/viewers/[id]/index.ts (4 hunks)
  • pages/api/teams/[teamId]/viewers/index.ts (3 hunks)
  • prisma/migrations/20250624042156_viewers_performance_optimization/migration.sql (1 hunks)
  • prisma/schema/schema.prisma (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • prisma/migrations/20250624042156_viewers_performance_optimization/migration.sql
🚧 Files skipped from review as they are similar to previous changes (2)
  • prisma/schema/schema.prisma
  • pages/api/teams/[teamId]/viewers/index.ts
🔇 Additional comments (9)
lib/swr/use-viewers.ts (5)

9-16: Well-defined type structure for viewer statistics.

The ViewerWithStats type provides a clean interface for viewer data with aggregated statistics. The field selection is appropriate for the use case.


18-32: Comprehensive response structure supports full pagination and sorting workflow.

The ViewersResponse type is well-designed with:

  • Clear separation of data, pagination metadata, and sorting state
  • Complete pagination fields for UI implementation
  • Consistent naming conventions

34-39: Function signature provides sensible defaults for pagination and sorting.

The parameter defaults are well-chosen:

  • page: 1 and pageSize: 10 are standard pagination defaults
  • sortBy: "lastViewed" and sortOrder: "desc" provide logical initial sorting

64-79: SWR configuration optimized for pagination use case.

The enhanced configuration includes appropriate settings:

  • Disabled automatic revalidation to prevent unwanted data refreshes during pagination
  • Reasonable error retry settings (2 attempts, 5s intervals)
  • keepPreviousData: true maintains smooth UX during page transitions

81-90: Return object structure aligns with new API response format.

The destructured return provides direct access to:

  • viewers, pagination, sorting from the response
  • mutate function for manual cache updates
  • Consistent loading state logic
pages/api/teams/[teamId]/viewers/[id]/index.ts (4)

13-59: Good implementation of the duration fetching helper function.

This addresses the duplicate code concern from previous reviews. The batch processing and error handling are well-implemented.

Consider documenting why the batch size was increased to 10 instead of the previously suggested 5, as this could impact API rate limits or memory usage.


72-91: Well-structured parameter parsing with good validation.

The pagination and sorting parameter validation is comprehensive, with appropriate defaults and performance safeguards (100 item limit).


243-261: Excellent optimization for duration-only requests.

The withDuration parameter provides a clean way to fetch only duration data when needed, reducing response payload and processing time. The error handling gracefully falls back to empty durations.


126-127: Well-designed caching strategy with comprehensive cache keys.

The cache key construction includes all parameters that affect the response, and the 10-minute expiry provides a good balance between performance and data freshness. The conditional caching based on the withDuration parameter is a smart optimization.

Also applies to: 294-298

@vercel
Copy link

vercel bot commented Jul 2, 2025

Deployment failed with the following error:

The provided GitHub repository does not contain the requested branch or commit reference. Please ensure the repository is not empty.

Copy link
Owner

@mfts mfts left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change pg_trgm with B-tree index

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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)
pages/api/teams/[teamId]/viewers/index.ts (1)

32-34: Consider adding validation for edge cases in pagination parameters.

While the current validation is good, consider adding checks for negative values and zero to prevent potential issues:

-const currentPage = parseInt(page || "1", 10);
-const limit = Math.min(parseInt(pageSize || "10", 10), 100);
+const currentPage = Math.max(1, parseInt(page || "1", 10));
+const limit = Math.min(Math.max(1, parseInt(pageSize || "10", 10)), 100);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1503349 and 282d26b.

📒 Files selected for processing (2)
  • pages/api/teams/[teamId]/viewers/index.ts (3 hunks)
  • prisma/migrations/20250624042156_viewers_performance_optimization/migration.sql (1 hunks)
🔇 Additional comments (4)
prisma/migrations/20250624042156_viewers_performance_optimization/migration.sql (1)

1-16: LGTM! Well-designed indexes for query optimization.

The indexes are strategically placed to support the common query patterns in the viewers API:

  • Email prefix search with text_pattern_ops (addressing previous feedback about using standard b-tree indexes)
  • Team-scoped filtering and sorting
  • View aggregation queries

The partial index on line 15 is particularly well-optimized for filtering views with documents. All indexes properly use IF NOT EXISTS for safe reapplication.

pages/api/teams/[teamId]/viewers/index.ts (3)

64-77: Excellent security improvement! SQL injection vulnerability resolved.

The use of Prisma.sql template literals with validated inputs effectively addresses the previous SQL injection concerns. The whitelist approach for sort fields and orders provides robust protection against malicious input.


79-104: Well-optimized CTE query design aligned with database indexes.

The CTE structure efficiently aggregates view statistics and should perform well with the new indexes from the migration:

  • teamId filtering uses Viewer_teamId_* indexes
  • Email search leverages Viewer_email_prefix_idx
  • View aggregation benefits from View_viewerId_* indexes
  • Sorting utilizes the appropriate date and composite indexes

The query design demonstrates good understanding of database performance optimization.


150-150: Appropriate caching strategy for viewer data.

The 30-second cache with 5-minute stale-while-revalidate is well-balanced for viewer data that changes moderately frequently while maintaining good performance.

Copy link
Owner

@mfts mfts left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well done! I really like the use of the cache here.

I think there's lots of potential for more like it

@vercel
Copy link

vercel bot commented Jul 7, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
papermark ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jul 7, 2025 3:29pm

@mfts mfts merged commit 6264f40 into mfts:main Jul 7, 2025
4 checks passed
@github-actions github-actions bot locked and limited conversation to collaborators Jul 7, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants