├── .gitignore ├── Procfile ├── README.md ├── app.js ├── config.js ├── controllers ├── product.js └── user.js ├── index.js ├── middlewares └── auth.js ├── models ├── product.js └── user.js ├── package.json ├── routes └── index.js ├── services └── index.js └── views ├── layouts └── default.hbs ├── login.hbs └── product.hbs /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | *.log 4 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: node index.js 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-api-rest-2017 2 | API REST con Node.js utilizada en el curso gratis de Youtube. 3 | 4 | ➡️ **[Curso de Node.js y MongoDB - Crea un API REST desde cero](https://www.youtube.com/playlist?list=PLUdlARNXMVkk7E88zOrphPyGdS50Tadlr 5 | )** 6 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const express = require('express') 4 | const bodyParser = require('body-parser') 5 | const hbs = require('express-handlebars') 6 | const app = express() 7 | const api = require('./routes') 8 | 9 | app.use(bodyParser.urlencoded({ extended: false })) 10 | app.use(bodyParser.json()) 11 | app.engine('.hbs', hbs({ 12 | defaultLayout: 'default', 13 | extname: '.hbs' 14 | })) 15 | app.set('view engine', '.hbs') 16 | 17 | app.use('/api', api) 18 | app.get('/login', (req, res) => { 19 | res.render('login') 20 | }) 21 | app.get('/', (req, res) => { 22 | res.render('product') 23 | }) 24 | 25 | module.exports = app 26 | -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | port: process.env.PORT || 3001, 3 | db: process.env.MONGODB_URI || 'mongodb://localhost:27017/shop', 4 | SECRET_TOKEN: 'miclavedetokens' 5 | } 6 | -------------------------------------------------------------------------------- /controllers/product.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Product = require('../models/product') 4 | 5 | function getProduct (req, res) { 6 | let productId = req.params.productId 7 | 8 | Product.findById(productId, (err, product) => { 9 | if (err) return res.status(500).send({message: `Error al realizar la petición: ${err}`}) 10 | if (!product) return res.status(404).send({message: `El producto no existe`}) 11 | 12 | res.status(200).send({ product }) 13 | }) 14 | } 15 | 16 | function getProducts (req, res) { 17 | Product.find({}, (err, products) => { 18 | if (err) return res.status(500).send({message: `Error al realizar la petición: ${err}`}) 19 | if (!products) return res.status(404).send({message: 'No existen productos'}) 20 | 21 | res.send(200, { products }) 22 | }) 23 | } 24 | 25 | function saveProduct (req, res) { 26 | console.log('POST /api/product') 27 | console.log(req.body) 28 | 29 | let product = new Product() 30 | product.name = req.body.name 31 | product.picture = req.body.picture 32 | product.price = req.body.price 33 | product.category = req.body.category 34 | product.description = req.body.description 35 | 36 | product.save((err, productStored) => { 37 | if (err) res.status(500).send({message: `Error al salvar en la base de datos: ${err} `}) 38 | 39 | res.status(200).send({ product: productStored }) 40 | }) 41 | } 42 | 43 | function updateProduct (req, res) { 44 | let productId = req.params.productId 45 | let update = req.body 46 | 47 | Product.findByIdAndUpdate(productId, update, (err, productUpdated) => { 48 | if (err) res.status(500).send({message: `Error al actualizar el producto: ${err}`}) 49 | 50 | res.status(200).send({ product: productUpdated }) 51 | }) 52 | } 53 | 54 | function deleteProduct (req, res) { 55 | let productId = req.params.productId 56 | 57 | Product.findById(productId, (err, product) => { 58 | if (err) res.status(500).send({message: `Error al borrar el producto: ${err}`}) 59 | 60 | product.remove(err => { 61 | if (err) res.status(500).send({message: `Error al borrar el producto: ${err}`}) 62 | res.status(200).send({message: 'El producto ha sido eliminado'}) 63 | }) 64 | }) 65 | } 66 | 67 | module.exports = { 68 | getProduct, 69 | getProducts, 70 | saveProduct, 71 | updateProduct, 72 | deleteProduct 73 | } 74 | -------------------------------------------------------------------------------- /controllers/user.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const User = require('../models/user') 4 | const service = require('../services') 5 | 6 | function signUp (req, res) { 7 | const user = new User({ 8 | email: req.body.email, 9 | displayName: req.body.displayName, 10 | password: req.body.password 11 | }) 12 | 13 | user.save((err) => { 14 | if (err) return res.status(500).send({ message: `Error al crear el usuario: ${err}` }) 15 | 16 | return res.status(201).send({ token: service.createToken(user) }) 17 | }) 18 | } 19 | 20 | function signIn (req, res) { 21 | User.find({ email: req.body.email }, (err, user) => { 22 | if (err) return res.status(500).send({ message: err }) 23 | if (!user) return res.status(404).send({ message: 'No existe el usuario' }) 24 | 25 | req.user = user 26 | res.status(200).send({ 27 | message: 'Te has logueado correctamente', 28 | token: service.createToken(user) 29 | }) 30 | }) 31 | } 32 | 33 | module.exports = { 34 | signUp, 35 | signIn 36 | } 37 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mongoose = require('mongoose') 4 | const app = require('./app') 5 | const config = require('./config') 6 | 7 | mongoose.connect(config.db, (err, res) => { 8 | if (err) { 9 | return console.log(`Error al conectar a la base de datos: ${err}`) 10 | } 11 | console.log('Conexión a la base de datos establecida...') 12 | 13 | app.listen(config.port, () => { 14 | console.log(`API REST corriendo en http://localhost:${config.port}`) 15 | }) 16 | }) 17 | -------------------------------------------------------------------------------- /middlewares/auth.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const services = require('../services') 4 | 5 | function isAuth (req, res, next) { 6 | if (!req.headers.authorization) { 7 | return res.status(403).send({ message: 'No tienes autorización' }) 8 | } 9 | 10 | const token = req.headers.authorization.split(' ')[1] 11 | 12 | services.decodeToken(token) 13 | .then(response => { 14 | req.user = response 15 | next() 16 | }) 17 | .catch(response => { 18 | res.status(response.status) 19 | }) 20 | } 21 | 22 | module.exports = isAuth 23 | -------------------------------------------------------------------------------- /models/product.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mongoose = require('mongoose') 4 | const Schema = mongoose.Schema 5 | 6 | const ProductSchema = Schema({ 7 | name: String, 8 | picture: String, 9 | price: { type: Number, default: 0 }, 10 | category: { type: String, enum: ['computers', 'phones', 'accesories'] }, 11 | description: String 12 | }) 13 | 14 | module.exports = mongoose.model('Product', ProductSchema) 15 | -------------------------------------------------------------------------------- /models/user.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mongoose = require('mongoose') 4 | const Schema = mongoose.Schema 5 | const bcrypt = require('bcrypt-nodejs') 6 | const crypto = require('crypto') 7 | 8 | const UserSchema = new Schema({ 9 | email: { type: String, unique: true, lowercase: true }, 10 | displayName: String, 11 | avatar: String, 12 | password: { type: String, select: false }, 13 | signupDate: { type: Date, default: Date.now() }, 14 | lastLogin: Date 15 | }) 16 | 17 | UserSchema.pre('save', (next) => { 18 | let user = this 19 | //if (!user.isModified('password')) return next() 20 | 21 | bcrypt.genSalt(10, (err, salt) => { 22 | if (err) return next(err) 23 | 24 | bcrypt.hash(user.password, salt, null, (err, hash) => { 25 | if (err) return next(err) 26 | 27 | user.password = hash 28 | next() 29 | }) 30 | }) 31 | }) 32 | 33 | UserSchema.methods.gravatar = function () { 34 | if (!this.email) return `https://gravatar.com/avatar/?s=200&d=retro` 35 | 36 | const md5 = crypto.createHash('md5').update(this.email).digest('hex') 37 | return `https://gravatar.com/avatar/${md5}?s=200&d=retro` 38 | } 39 | 40 | module.exports = mongoose.model('User', UserSchema) 41 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "api-rest", 3 | "version": "1.0.0", 4 | "description": "Proyecto de API RESTful con Node.js y Express", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "Carlos Azaustre (http://carlosazaustre.es)", 11 | "license": "MIT", 12 | "dependencies": { 13 | "bcrypt-nodejs": "0.0.3", 14 | "body-parser": "^1.15.2", 15 | "crypto": "0.0.3", 16 | "express": "^4.14.0", 17 | "express-handlebars": "^3.0.0", 18 | "jwt-simple": "^0.5.1", 19 | "moment": "^2.16.0", 20 | "mongoose": "^4.6.0" 21 | }, 22 | "devDependencies": { 23 | "nodemon": "^1.10.2" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /routes/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const express = require('express') 4 | const productCtrl = require('../controllers/product') 5 | const userCtrl = require('../controllers/user') 6 | const auth = require('../middlewares/auth') 7 | const api = express.Router() 8 | 9 | api.get('/product', auth, productCtrl.getProducts) 10 | api.get('/product/:productId', productCtrl.getProduct) 11 | api.post('/product', auth, productCtrl.saveProduct) 12 | api.put('/product/:productId', auth, productCtrl.updateProduct) 13 | api.delete('/product/:productId', auth, productCtrl.deleteProduct) 14 | api.post('/signup', userCtrl.signUp) 15 | api.post('/signin', userCtrl.signIn) 16 | api.get('/private', auth, (req, res) => { 17 | res.status(200).send({ message: 'Tienes acceso' }) 18 | }) 19 | 20 | module.exports = api 21 | -------------------------------------------------------------------------------- /services/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const jwt = require('jwt-simple') 4 | const moment = require('moment') 5 | const config = require('../config') 6 | 7 | function createToken (user) { 8 | const payload = { 9 | sub: user._id, 10 | iat: moment().unix(), 11 | exp: moment().add(14, 'days').unix() 12 | } 13 | 14 | return jwt.encode(payload, config.SECRET_TOKEN) 15 | } 16 | 17 | function decodeToken (token) { 18 | const decoded = new Promise((resolve, reject) => { 19 | try { 20 | const payload = jwt.decode(token, config.SECRET_TOKEN) 21 | 22 | if (payload.exp <= moment().unix()) { 23 | reject({ 24 | status: 401, 25 | message: 'El token ha expirado' 26 | }) 27 | } 28 | resolve(payload.sub) 29 | } catch (err) { 30 | reject({ 31 | status: 500, 32 | message: 'Invalid Token' 33 | }) 34 | } 35 | }) 36 | 37 | return decoded 38 | } 39 | 40 | module.exports = { 41 | createToken, 42 | decodeToken 43 | } 44 | -------------------------------------------------------------------------------- /views/layouts/default.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |