diff --git a/src/controllers/API/v1/HealthCheck.ts b/src/controllers/API/v1/HealthCheck.ts new file mode 100644 index 0000000..b1ad772 --- /dev/null +++ b/src/controllers/API/v1/HealthCheck.ts @@ -0,0 +1,22 @@ +import HTTP_STATUS from "../../../libs/HTTPStatus"; +import Environment from "../../../providers/Environment"; +import App from "../../../providers/App"; + +class HealthCheck { + public static async perform(req: any, res: any, next: any) { + Promise.resolve().then(async () => { + return res.status(HTTP_STATUS.OK).json({ + status: "OK", + environment: Environment.get().NODE_ENV, + version: App.versionNumber, + uptime: process.uptime(), + memoryUsage: process.memoryUsage(), + cpuUsage: process.cpuUsage(), + pid: process.pid, + ppid: process.ppid + }); + }).catch(next); + } +} + +export default HealthCheck; \ No newline at end of file diff --git a/src/exception/ExpressException.ts b/src/exception/ExpressException.ts new file mode 100644 index 0000000..dbeec65 --- /dev/null +++ b/src/exception/ExpressException.ts @@ -0,0 +1,21 @@ +import HTTP_STATUS from "../libs/HTTPStatus"; +import Logger from "../libs/Logger"; +import { ServiceError } from "./Errors"; + +class ExpressExceptionHandler { + public static errorLogger(error: any, req: any, res: any, next: any) { + if(error instanceof ServiceError) { + res.status(error.statusCode).json({ + error: error.message + }) + } + else { + res.status(HTTP_STATUS.INTERNAL_SERVER_ERROR).json({ + error: "Internal server error" + }) + throw error; + } + } +} + +export default ExpressExceptionHandler; \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 0f22973..5b4a1b6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,5 +9,6 @@ App.loadPrisma(); App.loadLocale(); App.loadDiscord(); App.load_osu(); +App.loadExpress(); export default App; diff --git a/src/providers/App.ts b/src/providers/App.ts index d16ad66..4959e06 100644 --- a/src/providers/App.ts +++ b/src/providers/App.ts @@ -3,6 +3,7 @@ import Configuration from './Configuration'; import Prisma from './Prisma'; import Discord from './Discord'; import osu from './osuAPI'; +import Express from './Express'; import Logger from '../libs/Logger'; import Locale from './Locale'; @@ -41,6 +42,16 @@ class App { Logger.log('info', 'Loading osu! Client'); osu.init(); } + + public loadExpress(): void { + Logger.log('info', 'Loading Express'); + Express.init(); + } + + public endExpress(): void { + Logger.log('info', 'Ending Express'); + Express.end(); + } } export default new App(); diff --git a/src/providers/Environment.ts b/src/providers/Environment.ts index 5a789e8..a2e041a 100644 --- a/src/providers/Environment.ts +++ b/src/providers/Environment.ts @@ -31,6 +31,9 @@ class Environment { const PRIVATE_BOT = process.env.PRIVATE_BOT; const SUPPORT_URL = process.env.SUPPORT_URL; + const WEB_HOST = process.env.WEB_HOST; + const WEB_PORT = process.env.WEB_PORT; + const OSU_API_KEY = process.env.OSU_API_KEY; const YOUTUBE_COOKIE_BASE64 = process.env.YOUTUBE_COOKIE_BASE64; @@ -47,6 +50,9 @@ class Environment { PRIVATE_BOT, SUPPORT_URL, + WEB_HOST, + WEB_PORT, + OSU_API_KEY, YOUTUBE_COOKIE_BASE64, diff --git a/src/providers/Express.ts b/src/providers/Express.ts new file mode 100644 index 0000000..70157bb --- /dev/null +++ b/src/providers/Express.ts @@ -0,0 +1,53 @@ +import express from 'express'; +import cors from 'cors'; + +import Logger from "../libs/Logger"; +import Environment from "./Environment"; + +import ExpressException from '../exception/ExpressException'; +import Routes from './Routes'; + +class Express { + + public server: any; + public express: express.Application; + + constructor () { + this.server = null; + this.express = express(); + } + + public init(): void { + + this.mountMiddlewares(); + this.mountRoutes(); + this.express = this.express.use(ExpressException.errorLogger); + + this.server = this.express.listen(Environment.get().WEB_PORT, Environment.get().WEB_HOST, () => { + Logger.log('info', `Express Webserver listening at ${Environment.get().WEB_HOST}:${Environment.get().WEB_PORT}`); + }); + + } + + public end(): void { + if(this.server != null) + this.server.close(); + } + + private mountRoutes (): void { + this.express = Routes.mountAPI(this.express); + this.express = Routes.mountWeb(this.express); + } + + private mountMiddlewares (): void { + + //this.express = RateLimit.init(this.express); + this.express = this.express.use(cors()); + + //This always have to be at the bottom! + this.express = this.express.use(express.json()); + } + +} + +export default new Express(); \ No newline at end of file diff --git a/src/providers/Routes.ts b/src/providers/Routes.ts new file mode 100644 index 0000000..7c46600 --- /dev/null +++ b/src/providers/Routes.ts @@ -0,0 +1,22 @@ +import express, { Application } from 'express'; +import Logger from '../libs/Logger'; +import apiv1Router from '../routes/API/v1'; + +class Routes { + + public mountWeb(_express: express.Application): Application { + Logger.log('info', 'Mounting Web routes'); + return _express; + } + + public mountAPI(_express: express.Application): Application { + Logger.log('info', 'Mounting API routes'); + + _express.use('/api/v1/', apiv1Router); + + return _express; + } + +} + +export default new Routes; \ No newline at end of file diff --git a/src/routes/API/v1.ts b/src/routes/API/v1.ts new file mode 100644 index 0000000..34464fb --- /dev/null +++ b/src/routes/API/v1.ts @@ -0,0 +1,20 @@ +import * as express from 'express'; +import Environment from '../../providers/Environment' + +import HealthCheck from '../../controllers/API/v1/HealthCheck'; + +const router = express.Router(); + +let isDevEnv = (req: any, res: any, next: any) => { + if(Environment.get().NODE_ENV == 'development') { + return next(); + } + else { + return res.sendStatus(403); + } +}; + + +router.get('/health', HealthCheck.perform); + +export default router; \ No newline at end of file