| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291 | import { Meteor } from 'meteor/meteor';import { mongodbDriverManager } from './mongodbDriverManager';/** * MongoDB Connection Manager *  * This module handles MongoDB connections with automatic driver selection * based on detected MongoDB server version and wire protocol compatibility. *  * Features: * - Automatic driver selection based on MongoDB version * - Connection retry with different drivers on wire protocol errors * - Fallback mechanism for unsupported versions * - Connection pooling and management */class MongoDBConnectionManager {  constructor() {    this.connections = new Map();    this.connectionConfigs = new Map();    this.retryAttempts = 3;    this.retryDelay = 1000; // 1 second  }  /**   * Create a MongoDB connection with automatic driver selection   * @param {string} connectionString - MongoDB connection string   * @param {Object} options - Connection options   * @returns {Promise<Object>} - MongoDB connection object   */  async createConnection(connectionString, options = {}) {    const connectionId = this.generateConnectionId(connectionString);        // Check if we already have a working connection    if (this.connections.has(connectionId)) {      const existingConnection = this.connections.get(connectionId);      if (existingConnection.status === 'connected') {        return existingConnection;      }    }    // Try to connect with automatic driver selection    return await this.connectWithDriverSelection(connectionString, options, connectionId);  }  /**   * Connect with automatic driver selection and retry logic   * @param {string} connectionString - MongoDB connection string   * @param {Object} options - Connection options   * @param {string} connectionId - Connection identifier   * @returns {Promise<Object>} - MongoDB connection object   */  async connectWithDriverSelection(connectionString, options, connectionId) {    let lastError = null;    let currentDriver = null;    // First, try with the default driver (if we have a detected version)    if (mongodbDriverManager.detectedVersion) {      currentDriver = mongodbDriverManager.getDriverForVersion(mongodbDriverManager.detectedVersion);    } else {      // Start with the most recent driver      currentDriver = 'mongodb8legacy';    }    // Try connection with different drivers    for (let attempt = 0; attempt < this.retryAttempts; attempt++) {      try {        console.log(`Attempting MongoDB connection with driver: ${currentDriver} (attempt ${attempt + 1})`);                const connection = await this.connectWithDriver(currentDriver, connectionString, options);                // Record successful connection        mongodbDriverManager.recordConnectionAttempt(          currentDriver,           mongodbDriverManager.detectedVersion || 'unknown',           true        );        // Store connection        this.connections.set(connectionId, {          connection,          driver: currentDriver,          version: mongodbDriverManager.detectedVersion || 'unknown',          status: 'connected',          connectionString,          options,          createdAt: new Date()        });        return connection;      } catch (error) {        lastError = error;        console.error(`Connection attempt ${attempt + 1} failed with driver ${currentDriver}:`, error.message);        // Try to detect MongoDB version from error        const detectedVersion = mongodbDriverManager.detectVersionFromError(error);        if (detectedVersion && detectedVersion !== 'unknown') {          mongodbDriverManager.detectedVersion = detectedVersion;          currentDriver = mongodbDriverManager.getDriverForVersion(detectedVersion);          console.log(`Detected MongoDB version ${detectedVersion}, switching to driver ${currentDriver}`);        } else {          // Try next fallback driver          const nextDriver = mongodbDriverManager.getNextFallbackDriver();          if (nextDriver) {            currentDriver = nextDriver;            console.log(`Trying fallback driver: ${currentDriver}`);          } else {            console.error('No more fallback drivers available');            break;          }        }        // Record failed attempt        mongodbDriverManager.recordConnectionAttempt(          currentDriver,           detectedVersion || 'unknown',           false,           error        );        // Wait before retry        if (attempt < this.retryAttempts - 1) {          await this.delay(this.retryDelay * (attempt + 1));        }      }    }    // All attempts failed    throw new Error(`Failed to connect to MongoDB after ${this.retryAttempts} attempts. Last error: ${lastError?.message}`);  }  /**   * Connect using a specific driver   * @param {string} driverName - Driver package name   * @param {string} connectionString - MongoDB connection string   * @param {Object} options - Connection options   * @returns {Promise<Object>} - MongoDB connection object   */  async connectWithDriver(driverName, connectionString, options) {    try {      // Dynamically import the driver      const driver = await import(driverName);      const MongoClient = driver.MongoClient;      // Set default options      const defaultOptions = {        useNewUrlParser: true,        useUnifiedTopology: true,        maxPoolSize: 10,        serverSelectionTimeoutMS: 5000,        socketTimeoutMS: 45000,        ...options      };      // Create connection      const client = new MongoClient(connectionString, defaultOptions);      await client.connect();      // Test the connection      await client.db('admin').admin().ping();      return client;    } catch (error) {      throw new Error(`Failed to connect with driver ${driverName}: ${error.message}`);    }  }  /**   * Get connection by ID   * @param {string} connectionId - Connection identifier   * @returns {Object|null} - Connection object or null   */  getConnection(connectionId) {    return this.connections.get(connectionId) || null;  }  /**   * Close a connection   * @param {string} connectionId - Connection identifier   * @returns {Promise<boolean>} - Whether connection was closed successfully   */  async closeConnection(connectionId) {    const connection = this.connections.get(connectionId);    if (connection && connection.connection) {      try {        await connection.connection.close();        this.connections.delete(connectionId);        console.log(`Closed MongoDB connection: ${connectionId}`);        return true;      } catch (error) {        console.error(`Error closing MongoDB connection ${connectionId}:`, error.message);        return false;      }    }    return false;  }  /**   * Close all connections   * @returns {Promise<number>} - Number of connections closed   */  async closeAllConnections() {    let closedCount = 0;    const connectionIds = Array.from(this.connections.keys());        for (const connectionId of connectionIds) {      if (await this.closeConnection(connectionId)) {        closedCount++;      }    }    console.log(`Closed ${closedCount} MongoDB connections`);    return closedCount;  }  /**   * Get connection statistics   * @returns {Object} - Connection statistics   */  getConnectionStats() {    const connections = Array.from(this.connections.values());    const connected = connections.filter(conn => conn.status === 'connected').length;    const disconnected = connections.length - connected;    return {      total: connections.length,      connected,      disconnected,      connections: connections.map(conn => ({        id: this.getConnectionIdFromConnection(conn),        driver: conn.driver,        version: conn.version,        status: conn.status,        createdAt: conn.createdAt      }))    };  }  /**   * Generate a unique connection ID   * @param {string} connectionString - MongoDB connection string   * @returns {string} - Unique connection ID   */  generateConnectionId(connectionString) {    // Create a hash of the connection string for unique ID    let hash = 0;    for (let i = 0; i < connectionString.length; i++) {      const char = connectionString.charCodeAt(i);      hash = ((hash << 5) - hash) + char;      hash = hash & hash; // Convert to 32-bit integer    }    return `mongodb_${Math.abs(hash)}`;  }  /**   * Get connection ID from connection object   * @param {Object} connection - Connection object   * @returns {string} - Connection ID   */  getConnectionIdFromConnection(connection) {    return this.generateConnectionId(connection.connectionString);  }  /**   * Utility function to delay execution   * @param {number} ms - Milliseconds to delay   * @returns {Promise} - Promise that resolves after delay   */  delay(ms) {    return new Promise(resolve => setTimeout(resolve, ms));  }  /**   * Reset all connections and driver manager state   */  reset() {    this.connections.clear();    this.connectionConfigs.clear();    mongodbDriverManager.reset();  }}// Create singleton instanceconst mongodbConnectionManager = new MongoDBConnectionManager();// Export for use in other modulesexport { mongodbConnectionManager, MongoDBConnectionManager };// MongoDB Connection Manager initialized (status available in Admin Panel)
 |