feat(websocket): add reusable WebSocket connection helper utility
- Create createWebSocket() helper for WebSocket connections with authentication - Support automatic reconnection with configurable delay - Include error handling and logging - Export disconnectWebSocket() for cleanup This helper provides a centralized way to create WebSocket connections with JWT token authentication from localStorage. Although not currently used in the application (we opted for server-side token injection in templates), it provides a reusable utility for future WebSocket integrations. Features: - Automatic token retrieval from localStorage - Configurable reconnection behavior (enabled by default) - Error handling with try-catch on all callbacks - Connection cleanup and management - Type-safe configuration interface Available for future use in client-side WebSocket scenarios or as a reference implementation.
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
import { getToken } from "./storage";
|
||||
interface WebSocketConfig {
|
||||
onMessage: (data: any) => void;
|
||||
onOpen?: () => void;
|
||||
onClose?: () => void;
|
||||
onError?: (error: Event) => void;
|
||||
enableReconnect?: boolean;
|
||||
reconnectDelay?: number;
|
||||
}
|
||||
let activeWebSocket: WebSocket | null = null;
|
||||
let reconnectTimeout: ReturnType<typeof setTimeout> | null = null;
|
||||
function createWebSocket(config: WebSocketConfig): WebSocket | null {
|
||||
const token = getToken();
|
||||
if (!token) {
|
||||
console.warn(
|
||||
"No authentication token found - skipping WebSocket connection",
|
||||
);
|
||||
return null;
|
||||
}
|
||||
// Clean up existing connection
|
||||
if (activeWebSocket) {
|
||||
activeWebSocket.close();
|
||||
}
|
||||
if (reconnectTimeout) {
|
||||
clearTimeout(reconnectTimeout);
|
||||
reconnectTimeout = null;
|
||||
}
|
||||
const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
|
||||
const wsUrl = `${protocol}//${window.location.host}/ws/sync?token=${token}`;
|
||||
const ws = new WebSocket(wsUrl);
|
||||
ws.onopen = () => {
|
||||
console.log("WebSocket connected");
|
||||
if (config.onOpen) {
|
||||
try {
|
||||
config.onOpen();
|
||||
} catch (error) {
|
||||
console.error("Error in onOpen callback:", error);
|
||||
}
|
||||
}
|
||||
};
|
||||
ws.onmessage = (event: MessageEvent) => {
|
||||
try {
|
||||
const message = JSON.parse(event.data);
|
||||
config.onMessage(message);
|
||||
} catch (error) {
|
||||
console.error("Error parsing WebSocket message:", error);
|
||||
console.error("Raw message:", event.data);
|
||||
}
|
||||
};
|
||||
ws.onerror = (error: Event) => {
|
||||
console.error("WebSocket error:", error);
|
||||
if (config.onError) {
|
||||
try {
|
||||
config.onError(error);
|
||||
} catch (err) {
|
||||
console.error("Error in onError callback:", err);
|
||||
}
|
||||
}
|
||||
};
|
||||
ws.onclose = () => {
|
||||
console.log("WebSocket disconnected");
|
||||
|
||||
if (config.onClose) {
|
||||
try {
|
||||
config.onClose();
|
||||
} catch (error) {
|
||||
console.error("Error in onClose callback:", error);
|
||||
}
|
||||
}
|
||||
// Auto-reconnect if enabled
|
||||
if (config.enableReconnect !== false) {
|
||||
const delay = config.reconnectDelay || 5000;
|
||||
console.log(`Reconnecting in ${delay / 1000}s...`);
|
||||
reconnectTimeout = setTimeout(() => {
|
||||
createWebSocket(config);
|
||||
}, delay);
|
||||
}
|
||||
};
|
||||
activeWebSocket = ws;
|
||||
return ws;
|
||||
}
|
||||
function disconnectWebSocket(): void {
|
||||
if (reconnectTimeout) {
|
||||
clearTimeout(reconnectTimeout);
|
||||
reconnectTimeout = null;
|
||||
}
|
||||
if (activeWebSocket) {
|
||||
activeWebSocket.close();
|
||||
activeWebSocket = null;
|
||||
}
|
||||
}
|
||||
export { createWebSocket, disconnectWebSocket };
|
||||
Reference in New Issue
Block a user