wekan/models/lib/meteorMongoIntegration.js
2025-10-11 10:32:20 +03:00

306 lines
8.5 KiB
JavaScript

import { Meteor } from 'meteor/meteor';
import { DDP } from 'meteor/ddp';
import { mongodbConnectionManager } from './mongodbConnectionManager';
import { mongodbDriverManager } from './mongodbDriverManager';
/**
* Meteor MongoDB Integration
*
* This module integrates the MongoDB driver manager with Meteor's
* built-in MongoDB connection system to provide automatic driver
* selection and version detection.
*
* Features:
* - Hooks into Meteor's MongoDB connection process
* - Automatic driver selection based on detected version
* - Fallback mechanism for connection failures
* - Integration with Meteor's DDP and reactive systems
*/
class MeteorMongoIntegration {
constructor() {
this.originalMongoConnect = null;
this.originalMongoCollection = null;
this.isInitialized = false;
this.connectionString = null;
this.customConnection = null;
}
/**
* Initialize the integration
* @param {string} connectionString - MongoDB connection string
*/
initialize(connectionString) {
if (this.isInitialized) {
console.log('Meteor MongoDB Integration already initialized');
return;
}
this.connectionString = connectionString;
console.log('Initializing Meteor MongoDB Integration...');
// Store original methods
this.originalMongoConnect = Meteor.connect;
this.originalMongoCollection = Mongo.Collection;
// Override Meteor's connection method
this.overrideMeteorConnection();
// Override Mongo.Collection to use our connection manager
this.overrideMongoCollection();
this.isInitialized = true;
console.log('Meteor MongoDB Integration initialized successfully');
}
/**
* Override Meteor's connection method
*/
overrideMeteorConnection() {
const self = this;
// Override Meteor.connect if it exists
if (typeof Meteor.connect === 'function') {
Meteor.connect = async function(url, options) {
try {
console.log('Meteor.connect called, using custom MongoDB connection manager');
return await self.createCustomConnection(url, options);
} catch (error) {
console.error('Custom connection failed, falling back to original method:', error.message);
return self.originalMongoConnect.call(this, url, options);
}
};
}
}
/**
* Override Mongo.Collection to use our connection manager
*/
overrideMongoCollection() {
const self = this;
const originalCollection = Mongo.Collection;
// Override Mongo.Collection constructor
Mongo.Collection = function(name, options = {}) {
// If we have a custom connection, use it
if (self.customConnection) {
options.connection = self.customConnection;
}
// Create the collection with original constructor
const collection = new originalCollection(name, options);
// Add our custom methods
self.enhanceCollection(collection);
return collection;
};
// Copy static methods from original constructor
Object.setPrototypeOf(Mongo.Collection, originalCollection);
Object.assign(Mongo.Collection, originalCollection);
}
/**
* Create a custom MongoDB connection
* @param {string} url - MongoDB connection URL
* @param {Object} options - Connection options
* @returns {Promise<Object>} - MongoDB connection object
*/
async createCustomConnection(url, options = {}) {
try {
console.log('Creating custom MongoDB connection...');
// Use our connection manager
const connection = await mongodbConnectionManager.createConnection(url, options);
// Store the custom connection
this.customConnection = connection;
// Create a Meteor-compatible connection object
const meteorConnection = this.createMeteorCompatibleConnection(connection);
console.log('Custom MongoDB connection created successfully');
return meteorConnection;
} catch (error) {
console.error('Failed to create custom MongoDB connection:', error.message);
throw error;
}
}
/**
* Create a Meteor-compatible connection object
* @param {Object} connection - MongoDB connection object
* @returns {Object} - Meteor-compatible connection
*/
createMeteorCompatibleConnection(connection) {
const self = this;
return {
// Basic connection properties
_driver: connection,
_name: 'custom-mongodb-connection',
// Collection creation method
createCollection: function(name, options = {}) {
const db = connection.db();
return db.collection(name);
},
// Database access
db: function(name = 'meteor') {
return connection.db(name);
},
// Connection status
status: function() {
return {
status: 'connected',
connected: true,
retryCount: 0
};
},
// Close connection
close: async function() {
try {
await connection.close();
self.customConnection = null;
console.log('Meteor-compatible connection closed');
} catch (error) {
console.error('Error closing Meteor-compatible connection:', error.message);
}
},
// Raw connection access
rawConnection: connection
};
}
/**
* Enhance a collection with additional methods
* @param {Object} collection - Mongo.Collection instance
*/
enhanceCollection(collection) {
const self = this;
// Add connection info method
collection.getConnectionInfo = function() {
if (self.customConnection) {
const stats = mongodbConnectionManager.getConnectionStats();
const driverStats = mongodbDriverManager.getConnectionStats();
return {
connectionType: 'custom',
driver: driverStats.currentDriver,
version: driverStats.detectedVersion,
connectionStats: stats,
driverStats: driverStats
};
}
return {
connectionType: 'default',
driver: 'meteor-default',
version: 'unknown'
};
};
// Add version detection method
collection.detectMongoDBVersion = async function() {
try {
if (self.customConnection) {
const admin = self.customConnection.db().admin();
const buildInfo = await admin.buildInfo();
return buildInfo.version;
}
return null;
} catch (error) {
console.error('Error detecting MongoDB version:', error.message);
return null;
}
};
}
/**
* Get connection statistics
* @returns {Object} - Connection statistics
*/
getStats() {
return {
isInitialized: this.isInitialized,
hasCustomConnection: !!this.customConnection,
connectionString: this.connectionString,
connectionStats: mongodbConnectionManager.getConnectionStats(),
driverStats: mongodbDriverManager.getConnectionStats()
};
}
/**
* Reset the integration
*/
reset() {
if (this.originalMongoConnect) {
Meteor.connect = this.originalMongoConnect;
}
if (this.originalMongoCollection) {
Mongo.Collection = this.originalMongoCollection;
}
this.isInitialized = false;
this.connectionString = null;
this.customConnection = null;
mongodbConnectionManager.reset();
mongodbDriverManager.reset();
console.log('Meteor MongoDB Integration reset');
}
/**
* Test the connection
* @returns {Promise<Object>} - Test results
*/
async testConnection() {
try {
if (!this.customConnection) {
throw new Error('No custom connection available');
}
const db = this.customConnection.db();
const result = await db.admin().ping();
return {
success: true,
result,
driver: mongodbDriverManager.selectedDriver,
version: mongodbDriverManager.detectedVersion
};
} catch (error) {
return {
success: false,
error: error.message,
driver: mongodbDriverManager.selectedDriver,
version: mongodbDriverManager.detectedVersion
};
}
}
}
// Create singleton instance
const meteorMongoIntegration = new MeteorMongoIntegration();
// Export for use in other modules
export { meteorMongoIntegration, MeteorMongoIntegration };
// Auto-initialize if MONGO_URL is available
if (Meteor.isServer && process.env.MONGO_URL) {
console.log('Auto-initializing Meteor MongoDB Integration with MONGO_URL');
meteorMongoIntegration.initialize(process.env.MONGO_URL);
}
// Log initialization
if (Meteor.isServer) {
console.log('Meteor MongoDB Integration module loaded');
}