backendium 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "backendium",
3
+ "version": "0.0.0",
4
+ "description": "Express-based javascript backend framework with websocket support",
5
+ "main": "dist/index.js",
6
+ "scripts": {
7
+ "build": "tsc",
8
+ "prepack": "tsc"
9
+ },
10
+ "dependencies": {
11
+ "chalk": "^5.3.0",
12
+ "event-emitter-typescript": "^2.1.1",
13
+ "express": "^4.21.0",
14
+ "websocket-express": "^3.1.1",
15
+ "ws": "^8.18.0"
16
+ },
17
+ "peerDependencies": {
18
+ "checkeasy": "^1.2.1"
19
+ },
20
+ "devDependencies": {
21
+ "@types/express": "^4.17.21",
22
+ "@types/ws": "^8.5.12",
23
+ "typescript": "^5.5.3"
24
+ },
25
+ "files": ["src", "dist", "package.json", "readme.md", "tsconfig.json"]
26
+ }
package/readme.md ADDED
@@ -0,0 +1,11 @@
1
+ <h1 align="center">Backendium</h1>
2
+
3
+ # Table of Contents
4
+ > 1. [Installation](#installation)
5
+ > 2. [Basics](#basics)
6
+ > 3. [Registering handlers](#registering-handlers)
7
+ # Installation
8
+ ```
9
+ npm i backendium checkeasy
10
+ ```
11
+ # Basics
package/src/handler.ts ADDED
@@ -0,0 +1,85 @@
1
+ import BackendiumResponse from "./response.js";
2
+ import Backendium from "./index.js";
3
+ import {NextFunction, Request, RequestHandler, Response} from "express";
4
+ import parseRequest, {BackendiumRequestOptionsType, BackendiumRequestType} from "./request.js";
5
+ import {BackendiumRouter} from "./router";
6
+ import {ValidationError} from "checkeasy";
7
+
8
+ export type BackendiumHandlerReturnType = void | undefined | {code?: number, next?: boolean};
9
+ export type BackendiumHandlerType<BodyType, ParamsType, QueryType, AuthType, HeadersType, GlobalAuthType> = (request: BackendiumRequestType<BodyType, ParamsType, QueryType, AuthType, HeadersType, GlobalAuthType>, response: BackendiumResponse, app: Backendium, next: NextFunction) => BackendiumHandlerReturnType | Promise<BackendiumHandlerReturnType>;
10
+
11
+ export type RawHandlerType<DefaultAuthType> = (router: BackendiumRouter<DefaultAuthType>) => (app: Backendium) => RequestHandler;
12
+
13
+ export function defaultAuthFailedHandler(request: Request, response: BackendiumResponse, app: Backendium) {
14
+ response.status(209);
15
+ response.end();
16
+ }
17
+
18
+ export function defaultErrorHandler(request: Request, response: BackendiumResponse, data: any, app: Backendium, error: any, message: string) {
19
+ response.status(500);
20
+ response.end(message ?? "Internal Server Error");
21
+ app.logger.requestError(request.url, data, error);
22
+ }
23
+
24
+ export function defaultValidationErrorHandler(request: Request, response: BackendiumResponse, app: Backendium, data: Buffer, error: ValidationError, message: string) {
25
+ response.status(400);
26
+ response.end(message ?? "Validation failed");
27
+ }
28
+
29
+ export default function backendiumHandler<BodyType, ParamsType, QueryType, AuthType, HeadersType, GlobalAuthType>(handler: BackendiumHandlerType<BodyType, ParamsType, QueryType, AuthType, HeadersType, GlobalAuthType>,
30
+ {auth, authChecker, authFailed, errorHandler, errorMessage, validationErrorHandler, validationErrorMessage, ...options}: BackendiumRequestOptionsType<BodyType, ParamsType, QueryType, AuthType, HeadersType>
31
+ ): RawHandlerType<GlobalAuthType> {
32
+ return (router: BackendiumRouter<GlobalAuthType>) => (app: Backendium) => (async (request: Request, response: Response, next: NextFunction) => {
33
+ let body: any;
34
+ try {
35
+ let req = await parseRequest(request, app, {...options, auth, authChecker, authFailed, errorHandler, validationErrorHandler, errorMessage, validationErrorMessage});
36
+ let res = new BackendiumResponse(response, app);
37
+ if (Array.isArray(req)) {
38
+ let [data, error] = req;
39
+ (validationErrorHandler ?? app.config.validationErrorHandler ?? defaultValidationErrorHandler)(request, res, app, data, error, validationErrorMessage ?? app.config.validationErrorMessage);
40
+ if (app.config.logging?.fullRequest) app.logger.requestFull(request.url, res.lastStatus, data, res.lastResponse);
41
+ else app.logger.request(request.url, res.lastStatus);
42
+ return;
43
+ }
44
+ body = req.body;
45
+ // @ts-ignore
46
+ let authData: AuthType = undefined;
47
+ if (authChecker) {
48
+ let ret = authChecker(request, res, app);
49
+ if (ret instanceof Promise) ret = await ret;
50
+ if (ret === null) {
51
+ (authFailed ?? router.authFailed ?? app.authFailed ?? defaultAuthFailedHandler)(request, res, app);
52
+ return;
53
+ }
54
+ authData = ret;
55
+ }
56
+ // @ts-ignore
57
+ let globalAuthData: GlobalAuthType = undefined;
58
+ if (!authChecker && auth && router.authChecker) {
59
+ let ret = router.authChecker(request, res, app);
60
+ if (ret instanceof Promise) ret = await ret;
61
+ if (ret === null) {
62
+ (authFailed ?? router.authFailed ?? app.authFailed ?? defaultAuthFailedHandler)(request, res, app);
63
+ return;
64
+ }
65
+ globalAuthData = ret;
66
+ }
67
+ let ret = handler({...req, auth: authData, globalAuth: globalAuthData}, res, app, next);
68
+ if (ret instanceof Promise) ret = await ret;
69
+ if (!ret) return;
70
+ let {code = 200, next: isNext = false} = ret;
71
+ response.status(code);
72
+ if (isNext) {
73
+ next();
74
+ return;
75
+ }
76
+ try {response.end();}
77
+ catch (error) {return;}
78
+ if (app.config.logging?.fullRequest) app.logger.requestFull(request.url, res.lastStatus, req.body, res.lastResponse);
79
+ else app.logger.request(request.url, res.lastStatus);
80
+ }
81
+ catch (error) {
82
+ (errorHandler ?? app.config.errorHandler ?? defaultErrorHandler)(request, new BackendiumResponse(response, app), body, app, error, errorMessage ?? app.config.errorMessage);
83
+ }
84
+ });
85
+ }
package/src/index.ts ADDED
@@ -0,0 +1,109 @@
1
+ import {Request, RequestHandler} from "express";
2
+ import {Server} from "node:http";
3
+ import {BackendiumRouter, MethodType} from "./router.js";
4
+ import {EventEmitter, EventKey} from "event-emitter-typescript";
5
+ import {WebSocketExpress, WSRequestHandler} from "websocket-express";
6
+ import {BackendiumWebSocket} from "./ws.js";
7
+ import Logger from "./logger";
8
+ import BackendiumResponse from "./response";
9
+ import {ValidationError} from "checkeasy";
10
+
11
+ export type BackendiumConfigType = {
12
+ port: number,
13
+ host: string,
14
+ name: string,
15
+ version: string | number,
16
+ logging: {
17
+ path?: string,
18
+ fullRequest?: boolean,
19
+ fullWs?: boolean,
20
+ replaceConsoleLog?: boolean
21
+ }
22
+ autoLogFull: boolean,
23
+ autoLog: boolean,
24
+ autoLogWsFull: boolean,
25
+ autoLogWs: boolean,
26
+ errorMessage: string,
27
+ errorHandler(request: Request, response: BackendiumResponse, data: any, app: Backendium, error: any, message?: string): void,
28
+ validationErrorMessage: string,
29
+ validationErrorHandler(request: Request, response: BackendiumResponse, app: Backendium, data: Buffer, error: ValidationError, message?: string): void,
30
+ wsErrorMessage: string,
31
+ wsErrorHandler(data: Buffer, connection: BackendiumWebSocket<any>, app: Backendium, error: any): void
32
+ }
33
+
34
+ export type BackendiumEvents = {
35
+ starting: [],
36
+ start: [Server]
37
+ };
38
+
39
+ export default class Backendium<GlobalAuthType = any> extends BackendiumRouter<GlobalAuthType> {
40
+ public express = new WebSocketExpress;
41
+ protected eventEmitter = new EventEmitter<BackendiumEvents>;
42
+ public logger = new Logger(console.log.bind(console));
43
+ protected config_: Partial<BackendiumConfigType> = {};
44
+
45
+ constructor(config: Partial<BackendiumConfigType> = {}) {
46
+ super();
47
+ this.config = config;
48
+ }
49
+
50
+ get config() {return this.config_;}
51
+ set config(config) {
52
+ this.config_ = {...this.config_, ...config, logging: {...this.config_.logging, ...config.logging}};
53
+ if (this.config_.logging?.path) this.logger.path = this.config_.logging?.path;
54
+ if (this.config_.logging?.replaceConsoleLog ?? true) {
55
+ console.log = this.logger.message.bind(this.logger);
56
+ console.error = this.logger.error.bind(this.logger);
57
+ }
58
+ }
59
+
60
+ protected addAnyRouteHandler(method: MethodType, handlers: Array<RequestHandler>) {
61
+ this.express[method]("*", ...handlers);
62
+ }
63
+
64
+ protected addRoutedHandler(method: MethodType, route: string, handlers: Array<RequestHandler>) {
65
+ this.express[method](route, ...handlers);
66
+ }
67
+
68
+ protected addWSHandler(route: string, handlers: Array<WSRequestHandler>) {
69
+ this.express.ws(route, ...handlers);
70
+ }
71
+
72
+ public on<E extends EventKey<BackendiumEvents>>(event: E, subscriber: (...args: BackendiumEvents[E]) => void): () => void {
73
+ return this.eventEmitter.on(event, (args) => subscriber(...args));
74
+ };
75
+
76
+ public once<E extends EventKey<BackendiumEvents>>(event: E, subscriber: (...args: BackendiumEvents[E]) => void): () => void {
77
+ return this.eventEmitter.once(event, (args) => subscriber(...args));
78
+ };
79
+
80
+ public off<E extends EventKey<BackendiumEvents>>(event: E, subscriber: (...args: BackendiumEvents[E]) => void): void {
81
+ this.eventEmitter.off(event, (args) => subscriber(...args));
82
+ };
83
+
84
+ public start(callback?: (server: Server) => void): Server {
85
+ this.handlers.forEach(([method, route, handlers]) => {
86
+ if (method == "ws") this.addWSHandler(route, handlers.map(handler => handler(this)));
87
+ else if (route) this.addRoutedHandler(method, route, handlers.map(handler => handler(this)));
88
+ else this.addAnyRouteHandler(method, handlers.map(handler => handler(this)));
89
+ });
90
+ this.eventEmitter.emit("starting", []);
91
+ // const server = this.express.createServer();
92
+ // server.listen(this.config.port ?? 8080, this.config.host ?? "localhost", () => {
93
+ // if (callback) callback(server);
94
+ // this.eventEmitter.emit("start", [server]);
95
+ // });
96
+ const server = this.express.listen(this.config_.port ?? 8080, this.config_.host ?? "localhost", () => {
97
+ this.logger.initMessage(this.config_.name ?? "app", this.config_.version ?? "0.0.0", this.config.port ?? 8080, this.config_.host ?? "localhost");
98
+ if (callback) callback(server);
99
+ this.eventEmitter.emit("start", [server]);
100
+ });
101
+ return server;
102
+ }
103
+
104
+ public async startAsync(): Promise<Server> {
105
+ return new Promise(resolve => {
106
+ this.start(resolve);
107
+ });
108
+ }
109
+ }
package/src/logger.ts ADDED
@@ -0,0 +1,273 @@
1
+ import chalk from "chalk";
2
+ import * as fs from "node:fs";
3
+ import * as util from "node:util";
4
+
5
+ export function getDate() {
6
+ let date = new Date();
7
+ return `${date.getFullYear()}.${date.getMonth() + 1}.${date.getDate()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`;
8
+ }
9
+
10
+ export default class Logger {
11
+ protected logData = "";
12
+
13
+ constructor(public log: (...data: Array<string>) => void, protected path_?: string) {}
14
+
15
+ get path(): string | undefined {return this.path_;}
16
+ set path(path: string) {
17
+ this.path_ = path;
18
+ if (this.path_) fs.writeFileSync(this.path_, this.logData);
19
+ }
20
+
21
+ clear() {
22
+ if (this.path_) fs.writeFileSync(this.path_, "");
23
+ }
24
+
25
+ logSeparately(consoleData: Array<any>, fileData: Array<string>) {
26
+ this.log(...consoleData);
27
+ this.logData += (this.logData.length ? '\n' : "") + fileData.join(' ');
28
+ if (this.path_) fs.writeFileSync(this.path_, this.logData);
29
+ }
30
+
31
+ getPrefix(tag: string) {
32
+ return `[${getDate()}] ${tag}:`;
33
+ }
34
+
35
+ Log(tag: string, func = chalk.white, ...data: Array<any>) {
36
+ let prefix = this.getPrefix(tag);
37
+ this.logSeparately([func(prefix), ...data.map((elem) => typeof elem !== "string" ? elem : func(elem))],
38
+ [prefix, ...data.map(elem => util.inspect(elem))]);
39
+ }
40
+
41
+ message(...data: Array<any>) {
42
+ return this.Log("info", chalk.white, ...data);
43
+ }
44
+
45
+ info(...data: Array<any>) {
46
+ return this.Log("info", chalk.white, ...data);
47
+ }
48
+
49
+ success(...data: Array<any>) {
50
+ return this.Log("success", chalk.green, ...data);
51
+ }
52
+
53
+ warning(...data: Array<any>) {
54
+ return this.Log("warn", chalk.yellow, ...data);
55
+ }
56
+
57
+ error(...data: Array<any>) {
58
+ return this.Log("error", chalk.redBright, ...data);
59
+ }
60
+
61
+ fatal(...data: Array<any>) {
62
+ return this.Log("fatal", chalk.red, ...data);
63
+ }
64
+
65
+ initMessage(name: string, version: string | number, port: number, host: string) {
66
+ this.logSeparately([chalk.greenBright(`--++== ${chalk.green(name)} ${chalk.cyan('v' + version)}; ${chalk.cyan(`${host}:${port}`)} ==++--`)],
67
+ [`--++== ${name} v${version}; port: ${port} ==++--`]);
68
+ }
69
+
70
+ protected formatData(data: any) {
71
+ return Buffer.isBuffer(data) ? `"${data.toString()}"` : typeof data === "string" ? `"${data}"` : data;
72
+ }
73
+
74
+ protected formatDataColors(data: any, color = chalk.cyan, defaultColor = chalk.green) {
75
+ return Buffer.isBuffer(data) ? defaultColor(`"${color(data.toString())}"`) : typeof data === "string" ? defaultColor(`"${color(data)}"`) : data;
76
+ }
77
+
78
+ requestFull(url: string, code: number, request: any, response: any) { // query, auth, headers
79
+ request = typeof request === "string" ? request.replaceAll("\n", "\\n") : request;
80
+ response = typeof response === "string" ? response.replaceAll("\n", "\\n") : response;
81
+ let toLog: Array<any> = [this.getPrefix("request"), `Handled request to`];
82
+ let toLogColours: Array<any> = [...toLog, chalk.cyan(url)];
83
+ // toLog[0] = '\n' + toLog[0];
84
+ toLog.push(url);
85
+ if (code !== undefined) {
86
+ toLog.push(". Code:", code);
87
+ toLogColours.push(". Code:", code);
88
+ }
89
+ if (request !== undefined) {
90
+ toLog.push(". Request:", this.formatData(request));
91
+ toLogColours.push(". Request:", this.formatDataColors(request));
92
+ }
93
+ if (response !== undefined) {
94
+ toLog.push(". Response:", this.formatData(response));
95
+ toLogColours.push(". Response:", this.formatDataColors(response));
96
+ }
97
+ this.logSeparately(toLogColours.map(elem => typeof elem === "string" ? chalk.green(elem) : elem), toLog);
98
+ }
99
+
100
+ request(url: string, code: number) {
101
+ let toLog: Array<any> = [this.getPrefix("request"), `Handled request to`];
102
+ let toLogColours: Array<any> = [...toLog, chalk.cyan(url)];
103
+ // toLog[0] = '\n' + toLog[0];
104
+ toLog.push(url);
105
+ if (code !== undefined) {
106
+ toLog.push(". Code:", code);
107
+ toLogColours.push(". Code:", code);
108
+ }
109
+ this.logSeparately(toLogColours.map(elem => typeof elem === "string" ? chalk.green(elem) : elem), toLog);
110
+ }
111
+
112
+ requestError(url: string, request: any, stackTrace: any) {
113
+ request = typeof request === "string" ? request.replaceAll("\n", "\\n") : request;
114
+ let toLog = [this.getPrefix("request"), `Error during handling request to`];
115
+ let toLogColours = [...toLog, chalk.cyan(url)];
116
+ // toLog[0] = '\n' + toLog[0];
117
+ toLog.push(url);
118
+ if (request !== undefined) {
119
+ toLog.push(". Request:", this.formatData(request));
120
+ toLogColours.push(`. Request:`, this.formatDataColors(request));
121
+ }
122
+ toLog.push(`\n${stackTrace}`);
123
+ toLogColours.push(`\n${stackTrace}`);
124
+ this.logSeparately(toLogColours.map(elem => typeof elem === "string" ? chalk.redBright(elem) : elem), toLog);
125
+ }
126
+
127
+ wsConnected(url: string) {
128
+ let prefix = this.getPrefix("wsConnected");
129
+ this.logSeparately([chalk.green(prefix), chalk.green("Websocket connected on url"), chalk.cyan(url)], [prefix, "Websocket connected on url", url]);
130
+ }
131
+
132
+ wsRejected(url: string) {
133
+ let prefix = this.getPrefix("wsRejected");
134
+ this.logSeparately([chalk.red(prefix), chalk.red("Websocket rejected on url"), chalk.cyan(url)], [prefix, "Websocket rejected on url", url]);
135
+ }
136
+
137
+ wsInitFull(url: string, data: any) {
138
+ let prefix = this.getPrefix("wsInit");
139
+ this.logSeparately(
140
+ [
141
+ chalk.green(prefix), chalk.green("Websocket init on url"), chalk.cyan(url), chalk.green("done"),
142
+ ...(data !== null && data !== undefined ? [chalk.green("Data:"), this.formatDataColors(data)] : [])
143
+ ],
144
+ [
145
+ prefix, "Websocket init on url", url, "done",
146
+ ...(data !== null && data !== undefined ? ["Data:", this.formatData(data)] : [])
147
+ ]
148
+ );
149
+ }
150
+
151
+ wsInitFailedFull(url: string, data: any) {
152
+ let prefix = this.getPrefix("wsInitFailed");
153
+ this.logSeparately(
154
+ [
155
+ chalk.green(prefix), chalk.green("Websocket init on url"), chalk.cyan(url), chalk.red("failed"),
156
+ ...(data !== null && data !== undefined ? [chalk.green("Data:"), this.formatDataColors(data)] : [])
157
+ ],
158
+ [
159
+ prefix, "Websocket init on url", url, "failed",
160
+ ...(data !== null && data !== undefined ? ["Data:", this.formatData(data)] : [])
161
+ ]
162
+ );
163
+ }
164
+
165
+ wsInit(url: string) {
166
+ let prefix = this.getPrefix("wsInit");
167
+ this.logSeparately([chalk.green(prefix), chalk.green("Websocket init on url"), chalk.cyan(url), chalk.red("done")],
168
+ [prefix, "Websocket init on url", url, "done"]);
169
+ }
170
+
171
+ wsInitFailed(url: string) {
172
+ let prefix = this.getPrefix("wsInitFailed");
173
+ this.logSeparately(
174
+ [chalk.green(prefix), chalk.green("Websocket init on url"), chalk.cyan(url), chalk.red("failed")],
175
+ [prefix, "Websocket init on url", url, "failed"]);
176
+ }
177
+
178
+ wsIncomingEventFull(url: string, event: string, data: any) {
179
+ let prefix = this.getPrefix("wsIncomingEvent");
180
+ this.logSeparately(
181
+ [
182
+ chalk.green(prefix), chalk.green("Client, connected on url"), chalk.cyan(url) + chalk.green(','), chalk.green("has emitted event"), chalk.green(`"${chalk.cyan(event)}"`),
183
+ ...(data !== null && data !== undefined ? [chalk.green("with data:"), this.formatDataColors(data)] : [])
184
+ ],
185
+ [
186
+ prefix, "Client, connected on url", url + ',', "has emitted event", `"${event}"`,
187
+ ...(data !== null && data !== undefined ? ["with data:", this.formatData(data)] : [])
188
+ ]
189
+ );
190
+ }
191
+
192
+ wsIncomingEvent(url: string, event: string) {
193
+ let prefix = this.getPrefix("wsIncomingEvent");
194
+ this.logSeparately([chalk.green(prefix), chalk.green("Client, connected on url"),
195
+ chalk.cyan(url) + chalk.green(','), chalk.green("has emitted event"), chalk.green(`${chalk.cyan(event)}`)],
196
+ [prefix, "Client, connected on url", url + ',', "has emitted event", `"${event}"`]);
197
+ }
198
+
199
+ wsOutgoingEventFull(url: string, event: string, data: any) {
200
+ let prefix = this.getPrefix("wsOutgoingEvent");
201
+ this.logSeparately(
202
+ [
203
+ chalk.green(prefix), chalk.green("Server has emitted event"), chalk.green(`"${chalk.cyan(event)}"`), chalk.green("to websocket connection on url"), chalk.cyan(url),
204
+ ...(data !== null && data !== undefined ? [chalk.green("with data:"), this.formatDataColors(data)] : [])
205
+ ], [
206
+ prefix, "Server has emitted event", `"${event}"`, "to websocket connection on url", url,
207
+ ...(data !== null && data !== undefined ? ["with data:", this.formatData(data)] : [])
208
+ ]
209
+ );
210
+ }
211
+
212
+ wsOutgoingEvent(url: string, event: string) {
213
+ let prefix = this.getPrefix("wsOutgoingEvent");
214
+ this.logSeparately([chalk.green(prefix), chalk.green("Server has emitted event"), chalk.green(`${chalk.cyan(event)}`), chalk.green("to websocket connection on url"), chalk.cyan(url)],
215
+ [prefix, "Server has emitted event", event, "to websocket connection on url", url]);
216
+ }
217
+
218
+ wsInputFull(url: string, data: any) {
219
+ let prefix = this.getPrefix("wsInput");
220
+ this.logSeparately([chalk.green(prefix), chalk.green("Client, connected on url"),
221
+ chalk.cyan(url) + chalk.green(','), chalk.green("has sent message"), this.formatDataColors(data)],
222
+ [prefix, "Client, connected on url", url + ',', "has sent message", this.formatData(data)]);
223
+ }
224
+
225
+ wsInput(url: string) {
226
+ let prefix = this.getPrefix("wsInput");
227
+ this.logSeparately([chalk.green(prefix), chalk.green("Client, connected on url"),
228
+ chalk.cyan(url) + chalk.green(','), chalk.green("has sent message")],
229
+ [prefix, "Client, connected on url", url + ',', "has sent message"]);
230
+ }
231
+
232
+ wsOutputFull(url: string, data: any) {
233
+ let prefix = this.getPrefix("wsOutput");
234
+ this.logSeparately([chalk.green(prefix), chalk.green("Server has sent message"), this.formatDataColors(data),
235
+ chalk.green("to websocket connection on url"),
236
+ chalk.cyan(url)], [prefix, "Server has sent message", this.formatData(data),
237
+ "to websocket connection on url", url]);
238
+ }
239
+
240
+ wsOutput(url: string) {
241
+ let prefix = this.getPrefix("wsOutput");
242
+ this.logSeparately([chalk.green(prefix), chalk.green("Server has sent message"),
243
+ chalk.green("to websocket connection on url"), chalk.cyan(url)],
244
+ [prefix, "Server has sent message", "to websocket connection on url", url]);
245
+ }
246
+
247
+ wsError(url: string, data: any, stackTrace: any) {
248
+ data = typeof data === "string" ? data.replaceAll("\n", "\\n") : data;
249
+ let toLog = [this.getPrefix("error"), `Error during handling websocket input to`];
250
+ let toLogColours = [...toLog, chalk.cyan(url)];
251
+ // toLog[0] = '\n' + toLog[0];
252
+ toLog.push(url);
253
+ if (data !== undefined) {
254
+ toLog.push(". Data:", this.formatData(data));
255
+ toLogColours.push(`. Data:`, this.formatDataColors(data));
256
+ }
257
+ toLog.push(`\n${stackTrace}`);
258
+ toLogColours.push(`\n${stackTrace}`);
259
+ this.logSeparately(toLogColours.map(elem => typeof elem === "string" ? chalk.redBright(elem) : elem), toLog);
260
+ }
261
+
262
+ wsClose(url: any) {
263
+ let prefix = this.getPrefix("wsClose");
264
+ this.logSeparately([chalk.green(prefix), chalk.green("Websocket connection on url"), chalk.cyan(url),
265
+ chalk.green("closed")], [prefix, "Websocket connection on url", url, "closed"]);
266
+ }
267
+
268
+ wsTerminate(url: any) {
269
+ let prefix = this.getPrefix("wsTerminate");
270
+ this.logSeparately([chalk.green(prefix), chalk.green("Websocket connection on url"), chalk.cyan(url),
271
+ chalk.red("terminated")], [prefix, "Websocket connection on url", url, "terminated"]);
272
+ }
273
+ }
package/src/request.ts ADDED
@@ -0,0 +1,91 @@
1
+ import {Request} from "express";
2
+ import Backendium from "./index.js";
3
+ import {ValidationError, Validator} from "checkeasy";
4
+ import BackendiumResponse from "./response";
5
+
6
+ export type ValidatorsType<BodyType, ParamsType, QueryType, HeadersType> = {
7
+ bodyValidator?: Validator<BodyType>,
8
+ paramsValidator?: Validator<ParamsType>,
9
+ queryValidator?: Validator<QueryType>,
10
+ headersValidator?: Validator<HeadersType>
11
+ }
12
+
13
+ export type AuthCheckerType<AuthType> = (request: Request, response: BackendiumResponse, app: Backendium) => AuthType | null | Promise<AuthType | null>;
14
+ export type AuthFailedType = (request: Request, response: BackendiumResponse, app: Backendium) => void;
15
+
16
+ export type BackendiumRequestOptionsType<BodyType, ParamsType, QueryType, AuthType, HeadersType> = ValidatorsType<BodyType, ParamsType, QueryType, HeadersType> & {
17
+ auth?: boolean,
18
+ authChecker?: AuthCheckerType<AuthType>,
19
+ authFailed?: AuthFailedType,
20
+ errorMessage?: string,
21
+ errorHandler?(request: Request, response: BackendiumResponse, data: any, app: Backendium, error: any): void,
22
+ validationErrorMessage?: string,
23
+ validationErrorHandler?(request: Request, response: BackendiumResponse, app: Backendium, data: Buffer, error: ValidationError, message?: string): void
24
+ };
25
+
26
+ export type BackendiumRequestType<BodyType, ParamsType, QueryType, AuthType, HeadersType, DefaultAuthType> = {
27
+ expressRequest: Request,
28
+ body: BodyType,
29
+ params: ParamsType,
30
+ query: QueryType,
31
+ headers: HeadersType,
32
+ bodyBuffer: Buffer,
33
+ options: BackendiumRequestOptionsType<BodyType, ParamsType, QueryType, AuthType, HeadersType>,
34
+ app: Backendium,
35
+ auth: AuthType,
36
+ globalAuth: DefaultAuthType
37
+ }
38
+
39
+ function parse<Type>(data: Buffer | object, validator?: Validator<Type>, root: string = ""): Type {
40
+ if (!validator) {
41
+ // @ts-ignore
42
+ return data;
43
+ }
44
+ try {
45
+ return validator(data, root);
46
+ }
47
+ catch (error0) {
48
+ if (Buffer.isBuffer(data)) {
49
+ try {
50
+ return validator(JSON.parse(data.toString()), root);
51
+ }
52
+ catch (error) {
53
+ if (error instanceof ValidationError) throw error;
54
+ throw error0;
55
+ }
56
+ }
57
+ throw error0;
58
+ }
59
+ }
60
+
61
+ async function getBody(request: Request): Promise<Buffer> {
62
+ if (request.body) return request.body;
63
+ return new Promise(resolve => {
64
+ let buffer = Buffer.alloc(0);
65
+ request.on("data", chunk => buffer = Buffer.concat([buffer, chunk]));
66
+ request.on("end", () => {
67
+ request.body = buffer;
68
+ resolve(buffer);
69
+ });
70
+ });
71
+ }
72
+
73
+ export default async function parseRequest<BodyType, ParamsType, QueryType, AuthType, HeadersType>(request: Request, app: Backendium,
74
+ {bodyValidator, paramsValidator, queryValidator, headersValidator, ...other}: BackendiumRequestOptionsType<BodyType, ParamsType, QueryType, AuthType, HeadersType>
75
+ ): Promise<Omit<Omit<BackendiumRequestType<BodyType, ParamsType, QueryType, AuthType, HeadersType, any>, "auth">, "globalAuth"> | [Buffer, ValidationError]> {
76
+ let bodyBuffer = await getBody(request);
77
+ try {
78
+ let body = parse(bodyBuffer, bodyValidator);
79
+ let params = parse(request.params, paramsValidator);
80
+ let query = parse(request.query, queryValidator);
81
+ let headers = parse(request.headers, headersValidator);
82
+ return {
83
+ expressRequest: request, body, params, query, headers, bodyBuffer, app,
84
+ options: {bodyValidator, paramsValidator, queryValidator, headersValidator, ...other}
85
+ };
86
+ }
87
+ catch (error) {
88
+ if (error instanceof ValidationError) return [bodyBuffer, error];
89
+ throw error;
90
+ }
91
+ }
@@ -0,0 +1,22 @@
1
+ import {Response} from "express";
2
+ import Backendium from "./index.js";
3
+
4
+ export default class BackendiumResponse {
5
+ public lastStatus = 200;
6
+ public lastResponse: any;
7
+
8
+ constructor(public expressResponse: Response, public app: Backendium) {
9
+
10
+ }
11
+
12
+ status(status: number) {
13
+ this.lastStatus = status;
14
+ this.expressResponse.status(status);
15
+ }
16
+
17
+ end(data?: any, status?: number) {
18
+ if (status) this.status(status);
19
+ this.lastResponse = data;
20
+ this.expressResponse.end(typeof data === "string" || data instanceof Buffer || data instanceof ArrayBuffer ? data : JSON.stringify(data));
21
+ }
22
+ }