semblance-dev/src/services/websocketService.ts
Vadym Samoilenko 7f0df54de3 Fix domain typo: oliver.solution → oliver.solutions across all files
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 13:40:00 +00:00

188 lines
No EOL
5.4 KiB
TypeScript
Executable file

/**
* WebSocket Service Types and Utilities
* Provides type definitions and helper functions for WebSocket events
*/
// Event types that can be received from the WebSocket server
export interface WebSocketEvents {
message_update: {
message: {
id: string;
senderId: string;
text: string;
timestamp: string;
type: string;
highlighted: boolean;
attached_assets?: string[];
activates_visual_context?: boolean;
visualAsset?: {
filename: string;
displayReference: string;
};
};
};
ai_status_update: {
status: {
status: string;
updated_at: string;
};
};
moderator_status_update: {
moderator_status: {
current_section_id: string;
current_item_id: string;
current_section: string;
current_item: string;
progress: number;
};
};
theme_update: {
theme: {
id: string;
title: string;
description: string;
quotes: any[];
source: string;
created_at: string;
};
action: 'added' | 'updated' | 'deleted';
};
focus_group_update: {
llm_model: string;
reasoning_effort?: string;
verbosity?: string;
updated_at: string;
};
analytics_update: {
analytics: {
overview: any;
participation: any;
sentiment_analysis: any;
quality_metrics: any;
recommendations: string[];
};
};
conversation_state_update: {
state: {
status: string;
conversation_flow: any;
conversation_health: any;
};
};
}
// Helper function to convert WebSocket message to frontend message format
export function convertWebSocketMessage(wsMessage: WebSocketEvents['message_update']['message']) {
console.log('🔍 [GPT-5 CONVERTER] Input wsMessage:', JSON.stringify(wsMessage, null, 2));
if (!wsMessage) {
console.error('🔍 [GPT-5 CONVERTER] ERROR: wsMessage is null/undefined');
return null;
}
const converted = {
id: wsMessage.id,
senderId: wsMessage.senderId,
text: wsMessage.text,
timestamp: new Date(wsMessage.timestamp),
type: wsMessage.type,
highlighted: wsMessage.highlighted,
attached_assets: wsMessage.attached_assets || [],
activates_visual_context: wsMessage.activates_visual_context || false,
visualAsset: wsMessage.visualAsset
};
console.log('🔍 [GPT-5 CONVERTER] Output converted:', JSON.stringify(converted, null, 2));
return converted;
}
// Helper function to convert WebSocket theme to frontend theme format
export function convertWebSocketTheme(wsTheme: WebSocketEvents['theme_update']['theme']) {
return {
id: wsTheme.id,
title: wsTheme.title,
description: wsTheme.description,
quotes: wsTheme.quotes,
source: wsTheme.source as 'generated' | 'highlight',
created_at: wsTheme.created_at
};
}
// WebSocket event names as constants
export const WS_EVENTS = {
MESSAGE_UPDATE: 'message_update',
AI_STATUS_UPDATE: 'ai_status_update',
MODERATOR_STATUS_UPDATE: 'moderator_status_update',
THEME_UPDATE: 'theme_update',
FOCUS_GROUP_UPDATE: 'focus_group_update',
ANALYTICS_UPDATE: 'analytics_update',
CONVERSATION_STATE_UPDATE: 'conversation_state_update'
} as const;
// Connection status indicators
export enum WebSocketConnectionStatus {
DISCONNECTED = 'disconnected',
CONNECTING = 'connecting',
CONNECTED = 'connected',
RECONNECTING = 'reconnecting',
ERROR = 'error'
}
// Helper to get user-friendly connection status messages
export function getConnectionStatusMessage(status: WebSocketConnectionStatus, error?: string): string {
switch (status) {
case WebSocketConnectionStatus.DISCONNECTED:
return 'Disconnected from real-time updates';
case WebSocketConnectionStatus.CONNECTING:
return 'Connecting to real-time updates...';
case WebSocketConnectionStatus.CONNECTED:
return 'Connected to real-time updates';
case WebSocketConnectionStatus.RECONNECTING:
return 'Reconnecting to real-time updates...';
case WebSocketConnectionStatus.ERROR:
return error ? `Connection error: ${error}` : 'Connection error occurred';
default:
return 'Unknown connection status';
}
}
// Helper to determine if WebSocket should be used based on environment
export function shouldUseWebSocket(): boolean {
// WebSocket is enabled by default, can be disabled with VITE_DISABLE_WEBSOCKET
return import.meta.env.VITE_DISABLE_WEBSOCKET !== 'true';
}
// Helper to get the appropriate WebSocket server URL
export function getWebSocketUrl(): string {
// In development, connect to localhost backend
if (import.meta.env.DEV) {
return 'http://localhost:5137';
}
// If VITE_WEBSOCKET_URL is explicitly set, use that
if (import.meta.env.VITE_WEBSOCKET_URL) {
return import.meta.env.VITE_WEBSOCKET_URL;
}
// TEMP DEBUG: Try direct connection to backend bypassing Apache
// Add ?direct=1 to URL to test direct connection
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.get('direct') === '1') {
console.log('🔧 USING DIRECT WEBSOCKET CONNECTION (bypassing Apache)');
return 'https://optical-dev.oliver.solutions:5137';
}
// For production with Apache proxy, use the current origin
// The Apache proxy handles the routing to backend
const protocol = window.location.protocol === 'https:' ? 'https:' : 'http:';
const host = window.location.host; // includes port if any
// Use root domain since we specify full path in socket options
return `${protocol}//${host}`;
}