import { db } from '@sim/db' import { workflowBlocks } from '@sim/db/schema' import { createLogger } from '@sim/logger' import { getActiveWorkflowRecord } from '@sim/platform-authz/workflow' import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { v1GetWorkflowContract } from '@/lib/api/contracts/v1/workflows' import { parseRequest } from '@/lib/api/server' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { extractInputFieldsFromBlocks } from '@/lib/workflows/input-format' import { createApiResponse, getUserLimits } from '@/app/api/v1/logs/meta' import { checkRateLimit, createRateLimitResponse, validateWorkspaceAccess, } from '@/app/api/v1/middleware' const logger = createLogger('V1WorkflowDetailsAPI') 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, 'workflow-detail') if (!rateLimit.allowed) { return createRateLimitResponse(rateLimit) } const userId = rateLimit.userId! const parsed = await parseRequest(v1GetWorkflowContract, request, context, { validationErrorResponse: () => NextResponse.json({ error: 'Invalid workflow ID' }, { status: 400 }), }) if (!parsed.success) return parsed.response const { id } = parsed.data.params logger.info(`[${requestId}] Fetching workflow details for ${id}`, { userId }) const workflowData = await getActiveWorkflowRecord(id) if (!workflowData) { return NextResponse.json({ error: 'Workflow not found' }, { status: 404 }) } const accessError = await validateWorkspaceAccess( rateLimit, userId, workflowData.workspaceId! ) if (accessError) { return NextResponse.json({ error: 'Workflow not found' }, { status: 404 }) } const blockRows = await db .select({ id: workflowBlocks.id, type: workflowBlocks.type, subBlocks: workflowBlocks.subBlocks, }) .from(workflowBlocks) .where(eq(workflowBlocks.workflowId, id)) const blocksRecord = Object.fromEntries( blockRows.map((block) => [block.id, { type: block.type, subBlocks: block.subBlocks }]) ) const inputs = extractInputFieldsFromBlocks(blocksRecord) const response = { id: workflowData.id, name: workflowData.name, description: workflowData.description, folderId: workflowData.folderId, workspaceId: workflowData.workspaceId, isDeployed: workflowData.isDeployed, deployedAt: workflowData.deployedAt?.toISOString() || null, runCount: workflowData.runCount, lastRunAt: workflowData.lastRunAt?.toISOString() || null, variables: workflowData.variables || {}, inputs, createdAt: workflowData.createdAt.toISOString(), updatedAt: workflowData.updatedAt.toISOString(), } const limits = await getUserLimits(userId) const apiResponse = createApiResponse({ data: response }, limits, rateLimit) return NextResponse.json(apiResponse.body, { headers: apiResponse.headers }) } catch (error: unknown) { const message = getErrorMessage(error, 'Unknown error') logger.error(`[${requestId}] Workflow details fetch error`, { error: message }) return NextResponse.json({ error: 'Internal server error' }, { status: 500 }) } } )