feat: send missing node data to ClickHouse for observability

When users open/import a workflow with missing nodes, the frontend
already tracks this via Mixpanel. This adds a parallel fire-and-forget
POST to /api/internal/cloud_analytics so the data also lands in
ClickHouse as "frontend:missing_nodes_detected" events.

This enables post-hoc analysis of which unsupported custom nodes
users are trying to use, supporting data-driven prioritization
of new node pack additions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Deep Mehta
2026-03-16 21:01:43 -07:00
parent a321d66583
commit c99a13122f

View File

@@ -8,6 +8,7 @@ import {
clearTopupTracking as clearTopupUtil,
startTopupTracking as startTopupUtil
} from '@/platform/telemetry/topupTracker'
import { api } from '@/scripts/api'
import type { AuditLog } from '@/services/customerEventsService'
import { getExecutionContext } from '../../utils/getExecutionContext'
@@ -359,10 +360,39 @@ export class MixpanelTelemetryProvider implements TelemetryProvider {
trackWorkflowImported(metadata: WorkflowImportMetadata): void {
this.trackEvent(TelemetryEvents.WORKFLOW_IMPORTED, metadata)
this.reportMissingNodesToClickHouse(metadata)
}
trackWorkflowOpened(metadata: WorkflowImportMetadata): void {
this.trackEvent(TelemetryEvents.WORKFLOW_OPENED, metadata)
this.reportMissingNodesToClickHouse(metadata)
}
/**
* Send missing node data to ClickHouse via the backend cloud_analytics endpoint.
* This supplements the Mixpanel tracking with data in our own analytics pipeline,
* enabling post-hoc analysis of which unsupported nodes users are trying to use.
* Fire-and-forget — errors are silently ignored.
*/
private reportMissingNodesToClickHouse(
metadata: WorkflowImportMetadata
): void {
if (metadata.missing_node_count <= 0) return
api
.fetchApi('/internal/cloud_analytics', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
event_name: 'missing_nodes_detected',
event_data: {
missing_class_types: metadata.missing_node_types,
missing_count: metadata.missing_node_count,
source: metadata.open_source ?? 'unknown'
}
})
})
.catch(() => {})
}
trackWorkflowSaved(metadata: WorkflowSavedMetadata): void {