import { db } from '@sim/db' import { workflow, workflowExecutionLogs } from '@sim/db/schema' import { createLogger } from '@sim/logger' import { generateId } from '@sim/utils/id' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { v1GetLogContract } from '@/lib/api/contracts/v1/logs' import { parseRequest } from '@/lib/api/server' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { materializeExecutionData } from '@/lib/logs/execution/trace-store' import { createApiResponse, getUserLimits } from '@/app/api/v1/logs/meta' import { checkRateLimit, createRateLimitResponse, validateWorkspaceAccess, } from '@/app/api/v1/middleware' const logger = createLogger('V1LogDetailsAPI') export const revalidate = 0 export const GET = withRouteHandler( async (request: NextRequest, context: { params: Promise<{ id: string }> }) => { const requestId = generateId().slice(0, 8) try { const rateLimit = await checkRateLimit(request, 'logs-detail') if (!rateLimit.allowed) { return createRateLimitResponse(rateLimit) } const userId = rateLimit.userId! const parsed = await parseRequest(v1GetLogContract, request, context, { validationErrorResponse: () => NextResponse.json({ error: 'Invalid log ID' }, { status: 400 }), }) if (!parsed.success) return parsed.response const { id } = parsed.data.params const rows = await db .select({ id: workflowExecutionLogs.id, workflowId: workflowExecutionLogs.workflowId, workspaceId: workflowExecutionLogs.workspaceId, executionId: workflowExecutionLogs.executionId, stateSnapshotId: workflowExecutionLogs.stateSnapshotId, level: workflowExecutionLogs.level, trigger: workflowExecutionLogs.trigger, startedAt: workflowExecutionLogs.startedAt, endedAt: workflowExecutionLogs.endedAt, totalDurationMs: workflowExecutionLogs.totalDurationMs, executionData: workflowExecutionLogs.executionData, costTotal: workflowExecutionLogs.costTotal, files: workflowExecutionLogs.files, createdAt: workflowExecutionLogs.createdAt, workflowName: workflow.name, workflowDescription: workflow.description, workflowFolderId: workflow.folderId, workflowUserId: workflow.userId, workflowWorkspaceId: workflow.workspaceId, workflowCreatedAt: workflow.createdAt, workflowUpdatedAt: workflow.updatedAt, }) .from(workflowExecutionLogs) .leftJoin(workflow, eq(workflowExecutionLogs.workflowId, workflow.id)) .where(eq(workflowExecutionLogs.id, id)) .limit(1) const log = rows[0] if (!log) { return NextResponse.json({ error: 'Log not found' }, { status: 404 }) } const accessError = await validateWorkspaceAccess(rateLimit, userId, log.workspaceId) if (accessError) { return NextResponse.json({ error: 'Log not found' }, { status: 404 }) } const workflowSummary = { id: log.workflowId, name: log.workflowName || 'Deleted Workflow', description: log.workflowDescription, folderId: log.workflowFolderId, userId: log.workflowUserId, workspaceId: log.workflowWorkspaceId, createdAt: log.workflowCreatedAt, updatedAt: log.workflowUpdatedAt, deleted: !log.workflowName, } const response = { id: log.id, workflowId: log.workflowId, executionId: log.executionId, level: log.level, trigger: log.trigger, startedAt: log.startedAt.toISOString(), endedAt: log.endedAt?.toISOString() || null, totalDurationMs: log.totalDurationMs, files: log.files || undefined, workflow: workflowSummary, executionData: (await materializeExecutionData( log.executionData as Record | null, { workspaceId: log.workspaceId, workflowId: log.workflowId, executionId: log.executionId, } )) as any, cost: log.costTotal != null ? { total: Number(log.costTotal) } : null, createdAt: log.createdAt.toISOString(), } // Get user's workflow execution limits and usage const limits = await getUserLimits(userId) // Create response with limits information const apiResponse = createApiResponse({ data: response }, limits, rateLimit) return NextResponse.json(apiResponse.body, { headers: apiResponse.headers }) } catch (error: any) { logger.error(`[${requestId}] Log details fetch error`, { error: error.message }) return NextResponse.json({ error: 'Internal server error' }, { status: 500 }) } } )