Skip to content

WIP: Move over relations to emailAccount #412

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 156 commits into
base: main
Choose a base branch
from
Open

WIP: Move over relations to emailAccount #412

wants to merge 156 commits into from

Conversation

elie222
Copy link
Owner

@elie222 elie222 commented Apr 20, 2025

Summary by CodeRabbit

  • New Features

    • Introduced multi-account support with an account switcher, allowing users to manage and switch between multiple email accounts.
    • Added account-specific context across the app, ensuring all actions and data are scoped to the selected account.
  • Improvements

    • Authentication and data access are now consistently based on email account rather than user ID, improving reliability for multi-account scenarios.
    • Refactored API routes and internal logic to use middleware-based authentication with direct email context, simplifying session handling.
    • Enhanced error handling and feedback with unified patterns checking server error properties and improved toast notifications.
    • Updated import paths and component structures to support account-scoped routing and features.
    • Centralized asynchronous action handling using useAction hooks bound with user email for consistent state and error management.
  • Bug Fixes

    • Fixed inconsistencies in user identification across API endpoints and UI components by standardizing on email account context.
  • Documentation

    • Updated and added guidelines for custom hooks, server actions, and data-fetching best practices.
  • Chores

    • Updated dependencies and refactored internal logic for better maintainability and scalability.

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 ✅ Ready (Inspect) Visit Preview Apr 28, 2025 9:57pm

Copy link
Contributor

coderabbitai bot commented Apr 20, 2025

Important

Review skipped

More than 25% of the files skipped due to max files limit. The review is being skipped to prevent a low-quality review.

122 files out of 274 files are above the max files limit of 100. Please upgrade to Pro plan to get higher limits.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This update implements a comprehensive migration from user-centric to email-account-centric data models and logic throughout the web application. All API routes, server actions, database queries, and React components are refactored to use email account identifiers (typically the user's email address or an account ID) instead of user IDs. Authentication and authorization flows are unified using a new withAuth middleware, replacing session-based checks and manual error handling. Numerous function signatures and component props are updated to accept email or email account IDs. The database schema is overhauled to associate all relevant entities with EmailAccount rather than User. Additionally, the UI and hooks are updated to manage and switch between multiple accounts, and many internal utilities and hooks are introduced or refactored to support this new model.

Changes

File(s) / Area Change Summary
Database & Models
apps/web/prisma/schema.prisma
Refactored all relevant models to associate with EmailAccount instead of User; updated foreign keys, relations, and unique constraints accordingly.
API Authentication & Routing
apps/web/app/api/**/*
Replaced session-based authentication and withError middleware with withAuth middleware; updated all handler signatures and internal logic to use email/emailAccountId for identification and queries.
Account Management
apps/web/app/api/user/accounts/route.ts
apps/web/hooks/useAccounts.ts
apps/web/providers/AccountProvider.tsx
apps/web/components/AccountSwitcher.tsx
Introduced API route, hook, provider, and UI for managing and switching between multiple user accounts; all use email/accountId as the primary identifier.
Actions & Utilities
apps/web/utils/account.ts
apps/web/utils/actions/**/*
Refactored all server actions and utilities to use email/accountId parameters; replaced manual session fetching with context-aware hooks and clients.
React Components & Hooks
apps/web/components//*
apps/web/hooks/
/
apps/web/providers/**/
Updated all components, hooks, and providers to use the new account context, removing reliance on session or userId; added account switcher UI and hotkey support.
App Pages & Layouts
apps/web/app/(app)//*
apps/web/app/(landing)/
/*
Refactored all pages to use account-based routing and context; updated import paths to reflect new [account]-scoped structure; removed session-based authentication in favor of parameter-driven or middleware-based approaches.
API & Controller Logic
apps/web/app/api/**/*
Updated all controller and helper functions to use email/accountId; revised queries, function signatures, and data flows accordingly.
Store & Queue Logic
apps/web/store/**/*
Updated queue and store logic to pass email/accountId throughout; refactored action invocation signatures.
Documentation & Snippets
.cursor/rules/*.mdc
.vscode/typescriptreact.code-snippets
Updated documentation and code snippets to reflect new authentication and data access patterns; removed outdated guidelines and examples.
Tests
apps/web/tests/*
Updated test helpers and mock data to use emailAccountId instead of userId.
Miscellaneous
apps/web/package.json
Added new dependency: next-safe-action.
Deleted Files
apps/web/app/api/google/labels/create/route.ts
apps/web/app/api/google/threads/archive/controller.ts
apps/web/app/api/google/threads/archive/route.ts
apps/web/app/(app)/PermissionsCheck.tsx
apps/web/app/(app)/usage/page.tsx
.cursor/rules/api-routes.mdc
Removed obsolete files related to deprecated user/session-based logic or superseded by new account-based implementations.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI (React)
    participant useAccount Provider
    participant API Route (withAuth)
    participant Database

    User->>UI (React): Interacts (e.g., switch account, fetch data)
    UI (React)->>useAccount Provider: Get/set current account (email/accountId)
    UI (React)->>API Route (withAuth): Make API call with account context
    API Route (withAuth)->>API Route (withAuth): Extract email/accountId from request
    API Route (withAuth)->>Database: Query/update using email/accountId
    Database-->>API Route (withAuth): Return data
    API Route (withAuth)-->>UI (React): Respond with data
    UI (React)-->>User: Render updated UI
Loading

Possibly related PRs

  • Email accounts #410: Both PRs implement foundational changes to support multiple email accounts per user, shifting data models and logic from user-centric to email-account-centric identifiers.
  • Add unsubscribe link to email update #321: Both PRs modify the resend/summary email API logic, refactoring authentication and data access to use email account-centric models and updating function signatures accordingly.
  • Simplify fix rule flow #277: Both PRs refactor AI rule actions, updating parameter names from userId to emailAccountId/email and modifying related helper logic for AI mistake reporting and rule processing.

Poem

Hopping through accounts, a rabbit finds,
Email, not user, now guides the lines.
With a switch and a click, identities change,
Queries and actions all rearrange.
No more userId in the burrow's deep,
Just email accounts, and secrets to keep.
🐇✨ The warren's grown smarter—time for a leap!

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • 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 generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

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

CodeRabbit Configuration File (.coderabbit.yaml)

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

Documentation and Community

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🔭 Outside diff range comments (5)
apps/web/utils/redis/clean.ts (1)

110-113: ⚠️ Potential issue

Inconsistency in deleteAllUserData function

While getThreadsByJobId has been updated to use emailAccountId, the deleteAllUserData function still uses userId as the parameter and in the Redis key pattern. This is inconsistent with the rest of the refactoring.

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

50-56: 💡 Verification agent

🧩 Analysis chain

Inconsistent use of userId in getUncategorizedSenders function.

While most of the file has been updated to use email-based identification, this function still uses userId. Consider updating this for consistency.

 async function getUncategorizedSenders(offset: number) {
   const result = await getSenders({
-    userId: user.id,
+    email: user.email,
     limit: LIMIT,
     offset,
   });

Note: This would require updating the signature of the getSenders function as well.


🏁 Script executed:

#!/bin/bash
# Find the getSenders function definition to see if it accepts email parameter
rg -A 5 "getSenders\s*=?\s*\(?\s*\{" apps/web/app/api/user/categorize/senders/uncategorized/get-senders.ts

Length of output: 205


Update getUncategorizedSenders and getSenders to use email instead of userId

To keep identification consistent across the file, you’ll need to adjust both the call site in categorize.ts and the getSenders API signature:

• apps/web/utils/actions/categorize.ts (around Lines 50–56)

 async function getUncategorizedSenders(offset: number) {
   const result = await getSenders({
 -   userId: user.id,
 +   email:  user.email,
     limit: LIMIT,
     offset,
   });

• apps/web/app/api/user/categorize/senders/uncategorized/get-senders.ts

-export async function getSenders({
-  userId,
+export async function getSenders({
+  email,
   offset = 0,
   limit  = 100,
 }: {
-  userId: string;
+  email:  string;
 }) {
   // …implementation…  

Make sure to update any internal logic in getSenders to query by email rather than userId.

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

38-44: ⚠️ Potential issue

Inconsistent parameter in resolveReplyTrackers call.

While the rest of the file has been updated to use email-based identification, this function call still passes user.userId. Consider updating this to use user.email for consistency with the updated function signature.

 await resolveReplyTrackers(
   gmail,
-  user.userId,
+  user.email,
   message.threadId,
   needsReplyLabelId,
 );
apps/web/app/api/resend/summary/route.ts (2)

218-236: ⚠️ Potential issue

Duplicate function identifier causes compilation error

sendEmail is declared at line 24 and redeclared here inside the same scope.
In ESM/TypeScript (strict mode) this is a duplicate lexical declaration and will fail to compile.

Rename the inner helper:

-async function sendEmail({ email }: { email: string }) {
+async function sendSummaryResend({ email }: { email: string }) {

and update the single call at line 239 accordingly.


238-244: 🛠️ Refactor suggestion

Call site still references shadowed name

After renaming the inner helper you’ll need:

-shouldSendEmail ? sendEmail({ email }) : Promise.resolve(),
+shouldSendEmail ? sendSummaryResend({ email }) : Promise.resolve(),

Failure to update will lead to an unresolved identifier once the duplicate definition is removed.

🧹 Nitpick comments (19)
apps/web/app/api/user/draft-actions/route.ts (1)

10-13: Authentication updated consistently

Good update changing the authentication check from user.id to user.email, aligning with the email-account-centric model. Consider adding validation to ensure the email is properly formatted.

Consider adding email format validation:

const session = await auth();
const email = session?.user.email;
-if (!email)
+if (!email || !isValidEmail(email))
  return NextResponse.json({ error: "Not authenticated" }, { status: 401 });
apps/web/app/(app)/reply-zero/page.tsx (1)

116-118: Update commented code for consistency

Although it's commented out, the NeedsAction component reference still uses userId and userEmail. For consistency, this should be updated to use only email.

{/* <TabsContent value="needsAction" className="mt-0 flex-1">
-  <NeedsAction userId={userId} userEmail={userEmail} page={page} />
+  <NeedsAction email={email} page={page} />
</TabsContent> */}
apps/web/utils/ai/assistant/process-user-request.ts (2)

629-639: Unused userId parameter in function definition

The getUpdateCategoryTool function still takes a userId parameter but doesn't use it in the implementation. Consider removing this unused parameter.

-const getUpdateCategoryTool = (
-  userId: string,
+const getUpdateCategoryTool = (
   categories: Pick<Category, "id" | "name">[],
   loggerOptions: {
     userId: string;
     email: string | null;
     messageId?: string | null;
     threadId?: string | null;
   },
   userEmail: string,
 ) =>

162-167: Consider updating loggerOptions to remove userId

While the code will work fine, for consistency with the email-account-centric model, consider updating loggerOptions to focus on email rather than userId.

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

437-438: Unused userId in authentication block

There's an unused userId variable that's extracted from the session but never used. Consider removing it.

  const session = await auth();
  const email = session?.user.email;
  if (!email) return { error: "Not logged in" };
- const userId = session?.user.id;
apps/web/scripts/migrateRedisPlansToPostgres.ts (1)

158-159: Minor nit: use object spreading instead of repeating emailAccountId

You set emailAccountId twice in the two create blocks. To keep both branches in sync, consider pulling the common fields into a helper:

 const baseData = {
   threadId: plan.threadId,
   messageId: plan.messageId,
   emailAccountId: emailAccount.email,
 };

-// inside “plan.rule” branch
+// inside “plan.rule” branch
 await prisma.executedRule.create({
   data: {
-    threadId: plan.threadId,
-    messageId: plan.messageId,
+    ...baseData,
     …
   },
 });

-// inside “else” branch
-const data = {
-  threadId: plan.threadId,
-  messageId: plan.messageId,
-  status: "SKIPPED" as const,
-  automated: false,
-  emailAccountId: emailAccount.email,
-};
+const data = { ...baseData, status: "SKIPPED" as const, automated: false };

This removes duplication and guarantees the two paths never diverge accidentally.

Also applies to: 208-209

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

49-61: Gracefully handle missing relations

emailAccount.rules is guaranteed to be an array, but coldEmailBlocker can be null.
If you ever introduce partial selects (e.g., feature flags) Prisma may return undefined for unselected fields. A defensive pattern avoids runtime surprises:

-const emailAccount = await prisma.emailAccount.findUnique({
+const emailAccount = await prisma.emailAccount.findUnique({
   where: { email },
   select: {
     rules: { … },
     coldEmailBlocker: true,
   },
 });
-if (!emailAccount) return undefined;
+if (!emailAccount) return undefined;
+
+const { rules = [], coldEmailBlocker = null } = emailAccount;

Then use rules / coldEmailBlocker downstream.


64-72: Minor optimisation: compute look‑ups only once

getRuleSetting scans the rules array for every system type.
For ~5 categories this is fine, but as the list grows you’ll traverse the array N×M times.

A quick win:

const rulesBySystem = new Map(
  emailAccount.rules.map((r) => [r.systemType, r]),
);

const getSetting = (system: SystemType) =>
  getRuleSetting(system, rulesBySystem.get(system));

Not blocking, just a future scalability note.

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

185-186: Consistency between create / update helpers

safeCreateRule, safeUpdateRule, and downstream helpers all expect email (not emailAccountId).
That’s fine, but make sure all callers switched; a lingering userId would silently create orphaned rows.

Consider adding a runtime assertion inside helpers:

invariant(email.includes("@"), "email param must be an email address");

to catch accidental IDs early.

Also applies to: 461-466, 493-495


245-248: Prefer update over updateMany for single‑row ops

updateMany ignores the case where zero rows match, hiding auth mistakes and typos.
update would surface a P2025 error in that scenario, which you could translate into a 404.


617-639: emailAccountId vs email variable naming

Inside reportAiMistakeAction you introduce:

const emailAccountId = session?.user.email;

This is actually the email string, not the database ID.
Rename to email (or emailAddress) to avoid confusion:

-const emailAccountId = session?.user.email;
-if (!emailAccountId) …
+const email = session?.user.email;
+if (!email) …

Then update subsequent occurrences.


687-690: Eager‑loading categoryFilters may explode row size

include: { actions: true, categoryFilters: true } pulls two potentially large relations for every enabled rule, even in flows that never touch them.

If you only need actions, drop categoryFilters; otherwise request specific fields:

-include: { actions: true, categoryFilters: true }
+include: {
+  actions: true,
+  categoryFilters: { select: { id: true, categoryId: true } },
+}

This keeps the response payload predictable and lowers the chances of hitting the 4 MB JSON limit on Vercel/Postgres.

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

17-22: Consider updating the ProcessAssistantEmailArgs interface in a future PR.

The interface still includes userId: string while the implementation now primarily uses email-based identification. While this maintains backward compatibility during the transition, it would be cleaner to update this interface once the refactoring is complete.

apps/web/utils/reply-tracker/draft-tracking.ts (1)

24-28: Unused userId variable in trackSentDraftStatus function.

The function extracts userId from user.id but after refactoring it's only used in the logger options. Consider removing it if no longer needed or keeping only email as the identifier.

-  const { id: userId, email: userEmail } = user;
+  const { email: userEmail } = user;
   const { threadId, id: sentMessageId, textPlain: sentTextPlain } = message;

-  const loggerOptions = { userId, userEmail, threadId, sentMessageId };
+  const loggerOptions = { userEmail, threadId, sentMessageId };
apps/web/utils/actions/categorize.ts (2)

80-83: Inconsistent use of userId in logging.

While most of the file has been updated to use email-based identification, the logging still uses userId. Consider updating this for consistency.

 logger.trace("Got uncategorized senders", {
-  userId: user.id,
+  email: user.email,
   uncategorizedSenders: newUncategorizedSenders.length,
 });

103-106: Inconsistent use of userId in logging.

Similar to the previous comment, consider updating this log to use email-based identification for consistency.

 logger.info("Queued senders for categorization", {
-  userId: user.id,
+  email: user.email,
   totalUncategorizedSenders,
 });
apps/web/utils/reply-tracker/inbound.ts (1)

32-37: Prefer accurate field naming in structured logs

The logger enriches context with email: emailAccountId.
Because emailAccountId now stores a stringified primary‑key (the user’s email) rather than a numeric/UUID id, tagging it as email is ambiguous – future readers may mistake it for the sender address.
Consider either:

-  const logger = createScopedLogger("reply-tracker/inbound").with({
-    email: emailAccountId,
+  const logger = createScopedLogger("reply-tracker/inbound").with({
+    emailAccountId,

or renaming the variable throughout to email for consistency.

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

67-70: Identifier confusion: emailAccountId holds an email string

Throughout the refactor a raw email string is now passed as emailAccountId.
Consider renaming the field (and composite index) to simply email to avoid mental overhead and mismatched expectations between “id” (usually opaque) and a public address.


148-149: Consistent parameter naming

savePatternCheck({ emailAccountId: emailAccount.email, ... }) again mixes concepts. If you keep the string‑id design, rename the function argument to email or accountEmail for clarity. This small change reduces future slip‑ups when the real numeric/UUID id is re‑introduced.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 441a347 and 4de18e3.

📒 Files selected for processing (61)
  • apps/web/app/(app)/automation/onboarding/page.tsx (3 hunks)
  • apps/web/app/(app)/automation/page.tsx (2 hunks)
  • apps/web/app/(app)/automation/rule/[ruleId]/page.tsx (1 hunks)
  • apps/web/app/(app)/clean/helpers.ts (1 hunks)
  • apps/web/app/(app)/clean/page.tsx (1 hunks)
  • apps/web/app/(app)/clean/run/page.tsx (1 hunks)
  • apps/web/app/(app)/reply-zero/AwaitingReply.tsx (2 hunks)
  • apps/web/app/(app)/reply-zero/NeedsReply.tsx (2 hunks)
  • apps/web/app/(app)/reply-zero/ReplyTrackerEmails.tsx (5 hunks)
  • apps/web/app/(app)/reply-zero/Resolved.tsx (4 hunks)
  • apps/web/app/(app)/reply-zero/fetch-trackers.ts (3 hunks)
  • apps/web/app/(app)/reply-zero/page.tsx (5 hunks)
  • apps/web/app/(app)/setup/page.tsx (1 hunks)
  • apps/web/app/(app)/smart-categories/page.tsx (1 hunks)
  • apps/web/app/(app)/smart-categories/setup/page.tsx (1 hunks)
  • apps/web/app/api/ai/analyze-sender-pattern/route.ts (8 hunks)
  • apps/web/app/api/clean/gmail/route.ts (1 hunks)
  • apps/web/app/api/google/labels/create/controller.ts (2 hunks)
  • apps/web/app/api/google/webhook/process-history.ts (3 hunks)
  • apps/web/app/api/reply-tracker/disable-unused-auto-draft/route.ts (6 hunks)
  • apps/web/app/api/resend/summary/route.ts (7 hunks)
  • apps/web/app/api/user/categories/route.ts (1 hunks)
  • apps/web/app/api/user/categorize/senders/batch/handle-batch.ts (2 hunks)
  • apps/web/app/api/user/draft-actions/route.ts (1 hunks)
  • apps/web/app/api/user/group/[groupId]/items/route.ts (2 hunks)
  • apps/web/app/api/user/group/route.ts (2 hunks)
  • apps/web/app/api/user/rules/[id]/route.ts (2 hunks)
  • apps/web/app/api/user/rules/route.ts (2 hunks)
  • apps/web/prisma/schema.prisma (14 hunks)
  • apps/web/scripts/migrateRedisPlansToPostgres.ts (4 hunks)
  • apps/web/utils/actions/ai-rule.ts (19 hunks)
  • apps/web/utils/actions/categorize.ts (7 hunks)
  • apps/web/utils/actions/clean.ts (2 hunks)
  • apps/web/utils/actions/cold-email.ts (2 hunks)
  • apps/web/utils/actions/group.ts (5 hunks)
  • apps/web/utils/actions/knowledge.ts (3 hunks)
  • apps/web/utils/actions/mail.ts (3 hunks)
  • apps/web/utils/actions/permissions.ts (1 hunks)
  • apps/web/utils/actions/reply-tracking.ts (1 hunks)
  • apps/web/utils/actions/rule.ts (15 hunks)
  • apps/web/utils/actions/unsubscriber.ts (1 hunks)
  • apps/web/utils/actions/user.ts (1 hunks)
  • apps/web/utils/ai/actions.ts (1 hunks)
  • apps/web/utils/ai/assistant/process-user-request.ts (4 hunks)
  • apps/web/utils/assistant/process-assistant-email.ts (5 hunks)
  • apps/web/utils/categorize/senders/categorize.ts (7 hunks)
  • apps/web/utils/category.server.ts (2 hunks)
  • apps/web/utils/group/group-item.ts (1 hunks)
  • apps/web/utils/redis/clean.ts (1 hunks)
  • apps/web/utils/redis/reply-tracker-analyzing.ts (1 hunks)
  • apps/web/utils/reply-tracker/check-previous-emails.ts (1 hunks)
  • apps/web/utils/reply-tracker/draft-tracking.ts (2 hunks)
  • apps/web/utils/reply-tracker/enable.ts (3 hunks)
  • apps/web/utils/reply-tracker/generate-draft.ts (1 hunks)
  • apps/web/utils/reply-tracker/inbound.ts (6 hunks)
  • apps/web/utils/reply-tracker/outbound.ts (4 hunks)
  • apps/web/utils/rule/prompt-file.ts (1 hunks)
  • apps/web/utils/rule/rule.ts (7 hunks)
  • apps/web/utils/sender.ts (1 hunks)
  • apps/web/utils/unsubscribe.ts (1 hunks)
  • apps/web/utils/user/delete.ts (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (23)
apps/web/app/(app)/clean/page.tsx (1)
apps/web/app/(app)/clean/helpers.ts (1)
  • getLastJob (15-20)
apps/web/app/(app)/smart-categories/setup/page.tsx (1)
apps/web/utils/category.server.ts (1)
  • getUserCategories (13-18)
apps/web/app/api/user/categorize/senders/batch/handle-batch.ts (1)
apps/web/utils/categorize/senders/categorize.ts (1)
  • getCategories (143-147)
apps/web/utils/actions/unsubscriber.ts (2)
apps/web/utils/actions/unsubscriber.validation.ts (1)
  • setNewsletterStatusBody (4-7)
apps/web/utils/email.ts (1)
  • extractEmailAddress (16-36)
apps/web/app/api/user/categories/route.ts (3)
apps/web/utils/categorize/senders/categorize.ts (1)
  • getCategories (143-147)
apps/web/utils/category.server.ts (1)
  • getUserCategories (13-18)
apps/web/utils/middleware.ts (1)
  • withError (16-70)
apps/web/app/(app)/smart-categories/page.tsx (1)
apps/web/utils/category.server.ts (1)
  • getUserCategoriesWithRules (20-31)
apps/web/app/(app)/clean/run/page.tsx (3)
apps/web/utils/redis/clean.ts (1)
  • getThreadsByJobId (75-108)
apps/web/app/(app)/clean/helpers.ts (2)
  • getJobById (3-13)
  • getLastJob (15-20)
apps/web/components/ui/card.tsx (1)
  • CardTitle (112-112)
apps/web/app/api/google/labels/create/controller.ts (3)
apps/web/utils/error.ts (1)
  • SafeError (87-95)
apps/web/utils/gmail/client.ts (1)
  • getGmailClient (28-33)
apps/web/utils/gmail/label.ts (1)
  • getOrCreateLabel (270-282)
apps/web/app/(app)/reply-zero/NeedsReply.tsx (2)
apps/web/app/(app)/reply-zero/date-filter.ts (1)
  • TimeRange (1-1)
apps/web/app/(app)/reply-zero/fetch-trackers.ts (1)
  • getPaginatedThreadTrackers (7-59)
apps/web/app/(app)/reply-zero/page.tsx (1)
apps/web/utils/redis/reply-tracker-analyzing.ts (1)
  • isAnalyzingReplyTracker (18-22)
apps/web/utils/actions/knowledge.ts (1)
apps/web/utils/actions/knowledge.validation.ts (3)
  • createKnowledgeBody (3-6)
  • updateKnowledgeBody (10-14)
  • deleteKnowledgeBody (18-20)
apps/web/utils/actions/group.ts (2)
apps/web/utils/actions/group.validation.ts (1)
  • createGroupBody (4-6)
apps/web/utils/group/group-item.ts (1)
  • deleteGroupItem (21-31)
apps/web/app/(app)/reply-zero/AwaitingReply.tsx (2)
apps/web/app/(app)/reply-zero/date-filter.ts (1)
  • TimeRange (1-1)
apps/web/app/(app)/reply-zero/fetch-trackers.ts (1)
  • getPaginatedThreadTrackers (7-59)
apps/web/utils/actions/rule.ts (2)
apps/web/utils/actions/rule.validation.ts (2)
  • updateRuleSettingsBody (154-157)
  • enableDraftRepliesBody (160-160)
apps/web/utils/rule/rule.ts (1)
  • deleteRule (207-223)
apps/web/utils/sender.ts (1)
apps/web/utils/email.ts (1)
  • extractEmailAddress (16-36)
apps/web/utils/actions/reply-tracking.ts (2)
apps/web/utils/redis/reply-tracker-analyzing.ts (2)
  • startAnalyzingReplyTracker (7-11)
  • stopAnalyzingReplyTracker (13-16)
apps/web/app/api/google/webhook/logger.ts (1)
  • logger (3-3)
apps/web/utils/actions/ai-rule.ts (3)
apps/web/utils/rule/rule.ts (2)
  • deleteRule (207-223)
  • safeUpdateRule (77-117)
apps/web/utils/category.server.ts (1)
  • getUserCategoriesForNames (33-54)
apps/web/utils/user/get.ts (1)
  • getAiUser (4-18)
apps/web/utils/assistant/process-assistant-email.ts (1)
apps/web/utils/email.ts (1)
  • extractEmailAddress (16-36)
apps/web/utils/reply-tracker/draft-tracking.ts (1)
apps/web/app/api/google/webhook/logger.ts (1)
  • logger (3-3)
apps/web/app/api/resend/summary/route.ts (2)
apps/web/utils/gmail/message.ts (1)
  • getMessagesBatch (57-107)
apps/web/utils/unsubscribe.ts (1)
  • createUnsubscribeToken (5-21)
apps/web/utils/rule/rule.ts (4)
apps/web/utils/ai/rule/create-rule-schema.ts (1)
  • CreateOrUpdateRuleSchemaWithCategories (128-131)
apps/web/utils/category.server.ts (1)
  • getUserCategoriesForNames (33-54)
apps/web/app/api/google/webhook/logger.ts (1)
  • logger (3-3)
apps/web/utils/prisma.ts (1)
  • isDuplicateError (18-27)
apps/web/app/api/ai/analyze-sender-pattern/route.ts (2)
apps/web/utils/email.ts (1)
  • extractEmailAddress (16-36)
apps/web/app/api/google/webhook/logger.ts (1)
  • logger (3-3)
apps/web/utils/categorize/senders/categorize.ts (2)
apps/web/utils/category.server.ts (1)
  • getUserCategories (13-18)
apps/web/utils/email.ts (1)
  • extractEmailAddress (16-36)
🔇 Additional comments (212)
apps/web/app/(app)/smart-categories/setup/page.tsx (1)

12-12: Function call correctly updated to use email-based identification.

The call to getUserCategories has been properly modified to pass an object with the email property instead of a user ID, aligning with the updated implementation in apps/web/utils/category.server.ts that now filters categories by emailAccountId.

apps/web/app/(app)/clean/page.tsx (2)

9-10: Authentication check correctly updated to use email.

The authentication check has been properly updated to extract and verify the user's email rather than the user ID, consistent with the broader shift to email-account-centric identification.


12-12: Function call correctly updated to use email parameter.

The call to getLastJob has been properly modified to pass an object with the email property rather than a user ID, matching the updated implementation shown in the context snippet that now queries by email instead of user ID.

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

141-141: Database query correctly updated to use email as identifier.

The Prisma query for fetching knowledge base entries has been properly updated to filter by emailAccountId using the user's email instead of user ID. This change aligns with the broader refactoring to move relations from the User model to the EmailAccount model.

apps/web/app/(app)/automation/page.tsx (2)

26-27: Authentication check correctly updated to use email.

The authentication check has been properly updated to extract the user's email from the session and redirect to the login page if the email is missing, replacing the previous check that likely verified the user ID.


36-36: Database query correctly updated to use email as identifier.

The Prisma query for checking if a rule exists has been properly updated to filter by emailAccountId using the user's email instead of user ID. This change aligns with the broader refactoring to move relations from the User model to the EmailAccount model.

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

117-124: Updated database association to use emailAccount instead of user

The change properly updates the database association from user to emailAccount, which aligns with the broader refactoring to shift from a user-centric model to an email-account-centric model.

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

47-47: Parameter structure change to match function signature

This change correctly updates the call to getCategories to use an object with an email property, matching the function signature shown in the relevant code snippet. This is aligned with the email-account-centric model refactoring.


108-108: Changed from userId to userEmail for sender category update

Properly updated to use the email as identifier instead of userId, consistent with the broader refactoring effort.

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

27-31: Simplified data structure by removing nested user object

The data selection is now more direct, with rules and newsletters selected directly on the emailAccount object instead of through a nested user object. This simplification makes the code more readable and aligns with the email-account-centric model.


37-38: Updated property access to match new data structure

The property access has been correctly updated to directly access rules and newsletters from the emailAccount object instead of through the previously nested user object.

apps/web/utils/group/group-item.ts (1)

21-31: Updated parameter and query from userId to email-based identification

The function has been properly updated to:

  1. Change the parameter name from userId to email
  2. Update the Prisma query to use emailAccountId: email instead of userId

This change aligns with the broader refactoring effort moving from user-centric to email-account-centric identification.

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

94-96: Filter change looks good but lacks proper email validation

The change from using userId to emailAccountId for filtering email messages is consistent with the refactoring goal. However, emails are being directly used as IDs without validation, which could lead to issues if session.user.email is undefined or malformed.

Consider adding validation or sanitization for the email before using it as an ID:

await prisma.emailMessage.deleteMany({
-  where: { emailAccountId: session.user.email },
+  where: { 
+    emailAccountId: session.user.email 
+    // Consider adding a check that email is valid before this query
+  },
});

118-123: Inconsistency with other user actions

Several other actions in this file (completedOnboardingAction, completedAppOnboardingAction, saveOnboardingAnswersAction) still use user.id for authentication and database queries, while resetAnalyticsAction has been updated to use emailAccountId.

Should these other actions also be updated to use email-based identification as part of this refactoring? This would ensure consistency in how users are identified throughout the codebase.

Also applies to: 130-136, 150-160

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

5-9: Function signature change looks good

The update to accept an email object parameter instead of userId aligns with the email-account-centric refactoring approach.


12-18: Good update to use emailAccountId

The change from userId to emailAccountId in the token creation process is consistent with the refactoring goals. However, using an email address directly as an ID has implications for database design:

  1. Emails can sometimes change (though rarely)
  2. Using a non-primary-key as a foreign key reference might impact query performance

Consider whether the EmailAccount model uses email as its primary key or if it has a separate ID field. If it has a separate ID, you might want to look up the ID first:

// Example alternative approach
const emailAccount = await prisma.emailAccount.findUnique({
  where: { email }
});

await prisma.emailToken.create({
  data: {
    token,
    emailAccountId: emailAccount.id, // Using the actual ID
    expiresAt: addDays(new Date(), 30),
  },
});
apps/web/utils/sender.ts (2)

4-9: Parameter naming improved for clarity

The change from userId to emailAccountId in the function parameters aligns with the email-account-centric refactoring approach and improves semantic clarity.


14-19: Query filter updated correctly

The Prisma query filter has been properly updated to use emailAccountId instead of userId, maintaining consistency with the parameter changes.

apps/web/app/api/user/draft-actions/route.ts (2)

15-15: Function call updated correctly

The call to getData has been properly updated to pass an object with the email property.


20-24: Query filtering updated correctly

The function signature and Prisma query have been properly updated to use email and emailAccountId instead of userId, maintaining consistency with the refactoring approach.

For consistent error handling, consider adding a check to ensure the email is provided and valid:

async function getData({ email }: { email: string }) {
  if (!email) {
    throw new Error("Email is required");
  }
  
  // Continue with existing code...
}
apps/web/app/(app)/automation/rule/[ruleId]/page.tsx (2)

16-17: Authentication check correctly updated to use email.

The authentication check now properly extracts the user's email from the session object and redirects if it's missing, aligning with the email-account-centric model refactoring.


20-20: Database query updated to use email account ID.

The query now correctly uses emailAccountId: email instead of user ID, consistent with the schema changes throughout the application.

apps/web/app/(app)/reply-zero/ReplyTrackerEmails.tsx (3)

38-38: Property renamed from userEmail to email.

The component props were correctly updated to use email instead of userEmail, maintaining consistency with the email-account-centric model.

Also applies to: 45-45


182-182: Updated prop passed to Row component.

The prop passed to the Row component has been properly renamed from userEmail to email.


260-260: Row component parameter renamed from userEmail to email.

The function parameter and its type annotation have been correctly updated to use email instead of userEmail.

Also applies to: 272-272

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

49-52: Updated Prisma query to select rules directly from emailAccount.

The query now correctly selects rules directly from the emailAccount entity instead of through user relations, aligning with the schema changes where relations moved from User to EmailAccount.


110-110: Updated reference to rules property.

The reference to rules has been properly updated from emailAccount.user.rules to emailAccount.rules to match the new schema structure.


178-178: Updated processHistory function call.

The processHistory function call now correctly passes emailAccount.rules instead of accessing them through the user property.

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

220-221: Property renamed from email to emailAccountId in coordinateReplyProcess.

The property has been correctly renamed to emailAccountId when calling coordinateReplyProcess, maintaining consistency with the email-account-centric model throughout the application.

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

49-50: Change aligns with architecture shift from User to EmailAccount.

This change to the unique constraint query is part of the broader refactoring to use email accounts as the primary identifier instead of user IDs. The code is now using emailAccountId: user.email instead of the previous userId in the unique constraint.

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

63-69: Data access pattern updated to use EmailAccount model.

The code now queries the emailAccount table directly and accesses tokens through the nested account relation, which aligns with the refactored data model. This is a cleaner approach that centralizes access through the email account.

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

247-248: Updated query to use email-based identification.

Changed from filtering by userId to using emailAccountId: email, aligning with the email-account-centric model being implemented across the codebase.


310-311: Consistent use of email identification in queries.

Changed from filtering by userId to using emailAccountId: email, maintaining consistency with the changes made throughout the codebase for email-account-centric identification.

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

16-17: Authentication check updated to use email identifier.

The code now uses session?.user.email instead of session?.user.id for authentication checks, consistent with the email-account-centric approach being implemented.


29-35: Database relations updated to use email as primary identifier.

The upsert operation now uses the composite key email_emailAccountId and sets emailAccountId: userEmail directly instead of connecting through a User entity. This properly aligns with the schema changes that moved relations from User to EmailAccount.

apps/web/app/(app)/clean/run/page.tsx (3)

18-20: Properly refactored to use email-based identification

The code correctly switches from using userId to userEmail as the primary identifier, and updates the function call to use emailAccountId parameter. This aligns with the PR objective of moving to an email-account-centric model.


22-24: Consistent parameter passing with updated helper functions

The code correctly updates calls to getJobById and getLastJob to use the email parameter instead of userId, matching the updated function signatures in the helpers file.


28-32: Database queries updated correctly

The Prisma queries have been properly updated to filter by emailAccountId: userEmail instead of userId, maintaining data consistency with the email-account-centric model.

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

8-10: Function signature and query filters properly updated

The getRules function signature has been correctly updated to accept email instead of userId, and the Prisma query filter now uses emailAccountId: email instead of userId. This is consistent with the schema changes moving relations from User to EmailAccount.


22-25: Authentication and function call updated correctly

The authentication check and function call have been properly updated to use email-based identification instead of user ID, consistent with the PR objective.

apps/web/app/api/user/rules/[id]/route.ts (2)

9-12: Function signature and query filter properly updated

The getRule function signature has been correctly updated to accept email instead of userId, and the Prisma query filter now uses emailAccountId: email instead of userId. This is consistent with the schema changes moving relations from User to EmailAccount.


24-27: Function call parameter updated correctly

The call to getRule now correctly passes email: session.user.email instead of userId, ensuring consistency with the updated function signature.

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

8-9: Function signature updated correctly

The getCategories function signature has been properly updated to accept an object with an email property instead of userId, and it correctly calls getUserCategories with the email parameter.


15-18: Authentication and function call updated correctly

The authentication check now properly verifies session.user.email instead of session.user.id, and the function call passes the email parameter correctly. This maintains consistency with the email-account-centric model being implemented.

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

8-10: Function signature and query updated to use email identifier instead of userId.

The getGroups function and its Prisma query have been correctly updated to use email parameter and emailAccountId filter instead of the previous userId approach. This change aligns with the PR objective of moving from a user-centric model to an email-account-centric model.


23-24: Authentication check correctly updated to use email.

The authentication check has been properly adapted to extract the email from the session and verify its presence. This maintains security while adapting to the new identity model.


26-26: Function call updated to use email parameter.

The getGroups function call now correctly passes the email parameter, completing the migration from userId to email-based identification for this endpoint.

apps/web/app/(app)/reply-zero/fetch-trackers.ts (3)

7-17: Function signature correctly updated to use email parameter.

The getPaginatedThreadTrackers function signature has been appropriately refactored to accept email instead of the previous userId, aligning with the email-account-centric model. The type definition has been correctly updated as well.


22-35: Prisma query properly updated to filter by emailAccountId.

The database query has been correctly modified to filter by emailAccountId: email instead of the previous userId filter. This ensures proper data retrieval based on the email account identity.


36-52: SQL raw queries consistently updated to use emailAccountId.

Both SQL raw queries have been correctly modified to use emailAccountId instead of userId, maintaining consistency with the Prisma queries. This ensures that the pagination counts are correctly calculated using the same identity model.

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

38-41: Newsletter query updated to use emailAccountId filter.

The database query for newsletters has been properly updated to filter by emailAccountId: email instead of the previous userId filter, maintaining consistency with the broader refactoring effort.


47-47: Function call updated to use email parameter.

The getUserCategoriesWithRules function call now correctly passes the email parameter, which matches the updated function signature in category.server.ts that accepts an email parameter and filters categories by emailAccountId.

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

52-52: Function call updated to use object with email parameter.

The call to deleteExecutedRulesInBatches has been correctly updated to pass an object with the email property, aligning with the function's new signature.


103-109: Function signature properly updated to accept email parameter.

The deleteExecutedRulesInBatches function now accepts an object with email and an optional batchSize, which is a more maintainable approach that allows for additional parameters in the future without breaking the API.


114-116: Database query correctly updated to filter by emailAccountId.

The Prisma query inside the batch deletion process has been properly updated to use emailAccountId: email instead of the previous userId filter, ensuring consistent data access throughout the application.

apps/web/app/(app)/reply-zero/NeedsReply.tsx (3)

6-16: Refactoring parameters from userId to email is consistent with schema changes.

The function signature now uses email instead of what was likely userId before, which aligns with the broader refactoring effort to move from user-centric to email-account-centric model.


17-22: Parameter update correctly propagated to data fetching function.

The call to getPaginatedThreadTrackers has been updated to pass email instead of userId, maintaining consistency with the changes in the function signature.


25-31: Component prop correctly updated to use email identifier.

The prop passed to the ReplyTrackerEmails component has been changed from what was likely userEmail to email, completing the refactoring of this component.

apps/web/utils/rule/prompt-file.ts (1)

66-70: Database query correctly updated to use email as identifier.

The where clause in the Prisma query has been properly changed to use emailAccountId: email instead of what was likely userId, aligning with the schema changes that moved relations from User to EmailAccount.

apps/web/app/api/reply-tracker/disable-unused-auto-draft/route.ts (6)

29-44: Query correctly updated to use EmailAccount model.

The query now fetches from the emailAccount table instead of user and selects the email field instead of id, consistent with the schema changes.


69-94: Filter correctly changed to use emailAccountId.

The query condition has been updated to filter by emailAccountId: user.email instead of userId, maintaining the same logical behavior while adapting to the new schema.


98-103: Logging fields updated to use email identifier.

The log message now references email: user.email instead of the previous user ID, ensuring consistent logging throughout the application.


112-114: Log message correctly updated to use email identifier.

Similar to other logging changes, this message now references the user by email instead of ID.


117-126: Action deletion filter updated to use emailAccountId.

The where clause for deleting DRAFT_EMAIL actions has been updated to use emailAccountId: user.email instead of userId, maintaining correct behavior with the new schema.


131-131: Error logging correctly updated to use email field.

The error log now uses email: user.email for identification instead of user ID.

apps/web/app/(app)/clean/helpers.ts (2)

3-13: Function signature and query correctly updated to use email identifier.

The getJobById function has been properly refactored to:

  1. Accept email instead of userId in its parameters
  2. Update the type annotation accordingly
  3. Use email in the where clause of the Prisma query

This maintains the same functionality while aligning with the schema changes.


15-20: Second helper function consistently updated to use email parameter.

The getLastJob function has been similarly refactored to use email instead of userId in both its signature and database query, completing the consistent transition to email-based identification.

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

75-83: Function parameter update aligns with email-account-centric model

The change from accepting separate parameters to a single object parameter with emailAccountId follows good practices for function design and is consistent with the refactoring to use email accounts as the primary identifier.


84-84: Pattern updated to use emailAccountId instead of userId

The Redis key pattern has been properly updated to use the emailAccountId as the identifier.

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

65-66: Authentication check updated to use email

The authentication check has been properly updated to check for the presence of the user's email instead of the user ID.


78-81: Composite key updated to use emailAccountId

The Prisma update operation now correctly uses the composite key emailAccountId_fromEmail with emailAccountId set to the user's email, aligning with the database schema changes.

apps/web/app/(app)/reply-zero/page.tsx (6)

29-30: Authentication simplified to use only email

Authentication has been simplified to check for the presence of the user's email, which is consistent with the email-account-centric model.


38-44: Database query updated to filter by emailAccountId

The Prisma query has been properly updated to filter the rule by emailAccountId matching the user's email.


48-48: Function call updated to use email parameter

The call to isAnalyzingReplyTracker has been updated to pass the email parameter, which aligns with the updated function signature shown in the relevant code snippet.


98-105: Component props updated to use email

The props passed to the NeedsReply component now use email instead of userId, which is consistent with the refactoring.


107-114: AwaitingReply props updated to use email

The props passed to the AwaitingReply component now use email instead of userId, consistent with the refactoring.


120-126: Resolved component props updated to use email

The props passed to the Resolved component now use email instead of userId, consistent with the refactoring.

apps/web/utils/actions/knowledge.ts (6)

20-21: Authentication updated to use email

The authentication check has been properly updated to use the user's email instead of the user ID, consistent with the email-account-centric model.


26-31: Database creation updated to use emailAccountId

The knowledge record creation now associates the record with the user's email account through emailAccountId, aligning with the schema changes.


41-42: Authentication in updateKnowledgeAction updated to use email

The authentication check in updateKnowledgeAction has been properly updated to use the user's email.


47-49: Ownership check in knowledge update uses emailAccountId

The Prisma update operation now correctly filters by both the record ID and the user's email, ensuring proper access control.


63-64: Authentication in deleteKnowledgeAction updated to use email

The authentication check in deleteKnowledgeAction has been properly updated to use the user's email.


69-71: Ownership check in knowledge deletion uses emailAccountId

The Prisma delete operation now correctly filters by both the record ID and the user's email, ensuring proper access control.

apps/web/app/(app)/reply-zero/Resolved.tsx (3)

9-9: Parameter replacement looks good.

The parameter replacement from userId to email is consistent with the PR objective of moving to an email-account-centric model. The type annotation is correctly updated to reflect this change.

Also applies to: 13-13


25-26: SQL query filter updates align with schema changes.

The filter condition in the raw SQL queries has been appropriately updated to use "emailAccountId" = ${email} instead of using userId. This change is consistent with the broader migration to using email as the primary identifier.

Also applies to: 36-37


55-55: Component prop update is consistent.

Renamed prop from userEmail to email to maintain consistency with the parameter changes. This ensures the data flows correctly through the component hierarchy.

apps/web/app/(app)/reply-zero/AwaitingReply.tsx (3)

7-7: Parameter replacement looks good.

The parameter replacement from userId to email is consistent with the PR objective of moving to an email-account-centric model. The type annotation is correctly updated to reflect this change.

Also applies to: 12-12


18-18: Argument update is correct.

The argument passed to getPaginatedThreadTrackers has been correctly updated to use email instead of userId, which matches the expected parameter in the function as shown in the relevant code snippet.


27-27: Component prop update is consistent.

The prop passed to ReplyTrackerEmails has been correctly renamed from userEmail to email, maintaining consistency with the parameter changes throughout the component hierarchy.

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

3-5: Redis key updating looks good.

The getKey function has been correctly updated to use email instead of userId for generating Redis keys, ensuring consistency with the new email-based identification pattern.


7-11: Improved function parameter pattern.

The function has been nicely refactored to use a destructured object parameter { email } instead of a direct string parameter. This makes the function more self-documenting and allows for future parameter additions without breaking the API.


13-16: Consistent pattern across all functions.

The same parameter pattern updates have been consistently applied to all functions in this file, ensuring a uniform API for working with reply tracker analysis state via Redis.

Also applies to: 18-22

apps/web/app/api/google/labels/create/controller.ts (4)

18-19: Authentication check update is correct.

Authentication now correctly uses session?.user.email instead of session?.user.id, aligning with the email-account-centric approach. The error message remains accurate for the new check.


26-31: Composite key structure updated correctly.

The Prisma where clause correctly reflects the database schema change from using a composite key of gmailLabelId_userId to gmailLabelId_emailAccountId. This is a critical change that aligns with the broader schema refactoring.


33-39: Label creation updated correctly.

The label creation properties now correctly use emailAccountId: email instead of userId, reflecting the ownership shift to the EmailAccount model.


42-45: Redis caching parameter updated.

The Redis caching function call has been updated to pass email instead of userId, maintaining consistency with the identification pattern changes.

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

63-64: Authentication updated to use email

The code now uses the email from the session for authentication instead of userId, consistent with the broader refactoring to an email-centric model.


70-72: API call updated to use email parameter

The call to startAnalyzingReplyTracker correctly passes the email instead of userId, matching the expected parameter structure in the imported function.


74-80: Database query filter updated to use emailAccountId

The Prisma query now correctly filters thread trackers by emailAccountId instead of userId, aligning with the database schema changes.


82-84: API call updated to use email parameter

The call to stopAnalyzingReplyTracker correctly passes the email instead of userId, matching the expected parameter structure in the imported function.

apps/web/utils/actions/mail.ts (5)

184-185: Authentication updated to use email

Authentication now uses the email from the session instead of userId, consistent with the broader refactoring.


195-195: Database constraint updated to use emailAccountId

The Prisma query's unique constraint has been updated to use name_emailAccountId instead of a user-based constraint.


201-201: Email account relation updated

The create operation now correctly connects the label to the email account via email rather than to the user.


212-215: Database query filter updated to use emailAccountId

The delete operation now correctly filters by emailAccountId instead of userId.


218-224: Helper function call updated to use email parameter

The call to saveUserLabels now passes the email parameter correctly.

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

355-358: Function call updated to use email parameter

The deleteGroupItem function now receives the user's email instead of userId, matching the updated function signature.


523-526: API call updated to use email parameter

The getUserCategoriesForNames function call has been updated to use the user's email rather than userId.


528-532: Function call updated to use email parameter

The createRule call now correctly passes the user's email instead of userId.


655-658: Database query filter updated to use emailAccountId

The findSenderByEmail function call now filters by emailAccountId: userEmail instead of userId.


671-676: Function call updated to use email parameter

The updateCategoryForSender call now correctly passes the userEmail parameter.

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

92-92: Database relation updated to use emailAccountId

The rule creation now correctly associates with the email account using the emailAccountId field instead of userId.


147-149: Database query filter updated to use emailAccountId

The Prisma query now correctly filters by emailAccountId: email instead of userId when finding a rule.


163-186: Database query filter updated to use emailAccountId

The rule update operation now correctly filters by emailAccountId: email instead of userId.


273-276: Database query filter updated to use emailAccountId

The rule lookup operation now correctly filters by emailAccountId: email instead of userId.


294-296: Authentication updated to use email

Authentication now uses the email from the session instead of userId, consistent with the broader refactoring.


301-303: Database query filter updated to use emailAccountId

The rule update operation now correctly filters by emailAccountId: email instead of userId.


315-317: Authentication updated to use email

Authentication now uses the email from the session instead of userId, consistent with the broader refactoring.


321-330: Database query filter updated to use emailAccountId_systemType

The rule lookup now correctly uses the composite key of emailAccountId_systemType with the user's email.


356-362: Permission check updated to use emailAccountId

The permission check on rule deletion now correctly compares rule.emailAccountId with the authenticated user's email.


365-369: Function call updated to use email parameter

The deleteRule call now passes the email parameter instead of userId, matching the function's updated signature in utils/rule/rule.ts.


492-501: Function parameter updated to use emailAccountId

The createRule function's parameters now include emailAccountId instead of userId.


503-504: Database query filter updated to use emailAccountId_systemType

The rule lookup now correctly uses the composite key of emailAccountId_systemType.


538-554: Database creation updated to use emailAccountId

The rule creation now correctly uses emailAccountId instead of userId.


572-581: Helper function updated to use emailAccountId

The deleteRule helper function now takes emailAccountId instead of userId and uses it in the query.


584-597: Function calls updated to pass email parameter

The calls to createRule and deleteRule now correctly pass the email parameter.

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

25-28: LGTM: Refactored query structure to access rules directly from emailAccount

The query now directly selects rules from the emailAccount with the appropriate filter, which aligns with the email-account-centric model shift mentioned in the PR objectives.


31-36: LGTM: Updated selected fields in the query

The selected fields have been appropriately updated to match the new data structure.


47-47: LGTM: Updated access pattern to reflect new data relationship

Correctly changed from accessing emailAccount.user.rules to emailAccount.rules, consistent with moving relations from User to EmailAccount.


100-100: LGTM: Updated parameter for rule creation

Changed from using userId to email as the identifier for rule creation, which aligns with the email-account-centric model.

apps/web/app/api/user/group/[groupId]/items/route.ts (4)

9-13: LGTM: Updated function signature to use email instead of userId

Function parameter has been correctly changed from userId to email, aligning with the email-account-centric model.


16-16: LGTM: Updated database query to use emailAccountId

Database query has been updated to filter by emailAccountId: email instead of userId, reflecting the schema changes.


29-30: LGTM: Updated authentication check to use email

Authentication check now correctly verifies the presence of session?.user.email instead of session?.user.id.


35-35: LGTM: Updated function call with email parameter

Function call to getGroupItems now correctly passes email instead of userId.

apps/web/utils/actions/group.ts (7)

19-20: LGTM: Updated authentication check in createGroupAction

Authentication now correctly checks for the presence of session?.user.email instead of session?.user.id.


26-26: LGTM: Updated rule lookup query

Rule lookup now correctly uses emailAccountId: email instead of userId for filtering.


35-35: LGTM: Updated group creation data

Group creation now correctly sets emailAccountId: email instead of userId.


50-51: LGTM: Updated authentication check in addGroupItemAction

Authentication now correctly checks for the presence of session?.user.email instead of session?.user.id.


60-60: LGTM: Updated permission check

Permission check now correctly compares group.emailAccountId to the user's email.


73-74: LGTM: Updated authentication check in deleteGroupItemAction

Authentication now correctly checks for the presence of session?.user.email instead of session?.user.id.


76-76: LGTM: Updated deleteGroupItem function call

Function call now correctly passes email instead of userId, consistent with the updated function signature in the imported module.

apps/web/utils/category.server.ts (3)

13-16: LGTM: Updated getUserCategories function

Function signature and implementation have been updated to accept email and use emailAccountId in the query, aligning with the email-account-centric model.


20-23: LGTM: Updated getUserCategoriesWithRules function

Function signature and query have been updated to use email as the parameter and emailAccountId in the filter.


33-44: LGTM: Updated getUserCategoriesForNames function

Function signature and query have been correctly updated to use email as the parameter and emailAccountId in the filter.

apps/web/scripts/migrateRedisPlansToPostgres.ts (1)

100-108: Double‑check the semantics of emailAccountId

All subsequent queries now pass emailAccount.email into emailAccountId.
That is correct only if the Prisma schema declares

model Rule {
  …
  emailAccountId String @map("email") …
}

Otherwise these lookups will fail or, worse, create orphaned rows.

Please verify the field mapping in schema.prisma; if emailAccountId is still the UUID primary key you must pass emailAccount.id instead.

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

27-39: Great use of Prisma.EmailAccountGetPayload

Casting the selected subset to UserPreferences gives you strict type‑safety while avoiding over‑fetching. 👍

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

108-110: Passing entire user object to AI helpers is 👍

runRules and friends often need both metadata (aiProvider, ...) and rule list.
Forwarding the whole emailAccount keeps signatures compact and future‑proof.


70-75:

✅ Verification successful

Confirm new composite key name

unique_emailAccount_thread_message now uses emailAccountId.
Prisma will generate a name such as
unique_emailAccountId_threadId_messageId (note the Id).
If the migration renamed the field only at application level and not in the DB, the lookup will miss.

Run the following verification to list composite indexes:


🏁 Script executed:

#!/bin/bash
ast-grep --pattern $'@@unique([emailAccountId, threadId, messageId])'

Length of output: 70


I couldn’t find any composite‐index definitions with that pattern. Let’s search all unique constraints in your Prisma schema to confirm the index name:


🏁 Script executed:

#!/bin/bash
# List all composite unique indexes in the Prisma schema
rg "@@unique" -n .

Length of output: 1196


Composite index name is correct
The Prisma schema explicitly names the composite unique constraint
@@unique([emailAccountId, threadId, messageId], name: "unique_emailAccount_thread_message")
(see apps/web/prisma/schema.prisma:293), so the lookup key matches and no changes are needed.

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

101-166: Correctly updated database queries to use email-based identification.

The Prisma queries have been properly modified to use email as the primary identifier, including:

  1. Using emailAccountId instead of userId in the unique constraint
  2. Removing the nested user references, now directly accessing rules and categories from emailAccount
  3. Maintaining correct include patterns for related data

This aligns well with the PR objective of moving relations to emailAccount.


168-171: Updated existence check to verify emailAccount.

The error check has been correctly updated to verify the existence of emailAccount instead of user, ensuring consistent error handling with the new data model.


211-219: Successfully adapted parameters for processUserRequest function.

The code now correctly passes the emailAccount object and directly accesses rules and categories from it rather than from a nested user structure.

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

46-48: Successfully updated ExecutedRule query to use emailAccountId.

The database query correctly uses emailAccountId: user.email instead of userId, which aligns with the email-account-centric approach of this refactoring.


137-138: Parameter properly renamed from userId to email.

The function parameter has been correctly renamed to better reflect the new identification approach.


144-145: Updated logger options to use email.

Logging context has been properly updated to include email instead of userId for consistent logging across the application.


152-154: Successfully updated ExecutedAction query to use emailAccountId.

The query now correctly filters by email account ID instead of user ID, maintaining consistent identification throughout the database operations.

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

62-64: Successfully updated Newsletter query to use emailAccountId.

The query now correctly filters by the user's email, consistent with the database model changes.


142-146: Updated authentication check and Category query correctly.

Both the authentication verification and the subsequent Category query now properly use email-based identification.


150-153: Successfully refactored categorizeSender parameters.

The function now correctly passes the user's email instead of userId for consistent identification.


208-212: Successfully refactored deleteCategory function.

The function signature and implementation have been properly updated to use email instead of userId.


214-236: Successfully refactored upsertCategory function.

The function now correctly uses email-based identification throughout, including in both the create and update operations.

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

82-83: Successfully updated parameter to use email instead of userId.

The createReplyTrackerOutbound function now correctly receives the user's email, aligning with the refactoring approach.


96-97: Successfully updated function signature to use email.

The function signature has been properly updated to use email instead of userId.


115-119: Successfully updated unique constraint to use emailAccountId.

The database query now correctly uses the email-based constraint instead of userId-based constraint.


123-124: Successfully updated ThreadTracker create data.

The create operation now properly uses emailAccountId instead of userId for consistent data storage.


157-158: Successfully updated resolveReplyTrackers function signature.

The function now correctly accepts email instead of userId.


163-164: Successfully updated ThreadTracker query.

The query now correctly filters by emailAccountId instead of userId.

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

74-115: Confirm compound unique key exists after model migration

upsert is keyed by emailAccountId_threadId_messageId.
After the refactor, ensure that the Prisma schema still declares @@unique([emailAccountId, threadId, messageId]) (or an equivalent @@id). Otherwise the call will throw at runtime.

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

24-25: Properly updated getUserCategories call with email parameter

The function now correctly passes the user's email to getUserCategories instead of userId, aligning with the broader refactoring.


47-47: Consistent parameter naming update

Changed from userId to userEmail in the updateSenderCategory call, maintaining consistency with the email-centric model.


61-68: Function signature updated to use email-based identification

The updateSenderCategory function now correctly accepts userEmail instead of userId in its parameter list, aligning with the email account centric model.


80-80: Database field updated from userId to emailAccountId

The category creation now correctly associates with emailAccountId using the user's email, which matches the updated schema.


89-91: Updated unique constraint key in Prisma query

The unique constraint has been properly updated from email_userId to email_emailAccountId to match the schema changes.


95-95: Create operation updated to use emailAccountId

The newsletter creation now correctly uses emailAccountId instead of userId, aligning with the schema changes.


106-112: Function signature updated for updateCategoryForSender

The updateCategoryForSender function properly accepts userEmail instead of userId in its parameter list.


117-117: Updated composite key in Prisma upsert operation

The unique constraint reference is correctly updated to use email_emailAccountId in the where clause.


121-121: Create operation field updated to emailAccountId

The newsletter creation correctly uses emailAccountId instead of userId to associate with the email account.


143-144: getCategories function updated to use email parameter

The function signature and implementation now correctly use email instead of userId for retrieving categories.

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

30-38: Function signature updated to use email parameter

The safeCreateRule function now correctly accepts email instead of userId, aligning with the email-centric model refactoring.


41-44: getUserCategoriesForNames call updated to use email

The function call now uses the updated API with a named parameter object containing email instead of positional userId, matching the updated helper function.


47-52: createRule call updated to use email parameter

The call to createRule now passes email instead of userId, maintaining consistency with the refactored function signature.


66-66: Logging statement updated to use email

Error logging now uses email instead of userId for consistent identification throughout the system.


77-87: Improved parameter structure in safeUpdateRule function

The function signature has been refactored to use a cleaner named parameter object with email instead of userId. This provides better clarity and extensibility.


89-94: updateRule call changed to use email-based identification

The call properly passes emailAccountId: email to match the updated function signature of updateRule.


119-127: createRule signature updated for email-based identification

The function now accepts email instead of userId, aligning with the broader refactoring goal.


135-135: Database field updated in rule creation

The rule creation now correctly uses emailAccountId: email instead of userId, matching the schema changes.


168-178: Refactored updateRule function with improved parameter structure

The function has been refactored to use named parameters with emailAccountId instead of userId, providing better clarity and consistency with the schema changes.


183-183: Database field updated in rule update operation

The update operation now correctly uses emailAccountId instead of userId to match the schema changes.


207-214: deleteRule function updated to use email parameter

The function signature has been updated to accept email instead of userId for user identification.


217-221: Delete operations updated to use emailAccountId

The Prisma delete operations now correctly filter by emailAccountId: email instead of userId, ensuring proper authorization and consistency with the schema.

apps/web/prisma/schema.prisma (24)

90-92: User model simplified with relations moved to EmailAccount

The User model has been correctly simplified to only maintain essential relations (apiKeys and emailAccounts), supporting the architectural shift to an email-account-centric model.


128-140: Relations properly moved from User to EmailAccount

All relevant relations (labels, rules, executedRules, etc.) have been correctly moved from the User model to the EmailAccount model, centralizing ownership under email accounts.


205-206: Label model references updated to EmailAccount

The Label model now correctly references EmailAccount via the emailAccountId field instead of referencing User via userId.


208-209: Updated unique constraints for Label model

The unique constraints have been correctly updated to use emailAccountId instead of userId, maintaining data integrity with the new schema.


221-222: Rule model references updated to EmailAccount

The Rule model now correctly uses emailAccountId and references EmailAccount instead of User, aligning with the architectural changes.


250-251: Updated unique constraints for Rule model

The unique constraints have been properly updated to use emailAccountId instead of userId, maintaining data integrity with the new schema structure.


288-289: ExecutedRule model references updated to EmailAccount

The ExecutedRule model now correctly references EmailAccount via emailAccountId instead of User via userId.


293-294: Updated unique constraints and indexes for ExecutedRule

The unique constraint and index have been properly updated to use emailAccountId instead of userId, maintaining efficient queries and data integrity.


333-334: Group model references updated to EmailAccount

The Group model now correctly references EmailAccount via emailAccountId instead of User via userId.


337-337: Updated unique constraint for Group model

The unique constraint has been properly updated to use emailAccountId instead of userId.


358-360: Category model references updated to EmailAccount

The Category model now correctly references EmailAccount via emailAccountId instead of User via userId.


364-364: Updated unique constraint for Category model

The unique constraint has been properly updated to use emailAccountId instead of userId, maintaining data integrity.


381-383: Newsletter model references updated to EmailAccount

The Newsletter model now correctly references EmailAccount via emailAccountId instead of User via userId.


387-388: Updated unique constraint and index for Newsletter model

The unique constraint and index have been properly updated to use emailAccountId instead of userId, maintaining data integrity and query performance.


401-403: ColdEmail model references updated to EmailAccount

The ColdEmail model now correctly references EmailAccount via emailAccountId instead of User.


404-406: Updated constraints and indexes for ColdEmail model

All constraints and indexes have been properly updated to use emailAccountId instead of userId.


425-427: EmailMessage model references updated to EmailAccount

The EmailMessage model now correctly references EmailAccount via emailAccountId instead of User.


428-431: Updated constraints and indexes for EmailMessage model

All constraints and indexes have been properly updated to use emailAccountId instead of userId, ensuring data integrity and query performance.


444-446: ThreadTracker model references updated to EmailAccount

The ThreadTracker model now correctly references EmailAccount via emailAccountId instead of User.


447-450: Updated constraints and indexes for ThreadTracker model

All constraints and indexes have been properly updated to use emailAccountId instead of userId.


479-481: CleanupThread model references updated to EmailAccount

The CleanupThread model now correctly references EmailAccount via emailAccountId instead of User.


490-492: Knowledge model references updated to EmailAccount

The Knowledge model now correctly references EmailAccount via emailAccountId instead of User.


493-493: Updated unique constraint for Knowledge model

The unique constraint has been properly updated to use emailAccountId instead of userId.


511-516: EmailToken model references updated to EmailAccount

The EmailToken model now correctly references EmailAccount via emailAccountId instead of User.

Comment on lines 77 to 83
const emailAccount = await prisma.emailAccount.findFirst({
where: { userId },
select: { email: true, userId: true },
});
if (!user) {
console.error(`User not found for user ${userId}`);
if (!emailAccount) {
console.error(`Email account not found for user ${userId}`);
processedUserIds.push(userId);
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Consider handling multi‑account users explicitly

findFirst will silently pick any EmailAccount that matches the userId.
If a single user can own several email accounts (very common with aliases), the migration will only process the first one it meets, dropping plans that belong to the remaining accounts.

Recommendation: either

-const emailAccount = await prisma.emailAccount.findFirst({
-  where: { userId },
-  select: { email: true, userId: true },
-});
+const emailAccounts = await prisma.emailAccount.findMany({
+  where: { userId },
+  select: { email: true, userId: true },
+});
+if (!emailAccounts.length) {
+
+}
+for (const emailAccount of emailAccounts) {
+  await migrateEmailAccountPlans({ userId, email: emailAccount.email });
+}
+return; // remove the rest of the function body or fold it into the helper

(or abort with a clear error) so you never lose data.

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 199 to 205
const email = session?.user.email;
if (!email) return { error: "Not logged in" };

await prisma.rule.update({
where: { id: ruleId, userId: session.user.id },
where: { id: ruleId, emailAccountId: email },
data: { runOnThreads },
});
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Use the primary key when updating rules

If id is the primary key of Rule, including emailAccountId in where is harmless but unnecessary.
If instead you defined a composite primary key (@@id([id, emailAccountId])) the correct syntax is:

where: {
  id_emailAccountId: { id: ruleId, emailAccountId: email },
}

Current code will throw UNKNOWN_ARGUMENT at runtime.

Comment on lines 165 to 167
const messages = user.account.access_token
? await getMessagesBatch(messageIds, user.account.access_token)
: [];
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Potential TypeError when account is null

user.account can be null (e.g. Gmail disconnected).
Accessing user.account.access_token before the ternary guard will crash.

-const messages = user.account.access_token
-  ? await getMessagesBatch(messageIds, user.account.access_token)
-  : [];
+const accessToken = user.account?.access_token;
+const messages = accessToken
+  ? await getMessagesBatch(messageIds, accessToken)
+  : [];
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const messages = user.account.access_token
? await getMessagesBatch(messageIds, user.account.access_token)
: [];
const accessToken = user.account?.access_token;
const messages = accessToken
? await getMessagesBatch(messageIds, accessToken)
: [];

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

🔭 Outside diff range comments (3)
apps/web/app/api/user/stats/newsletters/route.ts (1)

69-71: 🛠️ Refactor suggestion

Consider completing the email account ID refactoring in this file.

While findNewsletterStatus now uses emailAccountId, other parts of this file still reference userId. The getNewslettersTinybird function still accepts userId in its signature, and getNewsletterCounts still filters by userId in the SQL query. Consider completing the refactoring by updating these remaining instances to use email account IDs for consistency.

async function getNewslettersTinybird(
-  options: { ownerEmail: string; userId: string } & NewsletterStatsQuery,
+  options: { ownerEmail: string; emailAccountId: string } & NewsletterStatsQuery,
) {
  // ...
}

// Later in getNewsletterCounts
-  whereConditions.push(`"userId" = $${queryParams.length + 1}`);
-  queryParams.push(options.userId);
+  whereConditions.push(`"emailAccountId" = $${queryParams.length + 1}`);
+  queryParams.push(options.emailAccountId);

// And in the GET handler
  const result = await getNewslettersTinybird({
    ...params,
    ownerEmail: session.user.email,
-    userId: session.user.id,
+    emailAccountId: session.user.email,
  });

Also applies to: 165-166, 250-254

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

549-560: 💡 Verification agent

🧩 Analysis chain

Verify newsletter model update.

The getNewsletter function still returns an object with userId property. Consider updating this to use emailAccountId for full consistency with the refactoring.


🏁 Script executed:

#!/bin/bash
# Check if the Newsletter model in the Prisma schema has been updated to use emailAccountId
rg -A 20 -B 5 "model Newsletter" -g "*.prisma"

Length of output: 1637


Update getNewsletter to use emailAccountId instead of userId

We’ve confirmed the Prisma Newsletter model no longer has a userId field and now requires emailAccountId. Please update the test helper accordingly:

• File: apps/web/utils/ai/choose-rule/match-rules.test.ts
• Lines: 549–560 (the getNewsletter function)

Replace:

function getNewsletter(overrides: Partial<Newsletter> = {}): Newsletter {
  return {
    id: "newsletter1",
    createdAt: new Date(),
    updatedAt: new Date(),
    userId: "userId",                // remove this
    email: "[email protected]",
    status: null,
    categoryId: "category1",
    ...overrides,
  } as Newsletter;
}

With something like:

function getNewsletter(overrides: Partial<Newsletter> = {}): Newsletter {
  return {
    id: "newsletter1",
    createdAt: new Date(),
    updatedAt: new Date(),
    email: "[email protected]",
    emailAccountId: "emailAccount1", // new required field
    status: null,
    categoryId: "category1",
    ...overrides,
  } as Newsletter;
}

Also update any tests that relied on userId to use emailAccountId instead.

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

221-225: 🛠️ Refactor suggestion

Overlooked userId reference in trackSentDraftStatus call.

While most of the file has been updated to use email/emailAccountId identifiers, this function call still includes a user object with id: user.userId.

This should be updated to maintain consistency with the refactoring effort:

  trackSentDraftStatus({
-    user: { id: user.userId, email: user.email },
+    user: { email: user.email },
    message,
    gmail,
  }),

Ensure the trackSentDraftStatus function signature has also been updated to no longer require the id property.

🧹 Nitpick comments (4)
apps/web/app/(app)/reply-zero/NeedsAction.tsx (1)

6-18: Consider removing unused userId parameter.

The function signature still includes userId as a parameter, but it's no longer being used in the function body after the refactoring to email-centric identification. Consider removing this parameter to keep the interface clean and align with the new model.

export async function NeedsAction({
-  userId,
  userEmail,
  page,
  timeRange,
  isAnalyzing,
}: {
-  userId: string;
  userEmail: string;
  page: number;
  timeRange: TimeRange;
  isAnalyzing: boolean;
}) {
apps/web/utils/group/find-matching-group.ts (2)

7-7: Function signature refactored correctly but consider parameter naming.

The function has been properly updated to accept an object with an email property instead of a userId string. However, the parameter naming could be more consistent with its usage.

Consider renaming the parameter for clarity:

-export async function getGroupsWithRules({ email }: { email: string }) {
+export async function getGroupsWithRules({ emailAccountId }: { emailAccountId: string }) {

9-9: Query filter parameter naming inconsistency.

The parameter is named email in the function signature but used as emailAccountId in the query. This might cause confusion for future developers.

For consistency, either rename the parameter or adjust the query:

-  where: { emailAccountId: email, rule: { isNot: null } },
+  where: { emailAccountId: emailAccountId, rule: { isNot: null } },

Alternatively, if email is intended to be used as the emailAccountId:

-  where: { emailAccountId: email, rule: { isNot: null } },
+  where: { email: email, rule: { isNot: null } },
apps/web/app/api/unsubscribe/route.ts (1)

67-67: Inconsistent email reference style.

Line 67 uses emailToken.emailAccount.email while lines 82 and 89 use emailToken.emailAccountId. If emailToken.emailAccountId is the email string (as suggested by line 54), then using .email here is redundant.

For consistency, use the same reference style throughout:

-      email: emailToken.emailAccount.email,
+      email: emailToken.emailAccountId,
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 4de18e3 and 2fc3424.

📒 Files selected for processing (20)
  • apps/web/app/(app)/reply-zero/NeedsAction.tsx (2 hunks)
  • apps/web/app/api/google/threads/controller.ts (1 hunks)
  • apps/web/app/api/google/webhook/process-history-item.ts (3 hunks)
  • apps/web/app/api/unsubscribe/route.ts (4 hunks)
  • apps/web/app/api/user/categorize/senders/uncategorized/get-senders.ts (1 hunks)
  • apps/web/app/api/user/categorize/senders/uncategorized/get-uncategorized-senders.ts (3 hunks)
  • apps/web/app/api/user/categorize/senders/uncategorized/route.ts (1 hunks)
  • apps/web/app/api/user/stats/helpers.ts (2 hunks)
  • apps/web/app/api/user/stats/newsletters/helpers.ts (1 hunks)
  • apps/web/app/api/user/stats/newsletters/route.ts (1 hunks)
  • apps/web/app/api/user/stats/recipients/route.ts (4 hunks)
  • apps/web/app/api/user/stats/senders/route.ts (4 hunks)
  • apps/web/app/api/user/stats/tinybird/load/load-emails.ts (8 hunks)
  • apps/web/app/api/user/stats/tinybird/load/route.ts (2 hunks)
  • apps/web/utils/actions/categorize.ts (8 hunks)
  • apps/web/utils/ai/choose-rule/draft-management.ts (2 hunks)
  • apps/web/utils/ai/choose-rule/match-rules.test.ts (16 hunks)
  • apps/web/utils/ai/choose-rule/match-rules.ts (4 hunks)
  • apps/web/utils/ai/choose-rule/run-rules.ts (9 hunks)
  • apps/web/utils/group/find-matching-group.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/utils/actions/categorize.ts
🧰 Additional context used
🧬 Code Graph Analysis (4)
apps/web/app/api/user/stats/newsletters/route.ts (1)
apps/web/app/api/user/stats/newsletters/helpers.ts (1)
  • findNewsletterStatus (32-42)
apps/web/utils/ai/choose-rule/match-rules.ts (4)
apps/web/utils/group/find-matching-group.ts (1)
  • getGroupsWithRules (7-12)
apps/web/utils/email.ts (1)
  • extractEmailAddress (16-36)
apps/web/utils/types.ts (2)
  • RuleWithActionsAndCategories (16-18)
  • ParsedMessage (47-58)
apps/web/utils/llms/types.ts (1)
  • UserEmailWithAI (7-8)
apps/web/app/api/user/stats/recipients/route.ts (1)
apps/web/app/api/user/stats/helpers.ts (1)
  • getEmailFieldStats (16-65)
apps/web/app/api/user/stats/senders/route.ts (1)
apps/web/app/api/user/stats/helpers.ts (1)
  • getEmailFieldStats (16-65)
🔇 Additional comments (61)
apps/web/app/(app)/reply-zero/NeedsAction.tsx (2)

19-24: Correct parameter name change for email-centric model.

The parameter name change from userId to email in the call to getPaginatedThreadTrackers aligns with the broader refactoring effort to use email as the primary identifier instead of user ID. This change is consistent with the updated API expectations.


27-34: Prop name standardized to email.

The prop name change from userEmail to email for the ReplyTrackerEmails component follows the same standardization pattern, maintaining consistency with the email-centric model being adopted throughout the codebase.

apps/web/app/api/user/categorize/senders/uncategorized/route.ts (1)

21-23: Parameter updated to use email account ID instead of user ID.

The parameter for getUncategorizedSenders has been correctly changed from userId to emailAccountId, which aligns with the broader refactoring from user-centric to email-account-centric identification throughout the codebase.

apps/web/app/api/google/threads/controller.ts (1)

58-59: Prisma query filter refactored to use email as identifier.

The query filter has been properly updated to use emailAccountId: email instead of userId, which is consistent with the codebase refactoring to use email accounts as the primary entity for data relationships.

apps/web/app/api/user/stats/tinybird/load/route.ts (2)

14-15: User authentication check refactored to use email variable.

The authentication check has been refactored to extract the email into a variable first, which keeps the same functionality while making the code more consistent with the email-account-centric model.


27-28: Parameter updated to use email account ID instead of user ID.

The parameter for loadEmails has been correctly changed from userId to emailAccountId, consistent with the broader refactoring effort.

apps/web/app/api/user/stats/newsletters/route.ts (1)

81-82: Function parameter updated to use emailAccountId object.

The call to findNewsletterStatus has been properly updated to use the object parameter format with emailAccountId property, which matches the refactored function signature.

apps/web/app/api/user/stats/newsletters/helpers.ts (2)

32-36: Function parameter refactored correctly.

The function signature has been updated to accept an object with emailAccountId instead of a plain string userId, correctly aligning with the move from user-centric to email-account-centric model.


38-38: Query filter updated consistently.

The Prisma query filter has been properly changed from userId to emailAccountId, maintaining consistency with the function signature change.

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

111-111: All findMatchingRule calls updated correctly.

All calls to findMatchingRule have been consistently updated to use the new object parameter structure, passing rules, message, user, and gmail as properties instead of positional parameters.

Also applies to: 125-125, 139-139, 164-164, 183-183, 202-202, 238-238, 268-268, 299-299, 321-321, 342-342, 384-384, 424-424, 465-465


513-513: Entity model property updated appropriately.

The getCategory helper function now correctly returns emailAccountId instead of userId, maintaining consistency with the database schema changes.


529-529: Entity model property updated appropriately.

The getGroup helper function now correctly returns emailAccountId instead of userId, maintaining consistency with the database schema changes.

apps/web/app/api/user/stats/helpers.ts (2)

16-24: Function parameter updated correctly.

The function signature has been properly refactored to use emailAccountId instead of userId, both in the parameter name and type definition.


34-35: Query filter updated consistently.

The Prisma query filter has been properly changed from userId to emailAccountId, maintaining consistency with the function signature change.

apps/web/app/api/user/categorize/senders/uncategorized/get-senders.ts (2)

3-11: Function parameter updated correctly.

The function signature has been properly refactored to use emailAccountId instead of userId, both in the parameter name and type definition.


14-15: Query filter updated consistently.

The Prisma query filter has been properly changed from userId to emailAccountId, maintaining consistency with the function signature change.

apps/web/utils/ai/choose-rule/draft-management.ts (2)

17-17: Parameter type updated correctly.

The executedRule parameter type has been properly updated from "userId" to "emailAccountId" which aligns with the broader refactoring effort to use email accounts as the primary identifier.


26-26: Query filter updated correctly.

The filter criteria has been appropriately changed from userId to emailAccountId, maintaining consistency with the schema changes.

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

68-70: Constraint and filter updated correctly.

The unique constraint has been properly updated from unique_user_thread_message to unique_emailAccount_thread_message and the filter has been changed from userId: user.userId to emailAccountId: userEmail, aligning with the database schema changes.


170-172: Unique constraint updated appropriately.

The constraint has been correctly changed from email_userId to email_emailAccountId with the corresponding filter parameter update.


248-248: Parameter update for cleanupThreadAIDrafts.

The parameter has been appropriately changed from userId: user.userId to email: user.email.

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

30-30: Include clause correctly updated.

The include clause has been properly changed to reference emailAccount instead of user.


54-54: Query filter updated correctly.

The filter has been appropriately updated to use email: emailToken.emailAccountId which aligns with the schema changes.


82-83: Reference updated correctly.

The error logging has been properly updated to use emailToken.emailAccountId.


89-89: Info logging updated correctly.

The info logging has been appropriately changed to use emailToken.emailAccountId.

apps/web/app/api/user/categorize/senders/uncategorized/get-uncategorized-senders.ts (3)

8-8: Parameter type updated from userId to emailAccountId

The function parameter has been correctly updated from userId to emailAccountId to align with the PR objective of shifting from a user-centric model to an email-account-centric model.

Also applies to: 12-12


21-21: Updated function call parameter

The getSenders function call has been properly updated to pass emailAccountId instead of userId, maintaining consistency with the parameter changes.


30-30: Updated database query filter

The Prisma query correctly uses emailAccountId instead of userId in the filter conditions, ensuring data is fetched based on the email account rather than the user ID.

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

78-79: Updated function parameters to use email-based identification

The getGroups function now correctly accepts an object with an email property instead of a plain userId string, aligning with the broader shift to email account identification.


85-85: Updated singleton function and query key

The getSender function's parameter structure and Prisma query have been properly updated:

  1. Function now accepts an object with email property rather than a userId string
  2. The query uses the composite key email_emailAccountId with emailAccountId rather than filtering by userId

This change maintains consistency with the email-centric data model refactoring.

Also applies to: 89-92


115-115: Updated function call to use email-based parameter

The getGroups function call has been properly updated to use email: rule.emailAccountId instead of a userId parameter, maintaining consistency with the updated function signature.


152-152: Updated function call to use email-based parameter

The getSender function call has been properly updated to use email: rule.emailAccountId instead of a userId parameter, maintaining consistency with the updated function signature.


206-216: Improved function signature with destructured parameters

The findMatchingRule function has been refactored to use a single destructured object parameter instead of multiple positional parameters. This change:

  1. Makes the function signature more explicit and self-documenting
  2. Aligns with the email-centric architecture by using user: UserEmailWithAI instead of separate user ID parameters
  3. Improves maintainability as additional parameters can be added without breaking existing calls
  4. Provides better TypeScript type safety with explicit parameter naming

This is a positive change that improves both code readability and flexibility.

apps/web/app/api/user/stats/recipients/route.ts (5)

55-55: Updated function parameter type

The getRecipientStatistics function now correctly accepts emailAccountId instead of userId in its options parameter, maintaining consistency with the email-centric refactoring.


73-77: Updated parameter type in helper function

The getMostSentTo function parameter and its type definition have been properly updated to use emailAccountId instead of userId, aligning with the broader refactoring effort.


80-80: Updated internal function call parameter

The call to getEmailFieldStats now correctly passes emailAccountId instead of userId, maintaining consistency with the helper function's updated signature (as shown in the relevant code snippets).


90-91: Updated authentication and authorization logic

The authentication logic has been properly updated to:

  1. Use session?.user.email as emailAccountId instead of session?.user.id
  2. Check for the presence of emailAccountId instead of userId

This change aligns with the shift to email-based user identification.


101-101: Updated function call parameter

The getRecipientStatistics function call now correctly passes emailAccountId instead of userId, maintaining consistency with the function's updated signature.

apps/web/app/api/user/stats/senders/route.ts (6)

22-22: Updated function parameter type

The getSenderStatistics function now correctly accepts emailAccountId instead of userId in its options parameter, maintaining consistency with the email-centric refactoring.


49-53: Updated parameter type in helper function

The getMostReceivedFrom function parameter and its type definition have been properly updated to use emailAccountId instead of userId, aligning with the broader refactoring effort.


56-56: Updated internal function call parameter

The call to getEmailFieldStats now correctly passes emailAccountId instead of userId, maintaining consistency with the helper function's updated signature (as shown in the relevant code snippets).


68-72: Updated parameter type in helper function

The getDomainsMostReceivedFrom function parameter and its type definition have been properly updated to use emailAccountId instead of userId, aligning with the broader refactoring effort.


75-75: Updated internal function call parameter

The call to getEmailFieldStats now correctly passes emailAccountId instead of userId, maintaining consistency with the helper function's updated signature.


96-96: Updated function call parameter

The getSenderStatistics function call now correctly passes emailAccountId: session.user.email instead of userId: session.user.id, maintaining consistency with the function's updated signature and the overall refactoring to email-based identification.

apps/web/app/api/user/stats/tinybird/load/load-emails.ts (8)

18-29: Function signature change looks correct

The change from userId to emailAccountId in the function signature is consistent with the broader refactor to shift from a user-centric model to an email-account-centric model. All consumers of this API will need to be updated accordingly.


33-36: Prisma query filter update looks good

The database query filter has been correctly updated to use emailAccountId instead of userId, matching the schema changes in the broader refactor.


43-50: Parameter update in saveBatch call is consistent

The refactoring from userId to emailAccountId is properly applied in the call to saveBatch.


62-65: Consistent query filter change

The second Prisma query filter is also consistently updated to use emailAccountId.


75-82: Second saveBatch call parameters updated properly

The refactoring is consistently applied to the second call to saveBatch.


95-111: Function parameter changes look good

The saveBatch function signature has been appropriately updated to accept emailAccountId instead of userId while maintaining the rest of the function parameters.


142-147: Error logging field updated correctly

The error logging field has been updated from userId to email to reflect the new identification scheme.


150-163: Data structure field updated correctly

The field in the saved email data structure has been updated from userId to emailAccountId to match the new schema.

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

70-76: saveSkippedExecutedRule parameter update looks good

The function call has been correctly updated to use emailAccountId: user.email instead of userId: user.id.


99-111: saveExecutedRule parameter update is consistent

The parameter to saveExecutedRule has been updated correctly to use emailAccountId: user.email.


133-151: Function signature and relation update looks good

The function has been correctly updated to use emailAccountId instead of userId. The relation in the data object has been updated from user: { connect: { id: userId } } to emailAccount: { connect: { email: emailAccountId } }, which aligns with the schema changes.


161-194: saveExecutedRule function updated consistently

The function signature and relation updates are consistent with the overall refactoring approach.


196-201: Updated function call to use named parameters

The call to upsertExecutedRule now uses a more explicit object parameter structure, which is good for readability and maintainability.


235-239: Logging field updated correctly

The logging field has been updated from userId to email to match the new identification scheme.


240-250: Updated findUnique query consistently

The unique constraint and field names have been updated consistently in the findUnique query.


48-49:

✅ Verification successful

Updated findMatchingRule call pattern

The call to findMatchingRule has been updated to use a single object parameter instead of multiple positional parameters. Make sure the implementation of findMatchingRule has been updated to match this calling pattern.

Run this script to verify the findMatchingRule function signature:


🏁 Script executed:

#!/bin/bash
# Check if findMatchingRule function has been updated to accept an object parameter
rg "export\s+(?:async\s+)?function\s+findMatchingRule" -A 10 --glob "*.ts"

Length of output: 776


Confirmed: findMatchingRule signature matches the updated call

Verified in apps/web/utils/ai/choose-rule/match-rules.ts that findMatchingRule now accepts a single destructured object parameter { rules, message, user, gmail }. No further changes are needed.


204-227:

✅ Verification successful

Updated upsertExecutedRule function and unique constraint

The function signature has been updated to use emailAccountId, and the unique constraint has been renamed from unique_user_thread_message to unique_emailAccount_thread_message. This suggests a corresponding change in the Prisma schema.

Verify that the Prisma schema has been updated with the new unique constraint:


🏁 Script executed:

#!/bin/bash
# Check for the updated unique constraint in the Prisma schema
rg "unique_emailAccount_thread_message" --glob "*.prisma"

Length of output: 181


Prisma unique constraint confirmed

The unique_emailAccount_thread_message constraint is defined in apps/web/prisma/schema.prisma:

  • @@unique([emailAccountId, threadId, messageId], name: "unique_emailAccount_thread_message")

This matches the updated upsertExecutedRule usage—no further changes needed.

Comment on lines 116 to 121
await executeAct({
gmail,
userEmail: user.email || "",
userEmail: user.email,
executedRule,
message,
});
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Removed null coalescing operator

The code previously used userEmail: user.email || "", but now uses userEmail: user.email without a fallback. Make sure that user.email is always defined to avoid potential issues with undefined or null values.

Verify that there are proper checks to ensure user.email is defined before this function is called:


🏁 Script executed:

#!/bin/bash
# Look for places where executeAct is called and check if there are any null checks for user.email
rg "executeAct\(" -B 10 -A 2 --glob "*.ts"

Length of output: 2588


Validate user.email before calling executeAct

The call in apps/web/utils/ai/choose-rule/run-rules.ts (around lines 116–121) now passes user.email directly, but there’s no guarantee it’s defined. To prevent runtime errors when it’s undefined or null, add a guard or fallback:

• File: apps/web/utils/ai/choose-rule/run-rules.ts (lines 116–121)

  • Before invoking executeAct, verify user.email is present.
  • Example using a fallback:
      if (shouldExecute) {
    -   await executeAct({ gmail, userEmail: user.email, executedRule, message });
    +   const email = user.email ?? "";
    +   await executeAct({ gmail, userEmail: email, executedRule, message });
      }
  • Or return/throw early on missing email:
    if (!user.email) {
      throw new Error("User email is required to execute actions");
    }
    await executeAct({ gmail, userEmail: user.email, executedRule, message });

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

🔭 Outside diff range comments (1)
apps/web/utils/cold-email/is-cold-email.ts (1)

1-228: 💡 Verification agent

🧩 Analysis chain

Verify email as foreign key in related tables

This file handles cold email detection and blocking, with all references to userId replaced by emailAccountId. While the changes look correct, ensure that all related database tables are updated to use email as the foreign key.


🏁 Script executed:

#!/bin/bash
# Check if the Prisma schema has been updated to use emailAccountId
rg "emailAccountId" apps/web/prisma/schema.prisma

# Check for any remaining userId references in cold email related code
rg "userId" --glob "**/*cold-email*" apps/web

Length of output: 2942


Fix lingering userId references in cold‑email code and tests

The Prisma schema is correctly using emailAccountId, but there are still userId references that need updating:

• apps/web/utils/cold-email/is-cold-email.test.ts
– Replace userId and userId_fromEmail in your mock fixtures and expectations with emailAccountId/emailAccountId_fromEmail.
• apps/web/utils/actions/cold-email.ts
– Update the userId: true selection and any where: { userId_… } clauses to use emailAccountId instead.

These changes will align your tests and action helpers with the updated Prisma foreign key.

🧹 Nitpick comments (3)
apps/web/app/api/user/rules/[id]/example/route.ts (1)

32-33: Consider updating authentication check for consistency

While the getExamples function now uses emailAccountId for authentication checks, the GET handler still directly checks session?.user.email. Consider updating this check for consistency with the rest of the refactoring.

- if (!session?.user.email)
+ const emailAccountId = session?.user.email;
+ if (!emailAccountId)
    return NextResponse.json({ error: "Not authenticated" });
apps/web/app/api/knowledge/route.ts (1)

13-14: Authentication refactor looks good, but variable name could be clearer.

The refactoring from user ID to email for authentication is consistent with the PR objective of moving relations to emailAccount. However, the variable name emailAccountId might be slightly misleading since it contains an email address, not an ID.

Consider renaming for clarity:

- const emailAccountId = session?.user.email;
- if (!emailAccountId) return NextResponse.json({ error: "Not authenticated" });
+ const email = session?.user.email;
+ if (!email) return NextResponse.json({ error: "Not authenticated" });
apps/web/utils/ai/choose-rule/ai-choose-rule.ts (1)

104-104: Consistent with other AI utility changes.

This change follows the same pattern as the other files, removing the fallback empty string for userEmail. Consider adding unit tests to verify the behavior when user.email is undefined if they don't already exist.

Since the email is logged to Braintrust on line 120, you might want to ensure consistent handling there as well if user.email can be undefined.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 2fc3424 and 5dcef89.

📒 Files selected for processing (28)
  • apps/web/__tests__/ai-choose-args.test.ts (1 hunks)
  • apps/web/__tests__/ai-extract-knowledge.test.ts (6 hunks)
  • apps/web/__tests__/ai-process-user-request.test.ts (2 hunks)
  • apps/web/app/(app)/reply-zero/onboarding/page.tsx (1 hunks)
  • apps/web/app/api/google/webhook/block-unsubscribed-emails.ts (2 hunks)
  • apps/web/app/api/google/webhook/process-history-item.ts (4 hunks)
  • apps/web/app/api/knowledge/route.ts (1 hunks)
  • apps/web/app/api/user/group/[groupId]/messages/controller.ts (1 hunks)
  • apps/web/app/api/user/group/[groupId]/messages/route.ts (2 hunks)
  • apps/web/app/api/user/group/[groupId]/rules/route.ts (2 hunks)
  • apps/web/app/api/user/labels/route.ts (1 hunks)
  • apps/web/app/api/user/rules/[id]/example/route.ts (1 hunks)
  • apps/web/app/api/user/stats/newsletters/summary/route.ts (2 hunks)
  • apps/web/utils/ai/assistant/process-user-request.ts (5 hunks)
  • apps/web/utils/ai/categorize-sender/ai-categorize-senders.ts (1 hunks)
  • apps/web/utils/ai/categorize-sender/ai-categorize-single-sender.ts (1 hunks)
  • apps/web/utils/ai/choose-rule/ai-choose-args.ts (1 hunks)
  • apps/web/utils/ai/choose-rule/ai-choose-rule.ts (1 hunks)
  • apps/web/utils/ai/choose-rule/ai-detect-recurring-pattern.ts (1 hunks)
  • apps/web/utils/ai/clean/ai-clean-select-labels.ts (1 hunks)
  • apps/web/utils/ai/clean/ai-clean.ts (1 hunks)
  • apps/web/utils/ai/example-matches/find-example-matches.ts (1 hunks)
  • apps/web/utils/ai/group/create-group.ts (2 hunks)
  • apps/web/utils/ai/reply/check-if-needs-reply.ts (1 hunks)
  • apps/web/utils/ai/rule/generate-prompt-on-delete-rule.ts (1 hunks)
  • apps/web/utils/ai/rule/generate-prompt-on-update-rule.ts (1 hunks)
  • apps/web/utils/ai/rule/generate-rules-prompt.ts (1 hunks)
  • apps/web/utils/cold-email/is-cold-email.ts (5 hunks)
✅ Files skipped from review due to trivial changes (2)
  • apps/web/tests/ai-process-user-request.test.ts
  • apps/web/tests/ai-extract-knowledge.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/web/app/api/google/webhook/process-history-item.ts
  • apps/web/utils/ai/assistant/process-user-request.ts
🧰 Additional context used
🧬 Code Graph Analysis (2)
apps/web/app/api/user/rules/[id]/example/route.ts (1)
apps/web/utils/error.ts (1)
  • SafeError (87-95)
apps/web/utils/cold-email/is-cold-email.ts (1)
apps/web/app/api/google/webhook/logger.ts (1)
  • logger (3-3)
🔇 Additional comments (41)
apps/web/app/api/google/webhook/block-unsubscribed-emails.ts (3)

15-17: Function signature updated correctly to use emailAccountId.

The parameter name has been changed from userId to emailAccountId in both the function parameter and type annotation, which aligns with the broader refactoring to an email-account-centric model.

Also applies to: 21-22


27-30: Database query properly updated to use emailAccountId.

The Prisma query's filter condition has been updated to use emailAccountId instead of userId, maintaining consistency with the schema changes that moved relations from User to EmailAccount.


40-41: Error logging updated to use emailAccountId.

The error logging context now correctly includes emailAccountId instead of userId, ensuring consistent error reporting throughout the application.

apps/web/app/api/user/rules/[id]/example/route.ts (2)

13-14: Correct implementation of email account identification

The change to use an intermediate variable emailAccountId instead of directly using session?.user.email is aligned with the broader refactoring effort to move from a user-centric model to an email-account-centric model.


17-17: Appropriate database query modification

The Prisma query has been correctly updated to filter by emailAccountId instead of userId, which aligns with the schema changes made as part of this refactoring.

apps/web/app/api/user/stats/newsletters/summary/route.ts (4)

10-12: Function signature updated to use emailAccountId

The parameter has been correctly updated from userId to emailAccountId to reflect the shift from a user-centric model to an email-account-centric model.


14-14: Updated query filter to use emailAccountId

The Prisma query's where clause has been properly updated to filter by emailAccountId instead of userId, consistent with the database schema changes.


31-31: Updated function call with emailAccountId

The function call has been correctly updated to pass emailAccountId instead of userId.


28-29:

✅ Verification successful

Verify email extraction logic

The code now uses session?.user.email as the emailAccountId, which suggests that the database is using email addresses as identifiers. This is a significant change from using traditional user IDs.


🏁 Script executed:

#!/bin/bash
# Check how EmailAccount schema is defined and how emailAccountId is used elsewhere
echo "Checking EmailAccount schema definition:"
cat $(fd "schema.prisma$") | grep -A 20 "model EmailAccount"

echo "\nChecking other usages of emailAccountId in API routes:"
rg "emailAccountId.*session.*user.email" --type ts

Length of output: 2318


Email as Primary Key Confirmed – No Changes Needed

The Prisma schema defines EmailAccount.email as the primary key, and every API route consistently derives emailAccountId from session.user.email. This aligns with all existing usages and requires no updates.

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

17-18:

✅ Verification successful

Database query filter updated correctly.

The query filter change from userId to emailAccountId correctly implements the migration from user-centric to email-account-centric model. The orderBy clause reformatting to a more concise inline object is a good styling improvement.


🏁 Script executed:

#!/bin/bash
# Verify Prisma schema has emailAccountId field on Knowledge model
grep -A 10 "model Knowledge" $(fd "schema.prisma" --type f)

Length of output: 407


Confirmed Prisma schema alignment for emailAccountId filter

The Prisma schema for the Knowledge model includes the emailAccountId field as demonstrated below, so the query update is correct:

• apps/web/prisma/schema.prisma (model Knowledge):

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

• apps/web/app/api/knowledge/route.ts (Lines 17–18):

where:    { emailAccountId },
orderBy:  { updatedAt: "desc" },

No further changes required—this migration from userId to emailAccountId is properly implemented, and the inline orderBy is a neat styling improvement.

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

8-10: LGTM: Consistent migration to email-based identification

The change from userId to emailAccountId for filtering labels correctly implements the shift from a user-centric to an email-account-centric model as described in the PR objective.


16-17: Authentication check looks correct

The code now properly extracts the email account ID from the session and checks for its existence before proceeding.


19-19: Proper function call with updated parameter name

The getLabels function is now correctly called with the emailAccountId parameter, completing the migration from user ID to email account ID.

apps/web/utils/ai/reply/check-if-needs-reply.ts (1)

73-73:

✅ Verification successful

Verify that user.email is always defined

The fallback to an empty string for userEmail has been removed, which aligns with the email-account-centric model. However, this assumes user.email is always defined.

Let's verify that the UserEmailWithAI type enforces the presence of an email:


🏁 Script executed:

#!/bin/bash
# Check the definition of UserEmailWithAI type
rg -A 3 "type UserEmailWithAI" --type ts

# Check if there are any null/undefined checks for user.email before calling chatCompletionObject
rg "chatCompletionObject\(\{[^}]*userEmail: .*\?\?" --type ts

Length of output: 261


🏁 Script executed:

#!/bin/bash
# Retrieve EmailAccount definition to inspect the 'email' property's type
rg -A5 "export interface EmailAccount" --type ts
rg -A5 "export type EmailAccount" --type ts

Length of output: 96


🏁 Script executed:

#!/bin/bash
# Show the top of the file and import context for EmailAccount in the TS types file
sed -n '1,30p' apps/web/utils/llms/types.ts
rg -n "EmailAccount" -C2 apps/web/utils/llms/types.ts

# Locate Prisma schema definition for EmailAccount
rg -n "model EmailAccount" -C5 --glob "*.prisma"

Length of output: 1338


Email is guaranteed by the Prisma schema

The EmailAccount model in schema.prisma declares email String @id (required), and the generated TypeScript EmailAccount type reflects that email: string (non‑nullable). Since UserEmailWithAI picks this field, user.email is always defined—no fallback is needed.

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

65-65:

✅ Verification successful

Consistent with email-account-centric model

The removal of the fallback to an empty string for userEmail is consistent with the PR's objective of moving to an email-account-centric model.

Let's verify that the chatCompletionObject function handles undefined email appropriately:


🏁 Script executed:

#!/bin/bash
# Check the implementation of chatCompletionObject to see how it handles userEmail
rg -A 10 "export (async )?function chatCompletionObject" --type ts

Length of output: 644


🏁 Script executed:

#!/bin/bash
echo "=== chatCompletionObjectInternal signature ==="
rg -n "async function chatCompletionObjectInternal" -A 20 -B 5 apps/web/utils/llms/index.ts

echo
echo "=== ChatCompletionObjectArgs definition ==="
rg -n "interface ChatCompletionObjectArgs" -A 10 apps/web/utils/llms/index.ts

Length of output: 1009


🏁 Script executed:

#!/bin/bash
echo "=== Top 50 lines of apps/web/utils/llms/index.ts ==="
sed -n '1,50p' apps/web/utils/llms/index.ts

echo
echo "=== All occurrences of ChatCompletionObjectArgs ==="
rg -n "ChatCompletionObjectArgs" -n .

echo
echo "=== Definitions of generateObject ==="
rg -nE "export (async )?function generateObject" -n .

echo
echo "=== Any internal helper around generateObject ==="
rg -n "generateObjectInternal" -n .

Length of output: 2168


🏁 Script executed:

#!/bin/bash
echo "=== Extract ChatCompletionObjectArgs definition ==="
sed -n '75,95p' apps/web/utils/llms/index.ts

Length of output: 535


Approve: userEmail is required by type, so no undefined fallback is needed

Type ChatCompletionObjectArgs defines userEmail: string as mandatory, and the chatCompletionObjectInternal function signature enforces that. Removing the '' fallback is safe and aligns with the email-centric model.

apps/web/utils/ai/clean/ai-clean.ts (1)

100-100: Consistent pattern across AI utility files

This change follows the same pattern seen in other AI utility files, removing the fallback to an empty string for userEmail. This is aligned with the email-account-centric model refactoring.

apps/web/utils/ai/clean/ai-clean-select-labels.ts (1)

38-38:

❓ Verification inconclusive

Empty string fallback removed for userEmail parameter

The change removes the previous fallback that would have converted undefined or null email values to an empty string. This aligns with the PR's objective to shift from user ID to email account as the primary identifier.

However, ensure that user.email is guaranteed to be defined throughout the authentication flow to prevent passing undefined values to the chatCompletionObject function.


🏁 Script executed:

#!/bin/bash
# Check if there are any null/undefined email handling in the authentication flow or userAi validation

# Look for email nullability checks in authentication code
echo "Checking for email null checks in auth code:"
rg -C 2 "user\.(email|emailAccountId)\s*(\?\?|!=\s*null|!==\s*null|\|\|)" --type ts apps/web/

Length of output: 1639


Empty string fallback removed for userEmail in ai-clean-select-labels.ts

The previous ?? "" fallback has been removed in apps/web/utils/ai/clean/ai-clean-select-labels.ts so that userEmail: user.email is passed directly. However, other parts of the codebase still guard against null/undefined emails (e.g. find-snippets.ts, rule-fix.ts, EmailList.tsx, Pending.tsx), which suggests session.data?.user.email may not always be defined.

Please confirm that your authentication/session setup (e.g. NextAuth callbacks or database schema) guarantees a non‑empty user.email. If not, either:

  • Enforce email presence at the source of truth, or
  • Reintroduce a fallback (e.g. user.email ?? "") when calling chatCompletionObject.
apps/web/utils/ai/categorize-sender/ai-categorize-senders.ts (1)

97-97:

❓ Verification inconclusive

Empty string fallback removed for userEmail parameter

The fallback to empty string for the userEmail parameter has been removed, consistent with the PR's shift to an email-account-centric model. Now user.email is passed directly to the chatCompletionObject function.

Ensure that any potential null or undefined email values are handled appropriately in the authentication flow or in the chatCompletionObject implementation to prevent unexpected behavior.


🏁 Script executed:

#!/bin/bash
# Check type definition of UserEmailWithAI to verify email property requirements

# Look at the UserEmailWithAI type definition
echo "Checking UserEmailWithAI type definition:"
rg -A 10 "export(\s+)type(\s+)UserEmailWithAI" --type ts apps/web/

# Check if there's validation for user.email before calls to AI functions
echo "Checking for validation of user.email before AI function calls:"
rg -B 5 "userEmail: user\.email" --type ts apps/web/utils/ai/

Length of output: 12821


🏁 Script executed:

#!/bin/bash
# Inspect the llms types file for EmailAccount import and UserEmailWithAI definition
echo "=== apps/web/utils/llms/types.ts ==="
sed -n '1,80p' apps/web/utils/llms/types.ts
echo ""
rg -n -C3 "UserEmailWithAI" --type ts apps/web/utils/llms/types.ts

# Search the entire repo for EmailAccount type/interface definitions
echo ""
echo "Searching for EmailAccount definitions across the repo:"
rg -n -A5 "export.*(interface|type).*EmailAccount" --type ts

Length of output: 966


🏁 Script executed:

#!/bin/bash
# Locate and inspect the Prisma schema to confirm EmailAccount.email nullability
schema_file=$(find . -maxdepth 3 -type f -name "schema.prisma" | head -n1)
echo "Schema file: $schema_file"
echo "=== EmailAccount model definition ==="
sed -n '/model EmailAccount/,/}/p' "$schema_file"

Length of output: 282


Verify non-nullability of user.email before AI calls

The empty‐string fallback was removed for the userEmail parameter across all chatCompletionObject invocations so that user.email is passed directly.

• The UserEmailWithAI type picks "email" from EmailAccount, but we haven’t confirmed whether EmailAccount.email is always non‑nullable in your Prisma schema.
• Ensure that your authentication flow guarantees user.email is neither null nor undefined before any AI function is called.
• Alternatively, validate or coerce user.email at the entrypoint of the AI layer (e.g., in chatCompletionObject) to avoid runtime errors.

Please confirm that the Prisma model defines email as required or add explicit checks/fallbacks where appropriate.

apps/web/utils/ai/categorize-sender/ai-categorize-single-sender.ts (1)

65-65:

✅ Verification successful

Empty string fallback removed for userEmail parameter

The code no longer uses a fallback empty string for userEmail, now passing user.email directly. This is part of the larger refactoring to use email instead of user ID as the primary identifier.

To ensure stability, verify that UserEmailWithAI type guarantees the presence of an email property and that the authentication flow ensures that user objects always have valid email values before reaching this code.


🌐 Web query:

What are best practices for handling potentially undefined values when making API calls to external services in TypeScript?

💡 Result:

Best Practices for Handling Potentially Undefined Values in TypeScript API Calls

When interfacing with external APIs in TypeScript, responses often include fields that are optional, undefined, or explicitly set to null. Robust handling of these values is crucial to avoid runtime errors and ensure type safety. Here are widely accepted best practices:


Enable Strict Null Checks

  • Always enable strictNullChecks in your tsconfig.json. This setting enforces explicit handling of null and undefined, and prevents many common bugs by making the compiler alert you to places where you might access a value that might not be defined[4][5][9].

Use Union Types and Optional Types

  • Define your API response interfaces to reflect reality by making properties optional or explicitly unioned with undefined or null as appropriate:
    interface UserResponse {
      name?: string;
      age: number | undefined;
      email: string | null;
    }
    This makes the intent clear and helps TypeScript catch improper accesses[1][4][5].

Utilize Optional Chaining (?.)

  • Use optional chaining to safely access nested properties that may be undefined:
    const city = response.address?.city;
    This prevents runtime errors when intermediate properties are missing[1][6][9].

Leverage Nullish Coalescing (??) for Fallbacks

  • The nullish coalescing operator provides concise, safe fallbacks for undefined or null values:
    const userName = response.user?.name ?? 'Anonymous';
    This ensures only null or undefined trigger the fallback, not other falsy values like '' or 0[1][9].

Explicit Checks for Undefined/Null

  • Before using values that may be undefined or null, validate them:
    if (user.email !== undefined && user.email !== null) {
      // safe to use user.email
    }
    Or, for a concise approach:
    if (user.email != null) { /* ... */ }
    This guards against both null and undefined[2][6].

Type Guards and Narrowing

  • Use if or switch statements to narrow the type and ensure values are defined before use, allowing TypeScript to infer the safe type within the guard:
    if (result) {
      // result is not undefined or null here
    } else {
      // handle the undefined/null case
    }
    This approach is the safest and most explicit[2][7].

Be Cautious with Non-Null Assertion (!)

  • The ! operator forces TypeScript to treat a value as defined, but it bypasses safety and can introduce runtime errors if misused:
    const length = response.name!.length; // unsafe if name can be undefined
    Use this only when you're absolutely certain the value is defined at that point in your code[2][6][9].

Throw Explicit Errors Where Needed

  • If undefined values are unexpected or indicate a logic error, throw clear, descriptive errors:
    if (!user.email) {
      throw new Error("Email is required but undefined.");
    }
    This makes debugging easier and failures more informative[2].

Model API Responses Accurately

  • When designing interfaces for API responses, match the external service’s schema as closely as possible. If the API may omit fields, mark them as optional or include undefined and/or null in their type definitions[3][10].

Summary Table

Technique Purpose Safe?
strictNullChecks Enforces explicit undefined/null handling Yes
Union/Optional types Models data accurately, prevents unsafe access Yes
Optional chaining (?.) Safe access for nested possibly-undefined values Yes
Nullish coalescing (??) Provides default/fallback values when undefined or null Yes
Type guards/explicit checks Ensures value is defined before use Yes
Non-null assertion (!) Bypasses type checking, use with caution No*
Throwing explicit errors Defensive failure handling for unexpected undefined Yes

(*Non-null assertion is unsafe if the variable can truly be undefined/null.)


By following these practices, you can write safer and more robust TypeScript code when handling potentially undefined values in API responses[1][2][4][5][6][7][9].

Citations:


🏁 Script executed:

#!/bin/bash
# Search for UserEmailWithAI definition
rg -n "type UserEmailWithAI" -g "apps/web/utils/ai/categorize-sender/*.ts"
rg -n "interface UserEmailWithAI" -g "apps/web/utils/ai/categorize-sender/*.ts"

# Find the function handling the payload and inspect surrounding context
rg -n "categorizeSingleSender" -g "apps/web/utils/ai/categorize-sender/*.ts" -A 5 -B 5

# Locate all user.email references in this module
rg -n "user.email" -g "apps/web/utils/ai/categorize-sender/*.ts" -C 3

Length of output: 1526


🏁 Script executed:

#!/bin/bash
# Show imports and function signature for categorizeSingleSender
sed -n '1,80p' apps/web/utils/ai/categorize-sender/ai-categorize-single-sender.ts

Length of output: 2520


🏁 Script executed:

#!/bin/bash
# Locate the file defining UserEmailWithAI
file=$(rg -l "type UserEmailWithAI" -g "apps/**/*.ts")
echo "Found file: $file"
# Display the type definition and related lines
sed -n '1,200p' "$file"

Length of output: 463


🏁 Script executed:

#!/bin/bash
# Locate all Prisma schema files
echo "Prisma files:"
find . -maxdepth 4 -type f -name "*.prisma"

# Display the EmailAccount model definition from each schema
for file in $(find . -maxdepth 4 -type f -name "*.prisma"); do
  echo -e "\n--- $file ---"
  sed -n '/model EmailAccount {/,/}/p' "$file"
done

Length of output: 1837


Confirm non-nullable user.email guarantee

The removal of the "" fallback for userEmail is safe:

- The UserEmailWithAI type is defined as a Pick<EmailAccount, "userId" | "email" | "about"> & UserAIFields.
- In your Prisma schema, model EmailAccount { email String @id … } marks email as a required, non-nullable field.

This ensures at compile‑time (via TypeScript’s strict null checks) and at runtime (via the database schema) that user.email is always a valid string. No further fallback is needed.

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

60-60:

✅ Verification successful

Potential edge case with undefined email.

The change removes a fallback empty string for userEmail parameter, which aligns with the PR's objective of moving to an email-account-centric model. However, ensure that the chatCompletionObject function properly handles cases where user.email could be undefined or null.


🏁 Script executed:

#!/bin/bash
# Check if chatCompletionObject handles undefined userEmail
grep -r "userEmail" --include="*.ts" apps/web/utils/llms

Length of output: 1334


I’d like to confirm how chatCompletionObject is declared to see how it types and uses userEmail. Let’s pull its signature and immediate implementation context:


🏁 Script executed:

#!/bin/bash
# Show declaration and use of chatCompletionObject
grep -n -C4 "chatCompletionObject" apps/web/utils/llms/index.ts

Length of output: 417


🏁 Script executed:

#!/bin/bash
# Find ChatCompletionObjectArgs definition
grep -n -C5 "ChatCompletionObjectArgs" apps/web/utils/llms/index.ts

# Show function signature for generatePromptOnDeleteRule
grep -n -C5 "function generatePromptOnDeleteRule" apps/web/utils/ai/rule/generate-prompt-on-delete-rule.ts

Length of output: 1240


Let’s verify whether user.email can actually be undefined by inspecting the UserEmailWithAI type:


🏁 Script executed:

#!/bin/bash
# Locate where UserEmailWithAI is defined
grep -R "UserEmailWithAI" -n apps/web

# Show its declaration context
grep -n -C3 "type UserEmailWithAI" -n $(grep -R "UserEmailWithAI" -l apps/web)

Length of output: 10976


Here’s how UserEmailWithAI and related types are defined:

Let me know once we have those details.


🏁 Script executed:

#!/bin/bash
# Show import and definitions in types.ts
sed -n '1,50p' apps/web/utils/llms/types.ts

Length of output: 285


🏁 Script executed:

#!/bin/bash
# Locate EmailAccount definition to see if email is optional
grep -R "export type EmailAccount" -n .

Length of output: 41


🏁 Script executed:

#!/bin/bash
# Locate Prisma schema file(s) and inspect EmailAccount model definition
find . -type f -name '*.prisma' -exec grep -n -C3 "model EmailAccount" {} \;

Length of output: 370


No Action Required: user.email Is Always Defined

The Prisma model defines EmailAccount.email as a non-nullable String @id, and UserEmailWithAI.email is typed as string. Since ChatCompletionObjectArgs.userEmail also requires a string, user.email can never be undefined or null. Removing the empty‐string fallback is safe and no further handling is needed.

apps/web/utils/ai/choose-rule/ai-detect-recurring-pattern.ts (1)

106-106: Consistent change removing email fallback.

This change aligns with the PR objective of shifting from user ID to email-based identification. Make sure to verify that all callers provide a valid email value and that error handling is in place for cases where the email might be missing.

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

121-121: Consistent approach to email-based identification.

The removal of the fallback empty string matches the pattern across other AI utility files, maintaining consistency with your migration to an email-account-centric model.

apps/web/utils/ai/choose-rule/ai-choose-args.ts (1)

94-94: Consistent parameter refactoring for user identification

This change aligns with the broader refactor shifting from userId to email as the primary identifier.

apps/web/app/api/user/group/[groupId]/messages/route.ts (2)

9-10: Refactored authentication to use email identifier

This change properly extracts the email as the identifier and uses it for authentication checks.


19-19: Updated parameter to use emailAccountId

Properly updated the getGroupEmails call to use the email-based identifier consistent with the controller changes.

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

188-188: Updated test helper to use emailAccountId

Good job updating the test helper to reflect the data model changes, ensuring tests remain valid with the new schema.

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

103-103: Removed fallback to empty string for consistent user identification

This change aligns with the broader refactor to use email as a direct identifier without fallbacks.


174-174: Removed fallback to empty string in second occurrence

Consistent with the previous change, maintaining the same pattern throughout the file.

apps/web/app/(app)/reply-zero/onboarding/page.tsx (2)

13-16: LGTM! Database query updated correctly

The Prisma query has been properly updated to filter by emailAccountId instead of userId.


9-10:

❓ Verification inconclusive

Verify that email is properly validated before using as ID

The code now uses session?.user.email as the emailAccountId instead of session?.user.id. This aligns with the overall refactoring to an email-account-centric model, but ensure that proper email validation is in place elsewhere in the authentication flow.


🏁 Script executed:

#!/bin/bash
# Check if there's email validation in the auth setup
rg -A 3 -B 3 "user.email" apps/web/app/api/auth

Length of output: 47


Validate email before using as account ID
I didn’t find any explicit email‐validation logic in the auth API—before relying on session.user.email as your primary identifier, please confirm that incoming emails are properly validated and sanitized. In particular, verify validation in:

  • apps/web/app/api/auth/[...nextauth].ts (NextAuth email provider setup)
  • Any custom sign‑up or user‐creation handlers (e.g. apps/web/app/api/auth/signup.ts)

Ensure you enforce a robust email regex or schema (e.g. via Zod) and reject invalid addresses at authentication time.

apps/web/app/api/user/group/[groupId]/messages/controller.ts (2)

24-38: Function signature updated correctly

The function signature and type definition have been properly updated to use emailAccountId instead of userId, which aligns with the refactoring to an email-account-centric model.


39-42: Database query updated correctly

The Prisma query has been properly updated to filter by emailAccountId instead of userId.

apps/web/app/api/user/group/[groupId]/rules/route.ts (4)

9-15: Function signature updated correctly

The function signature and parameter types have been properly updated to use emailAccountId instead of userId.


16-25: Database query updated correctly

The Prisma query has been properly updated to filter by emailAccountId instead of userId.


32-35: Authentication check updated to use email

The authentication check now correctly extracts and verifies the user's email from the session instead of the user ID.


40-40: Function call updated correctly

The function call to getGroupRules has been updated to pass emailAccountId instead of userId.

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

23-48: Email parameter usage consistent with refactoring

The isColdEmail function's usage of user.email is consistent with the refactoring approach. The reference to emailAccountId on line 47 has been updated correctly.


87-105: Update to composite key field names

The function isKnownColdEmailSender has been correctly updated to use emailAccountId instead of userId, and the composite key has been changed from userId_fromEmail to emailAccountId_fromEmail.


138-149: Direct use of email without fallback

The chatCompletionObject call now directly uses user.email without falling back to an empty string, which is more consistent with the new approach where email is the primary identifier.


175-191: Database operations updated correctly

The upsert operation has been properly updated to use emailAccountId_fromEmail as the composite key and user.email as the emailAccountId value.


204-204: Logging updated to use emailAccountId

The error logging has been updated to use emailAccountId instead of userId, which is consistent with the refactoring approach.

@@ -105,7 +105,7 @@ Remember, precision is crucial - only include matches you are absolutely sure ab
parameters: findExampleMatchesSchema,
},
},
userEmail: user.email || "",
userEmail: user.email,
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Empty string fallback removed for userEmail parameter

The fallback to empty string (user.email || "") has been removed, now directly passing user.email to the chatCompletionTools function. This is consistent with the PR's objective to refactor toward an email-account-centric model.

Make sure that all authentication flows properly validate the presence of user.email before reaching this code to avoid potential issues with undefined values being passed to the AI service.


🏁 Script executed:

#!/bin/bash
# Check if the chatCompletionTools function has null-safety for userEmail parameter

# Look at the implementation of chatCompletionTools
echo "Checking how chatCompletionTools handles null/undefined userEmail:"
rg -A 5 -B 5 "export (const|function) chatCompletionTools" --type ts apps/web/utils/

Length of output: 229


🏁 Script executed:

#!/bin/bash
# Search entire repository for the definition of chatCompletionTools
rg -n "chatCompletionTools" -C 3 --type ts .

Length of output: 10616


🏁 Script executed:

#!/bin/bash
# Extract the implementation of chatCompletionTools and its internal function
echo "=== chatCompletionTools & chatCompletionToolsInternal implementation ==="
sed -n '200,260p' apps/web/utils/llms/index.ts

# Locate the ChatCompletionToolsArgs type definition
echo "=== ChatCompletionToolsArgs type definition ==="
rg -n "ChatCompletionToolsArgs" -A5 -B5 --type ts apps/web/utils/llms

Length of output: 3243


Missing userEmail fallback in chatCompletionTools

The user.email || "" fallback was removed, and user.email is now passed directly to chatCompletionTools. Since chatCompletionToolsInternal uses userEmail when saving usage and handling errors, an undefined value will propagate and may cause unexpected failures.

• File: apps/web/utils/ai/example-matches/find-example-matches.ts line 108 – now passes user.email without a default.
• Action: Ensure every auth flow asserts user.email is non-null before this call, or reinstate a fallback (e.g. user.email ?? "").
• Consider tightening the ChatCompletionToolsArgs type to require a non-optional userEmail.

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

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

88-93: Consider adding a TypeScript interface for the return type

The return object now includes the accountId alongside other properties, which correctly supports the email-account-centric refactoring. However, the function lacks an explicit TypeScript return type.

Consider defining a TypeScript interface for the return type to improve code readability and maintainability:

interface GmailClientResult {
  gmail: any; // Use appropriate type from Gmail client
  accessToken: string;
  userId: string;
  accountId: string;
}

// Then update the function signature:
export async function validateApiKeyAndGetGmailClient(request: NextRequest): Promise<GmailClientResult> {
  // ...existing implementation...
}
apps/web/app/api/v1/reply-tracker/route.ts (2)

83-86: Consider updating userId in logger

The logger statement still uses userId for identification. Consider updating this to use emailAccountId for consistency with the refactoring.

logger.info("Retrieved emails needing reply", {
-  userId,
+  emailAccountId,
  count: response.emails.length,
});

90-93: Consider updating userId in error logger

Similarly, the error logger still uses userId. Consider updating this to use emailAccountId for consistency.

logger.error("Error retrieving emails needing reply", {
-  userId,
+  emailAccountId,
  error,
});
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 5dcef89 and d9e4ab5.

📒 Files selected for processing (6)
  • apps/web/app/api/v1/group/[groupId]/emails/route.ts (2 hunks)
  • apps/web/app/api/v1/group/[groupId]/emails/validation.ts (1 hunks)
  • apps/web/app/api/v1/helpers.ts (1 hunks)
  • apps/web/app/api/v1/reply-tracker/route.ts (4 hunks)
  • apps/web/app/api/v1/reply-tracker/validation.ts (1 hunks)
  • apps/web/utils/api-auth.ts (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
apps/web/app/api/v1/group/[groupId]/emails/route.ts (4)
apps/web/utils/middleware.ts (1)
  • withError (16-70)
apps/web/utils/api-auth.ts (1)
  • validateApiKeyAndGetGmailClient (67-94)
apps/web/app/api/v1/helpers.ts (1)
  • getEmailAccountId (9-36)
apps/web/app/api/user/group/[groupId]/messages/controller.ts (1)
  • getGroupEmails (24-55)
🔇 Additional comments (16)
apps/web/app/api/v1/reply-tracker/validation.ts (1)

7-7: LGTM: Optional email parameter correctly added

The addition of an optional email field to the validation schema aligns with the codebase refactoring towards an email-account-centric model. The implementation using Zod is correct.

apps/web/app/api/v1/group/[groupId]/emails/validation.ts (1)

8-8: LGTM: Optional email parameter correctly added

The addition of an optional email field to the group emails query schema is correctly implemented using Zod. This aligns with the codebase refactoring towards an email-account-centric model.

apps/web/utils/api-auth.ts (1)

43-43: LGTM: Account ID selection correctly added

Adding the id field to the accounts selection is correctly implemented, enabling the return of the account ID in the validation function.

apps/web/app/api/v1/group/[groupId]/emails/route.ts (7)

1-1: Import optimization - unused import removed

Good job removing the unused NextRequest import and keeping only NextResponse.


9-9: Addition of helper import

The new getEmailAccountId helper import aligns with the broader refactoring to an email-account-centric model.


12-13: Updated destructuring to include accountId

Good update to extract the accountId from the validateApiKeyAndGetGmailClient result, which is required for the new email account resolution logic.


31-32: Updated query parameter extraction

Correctly updated to extract the optional email parameter from the validated query data.


33-37: Email account resolution implementation

Good implementation of the email account resolution logic using the new helper function.


39-44: Proper error handling for missing email account

Appropriate error handling when no email account is found, returning a clear error message with a 400 status code.


48-48: Updated parameter name in function call

Correctly updated to use emailAccountId instead of the previous userId parameter in the call to getGroupEmails.

apps/web/app/api/v1/reply-tracker/route.ts (6)

1-1: Import optimization - unused import removed

Good job removing the unused NextRequest import and keeping only NextResponse.


13-13: Addition of helper import

The new getEmailAccountId helper import aligns with the broader refactoring to an email-account-centric model.


18-19: Updated destructuring to include accountId

Good update to extract the accountId from the validateApiKeyAndGetGmailClient result, which is required for the new email account resolution logic.


33-37: Email account resolution implementation

Good implementation of the email account resolution logic using the new helper function. This correctly extracts the optional email parameter from the query result.


39-44: Proper error handling for missing email account

Appropriate error handling when no email account is found, returning a clear error message with a 400 status code.


54-54: Updated parameter in function call

Correctly updated to use email: emailAccountId in the call to getPaginatedThreadTrackers, replacing the previous userId-based approach.

Comment on lines +1 to +36
import prisma from "@/utils/prisma";

/**
* Gets the email account ID from the provided email or looks it up using the account ID
* @param email Optional email address
* @param accountId Account ID to look up the email if not provided
* @returns The email account ID or undefined if not found
*/
export async function getEmailAccountId({
email,
accountId,
userId,
}: {
email?: string;
accountId?: string;
userId: string;
}): Promise<string | undefined> {
if (email) {
// check user owns email account
const emailAccount = await prisma.emailAccount.findUnique({
where: { email, userId },
select: { email: true },
});

return emailAccount?.email;
}

if (!accountId) return undefined;

const emailAccount = await prisma.emailAccount.findFirst({
where: { accountId },
select: { email: true },
});

return emailAccount?.email;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Well-implemented helper function for email account ID resolution

This new helper function getEmailAccountId correctly implements the logic to resolve an email account ID from either an email address or an account ID, scoped by user ID. This supports the refactoring from user-centric to email-account-centric model.

However, when looking up by accountId, there's no check that the account belongs to the given userId (unlike the email lookup). This could potentially allow a user to access email accounts they don't own if they know the accountId.

Consider adding the userId check to the accountId lookup:

  const emailAccount = await prisma.emailAccount.findFirst({
-   where: { accountId },
+   where: { accountId, userId },
    select: { email: true },
  });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import prisma from "@/utils/prisma";
/**
* Gets the email account ID from the provided email or looks it up using the account ID
* @param email Optional email address
* @param accountId Account ID to look up the email if not provided
* @returns The email account ID or undefined if not found
*/
export async function getEmailAccountId({
email,
accountId,
userId,
}: {
email?: string;
accountId?: string;
userId: string;
}): Promise<string | undefined> {
if (email) {
// check user owns email account
const emailAccount = await prisma.emailAccount.findUnique({
where: { email, userId },
select: { email: true },
});
return emailAccount?.email;
}
if (!accountId) return undefined;
const emailAccount = await prisma.emailAccount.findFirst({
where: { accountId },
select: { email: true },
});
return emailAccount?.email;
}
import prisma from "@/utils/prisma";
/**
* Gets the email account ID from the provided email or looks it up using the account ID
* @param email Optional email address
* @param accountId Account ID to look up the email if not provided
* @param userId ID of the user (to scope lookups)
* @returns The email account ID or undefined if not found
*/
export async function getEmailAccountId({
email,
accountId,
userId,
}: {
email?: string;
accountId?: string;
userId: string;
}): Promise<string | undefined> {
if (email) {
// check user owns email account
const emailAccount = await prisma.emailAccount.findUnique({
where: { email, userId },
select: { email: true },
});
return emailAccount?.email;
}
if (!accountId) return undefined;
const emailAccount = await prisma.emailAccount.findFirst({
where: { accountId, userId },
select: { email: true },
});
return emailAccount?.email;
}

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

🧹 Nitpick comments (2)
apps/web/components/AccountSwitcher.tsx (2)

94-101: "Add account" menu item lacks implementation.

The "Add account" menu item is currently just a UI element without any functionality (no onClick handler). This should be implemented or marked with a TODO comment if intended for a future PR.

-            <DropdownMenuItem className="gap-2 p-2">
+            <DropdownMenuItem className="gap-2 p-2" onClick={() => {/* TODO: Implement add account functionality */}}>
               <div className="flex size-6 items-center justify-center rounded-md border bg-background">
                 <Plus className="size-4" />
               </div>
               <div className="font-medium text-muted-foreground">
                 Add account
               </div>
             </DropdownMenuItem>

90-90: Keyboard shortcuts shown but not implemented.

The dropdown displays keyboard shortcuts (⌘1, ⌘2, etc.) but there don't appear to be any actual keyboard event handlers to make these shortcuts functional.

Consider adding actual keyboard shortcut handlers or marking with a TODO if this will be implemented later.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between d9e4ab5 and 066425a.

📒 Files selected for processing (3)
  • apps/web/components/AccountSwitcher.tsx (1 hunks)
  • apps/web/components/SideNav.tsx (3 hunks)
  • apps/web/components/SideNavWithTopNav.tsx (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/web/components/SideNavWithTopNav.tsx
🧰 Additional context used
🧬 Code Graph Analysis (1)
apps/web/components/AccountSwitcher.tsx (2)
apps/web/components/ui/sidebar.tsx (4)
  • useSidebar (776-776)
  • SidebarMenu (763-763)
  • SidebarMenuItem (767-767)
  • SidebarMenuButton (766-766)
apps/web/components/ui/dropdown-menu.tsx (7)
  • DropdownMenu (185-185)
  • DropdownMenuTrigger (186-186)
  • DropdownMenuContent (187-187)
  • DropdownMenuLabel (191-191)
  • DropdownMenuItem (188-188)
  • DropdownMenuShortcut (193-193)
  • DropdownMenuSeparator (192-192)
🔇 Additional comments (7)
apps/web/components/SideNav.tsx (4)

53-53: Import added for new AccountSwitcher component.

This import allows using the new account switching functionality in the sidebar, aligning with the PR objective of moving to an email-account-centric model.


231-231: Component renamed from AppSidebar to SideNav.

The rename makes the component name more specific and descriptive of its function as a side navigation element, improving code clarity.


255-264: SidebarHeader now renders unconditionally with AccountSwitcher.

The header structure has been improved to:

  1. Always render the SidebarHeader regardless of sidebar state
  2. Only conditionally render the logo when expanded
  3. Include the new AccountSwitcher component

This change supports the email-account-centric model by prominently featuring account switching functionality in the UI.


259-259: Logo size reduced.

The logo height has been reduced to 3.5 (likely 3.5rem or a similar unit) which helps balance the visual hierarchy with the newly added AccountSwitcher component.

apps/web/components/AccountSwitcher.tsx (3)

1-107: New AccountSwitcher component implements email account selection UI.

This component successfully implements a UI for switching between multiple email accounts, which is a key part of the PR objective to move to an email-account-centric model.


46-46: State management for active account.

The component manages local UI state for the selected account but doesn't appear to sync this selection with any global state or make API calls when an account is switched.

Consider whether this component should trigger actual account switching in the application, or if this will be implemented in a future PR.


76-76: Responsive dropdown positioning.

Good implementation of responsive design by changing the dropdown position based on whether the sidebar is in mobile mode.

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/app/api/user/accounts/route.ts (1)

7-7: Unused logger variable.

The logger is initialized but never used in the code. Either use it for logging important events or remove it.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 716ed96 and 653f533.

📒 Files selected for processing (2)
  • apps/web/app/api/user/accounts/route.ts (1 hunks)
  • apps/web/hooks/useAccounts.ts (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
apps/web/hooks/useAccounts.ts (1)
apps/web/app/api/user/accounts/route.ts (1)
  • GetAccountsResponse (9-9)
🔇 Additional comments (1)
apps/web/hooks/useAccounts.ts (1)

1-8: Well-implemented data fetching hook.

This is a clean implementation of a React hook using SWR to fetch email account data. Setting revalidateOnFocus: false is a good choice to avoid unnecessary refetches when the user returns to the tab.

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

🧹 Nitpick comments (3)
apps/web/components/AccountSwitcher.tsx (3)

25-36: Add a lightweight loading / empty‑state instead of returning null.

When useAccounts() is still loading the SWR cache, the component renders nothing, which causes a brief layout shift once the data arrives. A tiny placeholder (spinner / skeleton avatar) makes the UI feel more responsive and avoids CLS.

-  return (
-    <AccountSwitcherInternal
-      accounts={accountsData?.accounts ?? []}
-      accountId={accountId}
-      setAccountId={setAccountId}
-    />
-  );
+  if (!accountsData) {
+    // TODO: replace with your design‐system skeleton
+    return <div className="animate-pulse h-8 w-24 rounded-md bg-muted" />;
+  }
+
+  return (
+    <AccountSwitcherInternal
+      accounts={accountsData.accounts}
+      accountId={accountId}
+      setAccountId={setAccountId}
+    />
+  );

85-94: Guard keyboard shortcuts > 9 to avoid impossible chords.

⌘{index + 1} yields “⌘10”, “⌘11”… for larger lists, which aren’t single‑key shortcuts and may confuse users.
Consider limiting to the first nine accounts or switching to letters after 9.

-                <DropdownMenuShortcut>⌘{index + 1}</DropdownMenuShortcut>
+                {index < 9 && (
+                  <DropdownMenuShortcut>⌘{index + 1}</DropdownMenuShortcut>
+                )}

118-125: Provide an accessible alt description for profile images.

alt="" hides the image from assistive tech, but the avatar is meaningful (it identifies the account).
Pass the user’s name to improve accessibility; fall back to a generic label if unavailable.

-      alt=""
+      alt={name ? `${name}'s avatar` : 'Account avatar'}

You’d need to extend ProfileImage’s props with name?: string and supply it at call‑sites.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 653f533 and 609e433.

📒 Files selected for processing (2)
  • apps/web/app/api/user/accounts/route.ts (1 hunks)
  • apps/web/components/AccountSwitcher.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/app/api/user/accounts/route.ts
🧰 Additional context used
🧬 Code Graph Analysis (1)
apps/web/components/AccountSwitcher.tsx (4)
apps/web/hooks/useAccounts.ts (1)
  • useAccounts (4-8)
apps/web/app/api/user/accounts/route.ts (1)
  • GetAccountsResponse (6-6)
apps/web/components/ui/sidebar.tsx (4)
  • useSidebar (776-776)
  • SidebarMenu (763-763)
  • SidebarMenuItem (767-767)
  • SidebarMenuButton (766-766)
apps/web/components/ui/dropdown-menu.tsx (7)
  • DropdownMenu (185-185)
  • DropdownMenuTrigger (186-186)
  • DropdownMenuContent (187-187)
  • DropdownMenuLabel (191-191)
  • DropdownMenuItem (188-188)
  • DropdownMenuShortcut (193-193)
  • DropdownMenuSeparator (192-192)

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.

Add Multiple Email Addresses
1 participant