meteorMongoIntegration.js 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. import { Meteor } from 'meteor/meteor';
  2. import { DDP } from 'meteor/ddp';
  3. import { mongodbConnectionManager } from './mongodbConnectionManager';
  4. import { mongodbDriverManager } from './mongodbDriverManager';
  5. /**
  6. * Meteor MongoDB Integration
  7. *
  8. * This module integrates the MongoDB driver manager with Meteor's
  9. * built-in MongoDB connection system to provide automatic driver
  10. * selection and version detection.
  11. *
  12. * Features:
  13. * - Hooks into Meteor's MongoDB connection process
  14. * - Automatic driver selection based on detected version
  15. * - Fallback mechanism for connection failures
  16. * - Integration with Meteor's DDP and reactive systems
  17. */
  18. class MeteorMongoIntegration {
  19. constructor() {
  20. this.originalMongoConnect = null;
  21. this.originalMongoCollection = null;
  22. this.isInitialized = false;
  23. this.connectionString = null;
  24. this.customConnection = null;
  25. }
  26. /**
  27. * Initialize the integration
  28. * @param {string} connectionString - MongoDB connection string
  29. */
  30. initialize(connectionString) {
  31. if (this.isInitialized) {
  32. console.log('Meteor MongoDB Integration already initialized');
  33. return;
  34. }
  35. this.connectionString = connectionString;
  36. console.log('Initializing Meteor MongoDB Integration...');
  37. // Store original methods
  38. this.originalMongoConnect = Meteor.connect;
  39. this.originalMongoCollection = Mongo.Collection;
  40. // Override Meteor's connection method
  41. this.overrideMeteorConnection();
  42. // Override Mongo.Collection to use our connection manager
  43. this.overrideMongoCollection();
  44. this.isInitialized = true;
  45. // Meteor MongoDB Integration initialized successfully (status available in Admin Panel)
  46. }
  47. /**
  48. * Override Meteor's connection method
  49. */
  50. overrideMeteorConnection() {
  51. const self = this;
  52. // Override Meteor.connect if it exists
  53. if (typeof Meteor.connect === 'function') {
  54. Meteor.connect = async function(url, options) {
  55. try {
  56. console.log('Meteor.connect called, using custom MongoDB connection manager');
  57. return await self.createCustomConnection(url, options);
  58. } catch (error) {
  59. console.error('Custom connection failed, falling back to original method:', error.message);
  60. return self.originalMongoConnect.call(this, url, options);
  61. }
  62. };
  63. }
  64. }
  65. /**
  66. * Override Mongo.Collection to use our connection manager
  67. */
  68. overrideMongoCollection() {
  69. const self = this;
  70. const originalCollection = Mongo.Collection;
  71. // Override Mongo.Collection constructor
  72. Mongo.Collection = function(name, options = {}) {
  73. // If we have a custom connection, use it
  74. if (self.customConnection) {
  75. options.connection = self.customConnection;
  76. }
  77. // Create the collection with original constructor
  78. const collection = new originalCollection(name, options);
  79. // Add our custom methods
  80. self.enhanceCollection(collection);
  81. return collection;
  82. };
  83. // Copy static methods from original constructor
  84. Object.setPrototypeOf(Mongo.Collection, originalCollection);
  85. Object.assign(Mongo.Collection, originalCollection);
  86. }
  87. /**
  88. * Create a custom MongoDB connection
  89. * @param {string} url - MongoDB connection URL
  90. * @param {Object} options - Connection options
  91. * @returns {Promise<Object>} - MongoDB connection object
  92. */
  93. async createCustomConnection(url, options = {}) {
  94. try {
  95. console.log('Creating custom MongoDB connection...');
  96. // Use our connection manager
  97. const connection = await mongodbConnectionManager.createConnection(url, options);
  98. // Store the custom connection
  99. this.customConnection = connection;
  100. // Create a Meteor-compatible connection object
  101. const meteorConnection = this.createMeteorCompatibleConnection(connection);
  102. console.log('Custom MongoDB connection created successfully');
  103. return meteorConnection;
  104. } catch (error) {
  105. console.error('Failed to create custom MongoDB connection:', error.message);
  106. throw error;
  107. }
  108. }
  109. /**
  110. * Create a Meteor-compatible connection object
  111. * @param {Object} connection - MongoDB connection object
  112. * @returns {Object} - Meteor-compatible connection
  113. */
  114. createMeteorCompatibleConnection(connection) {
  115. const self = this;
  116. return {
  117. // Basic connection properties
  118. _driver: connection,
  119. _name: 'custom-mongodb-connection',
  120. // Collection creation method
  121. createCollection: function(name, options = {}) {
  122. const db = connection.db();
  123. return db.collection(name);
  124. },
  125. // Database access
  126. db: function(name = 'meteor') {
  127. return connection.db(name);
  128. },
  129. // Connection status
  130. status: function() {
  131. return {
  132. status: 'connected',
  133. connected: true,
  134. retryCount: 0
  135. };
  136. },
  137. // Close connection
  138. close: async function() {
  139. try {
  140. await connection.close();
  141. self.customConnection = null;
  142. console.log('Meteor-compatible connection closed');
  143. } catch (error) {
  144. console.error('Error closing Meteor-compatible connection:', error.message);
  145. }
  146. },
  147. // Raw connection access
  148. rawConnection: connection
  149. };
  150. }
  151. /**
  152. * Enhance a collection with additional methods
  153. * @param {Object} collection - Mongo.Collection instance
  154. */
  155. enhanceCollection(collection) {
  156. const self = this;
  157. // Add connection info method
  158. collection.getConnectionInfo = function() {
  159. if (self.customConnection) {
  160. const stats = mongodbConnectionManager.getConnectionStats();
  161. const driverStats = mongodbDriverManager.getConnectionStats();
  162. return {
  163. connectionType: 'custom',
  164. driver: driverStats.currentDriver,
  165. version: driverStats.detectedVersion,
  166. connectionStats: stats,
  167. driverStats: driverStats
  168. };
  169. }
  170. return {
  171. connectionType: 'default',
  172. driver: 'meteor-default',
  173. version: 'unknown'
  174. };
  175. };
  176. // Add version detection method
  177. collection.detectMongoDBVersion = async function() {
  178. try {
  179. if (self.customConnection) {
  180. const admin = self.customConnection.db().admin();
  181. const buildInfo = await admin.buildInfo();
  182. return buildInfo.version;
  183. }
  184. return null;
  185. } catch (error) {
  186. console.error('Error detecting MongoDB version:', error.message);
  187. return null;
  188. }
  189. };
  190. }
  191. /**
  192. * Get connection statistics
  193. * @returns {Object} - Connection statistics
  194. */
  195. getStats() {
  196. return {
  197. isInitialized: this.isInitialized,
  198. hasCustomConnection: !!this.customConnection,
  199. connectionString: this.connectionString,
  200. connectionStats: mongodbConnectionManager.getConnectionStats(),
  201. driverStats: mongodbDriverManager.getConnectionStats()
  202. };
  203. }
  204. /**
  205. * Reset the integration
  206. */
  207. reset() {
  208. if (this.originalMongoConnect) {
  209. Meteor.connect = this.originalMongoConnect;
  210. }
  211. if (this.originalMongoCollection) {
  212. Mongo.Collection = this.originalMongoCollection;
  213. }
  214. this.isInitialized = false;
  215. this.connectionString = null;
  216. this.customConnection = null;
  217. mongodbConnectionManager.reset();
  218. mongodbDriverManager.reset();
  219. console.log('Meteor MongoDB Integration reset');
  220. }
  221. /**
  222. * Test the connection
  223. * @returns {Promise<Object>} - Test results
  224. */
  225. async testConnection() {
  226. try {
  227. if (!this.customConnection) {
  228. throw new Error('No custom connection available');
  229. }
  230. const db = this.customConnection.db();
  231. const result = await db.admin().ping();
  232. return {
  233. success: true,
  234. result,
  235. driver: mongodbDriverManager.selectedDriver,
  236. version: mongodbDriverManager.detectedVersion
  237. };
  238. } catch (error) {
  239. return {
  240. success: false,
  241. error: error.message,
  242. driver: mongodbDriverManager.selectedDriver,
  243. version: mongodbDriverManager.detectedVersion
  244. };
  245. }
  246. }
  247. }
  248. // Create singleton instance
  249. const meteorMongoIntegration = new MeteorMongoIntegration();
  250. // Export for use in other modules
  251. export { meteorMongoIntegration, MeteorMongoIntegration };
  252. // Auto-initialize if MONGO_URL is available
  253. if (Meteor.isServer && process.env.MONGO_URL) {
  254. // Auto-initializing Meteor MongoDB Integration with MONGO_URL (status available in Admin Panel)
  255. meteorMongoIntegration.initialize(process.env.MONGO_URL);
  256. }
  257. // Meteor MongoDB Integration module loaded (status available in Admin Panel)