├── .donotdelete ├── .env ├── .env.example ├── .gitignore ├── README.md ├── config └── index.js ├── features ├── model-definitions │ └── index.js ├── stories │ ├── index.js │ └── story.js └── users │ ├── index.js │ └── user.js ├── package.json └── server.js /.donotdelete: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/angularjs-in-action/angello-express-api/4c624749902537f45748e4d13bae7ef3b2163485/.donotdelete -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | MONGO_URL=mongodb://localhost/angello 2 | AUTH0_CLIENT_ID=Fq8hKAkghu45WpnqrYTc6dbvXhBUdP7l 3 | AUTH0_CLIENT_SECRET=EsJ5GZM-P48uYu2NPbuEEG4pRGdhgQ9XO3updWnqKS2oggBbOcI4TNHuJ8N4knI2 4 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | MONGO_URL=mongodb://localhost/angello 2 | AUTH0_CLIENT_ID= 3 | AUTH0_CLIENT_SECRET= 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | coverage 3 | .idea/ 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Angello: NodeJS express app for AngularJS In Action 2 | 3 | NodeJS app for Angello sample in AngularJS in action book. 4 | 5 | ## Running it 6 | 7 | First, you need to set the `AUTH0_CLIENT_ID`, `AUTH0_CLIENT_SECRET` and `MONGO_URL` enviroment variables. For that, you can just create a `.env` file with those variables (similar to the `.env.example` file). 8 | 9 | Then run `npm install` in a terminal (or command prompt) window. 10 | 11 | In a separate terminal window, run the mongo driver by executing `mongod`. 12 | 13 | Finally, go back to the first terminal window and run `node server.js` to start the server. 14 | -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | var logger = require('morgan'), 2 | cors = require('cors'), 3 | mongoose = require('mongoose'), 4 | jwt = require('express-jwt'); 5 | express = require('express'), 6 | bodyParser = require('body-parser'); 7 | 8 | exports.parsers = function(app) { 9 | app.use(bodyParser.urlencoded({extended: true})); 10 | app.use(bodyParser.json()); 11 | app.use(cors()); 12 | } 13 | 14 | exports.log = function(app) { 15 | if (process.env.NODE_ENV === 'development') { 16 | app.use(logger('dev')); 17 | } 18 | } 19 | 20 | exports.db = function(app) { 21 | var db = mongoose.connect(process.env.MONGO_URL); 22 | } 23 | 24 | exports.authentication = function(app) { 25 | var jwtCheck = jwt({ 26 | secret: new Buffer(process.env.AUTH0_CLIENT_SECRET, 'base64'), 27 | audience: process.env.AUTH0_CLIENT_ID 28 | }); 29 | 30 | app.use('/api', jwtCheck); 31 | } 32 | -------------------------------------------------------------------------------- /features/model-definitions/index.js: -------------------------------------------------------------------------------- 1 | var validate = require('mongoose-validator'); 2 | _ = require('lodash'); 3 | 4 | module.exports = { 5 | string: function(len, required) { 6 | return { 7 | type: String, 8 | required: !!required, 9 | validate: validate({ 10 | validator: "isLength", 11 | arguments: [0, len || 100] 12 | }) 13 | }; 14 | }, 15 | date: function(start) { 16 | return { 17 | type: Date, 18 | validate: validate({ 19 | validator: "isAfter", 20 | arguments: null || start 21 | }) 22 | }; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /features/stories/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'), 2 | _ = require('lodash'), 3 | Story = require('./story'); 4 | 5 | 6 | 7 | var app = module.exports = express.Router(); 8 | 9 | app.get('/', function(req, res) { 10 | Story.find({userId: req.user.sub}).exec().then(function(stories) { 11 | res.status(200).send(stories); 12 | }, function(error) { 13 | res.status(400).send(err); 14 | }); 15 | }); 16 | 17 | app.get('/:id', function(req, res) { 18 | Story.findOne({_id: req.params.id, userId: req.user.sub}, function(err, story){ 19 | if (err) { 20 | res.status(400).send(err); 21 | } else if (!story) { 22 | res.status(404).send(err); 23 | } else { 24 | res.status(200).send(story); 25 | } 26 | }); 27 | }); 28 | 29 | app.put('/:id', function(req, res) { 30 | Story.update({_id: req.params.id, userId: req.user.sub}, _.omit(req.body, '_id'), function(err, story){ 31 | if (err) { 32 | res.status(400).send(err); 33 | } else { 34 | res.status(200).send(req.body); 35 | } 36 | }); 37 | }); 38 | 39 | app.delete('/:id', function(req, res) { 40 | Story.remove({_id: req.params.id, userId: req.user.sub}, function(err, story){ 41 | if (err) { 42 | res.status(400).send(err); 43 | } else { 44 | res.status(200).send(req.body); 45 | } 46 | }); 47 | }); 48 | 49 | app.post('/', function(req, res) { 50 | var story = new Story(_.extend(req.body, { 51 | userId: req.user.sub 52 | })); 53 | story.save(function(err, newStoryCreated) { 54 | if (err) { 55 | res.status(400).send(err); 56 | } else { 57 | res.status(201).send(newStoryCreated); 58 | } 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /features/stories/story.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'), 2 | Schema = mongoose.Schema, 3 | _ = require('lodash'), 4 | timestamps = require('mongoose-timestamp'), 5 | Definitions = require('../model-definitions'); 6 | 7 | 8 | var StorySchema = new Schema({ 9 | assignee: Definitions.string(100, true), 10 | criteria: Definitions.string(100, false), 11 | description: Definitions.string(100, false), 12 | reporter: Definitions.string(100, true), 13 | status: Definitions.string(100, true), 14 | title: Definitions.string(100, true), 15 | type: Definitions.string(100, true), 16 | userId: Definitions.string(100, true) 17 | }); 18 | 19 | StorySchema.virtual('id').get(function() { 20 | return this._id; 21 | }); 22 | 23 | StorySchema.set('toJSON', { 24 | virtuals: true 25 | }); 26 | 27 | StorySchema.plugin(timestamps); 28 | module.exports = mongoose.model('Story', StorySchema); 29 | -------------------------------------------------------------------------------- /features/users/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'), 2 | _ = require('lodash'), 3 | User = require('./user'); 4 | 5 | 6 | 7 | var app = module.exports = express.Router(); 8 | 9 | app.get('/', function(req, res) { 10 | User.find({userId: req.user.sub}).exec().then(function(users) { 11 | res.status(200).send(users); 12 | }, function(error) { 13 | res.status(400).send(err); 14 | }); 15 | }); 16 | 17 | app.get('/:id', function(req, res) { 18 | User.findOne({_id: req.params.id, userId: req.user.sub}, function(err, user){ 19 | if (err) { 20 | res.status(400).send(err); 21 | } else if (!user) { 22 | res.status(404).send(err); 23 | } else { 24 | res.status(200).send(user); 25 | } 26 | }); 27 | }); 28 | 29 | app.put('/:id', function(req, res) { 30 | User.update({_id: req.params.id, userId: req.user.sub}, _.omit(req.body, '_id'), function(err, user){ 31 | if (err) { 32 | res.status(400).send(err); 33 | } else { 34 | res.status(200).send(req.body); 35 | } 36 | }); 37 | }); 38 | 39 | app.delete('/:id', function(req, res) { 40 | User.remove({_id: req.params.id, userId: req.user.sub}, function(err, user){ 41 | if (err) { 42 | res.status(400).send(err); 43 | } else { 44 | res.status(200).send(req.body); 45 | } 46 | }); 47 | }); 48 | 49 | app.post('/', function(req, res) { 50 | var user = new User(_.extend(req.body, { 51 | userId: req.user.sub 52 | })); 53 | user.save(function(err, newUserCreated) { 54 | if (err) { 55 | res.status(400).send(err); 56 | } else { 57 | res.status(201).send(newUserCreated); 58 | } 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /features/users/user.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'), 2 | Schema = mongoose.Schema, 3 | _ = require('lodash'), 4 | timestamps = require('mongoose-timestamp'), 5 | Definitions = require('../model-definitions'); 6 | 7 | 8 | var UserSchema = new Schema({ 9 | email: Definitions.string(100, true), 10 | name: Definitions.string(100), 11 | userId: Definitions.string(100, true) 12 | }); 13 | 14 | UserSchema.virtual('id').get(function() { 15 | return this._id; 16 | }); 17 | 18 | UserSchema.set('toJSON', { 19 | virtuals: true 20 | }); 21 | 22 | UserSchema.plugin(timestamps); 23 | module.exports = mongoose.model('User', UserSchema); 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "garage-backend", 3 | "version": "0.1.0", 4 | "description": "Garage Backend", 5 | "main": "server.js", 6 | "dependencies": { 7 | "body-parser": "^1.6.5", 8 | "cors": "^2.4.1", 9 | "dotenv": "^0.4.0", 10 | "express": "^4.8.5", 11 | "express-jwt": "^0.3.2", 12 | "jsonwebtoken": "^1.1.0", 13 | "kerberos": "^0.0.18", 14 | "lodash": "^2.4.1", 15 | "mongoose": "^4.4.0", 16 | "mongoose-timestamp": "^0.5.0", 17 | "mongoose-validator": "^1.2.4", 18 | "morgan": "^1.2.3" 19 | }, 20 | "devDependencies": { 21 | "mocha": "^1.21.4", 22 | "supertest": "^0.13.0", 23 | "istanbul": "^0.3.0", 24 | "better-assert": "^1.0.1" 25 | }, 26 | "scripts": { 27 | "test": "NODE_PATH=. NODE_ENV=test ./node_modules/istanbul/lib/cli.js cover node_modules/.bin/_mocha", 28 | "start": "node server.js" 29 | }, 30 | "author": "Martin Gontovnikas", 31 | "license": "MIT" 32 | } 33 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | var express = require('express'), 2 | http = require('http'), 3 | config = require('./config'), 4 | dotenv = require('dotenv'); 5 | 6 | 7 | var app = module.exports = express(); 8 | 9 | dotenv.load(); 10 | 11 | app.set('showStackError', true); 12 | 13 | // Prettify HTML 14 | app.locals.pretty = true; 15 | 16 | // Configure Logging 17 | config.log(app); 18 | 19 | // Configure parsers 20 | config.parsers(app); 21 | 22 | // Configure DB 23 | config.db(app); 24 | 25 | // Configure authentication 26 | config.authentication(app); 27 | 28 | // Routes 29 | app.use('/api/clients/:userId/stories', require('./features/stories')); 30 | app.use('/api/clients/:userId/users', require('./features/users')); 31 | 32 | 33 | var port = process.env.PORT || 4000; 34 | 35 | http.createServer(app).listen(port, function (err) { 36 | console.log('listening in http://localhost:' + port); 37 | }); 38 | --------------------------------------------------------------------------------