Skip to content
Permalink
Browse files

Add API server

  • Loading branch information
AvrahamO committed May 10, 2018
1 parent 54052a6 commit ca913c20a71da61cd86b2116117d31c58872226f
@@ -0,0 +1,5 @@
NODE_ENV=development
SERVER_PORT=4040
JWT_SECRET=0a6b944d-d2fb-46fc-a85e-0295c986cd9f
MONGO_HOST=mongodb://localhost/new-mean-five
MONGO_PORT=27017
@@ -42,3 +42,6 @@ testem.log
# System Files
.DS_Store
Thumbs.db

# Env file
*.env

This file was deleted.

This file was deleted.

This file was deleted.

@@ -4,7 +4,7 @@
"license": "MIT",
"scripts": {
"ng": "ng",
"start": "ng serve",
"start": "concurrently -c 'yellow.bold,green.bold' -n 'SERVER,BUILD' 'node server' 'ng build --watch'",
"build": "ng build --prod",
"test": "ng test",
"lint": "ng lint",
@@ -23,7 +23,21 @@
"@angular/platform-browser": "5.2.0",
"@angular/platform-browser-dynamic": "5.2.0",
"@angular/router": "5.2.0",
"body-parser": "^1.18.2",
"compression": "^1.7.2",
"cookie-parser": "^1.4.3",
"core-js": "2.4.1",
"cors": "^2.8.4",
"dotenv": "^5.0.1",
"express": "^4.16.3",
"express-jwt": "^5.3.1",
"express-validation": "^1.0.2",
"helmet": "^3.12.0",
"joi": "^13.3.0",
"jsonwebtoken": "^8.2.1",
"method-override": "^2.3.10",
"mongoose": "^5.0.18",
"morgan": "^1.9.0",
"rxjs": "5.5.6",
"zone.js": "0.8.19"
},
@@ -35,6 +49,7 @@
"@types/jasminewd2": "~2.0.2",
"@types/node": "~6.0.60",
"codelyzer": "4.0.1",
"concurrently": "^3.5.1",
"jasmine-core": "~2.8.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~2.0.0",
@@ -0,0 +1,44 @@
const Joi = require('joi');

// require and configure dotenv, will load vars in .env in PROCESS.ENV
require('dotenv').config();

// define validation for all the env vars
const envVarsSchema = Joi.object({
NODE_ENV: Joi.string()
.allow(['development', 'production', 'test', 'provision'])
.default('development'),
SERVER_PORT: Joi.number()
.default(4040),
MONGOOSE_DEBUG: Joi.boolean()
.when('NODE_ENV', {
is: Joi.string().equal('development'),
then: Joi.boolean().default(true),
otherwise: Joi.boolean().default(false)
}),
JWT_SECRET: Joi.string().required()
.description('JWT Secret required to sign'),
MONGO_HOST: Joi.string().required()
.description('Mongo DB host url'),
MONGO_PORT: Joi.number()
.default(27017)
}).unknown()
.required();

const { error, value: envVars } = Joi.validate(process.env, envVarsSchema);
if (error) {
throw new Error(`Config validation error: ${error.message}`);
}

const config = {
env: envVars.NODE_ENV,
port: envVars.SERVER_PORT,
mongooseDebug: envVars.MONGOOSE_DEBUG,
jwtSecret: envVars.JWT_SECRET,
mongo: {
host: envVars.MONGO_HOST,
port: envVars.MONGO_PORT
}
};

module.exports = config;
@@ -0,0 +1,50 @@
const express = require('express');
const logger = require('morgan');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const compress = require('compression');
const methodOverride = require('method-override');
const cors = require('cors');
const helmet = require('helmet');
const routes = require('../routes/index.route');
const config = require('./config');

const app = express();

if (config.env === 'development') {
app.use(logger('dev'));
}

// parse body params and attache them to req.body
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

app.use(cookieParser());
app.use(compress());
app.use(methodOverride());

// secure apps by setting various HTTP headers
app.use(helmet());

// enable CORS - Cross Origin Resource Sharing
app.use(cors());

app.use('/', express.static('../../dist'))
app.use('/api', routes);

// catch 404 and forward to error handler
app.use((req, res, next) => {
const err = new Error('not found');
err.status = 404;
return next(err);
});

// error handler, send stacktrace only during development
app.use((err, req, res, next) => {
res.status(err.status || 500).json({
message: err.message
});
next(err);
});

module.exports = app;
@@ -0,0 +1,20 @@
const mongoose = require('mongoose');
const util = require('util');
const debug = require('debug')('express-mongoose-es6-rest-api:index');

const config = require('./config');

// connect to mongo db
const mongoUri = config.mongo.host;
mongoose.connect(mongoUri, { server: { socketOptions: { keepAlive: 1 } } });
mongoose.connection.on('error', () => {
throw new Error(`unable to connect to database: ${mongoUri}`);
});

// print mongoose logs in dev env
if (config.MONGOOSE_DEBUG) {
mongoose.set('debug', (collectionName, method, query, doc) => {
debug(`${collectionName}.${method}`, util.inspect(query, false, 20), doc);
});
}

@@ -0,0 +1,46 @@
const Joi = require('joi');

module.exports = {
// POST /api/users
createUser: {
body: {
username: Joi.string().required(),
mobileNumber: Joi.string().regex(/^[1-9][0-9]{9}$/).required()
}
},
// POST /api/posts
createPost: {
body: {
title: Joi.string().required(),
}
},

// UPDATE /api/users/:userId
updateUser: {
body: {
username: Joi.string().required(),
mobileNumber: Joi.string().regex(/^[1-9][0-9]{9}$/).required()
},
params: {
userId: Joi.string().hex().required()
}
},

// UPDATE /api/posts/:postId
updatePost: {
body: {
title: Joi.string().required(),
},
params: {
postId: Joi.string().hex().required()
}
},

// POST /api/auth/login
login: {
body: {
username: Joi.string().required(),
password: Joi.string().required()
}
}
};
@@ -0,0 +1,52 @@
const passport = require('passport'),
User = require('../models/user.model'),
JwtStrategy = require('passport-jwt').Strategy,
ExtractJwt = require('passport-jwt').ExtractJwt,
LocalStrategy = require('passport-local');

import config from './config';
// Setting username field to email rather than username
const localOptions = {
usernameField: 'email'
};

// Setting up local login strategy
const localLogin = new LocalStrategy(localOptions, (email, password, done) => {
User.findOne({ email }, (err, user) => {
if (err) { return done(err); }
if (!user) { return done(null, false, { error: 'Your login details could not be verified. Please try again.' }); }

user.comparePassword(password, (err, isMatch) => {
if (err) { return done(err); }
if (!isMatch) { return done(null, false, { error: 'Your login details could not be verified. Please try again.' }); }

return done(null, user);
});
});
});
// Setting JWT strategy options
const jwtOptions = {
// Telling Passport to check authorization headers for JWT
jwtFromRequest: ExtractJwt.fromAuthHeader(),
// Telling Passport where to find the secret
secretOrKey: config.jwtSecret

// TO-DO: Add issuer and audience checks
};


// Setting up JWT login strategy
const jwtLogin = new JwtStrategy(jwtOptions, (payload, done) => {
User.findById(payload._id, (err, user) => {
if (err) { return done(err, false); }

if (user) {
done(null, user);
} else {
done(null, false);
}
});
});

passport.use(jwtLogin);
passport.use(localLogin);
@@ -0,0 +1,62 @@
const jwt = require('jsonwebtoken');
const config = require('../config/config');
const User = require('../models/user.model');
const { verifyToken, generateToken } = require('./token.controller');

// sample user, used for authentication
const user = {
username: 'react',
password: 'express'
};

/**
* Returns jwt token if valid username and password is provided
* @param req
* @param res
* @param next
* @returns {*}
*/
function login(params) {
// Ideally you'll fetch this from the db Idea here was to show how jwt works
// with simplicity
return new Promise((resolve, reject) => {
User
.findOne({email: params.email})
.then((user) => {
if (!user) return resolve({ error: 'user do not exist' });
if (user.authenticate(params.password)) {
return resolve({
user: user.clean(),
token: generateToken(user)
});
}
resolve({
error: 'password do not match'
});
})
.catch((err) => {
console.log(err);
return resolve({ error: 'user do not exist' });
});
});
}

/**
* This is a protected route. Will return random number only if jwt token is provided in header.
* @param req
* @param res
* @returns {*}
*/
function getRandomNumber(req, res) {
// req.user is assigned by jwt middleware if valid token is provided
return res.json({
user: req.user,
num: Math.random() * 100
});
}

module.exports = {
login,
getRandomNumber,
verifyToken,
};

0 comments on commit ca913c2

Please sign in to comment.
You can’t perform that action at this time.