nodejs-quickstart-structure 1.18.0 → 1.19.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/CHANGELOG.md +17 -4
- package/README.md +2 -1
- package/bin/index.js +93 -92
- package/lib/generator.js +1 -1
- package/lib/modules/caching-setup.js +76 -73
- package/lib/modules/config-files.js +4 -0
- package/lib/modules/kafka-setup.js +249 -191
- package/lib/modules/project-setup.js +1 -0
- package/package.json +13 -2
- package/templates/clean-architecture/js/src/errors/BadRequestError.js +11 -10
- package/templates/clean-architecture/js/src/errors/BadRequestError.spec.js.ejs +22 -21
- package/templates/clean-architecture/js/src/errors/NotFoundError.js +11 -10
- package/templates/clean-architecture/js/src/errors/NotFoundError.spec.js.ejs +22 -21
- package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.js.ejs +69 -39
- package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.spec.js.ejs +142 -81
- package/templates/clean-architecture/js/src/infrastructure/webserver/server.js.ejs +1 -1
- package/templates/clean-architecture/js/src/interfaces/controllers/userController.js.ejs +156 -75
- package/templates/clean-architecture/js/src/interfaces/controllers/userController.spec.js.ejs +234 -138
- package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.js.ejs +27 -21
- package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.spec.js.ejs +66 -49
- package/templates/clean-architecture/js/src/interfaces/graphql/typeDefs/user.types.js.ejs +19 -17
- package/templates/clean-architecture/js/src/interfaces/routes/api.js +12 -10
- package/templates/clean-architecture/js/src/usecases/DeleteUser.js +11 -0
- package/templates/clean-architecture/js/src/usecases/DeleteUser.spec.js.ejs +47 -0
- package/templates/clean-architecture/js/src/usecases/UpdateUser.js +11 -0
- package/templates/clean-architecture/js/src/usecases/UpdateUser.spec.js.ejs +48 -0
- package/templates/clean-architecture/js/src/utils/errorMessages.js +14 -0
- package/templates/clean-architecture/ts/src/errors/BadRequestError.spec.ts.ejs +22 -21
- package/templates/clean-architecture/ts/src/errors/BadRequestError.ts +9 -8
- package/templates/clean-architecture/ts/src/errors/NotFoundError.spec.ts.ejs +22 -21
- package/templates/clean-architecture/ts/src/errors/NotFoundError.ts +9 -8
- package/templates/clean-architecture/ts/src/index.ts.ejs +1 -1
- package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.spec.ts.ejs +175 -85
- package/templates/clean-architecture/ts/src/infrastructure/repositories/userRepository.ts.ejs +74 -0
- package/templates/clean-architecture/ts/src/interfaces/controllers/userController.spec.ts.ejs +331 -185
- package/templates/clean-architecture/ts/src/interfaces/controllers/userController.ts.ejs +173 -84
- package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.spec.ts.ejs +68 -51
- package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.ts.ejs +29 -21
- package/templates/clean-architecture/ts/src/interfaces/graphql/typeDefs/user.types.ts.ejs +17 -15
- package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.ts +13 -11
- package/templates/clean-architecture/ts/src/usecases/deleteUser.spec.ts.ejs +47 -0
- package/templates/clean-architecture/ts/src/usecases/deleteUser.ts +9 -0
- package/templates/clean-architecture/ts/src/usecases/updateUser.spec.ts.ejs +48 -0
- package/templates/clean-architecture/ts/src/usecases/updateUser.ts +9 -0
- package/templates/clean-architecture/ts/src/utils/errorMessages.ts +12 -0
- package/templates/common/.gitattributes +46 -0
- package/templates/common/.snyk.ejs +45 -0
- package/templates/common/Dockerfile +17 -9
- package/templates/common/README.md.ejs +295 -263
- package/templates/common/caching/clean/js/DeleteUser.js.ejs +27 -0
- package/templates/common/caching/clean/js/UpdateUser.js.ejs +27 -0
- package/templates/common/caching/clean/ts/deleteUser.ts.ejs +24 -0
- package/templates/common/caching/clean/ts/updateUser.ts.ejs +25 -0
- package/templates/common/caching/ts/memoryCache.ts.ejs +73 -64
- package/templates/common/caching/ts/redisClient.ts.ejs +89 -80
- package/templates/common/database/js/models/User.js.ejs +79 -53
- package/templates/common/database/js/models/User.js.mongoose.ejs +23 -19
- package/templates/common/database/js/models/User.spec.js.ejs +94 -84
- package/templates/common/database/ts/models/User.spec.ts.ejs +100 -84
- package/templates/common/database/ts/models/User.ts.ejs +87 -61
- package/templates/common/database/ts/models/User.ts.mongoose.ejs +30 -25
- package/templates/common/health/js/healthRoute.js.ejs +50 -47
- package/templates/common/health/ts/healthRoute.ts.ejs +49 -46
- package/templates/common/jest.e2e.config.js.ejs +8 -8
- package/templates/common/kafka/js/messaging/baseConsumer.js.ejs +30 -30
- package/templates/common/kafka/js/messaging/userEventSchema.js.ejs +12 -11
- package/templates/common/kafka/js/messaging/welcomeEmailConsumer.js.ejs +44 -31
- package/templates/common/kafka/js/messaging/welcomeEmailConsumer.spec.js.ejs +86 -49
- package/templates/common/kafka/js/services/kafkaService.js.ejs +93 -93
- package/templates/common/kafka/js/utils/kafkaEvents.js.ejs +7 -0
- package/templates/common/kafka/ts/messaging/userEventSchema.spec.ts.ejs +51 -51
- package/templates/common/kafka/ts/messaging/userEventSchema.ts.ejs +12 -11
- package/templates/common/kafka/ts/messaging/welcomeEmailConsumer.spec.ts.ejs +86 -49
- package/templates/common/kafka/ts/messaging/welcomeEmailConsumer.ts.ejs +38 -25
- package/templates/common/kafka/ts/services/kafkaService.ts.ejs +95 -95
- package/templates/common/kafka/ts/utils/kafkaEvents.ts.ejs +5 -0
- package/templates/common/package.json.ejs +10 -2
- package/templates/common/shutdown/js/gracefulShutdown.js.ejs +65 -61
- package/templates/common/shutdown/js/gracefulShutdown.spec.js.ejs +149 -160
- package/templates/common/shutdown/ts/gracefulShutdown.spec.ts.ejs +179 -158
- package/templates/common/shutdown/ts/gracefulShutdown.ts.ejs +59 -55
- package/templates/common/src/tests/e2e/e2e.users.test.js.ejs +120 -49
- package/templates/common/src/tests/e2e/e2e.users.test.ts.ejs +120 -49
- package/templates/common/swagger.yml.ejs +118 -66
- package/templates/db/mysql/V1__Initial_Setup.sql.ejs +10 -9
- package/templates/db/postgres/V1__Initial_Setup.sql.ejs +10 -9
- package/templates/mvc/js/src/controllers/userController.js.ejs +246 -105
- package/templates/mvc/js/src/controllers/userController.spec.js.ejs +481 -209
- package/templates/mvc/js/src/errors/BadRequestError.js +11 -10
- package/templates/mvc/js/src/errors/BadRequestError.spec.js.ejs +22 -21
- package/templates/mvc/js/src/errors/NotFoundError.js +11 -10
- package/templates/mvc/js/src/errors/NotFoundError.spec.js.ejs +22 -21
- package/templates/mvc/js/src/graphql/resolvers/user.resolvers.js.ejs +25 -19
- package/templates/mvc/js/src/graphql/resolvers/user.resolvers.spec.js.ejs +64 -47
- package/templates/mvc/js/src/graphql/typeDefs/user.types.js.ejs +19 -17
- package/templates/mvc/js/src/index.js.ejs +1 -1
- package/templates/mvc/js/src/routes/api.js +10 -8
- package/templates/mvc/js/src/routes/api.spec.js.ejs +41 -36
- package/templates/mvc/js/src/utils/errorMessages.js +14 -0
- package/templates/mvc/ts/src/controllers/userController.spec.ts.ejs +481 -203
- package/templates/mvc/ts/src/controllers/userController.ts.ejs +248 -107
- package/templates/mvc/ts/src/errors/BadRequestError.spec.ts.ejs +22 -21
- package/templates/mvc/ts/src/errors/BadRequestError.ts +9 -8
- package/templates/mvc/ts/src/errors/NotFoundError.spec.ts.ejs +27 -21
- package/templates/mvc/ts/src/errors/NotFoundError.ts +9 -8
- package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.spec.ts.ejs +68 -51
- package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.ts.ejs +29 -21
- package/templates/mvc/ts/src/graphql/typeDefs/user.types.ts.ejs +17 -15
- package/templates/mvc/ts/src/index.ts.ejs +156 -153
- package/templates/mvc/ts/src/routes/api.spec.ts.ejs +59 -40
- package/templates/mvc/ts/src/routes/api.ts +12 -10
- package/templates/mvc/ts/src/utils/errorMessages.ts +12 -0
- package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.ts.ejs +0 -37
|
@@ -1,95 +1,95 @@
|
|
|
1
|
-
import { kafka } from '<%= configPath %>';
|
|
2
|
-
import { EachMessagePayload, Producer, Consumer } from 'kafkajs';
|
|
3
|
-
import logger from '<%= loggerPath %>';
|
|
4
|
-
|
|
5
|
-
export class KafkaService {
|
|
6
|
-
private producer: Producer;
|
|
7
|
-
private consumer: Consumer;
|
|
8
|
-
private isConnected = false;
|
|
9
|
-
private connectionPromise: Promise<void> | null = null;
|
|
10
|
-
|
|
11
|
-
constructor() {
|
|
12
|
-
this.producer = kafka.producer();
|
|
13
|
-
this.consumer = kafka.consumer({ groupId: 'test-group' });
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
async connect(retries = 10) {
|
|
17
|
-
if (this.connectionPromise) return this.connectionPromise;
|
|
18
|
-
|
|
19
|
-
this.connectionPromise = (async () => {
|
|
20
|
-
let attempt = 0;
|
|
21
|
-
// Auto-register WelcomeEmailConsumer if it exists
|
|
22
|
-
// Note: Dynamic import used here for simplicity and to avoid startup crashes.
|
|
23
|
-
// In enterprise production, consider using Dependency Injection.
|
|
24
|
-
const { WelcomeEmailConsumer } = await import('<% if (architecture === "Clean Architecture") { %>@/interfaces/messaging/consumers/instances/welcomeEmailConsumer<% } else { %>@/messaging/consumers/instances/welcomeEmailConsumer<% } %>');
|
|
25
|
-
while (attempt < retries) {
|
|
26
|
-
try {
|
|
27
|
-
await this.producer.connect();
|
|
28
|
-
await this.consumer.connect();
|
|
29
|
-
logger.info('[Kafka] Producer connected successfully');
|
|
30
|
-
logger.info('[Kafka] Consumer connected successfully');
|
|
31
|
-
this.isConnected = true;
|
|
32
|
-
|
|
33
|
-
try {
|
|
34
|
-
const welcomeConsumer = new WelcomeEmailConsumer();
|
|
35
|
-
await this.consumer.subscribe({ topic: welcomeConsumer.topic, fromBeginning: true });
|
|
36
|
-
logger.info(`[Kafka] Registered consumer for topic: ${welcomeConsumer.topic}`);
|
|
37
|
-
|
|
38
|
-
await this.consumer.run({
|
|
39
|
-
eachMessage: async (payload) => welcomeConsumer.onMessage(payload),
|
|
40
|
-
});
|
|
41
|
-
} catch (e) {
|
|
42
|
-
// Fallback or no consumers found
|
|
43
|
-
logger.warn(`[Kafka] Could not load WelcomeEmailConsumer, using fallback: ${(e as Error).message}`);
|
|
44
|
-
await this.consumer.subscribe({ topic: 'user-topic', fromBeginning: true });
|
|
45
|
-
await this.consumer.run({
|
|
46
|
-
eachMessage: async ({ message }: EachMessagePayload) => {
|
|
47
|
-
logger.info(`[Kafka] Consumer: Received message on user-topic: ${message.value?.toString()}`);
|
|
48
|
-
},
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
return; // Success
|
|
52
|
-
} catch (error) {
|
|
53
|
-
attempt++;
|
|
54
|
-
logger.error(`[Kafka] Connection attempt ${attempt} failed:`, (error as Error).message);
|
|
55
|
-
if (attempt >= retries) {
|
|
56
|
-
throw error;
|
|
57
|
-
}
|
|
58
|
-
await new Promise(res => setTimeout(res, 3000));
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
})();
|
|
62
|
-
|
|
63
|
-
return this.connectionPromise;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
async sendMessage(topic: string, message: string) {
|
|
67
|
-
if (this.connectionPromise) {
|
|
68
|
-
await this.connectionPromise;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (!this.isConnected) {
|
|
72
|
-
throw new Error('[Kafka] Producer not connected. Check logs for connection errors.');
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
await this.producer.send({
|
|
76
|
-
topic,
|
|
77
|
-
messages: [
|
|
78
|
-
{ value: message },
|
|
79
|
-
],
|
|
80
|
-
});
|
|
81
|
-
try {
|
|
82
|
-
const parsed = JSON.parse(message);
|
|
83
|
-
logger.info(`[Kafka] Producer: Sent ${parsed.action} event for '${parsed.payload?.email || 'unknown'}'`);
|
|
84
|
-
} catch {
|
|
85
|
-
logger.info(`[Kafka] Producer: Sent message to ${topic}`);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
async disconnect() {
|
|
90
|
-
await this.producer.disconnect();
|
|
91
|
-
await this.consumer.disconnect();
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export const kafkaService = new KafkaService();
|
|
1
|
+
import { kafka } from '<%= configPath %>';
|
|
2
|
+
import { EachMessagePayload, Producer, Consumer } from 'kafkajs';
|
|
3
|
+
import logger from '<%= loggerPath %>';
|
|
4
|
+
|
|
5
|
+
export class KafkaService {
|
|
6
|
+
private producer: Producer;
|
|
7
|
+
private consumer: Consumer;
|
|
8
|
+
private isConnected = false;
|
|
9
|
+
private connectionPromise: Promise<void> | null = null;
|
|
10
|
+
|
|
11
|
+
constructor() {
|
|
12
|
+
this.producer = kafka.producer();
|
|
13
|
+
this.consumer = kafka.consumer({ groupId: 'test-group' });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async connect(retries = 10) {
|
|
17
|
+
if (this.connectionPromise) return this.connectionPromise;
|
|
18
|
+
|
|
19
|
+
this.connectionPromise = (async () => {
|
|
20
|
+
let attempt = 0;
|
|
21
|
+
// Auto-register WelcomeEmailConsumer if it exists
|
|
22
|
+
// Note: Dynamic import used here for simplicity and to avoid startup crashes.
|
|
23
|
+
// In enterprise production, consider using Dependency Injection.
|
|
24
|
+
const { WelcomeEmailConsumer } = await import('<% if (architecture === "Clean Architecture") { %>@/interfaces/messaging/consumers/instances/welcomeEmailConsumer<% } else { %>@/messaging/consumers/instances/welcomeEmailConsumer<% } %>');
|
|
25
|
+
while (attempt < retries) {
|
|
26
|
+
try {
|
|
27
|
+
await this.producer.connect();
|
|
28
|
+
await this.consumer.connect();
|
|
29
|
+
logger.info('[Kafka] Producer connected successfully');
|
|
30
|
+
logger.info('[Kafka] Consumer connected successfully');
|
|
31
|
+
this.isConnected = true;
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const welcomeConsumer = new WelcomeEmailConsumer();
|
|
35
|
+
await this.consumer.subscribe({ topic: welcomeConsumer.topic, fromBeginning: true });
|
|
36
|
+
logger.info(`[Kafka] Registered consumer for topic: ${welcomeConsumer.topic}`);
|
|
37
|
+
|
|
38
|
+
await this.consumer.run({
|
|
39
|
+
eachMessage: async (payload) => welcomeConsumer.onMessage(payload),
|
|
40
|
+
});
|
|
41
|
+
} catch (e) {
|
|
42
|
+
// Fallback or no consumers found
|
|
43
|
+
logger.warn(`[Kafka] Could not load WelcomeEmailConsumer, using fallback: ${(e as Error).message}`);
|
|
44
|
+
await this.consumer.subscribe({ topic: 'user-topic', fromBeginning: true });
|
|
45
|
+
await this.consumer.run({
|
|
46
|
+
eachMessage: async ({ message }: EachMessagePayload) => {
|
|
47
|
+
logger.info(`[Kafka] Consumer: Received message on user-topic: ${message.value?.toString()}`);
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
return; // Success
|
|
52
|
+
} catch (error) {
|
|
53
|
+
attempt++;
|
|
54
|
+
logger.error(`[Kafka] Connection attempt ${attempt} failed:`, (error as Error).message);
|
|
55
|
+
if (attempt >= retries) {
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
await new Promise(res => setTimeout(res, 3000));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
})();
|
|
62
|
+
|
|
63
|
+
return this.connectionPromise;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async sendMessage(topic: string, message: string, key?: string) {
|
|
67
|
+
if (this.connectionPromise) {
|
|
68
|
+
await this.connectionPromise;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (!this.isConnected) {
|
|
72
|
+
throw new Error('[Kafka] Producer not connected. Check logs for connection errors.');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
await this.producer.send({
|
|
76
|
+
topic,
|
|
77
|
+
messages: [
|
|
78
|
+
{ key, value: message },
|
|
79
|
+
],
|
|
80
|
+
});
|
|
81
|
+
try {
|
|
82
|
+
const parsed = JSON.parse(message);
|
|
83
|
+
logger.info(`[Kafka] Producer: Sent ${parsed.action} event for '${parsed.payload?.email || 'unknown'}'`);
|
|
84
|
+
} catch {
|
|
85
|
+
logger.info(`[Kafka] Producer: Sent message to ${topic}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async disconnect() {
|
|
90
|
+
await this.producer.disconnect();
|
|
91
|
+
await this.consumer.disconnect();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export const kafkaService = new KafkaService();
|
|
@@ -22,6 +22,13 @@
|
|
|
22
22
|
"security:check": "npm audit && npm run snyk:test",
|
|
23
23
|
"snyk:test": "snyk test"<% } %>
|
|
24
24
|
},
|
|
25
|
+
"overrides": {
|
|
26
|
+
"brace-expansion": "^5.0.5",
|
|
27
|
+
"jake": "^10.9.4",
|
|
28
|
+
"micromatch": "^4.0.8",
|
|
29
|
+
"braces": "^3.0.3",
|
|
30
|
+
"picomatch": "^4.0.4"
|
|
31
|
+
},
|
|
25
32
|
"dependencies": {
|
|
26
33
|
"express": "^4.18.2",
|
|
27
34
|
"dotenv": "^16.3.1",
|
|
@@ -54,7 +61,8 @@
|
|
|
54
61
|
"morgan": "^1.10.0"<% if (communication === 'REST APIs' || communication === 'Kafka') { %>,
|
|
55
62
|
"swagger-ui-express": "^5.0.0",
|
|
56
63
|
"yamljs": "^0.3.0"<% } %><% if (communication === 'GraphQL') { %>,
|
|
57
|
-
"@apollo/server": "^
|
|
64
|
+
"@apollo/server": "^5.5.0",
|
|
65
|
+
"@as-integrations/express4": "^1.1.2",
|
|
58
66
|
"graphql": "^16.8.1",
|
|
59
67
|
"@graphql-tools/merge": "^9.0.3"<% } %>
|
|
60
68
|
},
|
|
@@ -76,7 +84,7 @@
|
|
|
76
84
|
"@types/morgan": "^1.9.9",
|
|
77
85
|
"rimraf": "^6.0.1"<% if ((viewEngine && viewEngine !== 'None') || communication === 'REST APIs' || communication === 'Kafka') { %>,
|
|
78
86
|
"cpx2": "^8.0.0"<% } %><% } %>,
|
|
79
|
-
"eslint": "^
|
|
87
|
+
"eslint": "^10.1.0",
|
|
80
88
|
"@eslint/js": "^9.20.0",
|
|
81
89
|
"globals": "^15.14.0",
|
|
82
90
|
"prettier": "^3.5.1",
|
|
@@ -1,61 +1,65 @@
|
|
|
1
|
-
<%_
|
|
2
|
-
let loggerPath = './logger';
|
|
3
|
-
let dbPath = '../config/database';
|
|
4
|
-
let redisPath = '../config/redisClient';
|
|
5
|
-
let kafkaPath = '../services/kafkaService';
|
|
6
|
-
|
|
7
|
-
if (architecture === 'Clean Architecture') {
|
|
8
|
-
loggerPath = '../infrastructure/log/logger';
|
|
9
|
-
dbPath = '../infrastructure/database/database';
|
|
10
|
-
redisPath = '../infrastructure/caching/redisClient';
|
|
11
|
-
kafkaPath = '../infrastructure/messaging/kafkaClient';
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const logger = require('<%- loggerPath %>');
|
|
15
|
-
|
|
16
|
-
const setupGracefulShutdown = (server) => {
|
|
17
|
-
const gracefulShutdown = async (signal) => {
|
|
18
|
-
logger.info(`Received ${signal}. Shutting down gracefully...`);
|
|
19
|
-
server.close(async () => {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
<%_
|
|
28
|
-
const
|
|
29
|
-
await
|
|
30
|
-
logger.info('
|
|
31
|
-
<%_ } -%>
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
<%_
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
<%_
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
|
|
1
|
+
<%_
|
|
2
|
+
let loggerPath = './logger';
|
|
3
|
+
let dbPath = '../config/database';
|
|
4
|
+
let redisPath = '../config/redisClient';
|
|
5
|
+
let kafkaPath = '../services/kafkaService';
|
|
6
|
+
|
|
7
|
+
if (architecture === 'Clean Architecture') {
|
|
8
|
+
loggerPath = '../infrastructure/log/logger';
|
|
9
|
+
dbPath = '../infrastructure/database/database';
|
|
10
|
+
redisPath = '../infrastructure/caching/redisClient';
|
|
11
|
+
kafkaPath = '../infrastructure/messaging/kafkaClient';
|
|
12
|
+
}
|
|
13
|
+
-%>
|
|
14
|
+
const logger = require('<%- loggerPath %>');
|
|
15
|
+
|
|
16
|
+
const setupGracefulShutdown = (server) => {
|
|
17
|
+
const gracefulShutdown = async (signal) => {
|
|
18
|
+
logger.info(`Received ${signal}. Shutting down gracefully...`);
|
|
19
|
+
server.close(async (err) => {
|
|
20
|
+
if (err) {
|
|
21
|
+
logger.error('Error closing HTTP server:', err);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
logger.info('HTTP server closed.');
|
|
25
|
+
try {
|
|
26
|
+
<%_ if (database !== 'None') { -%>
|
|
27
|
+
<%_ if (database === 'MongoDB') { -%>
|
|
28
|
+
const mongoose = require('mongoose');
|
|
29
|
+
await mongoose.connection.close(false);
|
|
30
|
+
logger.info('MongoDB connection closed.');
|
|
31
|
+
<%_ } else { -%>
|
|
32
|
+
const sequelize = require('<%- dbPath %>');
|
|
33
|
+
await sequelize.close();
|
|
34
|
+
logger.info('Database connection closed.');
|
|
35
|
+
<%_ } -%>
|
|
36
|
+
<%_ } -%>
|
|
37
|
+
<%_ if (caching === 'Redis') { -%>
|
|
38
|
+
const redisService = require('<%- redisPath %>');
|
|
39
|
+
await redisService.quit();
|
|
40
|
+
logger.info('Redis connection closed.');
|
|
41
|
+
<%_ } -%>
|
|
42
|
+
<%_ if (communication === 'Kafka') { -%>
|
|
43
|
+
const { disconnectKafka } = require('<%- kafkaPath %>');
|
|
44
|
+
await disconnectKafka();
|
|
45
|
+
logger.info('Kafka connection closed.');
|
|
46
|
+
<%_ } -%>
|
|
47
|
+
logger.info('Graceful shutdown fully completed.');
|
|
48
|
+
process.exit(0);
|
|
49
|
+
} catch (err) {
|
|
50
|
+
logger.error('Error during shutdown:', err);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
setTimeout(() => {
|
|
56
|
+
logger.error('Could not close connections in time, forcefully shutting down');
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}, 15000);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
|
|
62
|
+
process.on('SIGINT', () => gracefulShutdown('SIGINT'));
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
module.exports = setupGracefulShutdown;
|