├── .gitignore ├── README.md ├── data ├── collection.js └── schema.js ├── package.json ├── routes ├── auth.js ├── category.js ├── tag.js ├── blogpost.js └── user.js ├── models └── index.js ├── server.js └── database.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Node.js Backend Code Sample In PostgreSql 2 | 3 | ## Description 4 | Code sample to show the backend setup of a generic blogsite using the node package Bookshelf. Common packages such as Express, Bluebird, and Async are also in use. Database is in PostgreSQL. 5 | 6 | Upon successful user login, a user token will be returned by the server. It's to be used for verification with most other HTTP requests listed here, such as getting a list of blogposts written by a particular user. 7 | -------------------------------------------------------------------------------- /data/collection.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var Model = require("../models/index.js"); 4 | 5 | var Users = Model.Bookshelf.Collection.extend({ 6 | model: Model.User 7 | }); 8 | exports.UserCollection = Users; 9 | 10 | var Blogposts = Model.Bookshelf.Collection.extend({ 11 | model: Model.Blogpost 12 | }); 13 | exports.BlogpostCollection = Blogposts; 14 | 15 | var Categories = Model.Bookshelf.Collection.extend({ 16 | model: Model.Category 17 | }); 18 | exports.CategoryCollection = Categories; 19 | 20 | var Tags = Model.Bookshelf.Collection.extend({ 21 | model: Model.Tag 22 | }); 23 | exports.TagsCollection = Tags; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Projects_Site", 3 | "version": "0.0.1", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "start": "node app.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "engines": { 11 | "node": "0.10.x" 12 | }, 13 | "dependencies": { 14 | "async": "*", 15 | "bcrypt": "^0.8.2", 16 | "bluebird": "*", 17 | "body-parser": "*", 18 | "bookshelf": "*", 19 | "ejs": "*", 20 | "errorhandler": "*", 21 | "express": "*", 22 | "imagemagick": "*", 23 | "jquery": "*", 24 | "knex": "*", 25 | "lodash": "*", 26 | "method-override": "^2.3.2", 27 | "moment": "^2.10.2", 28 | "multer": "^0.1.8", 29 | "pg": "*", 30 | "request": "*" 31 | }, 32 | "author": "Desmond Ng", 33 | "license": "ISC" 34 | } 35 | -------------------------------------------------------------------------------- /routes/auth.js: -------------------------------------------------------------------------------- 1 | var Model = require("./user.js"), 2 | AuthController = {}, 3 | Collections = require("../data/collection.js"); 4 | 5 | AuthController.requireUser = function () { 6 | return function (req, res, next) { 7 | var token; 8 | 9 | var token_method = "NONE"; 10 | 11 | if (req.body.token) { 12 | token = req.body.token; 13 | token_method = "BODY"; 14 | } else if (req.query.token) { 15 | token = req.query.token; 16 | token_method = "QUERY"; 17 | } else if (req.headers.token) { 18 | token = req.headers.token; 19 | token_method = "HEADER"; 20 | } 21 | 22 | token_method += "_TOKEN"; 23 | 24 | console.log("Auth method: " + token_method + " - " + req.url); 25 | 26 | if (token) { 27 | Collections.UserCollection.forge() 28 | .query(function (qb) { 29 | qb.where("token", "=", token); 30 | }) 31 | .fetchOne() 32 | .then(function (user) { 33 | if (user) { 34 | // Keep a copy of the user info for future use 35 | req.user = user; 36 | next(); 37 | } else { 38 | res.status(404).json({error: "user_not_found"}); 39 | } 40 | }) 41 | .catch(function (err) { 42 | res.status(500).json({error: err.message}); 43 | }); 44 | } else { 45 | res.status(400).json({error: "require_token"}); 46 | } 47 | }; 48 | }; 49 | 50 | module.exports = AuthController; -------------------------------------------------------------------------------- /data/schema.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var Schema = { 4 | users: { 5 | id: {type: "increments", nullable: false, primary: true}, 6 | email: {type: "string", maxlength: 254, nullable: false, unique: true}, 7 | name: {type: "string", maxlength: 150, nullable: false}, 8 | password: {type: "string", nullable: false}, 9 | token: {type: "string", nullable: true} 10 | }, 11 | 12 | categories: { 13 | id: {type: "increments", nullable: false, primary: true}, 14 | name: {type: "string", maxlength: 150, nullable: false, unique: true} 15 | }, 16 | // html: {type: "text", fieldtype: "medium", nullable: false}, 17 | blogposts: { 18 | id: {type: "increments", nullable: false, primary: true}, 19 | user_id: {type: "integer", nullable: false, unsigned: true}, 20 | category_id: {type: "integer", nullable: false, unsigned: true}, 21 | title: {type: "string", maxlength: 150, nullable: false}, 22 | html: {type: "string", maxlength: 150, nullable: false}, 23 | created_at: {type: "dateTime", nullable: false}, 24 | updated_at: {type: "dateTime", nullable: true} 25 | }, 26 | 27 | tags: { 28 | id: {type: "increments", nullable: false, primary: true}, 29 | name: {type: "string", nullable: false, unique: true} 30 | }, 31 | 32 | // A table for many-to-many relation between tags table & posts table 33 | posts_tags: { 34 | id: {type: "increments", nullable: false, primary: true}, 35 | post_id: {type: "integer", nullable: false, unsigned: true, references: "blogposts.id"}, 36 | tag_id: {type: "integer", nullable: false, unsigned: true, references: "tags.id"} 37 | } 38 | }; 39 | 40 | module.exports = Schema; -------------------------------------------------------------------------------- /models/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var moment = require("moment"); 4 | 5 | var Knex = require("knex")({ 6 | client: "pg", 7 | connection: { 8 | host: "localhost", 9 | user: "", 10 | password: "", 11 | database: "blogsite" 12 | } 13 | }); 14 | 15 | var Bookshelf = require("bookshelf")(Knex); 16 | Bookshelf.plugin("visibility"); 17 | 18 | var User = Bookshelf.Model.extend({ 19 | tableName: "users", 20 | blogpost: function() { 21 | // one-to-many 22 | this.hasMany(Blogpost, "blogpost_id"); 23 | } 24 | }); 25 | exports.User = User; 26 | 27 | var Category = Bookshelf.Model.extend({ 28 | tableName: "categories", 29 | blogpost: function() { 30 | // one-to-many 31 | this.hasMany(Blogpost, "blogpost_id"); 32 | } 33 | }); 34 | exports.Category = Category; 35 | 36 | var Blogpost = Bookshelf.Model.extend({ 37 | tableName: "blogposts", 38 | category: function() { 39 | // one-to-one or many-to-one 40 | return this.belongsTo(Category, "category_id"); 41 | }, 42 | tag: function() { 43 | // many-to-many 44 | // 1st param: ClassName of related table 45 | // 2nd param: Name of related table 46 | // Other params: Foreign Keys 47 | return this.belongsToMany(Tag, "posts_tags", "post_id"); 48 | }, 49 | author: function() { 50 | // Bookshelf assumes that table names are plurals 51 | // and that the foreignkey is the singular name of the related table fixed with _id 52 | return this.belongsTo(User, "user_id"); 53 | } 54 | }); 55 | exports.Blogpost = Blogpost; 56 | 57 | var Tag = Bookshelf.Model.extend({ 58 | tableName: "tags", 59 | blogpost: function() { 60 | return this.belongsToMany(Blogpost, "posts_tags", "tag_id"); 61 | } 62 | }); 63 | exports.Tag = Tag; 64 | 65 | exports.Bookshelf = Bookshelf; -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var Express = require("express"), 4 | App = Express(), 5 | Http = require("http"), 6 | BodyParser = require("body-parser"), 7 | Router = Express.Router(), 8 | MethodOverride = require("method-override"), 9 | Multer = require("multer"), 10 | Db = require("./database.js"), 11 | AuthController = require("./routes/auth.js"), 12 | UserController = require("./routes/user.js"), 13 | CategoryController = require("./routes/category.js"), 14 | BlogpostController = require("./routes/blogpost.js"), 15 | TagController = require("./routes/tag.js"); 16 | 17 | App.use(Multer()); 18 | App.use(MethodOverride()); 19 | App.use(BodyParser.json()); 20 | App.use(BodyParser.urlencoded({extended: true})); 21 | 22 | Db.initialisation(); 23 | 24 | Http.createServer(App).listen(3000); 25 | 26 | App.get("/users", UserController.getAll); 27 | App.get("/users/:id", AuthController.requireUser(), UserController.getUser); 28 | App.post("/users", UserController.create); 29 | App.post("/users/login", UserController.login); 30 | App.post("/users/logout", AuthController.requireUser(), UserController.logout); 31 | App.put("/users/:id", AuthController.requireUser(), UserController.update); 32 | App.delete("/users/:id",AuthController.requireUser(), UserController.destroy); 33 | 34 | App.get("/categories", CategoryController.getAll); 35 | App.get("/categories/:id", CategoryController.getCategory); 36 | App.post("/categories", AuthController.requireUser(), CategoryController.create); 37 | App.put("/categories/:id", AuthController.requireUser(), CategoryController.update); 38 | App.delete("/categories/:id", AuthController.requireUser(), CategoryController.destroy); 39 | 40 | App.get("/blogpost", BlogpostController.getAll); 41 | App.get("/blogpost/:id", AuthController.requireUser(), BlogpostController.getPost); 42 | App.post("/blogpost", AuthController.requireUser(), BlogpostController.create); 43 | App.put("/blogpost/:id", AuthController.requireUser(), BlogpostController.update); 44 | App.delete("/blogpost/:id", AuthController.requireUser(), BlogpostController.destroy); 45 | 46 | App.get("/tag", TagController.get); 47 | // App.post("/tag", TagController.create); -------------------------------------------------------------------------------- /routes/category.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var CategoryController = {}, 4 | Collections = require("../data/collection.js"); 5 | 6 | CategoryController.getAll = function (req, res) { 7 | Collections.CategoryCollection.forge() 8 | .fetch() 9 | .then(function (result) { 10 | res.status(200).json(result); 11 | }) 12 | .catch(function (err) { 13 | res.status(500).json(err); 14 | }); 15 | }; 16 | 17 | CategoryController.create = function (req, res) { 18 | Collections.CategoryCollection.forge() 19 | .create({ 20 | name: req.body.name 21 | }) 22 | .then(function (result) { 23 | res.status(200).json(result); 24 | }) 25 | .catch(function (err) { 26 | res.status(500).json(err); 27 | }); 28 | }; 29 | 30 | CategoryController.getCategory = function (req, res) { 31 | Collections.CategoryCollection.forge() 32 | .query(function (qb) { 33 | qb.where("id", "=", req.params.id); 34 | }) 35 | .fetchOne() 36 | .then(function (category) { 37 | if (!category) { 38 | res.status(404).json({}); 39 | } else { 40 | res.status(200).json(category); 41 | } 42 | }) 43 | .catch(function (err) { 44 | res.status(500).json(err); 45 | }); 46 | 47 | }; 48 | 49 | CategoryController.update = function (req, res) { 50 | Collections.CategoryCollection.forge() 51 | .query(function (qb) { 52 | qb.where("id", "=", req.params.id); 53 | }) 54 | .fetchOne({ 55 | require: true 56 | }) 57 | .then(function (category) { 58 | category.save({ 59 | name: req.body.name || category.get("name") 60 | }) 61 | .then(function (result) { 62 | res.status(200).json(result); 63 | }) 64 | .catch(function (err) { 65 | res.status(500).json(err); 66 | }); 67 | }) 68 | .catch(function (err) { 69 | res.status(500).json(err); 70 | }); 71 | }; 72 | 73 | CategoryController.destroy = function (req, res) { 74 | Collections.CategoryCollection.forge() 75 | .query(function (qb) { 76 | qb.where("id", "=", req.params.id); 77 | }) 78 | .fetchOne({ 79 | require: true 80 | }) 81 | .then(function (category) { 82 | category.destroy() 83 | .then(function () { 84 | res.status(200).json({}); 85 | }) 86 | .catch(function (err) { 87 | res.status(500).json(err); 88 | }); 89 | }) 90 | .catch(function (err) { 91 | res.status(500).json(err); 92 | }); 93 | }; 94 | 95 | module.exports = CategoryController; 96 | -------------------------------------------------------------------------------- /routes/tag.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var TagController = {}, 3 | _ = require("lodash"), 4 | Model = require("../models/index.js"), 5 | Collections = require("../data/collection.js"); 6 | 7 | TagController.getAll = function () { 8 | Collections.TagsCollection.forge() 9 | .fetch() 10 | .then(function (existingTags) { 11 | return existingTags.toJSON(); 12 | }); 13 | }; 14 | 15 | TagController.get = function (req, res) { 16 | Collections.TagsCollection.forge() 17 | .fetch() 18 | .then(function (result) { 19 | res.status(200).json(result); 20 | }) 21 | .otherwise(function (err) { 22 | res.status(500).json({message: err.message}); 23 | }); 24 | }; 25 | 26 | TagController.getMatchingTags = function (tags) { 27 | tags = tags.map(function (tagName) { 28 | return tagName.toLowerCase(); 29 | }); 30 | 31 | return Collections.TagsCollection.forge() 32 | .query(function (qb) { 33 | qb.whereIn("name", tags); 34 | }) 35 | .fetch() 36 | .then(function (existingTags) { 37 | return existingTags.toJSON(); 38 | }) 39 | .otherwise(function (err) { 40 | console.log({message: err.message}); 41 | }); 42 | }; 43 | 44 | TagController.create = function (tags) { 45 | // Create array of tag objects 46 | var tagObjs = tags.map(function (tag) { 47 | return { 48 | name: tag.toLowerCase() 49 | }; 50 | }); 51 | 52 | // Get tags that already existed 53 | return TagController.getMatchingTags(tags) 54 | .then(function (existingTags) { 55 | 56 | var doNotExist = []; 57 | 58 | if (existingTags && existingTags.length > 0) { 59 | var existingTagNames = existingTags.map(function (existingTag) { 60 | return existingTag.name; 61 | }); 62 | 63 | doNotExist = tagObjs.filter(function (tagObj) { 64 | return existingTagNames.indexOf(tagObj.name) < 0; 65 | }); 66 | } else { 67 | doNotExist = tagObjs; 68 | } 69 | 70 | // Save tags that do not exist 71 | return Collections.TagsCollection 72 | .forge(doNotExist) 73 | .mapThen(function (model) { 74 | return model 75 | .save() 76 | .then(function () { 77 | return model.get("id"); 78 | }); 79 | }) 80 | .then(function (ids) { 81 | // Union the newly created tags' ids 82 | return _.union(ids, _.pluck(existingTags, "id")); 83 | }) 84 | .otherwise(function (err) { 85 | console.log({message: err.message}); 86 | }); 87 | }); 88 | }; 89 | 90 | module.exports = TagController; -------------------------------------------------------------------------------- /database.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var Schema = require("./data/schema.js"); 4 | var Moment = require("moment"); 5 | var Promise = require("bluebird"); 6 | var Async = require("async"); 7 | var _ = require("lodash"); 8 | var Model = require("./models/index.js"); 9 | 10 | var createTable = function (tableName) { 11 | return Model.Bookshelf.knex.schema.createTable(tableName, function (table) { 12 | var column; 13 | var columnKeys = _.keys(Schema[tableName]); 14 | 15 | columnKeys.forEach(function (key) { 16 | 17 | if (Schema[tableName][key].type === "text" && Schema[tableName][key].hasOwnProperty("fieldtype")) { 18 | column = table[Schema[tableName][key].type](key, Schema[tableName][key].fieldtype); 19 | } else if (Schema[tableName][key].type === "string" && Schema[tableName][key].hasOwnProperty("maxlength")) { 20 | column = table[Schema[tableName][key].type](key, Schema[tableName][key].maxlength); 21 | } else { 22 | column = table[Schema[tableName][key].type](key); 23 | } 24 | 25 | if (Schema[tableName][key].hasOwnProperty("nullable") && Schema[tableName][key].nullable === true) { 26 | column.nullable(); 27 | } else { 28 | column.notNullable(); 29 | } 30 | 31 | if (Schema[tableName][key].hasOwnProperty("primary") && Schema[tableName][key].primary === true) { 32 | column.primary(); 33 | } 34 | 35 | if (Schema[tableName][key].hasOwnProperty("unique") && Schema[tableName][key].unique === true) { 36 | column.unique(); 37 | } 38 | 39 | if (Schema[tableName][key].hasOwnProperty("unsigned") && Schema[tableName][key].unsigned === true) { 40 | column.unsigned(); 41 | } 42 | 43 | if (Schema[tableName][key].hasOwnProperty("references")) { 44 | column.references(Schema[tableName][key].references); 45 | } 46 | 47 | if (Schema[tableName][key].hasOwnProperty("defaultTo")) { 48 | column.defaultTo(Schema[tableName][key].defaultTo); 49 | } 50 | }); 51 | }); 52 | }; 53 | 54 | var doesTableExist = function (tableName) { 55 | return Model.Bookshelf.knex.schema.hasTable(tableName); 56 | }; 57 | 58 | var initDb = function () { 59 | var calls = []; 60 | var tableNames = _.keys(Schema); 61 | 62 | tableNames.forEach(function (tableName) { 63 | 64 | var f = function (callback) { 65 | doesTableExist(tableName) 66 | .then(function (exists) { 67 | if (!exists) { 68 | console.log("Creating database table " + tableName + "..."); 69 | 70 | createTable(tableName) 71 | .then(function (result) { 72 | console.log("---> Created database table " + tableName); 73 | callback(null, result); 74 | }) 75 | .catch(function (err) { 76 | console.log("Error creating " + tableName + " table " + err); 77 | callback(err, null); 78 | }); 79 | 80 | } else { 81 | callback(null, exists); 82 | } 83 | }) 84 | .catch(function (error) { 85 | console.log("Error creating " + tableName + " table " + error); 86 | callback(error, null) 87 | }); 88 | }; 89 | 90 | calls.push(f); 91 | }); 92 | 93 | Async.series(calls, function (err, result) { 94 | if (!err) { 95 | console.log("Finished initialising database table"); 96 | } else { 97 | console.log("Error initialising database table: " + err); 98 | } 99 | }); 100 | }; 101 | 102 | exports.initialisation = initDb; -------------------------------------------------------------------------------- /routes/blogpost.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var BlogpostController = {}, 4 | Moment = require("moment"), 5 | TagController = require("./tag.js"), 6 | Collections = require("../data/collection.js"); 7 | 8 | BlogpostController.getAll = function (req, res) { 9 | Collections.BlogpostCollection.forge() 10 | .fetch() 11 | .then(function (result) { 12 | res.status(200).json(result); 13 | }) 14 | .catch(function (err) { 15 | res.status(500).json(err); 16 | }); 17 | }; 18 | 19 | BlogpostController.getPost = function (req, res) { 20 | getBlogpostWithId(req.params.id) 21 | .then(function (post) { 22 | if (!post) { 23 | res.status(404).json({message: "blogpost not found"}); 24 | } else { 25 | res.status(200).json(post); 26 | } 27 | }) 28 | .catch(function (err) { 29 | res.status(500).json({message: err.message}); 30 | }); 31 | }; 32 | 33 | BlogpostController.create = function (req, res) { 34 | var tags = getArrayOfTags(req, ["uncategories"]); 35 | 36 | // Save post 37 | Collections.BlogpostCollection.forge() 38 | .create({ 39 | user_id: req.body.user_id, 40 | category_id: req.body.category_id, 41 | title: req.body.title, 42 | html: req.body.content, 43 | created_at: Moment().format() 44 | }) 45 | .then(function (blogpost) { 46 | // save tags 47 | TagController.create(tags) 48 | .then(function (ids) { 49 | blogpost.tag().attach(ids); 50 | res.status(200).json(blogpost); 51 | }) 52 | .catch(function (err) { 53 | res.status(500).json({message: err.message}); 54 | }); 55 | }) 56 | .catch(function (err) { 57 | res.status(500).json({message: err.message}); 58 | }); 59 | }; 60 | 61 | var getArrayOfTags = function (req, defaultCategory) { 62 | var tags = req.body.tags; 63 | 64 | if (tags) { 65 | tags = tags.split(", ").map(function (tag) { 66 | return tag.trim(); 67 | }); 68 | } else { 69 | tags = defaultCategory; 70 | } 71 | 72 | return tags; 73 | }; 74 | 75 | BlogpostController.update = function (req, res) { 76 | var tags = getArrayOfTags(req, []); 77 | 78 | getBlogpostWithId(req.params.id) 79 | .then(function (blogpost) { 80 | if (!blogpost) { 81 | res.status(404).json({message: "blogpost not found"}); 82 | } else { 83 | blogpost.save({ 84 | user_id: req.body.user_id || blogpost.get("id"), 85 | category_id: req.body.category_id || blogpost.get("category_id"), 86 | title: req.body.title || blogpost.get("title"), 87 | html: req.body.content || blogpost.get("html"), 88 | updated_at: Moment().format() 89 | }) 90 | .then(function (result) { 91 | if (tags.length > 0) { 92 | TagController.create(tags) 93 | .then(function (ids) { 94 | // Save only newly added tags 95 | ids = result.toJSON().tag.filter(function (tag) { 96 | return ids.indexOf(tag.id) < 0; 97 | }); 98 | 99 | result.tag().attach(ids); 100 | res.status(200).json("blogpost updated"); 101 | }) 102 | .catch(function (err) { 103 | res.status(500).json({message: err.message}); 104 | }); 105 | } else { 106 | res.status(200).json(result); 107 | } 108 | }) 109 | .catch(function (err) { 110 | res.status(500).json({message: err.message}); 111 | }); 112 | } 113 | }) 114 | .catch(function (err) { 115 | res.status(500).json({message: err.message}); 116 | }) 117 | }; 118 | 119 | BlogpostController.destroy = function (req, res) { 120 | getBlogpostWithId(req.params.id) 121 | .then(function (blogpost) { 122 | if (!blogpost) { 123 | res.status(404).json({error:"Blogpost not found", message: err.message}); 124 | } else { 125 | var tagIds = blogpost.toJSON().tag.map(function (tag) { 126 | return tag.id; 127 | }); 128 | 129 | // Delete many-to-many relations first 130 | blogpost.tag().detach(tagIds); 131 | 132 | // Then delete the model itself 133 | blogpost.destroy() 134 | .then(function () { 135 | res.status(200).json({}); 136 | }) 137 | .catch(function (err) { 138 | res.status(500).json({error:"Blogpost delete failed", message: err.message}) 139 | }); 140 | } 141 | }) 142 | .catch(function (err) { 143 | res.status(500).json({message: err.message}) 144 | }); 145 | }; 146 | 147 | var getBlogpostWithId = function (id) { 148 | // To get back the tags in alphabatical order 149 | var tagRelation = function (qb) { 150 | qb.orderBy("name"); 151 | }; 152 | 153 | return Collections.BlogpostCollection.forge() 154 | .query(function (qb) { 155 | qb.where("id", "=", id); 156 | }) 157 | .fetchOne({ 158 | withRelated: ["category", {"tag" : tagRelation}] 159 | }); 160 | } 161 | 162 | module.exports = BlogpostController; 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | -------------------------------------------------------------------------------- /routes/user.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var UserController = {}, 4 | Bcrypt = require("bcrypt"), 5 | Crypto = require("crypto"), 6 | Promise = require("bluebird"), 7 | Collections = require("../data/collection.js"); 8 | 9 | UserController.getAll = function (req, res) { 10 | Collections.UserCollection.forge() 11 | // .query(function (qb) { 12 | // qb.where("id", "<", "8").andWhere("name", "=", "desmond"); 13 | // }) 14 | .fetch() 15 | .then(function (result) { 16 | result = result.map(removePasswordFromUserData); 17 | res.status(200).json(result); 18 | }) 19 | .catch(function (err) { 20 | res.status(500).json(err); 21 | }); 22 | }; 23 | 24 | UserController.create = function (req, res) { 25 | var salt = Bcrypt.genSaltSync(12); 26 | var hash = Bcrypt.hashSync(req.body.password, salt); 27 | 28 | Collections.UserCollection.forge() 29 | .create({ 30 | name: req.body.name, 31 | email: req.body.email.toLowerCase(), 32 | password: hash 33 | }) 34 | .then(function (result) { 35 | res.status(200).json(result); 36 | }) 37 | .catch(function (err) { 38 | res.status(500).json(err); 39 | }); 40 | }; 41 | 42 | UserController.login = function (req, res) { 43 | Collections.UserCollection.forge() 44 | .query(function (qb) { 45 | qb.where("email", "=", req.body.email.toLowerCase()); 46 | }) 47 | .fetchOne() 48 | .then(function (user) { 49 | if (user) { 50 | var isPassword = Bcrypt.compareSync(req.body.password, user.get("password")); 51 | if (isPassword) { 52 | if (user.get("token")) { 53 | return Promise.resolve(user); 54 | } else { 55 | // No token 56 | var randomBytes = Promise.promisify(Crypto.randomBytes); 57 | 58 | return randomBytes(48) 59 | .then(function (buf) { 60 | var aToken = buf.toString("hex"); 61 | // set the modified data before saving 62 | // user.set({token: aToken}); 63 | return user.save({token: aToken}); 64 | }); 65 | } 66 | } else { 67 | return Promise.reject("password-incorrect"); 68 | } 69 | } else { 70 | return Promise.reject("user_not_found"); 71 | } 72 | }) 73 | .then(function (user) { 74 | user = removePasswordFromUserData(user); 75 | res.status(200).json(user); 76 | }) 77 | .catch(function (err) { 78 | res.status(400).json({error: err}); 79 | }); 80 | }; 81 | 82 | UserController.logout = function (req, res) { 83 | Collections.UserCollection.forge() 84 | .query(function (qb) { 85 | qb.where("token", "=", req.body.token); 86 | }) 87 | .fetchOne() 88 | .then(function (user) { 89 | user.save({token: null}); 90 | res.status(200).json({message: "logout"}); 91 | }) 92 | .catch(function (err) { 93 | res.status(500).json({error: err.message}); 94 | }); 95 | }; 96 | 97 | UserController.getUser = function (req, res) { 98 | Collections.UserCollection.forge() 99 | .query(function (qb) { 100 | qb.where("id", "=", req.params.id); 101 | }) 102 | .fetchOne() 103 | .then(function (result) { 104 | if (!result) { 105 | res.status(404).json({}); 106 | } else { 107 | result = removePasswordFromUserData(result); 108 | res.status(200).json(result) 109 | } 110 | }) 111 | .catch(function (err) { 112 | res.status(500).json(err); 113 | }); 114 | }; 115 | 116 | UserController.update = function (req, res) { 117 | Collections.UserCollection.forge() 118 | .query(function (qb) { 119 | qb.where("id", "=", req.params.id); 120 | }) 121 | .fetchOne({ 122 | require: true 123 | }) 124 | .then(function (user) { 125 | if (!user) { 126 | res.status(404).json({}); 127 | } else { 128 | user 129 | .save({ 130 | name: req.body.name || user.get("name"), 131 | email: req.body.email || user.get("email") 132 | }) 133 | .then(function (result) { 134 | result = removePasswordFromUserData(result); 135 | res.status(200).json(result); 136 | }) 137 | .catch(function (err) { 138 | res.status(500).json(err); 139 | }); 140 | } 141 | }) 142 | .catch(function (err) { 143 | res.status(500).json(err); 144 | }); 145 | }; 146 | 147 | UserController.destroy = function (req, res) { 148 | Collections.UserCollection.forge() 149 | .query(function (qb) { 150 | qb.where("id", "=", req.params.id); 151 | }) 152 | .fetchOne({ 153 | require: true 154 | }) 155 | .then(function (user) { 156 | if (!user) { 157 | res.status(404).json({}); 158 | } else { 159 | user.destroy() 160 | .then(function () { 161 | res.status(200).json({}); 162 | }) 163 | .catch(function (err) { 164 | res.status(500).json(err); 165 | }); 166 | } 167 | }) 168 | .catch(function (err) { 169 | res.status(500).json(err); 170 | }) 171 | }; 172 | 173 | var removePasswordFromUserData = function (user) { 174 | var userObject = user.toJSON(); 175 | if (userObject.hasOwnProperty("password")) { 176 | delete(userObject.password); 177 | } 178 | return userObject; 179 | }; 180 | 181 | module.exports = UserController; --------------------------------------------------------------------------------