Ver código fonte

Moved middleware and helpers to new file. Added basic logging to server

main
Roxie Gibson 4 anos atrás
pai
commit
c76d0fdcb4
Nenhuma chave conhecida encontrada para esta assinatura no banco de dados
4 arquivos alterados com 119 adições e 46 exclusões
  1. +55
    -7
      package-lock.json
  2. +2
    -0
      package.json
  3. +14
    -39
      src/api.ts
  4. +48
    -0
      src/route.helpers.ts

+ 55
- 7
package-lock.json Ver arquivo

@@ -1,5 +1,5 @@
{
"name": "ml-curd_api",
"name": "ml-crud_api",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
@@ -123,6 +123,14 @@
"@types/node": "*"
}
},
"@types/morgan": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.0.tgz",
"integrity": "sha512-warrzirh5dlTMaETytBTKR886pRXwr+SMZD87ZE13gLMR8Pzz69SiYFkvoDaii78qGP1iyBIUYz5GiXyryO//A==",
"requires": {
"@types/express": "*"
}
},
"@types/node": {
"version": "14.0.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.5.tgz",
@@ -168,7 +176,8 @@
"arg": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA=="
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
"dev": true
},
"argparse": {
"version": "1.0.10",
@@ -190,6 +199,14 @@
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"dev": true
},
"basic-auth": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
"integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
"requires": {
"safe-buffer": "5.1.2"
}
},
"bl": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-2.2.0.tgz",
@@ -239,7 +256,8 @@
"buffer-from": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
"dev": true
},
"builtin-modules": {
"version": "1.1.1",
@@ -344,7 +362,8 @@
"diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A=="
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
"dev": true
},
"ee-first": {
"version": "1.1.1",
@@ -544,7 +563,8 @@
"make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
"dev": true
},
"media-typer": {
"version": "0.3.0",
@@ -652,6 +672,25 @@
"resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz",
"integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ=="
},
"morgan": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz",
"integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==",
"requires": {
"basic-auth": "~2.0.1",
"debug": "2.6.9",
"depd": "~2.0.0",
"on-finished": "~2.3.0",
"on-headers": "~1.0.2"
},
"dependencies": {
"depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
}
}
},
"mpath": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/mpath/-/mpath-0.7.0.tgz",
@@ -697,6 +736,11 @@
"ee-first": "1.1.1"
}
},
"on-headers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -890,12 +934,14 @@
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
},
"source-map-support": {
"version": "0.5.19",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
"integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
"dev": true,
"requires": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
@@ -947,6 +993,7 @@
"version": "8.10.1",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.1.tgz",
"integrity": "sha512-bdNz1L4ekHiJul6SHtZWs1ujEKERJnHs4HxN7rjTyyVOFf3HaJ6sLqe6aPG62XTzAB/63pKRh5jTSWL0D7bsvw==",
"dev": true,
"requires": {
"arg": "^4.1.0",
"diff": "^4.0.1",
@@ -1034,7 +1081,8 @@
"yn": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q=="
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
"dev": true
}
}
}

+ 2
- 0
package.json Ver arquivo

@@ -17,8 +17,10 @@
"@typegoose/typegoose": "^7.1.3",
"@types/express": "^4.17.6",
"@types/mongoose": "^5.7.21",
"@types/morgan": "^1.9.0",
"express": "^4.17.1",
"mongoose": "^5.9.16",
"morgan": "^1.10.0",
"typescript": "^3.9.3"
}
}

+ 14
- 39
src/api.ts Ver arquivo

@@ -1,40 +1,12 @@

import express from "express";
import { Application, NextFunction, Request, Response, json as jsonBodyParser } from "express";
import { Application, Request, Response, json as jsonBodyParser } from "express";
// Trying to import this using import doesn't work at all for some reason. This way does though.
const morgan = require('morgan');

import { Product, ProductModel } from "./db/models";
import { QueryOperators, connect as connectDB } from "./db/connection";
import { post } from "@typegoose/typegoose";



/**
* Middleware for the server to have a simple authentication system.
* Checks headers to see if "X-Token" is set to "SECRET_KEY". If it isn't, then the server will return a 401 due to lack of authentication.
* @param req - request object
* @param res - response object
* @param next - next function in the chain
*/
function checkAuthorization(req: Request, res: Response, next: NextFunction) {
const token: string | undefined = req.get("X-Token");
if (token === "SECRET_KEY") {
next();
} else {
res.status(401).end(); // Return 401 Unauthorized due to no authentication
}
}

/**
* Converts the integer parameters in the API requests
* @param param - the string parameter passed by the user
* @throws Error if the string cannot be converted into a positive integer and thus can't be used to filter the database query.
*/
function parseParamInt(param: string): number {
const int = parseInt(param, 10);
if ( isNaN(int) || int < 0 ) {
throw new Error("Param cannot be processed into a valid number");
}
return int;
}
import * as helpers from "./route.helpers";


/**
@@ -56,10 +28,10 @@ async function getProductsEndpoint(req: Request, res: Response): Promise<void> {
let priceTo: number = -1;
try {
if (typeof priceFromStr === "string") {
priceFrom = parseParamInt(priceFromStr);
priceFrom = helpers.parseParamInt(priceFromStr);
}
if (typeof priceToStr === "string") {
priceTo = parseParamInt(priceToStr);
priceTo = helpers.parseParamInt(priceToStr);
}
} catch(error) {
// Error processing numbers
@@ -240,23 +212,26 @@ function defineEndpoints(server: Application): void {

// POST create new product
// middleware - Check if client is authorized, Parse JSON from body, then try and create product
server.post(apiBaseEndpoint, [checkAuthorization, jsonParser, createProductEndpoint]);
server.post(apiBaseEndpoint, [helpers.checkAuthorization, jsonParser, createProductEndpoint]);

// PATCH edit specified product
// middleware - Check if client is authorized, Parse JSON from body, then try and edit product
server.patch(specifiedProductEndpoint, [checkAuthorization, jsonParser, editProductEndpoint]);
server.patch(specifiedProductEndpoint, [helpers.checkAuthorization, jsonParser, editProductEndpoint]);

// DELETE specified product
// middleware - Check if client is authorized, then delete product
server.delete(specifiedProductEndpoint, [checkAuthorization, deleteProductEndpoint]);
server.delete(specifiedProductEndpoint, [helpers.checkAuthorization, deleteProductEndpoint]);
}

/**
* Runs REST API server. Makes sure the connection to the mongodb is established.
*/
export function runServer(): void {
const server = express();
connectDB();
const server = express();
server.use(morgan("dev"));
server.use(helpers.handleMiddlewareError);

defineEndpoints(server);
// Run Server
server.listen(8081, () => {

+ 48
- 0
src/route.helpers.ts Ver arquivo

@@ -0,0 +1,48 @@
import { Application, NextFunction, Request, Response } from "express";


/**
* Checks if an error has occurred and if so, return 500 to the user and don't continue processing
* @param err - error object
* @param req - request object
* @param res - response object
* @param next - next function in the chain
*/
export function handleMiddlewareError(err: Error, req: Request, res: Response, next: NextFunction) {
if (err) {
// General catch-all for any error in the middleware. Usually this is failing to parse provide JSON at all.
res.status(500).end("Something went wrong!");
return;
}
// If no error, just continue
next();
}

/**
* Middleware for the server to have a simple authentication system.
* Checks headers to see if "X-Token" is set to "SECRET_KEY". If it isn't, then the server will return a 401 due to lack of authentication.
* @param req - request object
* @param res - response object
* @param next - next function in the chain
*/
export function checkAuthorization(req: Request, res: Response, next: NextFunction) {
const token: string | undefined = req.get("X-Token");
if (token === "SECRET_KEY") {
next();
} else {
res.status(401).end(); // Return 401 Unauthorized due to no authentication
}
}

/**
* Converts the integer parameters in the API requests
* @param param - the string parameter passed by the user
* @throws Error if the string cannot be converted into a positive integer and thus can't be used to filter the database query.
*/
export function parseParamInt(param: string): number {
const int = parseInt(param, 10);
if ( isNaN(int) || int < 0 ) {
throw new Error("Param cannot be processed into a valid number");
}
return int;
}

Carregando…
Cancelar
Salvar