Skip to content

Commit d0b4ec4

Browse files
authored
add auth to workflow log route (#14)
1 parent 76e63cb commit d0b4ec4

File tree

2 files changed

+182
-69
lines changed

2 files changed

+182
-69
lines changed

app/api/workflow-log/route.ts

Lines changed: 172 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,193 @@
11
import { eq } from "drizzle-orm";
22
import { NextResponse } from "next/server";
3+
import { auth } from "@/lib/auth";
34
import { db } from "@/lib/db";
45
import { workflowExecutionLogs, workflowExecutions } from "@/lib/db/schema";
56

7+
async function handleStartAction(
8+
session: { user: { id: string } },
9+
data: {
10+
executionId: string;
11+
nodeId: string;
12+
nodeName: string;
13+
nodeType: string;
14+
input: unknown;
15+
}
16+
) {
17+
const { executionId, nodeId, nodeName, nodeType, input } = data;
18+
19+
// Verify the execution belongs to the user
20+
const execution = await db.query.workflowExecutions.findFirst({
21+
where: eq(workflowExecutions.id, executionId),
22+
with: {
23+
workflow: true,
24+
},
25+
});
26+
27+
if (!execution) {
28+
return NextResponse.json({ error: "Execution not found" }, { status: 404 });
29+
}
30+
31+
if (execution.workflow.userId !== session.user.id) {
32+
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
33+
}
34+
35+
const [log] = await db
36+
.insert(workflowExecutionLogs)
37+
.values({
38+
executionId,
39+
nodeId,
40+
nodeName,
41+
nodeType,
42+
status: "running",
43+
input,
44+
startedAt: new Date(),
45+
})
46+
.returning();
47+
48+
return NextResponse.json({
49+
logId: log.id,
50+
startTime: Date.now(),
51+
});
52+
}
53+
54+
async function handleWorkflowCompletion(
55+
session: { user: { id: string } },
56+
data: {
57+
executionId: string;
58+
status: "pending" | "running" | "success" | "error" | "cancelled";
59+
output: unknown;
60+
error: string;
61+
startTime: number;
62+
}
63+
) {
64+
const {
65+
executionId: execId,
66+
status: execStatus,
67+
output: execOutput,
68+
error: execError,
69+
startTime: execStartTime,
70+
} = data;
71+
72+
// Verify the execution belongs to the user
73+
const execution = await db.query.workflowExecutions.findFirst({
74+
where: eq(workflowExecutions.id, execId),
75+
with: {
76+
workflow: true,
77+
},
78+
});
79+
80+
if (!execution) {
81+
return NextResponse.json({ error: "Execution not found" }, { status: 404 });
82+
}
83+
84+
if (execution.workflow.userId !== session.user.id) {
85+
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
86+
}
87+
88+
const duration = Date.now() - execStartTime;
89+
90+
await db
91+
.update(workflowExecutions)
92+
.set({
93+
status: execStatus,
94+
output: execOutput,
95+
error: execError,
96+
completedAt: new Date(),
97+
duration: duration.toString(),
98+
})
99+
.where(eq(workflowExecutions.id, execId));
100+
101+
return NextResponse.json({ success: true });
102+
}
103+
104+
async function handleNodeCompletion(
105+
session: { user: { id: string } },
106+
data: {
107+
logId: string;
108+
startTime: number;
109+
status: "pending" | "running" | "success" | "error";
110+
output: unknown;
111+
error: string;
112+
}
113+
) {
114+
const {
115+
logId,
116+
startTime: nodeStartTime,
117+
status: nodeStatus,
118+
output: nodeOutput,
119+
error: nodeError,
120+
} = data;
121+
122+
if (!logId) {
123+
return NextResponse.json({ success: true });
124+
}
125+
126+
// Verify the log belongs to the user
127+
const log = await db.query.workflowExecutionLogs.findFirst({
128+
where: eq(workflowExecutionLogs.id, logId),
129+
});
130+
131+
if (!log) {
132+
return NextResponse.json({ error: "Log not found" }, { status: 404 });
133+
}
134+
135+
// Get the execution to verify ownership
136+
const execution = await db.query.workflowExecutions.findFirst({
137+
where: eq(workflowExecutions.id, log.executionId),
138+
with: {
139+
workflow: true,
140+
},
141+
});
142+
143+
if (!execution) {
144+
return NextResponse.json({ error: "Execution not found" }, { status: 404 });
145+
}
146+
147+
if (execution.workflow.userId !== session.user.id) {
148+
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
149+
}
150+
151+
const duration = Date.now() - nodeStartTime;
152+
153+
await db
154+
.update(workflowExecutionLogs)
155+
.set({
156+
status: nodeStatus,
157+
output: nodeOutput,
158+
error: nodeError,
159+
completedAt: new Date(),
160+
duration: duration.toString(),
161+
})
162+
.where(eq(workflowExecutionLogs.id, logId));
163+
164+
return NextResponse.json({ success: true });
165+
}
166+
6167
export async function POST(request: Request) {
7168
try {
169+
const session = await auth.api.getSession({
170+
headers: request.headers,
171+
});
172+
173+
if (!session?.user) {
174+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
175+
}
176+
8177
const body = await request.json();
9178
const { action, data } = body;
10179

11180
if (action === "start") {
12-
// Start node execution log
13-
const { executionId, nodeId, nodeName, nodeType, input } = data;
14-
15-
const [log] = await db
16-
.insert(workflowExecutionLogs)
17-
.values({
18-
executionId,
19-
nodeId,
20-
nodeName,
21-
nodeType,
22-
status: "running",
23-
input,
24-
startedAt: new Date(),
25-
})
26-
.returning();
27-
28-
return NextResponse.json({
29-
logId: log.id,
30-
startTime: Date.now(),
31-
});
181+
return handleStartAction(session, data);
32182
}
33183

34184
if (action === "complete") {
35185
// Check if this is a workflow execution completion or node execution completion
36186
if (data.executionId && !data.logId) {
37-
// This is the overall workflow execution completion
38-
const {
39-
executionId: execId,
40-
status: execStatus,
41-
output: execOutput,
42-
error: execError,
43-
startTime: execStartTime,
44-
} = data;
45-
const duration = Date.now() - execStartTime;
46-
47-
await db
48-
.update(workflowExecutions)
49-
.set({
50-
status: execStatus,
51-
output: execOutput,
52-
error: execError,
53-
completedAt: new Date(),
54-
duration: duration.toString(),
55-
})
56-
.where(eq(workflowExecutions.id, execId));
57-
58-
return NextResponse.json({ success: true });
59-
}
60-
61-
// Complete node execution log
62-
const {
63-
logId,
64-
startTime: nodeStartTime,
65-
status: nodeStatus,
66-
output: nodeOutput,
67-
error: nodeError,
68-
} = data;
69-
70-
if (!logId) {
71-
return NextResponse.json({ success: true });
187+
return handleWorkflowCompletion(session, data);
72188
}
73189

74-
const duration = Date.now() - nodeStartTime;
75-
76-
await db
77-
.update(workflowExecutionLogs)
78-
.set({
79-
status: nodeStatus,
80-
output: nodeOutput,
81-
error: nodeError,
82-
completedAt: new Date(),
83-
duration: duration.toString(),
84-
})
85-
.where(eq(workflowExecutionLogs.id, logId));
86-
87-
return NextResponse.json({ success: true });
190+
return handleNodeCompletion(session, data);
88191
}
89192

90193
return NextResponse.json({ error: "Invalid action" }, { status: 400 });

lib/db/schema.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,16 @@ export const workflowExecutionsRelations = relations(
151151
})
152152
);
153153

154+
export const workflowExecutionLogsRelations = relations(
155+
workflowExecutionLogs,
156+
({ one }) => ({
157+
execution: one(workflowExecutions, {
158+
fields: [workflowExecutionLogs.executionId],
159+
references: [workflowExecutions.id],
160+
}),
161+
})
162+
);
163+
154164
export type User = typeof users.$inferSelect;
155165
export type Session = typeof sessions.$inferSelect;
156166
export type Workflow = typeof workflows.$inferSelect;

0 commit comments

Comments
 (0)