Conversation
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
|
Warning Rate limit exceeded@elie222 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 19 minutes and 7 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (6)
WalkthroughThis update migrates user-related data and logic throughout the application from a user ID–centric model to an email account–centric model. The database schema and corresponding Prisma models are refactored to store user settings, AI configuration, and related fields directly on the Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant API
participant DB
Client->>API: Request with user email (not userId)
API->>DB: Query EmailAccount by email
DB-->>API: Return EmailAccount with settings, AI config, etc.
API-->>Client: Respond with data/actions using email-based identification
sequenceDiagram
participant API
participant Redis
API->>Redis: Save or retrieve data keyed by email
Redis-->>API: Return data (e.g., categorization progress, replies)
Possibly related PRs
Poem
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 11
🔭 Outside diff range comments (4)
apps/web/utils/actions/reply-tracking.ts (1)
59-89:⚠️ Potential issueInconsistent user identification in resolveThreadTrackerAction
While other functions in this file have been updated to use email-based identification,
resolveThreadTrackerActionstill usesuserId. This could potentially cause issues with multi-account support.Consider updating this function to use email-based identification for consistency with the rest of the codebase:
export const resolveThreadTrackerAction = withActionInstrumentation( "resolveThreadTracker", async (unsafeData: ResolveThreadTrackerBody) => { const session = await auth(); - const userId = session?.user.id; - if (!userId) return { error: "Not logged in" }; + const email = session?.user.email; + if (!email) return { error: "Not logged in" }; + + const emailAccount = await prisma.emailAccount.findUnique({ + where: { email }, + select: { userId: true }, + }); + + if (!emailAccount) return { error: "Email account not found" }; + const userId = emailAccount.userId; const { data, success, error } = resolveThreadTrackerSchema.safeParse(unsafeData); if (!success) return { error: error.message }; await startAnalyzingReplyTracker(userId).catch((error) => { logger.error("Error starting Reply Zero analysis", { error }); }); await prisma.threadTracker.updateMany({ where: { threadId: data.threadId, userId, }, data: { resolved: data.resolved }, }); await stopAnalyzingReplyTracker(userId).catch((error) => { logger.error("Error stopping Reply Zero analysis", { error }); }); revalidatePath("/reply-zero"); return { success: true }; }, );apps/web/utils/redis/clean.ts (2)
75-104:⚠️ Potential issueMissing refactoring: getThreadsByJobId still using userId
This function hasn't been updated to use email instead of userId for key patterns and parameters, which is inconsistent with the other changes in this file.
-export async function getThreadsByJobId( - userId: string, - jobId: string, - limit = 1000, -) { - const pattern = `thread:${userId}:${jobId}:*`; +export async function getThreadsByJobId( + email: string, + jobId: string, + limit = 1000, +) { + const pattern = `thread:${email}:${jobId}:*`;
106-127:⚠️ Potential issueMissing refactoring: deleteAllUserData still using userId
This function hasn't been updated to use email instead of userId, which is inconsistent with the other changes in this file and could lead to data management issues.
-export async function deleteAllUserData(userId: string) { - // Delete all thread keys for this user - const threadPattern = `thread:${userId}:*`; +export async function deleteAllUserData(email: string) { + // Delete all thread keys for this user + const threadPattern = `thread:${email}:*`;apps/web/utils/cold-email/is-cold-email.ts (1)
95-101:⚠️ Potential issueComposite‑key misuse breaks the query – switch to
findFirstor narrow thewhereclause.
prisma.coldEmail.findUniquecan only receive the full unique/composite key.
Appendingstatusinsidewheremakes the call invalid and will throw at runtime:-const coldEmail = await prisma.coldEmail.findUnique({ - where: { - userId_fromEmail: { userId, fromEmail: from }, - status: ColdEmailStatus.AI_LABELED_COLD, - }, - select: { id: true }, -}); +const coldEmail = await prisma.coldEmail.findFirst({ + where: { + userId, + fromEmail: from, + status: ColdEmailStatus.AI_LABELED_COLD, + }, + select: { id: true }, +});This keeps the semantics (unique hit expected) while complying with Prisma’s rules.
Alternatively, callfindUniquewithout thestatusfilter and checkstatusafterwards.
♻️ Duplicate comments (1)
apps/web/utils/actions/clean.ts (1)
309-312: Repeat the email filter when queryingcleanupThread.See comment above; mirror the change here to keep the data scoped to the correct email account.
🧹 Nitpick comments (9)
apps/web/utils/actions/categorize.ts (1)
37-38: Return value ofvalidateUserAndAiAccessis discarded
You callvalidateUserAndAiAccessonly for its side‑effect of throwing an
error, then immediately drop the returnedemailAccount.
If later logic needs the enriched object (e.g. AI provider/model), re‑use the
validated result to avoid an extra DB round‑trip elsewhere.apps/web/app/api/resend/summary/route.ts (1)
241-244:lastSummaryEmailAtmoved toemailAccount– great, but keep user field in sync
If the oldUser.lastSummaryEmailAtcolumn still exists, consider
deprecating or synchronising it to avoid stale data powering legacy code
paths or dashboards.apps/web/utils/cold-email/is-cold-email.ts (1)
186-198: IncludeOnly
userIdis logged when a label ID is missing. Adding the email (already available) helps triage multi‑account issues.-logger.error("No gmail label id", { userId: user.userId }); +logger.error("No gmail label id", { + userId: user.userId, + email: user.email, +});apps/web/app/api/google/watch/all/route.ts (2)
110-118: Tokens partially validatedYou verify
access_tokenandrefresh_tokenbut notproviderAccountId, which is mandatory forgetGmailClientWithRefresh. A missing ID will lead to a runtime throw further down. Add the check here to fail fast.- if (!emailAccount.account?.access_token || !emailAccount.account?.refresh_token) + if ( + !emailAccount.account?.access_token || + !emailAccount.account?.refresh_token || + !emailAccount.account?.providerAccountId + )
134-135: Log key misnamedThe structured‑log property is
userIdbut the value is an email address. Use-logger.error("Error for user", { userId: emailAccount.email, error }); +logger.error("Error for user", { email: emailAccount.email, error });apps/web/utils/upstash/categorize-senders.ts (1)
28-48: Parallelism hard‑coded to 3For very large sender lists the queue may throttle. Consider making
parallelismconfigurable via env or an optional parameter so ops can tune without redeploying.apps/web/app/api/ai/analyze-sender-pattern/route.ts (1)
168-171: Consider updating function signature for consistency.While the function implementation is correctly using userId from the emailAccount, consider updating the function signature to accept an email parameter instead of userId directly to maintain consistency with the refactoring approach.
async function savePatternCheck({ - userId, - from, -}: { userId: string; from: string }) { + email, + from, +}: { email: string; from: string }) {Then fetch the userId from the emailAccount internally.
apps/web/app/api/clean/gmail/route.ts (1)
45-47: Update error message for clarity.The error message still refers to "User not found" when it's checking for an email account, which might be confusing.
- if (!account) throw new SafeError("User not found", 404); + if (!account) throw new SafeError("Email account not found", 404);apps/web/utils/ai/choose-rule/run-rules.ts (1)
144-151: Consider updating user connection to be consistent.While updating the entire codebase to use email as the primary identifier, the user connection here still uses id. This is functionally correct but might be worth updating for consistency.
- user: { connect: { id: userId } }, + user: { connect: { email } },This would require updating the function signature and callers to pass email instead of userId.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (98)
apps/web/__tests__/ai-categorize-senders.test.ts(1 hunks)apps/web/__tests__/ai-choose-args.test.ts(2 hunks)apps/web/__tests__/ai-choose-rule.test.ts(1 hunks)apps/web/__tests__/ai-create-group.test.ts(2 hunks)apps/web/__tests__/ai-detect-recurring-pattern.test.ts(1 hunks)apps/web/__tests__/ai-diff-rules.test.ts(3 hunks)apps/web/__tests__/ai-example-matches.test.ts(2 hunks)apps/web/__tests__/ai-extract-from-email-history.test.ts(1 hunks)apps/web/__tests__/ai-extract-knowledge.test.ts(1 hunks)apps/web/__tests__/ai-find-snippets.test.ts(1 hunks)apps/web/__tests__/ai-process-user-request.test.ts(2 hunks)apps/web/__tests__/ai-prompt-to-rules.test.ts(7 hunks)apps/web/__tests__/ai-rule-fix.test.ts(1 hunks)apps/web/__tests__/ai/reply/draft-with-knowledge.test.ts(1 hunks)apps/web/__tests__/helpers.ts(1 hunks)apps/web/__tests__/writing-style.test.ts(1 hunks)apps/web/app/(app)/automation/onboarding/page.tsx(2 hunks)apps/web/app/(app)/license/page.tsx(1 hunks)apps/web/app/(app)/premium/Pricing.tsx(1 hunks)apps/web/app/(app)/settings/MultiAccountSection.tsx(3 hunks)apps/web/app/(app)/settings/page.tsx(1 hunks)apps/web/app/(app)/setup/page.tsx(1 hunks)apps/web/app/(app)/smart-categories/page.tsx(3 hunks)apps/web/app/(app)/usage/usage.tsx(1 hunks)apps/web/app/api/ai/analyze-sender-pattern/call-analyze-pattern-api.ts(1 hunks)apps/web/app/api/ai/analyze-sender-pattern/route.ts(9 hunks)apps/web/app/api/ai/compose-autocomplete/route.ts(2 hunks)apps/web/app/api/ai/models/route.ts(1 hunks)apps/web/app/api/ai/summarise/controller.ts(1 hunks)apps/web/app/api/ai/summarise/route.ts(2 hunks)apps/web/app/api/clean/gmail/route.ts(5 hunks)apps/web/app/api/clean/history/route.ts(2 hunks)apps/web/app/api/clean/route.ts(8 hunks)apps/web/app/api/clean/save-result.ts(0 hunks)apps/web/app/api/google/watch/all/route.ts(2 hunks)apps/web/app/api/google/watch/controller.ts(3 hunks)apps/web/app/api/google/watch/route.ts(1 hunks)apps/web/app/api/google/webhook/process-history-item.ts(6 hunks)apps/web/app/api/google/webhook/process-history.ts(5 hunks)apps/web/app/api/google/webhook/types.ts(2 hunks)apps/web/app/api/reply-tracker/process-previous/route.ts(2 hunks)apps/web/app/api/resend/summary/all/route.ts(2 hunks)apps/web/app/api/resend/summary/route.ts(2 hunks)apps/web/app/api/unsubscribe/route.ts(1 hunks)apps/web/app/api/user/categorize/senders/batch/handle-batch-validation.ts(1 hunks)apps/web/app/api/user/categorize/senders/batch/handle-batch.ts(4 hunks)apps/web/app/api/user/categorize/senders/progress/route.ts(1 hunks)apps/web/app/api/user/me/route.ts(1 hunks)apps/web/app/api/user/rules/prompt/route.ts(1 hunks)apps/web/app/api/user/settings/email-updates/route.ts(1 hunks)apps/web/app/api/user/settings/route.ts(2 hunks)apps/web/components/PremiumAlert.tsx(1 hunks)apps/web/prisma/schema.prisma(4 hunks)apps/web/utils/actions/ai-rule.ts(20 hunks)apps/web/utils/actions/assess.ts(4 hunks)apps/web/utils/actions/categorize.ts(4 hunks)apps/web/utils/actions/clean.ts(10 hunks)apps/web/utils/actions/cold-email.ts(4 hunks)apps/web/utils/actions/generate-reply.ts(3 hunks)apps/web/utils/actions/reply-tracking.ts(2 hunks)apps/web/utils/actions/rule.ts(14 hunks)apps/web/utils/actions/user.ts(2 hunks)apps/web/utils/actions/webhook.ts(1 hunks)apps/web/utils/ai/actions.ts(1 hunks)apps/web/utils/ai/assistant/process-user-request.ts(6 hunks)apps/web/utils/ai/choose-rule/match-rules.test.ts(1 hunks)apps/web/utils/ai/choose-rule/match-rules.ts(3 hunks)apps/web/utils/ai/choose-rule/run-rules.ts(7 hunks)apps/web/utils/ai/example-matches/find-example-matches.ts(2 hunks)apps/web/utils/ai/group/create-group.ts(3 hunks)apps/web/utils/ai/rule/create-rule.ts(2 hunks)apps/web/utils/ai/rule/diff-rules.ts(2 hunks)apps/web/utils/ai/rule/find-existing-rules.ts(2 hunks)apps/web/utils/ai/rule/generate-prompt-on-delete-rule.ts(2 hunks)apps/web/utils/ai/rule/generate-prompt-on-update-rule.ts(2 hunks)apps/web/utils/ai/rule/generate-rules-prompt.ts(2 hunks)apps/web/utils/ai/rule/prompt-to-rules.ts(2 hunks)apps/web/utils/assistant/process-assistant-email.ts(2 hunks)apps/web/utils/categorize/senders/categorize.ts(2 hunks)apps/web/utils/cold-email/is-cold-email.test.ts(2 hunks)apps/web/utils/cold-email/is-cold-email.ts(7 hunks)apps/web/utils/llms/types.ts(1 hunks)apps/web/utils/redis/categorization-progress.ts(2 hunks)apps/web/utils/redis/clean.ts(2 hunks)apps/web/utils/redis/clean.types.ts(1 hunks)apps/web/utils/redis/reply.ts(1 hunks)apps/web/utils/reply-tracker/check-previous-emails.ts(1 hunks)apps/web/utils/reply-tracker/enable.ts(6 hunks)apps/web/utils/reply-tracker/generate-draft.ts(4 hunks)apps/web/utils/reply-tracker/inbound.ts(3 hunks)apps/web/utils/reply-tracker/outbound.ts(4 hunks)apps/web/utils/rule/prompt-file.ts(2 hunks)apps/web/utils/rule/rule.ts(1 hunks)apps/web/utils/upstash/categorize-senders.ts(3 hunks)apps/web/utils/user/delete.ts(1 hunks)apps/web/utils/user/get.ts(3 hunks)apps/web/utils/user/validate.ts(1 hunks)apps/web/utils/webhook.ts(1 hunks)
💤 Files with no reviewable changes (1)
- apps/web/app/api/clean/save-result.ts
🧰 Additional context used
🧬 Code Graph Analysis (50)
apps/web/app/api/ai/analyze-sender-pattern/call-analyze-pattern-api.ts (1)
apps/web/app/api/google/webhook/logger.ts (1)
logger(3-3)
apps/web/utils/ai/rule/diff-rules.ts (1)
apps/web/utils/llms/types.ts (1)
UserEmailWithAI(7-8)
apps/web/__tests__/ai-create-group.test.ts (1)
apps/web/__tests__/helpers.ts (1)
getUser(3-12)
apps/web/__tests__/ai-example-matches.test.ts (1)
apps/web/__tests__/helpers.ts (1)
getUser(3-12)
apps/web/__tests__/ai-diff-rules.test.ts (1)
apps/web/__tests__/helpers.ts (1)
getUser(3-12)
apps/web/utils/ai/example-matches/find-example-matches.ts (1)
apps/web/utils/llms/types.ts (1)
UserEmailWithAI(7-8)
apps/web/app/api/user/rules/prompt/route.ts (2)
apps/web/app/api/user/me/route.ts (1)
GET(48-56)apps/web/utils/middleware.ts (1)
withError(16-70)
apps/web/app/api/ai/summarise/route.ts (2)
apps/web/utils/user/get.ts (1)
getAiUser(4-18)apps/web/app/api/ai/summarise/controller.ts (1)
summarise(6-30)
apps/web/utils/ai/rule/generate-prompt-on-update-rule.ts (1)
apps/web/utils/llms/types.ts (1)
UserEmailWithAI(7-8)
apps/web/utils/actions/reply-tracking.ts (2)
apps/web/utils/reply-tracker/enable.ts (1)
enableReplyTracker(11-147)apps/web/utils/user/get.ts (1)
getAiUser(4-18)
apps/web/app/(app)/usage/usage.tsx (1)
apps/web/utils/premium/index.ts (1)
isPremium(3-5)
apps/web/app/(app)/premium/Pricing.tsx (1)
apps/web/utils/premium/index.ts (1)
getUserTier(28-33)
apps/web/app/api/user/settings/route.ts (1)
apps/web/utils/error.ts (1)
SafeError(87-95)
apps/web/app/(app)/smart-categories/page.tsx (1)
apps/web/utils/redis/categorization-progress.ts (1)
getCategorizationProgress(14-23)
apps/web/app/api/ai/summarise/controller.ts (1)
apps/web/utils/llms/types.ts (1)
UserEmailWithAI(7-8)
apps/web/utils/ai/actions.ts (2)
apps/web/utils/reply-tracker/inbound.ts (1)
coordinateReplyProcess(19-67)apps/web/utils/date.ts (1)
internalDateToDate(54-61)
apps/web/utils/ai/rule/create-rule.ts (1)
apps/web/utils/llms/types.ts (1)
UserEmailWithAI(7-8)
apps/web/utils/ai/rule/generate-prompt-on-delete-rule.ts (1)
apps/web/utils/llms/types.ts (1)
UserEmailWithAI(7-8)
apps/web/utils/ai/rule/generate-rules-prompt.ts (1)
apps/web/utils/llms/types.ts (1)
UserEmailWithAI(7-8)
apps/web/app/api/ai/compose-autocomplete/route.ts (2)
apps/web/utils/middleware.ts (1)
withError(16-70)apps/web/utils/user/get.ts (1)
getAiUser(4-18)
apps/web/utils/ai/rule/find-existing-rules.ts (1)
apps/web/utils/llms/types.ts (1)
UserEmailWithAI(7-8)
apps/web/app/(app)/settings/MultiAccountSection.tsx (1)
apps/web/utils/premium/index.ts (1)
getUserTier(28-33)
apps/web/utils/actions/generate-reply.ts (1)
apps/web/utils/user/get.ts (1)
getAiUser(4-18)
apps/web/__tests__/helpers.ts (1)
apps/web/utils/types.ts (1)
EmailForLLM(104-113)
apps/web/utils/rule/rule.ts (1)
apps/web/utils/ai/rule/create-rule-schema.ts (1)
CreateOrUpdateRuleSchemaWithCategories(128-131)
apps/web/utils/user/validate.ts (1)
apps/web/utils/premium/index.ts (1)
hasAiAccess(67-77)
apps/web/app/api/user/categorize/senders/progress/route.ts (3)
apps/web/utils/redis/categorization-progress.ts (1)
getCategorizationProgress(14-23)apps/web/app/api/user/me/route.ts (1)
GET(48-56)apps/web/utils/middleware.ts (1)
withError(16-70)
apps/web/app/api/reply-tracker/process-previous/route.ts (3)
apps/web/utils/logger.ts (1)
createScopedLogger(17-65)apps/web/utils/gmail/client.ts (1)
getGmailClient(28-33)apps/web/utils/reply-tracker/check-previous-emails.ts (1)
processPreviousSentEmails(14-90)
apps/web/utils/user/get.ts (1)
apps/web/utils/llms/types.ts (1)
UserEmailWithAI(7-8)
apps/web/utils/actions/cold-email.ts (1)
apps/web/utils/actions/cold-email.validation.ts (2)
updateColdEmailSettingsBody(17-27)updateColdEmailPromptBody(32-34)
apps/web/utils/ai/rule/prompt-to-rules.ts (1)
apps/web/utils/llms/types.ts (1)
UserEmailWithAI(7-8)
apps/web/app/api/user/categorize/senders/batch/handle-batch.ts (4)
apps/web/app/api/google/webhook/logger.ts (1)
logger(3-3)apps/web/utils/user/validate.ts (1)
validateUserAndAiAccess(4-30)apps/web/utils/error.ts (1)
isActionError(82-84)apps/web/utils/categorize/senders/categorize.ts (1)
getCategories(140-144)
apps/web/utils/actions/categorize.ts (4)
apps/web/utils/user/validate.ts (1)
validateUserAndAiAccess(4-30)apps/web/utils/error.ts (1)
isActionError(82-84)apps/web/utils/upstash/categorize-senders.ts (2)
deleteEmptyCategorizeSendersQueues(53-62)publishToAiCategorizeSendersQueue(18-51)apps/web/utils/categorize/senders/categorize.ts (1)
categorizeSender(17-58)
apps/web/app/api/user/me/route.ts (7)
apps/web/utils/error.ts (1)
SafeError(87-95)apps/web/app/api/ai/models/route.ts (1)
GET(21-45)apps/web/app/api/clean/history/route.ts (1)
GET(17-25)apps/web/app/api/google/watch/route.ts (1)
GET(12-26)apps/web/app/api/user/categorize/senders/progress/route.ts (1)
GET(15-22)apps/web/app/api/user/rules/prompt/route.ts (1)
GET(15-23)apps/web/utils/middleware.ts (1)
withError(16-70)
apps/web/utils/actions/assess.ts (3)
apps/web/utils/gmail/client.ts (1)
getGmailClient(28-33)apps/web/utils/assess.ts (1)
assessUser(13-53)apps/web/utils/user/get.ts (1)
getAiUser(4-18)
apps/web/utils/reply-tracker/inbound.ts (1)
apps/web/utils/date.ts (1)
internalDateToDate(54-61)
apps/web/app/api/resend/summary/all/route.ts (3)
apps/web/app/api/google/webhook/logger.ts (1)
logger(3-3)apps/web/utils/upstash/index.ts (1)
publishToQstashQueue(57-79)apps/web/utils/cron.ts (1)
getCronSecretHeader(26-28)
apps/web/app/api/google/watch/controller.ts (2)
apps/web/utils/gmail/watch.ts (1)
watchGmail(5-16)apps/web/utils/error.ts (1)
captureException(60-72)
apps/web/utils/reply-tracker/generate-draft.ts (2)
apps/web/utils/user/get.ts (1)
getAiUser(4-18)apps/web/utils/redis/reply.ts (1)
getReply(13-21)
apps/web/utils/redis/clean.ts (3)
apps/web/utils/redis/clean.types.ts (1)
CleanThread(1-15)apps/web/utils/gmail/thread.ts (1)
getThread(11-17)apps/web/utils/prisma-extensions.ts (1)
update(33-53)
apps/web/app/api/ai/models/route.ts (1)
apps/web/utils/llms/config.ts (1)
Provider(5-12)
apps/web/utils/ai/group/create-group.ts (1)
apps/web/utils/llms/types.ts (1)
UserEmailWithAI(7-8)
apps/web/utils/ai/choose-rule/match-rules.ts (1)
apps/web/utils/llms/types.ts (1)
UserEmailWithAI(7-8)
apps/web/app/api/google/watch/all/route.ts (4)
apps/web/app/api/google/webhook/logger.ts (1)
logger(3-3)apps/web/utils/premium/index.ts (2)
hasAiAccess(67-77)hasColdEmailAccess(79-89)apps/web/utils/gmail/client.ts (1)
getGmailClientWithRefresh(54-92)apps/web/app/api/google/watch/controller.ts (1)
watchEmails(10-28)
apps/web/utils/actions/rule.ts (7)
apps/web/utils/rule/prompt-file.ts (2)
updatePromptFileOnRuleCreated(8-17)updatePromptFileOnRuleUpdated(19-53)apps/web/utils/ai/rule/generate-prompt-on-delete-rule.ts (1)
generatePromptOnDeleteRule(16-69)apps/web/utils/actions/rule.validation.ts (2)
rulesExamplesBody(151-151)createRulesOnboardingBody(165-173)apps/web/utils/gmail/client.ts (1)
getGmailClient(28-33)apps/web/utils/user/get.ts (1)
getAiUser(4-18)apps/web/utils/reply-tracker/enable.ts (1)
enableReplyTracker(11-147)apps/web/app/api/reply-tracker/process-previous/route.ts (1)
ProcessPreviousBody(16-16)
apps/web/app/api/google/webhook/process-history.ts (4)
apps/web/app/api/google/webhook/logger.ts (1)
logger(3-3)apps/web/utils/premium/index.ts (3)
isPremium(3-5)hasAiAccess(67-77)hasColdEmailAccess(79-89)apps/web/app/api/google/watch/controller.ts (1)
unwatchEmails(35-64)apps/web/utils/gmail/client.ts (1)
getGmailClientWithRefresh(54-92)
apps/web/utils/ai/assistant/process-user-request.ts (3)
apps/web/utils/llms/types.ts (1)
UserEmailWithAI(7-8)apps/web/utils/rule/rule.ts (1)
createRule(109-156)apps/web/utils/rule/prompt-file.ts (2)
updatePromptFileOnRuleCreated(8-17)updatePromptFileOnRuleUpdated(19-53)
apps/web/utils/cold-email/is-cold-email.ts (2)
apps/web/utils/llms/types.ts (1)
UserEmailWithAI(7-8)apps/web/app/api/google/webhook/logger.ts (1)
logger(3-3)
apps/web/utils/actions/clean.ts (1)
apps/web/utils/redis/clean.ts (1)
updateThread(34-53)
apps/web/utils/ai/choose-rule/run-rules.ts (3)
apps/web/utils/llms/types.ts (1)
UserEmailWithAI(7-8)apps/web/utils/ai/choose-rule/match-rules.ts (1)
findMatchingRule(206-217)apps/web/utils/types.ts (1)
ParsedMessage(47-58)
🔇 Additional comments (254)
apps/web/__tests__/ai-categorize-senders.test.ts (1)
16-17: Updated user schema for consistency with centralized fixturesThe addition of
userIdandaboutproperties to thetestUserobject aligns with the standardized user data structure being adopted across test files. This change prepares the test fixture for future transition to the centralizedgetUserhelper used in other tests.Also applies to: 21-22
apps/web/app/api/ai/analyze-sender-pattern/call-analyze-pattern-api.ts (2)
24-25: Updated user identifier from userId to emailThis change is part of the broader refactoring effort to use email as the primary user identifier throughout the application. The error logging now correctly references
userId.
32-33: Updated user identifier from userId to emailConsistent with the change above, this error logging now references
userId.apps/web/utils/redis/clean.types.ts (1)
3-3:Details
✅ Verification successful
Changed primary identifier from userId to email
Updated the
CleanThreadtype to useuserIdas the user identifier, which aligns with the broader refactoring to make email the primary key for user-related operations in Redis and throughout the application.
🏁 Script executed:
#!/bin/bash # Verify that all usages of CleanThread type now provide the email property instead of userId # Look for any instances where CleanThread is created but still using userId property rg -t typescript "CleanThread.*userId\s*:" --no-ignore-vcs # Look for clean thread related functions to ensure they're using email parameter rg -t typescript "function.*[Cc]lean[Tt]hread.*\(" -A 2 --no-ignore-vcsLength of output: 204
I’ll retry the grep using the correct TypeScript type filter (
ts) instead oftypescript.
🏁 Script executed:
#!/bin/bash # Verify that all CleanThread usages now use 'email' instead of 'userId' # 1) Look for any leftover ‘userId’ in CleanThread type definitions or usages rg -t ts "CleanThread.*userId\s*:" --no-ignore-vcs # 2) Look for any cleanThread-related functions still declaring or destructuring 'userId' rg -t ts "function.*[Cc]lean[Tt]hread.*\(" -A 2 --no-ignore-vcsLength of output: 264
Verified CleanThread usages now use email
All instances of theCleanThreadtype and related functions have been updated to useuserId:
apps/web/utils/redis/clean.types.tsdefinesCleanThreadwith anemail: stringproperty.apps/web/app/api/clean/route.ts’scleanThreadfunction destructures{ email, threadId }—nouserIdremains.All checks pass; no further changes needed.
apps/web/__tests__/ai-find-snippets.test.ts (1)
4-4: Refactored to use centralized test helpersUpdated the import to use shared test helpers instead of local implementations, promoting code reuse and consistency across test files. This change aligns with the broader standardization of test fixtures.
apps/web/__tests__/writing-style.test.ts (1)
3-3: Centralized test fixture is a good practice.The change to import
getUserfrom the shared helpers module instead of defining it locally improves test consistency across the codebase and aligns with the effort to support multiple email accounts.apps/web/app/(app)/license/page.tsx (1)
28-28: Consistent data structure update.The change to access premium data via
data?.user.premiumis aligned with the broader refactoring to make user-related data email-based rather than user ID-based, which is necessary for supporting multiple email accounts per user.apps/web/__tests__/ai-create-group.test.ts (1)
6-6: Uniform test fixtures help ensure consistency.Replacing the local user definition with the shared
getUser()helper function maintains consistency across tests and supports the refactoring towards email-based user identification.Also applies to: 19-19
apps/web/__tests__/ai-extract-knowledge.test.ts (1)
5-5: Standardized test fixtures improve maintainability.Using the centralized
getUser()helper ensures consistent user mock data across all AI-related tests and supports the migration to email-based user identification.apps/web/__tests__/ai-detect-recurring-pattern.test.ts (1)
5-5: Good refactoring to use centralized test helpers.Using the shared
getUserhelper from the test helpers module reduces duplication and promotes consistency across test files. This is a positive improvement that aligns with the PR objective of refactoring for multiple email accounts per user.apps/web/utils/ai/choose-rule/match-rules.test.ts (1)
20-20: Great use of shared test helper.Importing
getUserfrom a centralized helper module instead of defining it locally improves code maintainability and ensures consistent test fixtures across the test suite, which is particularly important when refactoring user identification patterns.apps/web/utils/user/delete.ts (1)
41-41: Proper refactoring to use email as identifier.This change correctly updates the
unwatchEmailscall to useuserIdas the primary identifier, which aligns with the broader refactoring to support multiple email accounts per user. The function now consistently uses email-based identification for external service operations.apps/web/utils/actions/webhook.ts (2)
12-13: Correct authentication check update.The authentication validation has been properly updated to check for the presence of an email rather than a user ID, which aligns with the email-centric approach needed for supporting multiple email accounts per user.
17-19: Appropriate database model update.The update operation now correctly targets the
emailAccounttable using the email as the primary key instead of theusertable with userId. This change is essential for the multi-email account architecture being implemented.apps/web/__tests__/ai-example-matches.test.ts (2)
7-7: Good refactoring to use centralized user helperUsing the centralized
getUser()helper function improves consistency across tests and makes future maintenance easier.
20-20: Clean implementation of shared test helperReplacing the inline user object with the shared helper function follows good testing practices by standardizing test data across the codebase.
apps/web/app/(app)/settings/page.tsx (1)
20-28:Details
✅ Verification successful
Data source changed from user to emailAccount for settings
This change shifts the data source from the
usertable to theemailAccounttable while maintaining the same field selections. This change aligns with the PR objective of supporting multiple email accounts per user.Verify that all these fields have been properly migrated to the
emailAccounttable in the schema:
🏁 Script executed:
#!/bin/bash # Check if the emailAccount table has the fields used in this query grep -A 50 "model EmailAccount" $(fd "schema.prisma" --type f)Length of output: 1495
All required fields exist on
EmailAccountmodelI’ve confirmed that
about,signature,statsEmailFrequency, andwebhookSecretare defined on theEmailAccountschema:
- about String?
- signature String?
- statsEmailFrequency Frequency @default(WEEKLY)
- webhookSecret String?
No further changes are needed here—this query correctly targets the new data source.
apps/web/utils/reply-tracker/check-previous-emails.ts (1)
50-50:Details
✅ Verification successful
Updated user identification from id to userId
Changed from using
user.idtouser.userIdin the Prisma unique constraint, aligning with the updated schema for user identification.Verify that the
UserEmailWithAItype has been updated to useuserIdinstead ofid:
🏁 Script executed:
#!/bin/bash # Check the definition of UserEmailWithAI type cat $(fd "types.ts" --type f | grep -l "UserEmailWithAI")Length of output: 56
🏁 Script executed:
#!/bin/bash # Search for all occurrences of UserEmailWithAI in the codebase rg "UserEmailWithAI" -n .Length of output: 8847
UserEmailWithAI type correctly uses userId
The
UserEmailWithAItype inapps/web/utils/llms/types.tsnow picks"userId"(not"id"), matching the updated Prisma schema. All imports and usages across the codebase reference theuserIdfield as intended.• Definition confirmed at
apps/web/utils/llms/types.ts:7export type UserEmailWithAI = Pick<EmailAccount, "userId" | "email" | "about"> & …No further changes required.
apps/web/__tests__/ai/reply/draft-with-knowledge.test.ts (1)
67-67:Details
✅ Verification successful
Updated user mock to use userId instead of id
Changed the property key from
idtouserIdin the test user fixture, maintaining consistency with the broader refactoring of user identification.Verify that the
aiDraftWithKnowledgefunction now expectsuserIdinstead ofid:
🏁 Script executed:
#!/bin/bash # Check the definition of aiDraftWithKnowledge function cat $(fd "draft-with-knowledge.ts" --type f) | grep -A 10 "aiDraftWithKnowledge"Length of output: 407
🏁 Script executed:
#!/bin/bash # Locate the definition of UserEmailWithAI to verify expected property keys rg "UserEmailWithAI" -nLength of output: 8659
Verified: aiDraftWithKnowledge uses
userId
TheUserEmailWithAItype (defined inapps/web/utils/llms/types.ts) picks theuserIdproperty, and theaiDraftWithKnowledgesignature inapps/web/utils/ai/reply/draft-with-knowledge.tsacceptsuser: UserEmailWithAI. The test fixture inapps/web/__tests__/ai/reply/draft-with-knowledge.test.tscorrectly usesuserId: "user-123". No further changes needed.apps/web/utils/ai/rule/diff-rules.ts (1)
4-4: Type refactoring aligns with multi-email account support.The change from
UserAIFields & { email: string }to the consolidatedUserEmailWithAItype provides better type safety and clarity. This supports the PR's goal of handling multiple email accounts per user by explicitly representing the relationship between email accounts and user data.Also applies to: 24-24
apps/web/app/api/user/settings/email-updates/route.ts (1)
19-19: Database target change supports multi-email architecture.Correctly migrates user settings from the
usertable to theemailAccounttable, which is a key change for supporting multiple email accounts per user. The email-based identification is properly maintained in the query condition.apps/web/app/(app)/premium/Pricing.tsx (1)
48-48:Details
✅ Verification successful
Updated premium data access path.
The premium data access path has been correctly updated to reflect the new data structure. This change aligns with the codebase's shift to email-based user identification.
🏁 Script executed:
#!/bin/bash # Description: Verify all accesses to premium data have been updated consistently # Look for any remaining direct accesses to data.premium that might have been missed rg -t ts -t tsx "data\?.premium" --glob '!apps/web/app/(app)/premium/Pricing.tsx' # Check that all getUserTier calls use the new structure rg -t ts -t tsx "getUserTier\(" -A 1 -B 1Length of output: 183
🏁 Script executed:
#!/bin/bash # Description: Verify consistent usage of premium data access across TS/TSX files # Find legacy direct accesses to premium data outside Pricing.tsx rg 'data\?\.premium' -g '*.ts' -g '*.tsx' --glob '!apps/web/app/(app)/premium/Pricing.tsx' rg 'data\.premium' -g '*.ts' -g '*.tsx' --glob '!apps/web/app/(app)/premium/Pricing.tsx' # Verify all getUserTier calls and their context rg 'getUserTier' -g '*.ts' -g '*.tsx' -C1Length of output: 1408
Premium data access path updated consistently
All instances of
getUserTiernow reference the newuser.premiumfield rather than the legacy top‐levelpremiumobject.• apps/web/app/(app)/premium/Pricing.tsx
const premiumTier = getUserTier(data?.user.premium);
• apps/web/app/(app)/settings/MultiAccountSection.tsxconst premiumTier = getUserTier(dataPremium?.user.premium);No remaining direct
data.premiumordata?.premiumreferences were found.apps/web/__tests__/ai-extract-from-email-history.test.ts (1)
4-4: Centralized test user fixture improves maintainability.Using an imported
getUserhelper instead of a local definition standardizes test data setup across the test suite. This makes tests more consistent and easier to maintain when user data structures change.apps/web/app/api/ai/summarise/controller.ts (1)
2-2: Type change is well-aligned with email-centric architectureThe change from
UserAIFieldstoUserEmailWithAIproperly aligns with the PR's objective of supporting multiple email accounts per user, making AI capabilities email-specific rather than user-specific.Also applies to: 9-9
apps/web/__tests__/ai-choose-args.test.ts (2)
5-5: Good refactoring to centralize test helpersUsing the imported
getUserhelper from the shared test module rather than a local implementation ensures consistency across tests and reduces code duplication.
201-201: Property rename for clarityThe change from
typetosystemTypemaintains consistency with other test files and likely better reflects the purpose of this field in the application.apps/web/utils/webhook.ts (2)
31-36: Database query shifted to email-centric modelThe query change from user to emailAccount properly implements the email-centric architecture required for supporting multiple email accounts per user.
43-43:Details
✅ Verification successful
Webhook secret now from email account
Using the email account's webhook secret is consistent with the email-centric architecture being implemented.
🏁 Script executed:
#!/bin/bash # Check if all callWebhook usages have been updated to pass email instead of user ID rg -A 2 "callWebhook\(" --type tsLength of output: 210
🏁 Script executed:
#!/bin/bash # Check all callWebhook usages across all file types (including .ts, .tsx, .js, .jsx) rg -A 2 "callWebhook\("Length of output: 200
Webhook secret now sourced from email account
Using the email account’s webhook secret aligns with our email‑centric architecture. All
callWebhookinvocations have been updated to passuserEmailinstead of a user ID (e.g. in apps/web/utils/ai/actions.ts).apps/web/__tests__/ai-choose-rule.test.ts (1)
5-5: Well-structured test refactoringMoving from locally defined helpers to centralized imports improves maintainability and ensures consistency across tests. This change supports the email-centric architecture being implemented throughout the application.
apps/web/utils/ai/rule/find-existing-rules.ts (2)
3-3: Type import update aligns with email-based user identificationThis update changes the import from a composite type (
UserAIFields & { email: string }) to the consolidatedUserEmailWithAItype, which is part of the broader refactoring to support multiple email accounts per user.
25-25: Type annotation change supports the multi-email accounts modelThe parameter type has been updated from the previous composite type to
UserEmailWithAI, maintaining all required fields while aligning with the new data structure that focuses on email-based identification rather than user ID.apps/web/app/api/unsubscribe/route.ts (1)
53-55: Database operation shifted from user to emailAccount tableThis change correctly updates the database operation to target the
emailAccounttable instead of theusertable, using the email address as the primary identifier. This supports the foundation for multiple email accounts per user.Since the unsubscribe token is still associated with a user who may have multiple email accounts, consider verifying this approach handles the multi-account scenario correctly:
#!/bin/bash # Check if the emailToken schema still references the user table rg -A 5 "model EmailToken" --glob "*.prisma"apps/web/utils/actions/user.ts (5)
22-23: Improved error handling for email validationGood improvement to extract the email into a variable and validate it early, providing a clear error message if not present.
28-30: Database operation refactored to use email as primary keyCorrectly updated to target the
emailAccounttable using email as the identifier instead of theusertable by user ID. This change aligns with the multi-email account architecture.
44-45: Consistent email validation applied to saveSignatureActionSimilar to the change in saveAboutAction, this improves error handling by validating the email early in the function.
50-52: Consistent database operation refactoringCorrectly updated to target the
emailAccounttable using email as the primary key, matching the pattern applied in saveAboutAction.
89-138:Details
❌ Incorrect review comment
Remaining functions still use userId for operations
Some server actions in this file (
resetAnalyticsAction,completedOnboardingAction,completedAppOnboardingAction) still use the user ID for database operations.Consider whether these should also be migrated to use email-based operations for consistency, or document why these specific operations still require user ID.
🏁 Script executed:
#!/bin/bash # Check whether these fields have been migrated to the EmailAccount model rg -A 10 "model User" --glob "*.prisma" rg -A 10 "model EmailAccount" --glob "*.prisma"Length of output: 1397
Use of userId is correct for User‐model operations
These server actions are operating on the User model (and its related tables via the userId foreign key) rather than the EmailAccount model. Since User.id is the primary key and relations like emailMessage.userId depend on it, continuing to use userId here is intentional and appropriate. The EmailAccount model is separate—migrating these calls to “email‑based” logic isn’t applicable.
Likely an incorrect or invalid review comment.
apps/web/utils/ai/rule/generate-prompt-on-delete-rule.ts (2)
3-3: Type import consolidation for email-based user identificationUpdated import to use the consolidated
UserEmailWithAItype, which simplifies the type system and aligns with the email-based user identification approach.
21-21: Type annotation updated for email-based data modelThe parameter type is correctly updated from
UserAIFields & Pick<User, "email">toUserEmailWithAI, ensuring consistency with the new data model that uses email accounts as the primary entity.apps/web/__tests__/ai-prompt-to-rules.test.ts (2)
19-20: LGTM: Consistent user object updates across test casesThe additions of
userIdandaboutproperties to all user test objects are consistent with the migration to support multiple email accounts per user. This change aligns with the broader refactoring of user identification across the codebase.Also applies to: 119-120, 139-140, 216-217, 260-261, 304-305
344-344: Update to reflect new reply content structureThe assertion has been correctly updated to check
replyAction?.fields?.contentinstead ofreplyAction?.content, reflecting the structural change in how reply content is now stored within a nestedfieldsobject.apps/web/utils/ai/example-matches/find-example-matches.ts (1)
5-5: LGTM: Type update aligns with email-based architectureThe change from the previous user type to
UserEmailWithAIreflects the architectural shift from user-based to email-based identification. The new type correctly combines essential email account fields (userId,about) with AI-related user fields, maintaining compatibility with the existing function implementation.Also applies to: 30-30
apps/web/utils/categorize/senders/categorize.ts (1)
24-24: LGTM: Consistent update of user identifierThe changes from
user.idtouser.userIdare consistent with the architectural shift to support multiple email accounts per user. This ensures proper user identification when interacting with categories and sender information.Also applies to: 46-46
apps/web/app/api/ai/models/route.ts (4)
23-24: LGTM: Improved authentication check with explicit email extractionThe code now explicitly extracts the email from the session and checks its presence before proceeding, improving the authentication flow clarity.
26-29: LGTM: Updated database query to use email accountThe database query has been correctly updated to fetch from the
emailAccounttable by email instead of theusertable, aligning with the new data model where AI configuration is stored with the email account.
31-35: LGTM: Consistent validation logic for email accountThe conditional check has been properly updated to verify the existence and properties of the
emailAccountrecord, maintaining the same validation logic while adapting to the new data structure.
39-39: LGTM: Updated API key sourceThe call to
getOpenAiModelsnow correctly uses the API key from the email account instead of the user record.apps/web/__tests__/ai-diff-rules.test.ts (1)
3-3: Great refactoring to centralize test data!Using the
getUser()helper function instead of inline user object literals improves maintainability and consistency across tests. This approach makes it easier to adapt tests when the user model changes, which aligns well with the PR's goal of supporting multiple email accounts per user.Also applies to: 13-13, 44-44
apps/web/components/PremiumAlert.tsx (1)
23-23: Property access path updated correctly for new data structureThis change correctly updates the premium access path from
data?.premiumtodata?.user.premium, aligning with the refactoring to support multiple email accounts per user.Please verify that
data.useris guaranteed to be defined whenever this component is rendered. If there are edge cases wheredataexists butdata.usermight be undefined, additional null checking may be needed.apps/web/app/api/user/categorize/senders/batch/handle-batch-validation.ts (1)
4-4: Key identifier changed from userId to emailThis change is fundamental to supporting multiple email accounts per user, as it shifts identification from
userIdtoSince this changes the expected payload structure for API consumers, verify that all client code has been updated to send
userId. Consider documenting this API change if it affects external consumers.apps/web/utils/ai/rule/generate-rules-prompt.ts (1)
3-3: Clean type refactoring that maintains existing functionalityReplacing the composite type with the unified
UserEmailWithAItype simplifies the code and aligns with the broader refactoring to make email accounts the central entity. This change maintains the same functionality while making the type definitions more consistent across the codebase.Also applies to: 36-36
apps/web/utils/ai/actions.ts (1)
220-227: LGTM: Function signature update improves type safetyThe update to use an object parameter with named properties for the
coordinateReplyProcessfunction call is a good improvement. This approach is more maintainable and provides better type safety compared to positional arguments, especially when dealing with multiple parameters.apps/web/app/(app)/usage/usage.tsx (1)
25-28: Data structure path updated correctlyThe change to access premium data through
data?.user.premiuminstead of directly fromdata?.premiumaligns with the broader refactoring to support multiple email accounts. Premium data is now nested under the user object, providing a consistent data access pattern.apps/web/app/api/google/watch/route.ts (2)
14-15: Authentication check correctly updated to use emailThe authentication check has been properly updated to verify the user's email presence instead of the user ID, which aligns with the shift to email-based identification.
19-23: Watch endpoint correctly migrated to email-based identificationThe function call to
watchEmailsand error logging have been appropriately updated to use email as the primary identifier, consistent with the codebase refactoring from user ID to email-based operations.apps/web/app/api/clean/history/route.ts (2)
8-11: Database query correctly migrated from userId to emailThe
getCleanHistoryfunction signature and Prisma query have been properly updated to filter by email instead of userId, which is consistent with the broader refactoring approach.
19-22: Authentication and function call updated consistentlyThe authentication check and the call to
getCleanHistorynow use email as the identifier, maintaining consistency with the overall refactoring approach.apps/web/utils/ai/rule/prompt-to-rules.ts (1)
4-4: Type updated to align with new email-based user modelThe changes update the user parameter type from an intersection type to the new consolidated
UserEmailWithAItype. This is consistent with the broader refactoring to support multiple email accounts per user, where user identification and AI-related data are now accessed through theEmailAccountentity rather than theUserentity.Also applies to: 25-25
apps/web/app/api/ai/summarise/route.ts (3)
15-16: Improved variable extraction for session emailExtracting the email into a dedicated variable improves code readability and reduces repetition when accessing session data.
33-33: Updated to use email-based user lookupThe API now uses the
getAiUserhelper to fetch user AI configuration by email instead of directly querying by user ID, aligning with the new data access pattern.
38-38: Updated function call to use extracted email variableThe
summarisefunction now receives the extracted email variable as a parameter, maintaining consistency with the new email-based identification approach.apps/web/__tests__/ai-rule-fix.test.ts (1)
5-5: Centralized test helpers for consistencyThe test now imports
getEmailandgetUserfrom a shared helpers module instead of defining them locally, improving consistency across test files and supporting the email account refactoring.apps/web/app/api/ai/compose-autocomplete/route.ts (4)
6-6: Updated imports to use the new email-based user helperReplaced the direct Prisma import with the new
getAiUserhelper function that retrieves user AI configuration by email address.
10-11: Improved variable naming and authentication checkRenamed
userEmailto
13-14: Replaced direct Prisma query with specialized helper functionReplaced a direct database query with the new
getAiUserhelper that fetches user AI configuration by email. This centralizes the data access logic and supports the migration to email-based user identification.
28-28: Updated parameter name to match variable renamingUpdated the parameter passed to
chatCompletionStreamto use the renamedapps/web/utils/ai/rule/generate-prompt-on-update-rule.ts (2)
3-3: Type import aligned with email-based user representationThe import was updated to use the consolidated
UserEmailWithAItype, which is part of the broader refactoring to use email accounts as the primary user identifier.
22-22: Parameter type updated to useUserEmailWithAIThe function parameter type has been updated from a composite type to the new
UserEmailWithAItype, improving type consistency across the codebase while maintaining the same functionality.apps/web/app/api/user/rules/prompt/route.ts (3)
8-11: Function refactored to use email-based identificationThe
getRulesPromptfunction has been appropriately updated to:
- Accept
userIdas the parameter- Query the
emailAccounttable instead of theusertable- Use
idThis aligns with the PR objective to support multiple email accounts per user.
17-18: Authentication check updated to use emailAuthentication verification now correctly uses the user's email from the session instead of userId, consistent with the new email-centric approach.
20-20: Function call updated to use email parameterThe call to
getRulesPromptnow correctly passes the email parameter, maintaining consistency with the function's updated signature.apps/web/utils/cold-email/is-cold-email.test.ts (3)
37-44: Mock user object updated to match EmailAccount structureThe mock user object has been properly updated to:
- Use
userIdinstead ofidas the identifier- Include new required fields:
about,aiProvider,aiModel, andaiApiKeyThis ensures the test remains compatible with the refactored database structure.
59-65: Database query updated for new composite key structureThe query's
whereclause has been correctly updated to use the composite keyuserId_fromEmail, which reflects the updated database schema and properly identifies records using the user's ID and sender's email address.
70-70: Create object updated to use userId fieldThe create object now correctly uses
mockUser.userIdinstead ofmockUser.id, maintaining consistency with the updated database schema.apps/web/utils/ai/rule/create-rule.ts (3)
2-2: Import updated to use consolidated user typeThe import has been changed to use
UserEmailWithAIinstead of the previous type, aligning with the codebase-wide standardization on email-based user representation.
8-8: Function parameter consolidated to use UserEmailWithAIThe function signature has been improved to use a single
userparameter of typeUserEmailWithAIinstead of separate parameters, which simplifies the API and improves type consistency.
24-24: Updated access to email propertyThe code now correctly accesses the email through
user.emailproperty instead of a separate parameter, maintaining functionality while improving code organization.apps/web/app/api/google/webhook/process-history-item.ts (6)
68-72: Property access updated for multi-account supportThe change from
user.idtouser.userIdis part of the email-account based refactoring to support multiple accounts per user. This correctly uses the EmailAccount model's userId property instead of directly accessing the User model's id.
100-102: User identifier updated to use EmailAccount modelConsistent with the multi-account refactoring, the function now correctly passes
user.userIdrather thanuser.idto the assistant email processor.
125-127: User identifier updated for blockUnsubscribedEmails functionThe function parameter is correctly updated to use
user.userIdrather thanuser.id, ensuring consistency with the email-based user identification approach.
169-171: Database query structure updated for multiple account supportThe query now correctly uses the compound key
email_userIdfor finding a unique newsletter entry. This change supports the schema refactoring for multiple email accounts by using the correct database access pattern.
219-221: User object structure updated for draft trackingThe
trackSentDraftStatusfunction now receives the correctly structured user object withuserIdinstead ofid. This maintains consistency with the email-account based approach.
244-247: User identifier updated for thread draft cleanupThe
cleanupThreadAIDraftsfunction now receivesuser.userIdinstead ofuser.id, completing the refactoring of this module to use EmailAccount-based identifiers.apps/web/__tests__/ai-process-user-request.test.ts (2)
8-8: Centralized test helper imported for consistent user dataGood refactoring to import
getUserfrom a shared test helper instead of defining it locally. This ensures consistency in test data across test files and aligns with the multi-account refactoring.
449-449: Added systemType field to align with schema updatesThe addition of
systemType: nullto the rule object ensures that test fixtures match the updated schema, which now distinguishes between user-created rules and system-generated rules.apps/web/utils/actions/reply-tracking.ts (2)
24-27: Authentication logic refactored to use email for identificationThe authentication logic now correctly uses the user's email rather than user ID, aligning with the email-account centric approach. This enables proper support for multiple email accounts per user.
39-43: User identification updated to use emailThe action now uses email to identify the user and fetch their details, which is consistent with the email-account based approach.
apps/web/app/(app)/settings/MultiAccountSection.tsx (4)
46-46: Premium data access path updatedThe component now correctly accesses premium data through
dataPremium?.user.premiumrather than directly fromdataPremium?.premium, which aligns with the restructured premium data model.
85-87: emailAccountsAccess property path updatedThe component now correctly accesses
emailAccountsAccessthrough the restructured pathdataPremium?.user.premium?.emailAccountsAccess.
96-97: Premium tier check path updatedThe component now correctly checks the premium tier through the restructured path
dataPremium?.user.premium?.tier.
98-103: Multiple premium properties paths updatedThe component now correctly accesses multiple premium-related properties (
emailAccountsAccessandpendingInvites) through the restructured paths underdataPremium?.user.premium.apps/web/__tests__/helpers.ts (2)
3-12: Well-structured user test fixture.This implementation provides a consistent user mock for tests with all the necessary fields, including both user ID and email, which aligns well with the multiple email accounts architecture being implemented.
14-29: Clean implementation of email test fixture with good defaults.The
getEmailfunction follows best practices by:
- Using TypeScript's Partial type for optional parameters
- Providing sensible defaults for required fields
- Using spread operator for conditional property inclusion
This will help maintain consistency across tests when working with email objects.
apps/web/app/api/user/categorize/senders/progress/route.ts (3)
10-12: LGTM: Consistent refactoring to email-based identification.The function now correctly accepts an email parameter instead of userId, maintaining the same functionality while aligning with the new architecture for multiple email accounts per user.
17-18: Authentication check properly updated to verify email presence.Authentication verification now correctly checks for the email from the session, which is the new primary identifier for user-related operations.
20-20: Function call consistently updated to use email parameter.The function call to
getCategorizeProgresshas been properly updated to use the email parameter, maintaining consistency with the new parameter structure.apps/web/utils/user/validate.ts (4)
4-8: LGTM: Function signature updated to use email-based identification.The function now correctly accepts an email parameter instead of userId, aligning with the new architecture for multiple email accounts per user.
9-20: Well-structured database query with proper nested relations.The query has been properly refactored to fetch an emailAccount by email, while maintaining access to user premium data through the nested relation. This structure correctly reflects the hierarchical relationship where a user can have multiple email accounts.
23-26: AI access validation properly adapted to the new data structure.The AI access validation now correctly uses the nested user premium data from the email account, maintaining the same validation logic while adapting to the new data model.
29-29: Function return value properly updated to return emailAccount.The function now returns the emailAccount object instead of the user object, which is consistent with the overall refactoring to email-based operations.
apps/web/app/(app)/smart-categories/page.tsx (4)
38-38: Variable name properly updated to reflect the new data source.The array destructuring now correctly includes
emailAccountinstead ofuser, maintaining consistency with the architectural changes.
48-50: Database query properly updated to fetch emailAccount by email.The query has been correctly refactored to fetch an emailAccount by email, while maintaining the selection of the same fields needed by the component.
52-52: Function call properly updated to use email parameter.The call to
getCategorizationProgresshas been properly updated to pass the email parameter, which aligns with the updated function signature.
135-137: Property access properly updated to use emailAccount.The
autoCategorizeSendersproperty is now correctly sourced fromemailAccountinstead ofuser, maintaining the same functionality while adapting to the new data structure.apps/web/utils/actions/generate-reply.ts (4)
18-19: Good auth validation pattern!Extracting the email to a separate variable and checking for its presence creates a cleaner authentication flow.
21-21: Verified function signature match withgetAiUserThe call to
getAiUsercorrectly passes an object with an email property, matching the function's updated signature inutils/user/get.ts.
32-35: Consistent parameter updateThe change from
userIdtogetReplyfunction call aligns with the PR's objective of supporting multiple email accounts per user.
50-54: Consistent parameter updateThe
saveReplycall now correctly uses the email as the primary identifier which is consistent with other changes in this PR.apps/web/utils/assistant/process-assistant-email.ts (3)
102-136: Successfully refactored to email-account-centric data modelThe query has been properly updated to retrieve data through the
emailAccounttable rather than directly from theusertable. The selection fields have been appropriately restructured to access user-related data through the nesteduserrelation.The inclusion of
group.itemswith detailed field selection is a good improvement for data completeness.
217-217: Updated reference to nested user dataThe reference to
ruleshas been correctly updated to use the nested pathuser.user.rules, matching the data structure from the updated query.
221-221: Updated reference to nested categoriesThe reference to
categorieshas been correctly updated to use the nested pathuser.user.categories, matching the data structure from the updated query.apps/web/utils/rule/rule.ts (2)
30-40: Improved function signature with named parametersReplacing positional parameters with a single object using named properties is a good practice. This change:
- Makes the function call sites more readable
- Reduces the risk of parameter order mistakes
- Makes the function more maintainable when adding new parameters
30-40:Details
✅ Verification successful
Verify all call sites of
safeCreateRuleSince the function signature has changed significantly, ensure all call sites have been updated to use the new object parameter format.
🏁 Script executed:
#!/bin/bash # Find all occurrences of safeCreateRule to verify they've been updated rg -A 1 "safeCreateRule\(" --glob "!*.md"Length of output: 525
🏁 Script executed:
#!/bin/bash rg -n -A3 "safeCreateRule\(" --glob "!*.md"Length of output: 1082
safeCreateRule call sites all use the new object signature
All occurrences of
safeCreateRulehave been updated to pass an object parameter. Verified in:
- apps/web/utils/reply-tracker/enable.ts (around line 83)
- apps/web/utils/actions/ai-rule.ts (around lines 183 and 488)
No further changes needed.
apps/web/app/api/user/settings/route.ts (3)
16-17: Good auth validation pattern!Extracting the email to a separate variable and checking for its presence improves code readability and error handling.
20-20: Simplified AI provider handlingRemoving the default fallback and using
options.aiProviderdirectly in the switch statement is a good simplification.
48-55: Successfully migrated to email-account-centric data modelThe code now correctly updates the
emailAccounttable using email as the primary key, which aligns with the PR objective of supporting multiple email accounts per user.apps/web/app/api/user/me/route.ts (2)
9-46: Refactored to support multiple email accounts per userThe
getUserfunction has been properly refactored to use email as the primary identifier instead of userId, aligning with the architectural shift to support multiple email accounts per user. The database query now properly fetches from theemailAccounttable and accesses user data through the nested relation.This approach matches the pattern seen in other API routes that extract email from the session and use it as the primary identifier for data access.
50-53: Authentication check updated to use emailThe authentication check now correctly extracts the email from the session and verifies its existence before proceeding, which is consistent with the email-centric approach being implemented throughout the codebase.
apps/web/utils/llms/types.ts (1)
1-8: Types updated to reflect email-centric data modelThe types have been properly updated to reflect that AI-related fields are now associated with email accounts rather than directly with users. The change from
idtouserIdcorrectly represents the new relationship where email accounts reference users.These type changes will ensure type safety across all AI-related modules that use
UserEmailWithAI.apps/web/app/(app)/automation/onboarding/page.tsx (3)
15-16: Authentication check updated to use emailThe authentication check has been correctly updated to verify the email from the session instead of the user ID, consistent with the new approach being implemented across the codebase.
41-66: Data access pattern refactored to use email accountsThe
getUserPreferencesfunction has been properly refactored to use email as the primary identifier and to access user data through the email account relation. The database query structure correctly navigates the updated data model to retrieve the same information.
68-78: Reference paths updated for the new data structureThe returned preferences now correctly reference data through the updated path structure, using
emailAccount.user.rulesandemailAccount.coldEmailBlockerto maintain the same functionality while adapting to the new data model.apps/web/app/api/google/webhook/types.ts (1)
4-20: Type definition updated to use EmailAccount fieldsThe
ProcessHistoryOptionstype has been correctly updated to pick fields fromEmailAccountinstead ofUser, aligning with the broader refactoring to support multiple email accounts per user.Note that while "about" has been removed from the explicitly picked fields, it should still be accessible through the
UserEmailWithAItype that's included via intersection, as that type now picks "about" from EmailAccount.apps/web/app/(app)/setup/page.tsx (4)
20-21: Authentication check correctly migrated to use email instead of user IDThis change aligns with the PR's objective of refactoring user identification to support multiple email accounts per user. Using email as the primary identifier is consistent with the broader refactoring across the codebase.
23-36: Database query updated to use emailAccount instead of user modelGood implementation of the data access pattern change. The query now correctly:
- Queries the
emailAccounttable using email as the primary key- Accesses rules and newsletters through the nested user relationship
- Maintains proper type-safety with appropriate selections
This change supports the foundation for multiple email accounts per user.
39-39: Error message updated to reflect new query targetThe error message has been appropriately updated to reference "User not found" when an email account isn't found, maintaining consistency with user expectations.
41-42: Feature flag logic updated to use nested data structureThe logic determining configured features has been correctly updated to access data through the new nested relationship structure (
emailAccount.user.rulesinstead ofuser.rules), preserving the same functionality with the new data model.apps/web/app/api/reply-tracker/process-previous/route.ts (4)
15-16: Schema updated to use email-based identificationThe request schema now correctly uses email as the primary identifier instead of userId, consistent with the broader refactoring to support multiple email accounts per user.
28-36: Database query migrated to emailAccount modelThe query has been properly updated to:
- Find an emailAccount by email instead of a user by ID
- Include the associated account with access and refresh tokens
- Maintain consistent error handling for the not-found case
This approach aligns well with the multi-account architecture.
40-46: Token access logic adjusted for new data structureThe check for and access to the OAuth tokens has been correctly migrated to use
emailAccount.accountdirectly instead of accessing via an accounts array as before.
48-48: Function call updated to pass emailAccount objectThe call to
processPreviousSentEmailscorrectly passes the emailAccount object, which based on the imported code snippet will contain the necessary email and userId properties expected by that function.apps/web/utils/reply-tracker/inbound.ts (3)
19-33: Improved function signature using object parametersGood refactoring to use an object parameter with named properties instead of positional arguments. This change:
- Improves code readability and maintainability
- Makes the function call sites more explicit
- Provides better IDE support with named parameters
- Allows easier expansion when adding new parameters in the future
This is a beneficial change beyond just supporting the email accounts refactoring.
123-123: Field reference updated to use userId from UserEmailWithAICorrectly updated from
user.idtouser.userIdto maintain consistency with the new data model, where userId is accessed through the emailAccount relationship.
146-154: Function call updated to use object parameter formatThe call to
coordinateReplyProcesshas been properly updated to use the new object parameter format, passing all required fields including the user's ID and email.apps/web/app/api/resend/summary/all/route.ts (3)
24-43: Query updated to fetch emailAccounts instead of usersThe database query has been correctly migrated to:
- Query the
emailAccounttable instead of theusertable- Filter by summaryEmailFrequency directly on the emailAccount
- Move the premium check to the nested user relationship
- Maintain the same filtering for account age
This aligns with the refactoring to make email the primary user identifier.
49-64: Iteration and queue publishing updated for email accountsThe loop now correctly:
- Iterates over emailAccounts instead of users
- Uses emailAccount.email in the message body for the queue
- Logs errors with the email identifier instead of userId
This ensures the summary email distribution works correctly with the new data structure.
66-67: Logging and return value updated for consistencyThe final logging and return value now correctly reference the count of emailAccounts instead of users, maintaining consistency in the terminology throughout the function.
apps/web/utils/reply-tracker/generate-draft.ts (4)
41-41: User identification refactored to use email instead of IDThe function now uses
idto query for the AI user, which aligns with the broader refactoring to support multiple email accounts per user.
121-124: Redis key scheme updated to use email for identificationThe
getReplyfunction now uses the user's email as the identifier instead of user ID, maintaining consistency with the updated Redis key scheme in the reply utilities.
141-141: Knowledge base query now references userId propertyChanged from
userId: user.idtouserId: user.userIdto reflect the updated user object structure that comes fromEmailAccountmodel rather thanUsermodel.
194-195: Redis cache updates now use email-based keysSaving reply cache entries now uses the email-based key scheme, ensuring consistency with the retrieval method and overall architecture change.
apps/web/utils/ai/group/create-group.ts (3)
6-6: Imported UserEmailWithAI type for standardized user representationThe imported type provides a consistent structure for user data with AI fields, supporting the multi-email account architecture.
57-57: Function parameter type updated to UserEmailWithAIThe
aiGenerateGroupItemsfunction now uses the standardizedUserEmailWithAItype, which includes the necessaryuserId,
130-130: Helper function parameter type updated to UserEmailWithAIThe
verifyGroupItemsfunction now also uses the standardizedUserEmailWithAItype, maintaining consistency with the parent function.apps/web/utils/user/get.ts (4)
2-2: Added import for UserEmailWithAI typeThis import supports the explicit return type annotation and standardizes user data representation across the codebase.
4-18: Refactored getAiUser to query by email instead of IDThe function has been completely refactored to:
- Accept an email parameter instead of user ID
- Query the
emailAccounttable instead of theusertable- Select
userIdinstead ofidin the result- Explicitly return
Promise<UserEmailWithAI | null>This change is fundamental to supporting multiple email accounts per user, as it shifts the primary lookup key from user ID to email address.
20-37: Updated getAiUserWithTokens to use email-based queriesSimilar to
getAiUser, this function now:
- Queries by email on the
emailAccounttable- Selects
userIdinstead ofid- References the singular
accountrelationship instead of an array ofaccountsThis maintains consistency with the overall architecture change.
43-43: Updated token access for singular account relationshipChanged from accessing tokens via an array index (
accounts[0]) to a direct property access (account), reflecting the updated data model where each email account has a direct one-to-one relationship with an authentication account.apps/web/utils/redis/reply.ts (3)
3-11: Updated Redis key generation to use email as identifierThe
getReplyKeyfunction now constructs keys using email addresses instead of user IDs, supporting the multi-email architecture and ensuring unique keys per email account rather than per user.
13-21: Updated getReply to use email-based parametersThe
getReplyfunction parameters and implementation now use email as the identifier instead of user ID, maintaining consistency with the key generation function.
23-35: Updated saveReply to use email-based parametersThe
saveReplyfunction now also uses email as the identifier instead of user ID, completing the refactoring of the Redis reply cache utilities to support the multi-email account architecture.apps/web/utils/ai/assistant/process-user-request.ts (2)
354-358: Verify authorization check when deleting group items
deleteGroupItemis now called withuserId: user.userId.
Please double‑check thatdeleteGroupIteminternally verifies that theid
really belongs to the provideduserId; otherwise an attacker could delete
another user’s pattern by supplying a foreigngroupItem.id.
If that check is already in place – great! If not, consider enforcing it in
the DBWHEREclause (e.g.id = ? AND userId = ?).
50-50:Details
❌ Incorrect review comment
Minor: ensure no lingering
user.idreferences remain
The type change toUserEmailWithAIremoves theidproperty. A quick
scan shows onlyuser.userIdis now used, but please run a repo search to
be safe.
🏁 Script executed:
#!/bin/bash # List any remaining occurrences of "user.id" in this file rg --line-number $'\buser\.id\b' apps/web/utils/ai/assistant/process-user-request.tsLength of output: 83
🏁 Script executed:
#!/bin/bash # Recursively search for any occurrences of `user.id` across all TypeScript files in the repo rg --with-filename --line-number -g '*.{ts,tsx}' 'user\.id'Length of output: 13241
No lingering
user.idin process-user-request.ts
I ran a targeted search in apps/web/utils/ai/assistant/process-user-request.ts and confirmed there are zerouser.idreferences—onlyuser.userIdis used. All otheruser.idmatches in the repo pertain tosession.user.id(NextAuth session objects), not theUserEmailWithAItype here. You can safely ignore the original suggestion to scan the entire repo.Likely an incorrect or invalid review comment.
apps/web/utils/actions/categorize.ts (2)
90-98: Redis progress key switched to email – confirm consumers were updated
saveCategorizationTotalItemsnow stores progress under the email key.
Make sure all readers (e.g./progress/route.ts) have been migrated;
otherwise the progress bar will always show 0 %.
121-128: Typed object now passed tocategorizeSender– good change
Passing the fully‑typedemailAccountinstead of a naked{ id, about }
improves type‑safety and avoids redundant look‑ups.apps/web/utils/actions/assess.ts (10)
22-23: Authentication now email-based, aligned with multi-email accounts supportThe authentication check is now based on email rather than user ID, which is consistent with the PR objective of supporting multiple email accounts per user.
27-32: Database query refactored to use emailAccount tableThis change shifts the data access pattern from the
usertable to theemailAccounttable, using email as the primary key. This is critical for supporting multiple email accounts per user while maintaining proper data isolation.
35-35: Function parameter updated to use email instead of user IDThe
saveBehaviorProfilefunction now takes email instead of user ID as a parameter, which aligns with the new identification pattern.
41-49: Updated saveBehaviorProfile to operate on emailAccountThe function signature and implementation now correctly use email as the identifier and update the
emailAccounttable instead of theusertable. This is consistent with the architectural shift to email-centric data organization.
55-58: Authentication and user fetching updated to use emailSimilar to the earlier change, authentication now validates using email, and the
getAiUserfunction now accepts an object with email property instead of user ID.
61-64: Database query updated to use email as the primary keyThe query to fetch the emailAccount is now using email as the key, which is consistent with the new data model.
97-99: Update operation uses email as identifierThe Prisma update operation now uses email in the where clause, ensuring consistency with the new data access patterns.
102-104: Account lookup switched to use emailThe account lookup is now performed with email instead of user ID, maintaining consistency across all database operations.
108-108: Error logging updated to use emailError logging now references email instead of user ID, making debugging more aligned with the new identification system.
112-119: EmailAccount creation includes both email and userIdThe creation of a new EmailAccount record now correctly includes both the email and the user ID, establishing the proper relationship between the user and their email account.
apps/web/utils/actions/cold-email.ts (6)
31-32: Authentication check updated to use emailThe authentication check now verifies the presence of
session.user.emailinstead ofsession.user.id, aligning with the multi-email account architecture.
37-40: Database operation updated to target emailAccount by emailThe update operation now correctly targets the
emailAccounttable using email as the identifier, instead of updating theusertable by user ID.
48-57: Authentication and data updates refactored for email-based approachBoth the authentication check and database operations are consistently updated to use email as the primary identifier, which is crucial for the multi-email account support.
129-129: Function call updated to pass emailThe call to
checkColdEmailnow passes the user's email instead of ID, ensuring consistency with the updated function signature.
135-151: Function signature and database query updated for email-based identificationThe
checkColdEmailfunction now:
- Takes email instead of userId as a parameter
- Queries the
emailAccounttable with email as the primary key- Selects additional fields including userId and about
This change is essential for supporting the new data model where multiple email accounts can belong to a single user.
168-168: Updated parameter to use emailAccount instead of userThe
isColdEmailfunction now receives theemailAccountobject instead ofuser, ensuring it works with the updated data structure.apps/web/app/api/user/categorize/senders/batch/handle-batch.ts (7)
39-41: Request parameter extraction and logging updated to use emailThe batch handler now correctly extracts email instead of userId from the request body and logs it accordingly, aligning with the new identification approach.
43-45: User validation and access check updated for email-based identificationThe function now uses
validateUserAndAiAccess({ email })and appropriately renames the returned user context toemailAccount, reflecting the architectural change.
47-47: Using emailAccount.userId for backward compatibilityThe function correctly uses
emailAccount.userIdwhen callinggetCategories, maintaining backward compatibility with functions that still require a user ID.
51-53: Account lookup changed to use email as the primary keyThe Prisma query now uses
findUniquewith email instead offindFirstwith userId and provider, simplifying the lookup and aligning with the new data model.
90-94: Updated parameter name from user to emailAccountThe function call to
categorizeWithAinow correctly passes the parameter asemailAccountinstead ofuser, ensuring compatibility with the updated function signature.
102-102: Using emailAccount.userId for category updateSimilar to line 47, this correctly uses
emailAccount.userIdfor theupdateSenderCategoryfunction to maintain backward compatibility.
131-134: Progress tracking updated to use emailThe
saveCategorizationProgressfunction now uses email instead of userId for tracking categorization progress, completing the shift to email-based identification.apps/web/utils/ai/choose-rule/match-rules.ts (3)
22-22: Updated type import to use the new UserEmailWithAI typeThe import statement now correctly imports the
UserEmailWithAItype, which consolidates user and AI fields that are now part of the EmailAccount entity.
206-210: Updated function signature to use the new user typeThe
findMatchingRulefunction signature now correctly usesUserEmailWithAIinstead of the previous composite type, ensuring type safety with the new data model.
219-223: Consistent type update in all related functionsThe
findMatchingRuleWithReasonsfunction also uses theUserEmailWithAItype, maintaining consistency across the codebase. This type change ensures that the function receives the correct user object structure with the fields it needs.apps/web/utils/reply-tracker/outbound.ts (5)
19-23: Logging improvement: consistent structured loggingThe logger initialization and context have been updated correctly to use email instead of userId. This aligns well with the PR's goal of supporting multiple email accounts per user.
25-30: Proper refactoring to email-based user identificationThe function call has been correctly updated to use email-based identification instead of user ID, following the PR's goal of supporting multiple email accounts per user.
80-88: Proper adaptation to use userId from user objectThe function now correctly uses
user.userIdwhere it previously likely useduser.id, maintaining compatibility with the email account refactoring.
178-186: Well-structured refactoring to email-based identificationThis function has been properly refactored to:
- Accept an object with an email property instead of a user ID
- Query the emailAccount table by email instead of the user table by ID
This change is consistent with the PR's goal of supporting multiple email accounts per user.
39-44:Details
✅ Verification successful
Verify that user.userId is consistently available
The code now uses
user.userIdinstead of what was likelyuser.idbefore. Ensure that this property is consistently available in theUserEmailWithAItype.
🏁 Script executed:
#!/bin/bash # Check if UserEmailWithAI type has userId property ast-grep --pattern 'type UserEmailWithAI = { $$$ userId: $_ $$$ }'Length of output: 74
🏁 Script executed:
#!/bin/bash # Locate the definition of UserEmailWithAI (either as a type alias or interface) rg -n -e "type UserEmailWithAI" -e "interface UserEmailWithAI" # If found, display the surrounding lines to confirm the presence of `userId` rg -n -C3 "userId" $(rg -l -e "type UserEmailWithAI" -e "interface UserEmailWithAI")Length of output: 456
All good –
user.userIdis defined
TheUserEmailWithAItype inapps/web/utils/llms/types.tsis declared as:export type UserEmailWithAI = Pick<EmailAccount, "userId" | "email" | "about"> & UserAIFields;Since it explicitly picks
userIdfromEmailAccount, the property will always be available. No further changes needed.apps/web/utils/redis/clean.ts (5)
7-8: Key pattern updated correctly for email-based identificationThe
threadKeyfunction now uses email instead of userId, which aligns with the PR's goal.
10-32: Well-structured refactoring to use email instead of userIdThe
saveThreadfunction has been properly updated to:
- Accept email instead of userId
- Include email in the cleanThread object
- Call publishThread with the appropriate parameters
This matches the PR's goal of supporting multiple email accounts.
34-53: Good refactoring of function signature and implementationThe
updateThreadfunction has been improved by:
- Changing from positional parameters to a named object parameter
- Using email instead of userId for user identification
- Updating the call to getThread and publishThread accordingly
This makes the function more maintainable and aligns with the email-based identification pattern.
55-68: Properly updated function signature and implementationThe
publishThreadfunction has been appropriately refactored to use a single object parameter with email and thread, maintaining consistency with other functions in this file.
70-73: Function updated to use email instead of userIdThe
getThreadfunction now correctly uses email for identifying users, consistent with the rest of the changes.apps/web/app/api/google/watch/controller.ts (3)
10-28: Well-structured function signature and implementation refactoringThe
watchEmailsfunction has been properly refactored to:
- Accept an object parameter with email and gmail properties
- Update the database query to use emailAccount table by email
- Update logging to include email instead of userId
This aligns with the PR's goal of supporting multiple email accounts per user.
35-58: Consistent refactoring to email-based identificationThe
unwatchEmailsfunction has been properly updated to use email instead of userId for parameter, logging, and error handling.
60-64: Database update correctly refactoredThe database update operation now correctly targets the emailAccount table by email instead of the user table by ID.
apps/web/app/api/clean/route.ts (7)
26-43: Schema correctly updated for email-based identificationThe
cleanThreadBodyschema has been properly updated to use email instead of userId, ensuring consistent parameter validation.
46-93: Comprehensive refactoring to use email instead of userIdThe
cleanThreadfunction has been thoroughly updated to:
- Accept email instead of userId
- Use getAiUserWithTokens with email parameter
- Update logging to include email
- Call saveThread with email
These changes align well with the PR's goal of supporting multiple email accounts.
94-102: Function call correctly updated to use emailThe call to
getPublishnow correctly passes email instead of userId.
216-230: Well-structured function signature refactoringThe
getPublishfunction has been properly refactored to accept an object with email instead of userId, maintaining consistency with other function changes.
252-258: Logging correctly updated to use emailThe logging statements have been properly updated to include email instead of userId for consistent tracking.
259-274: QStash key and updateThread call properly refactoredBoth the QStash key and the call to updateThread have been correctly updated:
- QStash key now uses email instead of userId
- updateThread now takes an object with named properties instead of positional arguments
These changes align with the broader refactoring pattern.
276-277: Logging correctly updatedThe final logging statement has been properly updated to include email instead of userId.
apps/web/utils/actions/ai-rule.ts (8)
60-62: Good refactoring to use emailAccount.The change to retrieve data from the email account model with rules is appropriate for supporting multiple email accounts per user.
128-132: Well-structured data access pattern.Using a dedicated helper function to fetch email account data with associated rules creates a consistent pattern for accessing user data throughout the application.
168-172: Consistent error handling for email account retrieval.The error handling pattern for non-existent email accounts is consistent with other implementations in the file.
183-186: Correctly adapted rule creation for multi-email support.The rule creation parameters now include the email account's userId, aligning with the database requirements while supporting the new multi-email architecture.
286-307: Consistent database query pattern.The query to fetch email account data contains all necessary fields for AI operations and correctly selects user categories, maintaining functionality while supporting multi-email architecture.
488-492: Safe rule creation with consistent parameters.The safeCreateRule call correctly provides the userId from the email account, maintaining proper database relationships.
496-499: Good adaptation of database operations.EmailAccount updates now correctly target the email field instead of userId, properly supporting the multi-email design.
672-692: Well-designed helper function.The
getEmailAccountWithRulesfunction centralizes the common data access pattern used throughout the file, reducing code duplication and ensuring consistent data retrieval.apps/web/utils/redis/categorization-progress.ts (4)
10-12: Good Redis key structure for multi-email support.The getKey function now correctly uses email as the unique identifier for Redis keys, which aligns with the multi-email account architecture.
14-23: Consistent function signature update.The getCategorizationProgress function now accepts email instead of userId, maintaining consistency with the database access patterns throughout the application.
25-42: Proper Redis key generation in saveCategorizationTotalItems.The function correctly uses the new email-based key structure and maintains the existing TTL settings.
44-63: Consistent parameter handling in saveCategorizationProgress.The function maintains consistent behavior with the updated parameter structure, using email for key generation and progress retrieval.
apps/web/app/api/google/webhook/process-history.ts (10)
28-65: Good adaptation of account data retrieval.The query has been appropriately updated to fetch from emailAccount instead of account, selecting all necessary fields including nested tokens and user data.
72-82: Premium status check properly adapted.The premium check now correctly uses the emailAccount.user path to access premium data, maintaining the existing functionality.
83-89: Correctly updated unwatchEmails parameters.The unwatchEmails call now passes specific token and email parameters from the emailAccount object, matching the expected parameters from the controller.
91-98: AI access checks properly adapted.The code correctly accesses AI-related fields from the appropriate locations in the new data structure.
110-114: Consistent feature access validation.The check for automation rules properly traverses the emailAccount.user path to access rules data.
119-125: Token validation properly adapted.The token validation now correctly checks emailAccount.account paths for access and refresh tokens.
128-135: Gmail client initialization updated correctly.The getGmailClientWithRefresh call now receives tokens from the emailAccount.account path.
143-148: History ID calculation properly updated.The startHistoryId calculation now correctly accesses lastSyncedHistoryId from the emailAccount object.
172-182: Properly adapted process history parameters.The processHistory call now passes the emailAccount object as the user context, providing all necessary data for processing.
267-271: Database update adapted correctly.The updateLastSyncedHistoryId function now properly updates the emailAccount record instead of the user record.
apps/web/utils/actions/rule.ts (10)
58-59: Authentication check properly updated.The authentication now verifies session?.user.email instead of session?.user.id to align with the multi-email account architecture.
112-112: Rule prompt file update adapted correctly.The updatePromptFileOnRuleCreated call now passes email instead of userId, matching the updated function signature.
236-240: Consistent parameter passing for prompt updates.The updatePromptFileOnRuleUpdated call now includes the email parameter while maintaining the existing rule parameters.
278-284: Updated parameter structure for instruction updates.The updateRuleInstructionsAndPromptFile call now includes both email and userId, ensuring backward compatibility during the transition.
352-353: Authentication check consistent with multi-email design.The authentication in deleteRuleAction now verifies email presence, aligning with the rest of the application.
370-382: Email account data retrieval adapted correctly.The query now fetches from emailAccount by email, selecting all necessary fields for prompt file operations.
386-390: Rule deletion prompt update adapted correctly.The generatePromptOnDeleteRule call now correctly passes the emailAccount object as the user parameter.
392-395: Database update correctly targets email account.The prompt file update now correctly updates the emailAccount record by email instead of the user record by userId.
470-486: API payload correctly uses email-based identification.The internal API call to process previous emails now includes email in the payload instead of userId, ensuring consistency across the application.
664-670: Rules prompt update correctly targets email account.The final rules prompt update properly targets the emailAccount record by email, maintaining consistency with the rest of the application.
apps/web/app/api/google/watch/all/route.ts (1)
17-24: Lifetime / legacy‑premium users unintentionally excludedThe
whereclause only selects accounts whereuser.premium.lemonSqueezyRenewsAtis in the future.
Users on lifetime licences (lemonSqueezyRenewsAt = NULL) or custom tiers without renewal dates will silently be skipped and lose inbox‑watching.Consider widening the predicate (e.g.
OR lemonSqueezyRenewsAt IS NULL) or delegating the “is‑premium” check to a dedicated helper so all premium edge‑cases remain in one place.apps/web/utils/upstash/categorize-senders.ts (1)
65-78:skipEmailambiguity
skipEmailexpects a raw email but comparison is against a queue name (queue.name). After hashing/sluggifying (see above), the skip logic will break. AcceptskipQueueNameinstead or transform consistently.-export async function deleteEmptyCategorizeSendersQueues({ skipEmail }: { skipEmail: string; }) { +export async function deleteEmptyCategorizeSendersQueues({ skipQueueName }: { skipQueueName: string; }) { return deleteEmptyQueues({ prefix: CATEGORIZE_SENDERS_PREFIX, - skipEmail, + skipQueueName, }); }apps/web/prisma/schema.prisma (2)
28-31: OneAccountnow references bothUserandEmailAccount– ensure consistency
email String? @unique emailAccount EmailAccount? @relation(fields: [email], references: [email])Because
Accountrow from belonging to neither side or to both mismatched sides. Consider:
- Making
provider = "google"(if that’s the intent), or- Adding a DB check / application‑level guard to keep
userId&Otherwise orphaned records will accumulate after the multi‑account migration.
472-474: Referencing a non‑PK field is okay but watch for future changes
CleanupJob.emailAccountreferencesEmailAccount.email. Onceid, remember to update both relation and foreign keys.apps/web/app/api/ai/analyze-sender-pattern/route.ts (6)
23-26: Schema updated correctly to use email instead of userId.The schema now uses email as the primary identifier instead of userId, which aligns with the goal of supporting multiple email accounts per user.
57-62: Good addition of email account existence check.Adding this check is important since you're now fetching by email. The error handling with appropriate logging and 404 response is a good practice.
64-72: Consistent update to the newsletter query.The update to use
email_userIdcomposite key is correct, maintaining the relationship between emails and users while supporting the multi-account model.
79-84: Consistent token access through email account.Accessing tokens through
emailAccount.accountmaintains the data hierarchy while supporting the multi-account structure.
129-136: Properly updated AI pattern detection call.Passing the entire emailAccount object as user context provides all necessary information to the AI function while maintaining compatibility with the multi-account model.
296-326: Well-structured replacement of getUserWithRules with getEmailAccountWithRules.The new function correctly queries by email instead of userId and includes all necessary nested relations. The selection of fields is comprehensive and aligns with the multi-account model.
apps/web/app/api/clean/gmail/route.ts (4)
16-25: Schema successfully updated to use email instead of userId.The schema definition now uses email as the primary identifier, which aligns with the goal of supporting multiple email accounts per user.
38-43: Query updated to fetch emailAccount by email.The database query has been correctly refactored to fetch the emailAccount by email instead of the account by userId, supporting the multi-account structure.
94-104: Function updated consistently with object parameters.The function now uses email as a parameter and properly passes it to both updateThread and saveToDatabase, maintaining consistency throughout the codebase.
117-124: Connection logic updated to use email.The database connection now uses email instead of id, which is consistent with the refactoring approach.
I noticed that the job connection syntax changed from
{ connect: { jobId } }to{ connect: { id: jobId } }. This seems unrelated to the email refactoring but appears to be a fix for how Prisma connects related records.apps/web/utils/ai/choose-rule/run-rules.ts (7)
7-7: Type updated to UserEmailWithAI.Importing UserEmailWithAI instead of combining User & UserAIFields aligns with the multi-account model and makes the code more maintainable.
35-46: Function signature correctly updated to use UserEmailWithAI type.The runRules function now accepts the UserEmailWithAI type, which is appropriate for supporting multiple email accounts per user.
50-55: Good refactoring to use object parameters.Changing from positional arguments to an object with named parameters improves code readability and maintainability. The switch from user.id to user.email is consistent with the multi-account refactoring.
70-76: Consistent update to use user.userId.The function call has been properly updated to use user.userId instead of user.id, which aligns with the UserEmailWithAI type.
251-261: Well-refactored function with clear parameter structure.The analyzeSenderPatternIfAiMatch function has been properly refactored to use a typed object parameter instead of positional arguments, which improves code readability and type safety.
276-281: API call correctly updated to use email.The analyzeSenderPattern call has been properly updated to use email instead of userId, which is consistent with the multi-account refactoring.
118-118:Details
❓ Verification inconclusive
Verify if nullish check is necessary.
userEmail: user.email || "",Consider if this nullish check is still necessary. If UserEmailWithAI guarantees that email is a required field, this check might be redundant.
🏁 Script executed:
#!/bin/bash # Check if email is a required field in UserEmailWithAI type rg -A 10 "export type UserEmailWithAI" --glob "*.ts"Length of output: 209
🏁 Script executed:
#!/bin/bash # Locate the definition of EmailAccount to check if `email` is optional or required rg -n -A5 "export (interface|type) EmailAccount" --glob "*.ts"Length of output: 64
#!/bin/bash # Find where EmailAccount is imported in types.ts rg -n "import.*EmailAccount" --glob "apps/web/utils/llms/types.ts"
#!/bin/bash # Display the first 200 lines of the types file to locate EmailAccount definition sed -n '1,200p' apps/web/utils/llms/types.ts
Unable to verify if
I couldn’t locate theEmailAccountdefinition in the repo, so it’s unclear whether itsUserEmailWithAI(which picks"email"fromEmailAccount) guarantees a non‑null, non‑undefined value:
- If
|| ""fallback.- If it can be missing, keep the nullish check to avoid passing
undefineddownstream.
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (2)
apps/web/prisma/migrations/20250420112535_email_account_settings/migration.sql (2)
10-18: Use IF EXISTS for dropping constraints and indexes.
To make this migration idempotent (and safe to re-run), addIF EXISTSclauses when dropping the foreign key and indexes. This prevents errors if the constraint/index is already gone.
124-136: Group the backfill updates into the same migration flow.
The separateUPDATEstatements onAccountandCleanupJobcould leave data in a transient inconsistent state if run outside a single transaction. Consider moving these into the same transactional block or ordering them immediately after the insert step to maintain referential integrity.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
apps/web/prisma/migrations/20250420112535_email_account_settings/migration.sql(1 hunks)apps/web/prisma/schema.prisma(4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/web/prisma/schema.prisma
🔇 Additional comments (2)
apps/web/prisma/migrations/20250420112535_email_account_settings/migration.sql (2)
50-61: Confirm foreign‐key referential actions match business rules.
The migration usesON DELETE SET NULLforAccount.emailbutON DELETE CASCADEforCleanupJob.email. Ensure these behaviors align with expected data retention and cleanup policies.
47-49:Details
✅ Verification successful
Verify no residual references to the dropped
idcolumn.
RemovingEmailAccount.idmay break code or foreign keys elsewhere. Search the codebase for any literal or inferred references before applying.
🏁 Script executed:
#!/bin/bash # Description: Find references to `EmailAccount.id` in the codebase rg -n "EmailAccount\\.id" -g "*.ts" -g "*.tsx" -g "*.js" -g "*.sql"Length of output: 68
🏁 Script executed:
#!/bin/bash # Check the Prisma schema for the EmailAccount model definition rg -n "model EmailAccount" -g "*.prisma" -A10 # Find all TypeScript/TSX usages of the Prisma client’s emailAccount API rg -n "prisma\.emailAccount" -g "*.ts" -g "*.tsx"Length of output: 5875
🏁 Script executed:
#!/bin/bash # Search for prisma.emailAccount queries that reference `id` in their `where` clause rg -n "prisma\.emailAccount\.(findUnique|findFirst|findMany|update|upsert|delete)\(\s*\{[^}]*where:\s*\{[^}]*id:" -g "*.ts" -g "*.tsx" # Look for any direct property access on `emailAccount.id` rg -n "emailAccount\.id" -g "*.ts" -g "*.tsx"Length of output: 184
No residual references to
EmailAccount.idfound
Verified via code search that no TypeScript, SQL, or Prisma queries reference the droppedidcolumn. It’s safe to apply this migration.
apps/web/prisma/migrations/20250420112535_email_account_settings/migration.sql
Show resolved
Hide resolved
apps/web/prisma/migrations/20250420112535_email_account_settings/migration.sql
Show resolved
Hide resolved
apps/web/prisma/migrations/20250420112535_email_account_settings/migration.sql
Show resolved
Hide resolved
apps/web/prisma/migrations/20250420112535_email_account_settings/migration.sql
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
apps/web/utils/auth.ts (1)
49-85: Well-structured implementation for multiple email accounts supportThis is a good approach to support multiple email accounts per user. The code creates an EmailAccount record alongside the Account record during the linking process, establishing the necessary relationships between user, email, and account.
However, I would suggest a few improvements:
- Consider wrapping these database operations in a transaction to ensure atomicity:
- // --- Step 1: Fetch the user to get their email --- - const user = await prisma.user.findUniqueOrThrow({ - where: { id: account.userId }, - select: { email: true }, - }); - - // --- Step 2: Create the Account record --- - const createdAccount = await prisma.account.create({ - data: { - ...account, - email: user.email, - }, - select: { id: true }, - }); - - // --- Step 3: Create the corresponding EmailAccount record --- - await prisma.emailAccount.create({ - data: { - email: user.email, - userId: account.userId, - accountId: createdAccount.id, - }, - }); + await prisma.$transaction(async (tx) => { + // --- Step 1: Fetch the user to get their email --- + const user = await tx.user.findUniqueOrThrow({ + where: { id: account.userId }, + select: { email: true }, + }); + + // --- Step 2: Create the Account record --- + const createdAccount = await tx.account.create({ + data: { + ...account, + email: user.email, + }, + select: { id: true }, + }); + + // --- Step 3: Create the corresponding EmailAccount record --- + await tx.emailAccount.create({ + data: { + email: user.email, + userId: account.userId, + accountId: createdAccount.id, + }, + }); + });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
apps/web/app/(app)/premium/Pricing.tsx(2 hunks)apps/web/app/(app)/settings/MultiAccountSection.tsx(2 hunks)apps/web/app/(app)/usage/usage.tsx(1 hunks)apps/web/components/PremiumAlert.tsx(2 hunks)apps/web/hooks/useUser.ts(1 hunks)apps/web/utils/auth.ts(2 hunks)
✅ Files skipped from review due to trivial changes (2)
- apps/web/app/(app)/settings/MultiAccountSection.tsx
- apps/web/app/(app)/premium/Pricing.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/web/app/(app)/usage/usage.tsx
- apps/web/components/PremiumAlert.tsx
🧰 Additional context used
🧬 Code Graph Analysis (2)
apps/web/hooks/useUser.ts (1)
apps/web/app/api/user/me/route.ts (1)
UserResponse(7-7)
apps/web/utils/auth.ts (2)
apps/web/app/api/google/webhook/logger.ts (1)
logger(3-3)apps/web/utils/error.ts (1)
captureException(60-72)
🔇 Additional comments (3)
apps/web/utils/auth.ts (3)
4-4: Good addition of the AdapterAccount type importThis type import is necessary for the custom linkAccount method signature.
77-82: Good error handling with contextual informationThe error handling approach here is solid. It:
- Logs the error with contextual information using the scoped logger
- Captures the exception with additional context for monitoring
- Re-throws the error to maintain the original behavior
This approach helps with debugging while still preserving the expected error propagation.
62-64:Details
✅ Verification successful
Verify Account model schema includes email field
The code is adding an email field to the Account record, but make sure the Prisma schema for Account has been updated to include this field.
🏁 Script executed:
#!/bin/bash # Check if the Account model in the Prisma schema has an email field grep -A 20 "model Account" $(fd --type f "schema.prisma" --exec echo {})Length of output: 947
Account model email field verified
Theapps/web/prisma/schema.prismafile’sAccountmodel already includes:email String? @uniqueNo further schema updates are needed.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
apps/web/utils/swr.ts (2)
18-38: Good error normalization approach for SWR responses.The error handling logic effectively normalizes both standard Error objects and non-Error SWR errors into a consistent format. This will make error handling more predictable throughout the application.
One minor issue: There's an extra blank line at line 40 that could be removed.
} - // Handle API error returned within data
49-57: Well-implemented successful data handling.The function properly handles the success case, preserving the original data when no errors are detected. The type casting approach correctly filters out the API error possibility while maintaining type safety.
Consider adding handling for more complex API error structures that might occur in the future as your multi-account implementation expands.
// No error found, return the data (might be null/undefined during loading) // Cast data to expected type, filtering out the TApiError possibility return { ...swrResult, data: data as TData | null, // SWR handles undefined during load error: undefined, } as SWRResponse<TData | null, NormalizedError>; } + +// Helper to check if a response is an API error +export function isApiError<T>(data: T | { error: string }): data is { error: string } { + return data !== null && + typeof data === 'object' && + 'error' in data && + typeof (data as any).error === 'string'; +}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
apps/web/app/(app)/license/page.tsx(2 hunks)apps/web/hooks/useUser.ts(1 hunks)apps/web/utils/swr.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/web/app/(app)/license/page.tsx
- apps/web/hooks/useUser.ts
🔇 Additional comments (2)
apps/web/utils/swr.ts (2)
1-17: Well-structured type definitions and documentation.The type definitions and JSDoc comments are clear and informative, providing good context for the utility function. The generic type parameters are appropriately defined with sensible defaults.
41-48: Effective API error detection and normalization.The API error handling logic correctly identifies error responses that might be returned as data objects containing an
errorproperty, ensuring consistent error formats throughout the application.
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (3)
apps/web/prisma/schema.prisma (3)
28-29: Consider making the inverse relation explicit
Prisma can infer the one‑to‑one betweenAccount.emailAccountandEmailAccount.accountId, but declaring the same@relation(name: ...)on both sides improves clarity and prevents accidental drift if you rename fields or adjust cascade rules.
55-79: Clean up deprecated fields inUser
You’ve marked a large number of fields as// deprecated. Leaving them in place indefinitely clutters the schema and makes it harder to see which fields are actively used. Consider planning a follow‑up migration to remove these columns or standardizing your deprecation comments (e.g., linking to a GitHub issue).Also applies to: 81-81
470-472: ReviewCleanupJobrelation redundancy
You’ve addedEmailAccount.emailalongside the existinguserId→User.id. If cleanup jobs should track per‐account settings only, consider droppinguserIdor enforcing it to matchemailAccount.userIdto avoid data divergence.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
apps/web/prisma/migrations/20250420131728_email_account_settings/migration.sql(1 hunks)apps/web/prisma/schema.prisma(4 hunks)apps/web/utils/auth.ts(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/web/utils/auth.ts
🔇 Additional comments (3)
apps/web/prisma/schema.prisma (2)
116-133: New settings fields need matchingADD COLUMNs
Fields likeabout,behaviorProfile,aiProvider, etc., are introduced onEmailAccountbut aren’t present in the migration’sALTER TABLEforEmailAccount. Every field here needs a correspondingADD COLUMN.
146-146: Index onlastSummaryEmailAt
Excellent addition—indexing this timestamp will speed up queries for summary scheduling.apps/web/prisma/migrations/20250420131728_email_account_settings/migration.sql (1)
52-53: Verify foreign key order and presence of columns
You later add a foreign key onEmailAccount.accountIdand one onCleanupJob.email, but if those columns were never created or populated, the constraints will fail. Please ensure:
ADD COLUMN "accountId"is executed beforeALTER TABLE ADD CONSTRAINT ... FOREIGN KEY ("accountId").ADD COLUMN "email"and backfill ofCleanupJob.emailcomplete successfully before settingNOT NULLand adding the FK.Also applies to: 116-127
Laying ground work for support of multiple accounts per user
Summary by CodeRabbit
New Features
Refactor
Bug Fixes
Chores
Documentation