Skip to content

Commit 14b7622

Browse files
committed
Merge branch 'staging' into improvement/queries
2 parents 4969c13 + f91beb3 commit 14b7622

File tree

119 files changed

+24334
-2487
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

119 files changed

+24334
-2487
lines changed

apps/sim/app/api/auth/sso/register/route.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { type NextRequest, NextResponse } from 'next/server'
22
import { z } from 'zod'
33
import { auth } from '@/lib/auth'
44
import { env } from '@/lib/core/config/env'
5+
import { REDACTED_MARKER } from '@/lib/core/security/redaction'
56
import { createLogger } from '@/lib/logs/console/logger'
67

78
const logger = createLogger('SSO-Register')
@@ -236,13 +237,13 @@ export async function POST(request: NextRequest) {
236237
oidcConfig: providerConfig.oidcConfig
237238
? {
238239
...providerConfig.oidcConfig,
239-
clientSecret: '[REDACTED]',
240+
clientSecret: REDACTED_MARKER,
240241
}
241242
: undefined,
242243
samlConfig: providerConfig.samlConfig
243244
? {
244245
...providerConfig.samlConfig,
245-
cert: '[REDACTED]',
246+
cert: REDACTED_MARKER,
246247
}
247248
: undefined,
248249
},

apps/sim/app/api/folders/[id]/route.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,23 @@ export async function DELETE(
141141
)
142142
}
143143

144+
// Check if deleting this folder would delete the last workflow(s) in the workspace
145+
const workflowsInFolder = await countWorkflowsInFolderRecursively(
146+
id,
147+
existingFolder.workspaceId
148+
)
149+
const totalWorkflowsInWorkspace = await db
150+
.select({ id: workflow.id })
151+
.from(workflow)
152+
.where(eq(workflow.workspaceId, existingFolder.workspaceId))
153+
154+
if (workflowsInFolder > 0 && workflowsInFolder >= totalWorkflowsInWorkspace.length) {
155+
return NextResponse.json(
156+
{ error: 'Cannot delete folder containing the only workflow(s) in the workspace' },
157+
{ status: 400 }
158+
)
159+
}
160+
144161
// Recursively delete folder and all its contents
145162
const deletionStats = await deleteFolderRecursively(id, existingFolder.workspaceId)
146163

@@ -202,6 +219,34 @@ async function deleteFolderRecursively(
202219
return stats
203220
}
204221

222+
/**
223+
* Counts the number of workflows in a folder and all its subfolders recursively.
224+
*/
225+
async function countWorkflowsInFolderRecursively(
226+
folderId: string,
227+
workspaceId: string
228+
): Promise<number> {
229+
let count = 0
230+
231+
const workflowsInFolder = await db
232+
.select({ id: workflow.id })
233+
.from(workflow)
234+
.where(and(eq(workflow.folderId, folderId), eq(workflow.workspaceId, workspaceId)))
235+
236+
count += workflowsInFolder.length
237+
238+
const childFolders = await db
239+
.select({ id: workflowFolder.id })
240+
.from(workflowFolder)
241+
.where(and(eq(workflowFolder.parentId, folderId), eq(workflowFolder.workspaceId, workspaceId)))
242+
243+
for (const childFolder of childFolders) {
244+
count += await countWorkflowsInFolderRecursively(childFolder.id, workspaceId)
245+
}
246+
247+
return count
248+
}
249+
205250
// Helper function to check for circular references
206251
async function checkForCircularReference(folderId: string, parentId: string): Promise<boolean> {
207252
let currentParentId: string | null = parentId
Lines changed: 32 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { runs } from '@trigger.dev/sdk'
22
import { type NextRequest, NextResponse } from 'next/server'
3-
import { authenticateApiKeyFromHeader, updateApiKeyLastUsed } from '@/lib/api-key/service'
4-
import { getSession } from '@/lib/auth'
3+
import { checkHybridAuth } from '@/lib/auth/hybrid'
54
import { generateRequestId } from '@/lib/core/utils/request'
65
import { createLogger } from '@/lib/logs/console/logger'
76
import { createErrorResponse } from '@/app/api/workflows/utils'
@@ -18,38 +17,44 @@ export async function GET(
1817
try {
1918
logger.debug(`[${requestId}] Getting status for task: ${taskId}`)
2019

21-
// Try session auth first (for web UI)
22-
const session = await getSession()
23-
let authenticatedUserId: string | null = session?.user?.id || null
24-
25-
if (!authenticatedUserId) {
26-
const apiKeyHeader = request.headers.get('x-api-key')
27-
if (apiKeyHeader) {
28-
const authResult = await authenticateApiKeyFromHeader(apiKeyHeader)
29-
if (authResult.success && authResult.userId) {
30-
authenticatedUserId = authResult.userId
31-
if (authResult.keyId) {
32-
await updateApiKeyLastUsed(authResult.keyId).catch((error) => {
33-
logger.warn(`[${requestId}] Failed to update API key last used timestamp:`, {
34-
keyId: authResult.keyId,
35-
error,
36-
})
37-
})
38-
}
39-
}
40-
}
20+
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
21+
if (!authResult.success || !authResult.userId) {
22+
logger.warn(`[${requestId}] Unauthorized task status request`)
23+
return createErrorResponse(authResult.error || 'Authentication required', 401)
4124
}
4225

43-
if (!authenticatedUserId) {
44-
return createErrorResponse('Authentication required', 401)
45-
}
26+
const authenticatedUserId = authResult.userId
4627

47-
// Fetch task status from Trigger.dev
4828
const run = await runs.retrieve(taskId)
4929

5030
logger.debug(`[${requestId}] Task ${taskId} status: ${run.status}`)
5131

52-
// Map Trigger.dev status to our format
32+
const payload = run.payload as any
33+
if (payload?.workflowId) {
34+
const { verifyWorkflowAccess } = await import('@/socket-server/middleware/permissions')
35+
const accessCheck = await verifyWorkflowAccess(authenticatedUserId, payload.workflowId)
36+
if (!accessCheck.hasAccess) {
37+
logger.warn(`[${requestId}] User ${authenticatedUserId} denied access to task ${taskId}`, {
38+
workflowId: payload.workflowId,
39+
})
40+
return createErrorResponse('Access denied', 403)
41+
}
42+
logger.debug(`[${requestId}] User ${authenticatedUserId} has access to task ${taskId}`)
43+
} else {
44+
if (payload?.userId && payload.userId !== authenticatedUserId) {
45+
logger.warn(
46+
`[${requestId}] User ${authenticatedUserId} attempted to access task ${taskId} owned by ${payload.userId}`
47+
)
48+
return createErrorResponse('Access denied', 403)
49+
}
50+
if (!payload?.userId) {
51+
logger.warn(
52+
`[${requestId}] Task ${taskId} has no ownership information in payload. Denying access for security.`
53+
)
54+
return createErrorResponse('Access denied', 403)
55+
}
56+
}
57+
5358
const statusMap = {
5459
QUEUED: 'queued',
5560
WAITING_FOR_DEPLOY: 'queued',
@@ -67,7 +72,6 @@ export async function GET(
6772

6873
const mappedStatus = statusMap[run.status as keyof typeof statusMap] || 'unknown'
6974

70-
// Build response based on status
7175
const response: any = {
7276
success: true,
7377
taskId,
@@ -77,21 +81,18 @@ export async function GET(
7781
},
7882
}
7983

80-
// Add completion details if finished
8184
if (mappedStatus === 'completed') {
8285
response.output = run.output // This contains the workflow execution results
8386
response.metadata.completedAt = run.finishedAt
8487
response.metadata.duration = run.durationMs
8588
}
8689

87-
// Add error details if failed
8890
if (mappedStatus === 'failed') {
8991
response.error = run.error
9092
response.metadata.completedAt = run.finishedAt
9193
response.metadata.duration = run.durationMs
9294
}
9395

94-
// Add progress info if still processing
9596
if (mappedStatus === 'processing' || mappedStatus === 'queued') {
9697
response.estimatedDuration = 180000 // 3 minutes max from our config
9798
}
@@ -107,6 +108,3 @@ export async function GET(
107108
return createErrorResponse('Failed to fetch task status', 500)
108109
}
109110
}
110-
111-
// TODO: Implement task cancellation via Trigger.dev API if needed
112-
// export async function DELETE() { ... }

apps/sim/app/api/knowledge/[id]/documents/[documentId]/chunks/route.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,13 +156,27 @@ export async function POST(
156156
const validatedData = CreateChunkSchema.parse(searchParams)
157157

158158
const docTags = {
159+
// Text tags (7 slots)
159160
tag1: doc.tag1 ?? null,
160161
tag2: doc.tag2 ?? null,
161162
tag3: doc.tag3 ?? null,
162163
tag4: doc.tag4 ?? null,
163164
tag5: doc.tag5 ?? null,
164165
tag6: doc.tag6 ?? null,
165166
tag7: doc.tag7 ?? null,
167+
// Number tags (5 slots)
168+
number1: doc.number1 ?? null,
169+
number2: doc.number2 ?? null,
170+
number3: doc.number3 ?? null,
171+
number4: doc.number4 ?? null,
172+
number5: doc.number5 ?? null,
173+
// Date tags (2 slots)
174+
date1: doc.date1 ?? null,
175+
date2: doc.date2 ?? null,
176+
// Boolean tags (3 slots)
177+
boolean1: doc.boolean1 ?? null,
178+
boolean2: doc.boolean2 ?? null,
179+
boolean3: doc.boolean3 ?? null,
166180
}
167181

168182
const newChunk = await createChunk(

apps/sim/app/api/knowledge/[id]/documents/[documentId]/route.test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,16 @@ describe('Document By ID API Route', () => {
7272
tag5: null,
7373
tag6: null,
7474
tag7: null,
75+
number1: null,
76+
number2: null,
77+
number3: null,
78+
number4: null,
79+
number5: null,
80+
date1: null,
81+
date2: null,
82+
boolean1: null,
83+
boolean2: null,
84+
boolean3: null,
7585
deletedAt: null,
7686
}
7787

apps/sim/app/api/knowledge/[id]/documents/[documentId]/route.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,27 @@ const UpdateDocumentSchema = z.object({
2323
processingError: z.string().optional(),
2424
markFailedDueToTimeout: z.boolean().optional(),
2525
retryProcessing: z.boolean().optional(),
26-
// Tag fields
26+
// Text tag fields
2727
tag1: z.string().optional(),
2828
tag2: z.string().optional(),
2929
tag3: z.string().optional(),
3030
tag4: z.string().optional(),
3131
tag5: z.string().optional(),
3232
tag6: z.string().optional(),
3333
tag7: z.string().optional(),
34+
// Number tag fields
35+
number1: z.string().optional(),
36+
number2: z.string().optional(),
37+
number3: z.string().optional(),
38+
number4: z.string().optional(),
39+
number5: z.string().optional(),
40+
// Date tag fields
41+
date1: z.string().optional(),
42+
date2: z.string().optional(),
43+
// Boolean tag fields
44+
boolean1: z.string().optional(),
45+
boolean2: z.string().optional(),
46+
boolean3: z.string().optional(),
3447
})
3548

3649
export async function GET(

apps/sim/app/api/knowledge/[id]/documents/route.test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,16 @@ describe('Knowledge Base Documents API Route', () => {
8080
tag5: null,
8181
tag6: null,
8282
tag7: null,
83+
number1: null,
84+
number2: null,
85+
number3: null,
86+
number4: null,
87+
number5: null,
88+
date1: null,
89+
date2: null,
90+
boolean1: null,
91+
boolean2: null,
92+
boolean3: null,
8393
deletedAt: null,
8494
}
8595

0 commit comments

Comments
 (0)