diff --git a/apps/web/app/api/outlook/linking/callback/route.ts b/apps/web/app/api/outlook/linking/callback/route.ts index 6e35efecc4..5607eada9e 100644 --- a/apps/web/app/api/outlook/linking/callback/route.ts +++ b/apps/web/app/api/outlook/linking/callback/route.ts @@ -113,17 +113,18 @@ export const GET = withError("outlook/linking/callback", async (request) => { } const profile = await profileResponse.json(); + const providerAccountId = profile.id; const providerEmail = profile.mail || profile.userPrincipalName; - if (!providerEmail) { - throw new Error("Profile missing required email"); + if (!providerAccountId || !providerEmail) { + throw new Error("Profile missing required id or email"); } - const existingAccountByProviderId = await prisma.account.findUnique({ + const existingAccount = await prisma.account.findUnique({ where: { provider_providerAccountId: { provider: "microsoft", - providerAccountId: profile.id || providerEmail, + providerAccountId, }, }, select: { @@ -134,24 +135,6 @@ export const GET = withError("outlook/linking/callback", async (request) => { }, }); - const existingAccountByEmail = await prisma.account.findFirst({ - where: { - provider: "microsoft", - user: { - email: providerEmail.trim().toLowerCase(), - }, - }, - select: { - id: true, - userId: true, - user: { select: { name: true, email: true } }, - emailAccount: true, - }, - }); - - const existingAccount = - existingAccountByProviderId || existingAccountByEmail; - const linkingResult = await handleAccountLinking({ existingAccountId: existingAccount?.id || null, hasEmailAccount: !!existingAccount?.emailAccount, @@ -207,15 +190,13 @@ export const GET = withError("outlook/linking/callback", async (request) => { logger.warn("Failed to fetch profile picture", { error }); } - const microsoftProviderAccountId = profile.id || providerEmail; - try { const newAccount = await prisma.account.create({ data: { userId: targetUserId, type: "oidc", provider: "microsoft", - providerAccountId: microsoftProviderAccountId, + providerAccountId, access_token: tokens.access_token, refresh_token: tokens.refresh_token, expires_at: expiresAt, @@ -247,7 +228,7 @@ export const GET = withError("outlook/linking/callback", async (request) => { where: { provider_providerAccountId: { provider: "microsoft", - providerAccountId: microsoftProviderAccountId, + providerAccountId, }, }, select: { userId: true }, @@ -258,7 +239,7 @@ export const GET = withError("outlook/linking/callback", async (request) => { "Account was created by concurrent request, continuing", { targetUserId, - providerAccountId: microsoftProviderAccountId, + providerAccountId, }, ); } else { diff --git a/apps/web/app/api/resend/summary/all/route.ts b/apps/web/app/api/resend/summary/all/route.ts index 56b171faf8..f7aa8382ae 100644 --- a/apps/web/app/api/resend/summary/all/route.ts +++ b/apps/web/app/api/resend/summary/all/route.ts @@ -13,6 +13,7 @@ import { captureException } from "@/utils/error"; import type { Logger } from "@/utils/logger"; import { publishToQstashQueue } from "@/utils/upstash"; import { getPremiumUserFilter } from "@/utils/premium"; +import type { SendSummaryEmailBody } from "../validation"; export const dynamic = "force-dynamic"; export const maxDuration = 300; @@ -45,7 +46,7 @@ async function sendSummaryAllUpdate(logger: Logger) { logger.info("Sending summary all update"); const emailAccounts = await prisma.emailAccount.findMany({ - select: { email: true }, + select: { id: true }, where: { summaryEmailFrequency: { not: Frequency.NEVER, @@ -64,16 +65,16 @@ async function sendSummaryAllUpdate(logger: Logger) { for (const emailAccount of emailAccounts) { try { - await publishToQstashQueue({ + await publishToQstashQueue({ queueName: "email-summary-all", parallelism: 3, // Allow up to 3 concurrent jobs from this queue url, - body: { email: emailAccount.email }, + body: { emailAccountId: emailAccount.id }, headers: getCronSecretHeader(), }); } catch (error) { logger.error("Failed to publish to Qstash", { - email: emailAccount.email, + emailAccountId: emailAccount.id, error, }); } diff --git a/apps/web/app/api/resend/summary/route.ts b/apps/web/app/api/resend/summary/route.ts index 7fe1918494..51e5ed6f33 100644 --- a/apps/web/app/api/resend/summary/route.ts +++ b/apps/web/app/api/resend/summary/route.ts @@ -1,4 +1,3 @@ -import { z } from "zod"; import { NextResponse } from "next/server"; import { subHours } from "date-fns/subHours"; import { sendSummaryEmail } from "@inboxzero/resend"; @@ -12,11 +11,10 @@ import type { Logger } from "@/utils/logger"; import { getMessagesBatch } from "@/utils/gmail/message"; import { decodeSnippet } from "@/utils/gmail/decode"; import { createUnsubscribeToken } from "@/utils/unsubscribe"; +import { sendSummaryEmailBody } from "./validation"; export const maxDuration = 60; -const sendSummaryEmailBody = z.object({ emailAccountId: z.string() }); - export const GET = withEmailAccount("resend/summary", async (request) => { // send to self const emailAccountId = request.auth.emailAccountId; diff --git a/apps/web/app/api/resend/summary/validation.ts b/apps/web/app/api/resend/summary/validation.ts new file mode 100644 index 0000000000..2e3493d9bd --- /dev/null +++ b/apps/web/app/api/resend/summary/validation.ts @@ -0,0 +1,7 @@ +import { z } from "zod"; + +export const sendSummaryEmailBody = z.object({ + emailAccountId: z.string(), +}); + +export type SendSummaryEmailBody = z.infer;