Skip to content

Commit 5adf45b

Browse files
author
Bruno Castro
committed
Update boot modules and remove unused code
1 parent db608e6 commit 5adf45b

File tree

9 files changed

+910
-832
lines changed

9 files changed

+910
-832
lines changed

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@
1414
},
1515
"dependencies": {
1616
"awilix": "^4.3.4",
17+
"cors": "^2.8.5",
1718
"dotenv": "^10.0.0",
1819
"express": "^4.17.1",
20+
"helmet": "^5.1.1",
1921
"joi": "^17.4.1",
2022
"lodash.template": "^4.5.0",
2123
"mongodb": "^4.0.0",
@@ -29,6 +31,7 @@
2931
"uuid-mongodb": "^2.4.4"
3032
},
3133
"devDependencies": {
34+
"@types/cors": "^2.8.12",
3235
"@types/express": "^4.17.13",
3336
"@types/jest": "^26.0.24",
3437
"@types/lodash.template": "^4.5.0",

src/_boot/appModules.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
import { articleModule, ArticleRegistry } from '@/article';
2-
import { ArticleMessages } from '@/article/messages';
32
import { commentModule, CommentRegistry } from '@/comment';
43

5-
type AppModulesMessages = ArticleMessages;
6-
4+
// eslint-disable-next-line @typescript-eslint/ban-types
75
type AppModulesConfig = {};
86

97
const appModules = [articleModule, commentModule];
108

119
type AppModulesRegistry = ArticleRegistry & CommentRegistry;
1210

1311
export { appModules };
14-
export type { AppModulesMessages, AppModulesConfig, AppModulesRegistry };
12+
export type { AppModulesConfig, AppModulesRegistry };

src/_boot/repl.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
import { makeModule } from '@/context';
2-
import { makeREPL, REPLConfigType } from '@/_lib/repl';
2+
import { makeREPL } from '@/_lib/repl';
33

4-
type REPLConfig = REPLConfigType<{ appName: string; cli: boolean; repl: { port: number } }>;
4+
type REPLConfig = { appName: string; environment: string; cli: boolean; repl: { port: number } };
55

66
const repl = makeModule('repl', async ({ app: { onReady, terminate }, container, config, logger }) => {
7-
const repl = makeREPL({ container, config, logger });
7+
const repl = makeREPL({
8+
context: { registry: container.cradle, container },
9+
cli: config.cli,
10+
prompt: config.appName,
11+
remote: !['production', 'test'].includes(config.environment) && config.repl,
12+
logger,
13+
});
814

915
onReady(async () => {
1016
await repl.start({ terminate });

src/_boot/server.ts

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,26 @@
1-
import express, { Router, Application, json, urlencoded } from 'express';
2-
import { asValue } from 'awilix';
3-
import httpLogger from 'pino-http';
4-
import { createServer } from 'http';
5-
import { requestId } from '@/_lib/http/middlewares/requestId';
6-
import { requestContainer } from '@/_lib/http/middlewares/requestContainer';
7-
import { errorHandler } from '@/_lib/http/middlewares/errorHandler';
81
import { makeModule } from '@/context';
2+
import { errorHandler } from '@/_lib/http/middlewares/errorHandler';
93
import { gracefulShutdown } from '@/_lib/http/middlewares/gracefulShutdown';
4+
import { httpLogger, reqStartTimeKey } from '@/_lib/http/middlewares/httpLogger';
5+
import { requestContainer } from '@/_lib/http/middlewares/requestContainer';
6+
import { requestId } from '@/_lib/http/middlewares/requestId';
7+
import { statusHandler } from '@/_lib/http/middlewares/statusHandler';
108
import { errorConverters } from '@/_sharedKernel/interface/http/ErrorConverters';
9+
import { asValue } from 'awilix';
10+
import cors from 'cors';
11+
import express, { Application, json, Router, urlencoded } from 'express';
12+
import helmet from 'helmet';
13+
import { createServer, Server } from 'http';
1114

1215
type ServerConfig = {
1316
http: {
1417
host: string;
1518
port: number;
19+
cors?:
20+
| boolean
21+
| {
22+
allowedOrigins: string | string[];
23+
};
1624
};
1725
};
1826

@@ -26,22 +34,43 @@ const server = makeModule(
2634

2735
const { shutdownHook, shutdownHandler } = gracefulShutdown(httpServer);
2836

37+
server.use((req, res, next) => {
38+
res[reqStartTimeKey] = Date.now();
39+
40+
next();
41+
});
42+
2943
server.use(shutdownHandler());
44+
45+
if (http.cors) {
46+
server.use((req, res, next) => {
47+
return cors({
48+
allowedHeaders:
49+
'accept, accept-encoding, origin, referer, sec-fetch-*, user-agent, content-type, authorization',
50+
credentials: true,
51+
origin: typeof http.cors === 'boolean' ? req.get('origin') : http.cors?.allowedOrigins,
52+
methods: '*',
53+
})(req, res, next);
54+
});
55+
}
56+
3057
server.use(requestId());
3158
server.use(requestContainer(container));
3259
server.use(httpLogger());
60+
server.use(helmet());
3361
server.use(json());
3462
server.use(urlencoded({ extended: false }));
3563

3664
const rootRouter = Router();
3765
const apiRouter = Router();
3866

67+
rootRouter.get('/status', statusHandler);
3968
rootRouter.use('/api', apiRouter);
4069

4170
server.use(rootRouter);
4271

4372
onBooted(async () => {
44-
server.use((req, res) => {
73+
server.use((_, res) => {
4574
res.sendStatus(404);
4675
});
4776

@@ -61,7 +90,9 @@ const server = makeModule(
6190
}
6291

6392
register({
93+
requestId: asValue(undefined),
6494
server: asValue(server),
95+
httpServer: asValue(httpServer),
6596
rootRouter: asValue(rootRouter),
6697
apiRouter: asValue(apiRouter),
6798
});
@@ -75,6 +106,7 @@ const server = makeModule(
75106
type ServerRegistry = {
76107
requestId?: string;
77108
server: Application;
109+
httpServer: Server;
78110
rootRouter: Router;
79111
apiRouter: Router;
80112
};
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { Request, RequestHandler } from 'express';
2+
import logger, { Options, startTime } from 'pino-http';
3+
import { randomUUID } from 'crypto';
4+
5+
type LoggerOptions = Options & { customProps?: (req: Request, res: Response) => any };
6+
7+
const httpLoggerOptions = (): LoggerOptions => {
8+
const getReqId = (req: Request) => `[req:${req.id}]`;
9+
10+
return {
11+
genReqId: () => randomUUID(),
12+
autoLogging: { ignorePaths: ['/status', '/favicon.ico'] },
13+
customSuccessMessage: function (res) {
14+
const req = res.req as Request;
15+
16+
const reqId = getReqId(req);
17+
18+
return `${reqId} ${res.statusCode} - ${req.method} ${req.originalUrl} ${Date.now() - res[startTime]}ms`;
19+
},
20+
customErrorMessage: function (error, res) {
21+
const req = res.req as Request;
22+
23+
const reqId = getReqId(req);
24+
25+
return `${reqId} ${res.statusCode} - ${req.method} ${req.originalUrl} - [${error.name}] ${error.message} ${
26+
Date.now() - res[startTime]
27+
}ms`;
28+
},
29+
customLogLevel: function (res, err) {
30+
if (res.statusCode >= 400 && res.statusCode < 500) {
31+
return 'warn';
32+
} else if (res.statusCode >= 500 || err) {
33+
return 'error';
34+
} else if (res.statusCode >= 300 && res.statusCode < 400) {
35+
return 'trace';
36+
}
37+
return 'info';
38+
},
39+
};
40+
};
41+
42+
const httpLogger = (opts: LoggerOptions = httpLoggerOptions()): RequestHandler =>
43+
logger({
44+
...opts,
45+
});
46+
47+
export { httpLogger, httpLoggerOptions, startTime as reqStartTimeKey };
48+
export type { LoggerOptions };
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { handler } from '@/_lib/http/handler';
2+
import { HttpStatus } from '@/_lib/http/HttpStatus';
3+
4+
type Dependencies = {
5+
startedAt: Date;
6+
};
7+
8+
const statusHandler = handler(({ startedAt }: Dependencies) => async (_, res) => {
9+
const uptime = Math.round((Date.now() - startedAt.getTime()) / 10) / 100;
10+
11+
res.status(HttpStatus.OK).json({
12+
startedAt,
13+
uptime,
14+
});
15+
});
16+
17+
export { statusHandler };

src/_lib/repl/index.ts

Lines changed: 21 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,16 @@
11
import REPL, { REPLEval, ReplOptions, REPLServer } from 'repl';
22
import vm from 'vm';
33
import { createServer, Server } from 'net';
4-
import { AwilixContainer } from 'awilix';
5-
import { Logger } from 'pino';
6-
import { EnvironmentConfig } from '../Environment';
74

8-
type REPLConfigType<T> = {
9-
[key in keyof T]: T[key];
5+
type REPLProps = {
6+
context: Record<string, any>;
7+
prompt: string;
8+
cli: boolean;
9+
remote: false | { port: number };
10+
logger: Pick<Console, 'error'>;
1011
};
1112

12-
type ReplParameters = {
13-
container: AwilixContainer;
14-
config: REPLConfigType<{
15-
appName: string;
16-
cli: boolean;
17-
repl: { port: number };
18-
environment: EnvironmentConfig['environment'];
19-
}>;
20-
logger: Logger;
21-
};
22-
23-
type REPL = {
13+
type REPLInstance = {
2414
create: (config: Partial<ReplOptions>) => REPLServer;
2515
start: ({ terminate }) => Promise<void>;
2616
close: () => Promise<void>;
@@ -38,46 +28,41 @@ const promisableEval: REPLEval = (cmd, context, filename, callback) => {
3828
return callback(null, result);
3929
};
4030

41-
const makeREPL = ({
42-
container,
43-
config: {
44-
appName,
45-
cli,
46-
environment,
47-
repl: { port },
48-
},
49-
logger,
50-
}: ReplParameters): REPL => {
31+
const makeREPL = ({ context, prompt, cli, remote, logger }: REPLProps): REPLInstance => {
5132
let server: Server;
5233

5334
const create = (config: Partial<ReplOptions> = { input: process.stdin, output: process.stdout }): REPLServer => {
5435
const repl = REPL.start({
5536
eval: promisableEval,
56-
prompt: `${appName}$ `,
37+
prompt: `${prompt}$ `,
5738
ignoreUndefined: true,
5839
...config,
5940
});
6041

61-
Object.assign(repl.context, { registry: container.cradle, container });
42+
Object.assign(repl.context, context);
6243

6344
return repl;
6445
};
6546

47+
let destroySocket: (...args: any) => void = () => null;
48+
6649
return {
6750
create,
6851
start: async ({ terminate }) => {
6952
if (cli) {
7053
const repl = create();
7154

7255
repl.on('close', terminate);
73-
} else if (!['production', 'test'].includes(environment)) {
56+
} else if (remote) {
7457
server = createServer((socket) => {
7558
const repl = create({
7659
input: socket,
7760
output: socket,
7861
terminal: true,
7962
});
8063

64+
destroySocket = socket.destroy.bind(socket);
65+
8166
repl.on('close', () => {
8267
socket.end();
8368
});
@@ -87,21 +72,22 @@ const makeREPL = ({
8772
logger.error(err);
8873
socket.end();
8974
});
90-
}).listen(port);
75+
}).listen(remote.port);
9176
}
9277
},
9378
close: async () => {
9479
if (server && server.listening) {
95-
await new Promise<void>((resolve, reject) =>
80+
await new Promise<void>((resolve, reject) => {
81+
destroySocket();
82+
9683
server.close((err) => {
9784
if (err) return reject(err);
9885
resolve();
99-
})
100-
);
86+
});
87+
});
10188
}
10289
},
10390
};
10491
};
10592

10693
export { makeREPL };
107-
export type { REPLConfigType };

src/_sharedKernel/infrastructure/MemoryDB.ts

Lines changed: 0 additions & 17 deletions
This file was deleted.

0 commit comments

Comments
 (0)