websocket-server.ts 4.1 KB

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