From 64cea269a9bcc99a5ae5c806a48c271085091f22 Mon Sep 17 00:00:00 2001 From: DC* Date: Thu, 14 Aug 2014 00:08:12 -0300 Subject: [PATCH 01/13] Initial heroku integration --- .gitignore | 2 ++ Procfile | 1 + README.md | 6 ++++++ db/seed/initial_tasks.js | 22 ++++++++++++---------- package.json | 9 +++++++++ 5 files changed, 30 insertions(+), 10 deletions(-) create mode 100644 Procfile diff --git a/.gitignore b/.gitignore index 11f1994..88206e8 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,5 @@ results npm-debug.log node_modules + +*.sqlite diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..489b270 --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: node server.js diff --git a/README.md b/README.md index e539c5a..2562ddc 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,12 @@ An open-source [Workflowy](http://workflowy.com) clone. * Run migrations and initialize the database: `sequelize-cli -m --config config/database.json` and `node db/seed/initial_tasks.js` * `node server.js` +##Heroku deploy + +* heroku create --stack cedar +* git remote add heroku URL from above +* git push heroku master + ##Controls * UP & DOWN: navigate through tasks diff --git a/db/seed/initial_tasks.js b/db/seed/initial_tasks.js index d5a39a6..cb740c0 100644 --- a/db/seed/initial_tasks.js +++ b/db/seed/initial_tasks.js @@ -1,14 +1,16 @@ var config = require('config'), - orm = require('../../orm').configure(config.get('database')), - Tasks = require('../../db/models/task').instance(orm); + orm = require('../../orm').configure(config.get('database')), + Tasks = require('../../db/models/task').instance(orm); -Tasks.bulkCreate([ - {content: 'Welcome to HackFlowy!', isCompleted: false}, - {content: 'An open-source WorkFlowy clone', isCompleted: false}, - {content: 'Built using Backbone + Socket.IO', isCompleted: false}, - {content: 'I pulled this together in a few hours to learn Backbone', isCompleted: false}, - {content: 'Feel free to try it out and hack on it', isCompleted: false}, - {content: 'Good Luck!', isCompleted: false} -]); +Tasks.destroy().success(function() { + Tasks.bulkCreate([ + {content: 'Welcome to HackFlowy!', isCompleted: false}, + {content: 'An open-source WorkFlowy clone', isCompleted: false}, + {content: 'Built using Backbone + Socket.IO', isCompleted: false}, + {content: 'I pulled this together in a few hours to learn Backbone', isCompleted: false}, + {content: 'Feel free to try it out and hack on it', isCompleted: false}, + {content: 'Good Luck!', isCompleted: false} + ]); +}); orm.sync(); diff --git a/package.json b/package.json index 6a172b5..5a8b8b4 100644 --- a/package.json +++ b/package.json @@ -10,5 +10,14 @@ "sequelize": "~1.7.9", "socket.io": "~1.0.6", "config": "~1.0.2" + }, + "engines": { + "node": "0.10.x" + }, + "scripts": { + "start": "node server.js", + "db:migrate": "./node_modules/sequelize/bin/sequelize -m --config config/database.json", + "db:seed": "node db/seed/initial_tasks.js", + "postinstall": "npm run db:migrate && npm run db:seed" } } From b4d3eab02c88c941e506153545335cae07f0cb36 Mon Sep 17 00:00:00 2001 From: DC* Date: Thu, 14 Aug 2014 22:05:54 -0300 Subject: [PATCH 02/13] Heroku integration with foreman Add foreman configuration for Heroku: - To test production configuration execute NODE_ENV=production foreman start - Database connection configuration now handles "use_env_variable" which should be defined as a environment variable such as "DATABASE_URL" with a proper database URI connection string, ie: postgres://user:password@db.example.com:5432/database - Added package.json configuration to run migrations and db seed after `npm install` Update frontend models to match sequelize definition. Update views to work according to the changes above. Handling isCompleted and parentId apropiately for PostgreSQL databases --- config/database.json | 6 +---- config/production.json | 9 +------- orm.js | 19 ++++++++++++++++ package.json | 6 +++-- public/javascripts/models/task.js | 10 ++++---- public/javascripts/views/list.js | 4 ++-- public/javascripts/views/task.js | 38 +++++++++++++++---------------- public/templates/task.html | 4 ++-- server.js | 4 ++-- 9 files changed, 55 insertions(+), 45 deletions(-) diff --git a/config/database.json b/config/database.json index bd795be..8f6ec63 100644 --- a/config/database.json +++ b/config/database.json @@ -8,10 +8,6 @@ "storage": "db/test.sqlite" }, "production": { - "username": "postgres", - "password": "", - "database": "hackflowy", - "host" : "localhost", - "dialect" : "postgres" + "use_env_variable": "DATABASE_URL" } } diff --git a/config/production.json b/config/production.json index 4e7dd98..2abad20 100644 --- a/config/production.json +++ b/config/production.json @@ -1,13 +1,6 @@ { "port": 3000, "database": { - "username": "postgres", - "password": "", - "database": "hackflowy", - "options" : { - "dialect": "postgres", - "host" : "localhost", - "port" : "5432" - } + "use_env_variable": "DATABASE_URL" } } diff --git a/orm.js b/orm.js index f988d00..8a0d20b 100644 --- a/orm.js +++ b/orm.js @@ -1,7 +1,26 @@ var Sequelize = require('sequelize'); +/* http://sequelizejs.com/articles/heroku */ +var parseUrl = function(url) { + var match = url.match(/([\w]+):\/\/([^:]+):([^@]+)@([^:]+):(\d+)\/(.+)/); + db = { + database : match[6], + username : match[2], + password : match[3], + options : { + port : match[5], + host : match[4], + dialect : match[1], + } + } + return db; +} + module.exports = { configure: function(db) { + db.url = db.use_env_variable ? process.env[db.use_env_variable] : null; + db = db.url ? parseUrl(db.url) : db; return new Sequelize(db.database, db.username, db.password, db.options); } } + diff --git a/package.json b/package.json index 5a8b8b4..b12458b 100644 --- a/package.json +++ b/package.json @@ -5,12 +5,14 @@ "dependencies": { "express": "~3.1.0", "path": "~0.4.9", - "mysql": "~2.0.0", - "sqlite3": "~2.1.0", + "pg": "~3.4.1", "sequelize": "~1.7.9", "socket.io": "~1.0.6", "config": "~1.0.2" }, + "devDependencies": { + "sqlite3": "~2.1.0" + }, "engines": { "node": "0.10.x" }, diff --git a/public/javascripts/models/task.js b/public/javascripts/models/task.js index 23e141e..2fbf3c7 100644 --- a/public/javascripts/models/task.js +++ b/public/javascripts/models/task.js @@ -9,20 +9,20 @@ Backbone var TaskModel = Backbone.Model.extend({ defaults: { - parent_id: '', + parentId: 0, content: '', - is_completed:'n' + isCompleted: 0 }, toggelCompletedStatus:function(isCompleted){ var prev_isCompleted = isCompleted, self = this; - this.save({'is_completed':isCompleted}, + this.save({'isCompleted':isCompleted}, { success:function(){}, error:function(){ //REVERT BACK ON ERROR - self.set({'is_completed':prev_isCompleted}); + self.set({'isCompleted':prev_isCompleted}); } }) } @@ -31,4 +31,4 @@ Backbone return TaskModel; -}); \ No newline at end of file +}); diff --git a/public/javascripts/views/list.js b/public/javascripts/views/list.js index 2f7f608..c9eeed3 100644 --- a/public/javascripts/views/list.js +++ b/public/javascripts/views/list.js @@ -37,8 +37,8 @@ TaskView model: task }); var a = taskView.render(); - if (a.model.get('parent_id')!=0) - a.$el.insertAfter($('*[data-id="'+a.model.get('parent_id')+'"]').parents('li:first')); + if (a.model.get('parentId')!=0) + a.$el.insertAfter($('*[data-id="'+a.model.get('parentId')+'"]').parents('li:first')); else this.$el.append(a.el); } diff --git a/public/javascripts/views/task.js b/public/javascripts/views/task.js index d84e47f..a9ae278 100644 --- a/public/javascripts/views/task.js +++ b/public/javascripts/views/task.js @@ -38,7 +38,7 @@ taskTemplate var task = this; this.socket.on('task', function(data){ if (task.model.id == data.id) { - task.model.set({'content':data.content, 'is_completed':data.is_completed}); + task.model.set({'content':data.content, 'isCompleted':data.isCompleted}); } }); }, @@ -47,9 +47,9 @@ taskTemplate var tmpl = _.template(this.template); var task = this; this.$el.html(tmpl({model:this.model.toJSON()})); - if (this.model.get('parent_id')!=0) { + if (this.model.get('parentId')!=0) { this.$el.addClass('shift1'); - var className = $('*[data-id="'+this.model.get('parent_id')+'"]').parents('li:first').attr('class'); + var className = $('*[data-id="'+this.model.get('parentId')+'"]').parents('li:first').attr('class'); if (className!=undefined && className!=0 && className.substring(0,5) == 'shift') { this.$el.removeClass(); this.$el.addClass('shift' + (parseInt(className.charAt(5))+1)); @@ -74,31 +74,31 @@ taskTemplate if (e.shiftKey && e.keyCode == 9) { var model = this.$el.next('li').find('input').data('id'); model = Tasks.get(model); - var old_parent = model.get('parent_id'); + var old_parent = model.get('parentId'); old_parent = Tasks.get(old_parent); - var new_parent = old_parent.get('parent_id'); + var new_parent = old_parent.get('parentId'); if (new_parent == null) new_parent = 0; - model.set('parent_id',new_parent); - model.save({content: model.get('content'), parent_id: model.get('parent_id')}); + model.set('parentId',new_parent); + model.save({content: model.get('content'), parentId: model.get('parentId')}); } else if (e.keyCode == 9) { var parent = this.$el.prev('li').prev('li').find('input').data('id'); var current = this.$el.prev('li').find('input').data('id'); var model = Tasks.get(current); - model.set('parent_id',parent); - model.save({content: model.get('content'), parent_id: model.get('parent_id')}); + model.set('parentId',parent); + model.save({content: model.get('content'), parentId: model.get('parentId')}); } this.socket.emit('task', { id: this.model.id, - parent_id: this.model.parent_id, + parentId: this.model.parentId, content: this.$input.val().trim(), - is_completed:this.model.toJSON().is_completed + isCompleted:this.model.toJSON().isCompleted }); }, update: function(e) { if ( e.which === constants.ENTER_KEY ) { - Tasks.add({content:'', parent_id: this.model.get('parent_id')}); + Tasks.add({content:'', parentId: this.model.get('parentId')}); this.$input.blur(); this.$el.next('li').find('input').focus(); } @@ -110,7 +110,7 @@ taskTemplate this.model.destroy(); } else { - this.model.save({content: value, parent_id: this.model.attributes.parent_id}); + this.model.save({content: value, parentId: this.model.attributes.parentId}); } this.$el.removeClass('editing'); }, @@ -124,22 +124,22 @@ taskTemplate }, markComplete:function(){ - this.model.toggelCompletedStatus('Y'); + this.model.toggelCompletedStatus(true); this.socket.emit('task', { id: this.model.id, - parent_id: this.model.parent_id, + parentId: this.model.parentId, content:this.model.toJSON().content, - is_completed: this.model.toJSON().is_completed + isCompleted: this.model.toJSON().isCompleted }); }, unmarkComlete:function(){ - this.model.toggelCompletedStatus('N'); + this.model.toggelCompletedStatus(false); this.socket.emit('task', { id: this.model.id, - parent_id: this.model.parent_id, + parentId: this.model.parentId, content:this.model.toJSON().content, - is_completed: this.model.toJSON().is_completed + isCompleted: this.model.toJSON().isCompleted }); }, diff --git a/public/templates/task.html b/public/templates/task.html index 9b320ed..b15103b 100644 --- a/public/templates/task.html +++ b/public/templates/task.html @@ -4,7 +4,7 @@
- <% if(model.is_completed=='Y'){%> + <% if(model.isCompleted){%> Uncomplete
<%}else {%> Complete
@@ -16,7 +16,7 @@
- <% if(model.is_completed=='Y'){%> + <% if(model.isCompleted){%> <%}else {%> diff --git a/server.js b/server.js index 439d9b7..cda47f9 100644 --- a/server.js +++ b/server.js @@ -29,7 +29,7 @@ app.get('/tasks', function(req,res){ app.post('/tasks', function(req,res){ Tasks.create({ content: req.body.content, - parent: req.body.parent_id, + parent: parseInt(req.body.parent_id) || 0, isCompleted: false }).success(function(task){ res.send(task); @@ -40,7 +40,7 @@ app.put('/tasks/:id', function(req,res){ console.log(req.body.isCompleted); Tasks.find(req.params.id).success(function(task){ task.content = req.body.content; - task.isCompleted = req.body.isCompleted; + task.isCompleted = req.body.isCompleted == 1; task.save().success(function(task){ res.send(task); }) From 41e5a53020bc63fa0594c871830551298d9cc9f8 Mon Sep 17 00:00:00 2001 From: DC* Date: Thu, 14 Aug 2014 22:20:03 -0300 Subject: [PATCH 03/13] Changed HTTP listening port for production --- config/production.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/production.json b/config/production.json index 2abad20..c447056 100644 --- a/config/production.json +++ b/config/production.json @@ -1,5 +1,5 @@ { - "port": 3000, + "port": 80, "database": { "use_env_variable": "DATABASE_URL" } From 0e16b74e9b3b5c6ce0664c19b14d152c208cadb1 Mon Sep 17 00:00:00 2001 From: DC* Date: Thu, 14 Aug 2014 22:38:09 -0300 Subject: [PATCH 04/13] Add option to override port configuration with environment variable: PORT --- .gitignore | 1 + server.js | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 88206e8..053a445 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ npm-debug.log node_modules *.sqlite +*.env diff --git a/server.js b/server.js index cda47f9..f1fb3ad 100644 --- a/server.js +++ b/server.js @@ -16,8 +16,9 @@ app.configure(function() { app.use(express.errorHandler({dumpExceptions: true, showStack: true})); }); -server.listen(config.get('port'), function() { - console.log( 'Express server listening on port %d in %s mode', config.get('port'), app.settings.env ); +var port = process.env.PORT || config.get('port'); +server.listen(port, function() { + console.log( 'Express server listening on port %d in %s mode', port, app.settings.env ); }); app.get('/tasks', function(req,res){ From a38d33c39d9cb72d1f5197fa84f3fe1d413ee228 Mon Sep 17 00:00:00 2001 From: DC* Date: Thu, 14 Aug 2014 23:20:48 -0300 Subject: [PATCH 05/13] Update README.md with heroku deploy steps --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 2562ddc..dae4b28 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,8 @@ An open-source [Workflowy](http://workflowy.com) clone. * heroku create --stack cedar * git remote add heroku URL from above +* heroku addons:add heroku-postgresql:dev +* heroku config:set NODE_ENV=production * git push heroku master ##Controls From c20b692b289a513736fefe90f29f8b4b358e8c94 Mon Sep 17 00:00:00 2001 From: DC* Date: Sat, 16 Aug 2014 23:54:48 -0300 Subject: [PATCH 06/13] Update heroku deploy steps --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index dae4b28..30dc578 100644 --- a/README.md +++ b/README.md @@ -14,10 +14,9 @@ An open-source [Workflowy](http://workflowy.com) clone. ##Heroku deploy * heroku create --stack cedar -* git remote add heroku URL from above * heroku addons:add heroku-postgresql:dev * heroku config:set NODE_ENV=production -* git push heroku master +* git push heroku heroku-integration:master ##Controls From 832e629fdb515641b274fd3d65e78883e7096185 Mon Sep 17 00:00:00 2001 From: DC* Date: Sun, 17 Aug 2014 00:00:27 -0300 Subject: [PATCH 07/13] Added repository field on package.json --- package.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/package.json b/package.json index b12458b..3f81bd1 100644 --- a/package.json +++ b/package.json @@ -21,5 +21,9 @@ "db:migrate": "./node_modules/sequelize/bin/sequelize -m --config config/database.json", "db:seed": "node db/seed/initial_tasks.js", "postinstall": "npm run db:migrate && npm run db:seed" + }, + "repository": { + "type": "git", + "url": "git://github.com/abhshkdz/HackFlowy.git" } } From d7b943d8ad73b99e71018d9c78389cb1dc3b1425 Mon Sep 17 00:00:00 2001 From: DC* Date: Sun, 17 Aug 2014 00:08:06 -0300 Subject: [PATCH 08/13] Update install steps --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 30dc578..d673581 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,8 @@ An open-source [Workflowy](http://workflowy.com) clone. ##Installation -* Edit `config/development.json` to your needs +* Edit `config/development.json` and `config/database.json` to your needs * `npm install` -* Run migrations and initialize the database: `sequelize-cli -m --config config/database.json` and `node db/seed/initial_tasks.js` * `node server.js` ##Heroku deploy From 7b6a87a58bfb502b6507947bf9be612d759dd3e1 Mon Sep 17 00:00:00 2001 From: DC* Date: Sun, 17 Aug 2014 01:19:46 -0300 Subject: [PATCH 09/13] Fixed tasks' parent update --- public/javascripts/models/task.js | 2 +- public/javascripts/views/list.js | 4 ++-- server.js | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/public/javascripts/models/task.js b/public/javascripts/models/task.js index 2fbf3c7..460c228 100644 --- a/public/javascripts/models/task.js +++ b/public/javascripts/models/task.js @@ -9,7 +9,7 @@ Backbone var TaskModel = Backbone.Model.extend({ defaults: { - parentId: 0, + parent: 0, content: '', isCompleted: 0 }, diff --git a/public/javascripts/views/list.js b/public/javascripts/views/list.js index c9eeed3..5e7f665 100644 --- a/public/javascripts/views/list.js +++ b/public/javascripts/views/list.js @@ -37,8 +37,8 @@ TaskView model: task }); var a = taskView.render(); - if (a.model.get('parentId')!=0) - a.$el.insertAfter($('*[data-id="'+a.model.get('parentId')+'"]').parents('li:first')); + if (a.model.get('parent')!=0) + a.$el.insertAfter($('*[data-id="'+a.model.get('parent')+'"]').parents('li:first')); else this.$el.append(a.el); } diff --git a/server.js b/server.js index f1fb3ad..9e406fa 100644 --- a/server.js +++ b/server.js @@ -30,7 +30,7 @@ app.get('/tasks', function(req,res){ app.post('/tasks', function(req,res){ Tasks.create({ content: req.body.content, - parent: parseInt(req.body.parent_id) || 0, + parent: parseInt(req.body.parent) || 0, isCompleted: false }).success(function(task){ res.send(task); @@ -41,6 +41,7 @@ app.put('/tasks/:id', function(req,res){ console.log(req.body.isCompleted); Tasks.find(req.params.id).success(function(task){ task.content = req.body.content; + task.parent = parseInt(req.body.parent) || 0, task.isCompleted = req.body.isCompleted == 1; task.save().success(function(task){ res.send(task); From 9f80f349507eb35f499fcae8285c50b12e1dbca0 Mon Sep 17 00:00:00 2001 From: DC* Date: Sun, 17 Aug 2014 01:47:10 -0300 Subject: [PATCH 10/13] Added heroku app.json manifest --- README.md | 6 ++++++ app.json | 16 ++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 app.json diff --git a/README.md b/README.md index d673581..b711725 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,12 @@ An open-source [Workflowy](http://workflowy.com) clone. ##Heroku deploy +You can use our one-click heroku deploy: + +[![Deploy](https://www.herokucdn.com/deploy/button.png)](https://heroku.com/deploy) + +or proced manually as follow: + * heroku create --stack cedar * heroku addons:add heroku-postgresql:dev * heroku config:set NODE_ENV=production diff --git a/app.json b/app.json new file mode 100644 index 0000000..5793689 --- /dev/null +++ b/app.json @@ -0,0 +1,16 @@ +{ + "name": "HackFlowy", + "description": "Workflowy clone, built using Backbone.js & Socket.io", + "keywords": ["express", "backbone", "socket.io"], + "website": "https://github.com/abhshkdz/HackFlowy", + "repository": "https://github.com/abhshkdz/HackFlowy", + "scripts": { + "postdeploy": "" + }, + "env": { + "NODE_ENV": "production" + }, + "addons": [ + "heroku-postgresql:dev" + ] +} From 76858fb7c18ad4fe5f57a89db852b892145e6f9d Mon Sep 17 00:00:00 2001 From: DC* Date: Mon, 25 Aug 2014 13:00:09 -0300 Subject: [PATCH 11/13] Update app.json Fix addon wrong name: heroku-postgresql:dev is heroku-postgresql --- app.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.json b/app.json index 5793689..eaec800 100644 --- a/app.json +++ b/app.json @@ -11,6 +11,6 @@ "NODE_ENV": "production" }, "addons": [ - "heroku-postgresql:dev" + "heroku-postgresql" ] } From ef8e75cba0addb08dcf5946aab56a433700bd607 Mon Sep 17 00:00:00 2001 From: DC* Date: Mon, 25 Aug 2014 13:10:37 -0300 Subject: [PATCH 12/13] Update README.md Minor fixes --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b711725..d69f802 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ You can use our one-click heroku deploy: [![Deploy](https://www.herokucdn.com/deploy/button.png)](https://heroku.com/deploy) -or proced manually as follow: +Or proceed manually as follow: * heroku create --stack cedar * heroku addons:add heroku-postgresql:dev From 3d65dda277b7f930c4f70822d6b4cd389e666417 Mon Sep 17 00:00:00 2001 From: DC* Date: Mon, 25 Aug 2014 13:14:53 -0300 Subject: [PATCH 13/13] Update app.json Removed empty postscript block causing deploy failure. --- app.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/app.json b/app.json index eaec800..3338e9f 100644 --- a/app.json +++ b/app.json @@ -4,9 +4,6 @@ "keywords": ["express", "backbone", "socket.io"], "website": "https://github.com/abhshkdz/HackFlowy", "repository": "https://github.com/abhshkdz/HackFlowy", - "scripts": { - "postdeploy": "" - }, "env": { "NODE_ENV": "production" },