diff --git a/src/class/Route.ts b/src/class/Route.ts new file mode 100644 index 0000000..53a3852 --- /dev/null +++ b/src/class/Route.ts @@ -0,0 +1,75 @@ +/* eslint-disable consistent-return */ +import { Router, Request, Response } from 'express'; +import { Server } from '.'; + +export default class Route { + public server: Server; + + public conf: { path: string; deprecated?: boolean; maintenance?: boolean; }; + + public router: Router; + + constructor(server: Server, options: { path: string, deprecated?: boolean, maintenance?: boolean }) { + this.server = server; + this.conf = options; + this.router = Router(); + } + + public bind() {} + + public init() { + this.router.all('*', (req, res, next) => { + this.server.client.util.signale.log(`'${req.method}' request from '${req.ip}' to '${req.hostname}${req.path}'.`); + if (this.conf.maintenance === true) res.status(503).json({ code: this.constants.codes.MAINTENANCE_OR_UNAVAILABLE, message: this.constants.messages.MAINTENANCE_OR_UNAVAILABLE }); + else if (this.conf.deprecated === true) res.status(501).json({ code: this.constants.codes.DEPRECATED, message: this.constants.messages.DEPRECATED }); + else next(); + }); + } + + public deprecated(): void { + this.router.all('*', (_req, res) => { + res.status(501).json({ code: this.constants.codes.DEPRECATED, message: this.constants.messages.DEPRECATED }); + }); + } + + public maintenance(): void { + this.router.all('*', (_req, res) => { + res.status(503).json({ code: this.constants.codes.MAINTENANCE_OR_UNAVAILABLE, message: this.constants.messages.MAINTENANCE_OR_UNAVAILABLE }); + }); + } + + public handleError(error: Error, res: Response) { + res.status(500).json({ code: this.constants.codes.SERVER_ERROR, message: this.constants.messages.SERVER_ERROR }); + this.server.parent.client.util.handleError(error); + } + + get constants() { + return { + codes: { + SUCCESS: 100, + UNAUTHORIZED: 101, + PERMISSION_DENIED: 104, + ENDPOINT_NOT_FOUND: 104, + NOT_FOUND: 1041, + ACCOUNT_NOT_FOUND: 1041, + CLIENT_ERROR: 1044, + SERVER_ERROR: 105, + DEPRECATED: 1051, + MAINTENANCE_OR_UNAVAILABLE: 1053, + }, + messages: { + UNAUTHORIZED: ['CREDENTIALS_INVALID', 'The credentials you supplied are invalid.'], + BEARER_TOKEN_INVALID: ['BEARER_TOKEN_INVALID', 'The Bearer token you supplied is invalid.'], + PERMISSION_DENIED: ['PERMISSION_DENIED', 'You do not have valid credentials to access this resource.'], + NOT_FOUND: ['NOT_FOUND', 'The resource you requested cannot be located.'], + ENDPOINT_NOT_FOUND: ['ENDPOINT_NOT_FOUND', 'The endpoint you requested does not exist or cannot be located.'], + SERVER_ERROR: ['INTERNAL_ERROR', 'An internal error has occurred, Engineers have been notified.'], + DEPRECATED: ['ENDPOINT_OR_RESOURCE_DEPRECATED', 'The endpoint or resource you\'re trying to access has been deprecated.'], + MAINTENANCE_OR_UNAVAILABLE: ['SERVICE_UNAVAILABLE', 'The endpoint or resource you\'re trying to access is either in maintenance or is not available.'], + }, + discord: { + SERVER_ID: '446067825673633794', + }, + }; + } +} diff --git a/src/class/Server.ts b/src/class/Server.ts new file mode 100644 index 0000000..26fbb91 --- /dev/null +++ b/src/class/Server.ts @@ -0,0 +1,69 @@ +import express from 'express'; +import helmet from 'helmet'; +import { promises as fs } from 'fs'; +import { Server as HTTPServer } from 'http'; +import { Collection, ServerManagement, Route } from '.'; + +export default class Server { + public app: express.Application; + + public routes: Collection; + + public parent: ServerManagement; + + public port: number; + + private root: string; + + constructor(parent: ServerManagement, port: number, routeRoot: string) { + this.parent = parent; + this.app = express(); + this.routes = new Collection(); + this.port = port; + this.root = routeRoot; + + this.loadRoutes(); + this.init(); + } + + get client() { + return this.parent.client; + } + + public async loadRoutes() { + const routes = await fs.readdir(`${this.root}`); + for (const routeFile of routes) { + // eslint-disable-next-line new-cap + const route: Route = new (require(`${this.root}/${routeFile}`).default)(this); + if (route.conf.deprecated) { + route.deprecated(); + } else if (route.conf.maintenance) { + route.maintenance(); + } else { + route.init(); + route.bind(); + } + this.parent.client.util.signale.success(`Successfully loaded route 'http://localhost:${this.port}/${route.conf.path}'.`); + this.routes.add(route.conf.path, route); + this.app.use(route.conf.path, route.router); + } + this.app.listen(this.port); + } + + public init() { + this.app.set('trust proxy', 'loopback'); + this.app.use(helmet({ + hsts: false, + hidePoweredBy: false, + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + }, + }, + })); + } + + public listen(port: number): HTTPServer { + return this.app.listen(port); + } +} diff --git a/src/class/ServerManagement.ts b/src/class/ServerManagement.ts new file mode 100644 index 0000000..9936a84 --- /dev/null +++ b/src/class/ServerManagement.ts @@ -0,0 +1,27 @@ +import express from 'express'; +import { promises as fs } from 'fs'; +import { Client, Collection, Server } from '.'; +// import serverSetup from '../api/server'; + +export default class ServerManagement { + public client: Client; + + public servers: Collection; + + constructor(client: Client) { + this.client = client; + this.servers = new Collection(); + this.loadServers(); + } + + public async loadServers() { + const apiRoot = await fs.readdir(`${__dirname}/../api`); + for (const api of apiRoot) { + // eslint-disable-next-line no-continue + if (api === 'server.js') continue; + const server: Server = require(`${__dirname}/../api/${api}/main.js`).default(this); + this.servers.add(api, server); + this.client.util.signale.success(`Successfully loaded server '${api}'.`); + } + } +} diff --git a/src/class/index.ts b/src/class/index.ts index d1cdba4..105aa46 100644 --- a/src/class/index.ts +++ b/src/class/index.ts @@ -3,4 +3,7 @@ export { default as Collection } from './Collection'; export { default as Command } from './Command'; export { default as Moderation } from './Moderation'; export { default as RichEmbed } from './RichEmbed'; +export { default as Route } from './Route'; +export { default as Server } from './Server'; +export { default as ServerManagement } from './ServerManagement'; export { default as Util } from './Util';