From 92ed0463fab89ef17a43e27e8fcee214da40ea26 Mon Sep 17 00:00:00 2001 From: edimdendidiwangga Date: Tue, 25 Apr 2017 17:43:14 +0700 Subject: [PATCH 1/2] api-auth done --- .gitignore | 2 + README.md | 14 +++- config/config.json | 10 +++ controllers/auth.js | 57 +++++++++++++++++ controllers/users.js | 81 ++++++++++++++++++++++++ helpers/verify_token.js | 35 ++++++++++ migrations/20170425042846-create-user.js | 39 ++++++++++++ models/index.js | 36 +++++++++++ models/user.js | 20 ++++++ package.json | 33 ++++++++++ routes/api.js | 18 ++++++ server.js | 14 ++++ 12 files changed, 358 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 config/config.json create mode 100644 controllers/auth.js create mode 100644 controllers/users.js create mode 100644 helpers/verify_token.js create mode 100644 migrations/20170425042846-create-user.js create mode 100644 models/index.js create mode 100644 models/user.js create mode 100644 package.json create mode 100644 routes/api.js create mode 100644 server.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..37d7e73 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules +.env diff --git a/README.md b/README.md index b2e3b8b..5851f18 100644 --- a/README.md +++ b/README.md @@ -1 +1,13 @@ -# api-auth \ No newline at end of file +# API BASIC +Demo app with basic REST API. + +# REST API +List of basic routes: + +| Route | HTTP | Description | +| ------ | ------ | ------ | +| /api/users | GET | Get all the users | +| /api/users/:id | GET | Get a single users | +| /api/users | POST | Create a users | +| /api/users/:id | PUT | Update a user with new info | +| /api/users/:id | DELETE | Delete a user | diff --git a/config/config.json b/config/config.json new file mode 100644 index 0000000..e54f683 --- /dev/null +++ b/config/config.json @@ -0,0 +1,10 @@ +{ + "development": { + "username": "edim", + "password": "edimdendy", + "database": "api_db", + "host": "127.0.0.1", + "dialect": "postgres", + "logging": false + } +} diff --git a/controllers/auth.js b/controllers/auth.js new file mode 100644 index 0000000..464808d --- /dev/null +++ b/controllers/auth.js @@ -0,0 +1,57 @@ +let model = require('../models') +let passwordHash = require('password-hash'); +let jwt = require('jsonwebtoken'); +require('dotenv').config() +let methods = {} + +methods.signup = function(req, res) { + req.body.password = passwordHash.generate(req.body.password) + model.User.create({ + name: req.body.name, + username: req.body.username, + password: req.body.password, + email: req.body.email, + role: req.body.role + }) + .then(data => { + res.json(data) + }) + .catch(err => { + res.json(err) + }) +} + +methods.signin = function(req, res) { + model.User.find({ + where: { + $or: [{ + username: req.body.username + }, { + email: req.body.email + }] + } + }) + .then(record => { + if (passwordHash.verify(req.body.password, record.password)) { + let data = Object.assign({}, record.toJSON()) + delete data.id + delete data.password + delete data.createdAt + delete data.updatedAt + let token = jwt.sign(data, process.env.SECRET_KEY, { + expiresIn: '1h' + }) + res.json({ + message: 'Login is successful', + token, + data + }) + } else { + res.json({ + message: 'Your password is not matched' + }) + } + }) +} + +module.exports = methods diff --git a/controllers/users.js b/controllers/users.js new file mode 100644 index 0000000..bf397c6 --- /dev/null +++ b/controllers/users.js @@ -0,0 +1,81 @@ +let model = require('../models'); +let passwordHash = require('password-hash'); +let methods = {} + +methods.getAllUsers = function(req, res) { + model.User.findAll() + .then(dataAllUser => { + res.json(dataAllUser) + }) +} + +methods.getById = function(req, res) { + model.User.findOne({ + where: { + id: req.params.id + } + }) + .then(data => { + res.json(data) + }) + .catch(err => { + res.json(err) + }) +} + +methods.createUser = function(req, res) { + req.body.password = passwordHash.generate(req.body.password) + model.User.create({ + name: req.body.name, + username: req.body.username, + password: req.body.password, + email: req.body.email, + role: req.body.role + }) + .then(data => { + res.json(data) + }) + .catch(err => { + res.json(err) + }) +} + +methods.updateById = function(req, res) { + model.User.find({ + where: { + id: req.params.id + } + }) + .then(function(users) { + if (users) { + users.updateAttributes({ + name: req.body.name, + username: req.body.username, + password: req.body.password, + email: req.body.email + }) + .then(function(data) { + res.json(data) + }) + .catch(err => { + res.json(err) + }) + } + }); +} + +methods.deleteById = function(req, res) { + model.User.destroy({ + where: { + id: req.params.id + } + }) + .then(function(data) { + res.json(data) + }) + .catch(err => { + res.json(err) + }) +} + +module.exports = methods diff --git a/helpers/verify_token.js b/helpers/verify_token.js new file mode 100644 index 0000000..4e0cb9f --- /dev/null +++ b/helpers/verify_token.js @@ -0,0 +1,35 @@ +const jwt = require('jsonwebtoken'); +const model = require('../models'); +require('dotenv').config(); + +module.exports = { + auth: function(req, res, next) { + jwt.verify(req.headers.token, process.env.SECRET_KEY, function(err, decoded) { + if (decoded) { + next() + } else { + res.send('You must login!') + } + }) + }, + isAdmin: function(req, res, next) { + jwt.verify(req.headers.token, process.env.SECRET_KEY, function(err, decoded) { + if (decoded) { + model.User.find({ + where : { + $or: [{ username: decoded.username }, { email: decoded.email }] + } + }) + .then((user) => { + if (user.role == 'admin') { + next() + } else { + res.send('You are not admin!') + } + }) + } else { + res.send(err) + } + }) + } +} diff --git a/migrations/20170425042846-create-user.js b/migrations/20170425042846-create-user.js new file mode 100644 index 0000000..9334189 --- /dev/null +++ b/migrations/20170425042846-create-user.js @@ -0,0 +1,39 @@ +'use strict'; +module.exports = { + up: function(queryInterface, Sequelize) { + return queryInterface.createTable('Users', { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: Sequelize.INTEGER + }, + name: { + type: Sequelize.STRING + }, + username: { + type: Sequelize.STRING + }, + password: { + type: Sequelize.TEXT + }, + email: { + type: Sequelize.STRING + }, + role: { + type: Sequelize.STRING + }, + createdAt: { + allowNull: false, + type: Sequelize.DATE + }, + updatedAt: { + allowNull: false, + type: Sequelize.DATE + } + }); + }, + down: function(queryInterface, Sequelize) { + return queryInterface.dropTable('Users'); + } +}; diff --git a/models/index.js b/models/index.js new file mode 100644 index 0000000..7540dba --- /dev/null +++ b/models/index.js @@ -0,0 +1,36 @@ +'use strict'; + +var fs = require('fs'); +var path = require('path'); +var Sequelize = require('sequelize'); +var basename = path.basename(module.filename); +var env = process.env.NODE_ENV || 'development'; +var config = require(__dirname + '/../config/config.json')[env]; +var db = {}; + +if (config.use_env_variable) { + var sequelize = new Sequelize(process.env[config.use_env_variable]); +} else { + var sequelize = new Sequelize(config.database, config.username, config.password, config); +} + +fs + .readdirSync(__dirname) + .filter(function(file) { + return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js'); + }) + .forEach(function(file) { + var model = sequelize['import'](path.join(__dirname, file)); + db[model.name] = model; + }); + +Object.keys(db).forEach(function(modelName) { + if (db[modelName].associate) { + db[modelName].associate(db); + } +}); + +db.sequelize = sequelize; +db.Sequelize = Sequelize; + +module.exports = db; diff --git a/models/user.js b/models/user.js new file mode 100644 index 0000000..8f47a18 --- /dev/null +++ b/models/user.js @@ -0,0 +1,20 @@ +'use strict'; +module.exports = function(sequelize, DataTypes) { + var User = sequelize.define('User', { + name: DataTypes.STRING, + username: { + type: DataTypes.STRING, + allowNull: false + }, + password: DataTypes.TEXT, + email: DataTypes.STRING, + role: DataTypes.STRING + }, { + classMethods: { + associate: function(models) { + // associations can be defined here + } + } + }); + return User; +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..b83aaa8 --- /dev/null +++ b/package.json @@ -0,0 +1,33 @@ +{ + "name": "api-basic", + "version": "1.0.0", + "description": "build api basic ", + "main": "index.js", + "scripts": { + "start": "node server.js", + "dev": "nodemon server.js", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/edimdendidiwangga/api-basic.git" + }, + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/edimdendidiwangga/api-basic/issues" + }, + "homepage": "https://github.com/edimdendidiwangga/api-basic#readme", + "dependencies": { + "body-parser": "^1.17.1", + "dotenv": "^4.0.0", + "express": "^4.15.2", + "jsonwebtoken": "^7.4.0", + "password-hash": "^1.2.2", + "pg": "^6.1.5", + "sequelize": "^3.30.4" + }, + "devDependencies": { + "nodemon": "^1.11.0" + } +} diff --git a/routes/api.js b/routes/api.js new file mode 100644 index 0000000..60a1302 --- /dev/null +++ b/routes/api.js @@ -0,0 +1,18 @@ +let router = require('express').Router(); +let userController = require('../controllers/users') +let authController = require('../controllers/auth') +let helper = require('../helpers/verify_token') + +router.get('/', function(req, res, next) { + res.send('Hello World'); +}) +// routes user +router.get('/users', helper.isAdmin, userController.getAllUsers) +router.get('/users/:id', helper.auth, userController.getById) +router.post('/users', helper.isAdmin, userController.createUser) +router.put('/users/:id', helper.auth, userController.updateById) +router.delete('/users/:id', helper.isAdmin, userController.deleteById) +router.post('/signup', authController.signup) +router.post('/signin', authController.signin) + +module.exports = router; diff --git a/server.js b/server.js new file mode 100644 index 0000000..325c24f --- /dev/null +++ b/server.js @@ -0,0 +1,14 @@ +const express = require('express'); +const bodyParser = require('body-parser'); +const app = express() + +app.set('port', process.env.PORT || 3000) + +app.use(require('body-parser').json()); +app.use(require('body-parser').urlencoded({ extended: false })); + +app.use('/api', require('./routes/api')); + +app.listen(app.get('port'), function(){ + console.log('listening on port '+app.get('port')) +}) From 54cfe4cc371a5123f92cf3256f869e7cc207606a Mon Sep 17 00:00:00 2001 From: edimdendidiwangga Date: Tue, 25 Apr 2017 17:54:20 +0700 Subject: [PATCH 2/2] edit markdown --- README.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 5851f18..767760a 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,10 @@ List of basic routes: | Route | HTTP | Description | | ------ | ------ | ------ | -| /api/users | GET | Get all the users | -| /api/users/:id | GET | Get a single users | -| /api/users | POST | Create a users | -| /api/users/:id | PUT | Update a user with new info | -| /api/users/:id | DELETE | Delete a user | +| /api/signup | GET | Sign up with new user info | +| /api/signin | GET | Sign in while get an access token based on credentials | +| /api/users | GET | Get all the users info (admin only) | +| /api/users/:id | GET | Get a single users info (admin only and authenticated user) | +| /api/users | POST | Create a users (admin only) | +| /api/users/:id | PUT | Update a user with new info (admin only and authenticated user) | +| /api/users/:id | DELETE | Delete a user (admin only) |