From 55f1c6e091d5e877250d7e864e9ae976e0d13a85 Mon Sep 17 00:00:00 2001 From: "Josepablo C." Date: Thu, 5 Oct 2023 11:29:24 -0600 Subject: [PATCH] Initial commit --- .env | 7 +++ .gitignore | 3 ++ Dockerfile | 21 +++++++++ README.md | 7 +++ config/apiConfig.json | 35 +++++++++++++++ dotenv | 7 +++ index.js | 62 +++++++++++++++++++++++++++ lib/Middlewares.js | 87 ++++++++++++++++++++++++++++++++++++++ lib/jwtValidator.js | 23 ++++++++++ package.json | 44 +++++++++++++++++++ sections/sections.js | 17 ++++++++ sections/test/routes.js | 8 ++++ sections/test/services.js | 20 +++++++++ sections/users/routes.js | 9 ++++ sections/users/services.js | 20 +++++++++ 15 files changed, 370 insertions(+) create mode 100644 .env create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 config/apiConfig.json create mode 100644 dotenv create mode 100644 index.js create mode 100644 lib/Middlewares.js create mode 100644 lib/jwtValidator.js create mode 100644 package.json create mode 100644 sections/sections.js create mode 100644 sections/test/routes.js create mode 100644 sections/test/services.js create mode 100644 sections/users/routes.js create mode 100644 sections/users/services.js diff --git a/.env b/.env new file mode 100644 index 0000000..ad40806 --- /dev/null +++ b/.env @@ -0,0 +1,7 @@ +SERVER_PORT=8080 +API_CONFIG=/config/apiConfig.json +ROOT_PATH=/home/josepablocb/Documents/Work/EnRuta/SysS/ETAAPI +############################ +# PATHS relative to ROOT +############################ +LIB_PATH=lib diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..65d7854 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +.env +package-lock.json diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..3bfa3f2 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,21 @@ +# Use an official Python runtime as a parent image +FROM node:14-alpine + +# Set the working directory to /app +WORKDIR /app + +# Copy the current directory contents into the container at /app +COPY sections /app/sections +COPY lib /app/lib +COPY config /app/config +COPY index.js /app +COPY package.json /app +COPY dotenv /app/.env + +RUN npm install 2>/dev/null + +EXPOSE 8080 + +ENV ROOT_PATH="/app" + +ENTRYPOINT npm start diff --git a/README.md b/README.md new file mode 100644 index 0000000..50a3da4 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# ETA API + +ETA Viaporte API + +# Dependencies + + - NodeJS v18 \ No newline at end of file diff --git a/config/apiConfig.json b/config/apiConfig.json new file mode 100644 index 0000000..c8a2733 --- /dev/null +++ b/config/apiConfig.json @@ -0,0 +1,35 @@ +{ + "authentication": { + "jwtSecret":"9o3BBz0EsrwXliwEJ/SFuywZoN8=", + "jwtTimeout":720, + "tokenSecret":"9Z'jMt|(h_f(&/S+zv.K", + "jwtOptions": { + "header": { + "typ": "access" + }, + "audience": "https://www.etaviaporte.com", + "issuer": "etaviaporte", + "algorithm": "HS256", + "expiresIn": "1d" + } + }, + "version" : { + "version" : "0.0.0", + "name": "ETA Beta", + "date":"10/2023" + }, + "S3" : { + "accessKeyId": "AKIAXTQEUF6MLCHTUIKW", + "secretAccessKey": "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", + "bucket": "enruta", + "region": "us-west-1" + }, + "sendgrid" : { + "HOST": "smtp.sendgrid.net", + "PORT": "465", + "username": "apikey", + "API_KEY": "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", + "FROM": "noreply@etaviaporte.com" + }, + "mongodb": "mongodb+srv://enruta_admin:NeptFx4RUZG8OsfA@enruta.vwofshy.mongodb.net/enrutaviaporte?retryWrites=true&w=majority" +} diff --git a/dotenv b/dotenv new file mode 100644 index 0000000..f645ca9 --- /dev/null +++ b/dotenv @@ -0,0 +1,7 @@ +SERVER_PORT=8080 +API_CONFIG=/config/apiConfig.json +ROOT_PATH=/home/josepablocb/Documents/Work/EnRuta/SysS/ETAAPI +############################ +# PATHS relative to ROOT +############################ +LIB_PATH=/lib diff --git a/index.js b/index.js new file mode 100644 index 0000000..1171e8b --- /dev/null +++ b/index.js @@ -0,0 +1,62 @@ +'use strict'; +const express = require('express'); +require('dotenv').config(); +const { ROOT_PATH , LIB_PATH } = process.env; + +const cors = require('cors'); +const compression = require('compression'); +const morgan = require('morgan'); +const helmet = require('helmet'); +const bodyParser = require('body-parser'); +const fileUpload = require('express-fileupload'); +const sections = require('./sections/sections.js'); +const middleWares = require( `${ROOT_PATH}/${LIB_PATH}/Middlewares.js` ); + +const app = express(); +const serverPort = process.env.SERVER_PORT || 3000; + +app.use( middleWares.Auth ); +app.use( + fileUpload({ + limits: { fileSize: 4 * 1024 * 1024 }, + abortOnLimit: true, + limitHandler: (req,res,next) => { + req.limitSize = true; + }, + }) +); +app.use((req, res, next) => { + if (req.limitSize) { + res.status(413).send({message:"File size limit has been reached",status:"PAYLOAD_TOO_LARGE"}); + }else{ + next() + } + +}); +app.use(bodyParser.urlencoded({ extended: true, limit: '50mb' })); +app.use(bodyParser.json({ limit: '50mb' })); +app.use(morgan('dev')); +app.use(helmet()); +app.use(compression()); +app.use(cors({ + origin: '*', + methods: [ + 'GET', + 'POST', + 'PATCH', + 'PUT', + 'DELETE' + ], + allowedHeaders: ['Content-Type', 'Authorization'] +})); +app.use( middleWares.errorJSON ); +app.use( sections ); +app.use( middleWares.error404 ); + +app.listen( serverPort , function(err){ + if( !err ){ + console.log('API listen on port', serverPort ); + }else{ + console.log( err ); + } +}); diff --git a/lib/Middlewares.js b/lib/Middlewares.js new file mode 100644 index 0000000..a843dc5 --- /dev/null +++ b/lib/Middlewares.js @@ -0,0 +1,87 @@ +'use strict'; +/** +* HASH +***************************************************** +* DEPENDENCIES +***************************************************** +* Based on Express Framework +* System +***************************************************** +* PUBLIC METHODS +***************************************************** +* Auth( req, res, next) +* Extract JWT or BasicAuth data +* errorJSON( error , request , response , next ) +* Generate error response on bad JSON format +* error404( request , response , next ) +* Generate error 404 response +* apiKey( request , response , next ) +* Generate error on invalid apikey +**/ + +/// Extract JWT or BasicAuth +function Auth( req, res , next ){ + /// + /// Try to extract the authorization data from headers + /// + let auth; + if( req.headers.hasOwnProperty( "authorization" ) ){ + auth = req.headers.authorization; + auth = auth.split(" ")[1]; + if( !auth ){ console.log( "NO HEADER AUTH available" ); return next(); } + //console.log( auth ); + /// Try BasicAuth { + try{ + let ba = Buffer.from( auth , 'base64' ).toString() + //const [user,pass] = ba.split(':'); + ba = ba.split(':'); + if( ba.length == 2 ){ + req.basicAuth = { user : ba[0] , password : ba[1] }; + } + }catch(error){ + console.log("MIDDLEWARE_AUTH_ERR_BA",error); + } + /// Try BasicAuth } + }else if( req.query.access_token ){ + auth = req.query.access_token; + if( !auth ){ console.log( "NO QUERY AUTH available" ); return next(); } + } + if( auth ){ + /// Try JWT { + try{ + let jwt = auth.split("."); + if( jwt.length == 3 ){ + req.JWT = {}; + req.JWT.raw = auth; + } + }catch( error ){ + console.log("MIDDLEWARE_AUTH_ERR_JWT",error); + } + /// Try JWT } + } + next(); +} + +function errorJSON( error , request , response , next ){ + console.log(error); + if( error !== null ){ + /// For body-parser errors + if( error instanceof SyntaxError && error.status === 400 && 'body' in error ){ + return response.status(400).json({ error : 'Invalid json' , code : 400 }); + } + /// For any error + return response.status(500).send( { error: "Internal server error" , code : 500 } ); + }else{ + return next(); + } +} + +function error404( request , response , next ){ + return response.status(404).send( { error : "Page not found", code : 404 } ); +} + +module.exports = { + Auth, + errorJSON, + error404, +}; diff --git a/lib/jwtValidator.js b/lib/jwtValidator.js new file mode 100644 index 0000000..b8ef7ba --- /dev/null +++ b/lib/jwtValidator.js @@ -0,0 +1,23 @@ +'user strict'; +const { ROOT_PATH, API_CONFIG } = process.env; +const jwt = require('jsonwebtoken'); + +const apiConfig = require(ROOT_PATH + API_CONFIG); +const secret = apiConfig.authentication.jwtSecret; + +function middleware( req, res, next ){ + if( req.JWT ){ + req.JWT.payload = jwt.verify( req.JWT.raw, apiConfig.authentication.jwtSecret , (err, user) => { + if( err ){ + return res.status(401).send({error:"Unauthorized",code:401}); + } + }); + next(); + }else{ + return res.status(401).send({error:"Unauthorized",code:401}); + } +} + +module.exports = { + middleware +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..e47a5d0 --- /dev/null +++ b/package.json @@ -0,0 +1,44 @@ +{ + "name": "etaapi", + "version": "1.0.0", + "description": "ETA API", + "main": "index.js", + "scripts": { + "start": "nodemon index.js", + "dev": "nodemon index.js" + }, + "repository": { + "type": "git", + "url": "git+https://gitlab.com/jcruzbaasworkspace/enruta/etaapi.git" + }, + "author": "Josepablo C.", + "license": "ISC", + "bugs": { + "url": "https://gitlab.com/jcruzbaasworkspace/enruta/etaapi/issues" + }, + "homepage": "https://gitlab.com/jcruzbaasworkspace/enruta/etaapi#readme", + "dependencies": { + "audit": "^0.0.6", + "aws-sdk": "^2.1470.0", + "axios": "^1.5.1", + "body-parser": "^1.20.2", + "compression": "^1.7.4", + "cors": "^2.8.5", + "crypto-js": "^4.1.1", + "dotenv": "^16.3.1", + "express": "^4.18.2", + "express-fileupload": "^1.4.1", + "express-jwt": "^8.4.1", + "form-data": "^4.0.0", + "helmet": "^7.0.0", + "jsonwebtoken": "^9.0.2", + "knex": "^2.5.1", + "mongodb-core": "^3.2.7", + "mongoose": "^7.5.4", + "morgan": "^1.10.0", + "nodemailer": "^6.9.5", + "nodemon": "^3.0.1", + "objection": "^3.1.2", + "uuid": "^9.0.1" + } +} diff --git a/sections/sections.js b/sections/sections.js new file mode 100644 index 0000000..e61a9b1 --- /dev/null +++ b/sections/sections.js @@ -0,0 +1,17 @@ +'use strict'; +const { ROOT_PATH , LIB_PATH } = process.env; + +/// Router instance +const router = require('express').Router(); +const jwtValidator = require( `${ROOT_PATH}/${LIB_PATH}/jwtValidator.js` ); + +const test = require('./test/routes.js'); +const users = require('./users/routes.js'); + +router.use("/test", test); + +router.use( jwtValidator.middleware ); + +router.use("/users", users); + +module.exports = router; diff --git a/sections/test/routes.js b/sections/test/routes.js new file mode 100644 index 0000000..efd3320 --- /dev/null +++ b/sections/test/routes.js @@ -0,0 +1,8 @@ +'use strict'; +const router = require('express').Router(); +const services= require('./services.js'); + +router.get('/apitest', services.postTest); +router.get('/version', services.getVersion); + +module.exports = router; diff --git a/sections/test/services.js b/sections/test/services.js new file mode 100644 index 0000000..316d4c2 --- /dev/null +++ b/sections/test/services.js @@ -0,0 +1,20 @@ +"use strict"; + +const apiConfig = require( process.env.ROOT_PATH + process.env.API_CONFIG ); + +const postTest = async(req, res) => { + res.send({ + msg:"Hello world!", + data:{ + apiQuery:req.query, + apiParams:req.params.params, + body:req.body, + } + }); +}; + +const getVersion = async(req, res) => { + res.send( apiConfig.version ); +}; + +module.exports = { postTest , getVersion }; \ No newline at end of file diff --git a/sections/users/routes.js b/sections/users/routes.js new file mode 100644 index 0000000..86b17e3 --- /dev/null +++ b/sections/users/routes.js @@ -0,0 +1,9 @@ +'use strict'; +const router = require('express').Router(); +const services= require('./services.js'); + +router.get('/', services.getUsers); +router.get('/profile', services.getProfileData); +router.get('/:userId', services.getUserData); + +module.exports = router; diff --git a/sections/users/services.js b/sections/users/services.js new file mode 100644 index 0000000..f5776a6 --- /dev/null +++ b/sections/users/services.js @@ -0,0 +1,20 @@ +"use strict"; + +const apiConfig = require( process.env.ROOT_PATH + process.env.API_CONFIG ); + +const getUsers = async(req, res) => { + console.log( req.params ); + res.send({ user : "hello world!" }); +}; + +const getUserData = async(req, res) => { + console.log( req.params ); + res.send({ user : "hello world!" }); +}; + +const getProfileData = async(req, res) => { + console.log( req.params ); + res.send({ user : "hello world!" }); +}; + +module.exports = { getUsers , getUserData , getProfileData}; \ No newline at end of file