Skip to content

Commit 9da6443

Browse files
authored
feat(vars) (#182)
* feat(vars) (#181) * feat(vars): started panel * improvement(variables): styling and connection to DB * fix(variables): GET fix * improvement(vars): sync, styling * feat(vars): execution; styling
1 parent c2499f6 commit 9da6443

File tree

25 files changed

+2652
-92
lines changed

25 files changed

+2652
-92
lines changed
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
import { NextRequest, NextResponse } from 'next/server'
2+
import { eq } from 'drizzle-orm'
3+
import { z } from 'zod'
4+
import { getSession } from '@/lib/auth'
5+
import { createLogger } from '@/lib/logs/console-logger'
6+
import { db } from '@/db'
7+
import { workflow } from '@/db/schema'
8+
import { Variable } from '@/stores/panel/variables/types'
9+
10+
const logger = createLogger('WorkflowVariablesAPI')
11+
12+
// Schema for workflow variables updates
13+
const VariablesSchema = z.object({
14+
variables: z.array(
15+
z.object({
16+
id: z.string(),
17+
workflowId: z.string(),
18+
name: z.string(),
19+
type: z.enum(['string', 'number', 'boolean', 'object', 'array']),
20+
value: z.union([z.string(), z.number(), z.boolean(), z.record(z.any()), z.array(z.any())]),
21+
})
22+
),
23+
})
24+
25+
export async function POST(
26+
req: NextRequest,
27+
{ params }: { params: Promise<{ id: string }> }
28+
) {
29+
const requestId = crypto.randomUUID().slice(0, 8)
30+
const workflowId = (await params).id
31+
32+
try {
33+
const session = await getSession()
34+
if (!session?.user?.id) {
35+
logger.warn(`[${requestId}] Unauthorized workflow variables update attempt`)
36+
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
37+
}
38+
39+
// Check if the workflow belongs to the user
40+
const workflowRecord = await db
41+
.select()
42+
.from(workflow)
43+
.where(eq(workflow.id, workflowId))
44+
.limit(1)
45+
46+
if (!workflowRecord.length) {
47+
logger.warn(`[${requestId}] Workflow not found: ${workflowId}`)
48+
return NextResponse.json({ error: 'Workflow not found' }, { status: 404 })
49+
}
50+
51+
if (workflowRecord[0].userId !== session.user.id) {
52+
logger.warn(
53+
`[${requestId}] User ${session.user.id} attempted to update variables for workflow ${workflowId} owned by ${workflowRecord[0].userId}`
54+
)
55+
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
56+
}
57+
58+
const body = await req.json()
59+
60+
try {
61+
const { variables } = VariablesSchema.parse(body)
62+
63+
// Format variables for storage
64+
const variablesRecord: Record<string, Variable> = {}
65+
variables.forEach((variable) => {
66+
variablesRecord[variable.id] = variable
67+
})
68+
69+
// Update workflow with variables
70+
await db
71+
.update(workflow)
72+
.set({
73+
variables: variablesRecord,
74+
updatedAt: new Date(),
75+
})
76+
.where(eq(workflow.id, workflowId))
77+
78+
return NextResponse.json({ success: true })
79+
} catch (validationError) {
80+
if (validationError instanceof z.ZodError) {
81+
logger.warn(`[${requestId}] Invalid workflow variables data`, {
82+
errors: validationError.errors,
83+
})
84+
return NextResponse.json(
85+
{ error: 'Invalid request data', details: validationError.errors },
86+
{ status: 400 }
87+
)
88+
}
89+
throw validationError
90+
}
91+
} catch (error) {
92+
logger.error(`[${requestId}] Error updating workflow variables`, error)
93+
return NextResponse.json(
94+
{ error: 'Failed to update workflow variables' },
95+
{ status: 500 }
96+
)
97+
}
98+
}
99+
100+
export async function GET(
101+
req: NextRequest,
102+
{ params }: { params: Promise<{ id: string }> }
103+
) {
104+
const requestId = crypto.randomUUID().slice(0, 8)
105+
const workflowId = (await params).id
106+
107+
try {
108+
// Get the session directly in the API route
109+
const session = await getSession()
110+
if (!session?.user?.id) {
111+
logger.warn(`[${requestId}] Unauthorized workflow variables access attempt`)
112+
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
113+
}
114+
115+
// Check if the workflow belongs to the user
116+
const workflowRecord = await db
117+
.select()
118+
.from(workflow)
119+
.where(eq(workflow.id, workflowId))
120+
.limit(1)
121+
122+
if (!workflowRecord.length) {
123+
logger.warn(`[${requestId}] Workflow not found: ${workflowId}`)
124+
return NextResponse.json({ error: 'Workflow not found' }, { status: 404 })
125+
}
126+
127+
if (workflowRecord[0].userId !== session.user.id) {
128+
logger.warn(
129+
`[${requestId}] User ${session.user.id} attempted to access variables for workflow ${workflowId} owned by ${workflowRecord[0].userId}`
130+
)
131+
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
132+
}
133+
134+
// Return variables if they exist
135+
const variables = workflowRecord[0].variables as Record<string, Variable> || {}
136+
137+
// Add cache headers to prevent frequent reloading
138+
const headers = new Headers({
139+
'Cache-Control': 'max-age=60, stale-while-revalidate=300', // Cache for 1 minute, stale for 5
140+
'ETag': `"${requestId}-${Object.keys(variables).length}"`,
141+
})
142+
143+
return NextResponse.json({ data: variables }, {
144+
status: 200,
145+
headers,
146+
})
147+
} catch (error: any) {
148+
logger.error(`[${requestId}] Workflow variables fetch error`, error)
149+
return NextResponse.json({ error: error.message }, { status: 500 })
150+
}
151+
}

sim/app/w/[id]/components/console/components/console-entry/console-entry.tsx renamed to sim/app/w/[id]/components/panel/components/console/components/console-entry/console-entry.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useMemo, useState } from 'react'
22
import { format } from 'date-fns'
33
import { AlertCircle, AlertTriangle, Calendar, CheckCircle2, Clock, Terminal } from 'lucide-react'
4-
import { ConsoleEntry as ConsoleEntryType } from '@/stores/console/types'
4+
import { ConsoleEntry as ConsoleEntryType } from '@/stores/panel/console/types'
55
import { getBlock } from '@/blocks'
66
import { JSONView } from '../json-view/json-view'
77

sim/app/w/[id]/components/console/components/json-view/json-view.tsx renamed to sim/app/w/[id]/components/panel/components/console/components/json-view/json-view.tsx

File renamed without changes.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
'use client'
2+
3+
import { useMemo } from 'react'
4+
import { ScrollArea } from '@/components/ui/scroll-area'
5+
import { useConsoleStore } from '@/stores/panel/console/store'
6+
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
7+
import { ConsoleEntry } from './components/console-entry/console-entry'
8+
9+
interface ConsoleProps {
10+
panelWidth: number
11+
}
12+
13+
export function Console({ panelWidth }: ConsoleProps) {
14+
const entries = useConsoleStore((state) => state.entries)
15+
const { activeWorkflowId } = useWorkflowRegistry()
16+
17+
const filteredEntries = useMemo(() => {
18+
return entries.filter((entry) => entry.workflowId === activeWorkflowId)
19+
}, [entries, activeWorkflowId])
20+
21+
return (
22+
<ScrollArea className="h-full">
23+
<div className="pb-16">
24+
{filteredEntries.length === 0 ? (
25+
<div className="flex items-center justify-center h-32 text-sm text-muted-foreground pt-4">
26+
No console entries
27+
</div>
28+
) : (
29+
filteredEntries.map((entry) => (
30+
<ConsoleEntry key={entry.id} entry={entry} consoleWidth={panelWidth} />
31+
))
32+
)}
33+
</div>
34+
</ScrollArea>
35+
)
36+
}

0 commit comments

Comments
 (0)