diff --git a/apps/web/app/api/chats/[chatId]/route.ts b/apps/web/app/api/chats/[chatId]/route.ts index 5ae8eda6ec..3abefa9c55 100644 --- a/apps/web/app/api/chats/[chatId]/route.ts +++ b/apps/web/app/api/chats/[chatId]/route.ts @@ -4,21 +4,24 @@ import { withEmailAccount } from "@/utils/middleware"; export type GetChatResponse = Awaited>; -export const GET = withEmailAccount(async (request, { params }) => { - const { emailAccountId } = request.auth; - const { chatId } = await params; +export const GET = withEmailAccount( + "chats/detail", + async (request, { params }) => { + const { emailAccountId } = request.auth; + const { chatId } = await params; - if (!chatId) { - return NextResponse.json( - { error: "Chat ID is required." }, - { status: 400 }, - ); - } + if (!chatId) { + return NextResponse.json( + { error: "Chat ID is required." }, + { status: 400 }, + ); + } - const chat = await getChat({ chatId, emailAccountId }); + const chat = await getChat({ chatId, emailAccountId }); - return NextResponse.json(chat); -}); + return NextResponse.json(chat); + }, +); async function getChat({ chatId, diff --git a/apps/web/app/api/chats/route.ts b/apps/web/app/api/chats/route.ts index 451ee0e713..bdba92be1c 100644 --- a/apps/web/app/api/chats/route.ts +++ b/apps/web/app/api/chats/route.ts @@ -4,7 +4,7 @@ import { withEmailAccount } from "@/utils/middleware"; export type GetChatsResponse = Awaited>; -export const GET = withEmailAccount(async (request) => { +export const GET = withEmailAccount("chats", async (request) => { const emailAccountId = request.auth.emailAccountId; const result = await getChats({ emailAccountId }); return NextResponse.json(result); diff --git a/apps/web/app/api/clean/history/route.ts b/apps/web/app/api/clean/history/route.ts index eaaab9dde6..9a55638fd8 100644 --- a/apps/web/app/api/clean/history/route.ts +++ b/apps/web/app/api/clean/history/route.ts @@ -13,7 +13,7 @@ async function getCleanHistory({ emailAccountId }: { emailAccountId: string }) { return { result }; } -export const GET = withEmailAccount(async (request) => { +export const GET = withEmailAccount("clean/history", async (request) => { const emailAccountId = request.auth.emailAccountId; const result = await getCleanHistory({ emailAccountId }); diff --git a/apps/web/app/api/email-stream/route.ts b/apps/web/app/api/email-stream/route.ts index f3c9bc2d4f..dec3407cd4 100644 --- a/apps/web/app/api/email-stream/route.ts +++ b/apps/web/app/api/email-stream/route.ts @@ -1,4 +1,3 @@ -import { createScopedLogger } from "@/utils/logger"; import { RedisSubscriber } from "@/utils/redis/subscriber"; import { withAuth } from "@/utils/middleware"; import { NextResponse } from "next/server"; @@ -6,19 +5,19 @@ import { getEmailAccount } from "@/utils/redis/account-validation"; export const maxDuration = 300; -const logger = createScopedLogger("email-stream"); - // 5 minutes in milliseconds const INACTIVITY_TIMEOUT = 5 * 60 * 1000; -export const GET = withAuth(async (request) => { +export const GET = withAuth("email-stream", async (request) => { const { userId } = request.auth; const url = new URL(request.url); const emailAccountId = url.searchParams.get("emailAccountId"); if (!emailAccountId) { - logger.warn("Bad Request: Email Account ID missing from query parameters."); + request.logger.warn( + "Bad Request: Email Account ID missing from query parameters.", + ); return NextResponse.json( { error: "Email account ID is required" }, { status: 400 }, @@ -30,7 +29,7 @@ export const GET = withAuth(async (request) => { if (!email) return NextResponse.json({ error: "Invalid account ID" }, { status: 403 }); - logger.info("Processing GET request for email stream", { + request.logger.info("Processing GET request for email stream", { userId, emailAccountId, }); @@ -39,7 +38,8 @@ export const GET = withAuth(async (request) => { const redisSubscriber = RedisSubscriber.getInstance(); redisSubscriber.psubscribe(pattern, (err) => { - if (err) logger.error("Error subscribing to threads", { error: err }); + if (err) + request.logger.error("Error subscribing to threads", { error: err }); }); // Set headers for SSE @@ -51,7 +51,7 @@ export const GET = withAuth(async (request) => { "X-Accel-Buffering": "no", // For anyone using Nginx }); - logger.info("Creating SSE stream", { emailAccountId }); + request.logger.info("Creating SSE stream", { emailAccountId }); const encoder = new TextEncoder(); @@ -64,7 +64,9 @@ export const GET = withAuth(async (request) => { const resetInactivityTimer = () => { if (inactivityTimer) clearTimeout(inactivityTimer); inactivityTimer = setTimeout(() => { - logger.info("Stream closed due to inactivity", { emailAccountId }); + request.logger.info("Stream closed due to inactivity", { + emailAccountId, + }); if (!isControllerClosed) { isControllerClosed = true; controller.close(); @@ -85,7 +87,7 @@ export const GET = withAuth(async (request) => { ); resetInactivityTimer(); // Reset timer on message } catch (error) { - logger.error("Error enqueueing message", { error }); + request.logger.error("Error enqueueing message", { error }); // If we hit an error, mark controller as closed and clean up isControllerClosed = true; redisSubscriber.punsubscribe(pattern); @@ -94,7 +96,9 @@ export const GET = withAuth(async (request) => { }); request.signal.addEventListener("abort", () => { - logger.info("Cleaning up Redis subscription", { emailAccountId }); + request.logger.info("Cleaning up Redis subscription", { + emailAccountId, + }); clearTimeout(inactivityTimer); if (!isControllerClosed) { isControllerClosed = true; diff --git a/apps/web/app/api/google/calendar/auth-url/route.ts b/apps/web/app/api/google/calendar/auth-url/route.ts index d467dfe974..f3e6f0a8ed 100644 --- a/apps/web/app/api/google/calendar/auth-url/route.ts +++ b/apps/web/app/api/google/calendar/auth-url/route.ts @@ -28,18 +28,21 @@ const getAuthUrl = ({ emailAccountId }: { emailAccountId: string }) => { return { url, state }; }; -export const GET = withEmailAccount(async (request) => { - const { emailAccountId } = request.auth; - const { url, state } = getAuthUrl({ emailAccountId }); - - const res: GetCalendarAuthUrlResponse = { url }; - const response = NextResponse.json(res); - - response.cookies.set( - CALENDAR_STATE_COOKIE_NAME, - state, - oauthStateCookieOptions, - ); - - return response; -}); +export const GET = withEmailAccount( + "google/calendar/auth-url", + async (request) => { + const { emailAccountId } = request.auth; + const { url, state } = getAuthUrl({ emailAccountId }); + + const res: GetCalendarAuthUrlResponse = { url }; + const response = NextResponse.json(res); + + response.cookies.set( + CALENDAR_STATE_COOKIE_NAME, + state, + oauthStateCookieOptions, + ); + + return response; + }, +); diff --git a/apps/web/app/api/google/contacts/route.ts b/apps/web/app/api/google/contacts/route.ts index 4022885923..b67b97626f 100644 --- a/apps/web/app/api/google/contacts/route.ts +++ b/apps/web/app/api/google/contacts/route.ts @@ -16,7 +16,7 @@ async function getContacts(client: people_v1.People, query: string) { return { result }; } -export const GET = withEmailAccount(async (request) => { +export const GET = withEmailAccount("google/contacts", async (request) => { if (!env.NEXT_PUBLIC_CONTACTS_ENABLED) return NextResponse.json({ error: "Contacts API not enabled" }); diff --git a/apps/web/app/api/google/linking/auth-url/route.ts b/apps/web/app/api/google/linking/auth-url/route.ts index 0e8d91f8a6..42e32a4bf4 100644 --- a/apps/web/app/api/google/linking/auth-url/route.ts +++ b/apps/web/app/api/google/linking/auth-url/route.ts @@ -25,7 +25,7 @@ const getAuthUrl = ({ userId }: { userId: string }) => { return { url, state }; }; -export const GET = withAuth(async (request) => { +export const GET = withAuth("google/linking/auth-url", async (request) => { const userId = request.auth.userId; const { url, state } = getAuthUrl({ userId }); diff --git a/apps/web/app/api/google/watch/all/route.ts b/apps/web/app/api/google/watch/all/route.ts index b23415d6e6..8611d206e2 100644 --- a/apps/web/app/api/google/watch/all/route.ts +++ b/apps/web/app/api/google/watch/all/route.ts @@ -128,7 +128,7 @@ async function watchAllEmails() { return NextResponse.json({ success: true }); } -export const GET = withError(async (request) => { +export const GET = withError("google/watch/all", async (request) => { if (!hasCronSecret(request)) { captureException( new Error("Unauthorized cron request: api/google/watch/all"), diff --git a/apps/web/app/api/google/watch/route.ts b/apps/web/app/api/google/watch/route.ts index 898df1686f..a78bcb87cb 100644 --- a/apps/web/app/api/google/watch/route.ts +++ b/apps/web/app/api/google/watch/route.ts @@ -1,15 +1,12 @@ import { NextResponse } from "next/server"; import { watchEmails } from "./controller"; import { withAuth } from "@/utils/middleware"; -import { createScopedLogger } from "@/utils/logger"; import prisma from "@/utils/prisma"; import { createEmailProvider } from "@/utils/email/provider"; export const dynamic = "force-dynamic"; -const logger = createScopedLogger("api/google/watch"); - -export const GET = withAuth(async (request) => { +export const GET = withAuth("google/watch", async (request) => { const userId = request.auth.userId; const results = []; @@ -43,7 +40,9 @@ export const GET = withAuth(async (request) => { expirationDate, }); } else { - logger.error("Error watching inbox for account", { emailAccountId }); + request.logger.error("Error watching inbox for account", { + emailAccountId, + }); results.push({ emailAccountId, status: "error", @@ -51,7 +50,7 @@ export const GET = withAuth(async (request) => { }); } } catch (error) { - logger.error("Exception while watching inbox for account", { + request.logger.error("Exception while watching inbox for account", { emailAccountId, error, }); diff --git a/apps/web/app/api/knowledge/route.ts b/apps/web/app/api/knowledge/route.ts index b3148d8920..405e5fb586 100644 --- a/apps/web/app/api/knowledge/route.ts +++ b/apps/web/app/api/knowledge/route.ts @@ -7,7 +7,7 @@ export type GetKnowledgeResponse = { items: Knowledge[]; }; -export const GET = withEmailAccount(async (request) => { +export const GET = withEmailAccount("knowledge", async (request) => { const emailAccountId = request.auth.emailAccountId; const items = await prisma.knowledge.findMany({ where: { emailAccountId }, diff --git a/apps/web/app/api/labels/route.ts b/apps/web/app/api/labels/route.ts index db44757b04..410af66de2 100644 --- a/apps/web/app/api/labels/route.ts +++ b/apps/web/app/api/labels/route.ts @@ -1,8 +1,5 @@ import { NextResponse } from "next/server"; import { withEmailProvider } from "@/utils/middleware"; -import { createScopedLogger } from "@/utils/logger"; - -const logger = createScopedLogger("labels"); export type UnifiedLabel = { id: string; @@ -23,7 +20,7 @@ export type LabelsResponse = { export const dynamic = "force-dynamic"; export const maxDuration = 30; -export const GET = withEmailProvider(async (request) => { +export const GET = withEmailProvider("labels", async (request) => { const { emailProvider } = request; try { @@ -39,7 +36,7 @@ export const GET = withEmailProvider(async (request) => { })); return NextResponse.json({ labels: unifiedLabels }); } catch (error) { - logger.error("Error fetching labels", { error }); + request.logger.error("Error fetching labels", { error }); return NextResponse.json({ labels: [] }, { status: 500 }); } }); diff --git a/apps/web/app/api/mcp/[integration]/auth-url/route.ts b/apps/web/app/api/mcp/[integration]/auth-url/route.ts index a6c3612158..24a163f493 100644 --- a/apps/web/app/api/mcp/[integration]/auth-url/route.ts +++ b/apps/web/app/api/mcp/[integration]/auth-url/route.ts @@ -1,7 +1,6 @@ import { NextResponse } from "next/server"; import { env } from "@/env"; import { withEmailAccount } from "@/utils/middleware"; -import { createScopedLogger } from "@/utils/logger"; import { SafeError } from "@/utils/error"; import { oauthStateCookieOptions, @@ -15,59 +14,61 @@ import { generateOAuthUrl } from "@/utils/mcp/oauth"; export type GetMcpAuthUrlResponse = { url: string }; -export const GET = withEmailAccount(async (request, { params }) => { - const { integration } = await params; - const { emailAccountId } = request.auth; - const userId = request.auth.userId; +export const GET = withEmailAccount( + "mcp/auth-url", + async (request, { params }) => { + const { integration } = await params; + const { emailAccountId } = request.auth; + const userId = request.auth.userId; - const logger = createScopedLogger("mcp/auth-url").with({ - userId, - integration, - }); + const logger = request.logger.with({ + integration, + }); - const integrationConfig = getIntegration(integration); + const integrationConfig = getIntegration(integration); - if (!integrationConfig) { - throw new SafeError(`Integration ${integration} not found`); - } + if (!integrationConfig) { + throw new SafeError(`Integration ${integration} not found`); + } - if (integrationConfig.authType !== "oauth") { - throw new SafeError(`Integration ${integration} does not support OAuth`); - } + if (integrationConfig.authType !== "oauth") { + throw new SafeError(`Integration ${integration} does not support OAuth`); + } - try { - const redirectUri = `${env.NEXT_PUBLIC_BASE_URL}/api/mcp/${integration}/callback`; + try { + const redirectUri = `${env.NEXT_PUBLIC_BASE_URL}/api/mcp/${integration}/callback`; - const state = generateOAuthState({ - userId, - emailAccountId, - type: getMcpOAuthStateType(integration), - }); + const state = generateOAuthState({ + userId, + emailAccountId, + type: getMcpOAuthStateType(integration), + }); - const { url, codeVerifier } = await generateOAuthUrl({ - integration, - redirectUri, - state, - }); + const { url, codeVerifier } = await generateOAuthUrl({ + integration, + redirectUri, + state, + }); - // Set secure cookies for state and PKCE verifier - const response = NextResponse.json({ url }); + // Set secure cookies for state and PKCE verifier + const response = NextResponse.json({ url }); - const maxAge = 60 * 10; // 10 minutes + const maxAge = 60 * 10; // 10 minutes - response.cookies.set(getMcpStateCookieName(integration), state, { - ...oauthStateCookieOptions, - maxAge, - }); + response.cookies.set(getMcpStateCookieName(integration), state, { + ...oauthStateCookieOptions, + maxAge, + }); - response.cookies.set(getMcpPkceCookieName(integration), codeVerifier, { - ...oauthStateCookieOptions, - maxAge, - }); + response.cookies.set(getMcpPkceCookieName(integration), codeVerifier, { + ...oauthStateCookieOptions, + maxAge, + }); - return response; - } catch (error) { - logger.error("Failed to generate MCP auth URL", { error }); - throw new SafeError("Failed to generate authorization URL"); - } -}); + return response; + } catch (error) { + logger.error("Failed to generate MCP auth URL", { error }); + throw new SafeError("Failed to generate authorization URL"); + } + }, +); diff --git a/apps/web/app/api/mcp/integrations/route.ts b/apps/web/app/api/mcp/integrations/route.ts index a9a8abb4eb..f6247f6723 100644 --- a/apps/web/app/api/mcp/integrations/route.ts +++ b/apps/web/app/api/mcp/integrations/route.ts @@ -5,7 +5,7 @@ import prisma from "@/utils/prisma"; export type GetIntegrationsResponse = Awaited>; -export const GET = withEmailAccount(async (request) => { +export const GET = withEmailAccount("mcp/integrations", async (request) => { const emailAccountId = request.auth.emailAccountId; return NextResponse.json(await getData(emailAccountId)); }); diff --git a/apps/web/app/api/messages/attachment/route.ts b/apps/web/app/api/messages/attachment/route.ts index 241892cb41..2707edf418 100644 --- a/apps/web/app/api/messages/attachment/route.ts +++ b/apps/web/app/api/messages/attachment/route.ts @@ -2,7 +2,7 @@ import { NextResponse } from "next/server"; import { withEmailProvider } from "@/utils/middleware"; import { attachmentQuery } from "@/app/api/messages/validation"; -export const GET = withEmailProvider(async (request) => { +export const GET = withEmailProvider("messages/attachment", async (request) => { const { emailProvider } = request; const { searchParams } = new URL(request.url); diff --git a/apps/web/app/api/messages/batch/route.ts b/apps/web/app/api/messages/batch/route.ts index 4b7e0d6621..3c5d274bb7 100644 --- a/apps/web/app/api/messages/batch/route.ts +++ b/apps/web/app/api/messages/batch/route.ts @@ -30,7 +30,7 @@ async function getMessagesBatch({ return messages; } -export const GET = withEmailProvider(async (request) => { +export const GET = withEmailProvider("messages/batch", async (request) => { const { emailProvider } = request; const { searchParams } = new URL(request.url); diff --git a/apps/web/app/api/messages/route.ts b/apps/web/app/api/messages/route.ts index 8f59182303..e9b0483198 100644 --- a/apps/web/app/api/messages/route.ts +++ b/apps/web/app/api/messages/route.ts @@ -1,17 +1,14 @@ import { NextResponse } from "next/server"; import { withEmailProvider } from "@/utils/middleware"; import { messageQuerySchema } from "@/app/api/messages/validation"; -import { createScopedLogger } from "@/utils/logger"; import { isAssistantEmail } from "@/utils/assistant/is-assistant-email"; import { GmailLabel } from "@/utils/gmail/label"; import type { EmailProvider } from "@/utils/email/types"; import { isGoogleProvider } from "@/utils/email/provider-types"; -const logger = createScopedLogger("api/messages"); - export type MessagesResponse = Awaited>; -export const GET = withEmailProvider(async (request) => { +export const GET = withEmailProvider("messages", async (request) => { const { emailProvider } = request; const { emailAccountId, email } = request.auth; @@ -26,6 +23,7 @@ export const GET = withEmailProvider(async (request) => { pageToken: r.pageToken, emailProvider, email, + logger: request.logger, }); return NextResponse.json(result); @@ -37,12 +35,14 @@ async function getMessages({ emailAccountId, emailProvider, email, + logger, }: { query?: string | null; pageToken?: string | null; emailAccountId: string; emailProvider: EmailProvider; email: string; + logger: any; }) { try { const { messages, nextPageToken } = diff --git a/apps/web/app/api/organizations/[organizationId]/executed-rules-count/route.ts b/apps/web/app/api/organizations/[organizationId]/executed-rules-count/route.ts index 2905c1ea6d..b9de53ddd1 100644 --- a/apps/web/app/api/organizations/[organizationId]/executed-rules-count/route.ts +++ b/apps/web/app/api/organizations/[organizationId]/executed-rules-count/route.ts @@ -7,16 +7,19 @@ export type GetExecutedRulesCountResponse = Awaited< ReturnType >; -export const GET = withAuth(async (request, { params }) => { - const { userId } = request.auth; - const { organizationId } = await params; +export const GET = withAuth( + "organizations/executed-rules-count", + async (request, { params }) => { + const { userId } = request.auth; + const { organizationId } = await params; - await fetchAndCheckIsAdmin({ organizationId, userId }); + await fetchAndCheckIsAdmin({ organizationId, userId }); - const result = await getExecutedRulesCount({ organizationId }); + const result = await getExecutedRulesCount({ organizationId }); - return NextResponse.json(result); -}); + return NextResponse.json(result); + }, +); async function getExecutedRulesCount({ organizationId, diff --git a/apps/web/app/api/organizations/[organizationId]/members/route.ts b/apps/web/app/api/organizations/[organizationId]/members/route.ts index 5a4c8c638b..7afd4b1d66 100644 --- a/apps/web/app/api/organizations/[organizationId]/members/route.ts +++ b/apps/web/app/api/organizations/[organizationId]/members/route.ts @@ -7,23 +7,26 @@ export type OrganizationMembersResponse = Awaited< ReturnType >; -export const GET = withAuth(async (request, { params }) => { - const { userId } = request.auth; - const { organizationId } = await params; - - if (!organizationId) { - return NextResponse.json( - { error: "Organization ID is required" }, - { status: 400 }, - ); - } - - await fetchAndCheckIsAdmin({ organizationId, userId }); - - const result = await getOrganizationMembers({ organizationId }); - - return NextResponse.json(result); -}); +export const GET = withAuth( + "organizations/members", + async (request, { params }) => { + const { userId } = request.auth; + const { organizationId } = await params; + + if (!organizationId) { + return NextResponse.json( + { error: "Organization ID is required" }, + { status: 400 }, + ); + } + + await fetchAndCheckIsAdmin({ organizationId, userId }); + + const result = await getOrganizationMembers({ organizationId }); + + return NextResponse.json(result); + }, +); async function getOrganizationMembers({ organizationId, diff --git a/apps/web/app/api/outlook/linking/auth-url/route.ts b/apps/web/app/api/outlook/linking/auth-url/route.ts index 54a4bb2e34..b4a4b890c8 100644 --- a/apps/web/app/api/outlook/linking/auth-url/route.ts +++ b/apps/web/app/api/outlook/linking/auth-url/route.ts @@ -18,7 +18,7 @@ const getAuthUrl = ({ userId, action }: { userId: string; action: string }) => { return { url, state }; }; -export const GET = withAuth(async (request) => { +export const GET = withAuth("outlook/linking/auth-url", async (request) => { const userId = request.auth.userId; const url = new URL(request.url); const action = url.searchParams.get("action") || "merge"; diff --git a/apps/web/app/api/outlook/watch/route.ts b/apps/web/app/api/outlook/watch/route.ts index 7675e580eb..a8910e33fa 100644 --- a/apps/web/app/api/outlook/watch/route.ts +++ b/apps/web/app/api/outlook/watch/route.ts @@ -1,14 +1,11 @@ import { NextResponse } from "next/server"; import { withAuth } from "@/utils/middleware"; -import { createScopedLogger } from "@/utils/logger"; import prisma from "@/utils/prisma"; import { createManagedOutlookSubscription } from "@/utils/outlook/subscription-manager"; export const dynamic = "force-dynamic"; -const logger = createScopedLogger("api/outlook/watch"); - -export const GET = withAuth(async (request) => { +export const GET = withAuth("outlook/watch", async (request) => { const userId = request.auth.userId; const results = []; @@ -45,7 +42,7 @@ export const GET = withAuth(async (request) => { }); if (!account?.account.access_token || !account?.account.refresh_token) { - logger.warn("Missing tokens for account", { emailAccountId }); + request.logger.warn("Missing tokens for account", { emailAccountId }); results.push({ emailAccountId, status: "error", @@ -64,7 +61,9 @@ export const GET = withAuth(async (request) => { expirationDate, }); } else { - logger.error("Error watching inbox for account", { emailAccountId }); + request.logger.error("Error watching inbox for account", { + emailAccountId, + }); results.push({ emailAccountId, status: "error", @@ -72,7 +71,7 @@ export const GET = withAuth(async (request) => { }); } } catch (error) { - logger.error("Exception while watching inbox for account", { + request.logger.error("Exception while watching inbox for account", { emailAccountId, error, }); diff --git a/apps/web/app/api/referrals/code/route.ts b/apps/web/app/api/referrals/code/route.ts index 6a8c914b82..483b16ad63 100644 --- a/apps/web/app/api/referrals/code/route.ts +++ b/apps/web/app/api/referrals/code/route.ts @@ -6,7 +6,7 @@ export type GetReferralCodeResponse = Awaited< ReturnType >; -export const GET = withAuth(async (request) => { +export const GET = withAuth("referrals/code", async (request) => { const userId = request.auth.userId; const result = await getOrCreateReferralCode(userId); return NextResponse.json(result); diff --git a/apps/web/app/api/referrals/stats/route.ts b/apps/web/app/api/referrals/stats/route.ts index 24147d30e7..5bc085e0aa 100644 --- a/apps/web/app/api/referrals/stats/route.ts +++ b/apps/web/app/api/referrals/stats/route.ts @@ -28,7 +28,7 @@ async function getReferralStats(userId: string) { return { stats }; } -export const GET = withAuth(async (request) => { +export const GET = withAuth("referrals/stats", async (request) => { const userId = request.auth.userId; const result = await getReferralStats(userId); return NextResponse.json(result); diff --git a/apps/web/app/api/resend/digest/route.ts b/apps/web/app/api/resend/digest/route.ts index ccf1088904..bbb8a1c3ed 100644 --- a/apps/web/app/api/resend/digest/route.ts +++ b/apps/web/app/api/resend/digest/route.ts @@ -29,12 +29,11 @@ type SendEmailResult = { message: string; }; -export const GET = withEmailAccount(async (request) => { +export const GET = withEmailAccount("resend/digest", async (request) => { // send to self const emailAccountId = request.auth.emailAccountId; - const logger = createScopedLogger("resend/digest").with({ - emailAccountId, + const logger = request.logger.with({ force: true, }); diff --git a/apps/web/app/api/resend/summary/route.ts b/apps/web/app/api/resend/summary/route.ts index 1b271da6e9..2de703cbbc 100644 --- a/apps/web/app/api/resend/summary/route.ts +++ b/apps/web/app/api/resend/summary/route.ts @@ -251,12 +251,11 @@ async function sendEmail({ return { success: true }; } -export const GET = withEmailAccount(async (request) => { +export const GET = withEmailAccount("resend/summary", async (request) => { // send to self - const logger = createScopedLogger("resend/summary"); const emailAccountId = request.auth.emailAccountId; - logger.info("Sending summary email to user GET", { emailAccountId }); + request.logger.info("Sending summary email to user GET", { emailAccountId }); const result = await sendEmail({ emailAccountId, force: true }); diff --git a/apps/web/app/api/sso/signin/route.ts b/apps/web/app/api/sso/signin/route.ts index 9dff6f4975..742527e50f 100644 --- a/apps/web/app/api/sso/signin/route.ts +++ b/apps/web/app/api/sso/signin/route.ts @@ -2,7 +2,6 @@ import { z } from "zod"; import { NextResponse } from "next/server"; import { betterAuthConfig } from "@/utils/auth"; import { SafeError } from "@/utils/error"; -import { createScopedLogger } from "@/utils/logger"; import { withError } from "@/utils/middleware"; import prisma from "@/utils/prisma"; @@ -16,16 +15,14 @@ export type GetSsoSignInResponse = { providerId: string; }; -const logger = createScopedLogger("api/sso/signin"); - -export const GET = withError(async (request) => { +export const GET = withError("sso/signin", async (request) => { const { searchParams } = new URL(request.url); const { email, organizationSlug } = getSsoSignInSchema.parse({ email: searchParams.get("email"), organizationSlug: searchParams.get("organizationSlug"), }); - logger.info("SSO sign-in requested", { email, organizationSlug }); + request.logger.info("SSO sign-in requested", { email, organizationSlug }); const provider = await prisma.ssoProvider.findFirst({ where: { @@ -39,7 +36,7 @@ export const GET = withError(async (request) => { }); if (!provider) { - logger.error("No SSO provider found for sign-in", { + request.logger.error("No SSO provider found for sign-in", { email, organizationSlug, }); diff --git a/apps/web/app/api/stripe/success/route.ts b/apps/web/app/api/stripe/success/route.ts index ddcbd6241c..df9a4550b2 100644 --- a/apps/web/app/api/stripe/success/route.ts +++ b/apps/web/app/api/stripe/success/route.ts @@ -5,7 +5,7 @@ import { withAuth } from "@/utils/middleware"; import prisma from "@/utils/prisma"; import { trackStripeCheckoutCompleted } from "@/utils/posthog"; -export const GET = withAuth(async (request) => { +export const GET = withAuth("stripe/success", async (request) => { const userId = request.auth.userId; after(async () => { diff --git a/apps/web/app/api/threads/[id]/route.ts b/apps/web/app/api/threads/[id]/route.ts index 4e4099e649..ef6e6e4069 100644 --- a/apps/web/app/api/threads/[id]/route.ts +++ b/apps/web/app/api/threads/[id]/route.ts @@ -1,15 +1,12 @@ import { z } from "zod"; import { NextResponse } from "next/server"; import { withEmailProvider } from "@/utils/middleware"; -import { createScopedLogger } from "@/utils/logger"; import type { EmailProvider } from "@/utils/email/types"; const threadQuery = z.object({ id: z.string() }); export type ThreadQuery = z.infer; export type ThreadResponse = Awaited>; -const logger = createScopedLogger("api/threads/[id]"); - async function getThread( id: string, includeDrafts: boolean, @@ -28,28 +25,31 @@ export const dynamic = "force-dynamic"; export const maxDuration = 30; -export const GET = withEmailProvider(async (request, context) => { - const { emailProvider } = request; - const { emailAccountId } = request.auth; - - const params = await context.params; - const { id } = threadQuery.parse(params); - - const { searchParams } = new URL(request.url); - const includeDrafts = searchParams.get("includeDrafts") === "true"; - - try { - const thread = await getThread(id, includeDrafts, emailProvider); - return NextResponse.json(thread); - } catch (error) { - logger.error("Error fetching thread", { - error, - emailAccountId, - threadId: id, - }); - return NextResponse.json( - { error: "Failed to fetch thread" }, - { status: 500 }, - ); - } -}); +export const GET = withEmailProvider( + "threads/detail", + async (request, context) => { + const { emailProvider } = request; + const { emailAccountId } = request.auth; + + const params = await context.params; + const { id } = threadQuery.parse(params); + + const { searchParams } = new URL(request.url); + const includeDrafts = searchParams.get("includeDrafts") === "true"; + + try { + const thread = await getThread(id, includeDrafts, emailProvider); + return NextResponse.json(thread); + } catch (error) { + request.logger.error("Error fetching thread", { + error, + emailAccountId, + threadId: id, + }); + return NextResponse.json( + { error: "Failed to fetch thread" }, + { status: 500 }, + ); + } + }, +); diff --git a/apps/web/app/api/threads/basic/route.ts b/apps/web/app/api/threads/basic/route.ts index 9299d2e716..8fc3f05df7 100644 --- a/apps/web/app/api/threads/basic/route.ts +++ b/apps/web/app/api/threads/basic/route.ts @@ -1,10 +1,7 @@ import { NextResponse } from "next/server"; import { withEmailProvider } from "@/utils/middleware"; -import { createScopedLogger } from "@/utils/logger"; import type { ThreadsResponse } from "@/app/api/threads/route"; -const logger = createScopedLogger("api/threads/basic"); - export type GetThreadsResponse = { threads: ThreadsResponse["threads"]; }; @@ -13,7 +10,7 @@ export const dynamic = "force-dynamic"; export const maxDuration = 30; -export const GET = withEmailProvider(async (request) => { +export const GET = withEmailProvider("threads/basic", async (request) => { const { emailProvider } = request; const { emailAccountId } = request.auth; @@ -31,7 +28,10 @@ export const GET = withEmailProvider(async (request) => { return NextResponse.json({ threads }); } catch (error) { - logger.error("Error fetching basic threads", { error, emailAccountId }); + request.logger.error("Error fetching basic threads", { + error, + emailAccountId, + }); return NextResponse.json( { error: "Failed to fetch threads" }, { status: 500 }, diff --git a/apps/web/app/api/threads/batch/route.ts b/apps/web/app/api/threads/batch/route.ts index 94e30d8dbe..3872ec2c9f 100644 --- a/apps/web/app/api/threads/batch/route.ts +++ b/apps/web/app/api/threads/batch/route.ts @@ -1,10 +1,7 @@ import { NextResponse } from "next/server"; import { withEmailProvider } from "@/utils/middleware"; -import { createScopedLogger } from "@/utils/logger"; import type { ThreadsResponse } from "@/app/api/threads/route"; -const logger = createScopedLogger("api/threads/batch"); - export type ThreadsBatchResponse = { threads: ThreadsResponse["threads"]; }; @@ -13,7 +10,7 @@ export const dynamic = "force-dynamic"; export const maxDuration = 30; -export const GET = withEmailProvider(async (request) => { +export const GET = withEmailProvider("threads/batch", async (request) => { const { emailProvider } = request; const { emailAccountId } = request.auth; @@ -40,7 +37,7 @@ export const GET = withEmailProvider(async (request) => { try { return await emailProvider.getThread(threadId); } catch (error) { - logger.error("Error fetching thread", { error, threadId }); + request.logger.error("Error fetching thread", { error, threadId }); return null; } }), @@ -52,7 +49,10 @@ export const GET = withEmailProvider(async (request) => { return NextResponse.json({ threads: validThreads }); } catch (error) { - logger.error("Error fetching batch threads", { error, emailAccountId }); + request.logger.error("Error fetching batch threads", { + error, + emailAccountId, + }); return NextResponse.json( { error: "Failed to fetch threads" }, { status: 500 }, diff --git a/apps/web/app/api/threads/route.ts b/apps/web/app/api/threads/route.ts index 1a56016c2b..db10b258fd 100644 --- a/apps/web/app/api/threads/route.ts +++ b/apps/web/app/api/threads/route.ts @@ -3,17 +3,14 @@ import { withEmailProvider } from "@/utils/middleware"; import { type ThreadsQuery, threadsQuery } from "@/app/api/threads/validation"; import { isDefined } from "@/utils/types"; import prisma from "@/utils/prisma"; -import { createScopedLogger } from "@/utils/logger"; import { isIgnoredSender } from "@/utils/filter-ignored-senders"; import type { EmailProvider } from "@/utils/email/types"; -const logger = createScopedLogger("api/threads"); - export const dynamic = "force-dynamic"; export const maxDuration = 30; -export const GET = withEmailProvider(async (request) => { +export const GET = withEmailProvider("threads", async (request) => { const { emailProvider } = request; const { emailAccountId } = request.auth; @@ -48,7 +45,7 @@ export const GET = withEmailProvider(async (request) => { }); return NextResponse.json(threads); } catch (error) { - logger.error("Error fetching threads", { error, emailAccountId }); + request.logger.error("Error fetching threads", { error, emailAccountId }); return NextResponse.json( { error: "Failed to fetch threads" }, { status: 500 }, diff --git a/apps/web/app/api/unsubscribe/route.ts b/apps/web/app/api/unsubscribe/route.ts index 4d048e64e2..ebc8be6b16 100644 --- a/apps/web/app/api/unsubscribe/route.ts +++ b/apps/web/app/api/unsubscribe/route.ts @@ -1,20 +1,17 @@ import { NextResponse } from "next/server"; -import { withError } from "@/utils/middleware"; +import { withError, type RequestWithLogger } from "@/utils/middleware"; import prisma from "@/utils/prisma"; -import { createScopedLogger } from "@/utils/logger"; import { Frequency } from "@prisma/client"; -const logger = createScopedLogger("unsubscribe"); - -export const GET = withError(async (request) => { +export const GET = withError("unsubscribe", async (request) => { return unsubscribe(request); }); export const POST = withError(async (request) => { - return unsubscribe(request); + return unsubscribe(request as RequestWithLogger); }); -async function unsubscribe(request: Request) { +async function unsubscribe(request: RequestWithLogger) { const url = new URL(request.url); const encodedToken = url.searchParams.get("token"); @@ -63,7 +60,7 @@ async function unsubscribe(request: Request) { ]); if (userUpdate.status === "rejected") { - logger.error("Error updating user preferences", { + request.logger.error("Error updating user preferences", { email: emailToken.emailAccount.email, error: userUpdate.reason, }); @@ -78,14 +75,14 @@ async function unsubscribe(request: Request) { } if (tokenDelete.status === "rejected") { - logger.error("Error deleting token", { + request.logger.error("Error deleting token", { email: emailToken.emailAccountId, tokenId: emailToken.id, error: tokenDelete.reason, }); } - logger.info("User unsubscribed from emails", { + request.logger.info("User unsubscribed from emails", { email: emailToken.emailAccountId, }); diff --git a/apps/web/app/api/user/api-keys/route.ts b/apps/web/app/api/user/api-keys/route.ts index e7a0cbf8d9..7839403744 100644 --- a/apps/web/app/api/user/api-keys/route.ts +++ b/apps/web/app/api/user/api-keys/route.ts @@ -17,7 +17,7 @@ async function getApiKeys({ userId }: { userId: string }) { return { apiKeys }; } -export const GET = withAuth(async (request) => { +export const GET = withAuth("user/api-keys", async (request) => { const userId = request.auth.userId; const apiKeys = await getApiKeys({ userId }); diff --git a/apps/web/app/api/user/calendars/route.ts b/apps/web/app/api/user/calendars/route.ts index 525d56253b..fc976ee794 100644 --- a/apps/web/app/api/user/calendars/route.ts +++ b/apps/web/app/api/user/calendars/route.ts @@ -4,7 +4,7 @@ import { withEmailAccount } from "@/utils/middleware"; export type GetCalendarsResponse = Awaited>; -export const GET = withEmailAccount(async (request) => { +export const GET = withEmailAccount("user/calendars", async (request) => { const { emailAccountId } = request.auth; const result = await getData({ emailAccountId }); diff --git a/apps/web/app/api/user/categories/route.ts b/apps/web/app/api/user/categories/route.ts index 9ad689d633..8aa0a21c17 100644 --- a/apps/web/app/api/user/categories/route.ts +++ b/apps/web/app/api/user/categories/route.ts @@ -9,7 +9,7 @@ async function getCategories({ emailAccountId }: { emailAccountId: string }) { return { result }; } -export const GET = withEmailAccount(async (request) => { +export const GET = withEmailAccount("user/categories", async (request) => { const emailAccountId = request.auth.emailAccountId; const result = await getCategories({ emailAccountId }); return NextResponse.json(result); diff --git a/apps/web/app/api/user/categorization-preferences/route.ts b/apps/web/app/api/user/categorization-preferences/route.ts index 2e9fe03a3b..d804d9c838 100644 --- a/apps/web/app/api/user/categorization-preferences/route.ts +++ b/apps/web/app/api/user/categorization-preferences/route.ts @@ -30,11 +30,14 @@ export type GetCategorizationPreferencesResponse = Awaited< ReturnType >; -export const GET = withEmailProvider(async (request) => { - const emailAccountId = request.auth.emailAccountId; - const result = await getUserPreferences({ emailAccountId }); - return NextResponse.json(result); -}); +export const GET = withEmailProvider( + "user/categorization-preferences", + async (request) => { + const emailAccountId = request.auth.emailAccountId; + const result = await getUserPreferences({ emailAccountId }); + return NextResponse.json(result); + }, +); async function getUserPreferences({ emailAccountId, diff --git a/apps/web/app/api/user/categorize/senders/progress/route.ts b/apps/web/app/api/user/categorize/senders/progress/route.ts index 7d6665e992..9c61172c8a 100644 --- a/apps/web/app/api/user/categorize/senders/progress/route.ts +++ b/apps/web/app/api/user/categorize/senders/progress/route.ts @@ -15,8 +15,11 @@ async function getCategorizeProgress({ return progress; } -export const GET = withEmailAccount(async (request) => { - const emailAccountId = request.auth.emailAccountId; - const result = await getCategorizeProgress({ emailAccountId }); - return NextResponse.json(result); -}); +export const GET = withEmailAccount( + "user/categorize/senders/progress", + async (request) => { + const emailAccountId = request.auth.emailAccountId; + const result = await getCategorizeProgress({ emailAccountId }); + return NextResponse.json(result); + }, +); diff --git a/apps/web/app/api/user/categorize/senders/uncategorized/route.ts b/apps/web/app/api/user/categorize/senders/uncategorized/route.ts index 92e4250edb..29d01f34e5 100644 --- a/apps/web/app/api/user/categorize/senders/uncategorized/route.ts +++ b/apps/web/app/api/user/categorize/senders/uncategorized/route.ts @@ -7,16 +7,19 @@ export type UncategorizedSendersResponse = { nextOffset?: number; }; -export const GET = withEmailAccount(async (request) => { - const emailAccountId = request.auth.emailAccountId; +export const GET = withEmailAccount( + "user/categorize/senders/uncategorized", + async (request) => { + const emailAccountId = request.auth.emailAccountId; - const url = new URL(request.url); - const offset = Number.parseInt(url.searchParams.get("offset") || "0"); + const url = new URL(request.url); + const offset = Number.parseInt(url.searchParams.get("offset") || "0"); - const result = await getUncategorizedSenders({ - emailAccountId, - offset, - }); + const result = await getUncategorizedSenders({ + emailAccountId, + offset, + }); - return NextResponse.json(result); -}); + return NextResponse.json(result); + }, +); diff --git a/apps/web/app/api/user/cold-email/route.ts b/apps/web/app/api/user/cold-email/route.ts index 0871d04f1d..cb274cb4fc 100644 --- a/apps/web/app/api/user/cold-email/route.ts +++ b/apps/web/app/api/user/cold-email/route.ts @@ -41,7 +41,7 @@ async function getColdEmails( return { coldEmails, totalPages: Math.ceil(count / LIMIT) }; } -export const GET = withEmailAccount(async (request) => { +export const GET = withEmailAccount("user/cold-email", async (request) => { const emailAccountId = request.auth.emailAccountId; const url = new URL(request.url); diff --git a/apps/web/app/api/user/digest-schedule/route.ts b/apps/web/app/api/user/digest-schedule/route.ts index ddcd64d24e..d9e3cdca70 100644 --- a/apps/web/app/api/user/digest-schedule/route.ts +++ b/apps/web/app/api/user/digest-schedule/route.ts @@ -6,7 +6,7 @@ export type GetDigestScheduleResponse = Awaited< ReturnType >; -export const GET = withEmailAccount(async (request) => { +export const GET = withEmailAccount("user/digest-schedule", async (request) => { const emailAccountId = request.auth.emailAccountId; const result = await getDigestSchedule({ emailAccountId }); diff --git a/apps/web/app/api/user/digest-settings/route.ts b/apps/web/app/api/user/digest-settings/route.ts index 1cbd401d66..91df70c78f 100644 --- a/apps/web/app/api/user/digest-settings/route.ts +++ b/apps/web/app/api/user/digest-settings/route.ts @@ -21,7 +21,7 @@ export type GetDigestSettingsResponse = Awaited< ReturnType >; -export const GET = withEmailAccount(async (request) => { +export const GET = withEmailAccount("user/digest-settings", async (request) => { const emailAccountId = request.auth.emailAccountId; const result = await getDigestSettings({ emailAccountId }); diff --git a/apps/web/app/api/user/draft-actions/route.ts b/apps/web/app/api/user/draft-actions/route.ts index 39ea218c87..7a7a480408 100644 --- a/apps/web/app/api/user/draft-actions/route.ts +++ b/apps/web/app/api/user/draft-actions/route.ts @@ -5,7 +5,7 @@ import { ActionType } from "@prisma/client"; export type DraftActionsResponse = Awaited>; -export const GET = withEmailAccount(async (request) => { +export const GET = withEmailAccount("user/draft-actions", async (request) => { const emailAccountId = request.auth.emailAccountId; const response = await getData({ emailAccountId }); diff --git a/apps/web/app/api/user/email-accounts/route.ts b/apps/web/app/api/user/email-accounts/route.ts index 1cd89712e1..074d92f8a3 100644 --- a/apps/web/app/api/user/email-accounts/route.ts +++ b/apps/web/app/api/user/email-accounts/route.ts @@ -50,7 +50,7 @@ async function getEmailAccounts({ userId }: { userId: string }) { return { emailAccounts: accountsWithNames }; } -export const GET = withAuth(async (request) => { +export const GET = withAuth("user/email-accounts", async (request) => { const userId = request.auth.userId; const result = await getEmailAccounts({ userId }); return NextResponse.json(result); diff --git a/apps/web/app/api/user/executed-rules/batch/route.ts b/apps/web/app/api/user/executed-rules/batch/route.ts index b5ef82bb18..4d7ffd16cc 100644 --- a/apps/web/app/api/user/executed-rules/batch/route.ts +++ b/apps/web/app/api/user/executed-rules/batch/route.ts @@ -45,21 +45,24 @@ async function getData({ return { rulesMap }; } -export const GET = withEmailAccount(async (request) => { - const emailAccountId = request.auth.emailAccountId; +export const GET = withEmailAccount( + "user/executed-rules/batch", + async (request) => { + const emailAccountId = request.auth.emailAccountId; - const { searchParams } = new URL(request.url); + const { searchParams } = new URL(request.url); - const parsed = batchRequestSchema.safeParse({ - messageIds: searchParams.get("messageIds")?.split(",") || [], - }); - if (!parsed.success) { - return NextResponse.json({ error: "Invalid request" }, { status: 400 }); - } + const parsed = batchRequestSchema.safeParse({ + messageIds: searchParams.get("messageIds")?.split(",") || [], + }); + if (!parsed.success) { + return NextResponse.json({ error: "Invalid request" }, { status: 400 }); + } - const result = await getData({ - emailAccountId, - messageIds: parsed.data.messageIds, - }); - return NextResponse.json(result); -}); + const result = await getData({ + emailAccountId, + messageIds: parsed.data.messageIds, + }); + return NextResponse.json(result); + }, +); diff --git a/apps/web/app/api/user/executed-rules/history/route.ts b/apps/web/app/api/user/executed-rules/history/route.ts index db08c73c08..4a6281c2c7 100644 --- a/apps/web/app/api/user/executed-rules/history/route.ts +++ b/apps/web/app/api/user/executed-rules/history/route.ts @@ -4,8 +4,8 @@ import { withEmailProvider } from "@/utils/middleware"; import { isDefined } from "@/utils/types"; import prisma from "@/utils/prisma"; import { ExecutedRuleStatus, type Prisma } from "@prisma/client"; -import { createScopedLogger } from "@/utils/logger"; import type { EmailProvider } from "@/utils/email/types"; +import type { Logger } from "@/utils/logger"; const LIMIT = 50; @@ -15,39 +15,40 @@ export type GetExecutedRulesResponse = Awaited< ReturnType >; -export const GET = withEmailProvider(async (request) => { - const emailAccountId = request.auth.emailAccountId; +export const GET = withEmailProvider( + "user/executed-rules/history", + async (request) => { + const emailAccountId = request.auth.emailAccountId; - const url = new URL(request.url); - const page = Number.parseInt(url.searchParams.get("page") || "1"); - const ruleId = url.searchParams.get("ruleId") || "all"; + const url = new URL(request.url); + const page = Number.parseInt(url.searchParams.get("page") || "1"); + const ruleId = url.searchParams.get("ruleId") || "all"; - const result = await getExecutedRules({ - page, - ruleId, - emailAccountId, - emailProvider: request.emailProvider, - }); + const result = await getExecutedRules({ + page, + ruleId, + emailAccountId, + emailProvider: request.emailProvider, + logger: request.logger, + }); - return NextResponse.json(result); -}); + return NextResponse.json(result); + }, +); async function getExecutedRules({ page, ruleId, emailAccountId, emailProvider, + logger, }: { page: number; ruleId?: string; emailAccountId: string; emailProvider: EmailProvider; + logger: Logger; }) { - const logger = createScopedLogger("api/user/executed-rules/history").with({ - emailAccountId, - ruleId, - }); - const where: Prisma.ExecutedRuleWhereInput = { emailAccountId, status: diff --git a/apps/web/app/api/user/folders/route.ts b/apps/web/app/api/user/folders/route.ts index 5c7b7fefd8..f5e4463d1d 100644 --- a/apps/web/app/api/user/folders/route.ts +++ b/apps/web/app/api/user/folders/route.ts @@ -5,7 +5,7 @@ import type { EmailProvider } from "@/utils/email/types"; export type GetFoldersResponse = Awaited>; -export const GET = withEmailProvider(async (request) => { +export const GET = withEmailProvider("user/folders", async (request) => { const emailProvider = request.emailProvider; if (!isMicrosoftProvider(emailProvider.name)) { diff --git a/apps/web/app/api/user/group/[groupId]/items/route.ts b/apps/web/app/api/user/group/[groupId]/items/route.ts index affbd25690..fcd9ccca95 100644 --- a/apps/web/app/api/user/group/[groupId]/items/route.ts +++ b/apps/web/app/api/user/group/[groupId]/items/route.ts @@ -23,13 +23,16 @@ async function getGroupItems({ return { group }; } -export const GET = withEmailAccount(async (request, { params }) => { - const emailAccountId = request.auth.emailAccountId; +export const GET = withEmailAccount( + "user/group/items", + async (request, { params }) => { + const emailAccountId = request.auth.emailAccountId; - const { groupId } = await params; - if (!groupId) return NextResponse.json({ error: "Group id required" }); + const { groupId } = await params; + if (!groupId) return NextResponse.json({ error: "Group id required" }); - const result = await getGroupItems({ emailAccountId, groupId }); + const result = await getGroupItems({ emailAccountId, groupId }); - return NextResponse.json(result); -}); + return NextResponse.json(result); + }, +); diff --git a/apps/web/app/api/user/group/[groupId]/messages/route.ts b/apps/web/app/api/user/group/[groupId]/messages/route.ts index dda0a98660..efc7ad7f45 100644 --- a/apps/web/app/api/user/group/[groupId]/messages/route.ts +++ b/apps/web/app/api/user/group/[groupId]/messages/route.ts @@ -2,20 +2,23 @@ import { NextResponse } from "next/server"; import { withEmailProvider } from "@/utils/middleware"; import { getGroupEmails } from "@/app/api/user/group/[groupId]/messages/controller"; -export const GET = withEmailProvider(async (request, { params }) => { - const emailAccountId = request.auth.emailAccountId; +export const GET = withEmailProvider( + "user/group/messages", + async (request, { params }) => { + const emailAccountId = request.auth.emailAccountId; - const { groupId } = await params; - if (!groupId) return NextResponse.json({ error: "Missing group id" }); + const { groupId } = await params; + if (!groupId) return NextResponse.json({ error: "Missing group id" }); - const { messages } = await getGroupEmails({ - provider: request.emailProvider.name, - groupId, - emailAccountId, - from: undefined, - to: undefined, - pageToken: "", - }); + const { messages } = await getGroupEmails({ + provider: request.emailProvider.name, + groupId, + emailAccountId, + from: undefined, + to: undefined, + pageToken: "", + }); - return NextResponse.json({ messages }); -}); + return NextResponse.json({ messages }); + }, +); diff --git a/apps/web/app/api/user/group/[groupId]/rules/route.ts b/apps/web/app/api/user/group/[groupId]/rules/route.ts index 12ff7d8c80..4567ba3431 100644 --- a/apps/web/app/api/user/group/[groupId]/rules/route.ts +++ b/apps/web/app/api/user/group/[groupId]/rules/route.ts @@ -28,13 +28,16 @@ async function getGroupRules({ return { rule: groupWithRules.rule }; } -export const GET = withEmailAccount(async (request, { params }) => { - const emailAccountId = request.auth.emailAccountId; +export const GET = withEmailAccount( + "user/group/rules", + async (request, { params }) => { + const emailAccountId = request.auth.emailAccountId; - const { groupId } = await params; - if (!groupId) return NextResponse.json({ error: "Group id required" }); + const { groupId } = await params; + if (!groupId) return NextResponse.json({ error: "Group id required" }); - const result = await getGroupRules({ emailAccountId, groupId }); + const result = await getGroupRules({ emailAccountId, groupId }); - return NextResponse.json(result); -}); + return NextResponse.json(result); + }, +); diff --git a/apps/web/app/api/user/group/route.ts b/apps/web/app/api/user/group/route.ts index 165fccba0b..a0a98eee16 100644 --- a/apps/web/app/api/user/group/route.ts +++ b/apps/web/app/api/user/group/route.ts @@ -17,7 +17,7 @@ async function getGroups({ emailAccountId }: { emailAccountId: string }) { return { groups }; } -export const GET = withEmailAccount(async (request) => { +export const GET = withEmailAccount("user/group", async (request) => { const emailAccountId = request.auth.emailAccountId; const result = await getGroups({ emailAccountId }); return NextResponse.json(result); diff --git a/apps/web/app/api/user/labels/route.ts b/apps/web/app/api/user/labels/route.ts index acc23c7b58..b1b2bd8908 100644 --- a/apps/web/app/api/user/labels/route.ts +++ b/apps/web/app/api/user/labels/route.ts @@ -10,7 +10,7 @@ async function getLabels(options: { emailAccountId: string }) { }); } -export const GET = withEmailAccount(async (request) => { +export const GET = withEmailAccount("user/labels", async (request) => { const emailAccountId = request.auth.emailAccountId; const labels = await getLabels({ emailAccountId }); diff --git a/apps/web/app/api/user/me/route.ts b/apps/web/app/api/user/me/route.ts index 98d7c0f110..68d3326201 100644 --- a/apps/web/app/api/user/me/route.ts +++ b/apps/web/app/api/user/me/route.ts @@ -65,7 +65,7 @@ async function getUser({ userId }: { userId: string }) { } // Intentionally not using withAuth because we want to return null if the user is not authenticated -export const GET = withError(async () => { +export const GET = withError("user/me", async () => { const session = await auth(); const userId = session?.user.id; if (!userId) return NextResponse.json(null); diff --git a/apps/web/app/api/user/no-reply/route.ts b/apps/web/app/api/user/no-reply/route.ts index c5e8ea0261..3d8edaa672 100644 --- a/apps/web/app/api/user/no-reply/route.ts +++ b/apps/web/app/api/user/no-reply/route.ts @@ -45,7 +45,7 @@ async function getNoReply({ return sentEmailsWithThreads; } -export const GET = withEmailProvider(async (request) => { +export const GET = withEmailProvider("user/no-reply", async (request) => { const emailAccountId = request.auth.emailAccountId; const userEmail = request.auth.email; diff --git a/apps/web/app/api/user/persona/route.ts b/apps/web/app/api/user/persona/route.ts index 466537c4e2..5017df6f47 100644 --- a/apps/web/app/api/user/persona/route.ts +++ b/apps/web/app/api/user/persona/route.ts @@ -5,7 +5,7 @@ import type { PersonaAnalysis } from "@/utils/ai/knowledge/persona"; export type GetPersonaResponse = Awaited>; -export const GET = withEmailAccount(async (request) => { +export const GET = withEmailAccount("user/persona", async (request) => { const { emailAccountId } = request.auth; const result = await getData({ emailAccountId }); diff --git a/apps/web/app/api/user/rules/[id]/example/route.ts b/apps/web/app/api/user/rules/[id]/example/route.ts index 5c3d03dbb1..c556b62e6f 100644 --- a/apps/web/app/api/user/rules/[id]/example/route.ts +++ b/apps/web/app/api/user/rules/[id]/example/route.ts @@ -33,14 +33,17 @@ async function getExamples({ return exampleMessages; } -export const GET = withEmailProvider(async (request, { params }) => { - const emailAccountId = request.auth.emailAccountId; - const provider = request.emailProvider.name; +export const GET = withEmailProvider( + "user/rules/example", + async (request, { params }) => { + const emailAccountId = request.auth.emailAccountId; + const provider = request.emailProvider.name; - const { id } = await params; - if (!id) return NextResponse.json({ error: "Missing rule id" }); + const { id } = await params; + if (!id) return NextResponse.json({ error: "Missing rule id" }); - const result = await getExamples({ ruleId: id, emailAccountId, provider }); + const result = await getExamples({ ruleId: id, emailAccountId, provider }); - return NextResponse.json(result); -}); + return NextResponse.json(result); + }, +); diff --git a/apps/web/app/api/user/rules/[id]/route.ts b/apps/web/app/api/user/rules/[id]/route.ts index 4b336ba1a6..873232aac0 100644 --- a/apps/web/app/api/user/rules/[id]/route.ts +++ b/apps/web/app/api/user/rules/[id]/route.ts @@ -47,13 +47,16 @@ async function getRule({ return { rule: ruleWithActions }; } -export const GET = withEmailAccount(async (request, { params }) => { - const emailAccountId = request.auth.emailAccountId; +export const GET = withEmailAccount( + "user/rules/detail", + async (request, { params }) => { + const emailAccountId = request.auth.emailAccountId; - const { id } = await params; - if (!id) return NextResponse.json({ error: "Missing rule id" }); + const { id } = await params; + if (!id) return NextResponse.json({ error: "Missing rule id" }); - const result = await getRule({ ruleId: id, emailAccountId }); + const result = await getRule({ ruleId: id, emailAccountId }); - return NextResponse.json(result); -}); + return NextResponse.json(result); + }, +); diff --git a/apps/web/app/api/user/rules/prompt/route.ts b/apps/web/app/api/user/rules/prompt/route.ts index 2a185d21f4..6839b5a7c9 100644 --- a/apps/web/app/api/user/rules/prompt/route.ts +++ b/apps/web/app/api/user/rules/prompt/route.ts @@ -11,7 +11,7 @@ async function getRulesPrompt({ emailAccountId }: { emailAccountId: string }) { }); } -export const GET = withEmailAccount(async (request) => { +export const GET = withEmailAccount("user/rules/prompt", async (request) => { const emailAccountId = request.auth.emailAccountId; const result = await getRulesPrompt({ emailAccountId }); diff --git a/apps/web/app/api/user/rules/route.ts b/apps/web/app/api/user/rules/route.ts index 49c18fb37c..80a67505cc 100644 --- a/apps/web/app/api/user/rules/route.ts +++ b/apps/web/app/api/user/rules/route.ts @@ -15,7 +15,7 @@ async function getRules({ emailAccountId }: { emailAccountId: string }) { }); } -export const GET = withEmailAccount(async (request) => { +export const GET = withEmailAccount("user/rules", async (request) => { const emailAccountId = request.auth.emailAccountId; const result = await getRules({ emailAccountId }); return NextResponse.json(result); diff --git a/apps/web/app/api/user/settings/multi-account/route.ts b/apps/web/app/api/user/settings/multi-account/route.ts index c0b4336dfb..b871425e5d 100644 --- a/apps/web/app/api/user/settings/multi-account/route.ts +++ b/apps/web/app/api/user/settings/multi-account/route.ts @@ -25,7 +25,7 @@ async function getMultiAccountEmails({ userId }: { userId: string }) { }; } -export const GET = withAuth(async (request) => { +export const GET = withAuth("user/settings/multi-account", async (request) => { const userId = request.auth.userId; const result = await getMultiAccountEmails({ userId }); diff --git a/apps/web/app/api/user/setup-progress/route.ts b/apps/web/app/api/user/setup-progress/route.ts index 0561ffbca8..1c45c48327 100644 --- a/apps/web/app/api/user/setup-progress/route.ts +++ b/apps/web/app/api/user/setup-progress/route.ts @@ -8,7 +8,7 @@ export type GetSetupProgressResponse = Awaited< ReturnType >; -export const GET = withEmailAccount(async (request) => { +export const GET = withEmailAccount("user/setup-progress", async (request) => { const { emailAccountId } = request.auth; const result = await getSetupProgress({ emailAccountId }); diff --git a/apps/web/app/api/user/stats/day/route.ts b/apps/web/app/api/user/stats/day/route.ts index f9c22871cb..3fae4e58f1 100644 --- a/apps/web/app/api/user/stats/day/route.ts +++ b/apps/web/app/api/user/stats/day/route.ts @@ -93,7 +93,7 @@ async function getPastSevenDayStats({ return lastSevenDaysCountsArray; } -export const GET = withEmailProvider(async (request) => { +export const GET = withEmailProvider("user/stats/day", async (request) => { const emailAccountId = request.auth.emailAccountId; const provider = request.emailProvider.name; diff --git a/apps/web/app/api/user/stats/newsletters/route.ts b/apps/web/app/api/user/stats/newsletters/route.ts index fcc98a0361..cbd4084d33 100644 --- a/apps/web/app/api/user/stats/newsletters/route.ts +++ b/apps/web/app/api/user/stats/newsletters/route.ts @@ -244,27 +244,30 @@ function getOrderByClause(orderBy: string): string { } } -export const GET = withEmailProvider(async (request) => { - const { emailProvider } = request; - const { emailAccountId } = request.auth; - - const { searchParams } = new URL(request.url); - const params = newsletterStatsQuery.parse({ - limit: searchParams.get("limit"), - fromDate: searchParams.get("fromDate"), - toDate: searchParams.get("toDate"), - orderBy: searchParams.get("orderBy"), - types: searchParams.get("types")?.split(",") || [], - filters: searchParams.get("filters")?.split(",") || [], - includeMissingUnsubscribe: - searchParams.get("includeMissingUnsubscribe") === "true", - }); +export const GET = withEmailProvider( + "user/stats/newsletters", + async (request) => { + const { emailProvider } = request; + const { emailAccountId } = request.auth; + + const { searchParams } = new URL(request.url); + const params = newsletterStatsQuery.parse({ + limit: searchParams.get("limit"), + fromDate: searchParams.get("fromDate"), + toDate: searchParams.get("toDate"), + orderBy: searchParams.get("orderBy"), + types: searchParams.get("types")?.split(",") || [], + filters: searchParams.get("filters")?.split(",") || [], + includeMissingUnsubscribe: + searchParams.get("includeMissingUnsubscribe") === "true", + }); - const result = await getEmailMessages({ - ...params, - emailAccountId, - emailProvider, - }); + const result = await getEmailMessages({ + ...params, + emailAccountId, + emailProvider, + }); - return NextResponse.json(result); -}); + return NextResponse.json(result); + }, +); diff --git a/apps/web/app/api/user/stats/recipients/route.ts b/apps/web/app/api/user/stats/recipients/route.ts index 4f4e6311ce..20bdc00d97 100644 --- a/apps/web/app/api/user/stats/recipients/route.ts +++ b/apps/web/app/api/user/stats/recipients/route.ts @@ -47,18 +47,21 @@ async function getMostSentTo({ }); } -export const GET = withEmailAccount(async (request) => { - const emailAccountId = request.auth.emailAccountId; - const { searchParams } = new URL(request.url); - const query = recipientStatsQuery.parse({ - fromDate: searchParams.get("fromDate"), - toDate: searchParams.get("toDate"), - }); +export const GET = withEmailAccount( + "user/stats/recipients", + async (request) => { + const emailAccountId = request.auth.emailAccountId; + const { searchParams } = new URL(request.url); + const query = recipientStatsQuery.parse({ + fromDate: searchParams.get("fromDate"), + toDate: searchParams.get("toDate"), + }); - const result = await getRecipientStatistics({ - ...query, - emailAccountId, - }); + const result = await getRecipientStatistics({ + ...query, + emailAccountId, + }); - return NextResponse.json(result); -}); + return NextResponse.json(result); + }, +); diff --git a/apps/web/app/api/user/stats/route.ts b/apps/web/app/api/user/stats/route.ts index c03ee97530..4dd8d456a3 100644 --- a/apps/web/app/api/user/stats/route.ts +++ b/apps/web/app/api/user/stats/route.ts @@ -70,7 +70,7 @@ async function getStats({ emailProvider }: { emailProvider: EmailProvider }) { }; } -export const GET = withEmailProvider(async (request) => { +export const GET = withEmailProvider("user/stats", async (request) => { const result = await getStats({ emailProvider: request.emailProvider }); return NextResponse.json(result); diff --git a/apps/web/app/api/user/stats/sender-emails/route.ts b/apps/web/app/api/user/stats/sender-emails/route.ts index 79dae129a5..190d988956 100644 --- a/apps/web/app/api/user/stats/sender-emails/route.ts +++ b/apps/web/app/api/user/stats/sender-emails/route.ts @@ -69,22 +69,25 @@ async function getSenderEmails( }; } -export const GET = withEmailAccount(async (request) => { - const emailAccountId = request.auth.emailAccountId; +export const GET = withEmailAccount( + "user/stats/sender-emails", + async (request) => { + const emailAccountId = request.auth.emailAccountId; - const { searchParams } = new URL(request.url); + const { searchParams } = new URL(request.url); - const query = senderEmailsQuery.parse({ - fromEmail: searchParams.get("fromEmail"), - period: searchParams.get("period") || "week", - fromDate: searchParams.get("fromDate"), - toDate: searchParams.get("toDate"), - }); + const query = senderEmailsQuery.parse({ + fromEmail: searchParams.get("fromEmail"), + period: searchParams.get("period") || "week", + fromDate: searchParams.get("fromDate"), + toDate: searchParams.get("toDate"), + }); - const result = await getSenderEmails({ - ...query, - emailAccountId, - }); + const result = await getSenderEmails({ + ...query, + emailAccountId, + }); - return NextResponse.json(result); -}); + return NextResponse.json(result); + }, +); diff --git a/apps/web/app/api/user/stats/senders/route.ts b/apps/web/app/api/user/stats/senders/route.ts index 5f34c6aea2..30f81a554a 100644 --- a/apps/web/app/api/user/stats/senders/route.ts +++ b/apps/web/app/api/user/stats/senders/route.ts @@ -79,7 +79,7 @@ async function getDomainsMostReceivedFrom({ }); } -export const GET = withEmailAccount(async (request) => { +export const GET = withEmailAccount("user/stats/senders", async (request) => { const emailAccountId = request.auth.emailAccountId; const { searchParams } = new URL(request.url); diff --git a/apps/web/app/api/watch/all/route.ts b/apps/web/app/api/watch/all/route.ts index 8655c89469..040562f925 100644 --- a/apps/web/app/api/watch/all/route.ts +++ b/apps/web/app/api/watch/all/route.ts @@ -20,7 +20,7 @@ async function watchAllEmails() { } } -export const GET = withError(async (request) => { +export const GET = withError("watch/all", async (request) => { if (!hasCronSecret(request)) { captureException(new Error("Unauthorized cron request: api/watch/all")); return new Response("Unauthorized", { status: 401 }); diff --git a/apps/web/app/api/watch/route.ts b/apps/web/app/api/watch/route.ts index 3aac8b9b5d..945c51b68b 100644 --- a/apps/web/app/api/watch/route.ts +++ b/apps/web/app/api/watch/route.ts @@ -5,7 +5,7 @@ import { ensureEmailAccountsWatched } from "@/utils/email/watch-manager"; export const dynamic = "force-dynamic"; -export const GET = withAuth(async (request) => { +export const GET = withAuth("watch", async (request) => { const userId = request.auth.userId; const emailAccountCount = await prisma.emailAccount.count({ where: { userId }, diff --git a/apps/web/utils/middleware.ts b/apps/web/utils/middleware.ts index 30fc38ba97..cf082cd79e 100644 --- a/apps/web/utils/middleware.ts +++ b/apps/web/utils/middleware.ts @@ -23,7 +23,7 @@ export type NextHandler = ( context: { params: Promise> }, ) => Promise; -interface RequestWithLogger extends NextRequest { +export interface RequestWithLogger extends NextRequest { logger: Logger; } @@ -318,7 +318,7 @@ async function emailProviderMiddleware( // withError overloads export function withError( scope: string, - handler: NextHandler, + handler: NextHandler, options?: MiddlewareOptions, ): NextHandler; export function withError( @@ -326,20 +326,20 @@ export function withError( options?: MiddlewareOptions, ): NextHandler; export function withError( - scopeOrHandler: string | NextHandler, - handlerOrOptions?: NextHandler | MiddlewareOptions, + scopeOrHandler: string | NextHandler | NextHandler, + handlerOrOptions?: NextHandler | MiddlewareOptions, options?: MiddlewareOptions, ): NextHandler { if (typeof scopeOrHandler === "string") { return withMiddleware( - handlerOrOptions as NextHandler, + handlerOrOptions as NextHandler, undefined, options, scopeOrHandler, ); } return withMiddleware( - scopeOrHandler, + scopeOrHandler as NextHandler, undefined, handlerOrOptions as MiddlewareOptions, ); @@ -421,7 +421,7 @@ function isErrorWithConfigAndHeaders( typeof error === "object" && error !== null && "config" in error && - "headers" in (error as { config: any }).config + "headers" in (error as { config: Record }).config ); } diff --git a/version.txt b/version.txt index f767e483f9..5d9b5ee282 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -v2.18.23 +v2.18.24