diff --git a/Dockerfile b/Dockerfile index 1f71686e5..e312007ca 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,16 +1,15 @@ FROM node:0.10-slim +RUN npm install -g mocha +RUN npm install -g istanbul +RUN npm install -g gulp COPY ./package.json /src/package.json RUN cd /src && npm install COPY ./ /src -RUN npm install -g mocha -RUN npm install -g istanbul -RUN npm install -g gulp WORKDIR /src #ENV DEBUG=* -EXPOSE 8080 5222 CMD ["npm", "start"] diff --git a/Dockerfile_prod b/Dockerfile_prod new file mode 100644 index 000000000..49e3ae6a7 --- /dev/null +++ b/Dockerfile_prod @@ -0,0 +1,12 @@ +FROM node:0.10-slim + +COPY ./package.json /src/package.json +RUN cd /src && npm install +COPY ./ /src + +WORKDIR /src +#ENV DEBUG=* + + +CMD ["npm", "start"] + diff --git a/README.md b/README.md index 8e485467e..da0c10594 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,12 @@ ![Let's Chat Greylock](http://i.imgur.com/0a3l5VF.png) -#test1 -#test2 ![Screenshot](http://i.imgur.com/C4uMD67.png) -Test Test A self-hosted chat app for small teams or big Gal by [Security Compass][seccom]. [![Build Status](https://travis-ci.org/sdelements/lets-chat.svg?branch=master)](https://travis-ci.org/sdelements/lets-chat) [![Dependency Status](https://david-dm.org/sdelements/lets-chat.svg)](https://david-dm.org/sdelements/lets-chat) [![devDependency Status](https://david-dm.org/sdelements/lets-chat/dev-status.svg)](https://david-dm.org/sdelements/lets-chat#info=devDependencies) + ## Features and Stuff * BYOS (bring your own server) diff --git a/app.js b/app.js index 0893b113f..42f861ecf 100644 --- a/app.js +++ b/app.js @@ -8,40 +8,40 @@ process.title = 'letschat'; require('colors'); -var _ = require('lodash'), - path = require('path'), - fs = require('fs'), - express = require('express.oi'), - i18n = require('i18n'), - bodyParser = require('body-parser'), +var _ = require('lodash'), + path = require('path'), + fs = require('fs'), + express = require('express.oi'), + i18n = require('i18n'), + bodyParser = require('body-parser'), cookieParser = require('cookie-parser'), - compression = require('compression'), - helmet = require('helmet'), - http = require('http'), - nunjucks = require('nunjucks'), - mongoose = require('mongoose'), - migroose = require('./migroose'), + compression = require('compression'), + helmet = require('helmet'), + http = require('http'), + nunjucks = require('nunjucks'), + mongoose = require('mongoose'), + migroose = require('./migroose'), connectMongo = require('connect-mongo'), - all = require('require-tree'), - psjon = require('./package.json'), - settings = require('./app/config'), - auth = require('./app/auth/index'), - core = require('./app/core/index'); - -var MongoStore = connectMongo(express.session), - httpEnabled = settings.http && settings.http.enable, + all = require('require-tree'), + psjon = require('./package.json'), + settings = require('./app/config'), + auth = require('./app/auth/index'), + core = require('./app/core/index'); + +var MongoStore = connectMongo(express.session), + httpEnabled = settings.http && settings.http.enable, httpsEnabled = settings.https && settings.https.enable, - models = all(path.resolve('./app/models')), - middlewares = all(path.resolve('./app/middlewares')), - controllers = all(path.resolve('./app/controllers')), + models = all(path.resolve('./app/models')), + middlewares = all(path.resolve('./app/middlewares')), + controllers = all(path.resolve('./app/controllers')), app; // // express.oi Setup // if (httpsEnabled) { - app = express().https({ - key: fs.readFileSync(settings.https.key), + app = express().https({ + key: fs.readFileSync(settings.https.key), cert: fs.readFileSync(settings.https.cert) }).io(); } else { @@ -56,17 +56,17 @@ if (settings.env === 'production') { // Session var sessionStore = new MongoStore({ - url: settings.database.uri, + url: settings.database.uri, autoReconnect: true }); // Session var session = { - key: 'connect.sid', - secret: settings.secrets.cookie, - store: sessionStore, - cookie: { secure: httpsEnabled }, - resave: false, + key: 'connect.sid', + secret: settings.secrets.cookie, + store: sessionStore, + cookie: { secure: httpsEnabled }, + resave: false, saveUninitialized: true }; @@ -85,32 +85,32 @@ app.use(helmet.ieNoOpen()); app.use(helmet.noSniff()); app.use(helmet.xssFilter()); app.use(helmet.hsts({ - maxAge: 31536000, + maxAge: 31536000, includeSubdomains: true, - force: httpsEnabled, - preload: true + force: httpsEnabled, + preload: true })); app.use(helmet.contentSecurityPolicy({ defaultSrc: ['\'none\''], connectSrc: ['*'], - scriptSrc: ['\'self\'', '\'unsafe-eval\''], - styleSrc: ['\'self\'', 'fonts.googleapis.com', '\'unsafe-inline\''], - fontSrc: ['\'self\'', 'fonts.gstatic.com'], - mediaSrc: ['\'self\''], - objectSrc: ['\'self\''], - imgSrc: ['*'] + scriptSrc: ['\'self\'', '\'unsafe-eval\''], + styleSrc: ['\'self\'', 'fonts.googleapis.com', '\'unsafe-inline\''], + fontSrc: ['\'self\'', 'fonts.gstatic.com'], + mediaSrc: ['\'self\''], + objectSrc: ['\'self\''], + imgSrc: ['*'] })); var bundles = {}; app.use(require('connect-assets')({ - paths: [ + paths: [ 'media/js', 'media/less' ], - helperContext: bundles, - build: settings.env === 'production', + helperContext: bundles, + build: settings.env === 'production', fingerprinting: settings.env === 'production', - servePath: 'media/dist' + servePath: 'media/dist' })); // Public @@ -121,24 +121,24 @@ app.use('/media', express.static(__dirname + '/media', { // Templates var nun = nunjucks.configure('templates', { autoescape: true, - express: app, - tags: { - blockStart: '<%', - blockEnd: '%>', + express: app, + tags: { + blockStart: '<%', + blockEnd: '%>', variableStart: '<$', - variableEnd: '$>', - commentStart: '<#', - commentEnd: '#>' + variableEnd: '$>', + commentStart: '<#', + commentEnd: '#>' } }); function wrapBundler(func) { // This method ensures all assets paths start with "./" // Making them relative, and not absolute - return function() { + return function () { return func.apply(func, arguments) - .replace(/href="\//g, 'href="./') - .replace(/src="\//g, 'src="./'); + .replace(/href="\//g, 'href="./') + .replace(/src="\//g, 'src="./'); }; } @@ -148,7 +148,7 @@ nun.addGlobal('text_search', false); // i18n i18n.configure({ - directory: __dirname + '/locales', + directory: __dirname + '/locales', defaultLocale: settings.i18n && settings.i18n.locale || 'en' }); app.use(i18n.init); @@ -160,7 +160,7 @@ app.use(bodyParser.urlencoded({ })); // IE header -app.use(function(req, res, next) { +app.use(function (req, res, next) { res.setHeader('X-UA-Compatible', 'IE=Edge,chrome=1'); next(); }); @@ -168,13 +168,13 @@ app.use(function(req, res, next) { // // Controllers // -_.each(controllers, function(controller) { +_.each(controllers, function (controller) { controller.apply({ - app: app, - core: core, - settings: settings, + app: app, + core: core, + settings: settings, middlewares: middlewares, - models: models, + models: models, controllers: controllers }); }); @@ -187,7 +187,7 @@ mongoose.connection.on('error', function (err) { throw new Error(err); }); -mongoose.connection.on('disconnected', function() { +mongoose.connection.on('disconnected', function () { throw new Error('Could not connect to database'); }); @@ -197,17 +197,16 @@ mongoose.connection.on('disconnected', function() { function startApp() { var port = httpsEnabled && settings.https.port || - httpEnabled && settings.http.port; + httpEnabled && settings.http.port; var host = httpsEnabled && settings.https.host || - httpEnabled && settings.http.host || '0.0.0.0'; - + httpEnabled && settings.http.host || '0.0.0.0'; if (httpsEnabled && httpEnabled) { // Create an HTTP -> HTTPS redirect server var redirectServer = express(); - redirectServer.get('*', function(req, res) { + redirectServer.get('*', function (req, res) { var urlPort = port === 80 ? '' : ':' + port; res.redirect('https://' + req.hostname + urlPort + req.path); }); @@ -247,11 +246,11 @@ function checkForMongoTextSearch() { return; } - if(version[0] < 2) { + if (version[0] < 2) { return; } - if(version[0] === '2' && version[1] < 6) { + if (version[0] === '2' && version[1] < 6) { return; } @@ -259,19 +258,32 @@ function checkForMongoTextSearch() { }); } -mongoose.connect(settings.database.uri, function(err) { +var connectionTries = 0; + +function handleMongoConnectionState(err) { + if (err) { - throw err; + connectionTries++; + if (connectionTries < 3) { + console.log('Error connecting to database (will retry in 2 seconds): ' + err.toString()); + setTimeout(tryConnect, 2000); + return; + } + else { + throw err; + } } checkForMongoTextSearch(); - migroose.needsMigration(function(err, migrationRequired) { + migroose.needsMigration(function (err, migrationRequired) { + if (err) { console.error(err); } else if (migrationRequired) { + console.log('Database migration required'.red); console.log('Ensure you backup your database first.'); console.log(''); @@ -281,7 +293,16 @@ mongoose.connect(settings.database.uri, function(err) { return process.exit(); } - + + console.log('Starting app.'); startApp(); }); -}); + +} + +function tryConnect() { + console.log('Connecting to database...'); + mongoose.connect(settings.database.uri, handleMongoConnectionState); +} + +tryConnect(); diff --git a/codefresh.yml b/codefresh.yml new file mode 100644 index 000000000..982009f1b --- /dev/null +++ b/codefresh.yml @@ -0,0 +1,48 @@ +version: '1.0' +steps: + + build_step: + title: Build + type: build + dockerfile: Dockerfile + image_name: containers101/demochat + tag: ${{CF_BRANCH}} + + unit_tests: + title: Unit Tests + image: ${{build_step}} + fail_fast: false + #working-directory : ${{initial-clone}} + commands: + #- npm install + #- npm install -g gulp + - npm test + + push_to_registry: + title: Push To Registry + type: push + candidate: ${{build_step}} + tag: ${{CF_BRANCH}} + + #demo-chat-integration: + # type: composition + # composition: letschat + # composition-candidates: + # main: + # image: nhoag/curl + # command: bash -c "sleep 20 && curl http://demochat:5000/" | echo 'works' + + deploy_to_ecs: + title: Deploy Container to ECS + image: codefresh/cf-deploy-ecs + commands: + - cfecs-update --image-name containers101/demochat --image-tag ${{CF_BRANCH}} eu-west-1 demochat-production demochat-service + environment: + - AWS_ACCESS_KEY_ID=AKIAISRGV2FLUTSZJPVQ + #${{AWS_ACCESS_KEY_ID}} + - AWS_SECRET_ACCESS_KEY=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 + #${{AWS_SECRET_ACCESS_KEY}} + when: + branch: + only: + - master diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..2e646c5aa --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,9 @@ +version: '2' +services: + web: + build: . + ports: + - 5000:5000 + mongo: + image: mongo + diff --git a/package.json b/package.json index cac06b685..64296ce00 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "helmet": "^0.11.0", "i18n": "^0.5.0", "js-yaml": "^3.4.2", - "less": "^2.5.1", + "less": "2.6.1", "lodash": "^3.10.1", "md5": "^2.0.0", "migroose": "^0.5.0", diff --git a/templates/login.html b/templates/login.html index 8548c7437..ea19b1b8a 100644 --- a/templates/login.html +++ b/templates/login.html @@ -14,7 +14,7 @@ <% block body %>
-

Let's Chat

+

Let's Chat!!