UNPKG

nsfw-api

Version:
491 lines (425 loc) 19.3 kB
import SequelizeSchemeController from "../controllers/SequelizeSchemeController"; import HttpStatus from 'http-status-codes'; import SystemInformationSchedule from '../schedules/SystemInformationSchedule' import SequelizeController from "../controllers/SequelizeController"; import Express from "express"; //express as server import MetricsHelper from "../helper/MetricsHelper"; import DateHelper from "../helper/DateHelper"; import DefaultControllerHelper from "../helper/DefaultControllerHelper"; import MyAuthMiddlewares from "../auth/MyAuthMiddlewares"; import CustomControllers from "../controllers/CustomControllers"; import ServerController from "../controllers/ServerController"; import ServerAPI from "../ServerAPI"; import FileSystemHelper from "../helper/FileSystemHelper"; const { promisify } = require("util"); const fileUpload = require('express-fileupload'); const bodyParser = require('body-parser'); //a parser for requests with body data /** * The My Express Router which is the core functionality. It setups all controllers and handles routes */ export default class MyExpressRouter { static urlAPI = "/api"; //the api url static redisClient = null; //the redis client static days_parameter = "date"; //parameter name for dates /*************************************** ************ Routes ******************* **************************************/ static routeVersion = MyExpressRouter.urlAPI + "/version"; static routeTime = MyExpressRouter.urlAPI + "/time"; static routeFunctions = MyExpressRouter.urlAPI + "/functions"; static routeModels = MyExpressRouter.urlAPI+"/models"; static routeSchemes = MyExpressRouter.urlAPI+"/schemes"; static routeAuth = MyExpressRouter.urlAPI + "/auth"; //Route to authenticate static routeServer = MyExpressRouter.urlAPI+"/server"; //Routes to Server Functions like Restart static routePlugins = MyExpressRouter.urlAPI+"/plugins"; //Routes to plugins API Endpoints static routeCustom = MyExpressRouter.urlAPI+"/custom"; //Routes to custom API Endpoints /** * 1. Declare a new Resource Name, a Parameter, AccessControll Resource, and RouteIdentifier */ // Functions static function_backup_resource_id_parameter = "backup_id"; static function_backup_routeIdentifier = "/backups"; static function_backup_resourceName = "backup"; static function_backup_accessControlResouce = "Function_Backup"; static function_database_resourceName = "database"; static function_database_accessControlResource = "Function_Database"; /** * 2. Create the Route and Associations */ //Functions static function_routeBackups = MyExpressRouter.routeFunctions + MyExpressRouter.function_backup_routeIdentifier; static function_routeBackupCreate = MyExpressRouter.function_routeBackups + "create"; static function_routeBackup = MyExpressRouter.function_routeBackups + "/:" + MyExpressRouter.function_backup_resource_id_parameter; static function_routeBackupDownload = MyExpressRouter.function_routeBackup + "/download"; static function_routeBackupRestore = MyExpressRouter.function_routeBackup + "/restore"; /** * Custom Routes */ static custom_routeIdentifier = "/custom"; static custom_routeResources = MyExpressRouter.urlAPI + MyExpressRouter.custom_routeIdentifier; //Define a ControlResource for general purpose for admins static adminRoutes_accessControlResource = "FUNCTIONS_Admin"; static custom_routeMetrics = MyExpressRouter.custom_routeResources + "/metrics"; static custom_bugReport = MyExpressRouter.custom_routeResources + "/bugReport"; static custom_showAllEndpoints = MyExpressRouter.custom_routeResources + "/showAllEndpoints"; static custom_routeSendPushNotification = MyExpressRouter.custom_routeResources + "/sendNotification"; /** * API */ /** * Constructor of the MyExpressRouter * @param logger The logger class * @param bugReportLogger The bug Report logger * @param expressApp the express app itseld * @param models the sequelize models * @param myAccessControl the access controll instance * @param redisClient the redis client */ constructor(logger, bugReportLogger, expressApp, models, myAccessController, redisClient, serverConfig) { this.logger = logger; this.serverConfig = serverConfig; this.logger.info("[MyExpressRouter] initialising"); this.startTime = new Date(); this.models = models; this.expressApp = expressApp; this.myAccessController = myAccessController; this.myAccessControl = myAccessController.getAccessControlInstance(); MyExpressRouter.redisClient = redisClient; this.configureExpressApp(); //configure parameters like which requests are allowed // !!BEFORE any other things, otherwise body content wont be parsed let authConfig = serverConfig["auth"]; this.myAuthMiddlewares = new MyAuthMiddlewares(logger, models, expressApp, MyExpressRouter.routeAuth, authConfig); //create the token helper //!!AFTER configureExpressApp, otherwise body content wont be parsed ... this.configurePublicParamCheckers(); this.configurePublicRoutes(); this.logger.info("[MyExpressRouter] initialised"); } static getPluginRoute(pluginName){ return MyExpressRouter.routePlugins + "/"+pluginName; } static async saveInRedis(key, value){ const setAsync = promisify(MyExpressRouter.redisClient.set).bind(MyExpressRouter.redisClient); return await setAsync(key, value); } static async deleteInRedis(key){ const delAsync = promisify(MyExpressRouter.redisClient.del).bind(MyExpressRouter.redisClient); return await delAsync(key); } static async getFromRedis(key){ const getAsync = promisify(MyExpressRouter.redisClient.get).bind(MyExpressRouter.redisClient); return await getAsync(key); } /** * Respond Helpers */ /** * Response with JSON to a request * @param res The response object * @param status the status * @param jsonData the json data */ static responseWithSuccessJSON(res, jsonData) { let envelope = { success: true, status: HttpStatus.OK, data: jsonData } MyExpressRouter.responseWithJSON(res, HttpStatus.OK, envelope); } static responseWithFiledownload(res, pathToFile, customFilename=null){ let filenameWithExtension = FileSystemHelper.getFilenameWithExtensionFromPath(pathToFile); let downloadName = customFilename || filenameWithExtension; res.download(pathToFile, downloadName); } /** * Response with JSON to a request * @param res The response object * @param status the status * @param jsonData the json data */ static responseWithErrorJSON(res, errStatus, error) { let envelope = { success: false, status: errStatus, error: error } MyExpressRouter.responseWithJSON(res, errStatus, envelope); } /** * Response with JSON to a request * @param res The response object * @param errorDetails */ static responseWithBadRequestErrorJSON(res, errorDetails) { if(!errorDetails){ errorDetails = {}; } let errObj = { //response with error error: 'Bad Request', } let err = Object.assign(errObj, errorDetails); MyExpressRouter.responseWithErrorJSON(res, HttpStatus.BAD_REQUEST, err); } /** * Response with JSON to a request * @param res The response object * @param errorDetails */ static responseWithNotFoundErrorJSON(res, errorDetails=null) { if(!errorDetails){ errorDetails = {}; } let errObj = { //response with error error: 'Not found', } let err = Object.assign(errObj, errorDetails); MyExpressRouter.responseWithErrorJSON(res, HttpStatus.NOT_FOUND, err); } /** * Response a Request with the default Message for a deletion of a resource * @param res The response object * @param err */ static respondWithInternalErrorMessage(res, err) { MyExpressRouter.responseWithErrorJSON(res, HttpStatus.INTERNAL_SERVER_ERROR, { errorCode: HttpStatus.INTERNAL_SERVER_ERROR, error: err.toString() }); } /** * Response with JSON to a request * @param res The response object * @param status the status * @param jsonData the json data */ static responseWithJSON(res, status, jsonData) { res.status(status); res.header('Content-Type', 'application/json'); res.header('Access-Control-Allow-Headers', 'Content-Type'); res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE'); res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With'); let jsonString = JSON.stringify(jsonData); res.end(jsonString); } /** * A Parameter Checker for Dates after ISO 8601 YYYY-MM-DD * @param req the request object * @param res the response object * @param next the next function * @param date the date to be checked * @returns boolean next function on success, else response with error and false * https://en.wikipedia.org/wiki/ISO_8601#:~:text=ISO%208601%20prescribes%2C%20as%20a,BC%20and%20all%20others%20AD. */ static paramcheckerDay(req, res, next, date) { let matches = /^(\d{4})[-](\d{1,2})[-](\d{1,2})$/.exec(date); //does date matches the regex DD-DD-DDDD ? if (matches == null) return false; let d = matches[3]; //get day let m = matches[2]; //get month let y = matches[1]; //get year let validDate = DateHelper.validateAndFormatDate(d, m, y); //is this a valid date ? if (!validDate) { //if not valid date MyExpressRouter.responseWithBadRequestErrorJSON(res, {message:'date has wrong format', date: date}) } else { //if valid date req.locals.date = validDate; next(); } } /** * Configure the Express App */ configureExpressApp() { this.logger.info("[MyExpressRouter] configuring Routes App"); let maxFileUploadSizeInMb = this.serverConfig.uploads.maxFileUploadSizeInMb || 50; let maxBodyUploadSizeInMb = this.serverConfig.uploads.maxBodyUploadSizeInMb || 50; this.expressApp.use(bodyParser.json({limit: maxBodyUploadSizeInMb+'mb'})); //set body limit this.expressApp.use(bodyParser.urlencoded({limit: maxBodyUploadSizeInMb+'mb', extended: true})); //set url limit this.expressApp.use(Express.json()); this.expressApp.use(fileUpload({ //set fileupload limit useTempFiles: true, tempFileDir: '/tmp/', limits: {fileSize: maxFileUploadSizeInMb * 1024 * 1024} })); //Accept Uploads from Cross Origins - for accepting uploads of files/images this.expressApp.options("/*", function (req, res, next) { res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS'); res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With'); res.send(200); }); } configurePublicParamCheckers(){ this.expressApp.param(MyExpressRouter.days_parameter, MyExpressRouter.paramcheckerDay.bind(this)); //set date param checker } configurePublicRoutes(){ this.expressApp.get(MyExpressRouter.custom_showAllEndpoints, this.handleCustomShowAllEndpoints.bind(this)); //for api stress test https://loader.io/ this.expressApp.get("/loaderio-4fffcfb2d8504b146819ec8df3a58421/", this.handleVerification.bind(this)); this.expressApp.get(MyExpressRouter.routeVersion, this.handleVersionRequest.bind(this)); //this.expressApp.get(MyExpressRouter.custom_routeMetrics, this.handleMetricsRequest.bind(this)); } /** * Configure all Controllers which then will register the routes and handle requests */ async configureController() { let logger = this.logger; let models = this.models; let expressApp = this.expressApp; let myAccessControl = this.myAccessControl; let instance = this; //Function // Helper this.defaultControllerHelper = new DefaultControllerHelper(logger, models, instance); this.sequelizeController = new SequelizeController(logger,models,expressApp,myAccessControl,instance); this.sequelizeSchemeController = new SequelizeSchemeController(logger,models,expressApp,myAccessControl,instance); this.serverController = new ServerController(logger, models, expressApp, myAccessControl, instance, MyExpressRouter.routeServer); this.customController = new CustomControllers(logger, models, expressApp, myAccessControl, instance, MyExpressRouter.routeCustom); //create controller for custom endpoints } withPermissionMiddleware(route, crudOperation, accessControlResource, next, isOwnCallback=false){ let expressOperation = null; switch (crudOperation){ case DefaultControllerHelper.CRUD_READ: expressOperation = "get"; break; case DefaultControllerHelper.CRUD_CREATE: expressOperation = "post"; break; case DefaultControllerHelper.CRUD_UPDATE: expressOperation = "put"; break; case DefaultControllerHelper.CRUD_DELETE: expressOperation = "delete"; break; } if(!!expressOperation){ let functionToHandle = async function(req, res){ //define the get function let isOwn = false; if(typeof isOwnCallback === "function"){ isOwn = await isOwnCallback(req, res); } else { isOwn = !!isOwnCallback; } let permission = DefaultControllerHelper.handleDefaultPermissionCheck(req, res, this.myAccessControl, accessControlResource, crudOperation, isOwn); if (permission.granted) { next(req, res); } } this.expressApp[expressOperation](route, functionToHandle.bind(this)); // register route in express } } static getSingleFileUpload(req, res){ if (!req.files) { // if no files given MyExpressRouter.responseWithBadRequestErrorJSON(res, {error: "No files given"}) return false; } if (req.files.length !== 1) { // more than one file MyExpressRouter.responseWithBadRequestErrorJSON(res, {error: "Only one file allowed"}) return false; } let file = req.files.file; if (file === undefined) { //whoo you named it other ? im sorry MyExpressRouter.responseWithBadRequestErrorJSON(res, { error: "Input Field name has to be: file!" }); return false; } return file; } static getFileUploadName(file){ return file.originalFilename || file.name || file.filename; } /** * Handle LoaderIO verification https://loader.io/ for stress tests * @param req the request object * @param res the response object */ handleVerification(req, res) { res.set('Content-Type', 'text/html'); res.status(200); res.send("loaderio-4fffcfb2d8504b146819ec8df3a58421"); } /** * Middlewares */ /** * Shows route metrics of the server * @param req the request object * @param res the response object * * @api {get} /api/custom/metrics Get All route Metrics * @apiDescription Shows alot of informations about the server * @apiName GetAllMetrics * @apiPermission Admin * @apiUse MyAuthorization * @apiGroup Custom * * @apiSuccess {JSON[Metrics]} Metrics All metrics for the server routes * @apiError (Error) {String} error The possible error that can occur. Possible Errors: INTERNAL_SERVER_ERROR, FORBIDDEN * * @apiExample Example usage: * curl -i http://localhost/api/custom/metrics */ async handleMetricsRequest(req,res){ console.log("handleMetricsRequest"); let metrics = await MetricsHelper.getMetrics(req); MyExpressRouter.responseWithSuccessJSON(res, metrics); } /** * Custom */ /** * Shows all possible routes which could be used * @param req the request object * @param res the response object * * @api {get} /api/custom/showAllEndpoints Get All Endpoint routes * @apiDescription Shows all possible routes which could be used * @apiName GetAllEndpoints * @apiPermission Admin * @apiUse MyAuthorization * @apiGroup Custom * * @apiSuccess {List[Routes]} Routes All possible routes * @apiError (Error) {String} error The possible error that can occur. Possible Errors: INTERNAL_SERVER_ERROR, FORBIDDEN * * @apiExample Example usage: * curl -i http://localhost/api/custom/showAllEndpoints */ handleCustomShowAllEndpoints(req, res) { let permission = this.myAccessControl .can(req.locals.currentUser.role) .readAny(MyExpressRouter.adminRoutes_accessControlResource); if (permission.granted) { try { console.log("handleShowAllEndpoints"); let expressApp = this.expressApp; let endpoints = expressApp._router.stack; let answer = endpoints; MyExpressRouter.responseWithSuccessJSON(res, answer); } catch (err) { MyExpressRouter.responseWithErrorJSON(res, HttpStatus.INTERNAL_SERVER_ERROR, {message:err.toString()}); } } else { MyExpressRouter.responseWithErrorJSON(res, HttpStatus.FORBIDDEN, { errorCode: HttpStatus.FORBIDDEN, error: "Forbidden to see all endpoints: " }); } } /** * Other */ /** * !! This Route will Never Change !! Get the actual Server API Version number. * @param req the request object * @param res the response object * * @api {get} /api/version Get the API version * @apiDescription !! This Route will Never Change !! Get the actual Server API Version number * @apiName GetAPIVersion * @apiPermission Anonym * @apiGroup 4Custom * * @apiSuccess {String} version The actual version of the Server API. * * @apiExample Example usage: * curl -i http://localhost/api/version */ handleVersionRequest(req, res) { let version = this.serverConfig.serverAPIVersion; //the server version https://semver.org/ let answer = {version: version}; MyExpressRouter.responseWithSuccessJSON(res, answer); } }