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` 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 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 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. 4. Run the server `NODE_ENV=production npm start`
5. Run the server `NODE_ENV=production npm start` 5. Open [http://localhost:3000/](http://localhost:3000/)
6. Open [http://localhost:3000/](http://localhost:3000/) 6. Authenticate as `admin`:`test`
7. 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/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
9. Navigate to [http://localhost:3000/users/account](http://localhost:3000/users/account) and update user information and password
## Upgrade ## Upgrade
* Replace old files with new ones by running in the Mailtrain folder `git pull origin master` * 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 ## 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 importer = require('./services/importer'); // eslint-disable-line global-require
let verpServer = require('./services/verp-server'); // 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 testServer = require('./services/test-server'); // eslint-disable-line global-require
let dbcheck = require('./lib/dbcheck');
let port = config.www.port; let port = config.www.port;
let host = config.www.host; let host = config.www.host;
@ -29,11 +30,19 @@ app.set('port', port);
let server = http.createServer(app); 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 => { server.on('error', err => {
if (err.syscall !== 'listen') { if (err.syscall !== 'listen') {
@ -46,12 +55,10 @@ server.on('error', err => {
switch (err.code) { switch (err.code) {
case 'EACCES': case 'EACCES':
log.error('Express', '%s requires elevated privileges', bind); log.error('Express', '%s requires elevated privileges', bind);
process.exit(1); return process.exit(1);
break;
case 'EADDRINUSE': case 'EADDRINUSE':
log.error('Express', '%s is already in use', bind); log.error('Express', '%s is already in use', bind);
process.exit(1); return process.exit(1);
break;
default: default:
throw err; throw err;
} }

View file

@ -2,15 +2,17 @@
let config = require('config'); let config = require('config');
let mysql = require('mysql'); 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 log = require('npmlog');
let fs = require('fs'); let fs = require('fs');
let pathlib = require('path'); let pathlib = require('path');
let Handlebars = require('handlebars'); 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) { function listTables(callback) {
db.getConnection((err, connection) => { db.getConnection((err, connection) => {
@ -49,21 +51,26 @@ function listTables(callback) {
}); });
} }
function getSchemaVersion(callback) { function getSchemaVersion(callback) {
settings.list(['db_schema_version'], (err, configItems) => { db.getConnection((err, connection) => {
if (err) { if (err) {
return callback(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) { function listUpdates(current, callback) {
current = current || 0; current = current || 0;
fs.readdir(__dirname, (err, list) => { fs.readdir(pathlib.join(__dirname, '..', 'setup', 'sql'), (err, list) => {
if (err) { if (err) {
return callback(err); return callback(err);
} }
@ -75,7 +82,7 @@ function listUpdates(current, callback) {
if (seq > current) { if (seq > current) {
updates.push({ updates.push({
seq: Number(seq), 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) { 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({ applyUpdate({
path path
}, callback); }, callback);
} }
function runUpdates(callback) { function runUpdates(callback, runCount) {
runCount = Number(runCount) || 0;
listTables((err, tables) => { listTables((err, tables) => {
if (err) { if (err) {
return callback(err); return callback(err);
} }
if (!tables.settings) { if (!tables.settings) {
if (runCount) {
return callback(new Error('Settings table not found from database'));
}
log.info('sql', 'SQL not set up, initializing'); log.info('sql', 'SQL not set up, initializing');
return runInitial(callback); return runInitial(runUpdates.bind(null, callback, ++runCount));
} }
getSchemaVersion((err, schemaVersion) => { getSchemaVersion((err, schemaVersion) => {
@ -118,6 +131,11 @@ function runUpdates(callback) {
return callback(err); return callback(err);
} }
if (schemaVersion >= meta.schemaVersion) {
// nothing to do here, already updated
return callback(null, false);
}
listUpdates(schemaVersion, (err, updates) => { listUpdates(schemaVersion, (err, updates) => {
if (err) { if (err) {
return callback(err); return callback(err);
@ -159,26 +177,31 @@ function applyUpdate(update, callback) {
return callback(err); 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]); db.getConnection((err, connection) => {
if (err) {
cmd.stdout.pipe(process.stdout); return callback(err);
cmd.stderr.pipe(process.stderr);
cmd.on('close', code => {
if (code) {
return callback(new Error('mysql command exited with code ' + code));
} }
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 => { module.exports = callback => {
if (err) { runUpdates(err => {
log.error('sql', err); if (err) {
process.exit(1); return callback(err);
} }
log.info('sql', 'Database check completed'); db.end(() => {
process.exit(0); 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", "main": "index.js",
"scripts": { "scripts": {
"test": "grunt", "test": "grunt",
"start": "node setup/sql/sql.js && node index.js", "start": "node index.js",
"sql": "node setup/sql/sql.js", "sqlinit": "node setup/sql/init.js",
"sqldump": "node setup/sql/dump.js | sed -e '/^--.*$/d' > setup/sql/mailtrain.sql", "sqldump": "node setup/sql/dump.js | sed -e '/^--.*$/d' > setup/sql/mailtrain.sql",
"sqldrop": "node setup/sql/drop.js" "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`) KEY `check_reset` (`username`(191),`reset_token`,`reset_expire`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4; ) 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'); 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; UNLOCK TABLES;