websocket-server.ts 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. /* eslint-disable @typescript-eslint/no-explicit-any */
  2. import { WebSocketServer, WebSocket } from 'ws';
  3. import { Server } from 'http';
  4. import { ImportProgress } from './types';
  5. export class ImportProgressServer {
  6. private wss: WebSocketServer | null = null;
  7. private clients: Set<WebSocket> = new Set();
  8. private static instance: ImportProgressServer | null = null;
  9. private port: number = 8081;
  10. private isInitialized: boolean = false;
  11. private constructor() {
  12. // Private constructor for singleton pattern
  13. }
  14. public static getInstance(): ImportProgressServer {
  15. if (!ImportProgressServer.instance) {
  16. ImportProgressServer.instance = new ImportProgressServer();
  17. }
  18. return ImportProgressServer.instance;
  19. }
  20. public initialize(): void {
  21. if (this.isInitialized) {
  22. console.log('WebSocket server already initialized');
  23. return;
  24. }
  25. try {
  26. // Try to create WebSocket server on the specified port
  27. this.wss = new WebSocketServer({ port: this.port });
  28. this.setupWebSocketHandlers();
  29. this.isInitialized = true;
  30. console.log(`WebSocket server started on port ${this.port}`);
  31. } catch (error: any) {
  32. if (error.code === 'EADDRINUSE') {
  33. console.log(`Port ${this.port} is already in use, using existing server`);
  34. // Server is already running, we'll use the existing one
  35. this.isInitialized = true;
  36. } else {
  37. console.error('Failed to start WebSocket server:', error);
  38. throw error;
  39. }
  40. }
  41. }
  42. private setupWebSocketHandlers(): void {
  43. if (!this.wss) return;
  44. this.wss.on('connection', (ws: WebSocket) => {
  45. console.log('New WebSocket connection established');
  46. this.clients.add(ws);
  47. ws.on('close', () => {
  48. console.log('WebSocket connection closed');
  49. this.clients.delete(ws);
  50. });
  51. ws.on('error', (error) => {
  52. console.error('WebSocket error:', error);
  53. this.clients.delete(ws);
  54. });
  55. });
  56. }
  57. // Alternative method to attach to existing HTTP server
  58. attachToServer(server: Server): void {
  59. if (this.wss) {
  60. this.wss.close();
  61. }
  62. this.wss = new WebSocketServer({
  63. server,
  64. path: '/api/imports/progress'
  65. });
  66. this.wss.on('connection', (ws: WebSocket, request) => {
  67. console.log('New WebSocket connection on import progress endpoint');
  68. this.clients.add(ws);
  69. const url = new URL(request.url || '', `http://${request.headers.host}`);
  70. const importId = url.searchParams.get('importId');
  71. if (importId) {
  72. ws.send(JSON.stringify({
  73. type: 'connected',
  74. importId: parseInt(importId),
  75. message: 'Connected to import progress updates'
  76. }));
  77. }
  78. ws.on('close', () => {
  79. console.log('WebSocket connection closed');
  80. this.clients.delete(ws);
  81. });
  82. ws.on('error', (error) => {
  83. console.error('WebSocket error:', error);
  84. this.clients.delete(ws);
  85. });
  86. });
  87. }
  88. broadcastProgress(importId: number, progress: ImportProgress): void {
  89. const message = JSON.stringify({
  90. type: 'progress',
  91. importId,
  92. progress
  93. });
  94. this.clients.forEach(client => {
  95. if (client.readyState === WebSocket.OPEN) {
  96. client.send(message);
  97. }
  98. });
  99. }
  100. broadcastError(importId: number, error: string): void {
  101. const message = JSON.stringify({
  102. type: 'error',
  103. importId,
  104. error
  105. });
  106. this.clients.forEach(client => {
  107. if (client.readyState === WebSocket.OPEN) {
  108. client.send(message);
  109. }
  110. });
  111. }
  112. broadcastComplete(importId: number, result: any): void {
  113. const message = JSON.stringify({
  114. type: 'complete',
  115. importId,
  116. result
  117. });
  118. this.clients.forEach(client => {
  119. if (client.readyState === WebSocket.OPEN) {
  120. client.send(message);
  121. }
  122. });
  123. }
  124. close(): void {
  125. if (this.wss) {
  126. this.wss.close();
  127. this.clients.clear();
  128. this.isInitialized = false;
  129. }
  130. }
  131. isServerInitialized(): boolean {
  132. return this.isInitialized;
  133. }
  134. }
  135. // Export a singleton instance
  136. export const progressServer = ImportProgressServer.getInstance();