From b9120808156305213748040846c60e987f1a485e Mon Sep 17 00:00:00 2001 From: Harshith Mullapudi Date: Mon, 22 Sep 2025 19:57:11 +0530 Subject: [PATCH] fix: logs api should with both API and access keys --- apps/webapp/app/hooks/use-logs.tsx | 2 +- apps/webapp/app/routes/api.v1.logs.tsx | 259 +++++++++++++------------ 2 files changed, 134 insertions(+), 127 deletions(-) diff --git a/apps/webapp/app/hooks/use-logs.tsx b/apps/webapp/app/hooks/use-logs.tsx index 8e0dfb6..2829405 100644 --- a/apps/webapp/app/hooks/use-logs.tsx +++ b/apps/webapp/app/hooks/use-logs.tsx @@ -47,7 +47,7 @@ export function useLogs({ endpoint, source, status, type }: UseLogsOptions) { (pageNum: number) => { const params = new URLSearchParams(); params.set("page", pageNum.toString()); - params.set("limit", "5"); + params.set("limit", "50"); if (source) params.set("source", source); if (status) params.set("status", status); if (type) params.set("type", type); diff --git a/apps/webapp/app/routes/api.v1.logs.tsx b/apps/webapp/app/routes/api.v1.logs.tsx index d9f1942..eea62f7 100644 --- a/apps/webapp/app/routes/api.v1.logs.tsx +++ b/apps/webapp/app/routes/api.v1.logs.tsx @@ -1,150 +1,157 @@ import { type LoaderFunctionArgs, json } from "@remix-run/node"; +import { z } from "zod"; import { prisma } from "~/db.server"; -import { requireUserId } from "~/services/session.server"; +import { createHybridLoaderApiRoute } from "~/services/routeBuilders/apiBuilder.server"; -/** - * Optimizations: - * - Use `findMany` with `select` instead of `include` to fetch only required fields. - * - Use `count` with the same where clause, but only after fetching logs (to avoid unnecessary count if no logs). - * - Use a single query for availableSources with minimal fields. - * - Avoid unnecessary object spreading and type casting. - * - Minimize nested object traversal in mapping. - */ -export async function loader({ request }: LoaderFunctionArgs) { - const userId = await requireUserId(request); - const url = new URL(request.url); +// Schema for logs search parameters +const LogsSearchParams = z.object({ + page: z.string().optional(), + limit: z.string().optional(), + source: z.string().optional(), + status: z.string().optional(), + type: z.string().optional(), +}); - const page = parseInt(url.searchParams.get("page") || "1"); - const limit = parseInt(url.searchParams.get("limit") || "100"); - const source = url.searchParams.get("source"); - const status = url.searchParams.get("status"); - const type = url.searchParams.get("type"); - const skip = (page - 1) * limit; +export const loader = createHybridLoaderApiRoute( + { + allowJWT: true, + searchParams: LogsSearchParams, + corsStrategy: "all", + findResource: async () => 1, + }, + async ({ authentication, searchParams }) => { + const page = parseInt(searchParams.page || "1"); + const limit = parseInt(searchParams.limit || "100"); + const source = searchParams.source; + const status = searchParams.status; + const type = searchParams.type; + const skip = (page - 1) * limit; - // Get user and workspace in one query - const user = await prisma.user.findUnique({ - where: { id: userId }, - select: { Workspace: { select: { id: true } } }, - }); + // Get user and workspace in one query + const user = await prisma.user.findUnique({ + where: { id: authentication.userId }, + select: { Workspace: { select: { id: true } } }, + }); - if (!user?.Workspace) { - throw new Response("Workspace not found", { status: 404 }); - } + if (!user?.Workspace) { + throw new Response("Workspace not found", { status: 404 }); + } - // Build where clause for filtering - const whereClause: any = { - workspaceId: user.Workspace.id, - }; - - if (status) { - whereClause.status = status; - } - - if (type) { - whereClause.data = { - path: ["type"], - equals: type, + // Build where clause for filtering + const whereClause: any = { + workspaceId: user.Workspace.id, }; - } - // If source filter is provided, filter by integration source - if (source) { - whereClause.activity = { - integrationAccount: { - integrationDefinition: { - slug: source, + if (status) { + whereClause.status = status; + } + + if (type) { + whereClause.data = { + path: ["type"], + equals: type, + }; + } + + // If source filter is provided, filter by integration source + if (source) { + whereClause.activity = { + integrationAccount: { + integrationDefinition: { + slug: source, + }, }, - }, - }; - } + }; + } - // Use select to fetch only required fields for logs - const [logs, totalCount, availableSources] = await Promise.all([ - prisma.ingestionQueue.findMany({ - where: whereClause, - select: { - id: true, - createdAt: true, - processedAt: true, - status: true, - error: true, - type: true, - output: true, - data: true, - activity: { - select: { - text: true, - sourceURL: true, - integrationAccount: { - select: { - integrationDefinition: { - select: { - name: true, - slug: true, + // Use select to fetch only required fields for logs + const [logs, totalCount, availableSources] = await Promise.all([ + prisma.ingestionQueue.findMany({ + where: whereClause, + select: { + id: true, + createdAt: true, + processedAt: true, + status: true, + error: true, + type: true, + output: true, + data: true, + activity: { + select: { + text: true, + sourceURL: true, + integrationAccount: { + select: { + integrationDefinition: { + select: { + name: true, + slug: true, + }, }, }, }, }, }, }, - }, - orderBy: { - createdAt: "desc", - }, - skip, - take: limit, - }), + orderBy: { + createdAt: "desc", + }, + skip, + take: limit, + }), - prisma.ingestionQueue.count({ - where: whereClause, - }), + prisma.ingestionQueue.count({ + where: whereClause, + }), - prisma.integrationDefinitionV2.findMany({ - where: { - IntegrationAccount: { - some: { - workspaceId: user.Workspace.id, + prisma.integrationDefinitionV2.findMany({ + where: { + IntegrationAccount: { + some: { + workspaceId: user.Workspace.id, + }, }, }, - }, - select: { - name: true, - slug: true, - }, - }), - ]); + select: { + name: true, + slug: true, + }, + }), + ]); - // Format the response - const formattedLogs = logs.map((log) => { - const integrationDef = - log.activity?.integrationAccount?.integrationDefinition; - const logData = log.data as any; + // Format the response + const formattedLogs = logs.map((log) => { + const integrationDef = + log.activity?.integrationAccount?.integrationDefinition; + const logData = log.data as any; - return { - id: log.id, - source: integrationDef?.name || logData?.source || "Unknown", - ingestText: - log.activity?.text || - logData?.episodeBody || - logData?.text || - "No content", - time: log.createdAt, - processedAt: log.processedAt, - episodeUUID: (log.output as any)?.episodeUuid, - status: log.status, - error: log.error, - sourceURL: log.activity?.sourceURL, - integrationSlug: integrationDef?.slug, - data: log.data, - }; - }); + return { + id: log.id, + source: integrationDef?.name || logData?.source || "Unknown", + ingestText: + log.activity?.text || + logData?.episodeBody || + logData?.text || + "No content", + time: log.createdAt, + processedAt: log.processedAt, + episodeUUID: (log.output as any)?.episodeUuid, + status: log.status, + error: log.error, + sourceURL: log.activity?.sourceURL, + integrationSlug: integrationDef?.slug, + data: log.data, + }; + }); - return json({ - logs: formattedLogs, - totalCount, - page, - limit, - hasMore: skip + logs.length < totalCount, - availableSources, - }); -} + return json({ + logs: formattedLogs, + totalCount, + page, + limit, + hasMore: skip + logs.length < totalCount, + availableSources, + }); + }, +);