|
1 | | -import { db } from '@sim/db' |
2 | | -import { |
3 | | - jobExecutionLogs, |
4 | | - permissions, |
5 | | - workflow, |
6 | | - workflowDeploymentVersion, |
7 | | - workflowExecutionLogs, |
8 | | -} from '@sim/db/schema' |
9 | 1 | import { createLogger } from '@sim/logger' |
10 | | -import { and, eq } from 'drizzle-orm' |
11 | 2 | import { type NextRequest, NextResponse } from 'next/server' |
12 | | -import { logIdParamsSchema } from '@/lib/api/contracts/logs' |
| 3 | +import { getLogDetailContract } from '@/lib/api/contracts/logs' |
| 4 | +import { parseRequest } from '@/lib/api/server' |
13 | 5 | import { getSession } from '@/lib/auth' |
14 | | -import { generateRequestId } from '@/lib/core/utils/request' |
15 | 6 | import { withRouteHandler } from '@/lib/core/utils/with-route-handler' |
| 7 | +import { fetchLogDetail } from '@/lib/logs/fetch-log-detail' |
16 | 8 |
|
17 | 9 | const logger = createLogger('LogDetailsByIdAPI') |
18 | 10 |
|
19 | | -export const revalidate = 0 |
20 | | - |
21 | 11 | export const GET = withRouteHandler( |
22 | | - async (_request: NextRequest, { params }: { params: Promise<{ id: string }> }) => { |
23 | | - const requestId = generateRequestId() |
24 | | - |
25 | | - try { |
26 | | - const session = await getSession() |
27 | | - if (!session?.user?.id) { |
28 | | - logger.warn(`[${requestId}] Unauthorized log details access attempt`) |
29 | | - return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) |
30 | | - } |
31 | | - |
32 | | - const userId = session.user.id |
33 | | - const { id } = logIdParamsSchema.parse(await params) |
34 | | - |
35 | | - const rows = await db |
36 | | - .select({ |
37 | | - id: workflowExecutionLogs.id, |
38 | | - workflowId: workflowExecutionLogs.workflowId, |
39 | | - executionId: workflowExecutionLogs.executionId, |
40 | | - stateSnapshotId: workflowExecutionLogs.stateSnapshotId, |
41 | | - deploymentVersionId: workflowExecutionLogs.deploymentVersionId, |
42 | | - level: workflowExecutionLogs.level, |
43 | | - status: workflowExecutionLogs.status, |
44 | | - trigger: workflowExecutionLogs.trigger, |
45 | | - startedAt: workflowExecutionLogs.startedAt, |
46 | | - endedAt: workflowExecutionLogs.endedAt, |
47 | | - totalDurationMs: workflowExecutionLogs.totalDurationMs, |
48 | | - executionData: workflowExecutionLogs.executionData, |
49 | | - cost: workflowExecutionLogs.cost, |
50 | | - files: workflowExecutionLogs.files, |
51 | | - createdAt: workflowExecutionLogs.createdAt, |
52 | | - workflowName: workflow.name, |
53 | | - workflowDescription: workflow.description, |
54 | | - workflowColor: workflow.color, |
55 | | - workflowFolderId: workflow.folderId, |
56 | | - workflowUserId: workflow.userId, |
57 | | - workflowWorkspaceId: workflow.workspaceId, |
58 | | - workflowCreatedAt: workflow.createdAt, |
59 | | - workflowUpdatedAt: workflow.updatedAt, |
60 | | - deploymentVersion: workflowDeploymentVersion.version, |
61 | | - deploymentVersionName: workflowDeploymentVersion.name, |
62 | | - }) |
63 | | - .from(workflowExecutionLogs) |
64 | | - .leftJoin(workflow, eq(workflowExecutionLogs.workflowId, workflow.id)) |
65 | | - .leftJoin( |
66 | | - workflowDeploymentVersion, |
67 | | - eq(workflowDeploymentVersion.id, workflowExecutionLogs.deploymentVersionId) |
68 | | - ) |
69 | | - .innerJoin( |
70 | | - permissions, |
71 | | - and( |
72 | | - eq(permissions.entityType, 'workspace'), |
73 | | - eq(permissions.entityId, workflowExecutionLogs.workspaceId), |
74 | | - eq(permissions.userId, userId) |
75 | | - ) |
76 | | - ) |
77 | | - .where(eq(workflowExecutionLogs.id, id)) |
78 | | - .limit(1) |
79 | | - |
80 | | - const log = rows[0] |
81 | | - |
82 | | - // Fallback: check job_execution_logs |
83 | | - if (!log) { |
84 | | - const jobRows = await db |
85 | | - .select({ |
86 | | - id: jobExecutionLogs.id, |
87 | | - executionId: jobExecutionLogs.executionId, |
88 | | - level: jobExecutionLogs.level, |
89 | | - status: jobExecutionLogs.status, |
90 | | - trigger: jobExecutionLogs.trigger, |
91 | | - startedAt: jobExecutionLogs.startedAt, |
92 | | - endedAt: jobExecutionLogs.endedAt, |
93 | | - totalDurationMs: jobExecutionLogs.totalDurationMs, |
94 | | - executionData: jobExecutionLogs.executionData, |
95 | | - cost: jobExecutionLogs.cost, |
96 | | - createdAt: jobExecutionLogs.createdAt, |
97 | | - }) |
98 | | - .from(jobExecutionLogs) |
99 | | - .innerJoin( |
100 | | - permissions, |
101 | | - and( |
102 | | - eq(permissions.entityType, 'workspace'), |
103 | | - eq(permissions.entityId, jobExecutionLogs.workspaceId), |
104 | | - eq(permissions.userId, userId) |
105 | | - ) |
106 | | - ) |
107 | | - .where(eq(jobExecutionLogs.id, id)) |
108 | | - .limit(1) |
109 | | - |
110 | | - const jobLog = jobRows[0] |
111 | | - if (!jobLog) { |
112 | | - return NextResponse.json({ error: 'Not found' }, { status: 404 }) |
113 | | - } |
| 12 | + async (request: NextRequest, context: { params: Promise<{ id: string }> }) => { |
| 13 | + const session = await getSession() |
| 14 | + if (!session?.user?.id) { |
| 15 | + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) |
| 16 | + } |
114 | 17 |
|
115 | | - const execData = jobLog.executionData as Record<string, any> | null |
116 | | - const response = { |
117 | | - id: jobLog.id, |
118 | | - workflowId: null, |
119 | | - executionId: jobLog.executionId, |
120 | | - deploymentVersionId: null, |
121 | | - deploymentVersion: null, |
122 | | - deploymentVersionName: null, |
123 | | - level: jobLog.level, |
124 | | - status: jobLog.status, |
125 | | - duration: jobLog.totalDurationMs ? `${jobLog.totalDurationMs}ms` : null, |
126 | | - trigger: jobLog.trigger, |
127 | | - createdAt: jobLog.startedAt.toISOString(), |
128 | | - workflow: null, |
129 | | - jobTitle: (execData?.trigger?.source as string) || null, |
130 | | - executionData: { |
131 | | - totalDuration: jobLog.totalDurationMs, |
132 | | - ...execData, |
133 | | - enhanced: true, |
134 | | - }, |
135 | | - cost: jobLog.cost as any, |
136 | | - } |
| 18 | + const parsed = await parseRequest(getLogDetailContract, request, context) |
| 19 | + if (!parsed.success) return parsed.response |
137 | 20 |
|
138 | | - return NextResponse.json({ data: response }) |
139 | | - } |
| 21 | + const { id } = parsed.data.params |
| 22 | + const { workspaceId } = parsed.data.query |
140 | 23 |
|
141 | | - const workflowSummary = log.workflowId |
142 | | - ? { |
143 | | - id: log.workflowId, |
144 | | - name: log.workflowName, |
145 | | - description: log.workflowDescription, |
146 | | - color: log.workflowColor, |
147 | | - folderId: log.workflowFolderId, |
148 | | - userId: log.workflowUserId, |
149 | | - workspaceId: log.workflowWorkspaceId, |
150 | | - createdAt: log.workflowCreatedAt, |
151 | | - updatedAt: log.workflowUpdatedAt, |
152 | | - } |
153 | | - : null |
| 24 | + const data = await fetchLogDetail({ |
| 25 | + userId: session.user.id, |
| 26 | + workspaceId, |
| 27 | + lookupColumn: 'id', |
| 28 | + lookupValue: id, |
| 29 | + }) |
154 | 30 |
|
155 | | - const response = { |
156 | | - id: log.id, |
157 | | - workflowId: log.workflowId, |
158 | | - executionId: log.executionId, |
159 | | - deploymentVersionId: log.deploymentVersionId, |
160 | | - deploymentVersion: log.deploymentVersion ?? null, |
161 | | - deploymentVersionName: log.deploymentVersionName ?? null, |
162 | | - level: log.level, |
163 | | - status: log.status, |
164 | | - duration: log.totalDurationMs ? `${log.totalDurationMs}ms` : null, |
165 | | - trigger: log.trigger, |
166 | | - createdAt: log.startedAt.toISOString(), |
167 | | - files: log.files || undefined, |
168 | | - workflow: workflowSummary, |
169 | | - executionData: { |
170 | | - totalDuration: log.totalDurationMs, |
171 | | - ...(log.executionData as any), |
172 | | - enhanced: true, |
173 | | - }, |
174 | | - cost: log.cost as any, |
175 | | - } |
| 31 | + if (!data) return NextResponse.json({ error: 'Not found' }, { status: 404 }) |
176 | 32 |
|
177 | | - return NextResponse.json({ data: response }) |
178 | | - } catch (error: any) { |
179 | | - logger.error(`[${requestId}] log details fetch error`, error) |
180 | | - return NextResponse.json({ error: error.message }, { status: 500 }) |
181 | | - } |
| 33 | + logger.debug('Fetched log detail', { id, workspaceId }) |
| 34 | + return NextResponse.json({ data }) |
182 | 35 | } |
183 | 36 | ) |
0 commit comments