fix: logs api should with both API and access keys

This commit is contained in:
Harshith Mullapudi 2025-09-22 19:57:11 +05:30
parent 169a2713d2
commit b912080815
2 changed files with 134 additions and 127 deletions

View File

@ -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);

View File

@ -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,
});
},
);