Do not use mysql command for setting up databases

This commit is contained in:
Andris Reinman 2016-04-25 13:59:00 +03:00
parent b93afd4f9f
commit 873d88658c
7 changed files with 94 additions and 49 deletions

View file

@ -28,17 +28,15 @@ Subscribe to Mailtrain Newsletter [here](http://mailtrain.org/subscription/EysIv
1. Download Mailtrain files using git: `git clone git://github.com/andris9/mailtrain.git` and open Mailtrain folder `cd mailtrain`
2. Run `npm install` in the Mailtrain folder to install required dependencies
3. Copy [config/default.toml](config/default.toml) as `config/production.toml` and update MySQL and any other settings in it
4. Import SQL tables by running `npm run sql` (invokes `mysql` command in the background, so you should have it installed. If you can't use this command, then you can import the [database file](setup/sql/mailtrain.sql) yourself). If MySQL configuration in config file is not correct then this command fails.
5. Run the server `NODE_ENV=production npm start`
6. Open [http://localhost:3000/](http://localhost:3000/)
7. Authenticate as `admin`:`test`
8. Navigate to [http://localhost:3000/settings](http://localhost:3000/settings) and update service configuration
9. Navigate to [http://localhost:3000/users/account](http://localhost:3000/users/account) and update user information and password
4. Run the server `NODE_ENV=production npm start`
5. Open [http://localhost:3000/](http://localhost:3000/)
6. Authenticate as `admin`:`test`
7. Navigate to [http://localhost:3000/settings](http://localhost:3000/settings) and update service configuration
8. Navigate to [http://localhost:3000/users/account](http://localhost:3000/users/account) and update user information and password
## Upgrade
* Replace old files with new ones by running in the Mailtrain folder `git pull origin master`
* Upgrade MySQL tables by running `npm run sql`
## Using environment variables

View file

@ -12,6 +12,7 @@ let sender = require('./services/sender');
let importer = require('./services/importer'); // eslint-disable-line global-require
let verpServer = require('./services/verp-server'); // eslint-disable-line global-require
let testServer = require('./services/test-server'); // eslint-disable-line global-require
let dbcheck = require('./lib/dbcheck');
let port = config.www.port;
let host = config.www.host;
@ -29,11 +30,19 @@ app.set('port', port);
let server = http.createServer(app);
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port, host);
// Check if database needs upgrading before starting the server
dbcheck(err => {
if (err) {
log.error('DB', err);
return process.exit(1);
}
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port, host);
});
server.on('error', err => {
if (err.syscall !== 'listen') {
@ -46,12 +55,10 @@ server.on('error', err => {
switch (err.code) {
case 'EACCES':
log.error('Express', '%s requires elevated privileges', bind);
process.exit(1);
break;
return process.exit(1);
case 'EADDRINUSE':
log.error('Express', '%s is already in use', bind);
process.exit(1);
break;
return process.exit(1);
default:
throw err;
}

View file

@ -2,15 +2,17 @@
let config = require('config');
let mysql = require('mysql');
let db = require('../../lib/db');
let spawn = require('child_process').spawn;
let settings = require('../../lib/models/settings');
let log = require('npmlog');
let fs = require('fs');
let pathlib = require('path');
let Handlebars = require('handlebars');
let meta = require('../meta.json');
log.level = 'verbose';
let mysqlConfig = {
multipleStatements: true
};
Object.keys(config.mysql).forEach(key => mysqlConfig[key] = config.mysql[key]);
let db = mysql.createPool(mysqlConfig);
function listTables(callback) {
db.getConnection((err, connection) => {
@ -49,21 +51,26 @@ function listTables(callback) {
});
}
function getSchemaVersion(callback) {
settings.list(['db_schema_version'], (err, configItems) => {
db.getConnection((err, connection) => {
if (err) {
return callback(err);
}
let dbSchemaVersion = Number(configItems.dbSchemaVersion) || 0;
connection.query('SELECT `value` FROM `settings` WHERE `key`=?', ['db_schema_version'], (err, rows) => {
connection.release();
if (err) {
return callback(err);
}
callback(null, dbSchemaVersion);
let dbSchemaVersion = rows && rows[0] && Number(rows[0].value) || 0;
callback(null, dbSchemaVersion);
});
});
}
function listUpdates(current, callback) {
current = current || 0;
fs.readdir(__dirname, (err, list) => {
fs.readdir(pathlib.join(__dirname, '..', 'setup', 'sql'), (err, list) => {
if (err) {
return callback(err);
}
@ -75,7 +82,7 @@ function listUpdates(current, callback) {
if (seq > current) {
updates.push({
seq: Number(seq),
path: pathlib.join(__dirname, row)
path: pathlib.join(__dirname, '..', 'setup', 'sql', row)
});
}
}
@ -96,21 +103,27 @@ function getSql(path, data, callback) {
}
function runInitial(callback) {
let path = pathlib.join(__dirname, process.env.FROM_START ? 'base.sql' : 'mailtrain.sql');
let fname = process.env.DB_FROM_START ? 'base.sql' : 'mailtrain.sql';
let path = pathlib.join(__dirname, '..', 'setup', 'sql', fname);
log.info('sql', 'Loading tables from %s', fname);
applyUpdate({
path
}, callback);
}
function runUpdates(callback) {
function runUpdates(callback, runCount) {
runCount = Number(runCount) || 0;
listTables((err, tables) => {
if (err) {
return callback(err);
}
if (!tables.settings) {
if (runCount) {
return callback(new Error('Settings table not found from database'));
}
log.info('sql', 'SQL not set up, initializing');
return runInitial(callback);
return runInitial(runUpdates.bind(null, callback, ++runCount));
}
getSchemaVersion((err, schemaVersion) => {
@ -118,6 +131,11 @@ function runUpdates(callback) {
return callback(err);
}
if (schemaVersion >= meta.schemaVersion) {
// nothing to do here, already updated
return callback(null, false);
}
listUpdates(schemaVersion, (err, updates) => {
if (err) {
return callback(err);
@ -159,26 +177,31 @@ function applyUpdate(update, callback) {
return callback(err);
}
let cmd = spawn(config.mysql.command || 'mysql', ['-h', config.mysql.host || 'localhost', '-P', config.mysql.port || 3306, '-u', config.mysql.user, '-p' + config.mysql.password, '-D', config.mysql.database]);
cmd.stdout.pipe(process.stdout);
cmd.stderr.pipe(process.stderr);
cmd.on('close', code => {
if (code) {
return callback(new Error('mysql command exited with code ' + code));
db.getConnection((err, connection) => {
if (err) {
return callback(err);
}
return callback(null, true);
connection.query(sql, err => {
connection.release();
if (err) {
return callback(err);
}
return callback(null, true);
});
});
cmd.stdin.end(sql);
});
}
runUpdates(err => {
if (err) {
log.error('sql', err);
process.exit(1);
}
log.info('sql', 'Database check completed');
process.exit(0);
});
module.exports = callback => {
runUpdates(err => {
if (err) {
return callback(err);
}
db.end(() => {
log.info('sql', 'Database check completed');
return callback(null, true);
});
});
};

3
meta.json Normal file
View file

@ -0,0 +1,3 @@
{
"schemaVersion": 1
}

View file

@ -6,8 +6,8 @@
"main": "index.js",
"scripts": {
"test": "grunt",
"start": "node setup/sql/sql.js && node index.js",
"sql": "node setup/sql/sql.js",
"start": "node index.js",
"sqlinit": "node setup/sql/init.js",
"sqldump": "node setup/sql/dump.js | sed -e '/^--.*$/d' > setup/sql/mailtrain.sql",
"sqldrop": "node setup/sql/drop.js"
},

14
setup/sql/init.js Normal file
View file

@ -0,0 +1,14 @@
'use strict';
let dbcheck = require('../../lib/dbcheck');
let log = require('npmlog');
log.level = 'verbose';
dbcheck(err => {
if (err) {
log.error('DB', err);
return process.exit(1);
}
return process.exit(0);
});

View file

@ -221,7 +221,7 @@ CREATE TABLE `users` (
KEY `check_reset` (`username`(191),`reset_token`,`reset_expire`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;
LOCK TABLES `settings` WRITE;
LOCK TABLES `users` WRITE;
INSERT INTO `users` VALUES (1,'admin','$2a$10$mzKU71G62evnGB2PvQA4k..Wf9jASk.c7a8zRMHh6qQVjYJ2r/g/K','admin@example.com',NULL,NULL,'2016-04-20 17:20:48');
UNLOCK TABLES;