Skip to content

Email accounts#410

Merged
elie222 merged 22 commits intomainfrom
multi-email-account
Apr 20, 2025
Merged

Email accounts#410
elie222 merged 22 commits intomainfrom
multi-email-account

Conversation

@elie222
Copy link
Owner

@elie222 elie222 commented Apr 20, 2025

Laying ground work for support of multiple accounts per user

Summary by CodeRabbit

  • New Features

    • Centralized and streamlined user identification by using email addresses instead of user IDs across all app features and settings.
    • Enhanced test helper utilities for consistent mock data generation.
  • Refactor

    • Migrated user profile, AI, and settings data from user records to email account records for improved data integrity and maintainability.
    • Updated all user-related operations, API endpoints, and database queries to use email-based identification.
    • Simplified and standardized function signatures and internal logic across the app.
  • Bug Fixes

    • Improved consistency and reliability in user-related operations by unifying identification methods.
  • Chores

    • Updated database schema and performed data migration to support the new email-centric data model.
    • Improved test code to utilize shared helpers and updated user object structures.
  • Documentation

    • Updated type definitions and documentation to reflect the new data model and usage patterns.

@vercel
Copy link

vercel bot commented Apr 20, 2025

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

Name Status Preview Updated (UTC)
inbox-zero ❌ Failed (Inspect) Apr 20, 2025 6:30pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Apr 20, 2025

Warning

Rate limit exceeded

@elie222 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 19 minutes and 7 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between f5bf22f and a23e4df.

📒 Files selected for processing (6)
  • apps/web/app/api/clean/route.ts (8 hunks)
  • apps/web/app/api/google/watch/all/route.ts (2 hunks)
  • apps/web/utils/actions/clean.ts (10 hunks)
  • apps/web/utils/hash.ts (1 hunks)
  • apps/web/utils/upstash/categorize-senders.ts (3 hunks)
  • apps/web/utils/upstash/index.ts (0 hunks)

Walkthrough

This update migrates user-related data and logic throughout the application from a user ID–centric model to an email account–centric model. The database schema and corresponding Prisma models are refactored to store user settings, AI configuration, and related fields directly on the EmailAccount entity, with email as the primary key. All backend API endpoints, server-side actions, and utility functions are updated to use email addresses for user identification instead of user IDs. Type definitions, function signatures, and Redis cache keys are revised accordingly. Test helpers and test cases are also updated to use the new user object structure.

Changes

File(s) / Path(s) Change Summary
prisma/schema.prisma, prisma/migrations/20250420131728_email_account_settings/migration.sql Migrated user settings and AI fields from User to EmailAccount, removed deprecated fields, changed primary key to email, added relations and indexes, and updated CleanupJob to link by email.
apps/web/app/api/*, apps/web/app/(app)/*, apps/web/utils/actions/*, apps/web/utils/ai/*, apps/web/utils/redis/*, apps/web/utils/user/* Refactored all user identification and queries from user ID to email; updated all function signatures, queries, and logic to operate on EmailAccount by email; adjusted logging and error handling for new structure.
apps/web/utils/llms/types.ts Updated UserAIFields and UserEmailWithAI types to select fields from EmailAccount instead of User.
apps/web/utils/redis/clean.types.ts, apps/web/utils/redis/reply.ts, apps/web/utils/redis/categorization-progress.ts Changed Redis keying and data structures from user ID to email; updated function signatures and internal logic.
apps/web/utils/rule/prompt-file.ts, apps/web/utils/rule/rule.ts, apps/web/utils/reply-tracker/enable.ts Refactored function signatures to use email and operate on EmailAccount; updated related helper and utility functions.
apps/web/utils/upstash/categorize-senders.ts Refactored queue naming and management from user ID to email; updated related function signatures and payloads.
apps/web/utils/auth.ts Customized PrismaAdapter to create EmailAccount records when linking accounts; handled errors and logging.
apps/web/hooks/useUser.ts, apps/web/utils/swr.ts Added SWR response normalization; updated hook to use new helper.
apps/web/components/PremiumAlert.tsx, apps/web/app/(app)/premium/Pricing.tsx, apps/web/app/(app)/settings/MultiAccountSection.tsx, apps/web/app/(app)/usage/usage.tsx Updated premium and user data access patterns to reflect new structure.
apps/web/__tests__/*, apps/web/__tests__/helpers.ts Centralized test user/email helpers; updated all tests to use new user structure and helper imports.
apps/web/utils/cold-email/is-cold-email.ts, apps/web/utils/categorize/senders/categorize.ts, etc. Updated function signatures and internal logic to use EmailAccount and email-based identification.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant API
    participant DB

    Client->>API: Request with user email (not userId)
    API->>DB: Query EmailAccount by email
    DB-->>API: Return EmailAccount with settings, AI config, etc.
    API-->>Client: Respond with data/actions using email-based identification
Loading
sequenceDiagram
    participant API
    participant Redis

    API->>Redis: Save or retrieve data keyed by email
    Redis-->>API: Return data (e.g., categorization progress, replies)
Loading

Possibly related PRs

  • Add posthog logs for assistant #327: Updates the user type in apps/web/app/api/google/webhook/types.ts from UserAIFields to UserEmailWithAI, reflecting a shared migration to the new user data model.

Poem

Oh, what a hop from ID to mail,
Now email’s the key that will always prevail!
Settings and AI, all in one place,
EmailAccount’s the star, setting the pace.
Redis and tests, all join the fun,
With helpers and hooks, the migration’s begun.
🐇💌 Here’s to new logic, all crisp and neat—
Email as the anchor, can’t be beat!


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.
    • Generate unit testing code for this file.
    • 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 generate unit testing code for this file.
    • @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 generate unit testing code.
    • @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.

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 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: 11

🔭 Outside diff range comments (4)
apps/web/utils/actions/reply-tracking.ts (1)

59-89: ⚠️ Potential issue

Inconsistent user identification in resolveThreadTrackerAction

While other functions in this file have been updated to use email-based identification, resolveThreadTrackerAction still uses userId. This could potentially cause issues with multi-account support.

Consider updating this function to use email-based identification for consistency with the rest of the codebase:

export const resolveThreadTrackerAction = withActionInstrumentation(
  "resolveThreadTracker",
  async (unsafeData: ResolveThreadTrackerBody) => {
    const session = await auth();
-   const userId = session?.user.id;
-   if (!userId) return { error: "Not logged in" };
+   const email = session?.user.email;
+   if (!email) return { error: "Not logged in" };
+   
+   const emailAccount = await prisma.emailAccount.findUnique({
+     where: { email },
+     select: { userId: true },
+   });
+   
+   if (!emailAccount) return { error: "Email account not found" };
+   const userId = emailAccount.userId;

    const { data, success, error } =
      resolveThreadTrackerSchema.safeParse(unsafeData);
    if (!success) return { error: error.message };

    await startAnalyzingReplyTracker(userId).catch((error) => {
      logger.error("Error starting Reply Zero analysis", { error });
    });

    await prisma.threadTracker.updateMany({
      where: {
        threadId: data.threadId,
        userId,
      },
      data: { resolved: data.resolved },
    });

    await stopAnalyzingReplyTracker(userId).catch((error) => {
      logger.error("Error stopping Reply Zero analysis", { error });
    });

    revalidatePath("/reply-zero");

    return { success: true };
  },
);
apps/web/utils/redis/clean.ts (2)

75-104: ⚠️ Potential issue

Missing refactoring: getThreadsByJobId still using userId

This function hasn't been updated to use email instead of userId for key patterns and parameters, which is inconsistent with the other changes in this file.

-export async function getThreadsByJobId(
-  userId: string,
-  jobId: string,
-  limit = 1000,
-) {
-  const pattern = `thread:${userId}:${jobId}:*`;
+export async function getThreadsByJobId(
+  email: string,
+  jobId: string,
+  limit = 1000,
+) {
+  const pattern = `thread:${email}:${jobId}:*`;

106-127: ⚠️ Potential issue

Missing refactoring: deleteAllUserData still using userId

This function hasn't been updated to use email instead of userId, which is inconsistent with the other changes in this file and could lead to data management issues.

-export async function deleteAllUserData(userId: string) {
-  // Delete all thread keys for this user
-  const threadPattern = `thread:${userId}:*`;
+export async function deleteAllUserData(email: string) {
+  // Delete all thread keys for this user
+  const threadPattern = `thread:${email}:*`;
apps/web/utils/cold-email/is-cold-email.ts (1)

95-101: ⚠️ Potential issue

Composite‑key misuse breaks the query – switch to findFirst or narrow the where clause.

prisma.coldEmail.findUnique can only receive the full unique/composite key.
Appending status inside where makes the call invalid and will throw at runtime:

-const coldEmail = await prisma.coldEmail.findUnique({
-  where: {
-    userId_fromEmail: { userId, fromEmail: from },
-    status: ColdEmailStatus.AI_LABELED_COLD,
-  },
-  select: { id: true },
-});
+const coldEmail = await prisma.coldEmail.findFirst({
+  where: {
+    userId,
+    fromEmail: from,
+    status: ColdEmailStatus.AI_LABELED_COLD,
+  },
+  select: { id: true },
+});

This keeps the semantics (unique hit expected) while complying with Prisma’s rules.
Alternatively, call findUnique without the status filter and check status afterwards.

♻️ Duplicate comments (1)
apps/web/utils/actions/clean.ts (1)

309-312: Repeat the email filter when querying cleanupThread.

See comment above; mirror the change here to keep the data scoped to the correct email account.

🧹 Nitpick comments (9)
apps/web/utils/actions/categorize.ts (1)

37-38: Return value of validateUserAndAiAccess is discarded
You call validateUserAndAiAccess only for its side‑effect of throwing an
error, then immediately drop the returned emailAccount.
If later logic needs the enriched object (e.g. AI provider/model), re‑use the
validated result to avoid an extra DB round‑trip elsewhere.

apps/web/app/api/resend/summary/route.ts (1)

241-244: lastSummaryEmailAt moved to emailAccount – great, but keep user field in sync
If the old User.lastSummaryEmailAt column still exists, consider
deprecating or synchronising it to avoid stale data powering legacy code
paths or dashboards.

apps/web/utils/cold-email/is-cold-email.ts (1)

186-198: Include email in error logs for better observability.

Only userId is logged when a label ID is missing. Adding the email (already available) helps triage multi‑account issues.

-logger.error("No gmail label id", { userId: user.userId });
+logger.error("No gmail label id", {
+  userId: user.userId,
+  email: user.email,
+});
apps/web/app/api/google/watch/all/route.ts (2)

110-118: Tokens partially validated

You verify access_token and refresh_token but not providerAccountId, which is mandatory for getGmailClientWithRefresh. A missing ID will lead to a runtime throw further down. Add the check here to fail fast.

- if (!emailAccount.account?.access_token || !emailAccount.account?.refresh_token)
+ if (
+   !emailAccount.account?.access_token ||
+   !emailAccount.account?.refresh_token ||
+   !emailAccount.account?.providerAccountId
+ )

134-135: Log key misnamed

The structured‑log property is userId but the value is an email address. Use email (or both) to avoid confusion in Kibana / Loki searches.

-logger.error("Error for user", { userId: emailAccount.email, error });
+logger.error("Error for user", { email: emailAccount.email, error });
apps/web/utils/upstash/categorize-senders.ts (1)

28-48: Parallelism hard‑coded to 3

For very large sender lists the queue may throttle. Consider making parallelism configurable via env or an optional parameter so ops can tune without redeploying.

apps/web/app/api/ai/analyze-sender-pattern/route.ts (1)

168-171: Consider updating function signature for consistency.

While the function implementation is correctly using userId from the emailAccount, consider updating the function signature to accept an email parameter instead of userId directly to maintain consistency with the refactoring approach.

async function savePatternCheck({
-  userId,
-  from,
-}: { userId: string; from: string }) {
+  email,
+  from,
+}: { email: string; from: string }) {

Then fetch the userId from the emailAccount internally.

apps/web/app/api/clean/gmail/route.ts (1)

45-47: Update error message for clarity.

The error message still refers to "User not found" when it's checking for an email account, which might be confusing.

-  if (!account) throw new SafeError("User not found", 404);
+  if (!account) throw new SafeError("Email account not found", 404);
apps/web/utils/ai/choose-rule/run-rules.ts (1)

144-151: Consider updating user connection to be consistent.

While updating the entire codebase to use email as the primary identifier, the user connection here still uses id. This is functionally correct but might be worth updating for consistency.

-    user: { connect: { id: userId } },
+    user: { connect: { email } },

This would require updating the function signature and callers to pass email instead of userId.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between e9d92c0 and ce0eac4.

📒 Files selected for processing (98)
  • apps/web/__tests__/ai-categorize-senders.test.ts (1 hunks)
  • apps/web/__tests__/ai-choose-args.test.ts (2 hunks)
  • apps/web/__tests__/ai-choose-rule.test.ts (1 hunks)
  • apps/web/__tests__/ai-create-group.test.ts (2 hunks)
  • apps/web/__tests__/ai-detect-recurring-pattern.test.ts (1 hunks)
  • apps/web/__tests__/ai-diff-rules.test.ts (3 hunks)
  • apps/web/__tests__/ai-example-matches.test.ts (2 hunks)
  • apps/web/__tests__/ai-extract-from-email-history.test.ts (1 hunks)
  • apps/web/__tests__/ai-extract-knowledge.test.ts (1 hunks)
  • apps/web/__tests__/ai-find-snippets.test.ts (1 hunks)
  • apps/web/__tests__/ai-process-user-request.test.ts (2 hunks)
  • apps/web/__tests__/ai-prompt-to-rules.test.ts (7 hunks)
  • apps/web/__tests__/ai-rule-fix.test.ts (1 hunks)
  • apps/web/__tests__/ai/reply/draft-with-knowledge.test.ts (1 hunks)
  • apps/web/__tests__/helpers.ts (1 hunks)
  • apps/web/__tests__/writing-style.test.ts (1 hunks)
  • apps/web/app/(app)/automation/onboarding/page.tsx (2 hunks)
  • apps/web/app/(app)/license/page.tsx (1 hunks)
  • apps/web/app/(app)/premium/Pricing.tsx (1 hunks)
  • apps/web/app/(app)/settings/MultiAccountSection.tsx (3 hunks)
  • apps/web/app/(app)/settings/page.tsx (1 hunks)
  • apps/web/app/(app)/setup/page.tsx (1 hunks)
  • apps/web/app/(app)/smart-categories/page.tsx (3 hunks)
  • apps/web/app/(app)/usage/usage.tsx (1 hunks)
  • apps/web/app/api/ai/analyze-sender-pattern/call-analyze-pattern-api.ts (1 hunks)
  • apps/web/app/api/ai/analyze-sender-pattern/route.ts (9 hunks)
  • apps/web/app/api/ai/compose-autocomplete/route.ts (2 hunks)
  • apps/web/app/api/ai/models/route.ts (1 hunks)
  • apps/web/app/api/ai/summarise/controller.ts (1 hunks)
  • apps/web/app/api/ai/summarise/route.ts (2 hunks)
  • apps/web/app/api/clean/gmail/route.ts (5 hunks)
  • apps/web/app/api/clean/history/route.ts (2 hunks)
  • apps/web/app/api/clean/route.ts (8 hunks)
  • apps/web/app/api/clean/save-result.ts (0 hunks)
  • apps/web/app/api/google/watch/all/route.ts (2 hunks)
  • apps/web/app/api/google/watch/controller.ts (3 hunks)
  • apps/web/app/api/google/watch/route.ts (1 hunks)
  • apps/web/app/api/google/webhook/process-history-item.ts (6 hunks)
  • apps/web/app/api/google/webhook/process-history.ts (5 hunks)
  • apps/web/app/api/google/webhook/types.ts (2 hunks)
  • apps/web/app/api/reply-tracker/process-previous/route.ts (2 hunks)
  • apps/web/app/api/resend/summary/all/route.ts (2 hunks)
  • apps/web/app/api/resend/summary/route.ts (2 hunks)
  • apps/web/app/api/unsubscribe/route.ts (1 hunks)
  • apps/web/app/api/user/categorize/senders/batch/handle-batch-validation.ts (1 hunks)
  • apps/web/app/api/user/categorize/senders/batch/handle-batch.ts (4 hunks)
  • apps/web/app/api/user/categorize/senders/progress/route.ts (1 hunks)
  • apps/web/app/api/user/me/route.ts (1 hunks)
  • apps/web/app/api/user/rules/prompt/route.ts (1 hunks)
  • apps/web/app/api/user/settings/email-updates/route.ts (1 hunks)
  • apps/web/app/api/user/settings/route.ts (2 hunks)
  • apps/web/components/PremiumAlert.tsx (1 hunks)
  • apps/web/prisma/schema.prisma (4 hunks)
  • apps/web/utils/actions/ai-rule.ts (20 hunks)
  • apps/web/utils/actions/assess.ts (4 hunks)
  • apps/web/utils/actions/categorize.ts (4 hunks)
  • apps/web/utils/actions/clean.ts (10 hunks)
  • apps/web/utils/actions/cold-email.ts (4 hunks)
  • apps/web/utils/actions/generate-reply.ts (3 hunks)
  • apps/web/utils/actions/reply-tracking.ts (2 hunks)
  • apps/web/utils/actions/rule.ts (14 hunks)
  • apps/web/utils/actions/user.ts (2 hunks)
  • apps/web/utils/actions/webhook.ts (1 hunks)
  • apps/web/utils/ai/actions.ts (1 hunks)
  • apps/web/utils/ai/assistant/process-user-request.ts (6 hunks)
  • apps/web/utils/ai/choose-rule/match-rules.test.ts (1 hunks)
  • apps/web/utils/ai/choose-rule/match-rules.ts (3 hunks)
  • apps/web/utils/ai/choose-rule/run-rules.ts (7 hunks)
  • apps/web/utils/ai/example-matches/find-example-matches.ts (2 hunks)
  • apps/web/utils/ai/group/create-group.ts (3 hunks)
  • apps/web/utils/ai/rule/create-rule.ts (2 hunks)
  • apps/web/utils/ai/rule/diff-rules.ts (2 hunks)
  • apps/web/utils/ai/rule/find-existing-rules.ts (2 hunks)
  • apps/web/utils/ai/rule/generate-prompt-on-delete-rule.ts (2 hunks)
  • apps/web/utils/ai/rule/generate-prompt-on-update-rule.ts (2 hunks)
  • apps/web/utils/ai/rule/generate-rules-prompt.ts (2 hunks)
  • apps/web/utils/ai/rule/prompt-to-rules.ts (2 hunks)
  • apps/web/utils/assistant/process-assistant-email.ts (2 hunks)
  • apps/web/utils/categorize/senders/categorize.ts (2 hunks)
  • apps/web/utils/cold-email/is-cold-email.test.ts (2 hunks)
  • apps/web/utils/cold-email/is-cold-email.ts (7 hunks)
  • apps/web/utils/llms/types.ts (1 hunks)
  • apps/web/utils/redis/categorization-progress.ts (2 hunks)
  • apps/web/utils/redis/clean.ts (2 hunks)
  • apps/web/utils/redis/clean.types.ts (1 hunks)
  • apps/web/utils/redis/reply.ts (1 hunks)
  • apps/web/utils/reply-tracker/check-previous-emails.ts (1 hunks)
  • apps/web/utils/reply-tracker/enable.ts (6 hunks)
  • apps/web/utils/reply-tracker/generate-draft.ts (4 hunks)
  • apps/web/utils/reply-tracker/inbound.ts (3 hunks)
  • apps/web/utils/reply-tracker/outbound.ts (4 hunks)
  • apps/web/utils/rule/prompt-file.ts (2 hunks)
  • apps/web/utils/rule/rule.ts (1 hunks)
  • apps/web/utils/upstash/categorize-senders.ts (3 hunks)
  • apps/web/utils/user/delete.ts (1 hunks)
  • apps/web/utils/user/get.ts (3 hunks)
  • apps/web/utils/user/validate.ts (1 hunks)
  • apps/web/utils/webhook.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • apps/web/app/api/clean/save-result.ts
🧰 Additional context used
🧬 Code Graph Analysis (50)
apps/web/app/api/ai/analyze-sender-pattern/call-analyze-pattern-api.ts (1)
apps/web/app/api/google/webhook/logger.ts (1)
  • logger (3-3)
apps/web/utils/ai/rule/diff-rules.ts (1)
apps/web/utils/llms/types.ts (1)
  • UserEmailWithAI (7-8)
apps/web/__tests__/ai-create-group.test.ts (1)
apps/web/__tests__/helpers.ts (1)
  • getUser (3-12)
apps/web/__tests__/ai-example-matches.test.ts (1)
apps/web/__tests__/helpers.ts (1)
  • getUser (3-12)
apps/web/__tests__/ai-diff-rules.test.ts (1)
apps/web/__tests__/helpers.ts (1)
  • getUser (3-12)
apps/web/utils/ai/example-matches/find-example-matches.ts (1)
apps/web/utils/llms/types.ts (1)
  • UserEmailWithAI (7-8)
apps/web/app/api/user/rules/prompt/route.ts (2)
apps/web/app/api/user/me/route.ts (1)
  • GET (48-56)
apps/web/utils/middleware.ts (1)
  • withError (16-70)
apps/web/app/api/ai/summarise/route.ts (2)
apps/web/utils/user/get.ts (1)
  • getAiUser (4-18)
apps/web/app/api/ai/summarise/controller.ts (1)
  • summarise (6-30)
apps/web/utils/ai/rule/generate-prompt-on-update-rule.ts (1)
apps/web/utils/llms/types.ts (1)
  • UserEmailWithAI (7-8)
apps/web/utils/actions/reply-tracking.ts (2)
apps/web/utils/reply-tracker/enable.ts (1)
  • enableReplyTracker (11-147)
apps/web/utils/user/get.ts (1)
  • getAiUser (4-18)
apps/web/app/(app)/usage/usage.tsx (1)
apps/web/utils/premium/index.ts (1)
  • isPremium (3-5)
apps/web/app/(app)/premium/Pricing.tsx (1)
apps/web/utils/premium/index.ts (1)
  • getUserTier (28-33)
apps/web/app/api/user/settings/route.ts (1)
apps/web/utils/error.ts (1)
  • SafeError (87-95)
apps/web/app/(app)/smart-categories/page.tsx (1)
apps/web/utils/redis/categorization-progress.ts (1)
  • getCategorizationProgress (14-23)
apps/web/app/api/ai/summarise/controller.ts (1)
apps/web/utils/llms/types.ts (1)
  • UserEmailWithAI (7-8)
apps/web/utils/ai/actions.ts (2)
apps/web/utils/reply-tracker/inbound.ts (1)
  • coordinateReplyProcess (19-67)
apps/web/utils/date.ts (1)
  • internalDateToDate (54-61)
apps/web/utils/ai/rule/create-rule.ts (1)
apps/web/utils/llms/types.ts (1)
  • UserEmailWithAI (7-8)
apps/web/utils/ai/rule/generate-prompt-on-delete-rule.ts (1)
apps/web/utils/llms/types.ts (1)
  • UserEmailWithAI (7-8)
apps/web/utils/ai/rule/generate-rules-prompt.ts (1)
apps/web/utils/llms/types.ts (1)
  • UserEmailWithAI (7-8)
apps/web/app/api/ai/compose-autocomplete/route.ts (2)
apps/web/utils/middleware.ts (1)
  • withError (16-70)
apps/web/utils/user/get.ts (1)
  • getAiUser (4-18)
apps/web/utils/ai/rule/find-existing-rules.ts (1)
apps/web/utils/llms/types.ts (1)
  • UserEmailWithAI (7-8)
apps/web/app/(app)/settings/MultiAccountSection.tsx (1)
apps/web/utils/premium/index.ts (1)
  • getUserTier (28-33)
apps/web/utils/actions/generate-reply.ts (1)
apps/web/utils/user/get.ts (1)
  • getAiUser (4-18)
apps/web/__tests__/helpers.ts (1)
apps/web/utils/types.ts (1)
  • EmailForLLM (104-113)
apps/web/utils/rule/rule.ts (1)
apps/web/utils/ai/rule/create-rule-schema.ts (1)
  • CreateOrUpdateRuleSchemaWithCategories (128-131)
apps/web/utils/user/validate.ts (1)
apps/web/utils/premium/index.ts (1)
  • hasAiAccess (67-77)
apps/web/app/api/user/categorize/senders/progress/route.ts (3)
apps/web/utils/redis/categorization-progress.ts (1)
  • getCategorizationProgress (14-23)
apps/web/app/api/user/me/route.ts (1)
  • GET (48-56)
apps/web/utils/middleware.ts (1)
  • withError (16-70)
apps/web/app/api/reply-tracker/process-previous/route.ts (3)
apps/web/utils/logger.ts (1)
  • createScopedLogger (17-65)
apps/web/utils/gmail/client.ts (1)
  • getGmailClient (28-33)
apps/web/utils/reply-tracker/check-previous-emails.ts (1)
  • processPreviousSentEmails (14-90)
apps/web/utils/user/get.ts (1)
apps/web/utils/llms/types.ts (1)
  • UserEmailWithAI (7-8)
apps/web/utils/actions/cold-email.ts (1)
apps/web/utils/actions/cold-email.validation.ts (2)
  • updateColdEmailSettingsBody (17-27)
  • updateColdEmailPromptBody (32-34)
apps/web/utils/ai/rule/prompt-to-rules.ts (1)
apps/web/utils/llms/types.ts (1)
  • UserEmailWithAI (7-8)
apps/web/app/api/user/categorize/senders/batch/handle-batch.ts (4)
apps/web/app/api/google/webhook/logger.ts (1)
  • logger (3-3)
apps/web/utils/user/validate.ts (1)
  • validateUserAndAiAccess (4-30)
apps/web/utils/error.ts (1)
  • isActionError (82-84)
apps/web/utils/categorize/senders/categorize.ts (1)
  • getCategories (140-144)
apps/web/utils/actions/categorize.ts (4)
apps/web/utils/user/validate.ts (1)
  • validateUserAndAiAccess (4-30)
apps/web/utils/error.ts (1)
  • isActionError (82-84)
apps/web/utils/upstash/categorize-senders.ts (2)
  • deleteEmptyCategorizeSendersQueues (53-62)
  • publishToAiCategorizeSendersQueue (18-51)
apps/web/utils/categorize/senders/categorize.ts (1)
  • categorizeSender (17-58)
apps/web/app/api/user/me/route.ts (7)
apps/web/utils/error.ts (1)
  • SafeError (87-95)
apps/web/app/api/ai/models/route.ts (1)
  • GET (21-45)
apps/web/app/api/clean/history/route.ts (1)
  • GET (17-25)
apps/web/app/api/google/watch/route.ts (1)
  • GET (12-26)
apps/web/app/api/user/categorize/senders/progress/route.ts (1)
  • GET (15-22)
apps/web/app/api/user/rules/prompt/route.ts (1)
  • GET (15-23)
apps/web/utils/middleware.ts (1)
  • withError (16-70)
apps/web/utils/actions/assess.ts (3)
apps/web/utils/gmail/client.ts (1)
  • getGmailClient (28-33)
apps/web/utils/assess.ts (1)
  • assessUser (13-53)
apps/web/utils/user/get.ts (1)
  • getAiUser (4-18)
apps/web/utils/reply-tracker/inbound.ts (1)
apps/web/utils/date.ts (1)
  • internalDateToDate (54-61)
apps/web/app/api/resend/summary/all/route.ts (3)
apps/web/app/api/google/webhook/logger.ts (1)
  • logger (3-3)
apps/web/utils/upstash/index.ts (1)
  • publishToQstashQueue (57-79)
apps/web/utils/cron.ts (1)
  • getCronSecretHeader (26-28)
apps/web/app/api/google/watch/controller.ts (2)
apps/web/utils/gmail/watch.ts (1)
  • watchGmail (5-16)
apps/web/utils/error.ts (1)
  • captureException (60-72)
apps/web/utils/reply-tracker/generate-draft.ts (2)
apps/web/utils/user/get.ts (1)
  • getAiUser (4-18)
apps/web/utils/redis/reply.ts (1)
  • getReply (13-21)
apps/web/utils/redis/clean.ts (3)
apps/web/utils/redis/clean.types.ts (1)
  • CleanThread (1-15)
apps/web/utils/gmail/thread.ts (1)
  • getThread (11-17)
apps/web/utils/prisma-extensions.ts (1)
  • update (33-53)
apps/web/app/api/ai/models/route.ts (1)
apps/web/utils/llms/config.ts (1)
  • Provider (5-12)
apps/web/utils/ai/group/create-group.ts (1)
apps/web/utils/llms/types.ts (1)
  • UserEmailWithAI (7-8)
apps/web/utils/ai/choose-rule/match-rules.ts (1)
apps/web/utils/llms/types.ts (1)
  • UserEmailWithAI (7-8)
apps/web/app/api/google/watch/all/route.ts (4)
apps/web/app/api/google/webhook/logger.ts (1)
  • logger (3-3)
apps/web/utils/premium/index.ts (2)
  • hasAiAccess (67-77)
  • hasColdEmailAccess (79-89)
apps/web/utils/gmail/client.ts (1)
  • getGmailClientWithRefresh (54-92)
apps/web/app/api/google/watch/controller.ts (1)
  • watchEmails (10-28)
apps/web/utils/actions/rule.ts (7)
apps/web/utils/rule/prompt-file.ts (2)
  • updatePromptFileOnRuleCreated (8-17)
  • updatePromptFileOnRuleUpdated (19-53)
apps/web/utils/ai/rule/generate-prompt-on-delete-rule.ts (1)
  • generatePromptOnDeleteRule (16-69)
apps/web/utils/actions/rule.validation.ts (2)
  • rulesExamplesBody (151-151)
  • createRulesOnboardingBody (165-173)
apps/web/utils/gmail/client.ts (1)
  • getGmailClient (28-33)
apps/web/utils/user/get.ts (1)
  • getAiUser (4-18)
apps/web/utils/reply-tracker/enable.ts (1)
  • enableReplyTracker (11-147)
apps/web/app/api/reply-tracker/process-previous/route.ts (1)
  • ProcessPreviousBody (16-16)
apps/web/app/api/google/webhook/process-history.ts (4)
apps/web/app/api/google/webhook/logger.ts (1)
  • logger (3-3)
apps/web/utils/premium/index.ts (3)
  • isPremium (3-5)
  • hasAiAccess (67-77)
  • hasColdEmailAccess (79-89)
apps/web/app/api/google/watch/controller.ts (1)
  • unwatchEmails (35-64)
apps/web/utils/gmail/client.ts (1)
  • getGmailClientWithRefresh (54-92)
apps/web/utils/ai/assistant/process-user-request.ts (3)
apps/web/utils/llms/types.ts (1)
  • UserEmailWithAI (7-8)
apps/web/utils/rule/rule.ts (1)
  • createRule (109-156)
apps/web/utils/rule/prompt-file.ts (2)
  • updatePromptFileOnRuleCreated (8-17)
  • updatePromptFileOnRuleUpdated (19-53)
apps/web/utils/cold-email/is-cold-email.ts (2)
apps/web/utils/llms/types.ts (1)
  • UserEmailWithAI (7-8)
apps/web/app/api/google/webhook/logger.ts (1)
  • logger (3-3)
apps/web/utils/actions/clean.ts (1)
apps/web/utils/redis/clean.ts (1)
  • updateThread (34-53)
apps/web/utils/ai/choose-rule/run-rules.ts (3)
apps/web/utils/llms/types.ts (1)
  • UserEmailWithAI (7-8)
apps/web/utils/ai/choose-rule/match-rules.ts (1)
  • findMatchingRule (206-217)
apps/web/utils/types.ts (1)
  • ParsedMessage (47-58)
🔇 Additional comments (254)
apps/web/__tests__/ai-categorize-senders.test.ts (1)

16-17: Updated user schema for consistency with centralized fixtures

The addition of userId and about properties to the testUser object aligns with the standardized user data structure being adopted across test files. This change prepares the test fixture for future transition to the centralized getUser helper used in other tests.

Also applies to: 21-22

apps/web/app/api/ai/analyze-sender-pattern/call-analyze-pattern-api.ts (2)

24-25: Updated user identifier from userId to email

This change is part of the broader refactoring effort to use email as the primary user identifier throughout the application. The error logging now correctly references email instead of userId.


32-33: Updated user identifier from userId to email

Consistent with the change above, this error logging now references email from the request body instead of the previous userId.

apps/web/utils/redis/clean.types.ts (1)

3-3:

Details

✅ Verification successful

Changed primary identifier from userId to email

Updated the CleanThread type to use email instead of userId as the user identifier, which aligns with the broader refactoring to make email the primary key for user-related operations in Redis and throughout the application.


🏁 Script executed:

#!/bin/bash
# Verify that all usages of CleanThread type now provide the email property instead of userId

# Look for any instances where CleanThread is created but still using userId property
rg -t typescript "CleanThread.*userId\s*:" --no-ignore-vcs

# Look for clean thread related functions to ensure they're using email parameter
rg -t typescript "function.*[Cc]lean[Tt]hread.*\(" -A 2 --no-ignore-vcs

Length of output: 204


I’ll retry the grep using the correct TypeScript type filter (ts) instead of typescript.


🏁 Script executed:

#!/bin/bash
# Verify that all CleanThread usages now use 'email' instead of 'userId'

# 1) Look for any leftover ‘userId’ in CleanThread type definitions or usages
rg -t ts "CleanThread.*userId\s*:" --no-ignore-vcs

# 2) Look for any cleanThread-related functions still declaring or destructuring 'userId'
rg -t ts "function.*[Cc]lean[Tt]hread.*\(" -A 2 --no-ignore-vcs

Length of output: 264


Verified CleanThread usages now use email
All instances of the CleanThread type and related functions have been updated to use email instead of userId:

  • apps/web/utils/redis/clean.types.ts defines CleanThread with an email: string property.
  • apps/web/app/api/clean/route.ts’s cleanThread function destructures { email, threadId }—no userId remains.

All checks pass; no further changes needed.

apps/web/__tests__/ai-find-snippets.test.ts (1)

4-4: Refactored to use centralized test helpers

Updated the import to use shared test helpers instead of local implementations, promoting code reuse and consistency across test files. This change aligns with the broader standardization of test fixtures.

apps/web/__tests__/writing-style.test.ts (1)

3-3: Centralized test fixture is a good practice.

The change to import getUser from the shared helpers module instead of defining it locally improves test consistency across the codebase and aligns with the effort to support multiple email accounts.

apps/web/app/(app)/license/page.tsx (1)

28-28: Consistent data structure update.

The change to access premium data via data?.user.premium is aligned with the broader refactoring to make user-related data email-based rather than user ID-based, which is necessary for supporting multiple email accounts per user.

apps/web/__tests__/ai-create-group.test.ts (1)

6-6: Uniform test fixtures help ensure consistency.

Replacing the local user definition with the shared getUser() helper function maintains consistency across tests and supports the refactoring towards email-based user identification.

Also applies to: 19-19

apps/web/__tests__/ai-extract-knowledge.test.ts (1)

5-5: Standardized test fixtures improve maintainability.

Using the centralized getUser() helper ensures consistent user mock data across all AI-related tests and supports the migration to email-based user identification.

apps/web/__tests__/ai-detect-recurring-pattern.test.ts (1)

5-5: Good refactoring to use centralized test helpers.

Using the shared getUser helper from the test helpers module reduces duplication and promotes consistency across test files. This is a positive improvement that aligns with the PR objective of refactoring for multiple email accounts per user.

apps/web/utils/ai/choose-rule/match-rules.test.ts (1)

20-20: Great use of shared test helper.

Importing getUser from a centralized helper module instead of defining it locally improves code maintainability and ensures consistent test fixtures across the test suite, which is particularly important when refactoring user identification patterns.

apps/web/utils/user/delete.ts (1)

41-41: Proper refactoring to use email as identifier.

This change correctly updates the unwatchEmails call to use email instead of userId as the primary identifier, which aligns with the broader refactoring to support multiple email accounts per user. The function now consistently uses email-based identification for external service operations.

apps/web/utils/actions/webhook.ts (2)

12-13: Correct authentication check update.

The authentication validation has been properly updated to check for the presence of an email rather than a user ID, which aligns with the email-centric approach needed for supporting multiple email accounts per user.


17-19: Appropriate database model update.

The update operation now correctly targets the emailAccount table using the email as the primary key instead of the user table with userId. This change is essential for the multi-email account architecture being implemented.

apps/web/__tests__/ai-example-matches.test.ts (2)

7-7: Good refactoring to use centralized user helper

Using the centralized getUser() helper function improves consistency across tests and makes future maintenance easier.


20-20: Clean implementation of shared test helper

Replacing the inline user object with the shared helper function follows good testing practices by standardizing test data across the codebase.

apps/web/app/(app)/settings/page.tsx (1)

20-28:

Details

✅ Verification successful

Data source changed from user to emailAccount for settings

This change shifts the data source from the user table to the emailAccount table while maintaining the same field selections. This change aligns with the PR objective of supporting multiple email accounts per user.

Verify that all these fields have been properly migrated to the emailAccount table in the schema:


🏁 Script executed:

#!/bin/bash
# Check if the emailAccount table has the fields used in this query
grep -A 50 "model EmailAccount" $(fd "schema.prisma" --type f)

Length of output: 1495


All required fields exist on EmailAccount model

I’ve confirmed that about, signature, statsEmailFrequency, and webhookSecret are defined on the EmailAccount schema:

  • about String?
  • signature String?
  • statsEmailFrequency Frequency @default(WEEKLY)
  • webhookSecret String?

No further changes are needed here—this query correctly targets the new data source.

apps/web/utils/reply-tracker/check-previous-emails.ts (1)

50-50:

Details

✅ Verification successful

Updated user identification from id to userId

Changed from using user.id to user.userId in the Prisma unique constraint, aligning with the updated schema for user identification.

Verify that the UserEmailWithAI type has been updated to use userId instead of id:


🏁 Script executed:

#!/bin/bash
# Check the definition of UserEmailWithAI type
cat $(fd "types.ts" --type f | grep -l "UserEmailWithAI")

Length of output: 56


🏁 Script executed:

#!/bin/bash
# Search for all occurrences of UserEmailWithAI in the codebase
rg "UserEmailWithAI" -n .

Length of output: 8847


UserEmailWithAI type correctly uses userId

The UserEmailWithAI type in apps/web/utils/llms/types.ts now picks "userId" (not "id"), matching the updated Prisma schema. All imports and usages across the codebase reference the userId field as intended.

• Definition confirmed at apps/web/utils/llms/types.ts:7

export type UserEmailWithAI = Pick<EmailAccount, "userId" | "email" | "about"> & 

No further changes required.

apps/web/__tests__/ai/reply/draft-with-knowledge.test.ts (1)

67-67:

Details

✅ Verification successful

Updated user mock to use userId instead of id

Changed the property key from id to userId in the test user fixture, maintaining consistency with the broader refactoring of user identification.

Verify that the aiDraftWithKnowledge function now expects userId instead of id:


🏁 Script executed:

#!/bin/bash
# Check the definition of aiDraftWithKnowledge function
cat $(fd "draft-with-knowledge.ts" --type f) | grep -A 10 "aiDraftWithKnowledge"

Length of output: 407


🏁 Script executed:

#!/bin/bash
# Locate the definition of UserEmailWithAI to verify expected property keys
rg "UserEmailWithAI" -n

Length of output: 8659


Verified: aiDraftWithKnowledge uses userId
The UserEmailWithAI type (defined in apps/web/utils/llms/types.ts) picks the userId property, and the aiDraftWithKnowledge signature in apps/web/utils/ai/reply/draft-with-knowledge.ts accepts user: UserEmailWithAI. The test fixture in apps/web/__tests__/ai/reply/draft-with-knowledge.test.ts correctly uses userId: "user-123". No further changes needed.

apps/web/utils/ai/rule/diff-rules.ts (1)

4-4: Type refactoring aligns with multi-email account support.

The change from UserAIFields & { email: string } to the consolidated UserEmailWithAI type provides better type safety and clarity. This supports the PR's goal of handling multiple email accounts per user by explicitly representing the relationship between email accounts and user data.

Also applies to: 24-24

apps/web/app/api/user/settings/email-updates/route.ts (1)

19-19: Database target change supports multi-email architecture.

Correctly migrates user settings from the user table to the emailAccount table, which is a key change for supporting multiple email accounts per user. The email-based identification is properly maintained in the query condition.

apps/web/app/(app)/premium/Pricing.tsx (1)

48-48:

Details

✅ Verification successful

Updated premium data access path.

The premium data access path has been correctly updated to reflect the new data structure. This change aligns with the codebase's shift to email-based user identification.


🏁 Script executed:

#!/bin/bash
# Description: Verify all accesses to premium data have been updated consistently

# Look for any remaining direct accesses to data.premium that might have been missed
rg -t ts -t tsx "data\?.premium" --glob '!apps/web/app/(app)/premium/Pricing.tsx'

# Check that all getUserTier calls use the new structure
rg -t ts -t tsx "getUserTier\(" -A 1 -B 1

Length of output: 183


🏁 Script executed:

#!/bin/bash
# Description: Verify consistent usage of premium data access across TS/TSX files

# Find legacy direct accesses to premium data outside Pricing.tsx
rg 'data\?\.premium' -g '*.ts' -g '*.tsx' --glob '!apps/web/app/(app)/premium/Pricing.tsx'
rg 'data\.premium'    -g '*.ts' -g '*.tsx' --glob '!apps/web/app/(app)/premium/Pricing.tsx'

# Verify all getUserTier calls and their context
rg 'getUserTier'      -g '*.ts' -g '*.tsx' -C1

Length of output: 1408


Premium data access path updated consistently

All instances of getUserTier now reference the new user.premium field rather than the legacy top‐level premium object.

• apps/web/app/(app)/premium/Pricing.tsx

  • const premiumTier = getUserTier(data?.user.premium);
    • apps/web/app/(app)/settings/MultiAccountSection.tsx
  • const premiumTier = getUserTier(dataPremium?.user.premium);

No remaining direct data.premium or data?.premium references were found.

apps/web/__tests__/ai-extract-from-email-history.test.ts (1)

4-4: Centralized test user fixture improves maintainability.

Using an imported getUser helper instead of a local definition standardizes test data setup across the test suite. This makes tests more consistent and easier to maintain when user data structures change.

apps/web/app/api/ai/summarise/controller.ts (1)

2-2: Type change is well-aligned with email-centric architecture

The change from UserAIFields to UserEmailWithAI properly aligns with the PR's objective of supporting multiple email accounts per user, making AI capabilities email-specific rather than user-specific.

Also applies to: 9-9

apps/web/__tests__/ai-choose-args.test.ts (2)

5-5: Good refactoring to centralize test helpers

Using the imported getUser helper from the shared test module rather than a local implementation ensures consistency across tests and reduces code duplication.


201-201: Property rename for clarity

The change from type to systemType maintains consistency with other test files and likely better reflects the purpose of this field in the application.

apps/web/utils/webhook.ts (2)

31-36: Database query shifted to email-centric model

The query change from user to emailAccount properly implements the email-centric architecture required for supporting multiple email accounts per user.


43-43:

Details

✅ Verification successful

Webhook secret now from email account

Using the email account's webhook secret is consistent with the email-centric architecture being implemented.


🏁 Script executed:

#!/bin/bash
# Check if all callWebhook usages have been updated to pass email instead of user ID

rg -A 2 "callWebhook\(" --type ts

Length of output: 210


🏁 Script executed:

#!/bin/bash
# Check all callWebhook usages across all file types (including .ts, .tsx, .js, .jsx)
rg -A 2 "callWebhook\("

Length of output: 200


Webhook secret now sourced from email account

Using the email account’s webhook secret aligns with our email‑centric architecture. All callWebhook invocations have been updated to pass userEmail instead of a user ID (e.g. in apps/web/utils/ai/actions.ts).

apps/web/__tests__/ai-choose-rule.test.ts (1)

5-5: Well-structured test refactoring

Moving from locally defined helpers to centralized imports improves maintainability and ensures consistency across tests. This change supports the email-centric architecture being implemented throughout the application.

apps/web/utils/ai/rule/find-existing-rules.ts (2)

3-3: Type import update aligns with email-based user identification

This update changes the import from a composite type (UserAIFields & { email: string }) to the consolidated UserEmailWithAI type, which is part of the broader refactoring to support multiple email accounts per user.


25-25: Type annotation change supports the multi-email accounts model

The parameter type has been updated from the previous composite type to UserEmailWithAI, maintaining all required fields while aligning with the new data structure that focuses on email-based identification rather than user ID.

apps/web/app/api/unsubscribe/route.ts (1)

53-55: Database operation shifted from user to emailAccount table

This change correctly updates the database operation to target the emailAccount table instead of the user table, using the email address as the primary identifier. This supports the foundation for multiple email accounts per user.

Since the unsubscribe token is still associated with a user who may have multiple email accounts, consider verifying this approach handles the multi-account scenario correctly:

#!/bin/bash
# Check if the emailToken schema still references the user table
rg -A 5 "model EmailToken" --glob "*.prisma"
apps/web/utils/actions/user.ts (5)

22-23: Improved error handling for email validation

Good improvement to extract the email into a variable and validate it early, providing a clear error message if not present.


28-30: Database operation refactored to use email as primary key

Correctly updated to target the emailAccount table using email as the identifier instead of the user table by user ID. This change aligns with the multi-email account architecture.


44-45: Consistent email validation applied to saveSignatureAction

Similar to the change in saveAboutAction, this improves error handling by validating the email early in the function.


50-52: Consistent database operation refactoring

Correctly updated to target the emailAccount table using email as the primary key, matching the pattern applied in saveAboutAction.


89-138:

Details

❌ Incorrect review comment

Remaining functions still use userId for operations

Some server actions in this file (resetAnalyticsAction, completedOnboardingAction, completedAppOnboardingAction) still use the user ID for database operations.

Consider whether these should also be migrated to use email-based operations for consistency, or document why these specific operations still require user ID.


🏁 Script executed:

#!/bin/bash
# Check whether these fields have been migrated to the EmailAccount model
rg -A 10 "model User" --glob "*.prisma"
rg -A 10 "model EmailAccount" --glob "*.prisma"

Length of output: 1397


Use of userId is correct for User‐model operations

These server actions are operating on the User model (and its related tables via the userId foreign key) rather than the EmailAccount model. Since User.id is the primary key and relations like emailMessage.userId depend on it, continuing to use userId here is intentional and appropriate. The EmailAccount model is separate—migrating these calls to “email‑based” logic isn’t applicable.

Likely an incorrect or invalid review comment.

apps/web/utils/ai/rule/generate-prompt-on-delete-rule.ts (2)

3-3: Type import consolidation for email-based user identification

Updated import to use the consolidated UserEmailWithAI type, which simplifies the type system and aligns with the email-based user identification approach.


21-21: Type annotation updated for email-based data model

The parameter type is correctly updated from UserAIFields & Pick<User, "email"> to UserEmailWithAI, ensuring consistency with the new data model that uses email accounts as the primary entity.

apps/web/__tests__/ai-prompt-to-rules.test.ts (2)

19-20: LGTM: Consistent user object updates across test cases

The additions of userId and about properties to all user test objects are consistent with the migration to support multiple email accounts per user. This change aligns with the broader refactoring of user identification across the codebase.

Also applies to: 119-120, 139-140, 216-217, 260-261, 304-305


344-344: Update to reflect new reply content structure

The assertion has been correctly updated to check replyAction?.fields?.content instead of replyAction?.content, reflecting the structural change in how reply content is now stored within a nested fields object.

apps/web/utils/ai/example-matches/find-example-matches.ts (1)

5-5: LGTM: Type update aligns with email-based architecture

The change from the previous user type to UserEmailWithAI reflects the architectural shift from user-based to email-based identification. The new type correctly combines essential email account fields (userId, email, about) with AI-related user fields, maintaining compatibility with the existing function implementation.

Also applies to: 30-30

apps/web/utils/categorize/senders/categorize.ts (1)

24-24: LGTM: Consistent update of user identifier

The changes from user.id to user.userId are consistent with the architectural shift to support multiple email accounts per user. This ensures proper user identification when interacting with categories and sender information.

Also applies to: 46-46

apps/web/app/api/ai/models/route.ts (4)

23-24: LGTM: Improved authentication check with explicit email extraction

The code now explicitly extracts the email from the session and checks its presence before proceeding, improving the authentication flow clarity.


26-29: LGTM: Updated database query to use email account

The database query has been correctly updated to fetch from the emailAccount table by email instead of the user table, aligning with the new data model where AI configuration is stored with the email account.


31-35: LGTM: Consistent validation logic for email account

The conditional check has been properly updated to verify the existence and properties of the emailAccount record, maintaining the same validation logic while adapting to the new data structure.


39-39: LGTM: Updated API key source

The call to getOpenAiModels now correctly uses the API key from the email account instead of the user record.

apps/web/__tests__/ai-diff-rules.test.ts (1)

3-3: Great refactoring to centralize test data!

Using the getUser() helper function instead of inline user object literals improves maintainability and consistency across tests. This approach makes it easier to adapt tests when the user model changes, which aligns well with the PR's goal of supporting multiple email accounts per user.

Also applies to: 13-13, 44-44

apps/web/components/PremiumAlert.tsx (1)

23-23: Property access path updated correctly for new data structure

This change correctly updates the premium access path from data?.premium to data?.user.premium, aligning with the refactoring to support multiple email accounts per user.

Please verify that data.user is guaranteed to be defined whenever this component is rendered. If there are edge cases where data exists but data.user might be undefined, additional null checking may be needed.

apps/web/app/api/user/categorize/senders/batch/handle-batch-validation.ts (1)

4-4: Key identifier changed from userId to email

This change is fundamental to supporting multiple email accounts per user, as it shifts identification from userId to email in the validation schema. This properly aligns with the PR objective.

Since this changes the expected payload structure for API consumers, verify that all client code has been updated to send email instead of userId. Consider documenting this API change if it affects external consumers.

apps/web/utils/ai/rule/generate-rules-prompt.ts (1)

3-3: Clean type refactoring that maintains existing functionality

Replacing the composite type with the unified UserEmailWithAI type simplifies the code and aligns with the broader refactoring to make email accounts the central entity. This change maintains the same functionality while making the type definitions more consistent across the codebase.

Also applies to: 36-36

apps/web/utils/ai/actions.ts (1)

220-227: LGTM: Function signature update improves type safety

The update to use an object parameter with named properties for the coordinateReplyProcess function call is a good improvement. This approach is more maintainable and provides better type safety compared to positional arguments, especially when dealing with multiple parameters.

apps/web/app/(app)/usage/usage.tsx (1)

25-28: Data structure path updated correctly

The change to access premium data through data?.user.premium instead of directly from data?.premium aligns with the broader refactoring to support multiple email accounts. Premium data is now nested under the user object, providing a consistent data access pattern.

apps/web/app/api/google/watch/route.ts (2)

14-15: Authentication check correctly updated to use email

The authentication check has been properly updated to verify the user's email presence instead of the user ID, which aligns with the shift to email-based identification.


19-23: Watch endpoint correctly migrated to email-based identification

The function call to watchEmails and error logging have been appropriately updated to use email as the primary identifier, consistent with the codebase refactoring from user ID to email-based operations.

apps/web/app/api/clean/history/route.ts (2)

8-11: Database query correctly migrated from userId to email

The getCleanHistory function signature and Prisma query have been properly updated to filter by email instead of userId, which is consistent with the broader refactoring approach.


19-22: Authentication and function call updated consistently

The authentication check and the call to getCleanHistory now use email as the identifier, maintaining consistency with the overall refactoring approach.

apps/web/utils/ai/rule/prompt-to-rules.ts (1)

4-4: Type updated to align with new email-based user model

The changes update the user parameter type from an intersection type to the new consolidated UserEmailWithAI type. This is consistent with the broader refactoring to support multiple email accounts per user, where user identification and AI-related data are now accessed through the EmailAccount entity rather than the User entity.

Also applies to: 25-25

apps/web/app/api/ai/summarise/route.ts (3)

15-16: Improved variable extraction for session email

Extracting the email into a dedicated variable improves code readability and reduces repetition when accessing session data.


33-33: Updated to use email-based user lookup

The API now uses the getAiUser helper to fetch user AI configuration by email instead of directly querying by user ID, aligning with the new data access pattern.


38-38: Updated function call to use extracted email variable

The summarise function now receives the extracted email variable as a parameter, maintaining consistency with the new email-based identification approach.

apps/web/__tests__/ai-rule-fix.test.ts (1)

5-5: Centralized test helpers for consistency

The test now imports getEmail and getUser from a shared helpers module instead of defining them locally, improving consistency across test files and supporting the email account refactoring.

apps/web/app/api/ai/compose-autocomplete/route.ts (4)

6-6: Updated imports to use the new email-based user helper

Replaced the direct Prisma import with the new getAiUser helper function that retrieves user AI configuration by email address.


10-11: Improved variable naming and authentication check

Renamed userEmail to email for clarity and updated the authentication check to verify the presence of the email from the session, aligning with the email-based authentication approach.


13-14: Replaced direct Prisma query with specialized helper function

Replaced a direct database query with the new getAiUser helper that fetches user AI configuration by email. This centralizes the data access logic and supports the migration to email-based user identification.


28-28: Updated parameter name to match variable renaming

Updated the parameter passed to chatCompletionStream to use the renamed email variable, maintaining consistency with the user identification refactoring.

apps/web/utils/ai/rule/generate-prompt-on-update-rule.ts (2)

3-3: Type import aligned with email-based user representation

The import was updated to use the consolidated UserEmailWithAI type, which is part of the broader refactoring to use email accounts as the primary user identifier.


22-22: Parameter type updated to use UserEmailWithAI

The function parameter type has been updated from a composite type to the new UserEmailWithAI type, improving type consistency across the codebase while maintaining the same functionality.

apps/web/app/api/user/rules/prompt/route.ts (3)

8-11: Function refactored to use email-based identification

The getRulesPrompt function has been appropriately updated to:

  1. Accept email instead of userId as the parameter
  2. Query the emailAccount table instead of the user table
  3. Use email as the lookup key instead of id

This aligns with the PR objective to support multiple email accounts per user.


17-18: Authentication check updated to use email

Authentication verification now correctly uses the user's email from the session instead of userId, consistent with the new email-centric approach.


20-20: Function call updated to use email parameter

The call to getRulesPrompt now correctly passes the email parameter, maintaining consistency with the function's updated signature.

apps/web/utils/cold-email/is-cold-email.test.ts (3)

37-44: Mock user object updated to match EmailAccount structure

The mock user object has been properly updated to:

  1. Use userId instead of id as the identifier
  2. Include new required fields: about, aiProvider, aiModel, and aiApiKey

This ensures the test remains compatible with the refactored database structure.


59-65: Database query updated for new composite key structure

The query's where clause has been correctly updated to use the composite key userId_fromEmail, which reflects the updated database schema and properly identifies records using the user's ID and sender's email address.


70-70: Create object updated to use userId field

The create object now correctly uses mockUser.userId instead of mockUser.id, maintaining consistency with the updated database schema.

apps/web/utils/ai/rule/create-rule.ts (3)

2-2: Import updated to use consolidated user type

The import has been changed to use UserEmailWithAI instead of the previous type, aligning with the codebase-wide standardization on email-based user representation.


8-8: Function parameter consolidated to use UserEmailWithAI

The function signature has been improved to use a single user parameter of type UserEmailWithAI instead of separate parameters, which simplifies the API and improves type consistency.


24-24: Updated access to email property

The code now correctly accesses the email through user.email property instead of a separate parameter, maintaining functionality while improving code organization.

apps/web/app/api/google/webhook/process-history-item.ts (6)

68-72: Property access updated for multi-account support

The change from user.id to user.userId is part of the email-account based refactoring to support multiple accounts per user. This correctly uses the EmailAccount model's userId property instead of directly accessing the User model's id.


100-102: User identifier updated to use EmailAccount model

Consistent with the multi-account refactoring, the function now correctly passes user.userId rather than user.id to the assistant email processor.


125-127: User identifier updated for blockUnsubscribedEmails function

The function parameter is correctly updated to use user.userId rather than user.id, ensuring consistency with the email-based user identification approach.


169-171: Database query structure updated for multiple account support

The query now correctly uses the compound key email_userId for finding a unique newsletter entry. This change supports the schema refactoring for multiple email accounts by using the correct database access pattern.


219-221: User object structure updated for draft tracking

The trackSentDraftStatus function now receives the correctly structured user object with userId instead of id. This maintains consistency with the email-account based approach.


244-247: User identifier updated for thread draft cleanup

The cleanupThreadAIDrafts function now receives user.userId instead of user.id, completing the refactoring of this module to use EmailAccount-based identifiers.

apps/web/__tests__/ai-process-user-request.test.ts (2)

8-8: Centralized test helper imported for consistent user data

Good refactoring to import getUser from a shared test helper instead of defining it locally. This ensures consistency in test data across test files and aligns with the multi-account refactoring.


449-449: Added systemType field to align with schema updates

The addition of systemType: null to the rule object ensures that test fixtures match the updated schema, which now distinguishes between user-created rules and system-generated rules.

apps/web/utils/actions/reply-tracking.ts (2)

24-27: Authentication logic refactored to use email for identification

The authentication logic now correctly uses the user's email rather than user ID, aligning with the email-account centric approach. This enables proper support for multiple email accounts per user.


39-43: User identification updated to use email

The action now uses email to identify the user and fetch their details, which is consistent with the email-account based approach.

apps/web/app/(app)/settings/MultiAccountSection.tsx (4)

46-46: Premium data access path updated

The component now correctly accesses premium data through dataPremium?.user.premium rather than directly from dataPremium?.premium, which aligns with the restructured premium data model.


85-87: emailAccountsAccess property path updated

The component now correctly accesses emailAccountsAccess through the restructured path dataPremium?.user.premium?.emailAccountsAccess.


96-97: Premium tier check path updated

The component now correctly checks the premium tier through the restructured path dataPremium?.user.premium?.tier.


98-103: Multiple premium properties paths updated

The component now correctly accesses multiple premium-related properties (emailAccountsAccess and pendingInvites) through the restructured paths under dataPremium?.user.premium.

apps/web/__tests__/helpers.ts (2)

3-12: Well-structured user test fixture.

This implementation provides a consistent user mock for tests with all the necessary fields, including both user ID and email, which aligns well with the multiple email accounts architecture being implemented.


14-29: Clean implementation of email test fixture with good defaults.

The getEmail function follows best practices by:

  1. Using TypeScript's Partial type for optional parameters
  2. Providing sensible defaults for required fields
  3. Using spread operator for conditional property inclusion

This will help maintain consistency across tests when working with email objects.

apps/web/app/api/user/categorize/senders/progress/route.ts (3)

10-12: LGTM: Consistent refactoring to email-based identification.

The function now correctly accepts an email parameter instead of userId, maintaining the same functionality while aligning with the new architecture for multiple email accounts per user.


17-18: Authentication check properly updated to verify email presence.

Authentication verification now correctly checks for the email from the session, which is the new primary identifier for user-related operations.


20-20: Function call consistently updated to use email parameter.

The function call to getCategorizeProgress has been properly updated to use the email parameter, maintaining consistency with the new parameter structure.

apps/web/utils/user/validate.ts (4)

4-8: LGTM: Function signature updated to use email-based identification.

The function now correctly accepts an email parameter instead of userId, aligning with the new architecture for multiple email accounts per user.


9-20: Well-structured database query with proper nested relations.

The query has been properly refactored to fetch an emailAccount by email, while maintaining access to user premium data through the nested relation. This structure correctly reflects the hierarchical relationship where a user can have multiple email accounts.


23-26: AI access validation properly adapted to the new data structure.

The AI access validation now correctly uses the nested user premium data from the email account, maintaining the same validation logic while adapting to the new data model.


29-29: Function return value properly updated to return emailAccount.

The function now returns the emailAccount object instead of the user object, which is consistent with the overall refactoring to email-based operations.

apps/web/app/(app)/smart-categories/page.tsx (4)

38-38: Variable name properly updated to reflect the new data source.

The array destructuring now correctly includes emailAccount instead of user, maintaining consistency with the architectural changes.


48-50: Database query properly updated to fetch emailAccount by email.

The query has been correctly refactored to fetch an emailAccount by email, while maintaining the selection of the same fields needed by the component.


52-52: Function call properly updated to use email parameter.

The call to getCategorizationProgress has been properly updated to pass the email parameter, which aligns with the updated function signature.


135-137: Property access properly updated to use emailAccount.

The autoCategorizeSenders property is now correctly sourced from emailAccount instead of user, maintaining the same functionality while adapting to the new data structure.

apps/web/utils/actions/generate-reply.ts (4)

18-19: Good auth validation pattern!

Extracting the email to a separate variable and checking for its presence creates a cleaner authentication flow.


21-21: Verified function signature match with getAiUser

The call to getAiUser correctly passes an object with an email property, matching the function's updated signature in utils/user/get.ts.


32-35: Consistent parameter update

The change from userId to email in the getReply function call aligns with the PR's objective of supporting multiple email accounts per user.


50-54: Consistent parameter update

The saveReply call now correctly uses the email as the primary identifier which is consistent with other changes in this PR.

apps/web/utils/assistant/process-assistant-email.ts (3)

102-136: Successfully refactored to email-account-centric data model

The query has been properly updated to retrieve data through the emailAccount table rather than directly from the user table. The selection fields have been appropriately restructured to access user-related data through the nested user relation.

The inclusion of group.items with detailed field selection is a good improvement for data completeness.


217-217: Updated reference to nested user data

The reference to rules has been correctly updated to use the nested path user.user.rules, matching the data structure from the updated query.


221-221: Updated reference to nested categories

The reference to categories has been correctly updated to use the nested path user.user.categories, matching the data structure from the updated query.

apps/web/utils/rule/rule.ts (2)

30-40: Improved function signature with named parameters

Replacing positional parameters with a single object using named properties is a good practice. This change:

  1. Makes the function call sites more readable
  2. Reduces the risk of parameter order mistakes
  3. Makes the function more maintainable when adding new parameters

30-40:

Details

✅ Verification successful

Verify all call sites of safeCreateRule

Since the function signature has changed significantly, ensure all call sites have been updated to use the new object parameter format.


🏁 Script executed:

#!/bin/bash
# Find all occurrences of safeCreateRule to verify they've been updated
rg -A 1 "safeCreateRule\(" --glob "!*.md"

Length of output: 525


🏁 Script executed:

#!/bin/bash
rg -n -A3 "safeCreateRule\(" --glob "!*.md"

Length of output: 1082


safeCreateRule call sites all use the new object signature

All occurrences of safeCreateRule have been updated to pass an object parameter. Verified in:

  • apps/web/utils/reply-tracker/enable.ts (around line 83)
  • apps/web/utils/actions/ai-rule.ts (around lines 183 and 488)

No further changes needed.

apps/web/app/api/user/settings/route.ts (3)

16-17: Good auth validation pattern!

Extracting the email to a separate variable and checking for its presence improves code readability and error handling.


20-20: Simplified AI provider handling

Removing the default fallback and using options.aiProvider directly in the switch statement is a good simplification.


48-55: Successfully migrated to email-account-centric data model

The code now correctly updates the emailAccount table using email as the primary key, which aligns with the PR objective of supporting multiple email accounts per user.

apps/web/app/api/user/me/route.ts (2)

9-46: Refactored to support multiple email accounts per user

The getUser function has been properly refactored to use email as the primary identifier instead of userId, aligning with the architectural shift to support multiple email accounts per user. The database query now properly fetches from the emailAccount table and accesses user data through the nested relation.

This approach matches the pattern seen in other API routes that extract email from the session and use it as the primary identifier for data access.


50-53: Authentication check updated to use email

The authentication check now correctly extracts the email from the session and verifies its existence before proceeding, which is consistent with the email-centric approach being implemented throughout the codebase.

apps/web/utils/llms/types.ts (1)

1-8: Types updated to reflect email-centric data model

The types have been properly updated to reflect that AI-related fields are now associated with email accounts rather than directly with users. The change from id to userId correctly represents the new relationship where email accounts reference users.

These type changes will ensure type safety across all AI-related modules that use UserEmailWithAI.

apps/web/app/(app)/automation/onboarding/page.tsx (3)

15-16: Authentication check updated to use email

The authentication check has been correctly updated to verify the email from the session instead of the user ID, consistent with the new approach being implemented across the codebase.


41-66: Data access pattern refactored to use email accounts

The getUserPreferences function has been properly refactored to use email as the primary identifier and to access user data through the email account relation. The database query structure correctly navigates the updated data model to retrieve the same information.


68-78: Reference paths updated for the new data structure

The returned preferences now correctly reference data through the updated path structure, using emailAccount.user.rules and emailAccount.coldEmailBlocker to maintain the same functionality while adapting to the new data model.

apps/web/app/api/google/webhook/types.ts (1)

4-20: Type definition updated to use EmailAccount fields

The ProcessHistoryOptions type has been correctly updated to pick fields from EmailAccount instead of User, aligning with the broader refactoring to support multiple email accounts per user.

Note that while "about" has been removed from the explicitly picked fields, it should still be accessible through the UserEmailWithAI type that's included via intersection, as that type now picks "about" from EmailAccount.

apps/web/app/(app)/setup/page.tsx (4)

20-21: Authentication check correctly migrated to use email instead of user ID

This change aligns with the PR's objective of refactoring user identification to support multiple email accounts per user. Using email as the primary identifier is consistent with the broader refactoring across the codebase.


23-36: Database query updated to use emailAccount instead of user model

Good implementation of the data access pattern change. The query now correctly:

  1. Queries the emailAccount table using email as the primary key
  2. Accesses rules and newsletters through the nested user relationship
  3. Maintains proper type-safety with appropriate selections

This change supports the foundation for multiple email accounts per user.


39-39: Error message updated to reflect new query target

The error message has been appropriately updated to reference "User not found" when an email account isn't found, maintaining consistency with user expectations.


41-42: Feature flag logic updated to use nested data structure

The logic determining configured features has been correctly updated to access data through the new nested relationship structure (emailAccount.user.rules instead of user.rules), preserving the same functionality with the new data model.

apps/web/app/api/reply-tracker/process-previous/route.ts (4)

15-16: Schema updated to use email-based identification

The request schema now correctly uses email as the primary identifier instead of userId, consistent with the broader refactoring to support multiple email accounts per user.


28-36: Database query migrated to emailAccount model

The query has been properly updated to:

  1. Find an emailAccount by email instead of a user by ID
  2. Include the associated account with access and refresh tokens
  3. Maintain consistent error handling for the not-found case

This approach aligns well with the multi-account architecture.


40-46: Token access logic adjusted for new data structure

The check for and access to the OAuth tokens has been correctly migrated to use emailAccount.account directly instead of accessing via an accounts array as before.


48-48: Function call updated to pass emailAccount object

The call to processPreviousSentEmails correctly passes the emailAccount object, which based on the imported code snippet will contain the necessary email and userId properties expected by that function.

apps/web/utils/reply-tracker/inbound.ts (3)

19-33: Improved function signature using object parameters

Good refactoring to use an object parameter with named properties instead of positional arguments. This change:

  1. Improves code readability and maintainability
  2. Makes the function call sites more explicit
  3. Provides better IDE support with named parameters
  4. Allows easier expansion when adding new parameters in the future

This is a beneficial change beyond just supporting the email accounts refactoring.


123-123: Field reference updated to use userId from UserEmailWithAI

Correctly updated from user.id to user.userId to maintain consistency with the new data model, where userId is accessed through the emailAccount relationship.


146-154: Function call updated to use object parameter format

The call to coordinateReplyProcess has been properly updated to use the new object parameter format, passing all required fields including the user's ID and email.

apps/web/app/api/resend/summary/all/route.ts (3)

24-43: Query updated to fetch emailAccounts instead of users

The database query has been correctly migrated to:

  1. Query the emailAccount table instead of the user table
  2. Filter by summaryEmailFrequency directly on the emailAccount
  3. Move the premium check to the nested user relationship
  4. Maintain the same filtering for account age

This aligns with the refactoring to make email the primary user identifier.


49-64: Iteration and queue publishing updated for email accounts

The loop now correctly:

  1. Iterates over emailAccounts instead of users
  2. Uses emailAccount.email in the message body for the queue
  3. Logs errors with the email identifier instead of userId

This ensures the summary email distribution works correctly with the new data structure.


66-67: Logging and return value updated for consistency

The final logging and return value now correctly reference the count of emailAccounts instead of users, maintaining consistency in the terminology throughout the function.

apps/web/utils/reply-tracker/generate-draft.ts (4)

41-41: User identification refactored to use email instead of ID

The function now uses email instead of id to query for the AI user, which aligns with the broader refactoring to support multiple email accounts per user.


121-124: Redis key scheme updated to use email for identification

The getReply function now uses the user's email as the identifier instead of user ID, maintaining consistency with the updated Redis key scheme in the reply utilities.


141-141: Knowledge base query now references userId property

Changed from userId: user.id to userId: user.userId to reflect the updated user object structure that comes from EmailAccount model rather than User model.


194-195: Redis cache updates now use email-based keys

Saving reply cache entries now uses the email-based key scheme, ensuring consistency with the retrieval method and overall architecture change.

apps/web/utils/ai/group/create-group.ts (3)

6-6: Imported UserEmailWithAI type for standardized user representation

The imported type provides a consistent structure for user data with AI fields, supporting the multi-email account architecture.


57-57: Function parameter type updated to UserEmailWithAI

The aiGenerateGroupItems function now uses the standardized UserEmailWithAI type, which includes the necessary userId, email, and AI-related fields.


130-130: Helper function parameter type updated to UserEmailWithAI

The verifyGroupItems function now also uses the standardized UserEmailWithAI type, maintaining consistency with the parent function.

apps/web/utils/user/get.ts (4)

2-2: Added import for UserEmailWithAI type

This import supports the explicit return type annotation and standardizes user data representation across the codebase.


4-18: Refactored getAiUser to query by email instead of ID

The function has been completely refactored to:

  1. Accept an email parameter instead of user ID
  2. Query the emailAccount table instead of the user table
  3. Select userId instead of id in the result
  4. Explicitly return Promise<UserEmailWithAI | null>

This change is fundamental to supporting multiple email accounts per user, as it shifts the primary lookup key from user ID to email address.


20-37: Updated getAiUserWithTokens to use email-based queries

Similar to getAiUser, this function now:

  1. Queries by email on the emailAccount table
  2. Selects userId instead of id
  3. References the singular account relationship instead of an array of accounts

This maintains consistency with the overall architecture change.


43-43: Updated token access for singular account relationship

Changed from accessing tokens via an array index (accounts[0]) to a direct property access (account), reflecting the updated data model where each email account has a direct one-to-one relationship with an authentication account.

apps/web/utils/redis/reply.ts (3)

3-11: Updated Redis key generation to use email as identifier

The getReplyKey function now constructs keys using email addresses instead of user IDs, supporting the multi-email architecture and ensuring unique keys per email account rather than per user.


13-21: Updated getReply to use email-based parameters

The getReply function parameters and implementation now use email as the identifier instead of user ID, maintaining consistency with the key generation function.


23-35: Updated saveReply to use email-based parameters

The saveReply function now also uses email as the identifier instead of user ID, completing the refactoring of the Redis reply cache utilities to support the multi-email account architecture.

apps/web/utils/ai/assistant/process-user-request.ts (2)

354-358: Verify authorization check when deleting group items
deleteGroupItem is now called with userId: user.userId.
Please double‑check that deleteGroupItem internally verifies that the id
really belongs to the provided userId; otherwise an attacker could delete
another user’s pattern by supplying a foreign groupItem.id.
If that check is already in place – great! If not, consider enforcing it in
the DB WHERE clause (e.g. id = ? AND userId = ?).


50-50:

Details

❌ Incorrect review comment

Minor: ensure no lingering user.id references remain
The type change to UserEmailWithAI removes the id property. A quick
scan shows only user.userId is now used, but please run a repo search to
be safe.


🏁 Script executed:

#!/bin/bash
# List any remaining occurrences of "user.id" in this file
rg --line-number $'\buser\.id\b' apps/web/utils/ai/assistant/process-user-request.ts

Length of output: 83


🏁 Script executed:

#!/bin/bash
# Recursively search for any occurrences of `user.id` across all TypeScript files in the repo
rg --with-filename --line-number -g '*.{ts,tsx}' 'user\.id'

Length of output: 13241


No lingering user.id in process-user-request.ts
I ran a targeted search in apps/web/utils/ai/assistant/process-user-request.ts and confirmed there are zero user.id references—only user.userId is used. All other user.id matches in the repo pertain to session.user.id (NextAuth session objects), not the UserEmailWithAI type here. You can safely ignore the original suggestion to scan the entire repo.

Likely an incorrect or invalid review comment.

apps/web/utils/actions/categorize.ts (2)

90-98: Redis progress key switched to email – confirm consumers were updated
saveCategorizationTotalItems now stores progress under the email key.
Make sure all readers (e.g. /progress/route.ts) have been migrated;
otherwise the progress bar will always show 0 %.


121-128: Typed object now passed to categorizeSender – good change
Passing the fully‑typed emailAccount instead of a naked { id, about }
improves type‑safety and avoids redundant look‑ups.

apps/web/utils/actions/assess.ts (10)

22-23: Authentication now email-based, aligned with multi-email accounts support

The authentication check is now based on email rather than user ID, which is consistent with the PR objective of supporting multiple email accounts per user.


27-32: Database query refactored to use emailAccount table

This change shifts the data access pattern from the user table to the emailAccount table, using email as the primary key. This is critical for supporting multiple email accounts per user while maintaining proper data isolation.


35-35: Function parameter updated to use email instead of user ID

The saveBehaviorProfile function now takes email instead of user ID as a parameter, which aligns with the new identification pattern.


41-49: Updated saveBehaviorProfile to operate on emailAccount

The function signature and implementation now correctly use email as the identifier and update the emailAccount table instead of the user table. This is consistent with the architectural shift to email-centric data organization.


55-58: Authentication and user fetching updated to use email

Similar to the earlier change, authentication now validates using email, and the getAiUser function now accepts an object with email property instead of user ID.


61-64: Database query updated to use email as the primary key

The query to fetch the emailAccount is now using email as the key, which is consistent with the new data model.


97-99: Update operation uses email as identifier

The Prisma update operation now uses email in the where clause, ensuring consistency with the new data access patterns.


102-104: Account lookup switched to use email

The account lookup is now performed with email instead of user ID, maintaining consistency across all database operations.


108-108: Error logging updated to use email

Error logging now references email instead of user ID, making debugging more aligned with the new identification system.


112-119: EmailAccount creation includes both email and userId

The creation of a new EmailAccount record now correctly includes both the email and the user ID, establishing the proper relationship between the user and their email account.

apps/web/utils/actions/cold-email.ts (6)

31-32: Authentication check updated to use email

The authentication check now verifies the presence of session.user.email instead of session.user.id, aligning with the multi-email account architecture.


37-40: Database operation updated to target emailAccount by email

The update operation now correctly targets the emailAccount table using email as the identifier, instead of updating the user table by user ID.


48-57: Authentication and data updates refactored for email-based approach

Both the authentication check and database operations are consistently updated to use email as the primary identifier, which is crucial for the multi-email account support.


129-129: Function call updated to pass email

The call to checkColdEmail now passes the user's email instead of ID, ensuring consistency with the updated function signature.


135-151: Function signature and database query updated for email-based identification

The checkColdEmail function now:

  1. Takes email instead of userId as a parameter
  2. Queries the emailAccount table with email as the primary key
  3. Selects additional fields including userId and about
    This change is essential for supporting the new data model where multiple email accounts can belong to a single user.

168-168: Updated parameter to use emailAccount instead of user

The isColdEmail function now receives the emailAccount object instead of user, ensuring it works with the updated data structure.

apps/web/app/api/user/categorize/senders/batch/handle-batch.ts (7)

39-41: Request parameter extraction and logging updated to use email

The batch handler now correctly extracts email instead of userId from the request body and logs it accordingly, aligning with the new identification approach.


43-45: User validation and access check updated for email-based identification

The function now uses validateUserAndAiAccess({ email }) and appropriately renames the returned user context to emailAccount, reflecting the architectural change.


47-47: Using emailAccount.userId for backward compatibility

The function correctly uses emailAccount.userId when calling getCategories, maintaining backward compatibility with functions that still require a user ID.


51-53: Account lookup changed to use email as the primary key

The Prisma query now uses findUnique with email instead of findFirst with userId and provider, simplifying the lookup and aligning with the new data model.


90-94: Updated parameter name from user to emailAccount

The function call to categorizeWithAi now correctly passes the parameter as emailAccount instead of user, ensuring compatibility with the updated function signature.


102-102: Using emailAccount.userId for category update

Similar to line 47, this correctly uses emailAccount.userId for the updateSenderCategory function to maintain backward compatibility.


131-134: Progress tracking updated to use email

The saveCategorizationProgress function now uses email instead of userId for tracking categorization progress, completing the shift to email-based identification.

apps/web/utils/ai/choose-rule/match-rules.ts (3)

22-22: Updated type import to use the new UserEmailWithAI type

The import statement now correctly imports the UserEmailWithAI type, which consolidates user and AI fields that are now part of the EmailAccount entity.


206-210: Updated function signature to use the new user type

The findMatchingRule function signature now correctly uses UserEmailWithAI instead of the previous composite type, ensuring type safety with the new data model.


219-223: Consistent type update in all related functions

The findMatchingRuleWithReasons function also uses the UserEmailWithAI type, maintaining consistency across the codebase. This type change ensures that the function receives the correct user object structure with the fields it needs.

apps/web/utils/reply-tracker/outbound.ts (5)

19-23: Logging improvement: consistent structured logging

The logger initialization and context have been updated correctly to use email instead of userId. This aligns well with the PR's goal of supporting multiple email accounts per user.


25-30: Proper refactoring to email-based user identification

The function call has been correctly updated to use email-based identification instead of user ID, following the PR's goal of supporting multiple email accounts per user.


80-88: Proper adaptation to use userId from user object

The function now correctly uses user.userId where it previously likely used user.id, maintaining compatibility with the email account refactoring.


178-186: Well-structured refactoring to email-based identification

This function has been properly refactored to:

  1. Accept an object with an email property instead of a user ID
  2. Query the emailAccount table by email instead of the user table by ID

This change is consistent with the PR's goal of supporting multiple email accounts per user.


39-44:

Details

✅ Verification successful

Verify that user.userId is consistently available

The code now uses user.userId instead of what was likely user.id before. Ensure that this property is consistently available in the UserEmailWithAI type.


🏁 Script executed:

#!/bin/bash
# Check if UserEmailWithAI type has userId property
ast-grep --pattern 'type UserEmailWithAI = {
  $$$
  userId: $_
  $$$
}'

Length of output: 74


🏁 Script executed:

#!/bin/bash
# Locate the definition of UserEmailWithAI (either as a type alias or interface)
rg -n -e "type UserEmailWithAI" -e "interface UserEmailWithAI"

# If found, display the surrounding lines to confirm the presence of `userId`
rg -n -C3 "userId" $(rg -l -e "type UserEmailWithAI" -e "interface UserEmailWithAI")

Length of output: 456


All good – user.userId is defined
The UserEmailWithAI type in apps/web/utils/llms/types.ts is declared as:

export type UserEmailWithAI = Pick<EmailAccount, "userId" | "email" | "about"> & UserAIFields;

Since it explicitly picks userId from EmailAccount, the property will always be available. No further changes needed.

apps/web/utils/redis/clean.ts (5)

7-8: Key pattern updated correctly for email-based identification

The threadKey function now uses email instead of userId, which aligns with the PR's goal.


10-32: Well-structured refactoring to use email instead of userId

The saveThread function has been properly updated to:

  1. Accept email instead of userId
  2. Include email in the cleanThread object
  3. Call publishThread with the appropriate parameters

This matches the PR's goal of supporting multiple email accounts.


34-53: Good refactoring of function signature and implementation

The updateThread function has been improved by:

  1. Changing from positional parameters to a named object parameter
  2. Using email instead of userId for user identification
  3. Updating the call to getThread and publishThread accordingly

This makes the function more maintainable and aligns with the email-based identification pattern.


55-68: Properly updated function signature and implementation

The publishThread function has been appropriately refactored to use a single object parameter with email and thread, maintaining consistency with other functions in this file.


70-73: Function updated to use email instead of userId

The getThread function now correctly uses email for identifying users, consistent with the rest of the changes.

apps/web/app/api/google/watch/controller.ts (3)

10-28: Well-structured function signature and implementation refactoring

The watchEmails function has been properly refactored to:

  1. Accept an object parameter with email and gmail properties
  2. Update the database query to use emailAccount table by email
  3. Update logging to include email instead of userId

This aligns with the PR's goal of supporting multiple email accounts per user.


35-58: Consistent refactoring to email-based identification

The unwatchEmails function has been properly updated to use email instead of userId for parameter, logging, and error handling.


60-64: Database update correctly refactored

The database update operation now correctly targets the emailAccount table by email instead of the user table by ID.

apps/web/app/api/clean/route.ts (7)

26-43: Schema correctly updated for email-based identification

The cleanThreadBody schema has been properly updated to use email instead of userId, ensuring consistent parameter validation.


46-93: Comprehensive refactoring to use email instead of userId

The cleanThread function has been thoroughly updated to:

  1. Accept email instead of userId
  2. Use getAiUserWithTokens with email parameter
  3. Update logging to include email
  4. Call saveThread with email

These changes align well with the PR's goal of supporting multiple email accounts.


94-102: Function call correctly updated to use email

The call to getPublish now correctly passes email instead of userId.


216-230: Well-structured function signature refactoring

The getPublish function has been properly refactored to accept an object with email instead of userId, maintaining consistency with other function changes.


252-258: Logging correctly updated to use email

The logging statements have been properly updated to include email instead of userId for consistent tracking.


259-274: QStash key and updateThread call properly refactored

Both the QStash key and the call to updateThread have been correctly updated:

  1. QStash key now uses email instead of userId
  2. updateThread now takes an object with named properties instead of positional arguments

These changes align with the broader refactoring pattern.


276-277: Logging correctly updated

The final logging statement has been properly updated to include email instead of userId.

apps/web/utils/actions/ai-rule.ts (8)

60-62: Good refactoring to use emailAccount.

The change to retrieve data from the email account model with rules is appropriate for supporting multiple email accounts per user.


128-132: Well-structured data access pattern.

Using a dedicated helper function to fetch email account data with associated rules creates a consistent pattern for accessing user data throughout the application.


168-172: Consistent error handling for email account retrieval.

The error handling pattern for non-existent email accounts is consistent with other implementations in the file.


183-186: Correctly adapted rule creation for multi-email support.

The rule creation parameters now include the email account's userId, aligning with the database requirements while supporting the new multi-email architecture.


286-307: Consistent database query pattern.

The query to fetch email account data contains all necessary fields for AI operations and correctly selects user categories, maintaining functionality while supporting multi-email architecture.


488-492: Safe rule creation with consistent parameters.

The safeCreateRule call correctly provides the userId from the email account, maintaining proper database relationships.


496-499: Good adaptation of database operations.

EmailAccount updates now correctly target the email field instead of userId, properly supporting the multi-email design.


672-692: Well-designed helper function.

The getEmailAccountWithRules function centralizes the common data access pattern used throughout the file, reducing code duplication and ensuring consistent data retrieval.

apps/web/utils/redis/categorization-progress.ts (4)

10-12: Good Redis key structure for multi-email support.

The getKey function now correctly uses email as the unique identifier for Redis keys, which aligns with the multi-email account architecture.


14-23: Consistent function signature update.

The getCategorizationProgress function now accepts email instead of userId, maintaining consistency with the database access patterns throughout the application.


25-42: Proper Redis key generation in saveCategorizationTotalItems.

The function correctly uses the new email-based key structure and maintains the existing TTL settings.


44-63: Consistent parameter handling in saveCategorizationProgress.

The function maintains consistent behavior with the updated parameter structure, using email for key generation and progress retrieval.

apps/web/app/api/google/webhook/process-history.ts (10)

28-65: Good adaptation of account data retrieval.

The query has been appropriately updated to fetch from emailAccount instead of account, selecting all necessary fields including nested tokens and user data.


72-82: Premium status check properly adapted.

The premium check now correctly uses the emailAccount.user path to access premium data, maintaining the existing functionality.


83-89: Correctly updated unwatchEmails parameters.

The unwatchEmails call now passes specific token and email parameters from the emailAccount object, matching the expected parameters from the controller.


91-98: AI access checks properly adapted.

The code correctly accesses AI-related fields from the appropriate locations in the new data structure.


110-114: Consistent feature access validation.

The check for automation rules properly traverses the emailAccount.user path to access rules data.


119-125: Token validation properly adapted.

The token validation now correctly checks emailAccount.account paths for access and refresh tokens.


128-135: Gmail client initialization updated correctly.

The getGmailClientWithRefresh call now receives tokens from the emailAccount.account path.


143-148: History ID calculation properly updated.

The startHistoryId calculation now correctly accesses lastSyncedHistoryId from the emailAccount object.


172-182: Properly adapted process history parameters.

The processHistory call now passes the emailAccount object as the user context, providing all necessary data for processing.


267-271: Database update adapted correctly.

The updateLastSyncedHistoryId function now properly updates the emailAccount record instead of the user record.

apps/web/utils/actions/rule.ts (10)

58-59: Authentication check properly updated.

The authentication now verifies session?.user.email instead of session?.user.id to align with the multi-email account architecture.


112-112: Rule prompt file update adapted correctly.

The updatePromptFileOnRuleCreated call now passes email instead of userId, matching the updated function signature.


236-240: Consistent parameter passing for prompt updates.

The updatePromptFileOnRuleUpdated call now includes the email parameter while maintaining the existing rule parameters.


278-284: Updated parameter structure for instruction updates.

The updateRuleInstructionsAndPromptFile call now includes both email and userId, ensuring backward compatibility during the transition.


352-353: Authentication check consistent with multi-email design.

The authentication in deleteRuleAction now verifies email presence, aligning with the rest of the application.


370-382: Email account data retrieval adapted correctly.

The query now fetches from emailAccount by email, selecting all necessary fields for prompt file operations.


386-390: Rule deletion prompt update adapted correctly.

The generatePromptOnDeleteRule call now correctly passes the emailAccount object as the user parameter.


392-395: Database update correctly targets email account.

The prompt file update now correctly updates the emailAccount record by email instead of the user record by userId.


470-486: API payload correctly uses email-based identification.

The internal API call to process previous emails now includes email in the payload instead of userId, ensuring consistency across the application.


664-670: Rules prompt update correctly targets email account.

The final rules prompt update properly targets the emailAccount record by email, maintaining consistency with the rest of the application.

apps/web/app/api/google/watch/all/route.ts (1)

17-24: Lifetime / legacy‑premium users unintentionally excluded

The where clause only selects accounts where user.premium.lemonSqueezyRenewsAt is in the future.
Users on lifetime licences (lemonSqueezyRenewsAt = NULL) or custom tiers without renewal dates will silently be skipped and lose inbox‑watching.

Consider widening the predicate (e.g. OR lemonSqueezyRenewsAt IS NULL) or delegating the “is‑premium” check to a dedicated helper so all premium edge‑cases remain in one place.

apps/web/utils/upstash/categorize-senders.ts (1)

65-78: skipEmail ambiguity

skipEmail expects a raw email but comparison is against a queue name (queue.name). After hashing/sluggifying (see above), the skip logic will break. Accept skipQueueName instead or transform consistently.

-export async function deleteEmptyCategorizeSendersQueues({ skipEmail }: { skipEmail: string; }) {
+export async function deleteEmptyCategorizeSendersQueues({ skipQueueName }: { skipQueueName: string; }) {
   return deleteEmptyQueues({
     prefix: CATEGORIZE_SENDERS_PREFIX,
-    skipEmail,
+    skipQueueName,
   });
 }
apps/web/prisma/schema.prisma (2)

28-31: One Account now references both User and EmailAccount – ensure consistency

email String? @unique emailAccount EmailAccount? @relation(fields: [email], references: [email])

Because email is optional, nothing prevents an Account row from belonging to neither side or to both mismatched sides. Consider:

  1. Making email required when provider = "google" (if that’s the intent), or
  2. Adding a DB check / application‑level guard to keep userId & email in sync.

Otherwise orphaned records will accumulate after the multi‑account migration.


472-474: Referencing a non‑PK field is okay but watch for future changes

CleanupJob.emailAccount references EmailAccount.email. Once email becomes the primary key this is fine. If you decide to re‑introduce an id, remember to update both relation and foreign keys.

apps/web/app/api/ai/analyze-sender-pattern/route.ts (6)

23-26: Schema updated correctly to use email instead of userId.

The schema now uses email as the primary identifier instead of userId, which aligns with the goal of supporting multiple email accounts per user.


57-62: Good addition of email account existence check.

Adding this check is important since you're now fetching by email. The error handling with appropriate logging and 404 response is a good practice.


64-72: Consistent update to the newsletter query.

The update to use email_userId composite key is correct, maintaining the relationship between emails and users while supporting the multi-account model.


79-84: Consistent token access through email account.

Accessing tokens through emailAccount.account maintains the data hierarchy while supporting the multi-account structure.


129-136: Properly updated AI pattern detection call.

Passing the entire emailAccount object as user context provides all necessary information to the AI function while maintaining compatibility with the multi-account model.


296-326: Well-structured replacement of getUserWithRules with getEmailAccountWithRules.

The new function correctly queries by email instead of userId and includes all necessary nested relations. The selection of fields is comprehensive and aligns with the multi-account model.

apps/web/app/api/clean/gmail/route.ts (4)

16-25: Schema successfully updated to use email instead of userId.

The schema definition now uses email as the primary identifier, which aligns with the goal of supporting multiple email accounts per user.


38-43: Query updated to fetch emailAccount by email.

The database query has been correctly refactored to fetch the emailAccount by email instead of the account by userId, supporting the multi-account structure.


94-104: Function updated consistently with object parameters.

The function now uses email as a parameter and properly passes it to both updateThread and saveToDatabase, maintaining consistency throughout the codebase.


117-124: Connection logic updated to use email.

The database connection now uses email instead of id, which is consistent with the refactoring approach.

I noticed that the job connection syntax changed from { connect: { jobId } } to { connect: { id: jobId } }. This seems unrelated to the email refactoring but appears to be a fix for how Prisma connects related records.

apps/web/utils/ai/choose-rule/run-rules.ts (7)

7-7: Type updated to UserEmailWithAI.

Importing UserEmailWithAI instead of combining User & UserAIFields aligns with the multi-account model and makes the code more maintainable.


35-46: Function signature correctly updated to use UserEmailWithAI type.

The runRules function now accepts the UserEmailWithAI type, which is appropriate for supporting multiple email accounts per user.


50-55: Good refactoring to use object parameters.

Changing from positional arguments to an object with named parameters improves code readability and maintainability. The switch from user.id to user.email is consistent with the multi-account refactoring.


70-76: Consistent update to use user.userId.

The function call has been properly updated to use user.userId instead of user.id, which aligns with the UserEmailWithAI type.


251-261: Well-refactored function with clear parameter structure.

The analyzeSenderPatternIfAiMatch function has been properly refactored to use a typed object parameter instead of positional arguments, which improves code readability and type safety.


276-281: API call correctly updated to use email.

The analyzeSenderPattern call has been properly updated to use email instead of userId, which is consistent with the multi-account refactoring.


118-118:

Details

❓ Verification inconclusive

Verify if nullish check is necessary.

userEmail: user.email || "",

Consider if this nullish check is still necessary. If UserEmailWithAI guarantees that email is a required field, this check might be redundant.


🏁 Script executed:

#!/bin/bash
# Check if email is a required field in UserEmailWithAI type
rg -A 10 "export type UserEmailWithAI" --glob "*.ts"

Length of output: 209


🏁 Script executed:

#!/bin/bash
# Locate the definition of EmailAccount to check if `email` is optional or required
rg -n -A5 "export (interface|type) EmailAccount" --glob "*.ts"

Length of output: 64


#!/bin/bash
# Find where EmailAccount is imported in types.ts
rg -n "import.*EmailAccount" --glob "apps/web/utils/llms/types.ts"

#!/bin/bash
# Display the first 200 lines of the types file to locate EmailAccount definition
sed -n '1,200p' apps/web/utils/llms/types.ts

Unable to verify if email is always present
I couldn’t locate the EmailAccount definition in the repo, so it’s unclear whether its email field is optional. Please confirm if UserEmailWithAI (which picks "email" from EmailAccount) guarantees a non‑null, non‑undefined value:

  • If email is always set, you can remove the || "" fallback.
  • If it can be missing, keep the nullish check to avoid passing undefined downstream.

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: 4

🧹 Nitpick comments (2)
apps/web/prisma/migrations/20250420112535_email_account_settings/migration.sql (2)

10-18: Use IF EXISTS for dropping constraints and indexes.
To make this migration idempotent (and safe to re-run), add IF EXISTS clauses when dropping the foreign key and indexes. This prevents errors if the constraint/index is already gone.


124-136: Group the backfill updates into the same migration flow.
The separate UPDATE statements on Account and CleanupJob could leave data in a transient inconsistent state if run outside a single transaction. Consider moving these into the same transactional block or ordering them immediately after the insert step to maintain referential integrity.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between ce0eac4 and d6caf73.

📒 Files selected for processing (2)
  • apps/web/prisma/migrations/20250420112535_email_account_settings/migration.sql (1 hunks)
  • apps/web/prisma/schema.prisma (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/prisma/schema.prisma
🔇 Additional comments (2)
apps/web/prisma/migrations/20250420112535_email_account_settings/migration.sql (2)

50-61: Confirm foreign‐key referential actions match business rules.
The migration uses ON DELETE SET NULL for Account.email but ON DELETE CASCADE for CleanupJob.email. Ensure these behaviors align with expected data retention and cleanup policies.


47-49:

Details

✅ Verification successful

Verify no residual references to the dropped id column.
Removing EmailAccount.id may break code or foreign keys elsewhere. Search the codebase for any literal or inferred references before applying.


🏁 Script executed:

#!/bin/bash
# Description: Find references to `EmailAccount.id` in the codebase
rg -n "EmailAccount\\.id" -g "*.ts" -g "*.tsx" -g "*.js" -g "*.sql"

Length of output: 68


🏁 Script executed:

#!/bin/bash
# Check the Prisma schema for the EmailAccount model definition
rg -n "model EmailAccount" -g "*.prisma" -A10

# Find all TypeScript/TSX usages of the Prisma client’s emailAccount API
rg -n "prisma\.emailAccount" -g "*.ts" -g "*.tsx"

Length of output: 5875


🏁 Script executed:

#!/bin/bash
# Search for prisma.emailAccount queries that reference `id` in their `where` clause
rg -n "prisma\.emailAccount\.(findUnique|findFirst|findMany|update|upsert|delete)\(\s*\{[^}]*where:\s*\{[^}]*id:" -g "*.ts" -g "*.tsx"

# Look for any direct property access on `emailAccount.id`
rg -n "emailAccount\.id" -g "*.ts" -g "*.tsx"

Length of output: 184


No residual references to EmailAccount.id found
Verified via code search that no TypeScript, SQL, or Prisma queries reference the dropped id column. It’s safe to apply this migration.

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: 2

🧹 Nitpick comments (1)
apps/web/utils/auth.ts (1)

49-85: Well-structured implementation for multiple email accounts support

This is a good approach to support multiple email accounts per user. The code creates an EmailAccount record alongside the Account record during the linking process, establishing the necessary relationships between user, email, and account.

However, I would suggest a few improvements:

  1. Consider wrapping these database operations in a transaction to ensure atomicity:
-        // --- Step 1: Fetch the user to get their email ---
-        const user = await prisma.user.findUniqueOrThrow({
-          where: { id: account.userId },
-          select: { email: true },
-        });
-
-        // --- Step 2: Create the Account record ---
-        const createdAccount = await prisma.account.create({
-          data: {
-            ...account,
-            email: user.email,
-          },
-          select: { id: true },
-        });
-
-        // --- Step 3: Create the corresponding EmailAccount record ---
-        await prisma.emailAccount.create({
-          data: {
-            email: user.email,
-            userId: account.userId,
-            accountId: createdAccount.id,
-          },
-        });
+        await prisma.$transaction(async (tx) => {
+          // --- Step 1: Fetch the user to get their email ---
+          const user = await tx.user.findUniqueOrThrow({
+            where: { id: account.userId },
+            select: { email: true },
+          });
+
+          // --- Step 2: Create the Account record ---
+          const createdAccount = await tx.account.create({
+            data: {
+              ...account,
+              email: user.email,
+            },
+            select: { id: true },
+          });
+
+          // --- Step 3: Create the corresponding EmailAccount record ---
+          await tx.emailAccount.create({
+            data: {
+              email: user.email,
+              userId: account.userId,
+              accountId: createdAccount.id,
+            },
+          });
+        });
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between d6caf73 and 12a603d.

📒 Files selected for processing (6)
  • apps/web/app/(app)/premium/Pricing.tsx (2 hunks)
  • apps/web/app/(app)/settings/MultiAccountSection.tsx (2 hunks)
  • apps/web/app/(app)/usage/usage.tsx (1 hunks)
  • apps/web/components/PremiumAlert.tsx (2 hunks)
  • apps/web/hooks/useUser.ts (1 hunks)
  • apps/web/utils/auth.ts (2 hunks)
✅ Files skipped from review due to trivial changes (2)
  • apps/web/app/(app)/settings/MultiAccountSection.tsx
  • apps/web/app/(app)/premium/Pricing.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/web/app/(app)/usage/usage.tsx
  • apps/web/components/PremiumAlert.tsx
🧰 Additional context used
🧬 Code Graph Analysis (2)
apps/web/hooks/useUser.ts (1)
apps/web/app/api/user/me/route.ts (1)
  • UserResponse (7-7)
apps/web/utils/auth.ts (2)
apps/web/app/api/google/webhook/logger.ts (1)
  • logger (3-3)
apps/web/utils/error.ts (1)
  • captureException (60-72)
🔇 Additional comments (3)
apps/web/utils/auth.ts (3)

4-4: Good addition of the AdapterAccount type import

This type import is necessary for the custom linkAccount method signature.


77-82: Good error handling with contextual information

The error handling approach here is solid. It:

  1. Logs the error with contextual information using the scoped logger
  2. Captures the exception with additional context for monitoring
  3. Re-throws the error to maintain the original behavior

This approach helps with debugging while still preserving the expected error propagation.


62-64:

Details

✅ Verification successful

Verify Account model schema includes email field

The code is adding an email field to the Account record, but make sure the Prisma schema for Account has been updated to include this field.


🏁 Script executed:

#!/bin/bash
# Check if the Account model in the Prisma schema has an email field
grep -A 20 "model Account" $(fd --type f "schema.prisma" --exec echo {})

Length of output: 947


Account model email field verified
The apps/web/prisma/schema.prisma file’s Account model already includes:

email String? @unique

No further schema updates are needed.

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 (2)
apps/web/utils/swr.ts (2)

18-38: Good error normalization approach for SWR responses.

The error handling logic effectively normalizes both standard Error objects and non-Error SWR errors into a consistent format. This will make error handling more predictable throughout the application.

One minor issue: There's an extra blank line at line 40 that could be removed.

  }

-

  // Handle API error returned within data

49-57: Well-implemented successful data handling.

The function properly handles the success case, preserving the original data when no errors are detected. The type casting approach correctly filters out the API error possibility while maintaining type safety.

Consider adding handling for more complex API error structures that might occur in the future as your multi-account implementation expands.

  // No error found, return the data (might be null/undefined during loading)
  // Cast data to expected type, filtering out the TApiError possibility
  return {
    ...swrResult,
    data: data as TData | null, // SWR handles undefined during load
    error: undefined,
  } as SWRResponse<TData | null, NormalizedError>;
} 
+
+// Helper to check if a response is an API error
+export function isApiError<T>(data: T | { error: string }): data is { error: string } {
+  return data !== null && 
+         typeof data === 'object' && 
+         'error' in data && 
+         typeof (data as any).error === 'string';
+}
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 12a603d and b1635f6.

📒 Files selected for processing (3)
  • apps/web/app/(app)/license/page.tsx (2 hunks)
  • apps/web/hooks/useUser.ts (1 hunks)
  • apps/web/utils/swr.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/web/app/(app)/license/page.tsx
  • apps/web/hooks/useUser.ts
🔇 Additional comments (2)
apps/web/utils/swr.ts (2)

1-17: Well-structured type definitions and documentation.

The type definitions and JSDoc comments are clear and informative, providing good context for the utility function. The generic type parameters are appropriately defined with sensible defaults.


41-48: Effective API error detection and normalization.

The API error handling logic correctly identifies error responses that might be returned as data objects containing an error property, ensuring consistent error formats throughout the application.

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: 3

🧹 Nitpick comments (3)
apps/web/prisma/schema.prisma (3)

28-29: Consider making the inverse relation explicit
Prisma can infer the one‑to‑one between Account.emailAccount and EmailAccount.accountId, but declaring the same @relation(name: ...) on both sides improves clarity and prevents accidental drift if you rename fields or adjust cascade rules.


55-79: Clean up deprecated fields in User
You’ve marked a large number of fields as // deprecated. Leaving them in place indefinitely clutters the schema and makes it harder to see which fields are actively used. Consider planning a follow‑up migration to remove these columns or standardizing your deprecation comments (e.g., linking to a GitHub issue).

Also applies to: 81-81


470-472: Review CleanupJob relation redundancy
You’ve added email → EmailAccount.email alongside the existing userId → User.id. If cleanup jobs should track per‐account settings only, consider dropping userId or enforcing it to match emailAccount.userId to avoid data divergence.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between b1635f6 and 2ab1949.

📒 Files selected for processing (3)
  • apps/web/prisma/migrations/20250420131728_email_account_settings/migration.sql (1 hunks)
  • apps/web/prisma/schema.prisma (4 hunks)
  • apps/web/utils/auth.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/utils/auth.ts
🔇 Additional comments (3)
apps/web/prisma/schema.prisma (2)

116-133: New settings fields need matching ADD COLUMNs
Fields like about, behaviorProfile, aiProvider, etc., are introduced on EmailAccount but aren’t present in the migration’s ALTER TABLE for EmailAccount. Every field here needs a corresponding ADD COLUMN.


146-146: Index on lastSummaryEmailAt
Excellent addition—indexing this timestamp will speed up queries for summary scheduling.

apps/web/prisma/migrations/20250420131728_email_account_settings/migration.sql (1)

52-53: Verify foreign key order and presence of columns
You later add a foreign key on EmailAccount.accountId and one on CleanupJob.email, but if those columns were never created or populated, the constraints will fail. Please ensure:

  1. ADD COLUMN "accountId" is executed before ALTER TABLE ADD CONSTRAINT ... FOREIGN KEY ("accountId").
  2. ADD COLUMN "email" and backfill of CleanupJob.email complete successfully before setting NOT NULL and adding the FK.

Also applies to: 116-127

@elie222 elie222 merged commit 59a32e0 into main Apr 20, 2025
2 of 4 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Aug 14, 2025
@coderabbitai coderabbitai bot mentioned this pull request Aug 28, 2025
@coderabbitai coderabbitai bot mentioned this pull request Nov 8, 2025
@elie222 elie222 deleted the multi-email-account branch December 18, 2025 23:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments