Procházet zdrojové kódy

Merge branch 'ProjectCleanup' of stefan.stamenovic/diligent-node-api into develop

develop
radivoje.milutinovic před 3 roky
rodič
revize
f3327d8ce5

+ 32
- 0
README.md Zobrazit soubor

@@ -0,0 +1,32 @@
# Node API template

This is template of web API with mongo db as database node.js as runtime and express as framework. We have user model and JWT tokens setup as well as mediator pattern and winston as logger.

## Setup

Here we will show you how to set up project and run it in localhost. If you want to run it in [docker]() or [portainer]() you can find setup on links.

- [Project setup](#project-setup)
- [Database setup](#database-setup)

### Database setup
Please follow the official tutorial on installing and running the latest Mongo database which can be found on this link:
> https://docs.mongodb.com/manual/tutorial/install-mongodb-on-windows/

### Project setup
In order to run our project you need to clone it from [git](https://git.dilig.net/stefan.stamenovic/diligent-node-api) first. Open terminal (cmd and powershell work as well) in folder that you want this project to be and run command
> git clone http://git.dilig.net/stefan.stamenovic/diligent-node-api.git

After cloning project you can open it with your preferred IDE/Code editor if you want to see the code. Before running project you need to open terminal and run command
> npm install

Running that command will download all necessary npm packages to run the project.

After that you want to move to src directory
> cd src

Now you can run the project using
> node server.js

Congratulations! You now ran backend api for template. You can see swagger documentation in the browser with the url
> http://localhost:3001/swagger

+ 11
- 0
package-lock.json Zobrazit soubor

@@ -16,6 +16,7 @@
"express-jwt": "^7.7.2",
"helmet": "^5.1.0",
"joi": "^17.6.0",
"joi-objectid": "^4.0.2",
"jsonwebtoken": "^8.5.1",
"migrate-mongo": "^9.0.0",
"mongodb": "^4.6.0",
@@ -1681,6 +1682,11 @@
"@sideway/pinpoint": "^2.0.0"
}
},
"node_modules/joi-objectid": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/joi-objectid/-/joi-objectid-4.0.2.tgz",
"integrity": "sha512-OjYM+wK/JGo2bSb9ADEyzxxROJPZYtrqIwBbywpJ2a98oKlbLtWTKvpzmrnXzou69Ey9EsjHsFvZYzpjeCdKuA=="
},
"node_modules/js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
@@ -4614,6 +4620,11 @@
"@sideway/pinpoint": "^2.0.0"
}
},
"joi-objectid": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/joi-objectid/-/joi-objectid-4.0.2.tgz",
"integrity": "sha512-OjYM+wK/JGo2bSb9ADEyzxxROJPZYtrqIwBbywpJ2a98oKlbLtWTKvpzmrnXzou69Ey9EsjHsFvZYzpjeCdKuA=="
},
"js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",

+ 1
- 0
package.json Zobrazit soubor

@@ -20,6 +20,7 @@
"express-jwt": "^7.7.2",
"helmet": "^5.1.0",
"joi": "^17.6.0",
"joi-objectid": "^4.0.2",
"jsonwebtoken": "^8.5.1",
"migrate-mongo": "^9.0.0",
"mongodb": "^4.6.0",

+ 1
- 1
src/database/models/user.js Zobrazit soubor

@@ -12,7 +12,7 @@ const userSchema = new mongoose.Schema({
type: String,
required: true
},
role: {
roles: {
type: String
},
tokens: [{

+ 41
- 0
src/endpoints/auth.js Zobrazit soubor

@@ -0,0 +1,41 @@
const Auth = require('../database/models/token')
const authMediator = require('../mediator/authMediator')

const token = async (req, res, next) => {
try {
if (!req.body.email || !req.body.password){
return res.status(400).send('Pass credentials')
}

const result = await authMediator.token()
if(!result.succeeded){
res.status(401).send(result.error)
}

return res.status(200).send(result.value)
} catch (e) {
next(e)
}
}

const logout = async (req, res) => {
const result = await Auth.destroyToken(req.body.token)
if (!result) {
return res.status(404).send('No user has the token provided!')
}
return res.send('Auth ' + req.body.token + ' invalidated!')
}

const refreshUserToken = async (req, res) => {
const form = {
token: req.body.token
}
const result = await Auth.refreshAuthToken(form.token)
if (!result) {
return res.status(404).send('Auth not valid!')
}

return res.status(201).send('Auth ' + result + ' refreshed successfully!')
}

module.exports = { loginUser: token, logout, refreshUserToken }

+ 0
- 43
src/endpoints/token.js Zobrazit soubor

@@ -1,43 +0,0 @@
const Token = require('../database/models/token')
const bcrypt = require('bcryptjs')

const loginUser = async (req, res, next) => {
try {
const findUser = await Token.findByCredentials(req.body.email, req.body.password)

if (!findUser) {
return res.status(400).send('Wrong credentials!')
}
const isValidPassword = await bcrypt.compare(req.body.password, findUser.password)
if (!isValidPassword) {
return res.status(400).send('Wrong credentials!')
}
const token = await Token.generateAuthToken(findUser)

return res.status(201).send(findUser)
} catch (e) {
next(e)
}
}

const logout = async (req, res) => {
const result = await Token.destroyToken(req.body.token)
if (!result) {
return res.status(404).send('No user has the token provided!')
}
return res.send('Token ' + req.body.token + ' invalidated!')
}

const refreshUserToken = async (req, res) => {
const form = {
token: req.body.token
}
const result = await Token.refreshAuthToken(form.token)
if (!result) {
return res.status(404).send('Token not valid!')
}

return res.status(201).send('Token ' + result + ' refreshed successfully!')
}

module.exports = { loginUser, logout, refreshUserToken }

+ 44
- 58
src/endpoints/user.js Zobrazit soubor

@@ -1,6 +1,6 @@
const bcrypt = require("bcryptjs/dist/bcrypt")
const User = require("../database/models/user")
const { getUserValidator } = require("../validators/users")
const { getUserValidator, getIdValidator, getUpdatedUserValidator } = require("../validators/users")
const mediator = require('../mediator/userMediator')

const getUsers = async (req, res, next) => {
const allUsers = await User.find({})
@@ -8,19 +8,18 @@ const getUsers = async (req, res, next) => {
}

const getUser = async (req, res, next) => {
const result = getUserValidator.validate(req.body)
console.log(result)
try {
const id = req.params.id
if (!id) {
return res.status(400).send('Bad request')
}
const user = await User.findById(id)
if (!user) {
return res.status(404).send("User with the id of: " + id + " doesnt exist")
const errId = getIdValidator.validate(id).error
if (errId) {
return res.status(404).send(errId.message)
}
const result = await mediator.getUser(id)

return res.json(user)
if (!result.succeeded) {
return res.status(400).send(result.error)
}
return res.status(200).json(result.value)
} catch (e) {
next(e)
}
@@ -29,20 +28,17 @@ const getUser = async (req, res, next) => {
const createUser = async (req, res, next) => {
try {
const userModel = req.body
if (Object.entries(userModel).length === 0) {
return res.status(400).send('Object cant be empty')
}

const err = getUserValidator.validate(userModel).error
if (err) {
return res.status(401).send(err.message)
return res.status(404).send(err.message)
}
const result = await mediator.createUser(userModel)
if (!result.succeeded) {
return res.status(400).send(result.error)
}

const newUser = new User(userModel)
newUser.password = await bcrypt.hash(newUser.password, 8)
await newUser.save()

return res.status(201).json(newUser)
return res.status(201).json(result.value)
} catch (e) {
next(e)
}
@@ -51,31 +47,40 @@ const createUser = async (req, res, next) => {
const updateUser = async (req, res, next) => {
try {
const id = req.params.id
const objBody = req.body
if (Object.entries(objBody).length == 0) {
return res.status(400).send('Invalid input parameters')
const errId = getIdValidator.validate(id).error
if (errId) {
return res.status(400).send(errId.message)
}

const err = getUserValidator.validate(objBody).error
const objBody = req.body

const err = getUpdatedUserValidator.validate(objBody).error
if (err) {
return res.status(400).send(err.message)
}

let user = await User.findById(id);
if (!user) {
return res.status(404).send("User with the id of: " + id + " doesnt exist")
const result = await mediator.updateUser(id, objBody)
if (!result.succeeded) {
return res.status(400).send(result.error)
}
return res.status(200).send(result.value)
} catch (e) {
next(e)
}
}

const updatedUser = {
name: objBody.name,
password: objBody.password,
email: objBody.email
const deleteUser = async (req, res, next) => {
try {
const id = req.params.id
const errId = getIdValidator.validate(id).error
if (errId) {
return res.status(400).send(errId.message)
}


await User.updateOne({ _id: req.params.id }, updatedUser)

return res.status(200).send('User updated successfully')
const result = await mediator.deleteUser(id)
if (!result.succeeded) {
return res.status(400).send(result.error)
}
return res.status(204).send(result.value)
} catch (e) {
next(e)
}
@@ -83,11 +88,11 @@ const updateUser = async (req, res, next) => {

const updateUserContacts = async (req, res, next) => {
try {
userFound = true
const userFound = true
if (!userFound) {
return res.status(404).send('user not found')
}
if (Object.entries(req.body).length == 0) {
if (!req.body) {
return res.status(400).send('invalid input parameters')
}
return res.status(200).send('user contacts updated successfully')
@@ -96,24 +101,5 @@ const updateUserContacts = async (req, res, next) => {
}
}

const deleteUser = async (req, res, next) => {
try {
const id = req.params.id
if (!id) {
return res.status(400).send('You need to provide valid Id')
}

const user = await User.findById(id)
if (!user) {
return res.status(404).send("User with the id of: " + id + " doesnt exist")
}

await User.deleteOne(user)

return res.status(204).send('Deleting user with id of ' + id)
} catch (e) {
next(e)
}
}
module.exports = { getUsers, getUser, createUser, updateUserContacts, updateUser, deleteUser }

module.exports = { getUsers, getUser, createUser, updateUser, updateUserContacts, deleteUser }

+ 1
- 1
src/logging/loggerDbCon.js Zobrazit soubor

@@ -6,7 +6,7 @@ const loggerWinston = winston.createLogger({
format: winston.format.json(),
defaultMeta: { service: 'user-service' },
transports: [
new winston.transports.File({ filename: '..public/loggerFiles/dbCon.log', level: 'silly' })
new winston.transports.File({ filename: '../public/loggerFiles/dbCon.log', level: 'silly' })
],
});

+ 31
- 0
src/mediator/authMediator.js Zobrazit soubor

@@ -0,0 +1,31 @@
const Auth = require("../database/models/token");
const bcrypt = require("bcryptjs");

const token = async (email, password) => {
const foundUser = await Auth.findByCredentials(email, password)

if (!foundUser) {
return {
succeeded: false,
value: null,
error: 'Wrong credentials!'
}
}
const isValidPassword = await bcrypt.compare(password, foundUser.password)
if (!isValidPassword) {
return {
succeeded: false,
value: null,
error: 'Wrong credentials!'
}
}
const token = await Auth.generateAuthToken(foundUser)

return {
succeeded: true,
value: token,
error: null
}
}

module.exports = {token}

+ 80
- 0
src/mediator/userMediator.js Zobrazit soubor

@@ -0,0 +1,80 @@
const bcrypt = require("bcryptjs/dist/bcrypt")
const User = require("../database/models/user")
const { getUserValidator } = require("../validators/users")

const getUser = async (id) => {
const user = await User.findById(id)
if (!user) {
return {
succeeded: false,
value: null,
error: 'User not found!'
}
}
return {
succeeded: true,
value: user,
error: null
}
}

const createUser = async (user) => {
const foundMail = await User.find({ email: user.email })
if (foundMail.length) {
return {
succeeded: false,
value: null,
error: 'User with email already exists!'
}
}

const newUser = new User(user)
newUser.password = await bcrypt.hash(newUser.password, 8)
await User.create(newUser)
return {
succeeded: true,
value: newUser,
error: null
}
}

const updateUser = async (id, body) => {
let user = await User.findById(id);
if (!user) {
return {
succeeded: false,
value: null,
error: 'User with id ' + id + ' does not exist!'
}
}

const updatedUser = {
name: body.name,
roles: body.roles
}
await User.updateOne({ _id: id }, updatedUser)
return {
succeeded: true,
value: 'User updated successfully!',
error: null
}
}

const deleteUser = async (id) => {
let user = await User.findById(id)
if (!user) {
return {
succeeded: false,
value: null,
error: 'User with id ' + id + ' does not exist!'
}
}
await User.deleteOne(user)
return {
succeeded: true,
value: null,
error: null
}
}

module.exports = {getUser, createUser, updateUser, deleteUser}

+ 0
- 2
src/middleware/errorHandling.js Zobrazit soubor

@@ -2,8 +2,6 @@ const logger = require('../logging/logger')
const config = require('config')
const errorLogger = (err, req, res, next) => {
//console.error(err)
if (config.util.getEnv('NODE_ENV') === 'development')
logger.error(err)

src/routes/token.js → src/routes/auth.js Zobrazit soubor

@@ -1,6 +1,6 @@
const express = require('express')
const router = new express.Router()
const endpoints = require('../endpoints/token')
const endpoints = require('../endpoints/auth')


router.post('/auth/token', endpoints.loginUser)

+ 2
- 2
src/server.js Zobrazit soubor

@@ -3,7 +3,7 @@ const express = require('express')
const app = express()
const port = process.env.NODE_ENV === 'production' ? 80 : 3001
require('./database/mongoose')
const docs = require('./swagger.js');
const docs = require('./swagger.js')
const swaggerUI = require('swagger-ui-express')
const { errorLogger, errorResponder } = require('./middleware/errorHandling.js')
const requestLogging = require('./middleware/requestLogging.js')
@@ -17,7 +17,7 @@ const routesDirectory = path.resolve(__dirname) + '/routes/'
app.use(errorLogger);
app.use(errorResponder);
app.use(express.json())
app.use('/api-docs', swaggerUI.serve, swaggerUI.setup(docs))
app.use('/swagger', swaggerUI.serve, swaggerUI.setup(docs))
app.use(requestLogging)
app.use(cors())
app.use(helmet())

+ 16
- 9
src/validators/users.js Zobrazit soubor

@@ -1,12 +1,19 @@
const Joi = require("joi");
Joi.objectId = require('joi-objectid')(Joi)

const schema = {
getUserValidator: Joi.object({
name: Joi.string().min(2).required(),
password: Joi.string().min(8).regex(/[a-zA-Z0-9]{3,30}/).required(),
email: Joi.string().email().required(),
role: Joi.string()
})
}

module.exports = schema
const getIdValidator = Joi.objectId().required();

const getUserValidator = Joi.object({
name: Joi.string().min(2).required(),
password: Joi.string().min(8).regex(/[a-zA-Z0-9]{3,30}/).required(),
email: Joi.string().email().required(),
roles: Joi.string()
})

const getUpdatedUserValidator = Joi.object({
name: Joi.string().min(2).required(),
roles: Joi.string()
})

module.exports = {getUserValidator, getIdValidator, getUpdatedUserValidator}

Načítá se…
Zrušit
Uložit