UNPKG

apostrophe

Version:
179 lines (174 loc) 6.58 kB
// This module establishes `apos.db`, the MongoDB database object. // // ## Options // // ### `uri` // // The MongoDB connection URI. See the [MongoDB URI documentation](https://docs.mongodb.com/manual/reference/connection-string/). // // ### `connect` // // If present, this object is passed on as options to MongoDB's "connect" // method, along with the uri. See the [MongoDB connect settings documentation](http://mongodb.github.io/node-mongodb-native/2.2/reference/connecting/connection-settings/). // // By default, Apostrophe sets options to retry lost connections forever, // however you can override this via the `connect` object if you want to. // // ### `user`, `host`, `port`, `name`, `password` // // These options are used only if `uri` is not present. // // ### `client` // // An existing MongoDB connection (MongoClient) object. If present, it is used // and `uri`, `host`, `connect`, etc. are ignored. // // ### `versionCheck` // // If `true`, check to make sure the database does not belong to an // older, incompatible major release release of Apostrophe and exit if it does. // Defaults to `true`. You can set this to `false` to avoid an extra query at // startup time. // // ## Command line tasks // // ### `@apostrophecms/db:reset` // // Drops ALL collections in the database (including those not created by // Apostrophe), then emits the `@apostrophecms/db:reset` async event to // allow other modules to drop related non-MongoDB resources at the // same time, if desired. // // Note that `apos.db` is the MongoDB database object, not this module. // You shouldn't need to talk to this module after startup, but you can // access it as `apos.modules['@apostrophecms/db']` if you wish. You can // also access `apos.dbClient` if you need the MongoClient object. // // If you need to change the way MongoDB connections are made, // override `connectToMongo` in `modules/@apostrophecms/db/index.js` // in your project. However you may find it easier to just use the // `client` option. const mongodbConnect = require('../../../lib/mongodb-connect'); const escapeHost = require('../../../lib/escape-host'); module.exports = { options: { versionCheck: true }, async init(self) { await self.connectToMongo(); await self.versionCheck(); }, handlers(self) { return { 'apostrophe:destroy': { async closeDb() { if (!self.apos.db) { return; } if (self.connectionReused) { // If we close our db object, which is reusing a connection // shared by someone else, they will lose their connection // too, resulting in unexpected "topology destroyed" errors. // This responsibility should fall to the parent return; } await self.apos.dbClient.close(false); } } }; }, methods(self) { return { // Open the database connection. Always uses MongoClient with its // sensible defaults. Builds a URI if necessary, so we can call it // in a consistent way. // // One default we override: if the connection is lost, we keep // attempting to reconnect forever. This is the most sensible behavior // for a persistent process that requires MongoDB in order to operate. async connectToMongo() { if (self.options.client) { // Reuse a single client connection http://mongodb.github.io/node-mongodb-native/2.2/api/Db.html#db self.apos.dbClient = self.options.client; self.apos.db = self.options.client.db(self.options.name || self.apos.shortName); self.connectionReused = true; return; } let uri = 'mongodb://'; if (process.env.APOS_MONGODB_URI) { uri = process.env.APOS_MONGODB_URI; } else if (self.options.uri) { uri = self.options.uri; } else { if (self.options.user) { uri += self.options.user + ':' + self.options.password + '@'; } if (!self.options.host) { self.options.host = 'localhost'; } if (!self.options.port) { self.options.port = 27017; } if (!self.options.name) { self.options.name = self.apos.shortName; } uri += escapeHost(self.options.host) + ':' + self.options.port + '/' + self.options.name; } self.apos.dbClient = await mongodbConnect(uri, self.options.connect); self.uri = uri; // Automatically uses the db name in the connection string self.apos.db = self.apos.dbClient.db(); }, async versionCheck() { if (!self.options.versionCheck) { return; } const oldGlobal = await self.apos.db.collection('aposDocs').findOne({ type: 'apostrophe-global' }); if (oldGlobal) { throw new Error(`There is a problem with the database: ${self.uri ? (self.uri + ' ') : ''} This database contains an Apostrophe 2.x website. Exiting to avoid content loss.`); } }, async dropAllCollections() { const collections = await self.apos.db.collections(); for (const collection of collections) { // drop the collections. Cannot drop system collections // of MongoDB if (!collection.collectionName.match(/^system\./)) { await collection.drop(); } } } }; }, tasks(self) { return { // Reset the database. Drops ALL collections. If you have // collections in the same database unrelated to Apostrophe they WILL // be removed. // // Then Apostrophe carries out the usual reinitialization of collection // indexes and creation of parked pages, etc. // // PLEASE NOTE: this will drop collections UNRELATED to apostrophe. // If that is a concern for you, drop Apostrophe's collections yourself // and start up your app, which will recreate them. reset: { usage: 'Usage: node app @apostrophecms/db:reset\n\nThis destroys ALL of your content. EVERYTHING in your database.\n', afterModuleReady: true, exitAfter: false, task: async () => { const argv = self.apos.argv; if (argv._.length !== 1) { throw new Error('Incorrect number of arguments.'); } await self.dropAllCollections(); // let other modules run their own tasks now that db has been reset await self.emit('reset'); } } }; } };