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
12 changes: 12 additions & 0 deletions apps/sim/app/api/chat/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,18 @@ export async function POST(request: NextRequest) {

logger.info(`Chat "${title}" deployed successfully at ${chatUrl}`)

try {
const { PlatformEvents } = await import('@/lib/core/telemetry')
PlatformEvents.chatDeployed({
chatId: id,
workflowId,
authType,
hasOutputConfigs: outputConfigs.length > 0,
})
} catch (_e) {
// Silently fail
}

return createSuccessResponse({
id,
chatUrl,
Expand Down
30 changes: 14 additions & 16 deletions apps/sim/app/api/knowledge/[id]/documents/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,15 +198,14 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
`[${requestId}] Starting controlled async processing of ${createdDocuments.length} documents`
)

// Track bulk document upload
try {
const { trackPlatformEvent } = await import('@/lib/core/telemetry')
trackPlatformEvent('platform.knowledge_base.documents_uploaded', {
'knowledge_base.id': knowledgeBaseId,
'documents.count': createdDocuments.length,
'documents.upload_type': 'bulk',
'processing.chunk_size': validatedData.processingOptions.chunkSize,
'processing.recipe': validatedData.processingOptions.recipe,
const { PlatformEvents } = await import('@/lib/core/telemetry')
PlatformEvents.knowledgeBaseDocumentsUploaded({
knowledgeBaseId,
documentsCount: createdDocuments.length,
uploadType: 'bulk',
chunkSize: validatedData.processingOptions.chunkSize,
recipe: validatedData.processingOptions.recipe,
})
} catch (_e) {
// Silently fail
Expand Down Expand Up @@ -262,15 +261,14 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
userId
)

// Track single document upload
try {
const { trackPlatformEvent } = await import('@/lib/core/telemetry')
trackPlatformEvent('platform.knowledge_base.documents_uploaded', {
'knowledge_base.id': knowledgeBaseId,
'documents.count': 1,
'documents.upload_type': 'single',
'document.mime_type': validatedData.mimeType,
'document.file_size': validatedData.fileSize,
const { PlatformEvents } = await import('@/lib/core/telemetry')
PlatformEvents.knowledgeBaseDocumentsUploaded({
knowledgeBaseId,
documentsCount: 1,
uploadType: 'single',
mimeType: validatedData.mimeType,
fileSize: validatedData.fileSize,
})
} catch (_e) {
// Silently fail
Expand Down
9 changes: 9 additions & 0 deletions apps/sim/app/api/knowledge/[id]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { getSession } from '@/lib/auth'
import { PlatformEvents } from '@/lib/core/telemetry'
import { generateRequestId } from '@/lib/core/utils/request'
import {
deleteKnowledgeBase,
Expand Down Expand Up @@ -183,6 +184,14 @@ export async function DELETE(

await deleteKnowledgeBase(id, requestId)

try {
PlatformEvents.knowledgeBaseDeleted({
knowledgeBaseId: id,
})
} catch {
// Telemetry should not fail the operation
}

logger.info(`[${requestId}] Knowledge base deleted: ${id} for user ${session.user.id}`)

return NextResponse.json({
Expand Down
11 changes: 11 additions & 0 deletions apps/sim/app/api/knowledge/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { getSession } from '@/lib/auth'
import { PlatformEvents } from '@/lib/core/telemetry'
import { generateRequestId } from '@/lib/core/utils/request'
import { createKnowledgeBase, getKnowledgeBases } from '@/lib/knowledge/service'

Expand Down Expand Up @@ -94,6 +95,16 @@ export async function POST(req: NextRequest) {

const newKnowledgeBase = await createKnowledgeBase(createData, requestId)

try {
PlatformEvents.knowledgeBaseCreated({
knowledgeBaseId: newKnowledgeBase.id,
name: validatedData.name,
workspaceId: validatedData.workspaceId,
})
} catch {
// Telemetry should not fail the operation
}

logger.info(
`[${requestId}] Knowledge base created: ${newKnowledgeBase.id} for user ${session.user.id}`
)
Expand Down
9 changes: 2 additions & 7 deletions apps/sim/app/api/knowledge/search/route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*
* @vitest-environment node
*/
import { createEnvMock } from '@sim/testing'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import {
createMockRequest,
Expand All @@ -26,13 +27,7 @@ vi.mock('drizzle-orm', () => ({

mockKnowledgeSchemas()

vi.mock('@/lib/core/config/env', () => ({
env: {
OPENAI_API_KEY: 'test-api-key',
},
isTruthy: (value: string | boolean | number | undefined) =>
typeof value === 'string' ? value === 'true' || value === '1' : Boolean(value),
}))
vi.mock('@/lib/core/config/env', () => createEnvMock({ OPENAI_API_KEY: 'test-api-key' }))

vi.mock('@/lib/core/utils/request', () => ({
generateRequestId: vi.fn(() => 'test-request-id'),
Expand Down
11 changes: 11 additions & 0 deletions apps/sim/app/api/knowledge/search/route.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { PlatformEvents } from '@/lib/core/telemetry'
import { generateRequestId } from '@/lib/core/utils/request'
import { ALL_TAG_SLOTS } from '@/lib/knowledge/constants'
import { getDocumentTagDefinitions } from '@/lib/knowledge/tags/service'
Expand Down Expand Up @@ -294,6 +295,16 @@ export async function POST(request: NextRequest) {
const documentIds = results.map((result) => result.documentId)
const documentNameMap = await getDocumentNamesByIds(documentIds)

try {
PlatformEvents.knowledgeBaseSearched({
knowledgeBaseId: accessibleKbIds[0],
resultsCount: results.length,
workspaceId: workspaceId || undefined,
})
} catch {
// Telemetry should not fail the operation
}

return NextResponse.json({
success: true,
data: {
Expand Down
8 changes: 2 additions & 6 deletions apps/sim/app/api/knowledge/search/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*
* @vitest-environment node
*/
import { createEnvMock } from '@sim/testing'
import { beforeEach, describe, expect, it, vi } from 'vitest'

vi.mock('drizzle-orm')
Expand All @@ -30,12 +31,7 @@ vi.stubGlobal(
})
)

vi.mock('@/lib/core/config/env', () => ({
env: {},
getEnv: (key: string) => process.env[key],
isTruthy: (value: string | boolean | number | undefined) =>
typeof value === 'string' ? value === 'true' || value === '1' : Boolean(value),
}))
vi.mock('@/lib/core/config/env', () => createEnvMock())

import {
generateSearchEmbedding,
Expand Down
8 changes: 2 additions & 6 deletions apps/sim/app/api/knowledge/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* This file contains unit tests for the knowledge base utility functions,
* including access checks, document processing, and embedding generation.
*/
import { createEnvMock } from '@sim/testing'
import { beforeEach, describe, expect, it, vi } from 'vitest'

vi.mock('drizzle-orm', () => ({
Expand All @@ -15,12 +16,7 @@ vi.mock('drizzle-orm', () => ({
sql: (strings: TemplateStringsArray, ...expr: any[]) => ({ strings, expr }),
}))

vi.mock('@/lib/core/config/env', () => ({
env: { OPENAI_API_KEY: 'test-key' },
getEnv: (key: string) => process.env[key],
isTruthy: (value: string | boolean | number | undefined) =>
typeof value === 'string' ? value === 'true' || value === '1' : Boolean(value),
}))
vi.mock('@/lib/core/config/env', () => createEnvMock({ OPENAI_API_KEY: 'test-key' }))

vi.mock('@/lib/knowledge/documents/utils', () => ({
retryWithExponentialBackoff: (fn: any) => fn(),
Expand Down
12 changes: 6 additions & 6 deletions apps/sim/app/api/mcp/servers/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,12 +140,12 @@ export const POST = withMcpAuth('write')(
)

try {
const { trackPlatformEvent } = await import('@/lib/core/telemetry')
trackPlatformEvent('platform.mcp.server_added', {
'mcp.server_id': serverId,
'mcp.server_name': body.name,
'mcp.transport': body.transport,
'workspace.id': workspaceId,
const { PlatformEvents } = await import('@/lib/core/telemetry')
PlatformEvents.mcpServerAdded({
serverId,
serverName: body.name,
transport: body.transport,
workspaceId,
})
} catch (_e) {
// Silently fail
Expand Down
12 changes: 6 additions & 6 deletions apps/sim/app/api/mcp/tools/execute/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,12 +194,12 @@ export const POST = withMcpAuth('read')(
logger.info(`[${requestId}] Successfully executed tool ${toolName} on server ${serverId}`)

try {
const { trackPlatformEvent } = await import('@/lib/core/telemetry')
trackPlatformEvent('platform.mcp.tool_executed', {
'mcp.server_id': serverId,
'mcp.tool_name': toolName,
'mcp.execution_status': 'success',
'workspace.id': workspaceId,
const { PlatformEvents } = await import('@/lib/core/telemetry')
PlatformEvents.mcpToolExecuted({
serverId,
toolName,
status: 'success',
workspaceId,
})
} catch {
// Telemetry failure is non-critical
Expand Down
17 changes: 7 additions & 10 deletions apps/sim/app/api/templates/[id]/use/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,18 +168,15 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
`[${requestId}] Successfully used template: ${id}, created workflow: ${newWorkflowId}`
)

// Track template usage
try {
const { trackPlatformEvent } = await import('@/lib/core/telemetry')
const { PlatformEvents } = await import('@/lib/core/telemetry')
const templateState = templateData.state as any
trackPlatformEvent('platform.template.used', {
'template.id': id,
'template.name': templateData.name,
'workflow.created_id': newWorkflowId,
'workflow.blocks_count': templateState?.blocks
? Object.keys(templateState.blocks).length
: 0,
'workspace.id': workspaceId,
PlatformEvents.templateUsed({
templateId: id,
templateName: templateData.name,
newWorkflowId,
blocksCount: templateState?.blocks ? Object.keys(templateState.blocks).length : 0,
workspaceId,
})
} catch (_e) {
// Silently fail
Expand Down
22 changes: 22 additions & 0 deletions apps/sim/app/api/webhooks/[id]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { and, eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { validateInteger } from '@/lib/core/security/input-validation'
import { PlatformEvents } from '@/lib/core/telemetry'
import { generateRequestId } from '@/lib/core/utils/request'
import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils'

Expand Down Expand Up @@ -314,6 +315,17 @@ export async function DELETE(
await db.delete(webhook).where(eq(webhook.id, wId))
}

try {
for (const wId of idsToDelete) {
PlatformEvents.webhookDeleted({
webhookId: wId,
workflowId: webhookData.workflow.id,
})
}
} catch {
// Telemetry should not fail the operation
}

logger.info(
`[${requestId}] Successfully deleted ${idsToDelete.length} webhooks for credential set`,
{
Expand All @@ -325,6 +337,16 @@ export async function DELETE(
} else {
await cleanupExternalWebhook(foundWebhook, webhookData.workflow, requestId)
await db.delete(webhook).where(eq(webhook.id, id))

try {
PlatformEvents.webhookDeleted({
webhookId: id,
workflowId: webhookData.workflow.id,
})
} catch {
// Telemetry should not fail the operation
}

logger.info(`[${requestId}] Successfully deleted webhook: ${id}`)
}

Expand Down
14 changes: 14 additions & 0 deletions apps/sim/app/api/webhooks/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { and, desc, eq } from 'drizzle-orm'
import { nanoid } from 'nanoid'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { PlatformEvents } from '@/lib/core/telemetry'
import { generateRequestId } from '@/lib/core/utils/request'
import { getBaseUrl } from '@/lib/core/utils/urls'
import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils'
Expand Down Expand Up @@ -790,6 +791,19 @@ export async function POST(request: NextRequest) {
}
// --- End Grain specific logic ---

if (!targetWebhookId && savedWebhook) {
try {
PlatformEvents.webhookCreated({
webhookId: savedWebhook.id,
workflowId: workflowId,
provider: provider || 'generic',
workspaceId: workflowRecord.workspaceId || undefined,
})
} catch {
// Telemetry should not fail the operation
}
}

const status = targetWebhookId ? 200 : 201
return NextResponse.json({ webhook: savedWebhook }, { status })
} catch (error: any) {
Expand Down
6 changes: 2 additions & 4 deletions apps/sim/app/api/workflows/[id]/deploy/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,10 +217,8 @@ export async function DELETE(
logger.info(`[${requestId}] Workflow undeployed successfully: ${id}`)

try {
const { trackPlatformEvent } = await import('@/lib/core/telemetry')
trackPlatformEvent('platform.workflow.undeployed', {
'workflow.id': id,
})
const { PlatformEvents } = await import('@/lib/core/telemetry')
PlatformEvents.workflowUndeployed({ workflowId: id })
} catch (_e) {
// Silently fail
}
Expand Down
11 changes: 11 additions & 0 deletions apps/sim/app/api/workflows/[id]/duplicate/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { getSession } from '@/lib/auth'
import { PlatformEvents } from '@/lib/core/telemetry'
import { generateRequestId } from '@/lib/core/utils/request'
import { duplicateWorkflow } from '@/lib/workflows/persistence/duplicate'

Expand Down Expand Up @@ -46,6 +47,16 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
requestId,
})

try {
PlatformEvents.workflowDuplicated({
sourceWorkflowId,
newWorkflowId: result.id,
workspaceId,
})
} catch {
// Telemetry should not fail the operation
}

const elapsed = Date.now() - startTime
logger.info(
`[${requestId}] Successfully duplicated workflow ${sourceWorkflowId} to ${result.id} in ${elapsed}ms`
Expand Down
Loading