diff --git a/staff/marti-herms/project/V-HUB/api/.gitignore b/staff/marti-herms/project/V-HUB/api/.gitignore new file mode 100644 index 000000000..97aca2ea1 --- /dev/null +++ b/staff/marti-herms/project/V-HUB/api/.gitignore @@ -0,0 +1,2 @@ +.env +node_modules \ No newline at end of file diff --git a/staff/marti-herms/project/V-HUB/api/handlers/authenticateUserHandler.js b/staff/marti-herms/project/V-HUB/api/handlers/authenticateUserHandler.js new file mode 100644 index 000000000..56004c9f9 --- /dev/null +++ b/staff/marti-herms/project/V-HUB/api/handlers/authenticateUserHandler.js @@ -0,0 +1 @@ +export default {} \ No newline at end of file diff --git a/staff/marti-herms/project/V-HUB/api/handlers/index.js b/staff/marti-herms/project/V-HUB/api/handlers/index.js new file mode 100644 index 000000000..5cba2fd31 --- /dev/null +++ b/staff/marti-herms/project/V-HUB/api/handlers/index.js @@ -0,0 +1,9 @@ +import authenticateUserHandler from './authenticateUserHandler.js' +import registerUserHandler from './registerUserHandler.js' + +const handle = { + authenticateUser: authenticateUserHandler, + registerUser: registerUserHandler +} + +export default handle \ No newline at end of file diff --git a/staff/marti-herms/project/V-HUB/api/handlers/registerUserHandler.js b/staff/marti-herms/project/V-HUB/api/handlers/registerUserHandler.js new file mode 100644 index 000000000..56004c9f9 --- /dev/null +++ b/staff/marti-herms/project/V-HUB/api/handlers/registerUserHandler.js @@ -0,0 +1 @@ +export default {} \ No newline at end of file diff --git a/staff/marti-herms/project/V-HUB/api/index.js b/staff/marti-herms/project/V-HUB/api/index.js index e69de29bb..beb72c954 100644 --- a/staff/marti-herms/project/V-HUB/api/index.js +++ b/staff/marti-herms/project/V-HUB/api/index.js @@ -0,0 +1,26 @@ +import 'dotenv/config' +import express from 'express' + +import { mongoose } from 'core' + +import { cors, jsonBodyParser, jwtVerifier, errorHandler } from './middleware' + +import handle from './handlers' + +mongoose.connect(process.env.MONGODB_URI) + .then(() => { + console.info(`API connected to ${process.env.MONGODB_URI}`) + + const api = express() + + api.use(cors) + + api.post('/users', jsonBodyParser, handle.registerUser) + + api.post('/users/auth', jsonBodyParser, handle.authenticateUser) + + api.use(errorHandler) + + api.listen(process.env.PORT, () => console.info(`API listening on PORT ${process.env.PORT}`)) + }) + .catch(error => console.error(error)) \ No newline at end of file diff --git a/staff/marti-herms/project/V-HUB/api/middleware/cors.js b/staff/marti-herms/project/V-HUB/api/middleware/cors.js new file mode 100644 index 000000000..88a3b4c3d --- /dev/null +++ b/staff/marti-herms/project/V-HUB/api/middleware/cors.js @@ -0,0 +1,7 @@ +export default (req, res, next) => { + res.setHeader('Access-Control-Allow-Origin', '*') + res.setHeader('Access-Control-Allow-Headers', '*') + res.setHeader('Access-Control-Allow-Methods', '*') + + next() +} \ No newline at end of file diff --git a/staff/marti-herms/project/V-HUB/api/middleware/errorHandler.js b/staff/marti-herms/project/V-HUB/api/middleware/errorHandler.js new file mode 100644 index 000000000..a63c41e2c --- /dev/null +++ b/staff/marti-herms/project/V-HUB/api/middleware/errorHandler.js @@ -0,0 +1,24 @@ +import { errors } from 'com' + +const { NotFoundError, CredentialsError, DuplicityError, SessionError, ValidationError } = errors + +export default (error, req, res, next) => { + let status = 500 + + if (error instanceof NotFoundError) + status = 404 + + else if (error instanceof CredentialsError || error instanceof DuplicityError) + ststus = 409 + + else if (error instanceof OwnershipError) + status = 403 + + else if (error instanceof ValidationError) + status = 400 + + else if (error instanceof SessionError) + status = 498 + + res.status(status).json({ error: error.constructor.name, message: error.message }) +} \ No newline at end of file diff --git a/staff/marti-herms/project/V-HUB/api/middleware/index.js b/staff/marti-herms/project/V-HUB/api/middleware/index.js new file mode 100644 index 000000000..3209532c9 --- /dev/null +++ b/staff/marti-herms/project/V-HUB/api/middleware/index.js @@ -0,0 +1,11 @@ +import cors from './cors.js' +import jsonBodyParser from './jsonBodyParser.js' +import jwtVerifier from './jwtVerifier.js' +import errorHandler from './errorHandler.js' + +export { + cors, + jsonBodyParser, + jwtVerifier, + errorHandler +} \ No newline at end of file diff --git a/staff/marti-herms/project/V-HUB/api/middleware/jsonBodyParser.js b/staff/marti-herms/project/V-HUB/api/middleware/jsonBodyParser.js new file mode 100644 index 000000000..e515e98ee --- /dev/null +++ b/staff/marti-herms/project/V-HUB/api/middleware/jsonBodyParser.js @@ -0,0 +1,11 @@ +export default (req, res, next) => { + req.setEncoding('utf-8') + + req.on('data', json => { + const body = JSON.parse(json) + + req.body = body + + next() + }) +} \ No newline at end of file diff --git a/staff/marti-herms/project/V-HUB/api/middleware/jwtVerifier.js b/staff/marti-herms/project/V-HUB/api/middleware/jwtVerifier.js new file mode 100644 index 000000000..114496875 --- /dev/null +++ b/staff/marti-herms/project/V-HUB/api/middleware/jwtVerifier.js @@ -0,0 +1,26 @@ +import 'dotenv/config' +import jwt from 'jsonwebtoken' + +import { errors } from 'com' + +const { SessionError } = errors + +export default (req, res, next) => { + const { authorization } = req.headers + + const token = authorization.slice(7) + + jwt.verifier(token, process.env.JWT_SECRET, (error, payload) => { + if (error) { + res.status(498).json({ error: SessionError.name, message: error.message }) + + return + } + + const { sub: userId } = payload + + req.userId = userId + + next() + }) +} \ No newline at end of file diff --git a/staff/marti-herms/project/V-HUB/api/package-lock.json b/staff/marti-herms/project/V-HUB/api/package-lock.json index ecc268fd2..5fdee33dc 100644 --- a/staff/marti-herms/project/V-HUB/api/package-lock.json +++ b/staff/marti-herms/project/V-HUB/api/package-lock.json @@ -9,9 +9,33 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "dotenv": "^16.4.5", + "com": "file:../com", + "core": "file:../core", "express": "^4.19.2", "jsonwebtoken": "^9.0.2" + }, + "devDependencies": { + "dotenv": "^16.4.5" + } + }, + "../com": { + "version": "1.0.0", + "license": "ISC" + }, + "../core": { + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "bcryptjs": "^2.4.3", + "com": "file:../com", + "mongoose": "^8.5.2" + }, + "devDependencies": { + "c8": "^10.1.2", + "chai": "^4.5.0", + "dotenv": "^16.4.5", + "mocha": "^10.7.0", + "monocart-coverage-reports": "^2.10.2" } }, "node_modules/accepts": { @@ -85,6 +109,10 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/com": { + "resolved": "../com", + "link": true + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -117,6 +145,10 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "node_modules/core": { + "resolved": "../core", + "link": true + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -162,6 +194,7 @@ "version": "16.4.5", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "dev": true, "engines": { "node": ">=12" }, diff --git a/staff/marti-herms/project/V-HUB/api/package.json b/staff/marti-herms/project/V-HUB/api/package.json index 99e134577..d753bfc8a 100644 --- a/staff/marti-herms/project/V-HUB/api/package.json +++ b/staff/marti-herms/project/V-HUB/api/package.json @@ -4,6 +4,9 @@ "main": "index.js", "type": "module", "scripts": { + "start": "node .", + "watch": "node --watch .", + "inspect": "node --inspect-brk .", "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], @@ -11,8 +14,12 @@ "license": "ISC", "description": "", "dependencies": { - "dotenv": "^16.4.5", + "com": "file:../com", + "core": "file:../core", "express": "^4.19.2", "jsonwebtoken": "^9.0.2" + }, + "devDependencies": { + "dotenv": "^16.4.5" } } \ No newline at end of file diff --git a/staff/marti-herms/project/V-HUB/com/errors.js b/staff/marti-herms/project/V-HUB/com/errors.js index 18989e4e9..6396fc998 100644 --- a/staff/marti-herms/project/V-HUB/com/errors.js +++ b/staff/marti-herms/project/V-HUB/com/errors.js @@ -38,12 +38,21 @@ class CredentialsError extends Error { } } +class SessionError extends Error { + constructor(message) { + super(message) + + this.name = this.constructor.name + } +} + const errors = { ValidationError, SystemError, DuplicityError, NotFoundError, - CredentialsError + CredentialsError, + SessionError } export default errors \ No newline at end of file diff --git a/staff/marti-herms/project/V-HUB/core/index.js b/staff/marti-herms/project/V-HUB/core/index.js index 070f4d855..4d2d6efd1 100644 --- a/staff/marti-herms/project/V-HUB/core/index.js +++ b/staff/marti-herms/project/V-HUB/core/index.js @@ -2,7 +2,7 @@ import mongoose from 'mongoose' import logic from './logic' import data from './data' -export default { +export { mongoose, logic, data diff --git a/staff/marti-herms/project/V-HUB/core/logic/index.js b/staff/marti-herms/project/V-HUB/core/logic/index.js index 56004c9f9..a58ac9e8d 100644 --- a/staff/marti-herms/project/V-HUB/core/logic/index.js +++ b/staff/marti-herms/project/V-HUB/core/logic/index.js @@ -1 +1,9 @@ -export default {} \ No newline at end of file +import authenticateUser from './authenticateUser.js' +import registerUser from './registerUser.js' + +const logic = { + authenticateUser, + registerUser +} + +export default logic \ No newline at end of file