diff --git a/.env.sample b/.env.sample new file mode 100644 index 0000000000..4b3f0019bb --- /dev/null +++ b/.env.sample @@ -0,0 +1,27 @@ + +# Server Config +PORT=8080 +HTTPS=1 + +# database configuration +DB_HOST='localhost' +DB_USER='YOUR_USERNAME' +DB_PASS='YOUR_PASSWORD' +DB_NAME='bhima' + +# session variables +SESS_SECRET='XopEn BlowFISH' +SESS_RESAVE=0 +SESS_SAVE_UNINITIALIZED=0 +SESS_REAP_INTERVAL=-1 +SESS_UNSET='destroy' + +# log level +LOG_LEVEL='none' + +# uploads +UPLOAD_FOLDER='client/upload' + +# SSL/TLS config +TLS_KEY='server/config/keys/server.key' +TLS_CERT='server/config/keys/server.crt' diff --git a/.gitignore b/.gitignore index 7d2a132e94..2850e9fae5 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ # ignore npm/node modules node_modules/ .npm-debug.log +npm-debug.log # ignore bower installation target client/vendor/ @@ -25,3 +26,6 @@ bin/* # ignore the upload folder client/src/upload/ +# ignore configuration +.env.production +.env.development diff --git a/.travis.yml b/.travis.yml index ca943cc014..97116b057e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ node_js: - "0.10" before_script: - - npm install -g gulp bower + - npm install -g gulp bower mocha - bower install - mysql -u root -e "CREATE DATABASE bhima;" - mysql -u root -e "CREATE USER 'bhima'@'localhost' IDENTIFIED BY 'HISCongo2013';" @@ -22,5 +22,10 @@ cache: - node_modules/ - client/vendor +# set the clone depth to a low value +git: + depth: 3 + +# make sure we aren't caching verbose output from NPM before_cache: - rm -f npm-debug.log diff --git a/client/src/partials/patients/search/search.css b/client/src/partials/patients/search/search.css index 91b39d9c5b..ea6fc216ff 100644 --- a/client/src/partials/patients/search/search.css +++ b/client/src/partials/patients/search/search.css @@ -9,5 +9,5 @@ /*FIXME*/ .hack-left { - margin-left: -15px; !important + margin-left: -15px !important; } diff --git a/client/src/partials/receipts/receipts.css b/client/src/partials/receipts/receipts.css index 9f54bebc38..482b78473a 100644 --- a/client/src/partials/receipts/receipts.css +++ b/client/src/partials/receipts/receipts.css @@ -1,5 +1,5 @@ .invoiceTable { - border none; + border: none; width: 100%; } diff --git a/gulpfile.js b/gulpfile.js index 730ee3bded..42c261e656 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -9,7 +9,8 @@ var gulp = require('gulp'), gulpif = require('gulp-if'), concat = require('gulp-concat'), uglify = require('gulp-uglify'), - minifycss = require('gulp-minify-css'), + cssnano = require('gulp-cssnano'), + cssnano = require('gulp-cssnano'), jshint = require('gulp-jshint'), flatten = require('gulp-flatten'), spawn = require('child_process').spawn, @@ -61,7 +62,7 @@ var paths = { }, server : { javascript : ['server/*.js', 'server/**/*.js'], - files : ['server/*', 'server/**/*'], + files : ['server/*', 'server/**/*', '.env.*'], plugins : ['plugins/*', 'plugins/**/*'], unittest : [] } @@ -76,7 +77,7 @@ var paths = { * and writes them to the /bin/client/ folder. There are tasks to do * the following: * - [client-lint-js] lint the client javascript code (via jshint) - * - [client-minify-css] minify (via minify css) the css + * - [client-minify-css] minify (via css nano) the css * - [client-minify-js] minify (via uglify) the clientside js code * - [client-lint-i18n] compares translation files for missing values * - [client-mv-static] moves the static files (html, img) to the client @@ -89,11 +90,6 @@ var paths = { * - [client-watch] watch the client/src/ for changes and run the build */ -// removes files with del, and continues -gulp.task('client-clean', function (cb) { - rimraf(CLIENT_FOLDER, cb); -}); - // run jshint on the client javascript code gulp.task('client-lint-js', function () { return gulp.src(paths.client.javascript.concat(paths.client.excludeLint)) @@ -115,7 +111,7 @@ gulp.task('client-minify-js', function () { // writes output to style.min.css gulp.task('client-minify-css', function () { return gulp.src(paths.client.css) - .pipe(minifycss()) + .pipe(cssnano()) .pipe(concat('style.min.css')) .pipe(gulp.dest(CLIENT_FOLDER + 'css/')); }); @@ -197,13 +193,13 @@ gulp.task('notify-lint-process', function () { }); // builds the client with all the options available -gulp.task('build-client', ['client-clean'], function () { +gulp.task('build-client', function () { gulp.start('client-minify-js', 'client-minify-css', 'client-mv-vendor', 'client-vendor-build-slickgrid', 'client-vendor-build-bootstrap', 'client-mv-static', 'notify-lint-process'); }); // Lint client code seperately from build process // TODO Processes for linting server code - requires uncategorised commit update -gulp.task('lint', ['client-clean'], function () { +gulp.task('lint', function () { gulp.start('client-lint-js', 'client-lint-i18n'); }); @@ -224,12 +220,6 @@ gulp.task('lint', ['client-clean'], function () { * To run all of the above, run the gulp task `gulp build-server`. */ -gulp.task('server-clean', function (cb) { - rimraf(SERVER_FOLDER, function () { - rimraf(PLUGIN_FOLDER, cb); - }); -}); - // run jshint on all server javascript files gulp.task('server-lint-js', function () { return gulp.src(paths.server.javascript) @@ -281,18 +271,13 @@ gulp.task('client-test-e2e', function () { * The following tasks will run unit tests on the bhima server using gulp-mocha */ -// ensure that the server actually runs -gulp.task('server-test-run', function () { - spawn('node', [path.join(SERVER_FOLDER, 'app.js')], {stdio: 'inherit'}); -}); - /* -------------------------------------------------------------------------- */ gulp.task('clean', function (cb) { rimraf('./bin/', cb); }); -gulp.task('build', function () { +gulp.task('build', ['clean'], function () { gulp.start('build-client', 'build-server'); }); @@ -301,6 +286,6 @@ gulp.task('test', ['build'], function () { }); // run the build-client and build-server tasks when no arguments -gulp.task('default', [], function () { +gulp.task('default', ['clean'], function () { gulp.start('build-client', 'build-server'); }); diff --git a/package.json b/package.json index e64d7155da..05cc7f1492 100644 --- a/package.json +++ b/package.json @@ -5,12 +5,13 @@ "main": "server/app.js", "scripts": { "test": "./sh/travis.sh", - "app": "gulp build && cd bin && node server/app.js", - "dev": "gulp build && cd bin && node server/app.js development" + "app": "gulp build && cd bin && NODE_ENV=production node server/app.js", + "dev": "gulp build && cd bin && NODE_ENV=development node server/app.js", + "start": "gulp build && cd bin && NODE_ENV=production node server/app.js" }, "repository": { "type": "git", - "url": "https://github.com/IMA-WorldHealth/bhima.git" + "url": "https://github.com/IMA-WorldHealth/bhima-2.X.git" }, "keywords": [ "bhima", @@ -19,8 +20,8 @@ "hospital information system", "rural accounting" ], - "author": "IMA World Health Web Development Team", - "license": "GPLv2", + "author": "IMA World Health", + "license": "GPL-2.0", "bugs": { "url": "https://github.com/IMA-WorldHealth/bhima/issues" }, @@ -28,17 +29,16 @@ "body-parser": "^1.14.1", "compression": "^1.5.2", "connect-multiparty": "^2.0.0", - "csv": "^0.4.1", "dot": "^1.0.3", + "dotenv": "^1.2.0", "express": "^4.13.3", "express-session": "^1.11.3", - "fast-csv": "^0.6.0", - "later": "^1.2.0", + "fast-csv": "1.0.0", "morgan": "^1.6.1", "mysql": "^2.9.0", "numeral": "^1.5.3", "q": "~1.4.1", - "session-file-store": "0.0.22", + "session-file-store": "0.0.24", "wkhtmltopdf": "^0.1.5" }, "devDependencies": { @@ -47,12 +47,12 @@ "chai-http": "^1.0.0", "gulp": "^3.9.0", "gulp-concat": "^2.6.0", + "gulp-cssnano": "^2.1.0", "gulp-flatten": "^0.2.0", "gulp-if": "^2.0.0", "gulp-iife": "^0.2.4", "gulp-jshint": "^2.0.0", "gulp-less": "^3.0.3", - "gulp-minify-css": "^1.2.1", "gulp-mocha": "^2.1.3", "gulp-protractor": "^2.1.0", "gulp-uglify": "^1.4.2", diff --git a/server/app.js b/server/app.js index 7df17e028a..2b918f1e07 100644 --- a/server/app.js +++ b/server/app.js @@ -2,18 +2,21 @@ var express = require('express'), https = require('https'), fs = require('fs'); -// Temporary switch between production and development -// TODO -- in the future, this should be done via environmental -// variablesk -var MODE = (process.argv[2]) ? process.argv[2] : 'production'; -var config = require('./config/environment/' + MODE); -process.env.NODE_ENV = MODE; // allow other modules to check the environment +// switch for environmental variables +var env = (process.env.NODE_ENV === 'production') ? + 'server/.env.production' : + 'server/.env.development'; + +// load the environmnetal variables into process +require('dotenv').config({ path : env }); // SSL credentials -var privateKey = fs.readFileSync(config.tls.key, 'utf8'); -var certificate = fs.readFileSync(config.tls.cert, 'utf8'); +var privateKey = fs.readFileSync(process.env.TLS_KEY, 'utf8'); +var certificate = fs.readFileSync(process.env.TLS_CERT, 'utf8'); var credentials = { key : privateKey, cert : certificate }; -var db = require('./lib/db').initialise(config.db); + +// configure the database for use within the application +require('./lib/db').initialise(); var app = express(); @@ -27,15 +30,16 @@ require('./config/routes').configure(app); require('./config/express').errorHandling(app); // Load and configure plugins -require('./lib/pluginManager')(app, config.plugins); +// TODO - find a better way to load in a list of plugins +require('./lib/pluginManager')(app, []); // start the server -https.createServer(credentials, app).listen(config.port, logApplicationStart); +https.createServer(credentials, app).listen(process.env.PORT, logApplicationStart); process.on('uncaughtException', forceExit); function logApplicationStart() { - console.log('[app] BHIMA server started in mode %s on port %s.', MODE.toUpperCase(), config.port); + console.log('[app] BHIMA server started in mode %s on port %s.', process.env.NODE_ENV.toUpperCase(), process.env.PORT); } function forceExit(err) { diff --git a/server/config/environment/development.js b/server/config/environment/development.js deleted file mode 100644 index 29f805cc68..0000000000 --- a/server/config/environment/development.js +++ /dev/null @@ -1,32 +0,0 @@ -// Export the same configuration object for use throughout modules -var development = { - 'port' : 8080, - 'db' : { - 'host': 'localhost', - 'user': 'bhima', - 'password': 'HISCongo2013', - 'database': 'bhima_test' - }, - 'logLevel' : 'none', - 'session' : { - 'secret' : 'xopen blowfish', - 'resave' : false, - 'saveUninitialized' : false, - 'reapInterval' : -1 // disables session reaping - }, - 'uploadFolder' : 'client/upload/', - - /* Configuration for plugins - * Each plugin REQUIRES two properties: - * name - Stored as the name of the plugin. Write in camelCase. - * script - The script relative to the plugins directory - *e.g : {'name' : 'mail', 'script' : '/../../plugins/mail/index.js'} - */ - 'plugins' : [], - 'tls' : { - 'key' : 'server/config/keys/server.key', - 'cert' : 'server/config/keys/server.crt' - } -}; - -module.exports = development; diff --git a/server/config/environment/production.js b/server/config/environment/production.js deleted file mode 100644 index 71c244703f..0000000000 --- a/server/config/environment/production.js +++ /dev/null @@ -1,34 +0,0 @@ -// Export the same configuration object for use throughout modules -var production = { - 'port' : 8080, - 'db' : { - 'host': 'localhost', - 'user': 'bhima', - 'password': 'HISCongo2013', - 'database': 'bhima' - }, - 'logLevel' : 'common', - 'session' : { - 'secret' : 'xopen blowfish', - 'resave' : false, - 'saveUninitialized' : false, - 'reapInterval' : -1 // disables session reaping - }, - 'uploadFolder' : 'client/upload/', - - /* Configuration for plugins - * Each plugin REQUIRES two properties: - * name - Stored as the name of the plugin. Write in camelCase. - * script - The script relative to the plugins directory - *e.g : {'name' : 'mail', 'script' : '/../../plugins/mail/index.js'} - */ - 'plugins' : [ - //{'name' : 'mail', 'script' : '/../../plugins/mail/index.js'} - ], - 'tls' : { - 'key' : 'server/config/keys/server.key', - 'cert' : 'server/config/keys/server.crt' - } -}; - -module.exports = production; diff --git a/server/config/express.js b/server/config/express.js index bbedd039f4..061dbf934e 100644 --- a/server/config/express.js +++ b/server/config/express.js @@ -9,12 +9,12 @@ var express = require('express'), morgan = require('morgan'), fs = require('fs'); -var cfg = require('../config/environment/' + process.env.NODE_ENV); var codes = require('../config/codes'); // Accept generic express instances (initialised in app.js) exports.configure = function configure(app) { 'use strict'; + console.log('[config/express] Configure express'); // middleware @@ -26,12 +26,12 @@ exports.configure = function configure(app) { // not interrupt sessions. app.use(session({ store : new FileStore({ - reapInterval : cfg.session.reapInterval, + reapInterval : Number(process.env.SESS_REAP_INTERVAL), }), - secret : cfg.session.secret, - saveUninitialized : cfg.session.saveUninitialized, - resave : cfg.session.resave, - unset : 'destroy', + secret : process.env.SESS_SECRET, + saveUninitialized : Boolean(process.env.SESS_SAVE_UNINITIALIZED), + resave : Boolean(process.env.SESS_RESAVE), + unset : process.env.SESS_UNSET, cookie : { secure : true } })); @@ -68,11 +68,11 @@ exports.configure = function configure(app) { // Uncomment if you want logs written to a file instead // of piped to standard out (default). //var logFile = fs.createWriteStream(__dirname + '/access.log', {flags : 'a'}); - //app.use(morgan('short', { stream : logFile })); + //app.use(morgan(process.env.LOG_LEVEL, { stream : logFile })); - // custom logLevel 'none' allows developers to turn off logging during tests - if (cfg.logLevel !== 'none') { - app.use(morgan(cfg.logLevel)); + // custom LOG_LEVEL 'none' turns off logging during tests + if (process.env.LOG_LEVEL !== 'none') { + app.use(morgan(process.env.LOG_LEVEL)); } // serve static files from a single location diff --git a/server/controllers/finance/budget.js b/server/controllers/finance/budget.js index 4e2525a673..b87b0436b2 100644 --- a/server/controllers/finance/budget.js +++ b/server/controllers/finance/budget.js @@ -1,16 +1,14 @@ /* Budget controller */ + 'use strict'; -var csv = require('fast-csv'), - fs = require('fs'), - q = require('q'); +var csv = require('fast-csv'), + fs = require('fs'), + q = require('q'); +var db = require('../../lib/db'); -// TODO this should be injected from the top -var config = require('../../config/environment/production'); -var db = require('../../lib/db'); - -var uploadDir = config.uploadFolder; +var uploadDir = process.env.UPLOAD_FOLDER; module.exports = { update : updateBudget, @@ -47,14 +45,14 @@ function uploadedFile(req, res, next) { .on('error', next); }); }); - } + } } function createBudget(csvArray, fiscal_year_id, period, next) { /** * Objectif : create a new budget via params given - * Params : + * Params : * - csvArray : an array of the budget csv file * - fiscal_year_id : the fiscal year id * - period : the period @@ -79,7 +77,7 @@ function createBudget(csvArray, fiscal_year_id, period, next) { function createAnnualBudget (periods) { /** * This function is responsible to create an annual budget - * the `periodLength` variable bellow is used in division of budget by period + * the `periodLength` variable bellow is used in division of budget by period */ var periodIds = periods; var periodLength = periodIds.length - 1 > 0 ? periodIds.length - 1 : 1; @@ -93,7 +91,7 @@ function createBudget(csvArray, fiscal_year_id, period, next) { function handleCSVArray(period_id, periodLength) { /** * This function is responsible to get budget from csvArray - * and insert these data into budget table in database + * and insert these data into budget table in database * the `periodLength` variable is used in divison of budget by period, and must be different to zero */ periodLength = (periodLength && periodLength > 0) ? periodLength : 1; diff --git a/server/lib/db.js b/server/lib/db.js index 579bed394b..b926abc767 100644 --- a/server/lib/db.js +++ b/server/lib/db.js @@ -1,6 +1,4 @@ -// /scripts/lib/database/db.js - -// Module: db.js +// /scripts/lib/db.js // TODO rewrite documentation - this module can now be required by any controller module throughout the application // TODO Seperate DB wrapper and DB methods - this module should just initialise a new DB instance @@ -11,30 +9,22 @@ // sharing connections between request sessions (also allowing for shraring a // transaction between unrelated components) -// The purpose of this module is managing client connections -// and disconnections to a variety of database management systems. -// All query formatting is expected to happen elsewhere. - var q = require('q'); +var mysql = require('mysql'); -var db, con, supportedDatabases, dbms; +var con; // Initiliase module on startup - create once and allow db to be required anywhere -function initialise(cfg) { +function initialise() { 'use strict'; - cfg = cfg || {}; - - // Select the system's database with this variable. - dbms = cfg.dbms || 'mysql'; - - // All supported dabases and their initializations - supportedDatabases = { - mysql : mysqlInit - }; - - // The database connection for all data interactions - con = supportedDatabases[dbms](cfg); + // configure MySQL via environmental variables + con = mysql.createPool({ + host : process.env.DB_HOST, + user : process.env.DB_USER, + password : process.env.DB_PASS, + database : process.env.DB_NAME + }); // FIXME reset all logged in users on event of server crashing / terminating - this // should be removed/ implemented into the error/logging module before shipping @@ -72,10 +62,6 @@ function execute(sql, callback) { }); } -function getSupportedDatabases() { - return Object.keys(supportedDatabases); -} - function execTransaction(queryList) { var deferred = q.defer(); var transactionPromises = []; @@ -166,57 +152,30 @@ function transaction() { return self; } -function mysqlInit (config) { +function flushUsers(handle) { 'use strict'; - var db = require('mysql'); - return db.createPool(config); -} - -function flushUsers (db_con) { - 'use strict'; - var permissions, reset, strict; + var permissions, reset; // Disable safe mode #420blazeit - // TODO This should be optionally set as a flag - and reported (logged) + // TODO This should be optionally set as a flag - and reported (logged) permissions = 'SET SQL_SAFE_UPDATES = 0;'; reset = 'UPDATE `user` SET user.active = 0 WHERE user.active = 1;'; - db_con.getConnection(function (err, con) { + handle.getConnection(function (err, con) { if (err) { throw err; } con.query(permissions, function (err) { if (err) { throw err; } con.release(); - db_con.getConnection(function (err, con) { + handle.getConnection(function (err, con) { if (err) { throw err; } con.query(reset, function (err) { if (err) { throw err; } - }); }); }); }); } -/* -// TODO: impliment PostgreSQL support -function postgresInit(config) { - var db = require('pg'); - return true; -} - -// TODO: impliment Firebird support -function firebirdInit(config) { - var db = require('node-firebird'); - return true; -} - -// TODO: impliment sqlite support -function sqliteInit(config) { - var db = require('sqlite3'); - return true; -} -*/ - // Uses an already existing connection to query the database, returning a promise function queryConnection(connection, sql, params) { var deferred = q.defer(); @@ -243,5 +202,3 @@ module.exports = { sanitize: sanitize, // FIXME: is this even used? escape: sanitize }; - -//module.exports = db;