Skip to content

Commit 39ca1f6

Browse files
committed
more integrations
1 parent 1da3407 commit 39ca1f6

File tree

67 files changed

+583
-79
lines changed

Some content is hidden

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

67 files changed

+583
-79
lines changed

apps/sim/app/api/files/parse/route.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import binaryExtensionsList from 'binary-extensions'
77
import { type NextRequest, NextResponse } from 'next/server'
88
import { checkHybridAuth } from '@/lib/auth/hybrid'
99
import { secureFetchWithPinnedIP, validateUrlWithDNS } from '@/lib/core/security/input-validation'
10+
import { sanitizeUrlForLog } from '@/lib/core/utils/logging'
1011
import { isSupportedFileType, parseFile } from '@/lib/file-parsers'
1112
import { isUsingCloudStorage, type StorageContext, StorageService } from '@/lib/uploads'
1213
import { uploadExecutionFile } from '@/lib/uploads/contexts/execution'
@@ -367,7 +368,7 @@ async function handleExternalUrl(
367368
throw new Error(`File too large: ${buffer.length} bytes (max: ${MAX_DOWNLOAD_SIZE_BYTES})`)
368369
}
369370

370-
logger.info(`Downloaded file from URL: ${url}, size: ${buffer.length} bytes`)
371+
logger.info(`Downloaded file from URL: ${sanitizeUrlForLog(url)}, size: ${buffer.length} bytes`)
371372

372373
let userFile: UserFile | undefined
373374
const mimeType = response.headers.get('content-type') || getMimeTypeFromExtension(extension)
@@ -420,7 +421,7 @@ async function handleExternalUrl(
420421

421422
return parseResult
422423
} catch (error) {
423-
logger.error(`Error handling external URL ${url}:`, error)
424+
logger.error(`Error handling external URL ${sanitizeUrlForLog(url)}:`, error)
424425
return {
425426
success: false,
426427
error: `Error fetching URL: ${(error as Error).message}`,

apps/sim/app/api/tools/discord/send-message/route.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,12 @@ export async function POST(request: NextRequest) {
102102
logger.info(`[${requestId}] Processing ${validatedData.files.length} file(s)`)
103103

104104
const userFiles = processFilesToUserFiles(validatedData.files, requestId, logger)
105+
const filesOutput: Array<{
106+
name: string
107+
mimeType: string
108+
data: string
109+
size: number
110+
}> = []
105111

106112
if (userFiles.length === 0) {
107113
logger.warn(`[${requestId}] No valid files to upload, falling back to text-only`)
@@ -138,6 +144,12 @@ export async function POST(request: NextRequest) {
138144
logger.info(`[${requestId}] Downloading file ${i}: ${userFile.name}`)
139145

140146
const buffer = await downloadFileFromStorage(userFile, requestId, logger)
147+
filesOutput.push({
148+
name: userFile.name,
149+
mimeType: userFile.type || 'application/octet-stream',
150+
data: buffer.toString('base64'),
151+
size: buffer.length,
152+
})
141153

142154
const blob = new Blob([new Uint8Array(buffer)], { type: userFile.type })
143155
formData.append(`files[${i}]`, blob, userFile.name)
@@ -174,6 +186,7 @@ export async function POST(request: NextRequest) {
174186
message: data.content,
175187
data: data,
176188
fileCount: userFiles.length,
189+
files: filesOutput,
177190
},
178191
})
179192
} catch (error) {

apps/sim/app/api/tools/image/route.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
22
import { type NextRequest, NextResponse } from 'next/server'
33
import { checkInternalAuth } from '@/lib/auth/hybrid'
44
import { validateImageUrl } from '@/lib/core/security/input-validation'
5+
import { sanitizeUrlForLog } from '@/lib/core/utils/logging'
56
import { generateRequestId } from '@/lib/core/utils/request'
67

78
const logger = createLogger('ImageProxyAPI')
@@ -29,13 +30,13 @@ export async function GET(request: NextRequest) {
2930
const urlValidation = validateImageUrl(imageUrl)
3031
if (!urlValidation.isValid) {
3132
logger.warn(`[${requestId}] Blocked image proxy request`, {
32-
url: imageUrl.substring(0, 100),
33+
url: sanitizeUrlForLog(imageUrl),
3334
error: urlValidation.error,
3435
})
3536
return new NextResponse(urlValidation.error || 'Invalid image URL', { status: 403 })
3637
}
3738

38-
logger.info(`[${requestId}] Proxying image request for: ${imageUrl}`)
39+
logger.info(`[${requestId}] Proxying image request for: ${sanitizeUrlForLog(imageUrl)}`)
3940

4041
try {
4142
const imageResponse = await fetch(imageUrl, {
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import { createLogger } from '@sim/logger'
2+
import { type NextRequest, NextResponse } from 'next/server'
3+
import { z } from 'zod'
4+
import { checkInternalAuth } from '@/lib/auth/hybrid'
5+
import { RawFileInputArraySchema } from '@/lib/uploads/utils/file-schemas'
6+
import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils'
7+
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
8+
import { getJiraCloudId } from '@/tools/jira/utils'
9+
10+
const logger = createLogger('JiraAddAttachmentAPI')
11+
12+
export const dynamic = 'force-dynamic'
13+
14+
const JiraAddAttachmentSchema = z.object({
15+
accessToken: z.string().min(1, 'Access token is required'),
16+
domain: z.string().min(1, 'Domain is required'),
17+
issueKey: z.string().min(1, 'Issue key is required'),
18+
files: RawFileInputArraySchema,
19+
cloudId: z.string().optional().nullable(),
20+
})
21+
22+
export async function POST(request: NextRequest) {
23+
const requestId = `jira-attach-${Date.now()}`
24+
25+
try {
26+
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
27+
if (!authResult.success) {
28+
return NextResponse.json(
29+
{ success: false, error: authResult.error || 'Unauthorized' },
30+
{ status: 401 }
31+
)
32+
}
33+
34+
const body = await request.json()
35+
const validatedData = JiraAddAttachmentSchema.parse(body)
36+
37+
const userFiles = processFilesToUserFiles(validatedData.files, requestId, logger)
38+
if (userFiles.length === 0) {
39+
return NextResponse.json(
40+
{ success: false, error: 'No valid files provided for upload' },
41+
{ status: 400 }
42+
)
43+
}
44+
45+
const cloudId =
46+
validatedData.cloudId ||
47+
(await getJiraCloudId(validatedData.domain, validatedData.accessToken))
48+
49+
const formData = new FormData()
50+
const filesOutput: Array<{ name: string; mimeType: string; data: string; size: number }> = []
51+
52+
for (const file of userFiles) {
53+
const buffer = await downloadFileFromStorage(file, requestId, logger)
54+
filesOutput.push({
55+
name: file.name,
56+
mimeType: file.type || 'application/octet-stream',
57+
data: buffer.toString('base64'),
58+
size: buffer.length,
59+
})
60+
const blob = new Blob([new Uint8Array(buffer)], {
61+
type: file.type || 'application/octet-stream',
62+
})
63+
formData.append('file', blob, file.name)
64+
}
65+
66+
const url = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/issue/${validatedData.issueKey}/attachments`
67+
68+
const response = await fetch(url, {
69+
method: 'POST',
70+
headers: {
71+
Authorization: `Bearer ${validatedData.accessToken}`,
72+
'X-Atlassian-Token': 'no-check',
73+
},
74+
body: formData,
75+
})
76+
77+
if (!response.ok) {
78+
const errorText = await response.text()
79+
logger.error(`[${requestId}] Jira attachment upload failed`, {
80+
status: response.status,
81+
statusText: response.statusText,
82+
error: errorText,
83+
})
84+
return NextResponse.json(
85+
{
86+
success: false,
87+
error: `Failed to upload attachments: ${response.statusText}`,
88+
},
89+
{ status: response.status }
90+
)
91+
}
92+
93+
const attachments = await response.json()
94+
const attachmentIds = Array.isArray(attachments)
95+
? attachments.map((attachment) => attachment.id).filter(Boolean)
96+
: []
97+
98+
return NextResponse.json({
99+
success: true,
100+
output: {
101+
ts: new Date().toISOString(),
102+
issueKey: validatedData.issueKey,
103+
attachmentIds,
104+
files: filesOutput,
105+
},
106+
})
107+
} catch (error) {
108+
if (error instanceof z.ZodError) {
109+
return NextResponse.json(
110+
{ success: false, error: 'Invalid request data', details: error.errors },
111+
{ status: 400 }
112+
)
113+
}
114+
115+
logger.error(`[${requestId}] Jira attachment upload error`, error)
116+
return NextResponse.json(
117+
{ success: false, error: error instanceof Error ? error.message : 'Internal server error' },
118+
{ status: 500 }
119+
)
120+
}
121+
}

apps/sim/app/api/tools/jsm/queues/route.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
22
import { type NextRequest, NextResponse } from 'next/server'
33
import { checkInternalAuth } from '@/lib/auth/hybrid'
44
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation'
5+
import { sanitizeUrlForLog } from '@/lib/core/utils/logging'
56
import { getJiraCloudId, getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'
67

78
export const dynamic = 'force-dynamic'
@@ -62,7 +63,7 @@ export async function POST(request: NextRequest) {
6263

6364
const url = `${baseUrl}/servicedesk/${serviceDeskId}/queue${params.toString() ? `?${params.toString()}` : ''}`
6465

65-
logger.info('Fetching queues from:', url)
66+
logger.info('Fetching queues from:', sanitizeUrlForLog(url))
6667

6768
const response = await fetch(url, {
6869
method: 'GET',

apps/sim/app/api/tools/jsm/request/route.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
validateJiraCloudId,
77
validateJiraIssueKey,
88
} from '@/lib/core/security/input-validation'
9+
import { sanitizeUrlForLog } from '@/lib/core/utils/logging'
910
import { getJiraCloudId, getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'
1011

1112
export const dynamic = 'force-dynamic'
@@ -66,7 +67,7 @@ export async function POST(request: NextRequest) {
6667
}
6768
const url = `${baseUrl}/request`
6869

69-
logger.info('Creating request at:', url)
70+
logger.info('Creating request at:', sanitizeUrlForLog(url))
7071

7172
const requestBody: Record<string, unknown> = {
7273
serviceDeskId,
@@ -128,7 +129,7 @@ export async function POST(request: NextRequest) {
128129

129130
const url = `${baseUrl}/request/${issueIdOrKey}`
130131

131-
logger.info('Fetching request from:', url)
132+
logger.info('Fetching request from:', sanitizeUrlForLog(url))
132133

133134
const response = await fetch(url, {
134135
method: 'GET',

apps/sim/app/api/tools/jsm/requests/route.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
22
import { type NextRequest, NextResponse } from 'next/server'
33
import { checkInternalAuth } from '@/lib/auth/hybrid'
44
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation'
5+
import { sanitizeUrlForLog } from '@/lib/core/utils/logging'
56
import { getJiraCloudId, getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'
67

78
export const dynamic = 'force-dynamic'
@@ -68,7 +69,7 @@ export async function POST(request: NextRequest) {
6869

6970
const url = `${baseUrl}/request${params.toString() ? `?${params.toString()}` : ''}`
7071

71-
logger.info('Fetching requests from:', url)
72+
logger.info('Fetching requests from:', sanitizeUrlForLog(url))
7273

7374
const response = await fetch(url, {
7475
method: 'GET',

apps/sim/app/api/tools/jsm/requesttypes/route.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
22
import { type NextRequest, NextResponse } from 'next/server'
33
import { checkInternalAuth } from '@/lib/auth/hybrid'
44
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation'
5+
import { sanitizeUrlForLog } from '@/lib/core/utils/logging'
56
import { getJiraCloudId, getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'
67

78
export const dynamic = 'force-dynamic'
@@ -53,7 +54,7 @@ export async function POST(request: NextRequest) {
5354

5455
const url = `${baseUrl}/servicedesk/${serviceDeskId}/requesttype${params.toString() ? `?${params.toString()}` : ''}`
5556

56-
logger.info('Fetching request types from:', url)
57+
logger.info('Fetching request types from:', sanitizeUrlForLog(url))
5758

5859
const response = await fetch(url, {
5960
method: 'GET',

apps/sim/app/api/tools/jsm/servicedesks/route.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
22
import { type NextRequest, NextResponse } from 'next/server'
33
import { checkInternalAuth } from '@/lib/auth/hybrid'
44
import { validateJiraCloudId } from '@/lib/core/security/input-validation'
5+
import { sanitizeUrlForLog } from '@/lib/core/utils/logging'
56
import { getJiraCloudId, getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'
67

78
export const dynamic = 'force-dynamic'
@@ -43,7 +44,7 @@ export async function POST(request: NextRequest) {
4344

4445
const url = `${baseUrl}/servicedesk${params.toString() ? `?${params.toString()}` : ''}`
4546

46-
logger.info('Fetching service desks from:', url)
47+
logger.info('Fetching service desks from:', sanitizeUrlForLog(url))
4748

4849
const response = await fetch(url, {
4950
method: 'GET',

apps/sim/app/api/tools/jsm/sla/route.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
22
import { type NextRequest, NextResponse } from 'next/server'
33
import { checkInternalAuth } from '@/lib/auth/hybrid'
44
import { validateJiraCloudId, validateJiraIssueKey } from '@/lib/core/security/input-validation'
5+
import { sanitizeUrlForLog } from '@/lib/core/utils/logging'
56
import { getJiraCloudId, getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'
67

78
export const dynamic = 'force-dynamic'
@@ -53,7 +54,7 @@ export async function POST(request: NextRequest) {
5354

5455
const url = `${baseUrl}/request/${issueIdOrKey}/sla${params.toString() ? `?${params.toString()}` : ''}`
5556

56-
logger.info('Fetching SLA info from:', url)
57+
logger.info('Fetching SLA info from:', sanitizeUrlForLog(url))
5758

5859
const response = await fetch(url, {
5960
method: 'GET',

0 commit comments

Comments
 (0)