Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 50 additions & 57 deletions apps/sim/app/api/__test-utils__/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -911,49 +911,44 @@ export function createStorageProviderMocks(options: StorageProviderMockOptions =
},
}))

vi.doMock('@/lib/uploads/core/setup', () => ({
vi.doMock('@/lib/uploads/config', () => ({
USE_S3_STORAGE: provider === 's3',
USE_BLOB_STORAGE: provider === 'blob',
USE_LOCAL_STORAGE: provider === 'local',
getStorageProvider: vi.fn().mockReturnValue(provider),
S3_CONFIG: {
bucket: 'test-s3-bucket',
region: 'us-east-1',
},
S3_KB_CONFIG: {
bucket: 'test-s3-kb-bucket',
region: 'us-east-1',
},
S3_CHAT_CONFIG: {
bucket: 'test-s3-chat-bucket',
region: 'us-east-1',
},
BLOB_CONFIG: {
accountName: 'testaccount',
accountKey: 'testkey',
containerName: 'test-container',
},
BLOB_KB_CONFIG: {
accountName: 'testaccount',
accountKey: 'testkey',
containerName: 'test-kb-container',
},
BLOB_CHAT_CONFIG: {
accountName: 'testaccount',
accountKey: 'testkey',
containerName: 'test-chat-container',
},
}))

if (provider === 's3') {
vi.doMock('@/lib/uploads/s3/s3-client', () => ({
vi.doMock('@/lib/uploads/providers/s3/client', () => ({
getS3Client: vi.fn().mockReturnValue({}),
sanitizeFilenameForMetadata: vi.fn((filename) => filename),
}))

vi.doMock('@/lib/uploads/setup', () => ({
S3_CONFIG: {
bucket: 'test-s3-bucket',
region: 'us-east-1',
},
S3_KB_CONFIG: {
bucket: 'test-s3-kb-bucket',
region: 'us-east-1',
},
S3_CHAT_CONFIG: {
bucket: 'test-s3-chat-bucket',
region: 'us-east-1',
},
BLOB_CONFIG: {
accountName: 'testaccount',
accountKey: 'testkey',
containerName: 'test-container',
},
BLOB_KB_CONFIG: {
accountName: 'testaccount',
accountKey: 'testkey',
containerName: 'test-kb-container',
},
BLOB_CHAT_CONFIG: {
accountName: 'testaccount',
accountKey: 'testkey',
containerName: 'test-chat-container',
},
}))

vi.doMock('@aws-sdk/client-s3', () => ({
PutObjectCommand: vi.fn(),
}))
Expand Down Expand Up @@ -983,29 +978,9 @@ export function createStorageProviderMocks(options: StorageProviderMockOptions =
}),
}

vi.doMock('@/lib/uploads/blob/blob-client', () => ({
vi.doMock('@/lib/uploads/providers/blob/client', () => ({
getBlobServiceClient: vi.fn().mockReturnValue(mockBlobServiceClient),
sanitizeFilenameForMetadata: vi.fn((filename) => filename),
}))

vi.doMock('@/lib/uploads/setup', () => ({
BLOB_CONFIG: {
accountName: 'testaccount',
accountKey: 'testkey',
containerName: 'test-container',
},
BLOB_KB_CONFIG: {
accountName: 'testaccount',
accountKey: 'testkey',
containerName: 'test-kb-container',
},
BLOB_CHAT_CONFIG: {
accountName: 'testaccount',
accountKey: 'testkey',
containerName: 'test-chat-container',
},
}))

vi.doMock('@azure/storage-blob', () => ({
BlobSASPermissions: {
parse: vi.fn(() => 'w'),
Expand Down Expand Up @@ -1355,6 +1330,25 @@ export function setupFileApiMocks(
authMocks.setUnauthenticated()
}

vi.doMock('@/lib/auth/hybrid', () => ({
checkHybridAuth: vi.fn().mockResolvedValue({
success: authenticated,
userId: authenticated ? 'test-user-id' : undefined,
error: authenticated ? undefined : 'Unauthorized',
}),
}))

vi.doMock('@/app/api/files/authorization', () => ({
verifyFileAccess: vi.fn().mockResolvedValue(true),
verifyWorkspaceFileAccess: vi.fn().mockResolvedValue(true),
verifyKBFileAccess: vi.fn().mockResolvedValue(true),
verifyCopilotFileAccess: vi.fn().mockResolvedValue(true),
lookupWorkspaceFileByKey: vi.fn().mockResolvedValue({
workspaceId: 'test-workspace-id',
uploadedBy: 'test-user-id',
}),
}))

mockFileSystem({
writeFileSuccess: true,
readFileContent: 'test content',
Expand Down Expand Up @@ -1510,11 +1504,10 @@ export function mockUploadUtils(
isUsingCloudStorage: vi.fn().mockReturnValue(isCloudStorage),
}))

vi.doMock('@/lib/uploads/setup', () => ({
vi.doMock('@/lib/uploads/config', () => ({
UPLOAD_DIR: '/test/uploads',
USE_S3_STORAGE: isCloudStorage,
USE_BLOB_STORAGE: false,
ensureUploadsDirectory: vi.fn().mockResolvedValue(true),
S3_CONFIG: {
bucket: 'test-bucket',
region: 'test-region',
Expand Down
30 changes: 7 additions & 23 deletions apps/sim/app/api/chat/[identifier]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ const logger = createLogger('ChatIdentifierAPI')
export const dynamic = 'force-dynamic'
export const runtime = 'nodejs'

// This endpoint handles chat interactions via the identifier
export async function POST(
request: NextRequest,
{ params }: { params: Promise<{ identifier: string }> }
Expand All @@ -29,15 +28,13 @@ export async function POST(
try {
logger.debug(`[${requestId}] Processing chat request for identifier: ${identifier}`)

// Parse the request body once
let parsedBody
try {
parsedBody = await request.json()
} catch (_error) {
return addCorsHeaders(createErrorResponse('Invalid request body', 400), request)
}

// Find the chat deployment for this identifier
const deploymentResult = await db
.select({
id: chat.id,
Expand All @@ -60,13 +57,11 @@ export async function POST(

const deployment = deploymentResult[0]

// Check if the chat is active
if (!deployment.isActive) {
logger.warn(`[${requestId}] Chat is not active: ${identifier}`)
return addCorsHeaders(createErrorResponse('This chat is currently unavailable', 403), request)
}

// Validate authentication with the parsed body
const authResult = await validateChatAuth(requestId, deployment, request, parsedBody)
if (!authResult.authorized) {
return addCorsHeaders(
Expand All @@ -75,26 +70,20 @@ export async function POST(
)
}

// Use the already parsed body
const { input, password, email, conversationId, files } = parsedBody

// If this is an authentication request (has password or email but no input),
// set auth cookie and return success
if ((password || email) && !input) {
const response = addCorsHeaders(createSuccessResponse({ authenticated: true }), request)

// Set authentication cookie
setChatAuthCookie(response, deployment.id, deployment.authType)

return response
}

// For chat messages, create regular response (allow empty input if files are present)
if (!input && (!files || files.length === 0)) {
return addCorsHeaders(createErrorResponse('No input provided', 400), request)
}

// Get the workflow and workspace owner for this chat
const workflowResult = await db
.select({
isDeployed: workflow.isDeployed,
Expand Down Expand Up @@ -141,20 +130,22 @@ export async function POST(
const { SSE_HEADERS } = await import('@/lib/utils')
const { createFilteredResult } = await import('@/app/api/workflows/[id]/execute/route')

// Generate executionId early so it can be used for file uploads and workflow execution
const executionId = crypto.randomUUID()

const workflowInput: any = { input, conversationId }
if (files && Array.isArray(files) && files.length > 0) {
logger.debug(`[${requestId}] Processing ${files.length} attached files`)

const executionContext = {
workspaceId: deployment.userId,
workspaceId: workflowResult[0].workspaceId || '',
workflowId: deployment.workflowId,
executionId,
}

const uploadedFiles = await ChatFiles.processChatFiles(files, executionContext, requestId)
const uploadedFiles = await ChatFiles.processChatFiles(
files,
executionContext,
requestId,
deployment.userId
)

if (uploadedFiles.length > 0) {
workflowInput.files = uploadedFiles
Expand Down Expand Up @@ -205,7 +196,6 @@ export async function POST(
}
}

// This endpoint returns information about the chat
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ identifier: string }> }
Expand All @@ -216,7 +206,6 @@ export async function GET(
try {
logger.debug(`[${requestId}] Fetching chat info for identifier: ${identifier}`)

// Find the chat deployment for this identifier
const deploymentResult = await db
.select({
id: chat.id,
Expand All @@ -241,13 +230,11 @@ export async function GET(

const deployment = deploymentResult[0]

// Check if the chat is active
if (!deployment.isActive) {
logger.warn(`[${requestId}] Chat is not active: ${identifier}`)
return addCorsHeaders(createErrorResponse('This chat is currently unavailable', 403), request)
}

// Check for auth cookie first
const cookieName = `chat_auth_${deployment.id}`
const authCookie = request.cookies.get(cookieName)

Expand All @@ -256,7 +243,6 @@ export async function GET(
authCookie &&
validateAuthToken(authCookie.value, deployment.id)
) {
// Cookie valid, return chat info
return addCorsHeaders(
createSuccessResponse({
id: deployment.id,
Expand All @@ -270,7 +256,6 @@ export async function GET(
)
}

// If no valid cookie, proceed with standard auth check
const authResult = await validateChatAuth(requestId, deployment, request)
if (!authResult.authorized) {
logger.info(
Expand All @@ -282,7 +267,6 @@ export async function GET(
)
}

// Return public information about the chat including auth type
return addCorsHeaders(
createSuccessResponse({
id: deployment.id,
Expand Down
Loading