Mailspring/packages/nylas-core/database-connector.js

153 lines
4.5 KiB
JavaScript
Raw Normal View History

2016-06-19 18:02:32 +08:00
const Sequelize = require('sequelize');
const fs = require('fs');
const path = require('path');
2016-06-24 07:28:51 +08:00
const HookTransactionLog = require('./hook-transaction-log');
const HookAccountCRUD = require('./hook-account-crud');
const HookIncrementVersionOnSave = require('./hook-increment-version-on-save');
const PromiseUtils = require('./promise-utils');
2016-06-19 18:02:32 +08:00
require('./database-extensions'); // Extends Sequelize on require
const STORAGE_DIR = path.join(__dirname, '..', '..', 'storage');
2016-06-19 18:02:32 +08:00
if (!fs.existsSync(STORAGE_DIR)) {
fs.mkdirSync(STORAGE_DIR);
}
class DatabaseConnector {
2016-06-19 18:02:32 +08:00
constructor() {
this._cache = {};
2016-06-19 18:02:32 +08:00
}
_readModelsInDirectory(sequelize, dirname, {schema} = {}) {
2016-06-19 18:02:32 +08:00
const db = {};
for (const filename of fs.readdirSync(dirname)) {
if (filename.endsWith('.js')) {
let model = sequelize.import(path.join(dirname, filename));
if (schema) {
model = model.schema(schema);
}
db[model.name[0].toUpperCase() + model.name.substr(1)] = model;
2016-06-19 18:02:32 +08:00
}
}
2016-06-19 18:02:32 +08:00
Object.keys(db).forEach((modelName) => {
if ("associate" in db[modelName]) {
db[modelName].associate(db);
}
});
return db;
}
_sequelizePoolForDatabase(dbname) {
if (process.env.DB_HOSTNAME) {
return new Sequelize(dbname, process.env.DB_USERNAME, process.env.DB_PASSWORD, {
host: process.env.DB_HOSTNAME,
dialect: "mysql",
charset: 'utf8',
2016-07-15 08:21:21 +08:00
logging: false,
pool: {
min: 1,
max: 30,
idle: 10000,
},
define: {
charset: 'utf8',
collate: 'utf8_general_ci',
},
});
}
2016-07-08 06:25:45 +08:00
return new Sequelize(dbname, '', '', {
storage: path.join(STORAGE_DIR, `${dbname}.sqlite`),
2016-06-19 18:02:32 +08:00
dialect: "sqlite",
logging: false,
})
}
2016-06-19 18:02:32 +08:00
forAccount(accountId) {
if (!accountId) {
return Promise.reject(new Error(`You need to pass an accountId to init the database!`))
}
if (this._cache[accountId]) {
return this._cache[accountId];
}
2016-07-15 08:21:02 +08:00
let newSequelize = null;
2016-07-15 08:21:02 +08:00
if (process.env.DB_HOSTNAME) {
if (!this._accountsRootSequelize) {
this._accountsRootSequelize = this._sequelizePoolForDatabase(`account_data`);
}
// Create a new sequelize instance, but tie it to the same connection pool
// as the other account instances.
newSequelize = this._sequelizePoolForDatabase(`account_data`);
newSequelize.dialect = this._accountsRootSequelize.dialect;
newSequelize.config = this._accountsRootSequelize.config;
newSequelize.connectionManager.close()
newSequelize.connectionManager = this._accountsRootSequelize.connectionManager;
} else {
newSequelize = this._sequelizePoolForDatabase(`a-${accountId}`);
}
2016-06-19 18:02:32 +08:00
const modelsPath = path.join(__dirname, 'models/account');
const db = this._readModelsInDirectory(newSequelize, modelsPath, {schema: `a${accountId}`})
2016-06-19 18:02:32 +08:00
HookTransactionLog(db, newSequelize);
HookIncrementVersionOnSave(db, newSequelize);
db.sequelize = newSequelize;
2016-06-19 18:02:32 +08:00
db.Sequelize = Sequelize;
db.accountId = accountId;
2016-06-19 18:02:32 +08:00
this._cache[accountId] = newSequelize.authenticate().thenReturn(db);
2016-06-19 18:02:32 +08:00
return this._cache[accountId];
2016-06-19 18:02:32 +08:00
}
2016-07-12 12:38:05 +08:00
ensureAccountDatabase(accountId) {
return this.forAccount(accountId).then((db) => {
// this is a bit of a hack, because sequelize.sync() doesn't work with
// schemas. It's necessary to sync models individually and in the right order.
const models = ['Contact', 'Folder', 'Label', 'Transaction', 'Thread', 'ThreadLabel', 'ThreadFolder', 'Message', 'MessageLabel', 'File', 'SyncbackRequest'];
return PromiseUtils.each(models, (n) =>
db[n].sync()
)
});
}
destroyAccountDatabase(accountId) {
const dbname = `a-${accountId}`;
if (process.env.DB_HOSTNAME) {
// todo
} else {
fs.removeFileSync(path.join(STORAGE_DIR, `${dbname}.sqlite`));
}
return Promise.resolve()
}
2016-06-19 18:02:32 +08:00
_sequelizeForShared() {
const sequelize = this._sequelizePoolForDatabase(`shared`);
2016-06-19 18:02:32 +08:00
const modelsPath = path.join(__dirname, 'models/shared');
const db = this._readModelsInDirectory(sequelize, modelsPath)
HookAccountCRUD(db, sequelize);
2016-06-19 18:02:32 +08:00
db.sequelize = sequelize;
db.Sequelize = Sequelize;
return sequelize.authenticate().then(() =>
sequelize.sync()
).thenReturn(db);
}
forShared() {
this._cache.shared = this._cache.shared || this._sequelizeForShared();
return this._cache.shared;
2016-06-19 18:02:32 +08:00
}
}
module.exports = new DatabaseConnector()