namirasoft-node 1.1.4 → 1.1.5

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.
@@ -1,187 +1,187 @@
1
- import express, { Router } from 'express';
2
- import cors from "cors";
3
- import fs from "fs";
4
- import swaggerUi from 'swagger-ui-express';
5
- import swaggerJSDoc from 'swagger-jsdoc';
6
- import { BaseDatabase } from './BaseDatabase';
7
- import { ILogger } from "namirasoft-log";
8
- import { BaseApplicationLink } from './BaseApplicationLink';
9
- import path from 'path';
10
- import { PackageService } from 'namirasoft-core';
11
-
12
- export abstract class BaseApplication<D extends BaseDatabase>
13
- {
14
- private title: string;
15
- private description: string;
16
- private logo: string;
17
- private version: string;
18
- private serverPath: string;
19
- private swaggerPath: string;
20
- private linkLoader?: () => Promise<void>;
21
- private links: BaseApplicationLink[] = [];
22
- public app!: express.Express;
23
- public database!: D;
24
- public logger: ILogger;
25
- protected abstract getDatabase(): D;
26
- protected abstract getLogger(): ILogger;
27
- protected abstract getRouter(): Router;
28
- protected abstract getPort(): number;
29
- constructor(serverPath: string, swaggerPath: string | null = null)
30
- {
31
- let json = PackageService.getMain();
32
- this.title = json?.getTitle() ?? "";
33
- this.description = json?.getDescription() ?? "";
34
- this.logo = json?.getLogo() ?? "";
35
- this.version = json?.getVersion() ?? "";
36
- this.serverPath = serverPath;
37
- this.swaggerPath = swaggerPath ?? (serverPath + "/swagger");
38
- this.logger = this.getLogger();
39
- this.addLink = this.addLink.bind(this);
40
- this.addSwaggerLink = this.addSwaggerLink.bind(this);
41
- }
42
- setLinkLoader(linkLoader: () => Promise<void>)
43
- {
44
- this.linkLoader = linkLoader;
45
- }
46
- addLink(link: BaseApplicationLink)
47
- {
48
- this.links.push(link);
49
- }
50
- addSwaggerLink()
51
- {
52
- this.addLink({
53
- name: "Swagger",
54
- url: this.swaggerPath + "/",
55
- logo: "https://static.namirasoft.com/logo/swagger/base.png",
56
- description: this.description + " Swagger"
57
- });
58
- }
59
- async start()
60
- {
61
- await this.startCrashHandler();
62
- await this.startDatabase();
63
- await this.startServer();
64
- await this.startSwagger();
65
- await this.startHomePage();
66
- await this.onStart();
67
- }
68
- protected async startCrashHandler(): Promise<void>
69
- {
70
- process.on('unhandledRejection', (reason) =>
71
- {
72
- if (reason instanceof Error)
73
- this.logger.onCatchCritical(reason);
74
- else
75
- this.logger.critical(reason + "");
76
- });
77
- process.on('uncaughtException', (error) =>
78
- {
79
- this.logger.onCatchFatal(error);
80
- });
81
- }
82
- protected async startDatabase(): Promise<void>
83
- {
84
- this.database = this.getDatabase();
85
- this.database.init();
86
- await this.database.sync(false);
87
- }
88
- protected async startServer(): Promise<void>
89
- {
90
- this.app = express();
91
- this.app.use((req, res, next) =>
92
- {
93
- let excludes: string[] = [];
94
- if (excludes.includes(req.path))
95
- next();
96
- else
97
- express.json({ limit: '100kb' })(req, res, next);
98
- });
99
- // Express
100
- this.app.use(express.static('static'));
101
- // Cors
102
- this.app.use(cors({ exposedHeaders: '*', }));
103
- // api routes
104
- this.app.use('/', this.getRouter());
105
- // start server
106
- const port = this.getPort();
107
- this.app.listen(port, async () =>
108
- {
109
- this.logger.info(`Server listening on port ${port}`);
110
- });
111
- }
112
- protected async startSwagger(): Promise<void>
113
- {
114
- const joptions = {
115
- definition: {
116
- openapi: "3.0.1",
117
- info: {
118
- title: this.title,
119
- description: this.description,
120
- version: this.version,
121
- // license: {
122
- // name: "MIT",
123
- // url: "https://spdx.org/licenses/MIT.html",
124
- // },
125
- // contact: {
126
- // name: "Amir Abolhasani",
127
- // url: "https://namirasoft.com",
128
- // email: "accounts@namirasoft.com",
129
- // },
130
- },
131
- servers: [
132
- {
133
- url: this.serverPath,
134
- },
135
- ],
136
- },
137
- apis: ['./src/route/*.ts'],
138
- };
139
- const swaggerSpec = swaggerJSDoc(joptions);
140
- var options = {
141
- explorer: true
142
- };
143
- this.app.use(this.swaggerPath + "/", swaggerUi.serve, swaggerUi.setup(swaggerSpec, options));
144
- }
145
- protected async startHomePage(): Promise<void>
146
- {
147
- this.app.get(this.serverPath + "/", (_, res) =>
148
- {
149
- const htmlFilePath = path.join(__dirname, '../public/index.html');
150
- fs.readFile(htmlFilePath, 'utf8', async (err: any, data: string) =>
151
- {
152
- if (err)
153
- return res.status(500).send('Error reading HTML file');
154
-
155
- data = data.replace(/\@title/gm, this.title);
156
- data = data.replace(/\@description/gm, this.description);
157
- data = data.replace(/\@logo/gm, this.logo);
158
- let match = /<\@row>([\w\W]*)<\/\@row>/gm.exec(data);
159
- if (match)
160
- {
161
- let rows: string[] = [];
162
- if (this.linkLoader)
163
- {
164
- this.links = [];
165
- await this.linkLoader();
166
- }
167
- this.links.forEach(link =>
168
- {
169
- if (match)
170
- {
171
- let row = match[1];
172
- row = row.replace(/\@row_name/gm, link.name);
173
- row = row.replace(/\@row_url/gm, link.url);
174
- row = row.replace(/\@row_logo/gm, link.logo);
175
- row = row.replace(/\@row_description/gm, link.description);
176
- rows.push(row);
177
- }
178
- });
179
- data = data.replace(match[0], rows.join(" "));
180
- }
181
- res.send(data);
182
- return;
183
- });
184
- });
185
- }
186
- abstract onStart(): Promise<void>;
1
+ import express, { Router } from 'express';
2
+ import cors from "cors";
3
+ import fs from "fs";
4
+ import swaggerUi from 'swagger-ui-express';
5
+ import swaggerJSDoc from 'swagger-jsdoc';
6
+ import { BaseDatabase } from './BaseDatabase';
7
+ import { ILogger } from "namirasoft-log";
8
+ import { BaseApplicationLink } from './BaseApplicationLink';
9
+ import path from 'path';
10
+ import { PackageService } from 'namirasoft-core';
11
+
12
+ export abstract class BaseApplication<D extends BaseDatabase>
13
+ {
14
+ private title: string;
15
+ private description: string;
16
+ private logo: string;
17
+ private version: string;
18
+ private serverPath: string;
19
+ private swaggerPath: string;
20
+ private linkLoader?: () => Promise<void>;
21
+ private links: BaseApplicationLink[] = [];
22
+ public app!: express.Express;
23
+ public database!: D;
24
+ public logger: ILogger;
25
+ protected abstract getDatabase(): D;
26
+ protected abstract getLogger(): ILogger;
27
+ protected abstract getRouter(): Router;
28
+ protected abstract getPort(): number;
29
+ constructor(serverPath: string, swaggerPath: string | null = null)
30
+ {
31
+ let json = PackageService.getMain();
32
+ this.title = json?.getTitle() ?? "";
33
+ this.description = json?.getDescription() ?? "";
34
+ this.logo = json?.getLogo() ?? "";
35
+ this.version = json?.getVersion() ?? "";
36
+ this.serverPath = serverPath;
37
+ this.swaggerPath = swaggerPath ?? (serverPath + "/swagger");
38
+ this.logger = this.getLogger();
39
+ this.addLink = this.addLink.bind(this);
40
+ this.addSwaggerLink = this.addSwaggerLink.bind(this);
41
+ }
42
+ setLinkLoader(linkLoader: () => Promise<void>)
43
+ {
44
+ this.linkLoader = linkLoader;
45
+ }
46
+ addLink(link: BaseApplicationLink)
47
+ {
48
+ this.links.push(link);
49
+ }
50
+ addSwaggerLink()
51
+ {
52
+ this.addLink({
53
+ name: "Swagger",
54
+ url: this.swaggerPath + "/",
55
+ logo: "https://static.namirasoft.com/logo/swagger/base.png",
56
+ description: this.description + " Swagger"
57
+ });
58
+ }
59
+ async start()
60
+ {
61
+ await this.startCrashHandler();
62
+ await this.startDatabase();
63
+ await this.startServer();
64
+ await this.startSwagger();
65
+ await this.startHomePage();
66
+ await this.onStart();
67
+ }
68
+ protected async startCrashHandler(): Promise<void>
69
+ {
70
+ process.on('unhandledRejection', (reason) =>
71
+ {
72
+ if (reason instanceof Error)
73
+ this.logger.onCatchCritical(reason);
74
+ else
75
+ this.logger.critical(reason + "");
76
+ });
77
+ process.on('uncaughtException', (error) =>
78
+ {
79
+ this.logger.onCatchFatal(error);
80
+ });
81
+ }
82
+ protected async startDatabase(): Promise<void>
83
+ {
84
+ this.database = this.getDatabase();
85
+ this.database.init();
86
+ await this.database.sync(false);
87
+ }
88
+ protected async startServer(): Promise<void>
89
+ {
90
+ this.app = express();
91
+ this.app.use((req, res, next) =>
92
+ {
93
+ let excludes: string[] = [];
94
+ if (excludes.includes(req.path))
95
+ next();
96
+ else
97
+ express.json({ limit: '100kb' })(req, res, next);
98
+ });
99
+ // Express
100
+ this.app.use(express.static('static'));
101
+ // Cors
102
+ this.app.use(cors({ exposedHeaders: '*', }));
103
+ // api routes
104
+ this.app.use('/', this.getRouter());
105
+ // start server
106
+ const port = this.getPort();
107
+ this.app.listen(port, async () =>
108
+ {
109
+ this.logger.info(`Server listening on port ${port}`);
110
+ });
111
+ }
112
+ protected async startSwagger(): Promise<void>
113
+ {
114
+ const joptions = {
115
+ definition: {
116
+ openapi: "3.0.1",
117
+ info: {
118
+ title: this.title,
119
+ description: this.description,
120
+ version: this.version,
121
+ // license: {
122
+ // name: "MIT",
123
+ // url: "https://spdx.org/licenses/MIT.html",
124
+ // },
125
+ // contact: {
126
+ // name: "Amir Abolhasani",
127
+ // url: "https://namirasoft.com",
128
+ // email: "accounts@namirasoft.com",
129
+ // },
130
+ },
131
+ servers: [
132
+ {
133
+ url: this.serverPath,
134
+ },
135
+ ],
136
+ },
137
+ apis: ['./src/route/*.ts'],
138
+ };
139
+ const swaggerSpec = swaggerJSDoc(joptions);
140
+ var options = {
141
+ explorer: true
142
+ };
143
+ this.app.use(this.swaggerPath + "/", swaggerUi.serve, swaggerUi.setup(swaggerSpec, options));
144
+ }
145
+ protected async startHomePage(): Promise<void>
146
+ {
147
+ this.app.get(this.serverPath + "/", (_, res) =>
148
+ {
149
+ const htmlFilePath = path.join(__dirname, '../public/index.html');
150
+ fs.readFile(htmlFilePath, 'utf8', async (err: any, data: string) =>
151
+ {
152
+ if (err)
153
+ return res.status(500).send('Error reading HTML file');
154
+
155
+ data = data.replace(/\@title/gm, this.title);
156
+ data = data.replace(/\@description/gm, this.description);
157
+ data = data.replace(/\@logo/gm, this.logo);
158
+ let match = /<\@row>([\w\W]*)<\/\@row>/gm.exec(data);
159
+ if (match)
160
+ {
161
+ let rows: string[] = [];
162
+ if (this.linkLoader)
163
+ {
164
+ this.links = [];
165
+ await this.linkLoader();
166
+ }
167
+ this.links.forEach(link =>
168
+ {
169
+ if (match)
170
+ {
171
+ let row = match[1];
172
+ row = row.replace(/\@row_name/gm, link.name);
173
+ row = row.replace(/\@row_url/gm, link.url);
174
+ row = row.replace(/\@row_logo/gm, link.logo);
175
+ row = row.replace(/\@row_description/gm, link.description);
176
+ rows.push(row);
177
+ }
178
+ });
179
+ data = data.replace(match[0], rows.join(" "));
180
+ }
181
+ res.send(data);
182
+ return;
183
+ });
184
+ });
185
+ }
186
+ abstract onStart(): Promise<void>;
187
187
  }
@@ -1,7 +1,7 @@
1
- export type BaseApplicationLink =
2
- {
3
- name: string;
4
- url: string;
5
- logo: string;
6
- description: string;
1
+ export type BaseApplicationLink =
2
+ {
3
+ name: string;
4
+ url: string;
5
+ logo: string;
6
+ description: string;
7
7
  };
@@ -1,132 +1,132 @@
1
- import * as express from 'express';
2
- import { BaseDatabase } from './BaseDatabase';
3
- import { SchemaLike } from 'joi';
4
- import { AnomalyDetector } from './AnomalyDetector';
5
- import { ILogger } from 'namirasoft-log';
6
- import { Meta } from './Meta';
7
- import Joi from 'joi';
8
- import { ErrorOperation, HTTPError } from 'namirasoft-core';
9
-
10
- export abstract class BaseController<D extends BaseDatabase, State, Props>
11
- {
12
- protected showLogAtTheBeginning: boolean = false;
13
- protected showLogAtTheEnd: boolean = true;
14
- protected req: express.Request;
15
- protected res: express.Response;
16
- protected logger: ILogger;
17
- protected meta!: Meta;
18
- protected result!: {};
19
- protected database!: D;
20
- protected state!: State;
21
- protected props!: Props;
22
- constructor(req: express.Request, res: express.Response)
23
- {
24
- this.req = req;
25
- this.res = res;
26
- this.logger = this.getLogger();
27
- }
28
- abstract getLogger(): ILogger;
29
- abstract getDatabase(): D;
30
- abstract getAnomaly(): AnomalyDetector | null;
31
- abstract getBodySchema(): SchemaLike | null;
32
- abstract getQuerySchema(): SchemaLike | null;
33
- abstract getState(): Promise<State>;
34
- abstract getProps(): Promise<Props>;
35
- abstract preHandle(): Promise<void>;
36
- abstract handle(): Promise<any>;
37
- abstract postHandle(): Promise<void>;
38
-
39
- async run()
40
- {
41
- this.meta = new Meta(this.req);
42
- // result
43
- this.result = {};
44
- try
45
- {
46
- // meta
47
- this.meta.onStart();
48
- if (this.showLogAtTheBeginning)
49
- this.logger.info(JSON.stringify(this.meta));
50
- // init controller
51
- this.database = this.getDatabase();
52
- this.database.init();
53
- this.state = await this.getState();
54
- this.props = await this.getProps();
55
-
56
- // preHandle
57
- await this.preHandle();
58
-
59
- // check for anomaly
60
- let anomaly: AnomalyDetector | null = this.getAnomaly();
61
- if (anomaly != null)
62
- if (anomaly.isAnomaly(this.meta.ip, this.meta.url))
63
- ErrorOperation.throwHTTP(403, 'Suspicious activity detected.');
64
-
65
- // check body validation
66
- let bodySchema = this.getBodySchema();
67
- if (bodySchema != null)
68
- {
69
- const validation = await Joi.compile(bodySchema)
70
- .prefs({ errors: { label: 'key' } })
71
- .validate(this.req.body);
72
- if (validation.error)
73
- {
74
- let message = validation.error.details.map((details) => details.message).join(', ');
75
- ErrorOperation.throwHTTP(400, message);
76
- }
77
- }
78
- // check query validation
79
- let querySchema = this.getQuerySchema();
80
- if (querySchema != null)
81
- {
82
- const validation = await Joi.compile(querySchema)
83
- .prefs({ errors: { label: 'key' } })
84
- .validate(this.req.query);
85
- if (validation.error)
86
- {
87
- let message = validation.error.details.map((details) => details.message).join(', ');
88
- ErrorOperation.throwHTTP(400, message);
89
- }
90
- }
91
-
92
- // call controller
93
- this.result = await this.handle();
94
- if (this.result == null)
95
- this.result = "Success";
96
-
97
- // postHandle
98
- await this.postHandle();
99
- } catch (error)
100
- {
101
- let message: string;
102
- if (error instanceof Error)
103
- {
104
- this.meta.error = error;
105
- message = error.message;
106
- }
107
- else
108
- message = error + "";
109
-
110
- if (error instanceof HTTPError)
111
- {
112
- this.meta.code = error.code;
113
- this.logger.error(error.message + "\n" + JSON.stringify(this.meta), undefined, error.stack);
114
- }
115
- else
116
- {
117
- this.meta.code = 500;
118
- if (error instanceof Error)
119
- this.logger.critical(error.message + "\n" + JSON.stringify(this.meta), undefined, error.stack);
120
- }
121
- this.meta.message = message;
122
- if (error instanceof HTTPError)
123
- this.result = this.meta.message;
124
- else
125
- this.result = "Sorry, internl server error.";
126
- }
127
- this.meta.onFinish();
128
- if (this.showLogAtTheEnd)
129
- this.logger.info(JSON.stringify(this.meta));
130
- return this.res.status(this.meta.code).send(this.result);
131
- }
1
+ import * as express from 'express';
2
+ import { BaseDatabase } from './BaseDatabase';
3
+ import { SchemaLike } from 'joi';
4
+ import { AnomalyDetector } from './AnomalyDetector';
5
+ import { ILogger } from 'namirasoft-log';
6
+ import { Meta } from './Meta';
7
+ import Joi from 'joi';
8
+ import { ErrorOperation, HTTPError } from 'namirasoft-core';
9
+
10
+ export abstract class BaseController<D extends BaseDatabase, State, Props>
11
+ {
12
+ protected showLogAtTheBeginning: boolean = false;
13
+ protected showLogAtTheEnd: boolean = true;
14
+ protected req: express.Request;
15
+ protected res: express.Response;
16
+ protected logger: ILogger;
17
+ protected meta!: Meta;
18
+ protected result!: {};
19
+ protected database!: D;
20
+ protected state!: State;
21
+ protected props!: Props;
22
+ constructor(req: express.Request, res: express.Response)
23
+ {
24
+ this.req = req;
25
+ this.res = res;
26
+ this.logger = this.getLogger();
27
+ }
28
+ abstract getLogger(): ILogger;
29
+ abstract getDatabase(): D;
30
+ abstract getAnomaly(): AnomalyDetector | null;
31
+ abstract getBodySchema(): SchemaLike | null;
32
+ abstract getQuerySchema(): SchemaLike | null;
33
+ abstract getState(): Promise<State>;
34
+ abstract getProps(): Promise<Props>;
35
+ abstract preHandle(): Promise<void>;
36
+ abstract handle(): Promise<any>;
37
+ abstract postHandle(): Promise<void>;
38
+
39
+ async run()
40
+ {
41
+ this.meta = new Meta(this.req);
42
+ // result
43
+ this.result = {};
44
+ try
45
+ {
46
+ // meta
47
+ this.meta.onStart();
48
+ if (this.showLogAtTheBeginning)
49
+ this.logger.info(JSON.stringify(this.meta));
50
+ // init controller
51
+ this.database = this.getDatabase();
52
+ this.database.init();
53
+ this.state = await this.getState();
54
+ this.props = await this.getProps();
55
+
56
+ // preHandle
57
+ await this.preHandle();
58
+
59
+ // check for anomaly
60
+ let anomaly: AnomalyDetector | null = this.getAnomaly();
61
+ if (anomaly != null)
62
+ if (anomaly.isAnomaly(this.meta.ip, this.meta.url))
63
+ ErrorOperation.throwHTTP(403, 'Suspicious activity detected.');
64
+
65
+ // check body validation
66
+ let bodySchema = this.getBodySchema();
67
+ if (bodySchema != null)
68
+ {
69
+ const validation = await Joi.compile(bodySchema)
70
+ .prefs({ errors: { label: 'key' } })
71
+ .validate(this.req.body);
72
+ if (validation.error)
73
+ {
74
+ let message = validation.error.details.map((details) => details.message).join(', ');
75
+ ErrorOperation.throwHTTP(400, message);
76
+ }
77
+ }
78
+ // check query validation
79
+ let querySchema = this.getQuerySchema();
80
+ if (querySchema != null)
81
+ {
82
+ const validation = await Joi.compile(querySchema)
83
+ .prefs({ errors: { label: 'key' } })
84
+ .validate(this.req.query);
85
+ if (validation.error)
86
+ {
87
+ let message = validation.error.details.map((details) => details.message).join(', ');
88
+ ErrorOperation.throwHTTP(400, message);
89
+ }
90
+ }
91
+
92
+ // call controller
93
+ this.result = await this.handle();
94
+ if (this.result == null)
95
+ this.result = "Success";
96
+
97
+ // postHandle
98
+ await this.postHandle();
99
+ } catch (error)
100
+ {
101
+ let message: string;
102
+ if (error instanceof Error)
103
+ {
104
+ this.meta.error = error;
105
+ message = error.message;
106
+ }
107
+ else
108
+ message = error + "";
109
+
110
+ if (error instanceof HTTPError)
111
+ {
112
+ this.meta.code = error.code;
113
+ this.logger.error(error.message + "\n" + JSON.stringify(this.meta), undefined, error.stack);
114
+ }
115
+ else
116
+ {
117
+ this.meta.code = 500;
118
+ if (error instanceof Error)
119
+ this.logger.critical(error.message + "\n" + JSON.stringify(this.meta), undefined, error.stack);
120
+ }
121
+ this.meta.message = message;
122
+ if (error instanceof HTTPError)
123
+ this.result = this.meta.message;
124
+ else
125
+ this.result = "Sorry, internl server error.";
126
+ }
127
+ this.meta.onFinish();
128
+ if (this.showLogAtTheEnd)
129
+ this.logger.info(JSON.stringify(this.meta));
130
+ return this.res.status(this.meta.code).send(this.result);
131
+ }
132
132
  }
@@ -1,5 +1,5 @@
1
- export abstract class BaseDatabase
2
- {
3
- abstract init(): void;
4
- abstract sync(force: boolean): Promise<void>;
1
+ export abstract class BaseDatabase
2
+ {
3
+ abstract init(): void;
4
+ abstract sync(force: boolean): Promise<void>;
5
5
  }