nodejs-quickstart-structure 1.19.0 → 2.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/CHANGELOG.md +320 -301
- package/LICENSE +15 -15
- package/README.md +45 -10
- package/bin/index.js +7 -1
- package/lib/generator.js +139 -139
- package/lib/modules/app-setup.js +401 -401
- package/lib/modules/config-files.js +151 -151
- package/lib/modules/database-setup.js +116 -116
- package/lib/modules/project-setup.js +32 -32
- package/lib/prompts.js +100 -100
- package/package.json +79 -78
- package/templates/clean-architecture/js/src/domain/models/User.js +9 -9
- package/templates/clean-architecture/js/src/errors/ApiError.js +14 -14
- package/templates/clean-architecture/js/src/index.js.ejs +55 -55
- package/templates/clean-architecture/js/src/infrastructure/config/env.js.ejs +47 -47
- package/templates/clean-architecture/js/src/infrastructure/log/logger.js +36 -36
- package/templates/clean-architecture/js/src/infrastructure/log/logger.spec.js.ejs +63 -63
- package/templates/clean-architecture/js/src/infrastructure/webserver/middleware/errorMiddleware.js +30 -30
- package/templates/clean-architecture/js/src/infrastructure/webserver/server.js.ejs +89 -89
- package/templates/clean-architecture/js/src/infrastructure/webserver/swagger.js.ejs +6 -6
- package/templates/clean-architecture/js/src/interfaces/graphql/context.js.ejs +13 -13
- package/templates/clean-architecture/js/src/interfaces/graphql/context.spec.js.ejs +31 -31
- package/templates/clean-architecture/js/src/interfaces/graphql/index.js.ejs +5 -5
- package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/index.js.ejs +6 -6
- package/templates/clean-architecture/js/src/interfaces/graphql/typeDefs/index.js.ejs +6 -6
- package/templates/clean-architecture/js/src/interfaces/routes/api.spec.js.ejs +38 -38
- package/templates/clean-architecture/js/src/usecases/CreateUser.js +14 -14
- package/templates/clean-architecture/js/src/usecases/CreateUser.spec.js.ejs +51 -51
- package/templates/clean-architecture/js/src/usecases/GetAllUsers.js +12 -12
- package/templates/clean-architecture/js/src/usecases/GetAllUsers.spec.js.ejs +61 -61
- package/templates/clean-architecture/js/src/utils/httpCodes.js +9 -9
- package/templates/clean-architecture/ts/src/config/env.ts.ejs +46 -46
- package/templates/clean-architecture/ts/src/config/swagger.ts.ejs +6 -6
- package/templates/clean-architecture/ts/src/domain/user.ts +7 -7
- package/templates/clean-architecture/ts/src/errors/ApiError.ts +15 -15
- package/templates/clean-architecture/ts/src/index.ts.ejs +139 -139
- package/templates/clean-architecture/ts/src/infrastructure/log/logger.spec.ts.ejs +63 -63
- package/templates/clean-architecture/ts/src/infrastructure/log/logger.ts +36 -36
- package/templates/clean-architecture/ts/src/interfaces/graphql/context.spec.ts.ejs +32 -32
- package/templates/clean-architecture/ts/src/interfaces/graphql/context.ts.ejs +17 -17
- package/templates/clean-architecture/ts/src/interfaces/graphql/index.ts.ejs +3 -3
- package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/index.ts.ejs +4 -4
- package/templates/clean-architecture/ts/src/interfaces/graphql/typeDefs/index.ts.ejs +4 -4
- package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.spec.ts.ejs +40 -40
- package/templates/clean-architecture/ts/src/usecases/createUser.spec.ts.ejs +51 -51
- package/templates/clean-architecture/ts/src/usecases/createUser.ts +13 -13
- package/templates/clean-architecture/ts/src/usecases/getAllUsers.spec.ts.ejs +63 -63
- package/templates/clean-architecture/ts/src/usecases/getAllUsers.ts +10 -10
- package/templates/clean-architecture/ts/src/utils/errorMiddleware.ts.ejs +27 -27
- package/templates/clean-architecture/ts/src/utils/httpCodes.ts +7 -7
- package/templates/common/.cursorrules.ejs +60 -60
- package/templates/common/.dockerignore +12 -12
- package/templates/common/.env.example.ejs +41 -41
- package/templates/common/.gitlab-ci.yml.ejs +86 -86
- package/templates/common/.lintstagedrc +6 -6
- package/templates/common/.prettierrc +7 -7
- package/templates/common/Dockerfile +73 -73
- package/templates/common/Jenkinsfile.ejs +87 -87
- package/templates/common/SECURITY.md +20 -20
- package/templates/common/_github/workflows/ci.yml.ejs +46 -46
- package/templates/common/_github/workflows/security.yml.ejs +36 -36
- package/templates/common/_gitignore +5 -5
- package/templates/common/_husky/pre-commit +4 -4
- package/templates/common/caching/clean/js/CreateUser.js.ejs +29 -29
- package/templates/common/caching/clean/js/GetAllUsers.js.ejs +37 -37
- package/templates/common/caching/clean/ts/createUser.ts.ejs +27 -27
- package/templates/common/caching/clean/ts/getAllUsers.ts.ejs +34 -34
- package/templates/common/caching/js/memoryCache.js.ejs +60 -60
- package/templates/common/caching/js/memoryCache.spec.js.ejs +101 -101
- package/templates/common/caching/js/redisClient.js.ejs +75 -75
- package/templates/common/caching/js/redisClient.spec.js.ejs +147 -147
- package/templates/common/caching/ts/memoryCache.spec.ts.ejs +102 -102
- package/templates/common/caching/ts/redisClient.spec.ts.ejs +157 -157
- package/templates/common/database/js/database.js.ejs +19 -19
- package/templates/common/database/js/database.spec.js.ejs +56 -56
- package/templates/common/database/js/mongoose.js.ejs +33 -33
- package/templates/common/database/js/mongoose.spec.js.ejs +43 -43
- package/templates/common/database/ts/database.spec.ts.ejs +56 -56
- package/templates/common/database/ts/database.ts.ejs +21 -21
- package/templates/common/database/ts/mongoose.spec.ts.ejs +42 -42
- package/templates/common/database/ts/mongoose.ts.ejs +28 -28
- package/templates/common/docker-compose.yml.ejs +159 -159
- package/templates/common/ecosystem.config.js.ejs +40 -40
- package/templates/common/eslint.config.mjs.ejs +77 -77
- package/templates/common/health/js/healthRoute.spec.js.ejs +70 -70
- package/templates/common/health/ts/healthRoute.spec.ts.ejs +76 -76
- package/templates/common/jest.config.js.ejs +32 -32
- package/templates/common/kafka/js/config/kafka.js +9 -9
- package/templates/common/kafka/js/config/kafka.spec.js.ejs +27 -27
- package/templates/common/kafka/js/messaging/baseConsumer.spec.js.ejs +58 -58
- package/templates/common/kafka/js/messaging/userEventSchema.spec.js.ejs +27 -27
- package/templates/common/kafka/js/services/kafkaService.spec.js.ejs +106 -106
- package/templates/common/kafka/ts/config/kafka.spec.ts.ejs +27 -27
- package/templates/common/kafka/ts/config/kafka.ts +7 -7
- package/templates/common/kafka/ts/messaging/baseConsumer.spec.ts.ejs +50 -50
- package/templates/common/kafka/ts/messaging/baseConsumer.ts.ejs +27 -27
- package/templates/common/kafka/ts/services/kafkaService.spec.ts.ejs +81 -81
- package/templates/common/migrate-mongo-config.js.ejs +31 -31
- package/templates/common/migrations/init.js.ejs +23 -23
- package/templates/common/package.json.ejs +119 -118
- package/templates/common/prompts/add-feature.md.ejs +26 -26
- package/templates/common/prompts/project-context.md.ejs +43 -43
- package/templates/common/prompts/troubleshoot.md.ejs +28 -28
- package/templates/common/public/css/style.css +147 -147
- package/templates/common/scripts/run-e2e.js.ejs +63 -63
- package/templates/common/sonar-project.properties.ejs +27 -27
- package/templates/common/src/utils/errorMiddleware.spec.js.ejs +79 -79
- package/templates/common/src/utils/errorMiddleware.spec.ts.ejs +94 -94
- package/templates/common/tsconfig.json +22 -22
- package/templates/common/views/ejs/index.ejs +55 -55
- package/templates/common/views/pug/index.pug +40 -40
- package/templates/mvc/js/src/config/env.js.ejs +46 -46
- package/templates/mvc/js/src/config/swagger.js.ejs +6 -6
- package/templates/mvc/js/src/errors/ApiError.js +14 -14
- package/templates/mvc/js/src/graphql/context.js.ejs +7 -7
- package/templates/mvc/js/src/graphql/context.spec.js.ejs +29 -29
- package/templates/mvc/js/src/graphql/index.js.ejs +5 -5
- package/templates/mvc/js/src/graphql/resolvers/index.js.ejs +6 -6
- package/templates/mvc/js/src/graphql/typeDefs/index.js.ejs +6 -6
- package/templates/mvc/js/src/index.js.ejs +136 -136
- package/templates/mvc/js/src/utils/errorMiddleware.js +29 -29
- package/templates/mvc/js/src/utils/httpCodes.js +9 -9
- package/templates/mvc/js/src/utils/logger.js +40 -40
- package/templates/mvc/js/src/utils/logger.spec.js.ejs +63 -63
- package/templates/mvc/ts/src/config/env.ts.ejs +45 -45
- package/templates/mvc/ts/src/config/swagger.ts.ejs +6 -6
- package/templates/mvc/ts/src/errors/ApiError.ts +15 -15
- package/templates/mvc/ts/src/graphql/context.spec.ts.ejs +30 -30
- package/templates/mvc/ts/src/graphql/context.ts.ejs +12 -12
- package/templates/mvc/ts/src/graphql/index.ts.ejs +3 -3
- package/templates/mvc/ts/src/graphql/resolvers/index.ts.ejs +4 -4
- package/templates/mvc/ts/src/graphql/typeDefs/index.ts.ejs +4 -4
- package/templates/mvc/ts/src/utils/errorMiddleware.ts.ejs +27 -27
- package/templates/mvc/ts/src/utils/httpCodes.ts +7 -7
- package/templates/mvc/ts/src/utils/logger.spec.ts.ejs +63 -63
- package/templates/mvc/ts/src/utils/logger.ts +36 -36
|
@@ -1,140 +1,140 @@
|
|
|
1
|
-
import { env } from '@/config/env';
|
|
2
|
-
import express from 'express';
|
|
3
|
-
import cors from 'cors';
|
|
4
|
-
import helmet from 'helmet';
|
|
5
|
-
import hpp from 'hpp';
|
|
6
|
-
import rateLimit from 'express-rate-limit';
|
|
7
|
-
import logger from '@/infrastructure/log/logger';
|
|
8
|
-
import morgan from 'morgan';
|
|
9
|
-
import { errorMiddleware } from '@/utils/errorMiddleware';
|
|
10
|
-
import { setupGracefulShutdown } from '@/utils/gracefulShutdown';
|
|
11
|
-
import healthRoutes from '@/interfaces/routes/healthRoute';
|
|
12
|
-
<% if (communication === 'REST APIs' || communication === 'Kafka') { -%>
|
|
13
|
-
import userRoutes from '@/interfaces/routes/userRoutes';
|
|
14
|
-
import swaggerUi from 'swagger-ui-express';
|
|
15
|
-
import swaggerSpecs from '@/config/swagger';<% } %>
|
|
16
|
-
<%_ if (communication === 'Kafka') { -%>import { kafkaService } from '@/infrastructure/messaging/kafkaClient';<%_ } -%>
|
|
17
|
-
<%_ if (communication === 'GraphQL') { -%>
|
|
18
|
-
import { ApolloServer } from '@apollo/server';
|
|
19
|
-
import { expressMiddleware } from '@as-integrations/express4';
|
|
20
|
-
import { ApolloServerPluginLandingPageLocalDefault } from '@apollo/server/plugin/landingPage/default';
|
|
21
|
-
import { unwrapResolverError } from '@apollo/server/errors';
|
|
22
|
-
import { ApiError } from '@/errors/ApiError';
|
|
23
|
-
import { typeDefs, resolvers } from '@/interfaces/graphql';
|
|
24
|
-
import { gqlContext, MyContext } from '@/interfaces/graphql/context';
|
|
25
|
-
<%_ } -%>
|
|
26
|
-
|
|
27
|
-
const app = express();
|
|
28
|
-
const port = env.PORT;
|
|
29
|
-
|
|
30
|
-
// Security Middleware
|
|
31
|
-
<%_ if (communication === 'GraphQL') { -%>
|
|
32
|
-
app.use(helmet({
|
|
33
|
-
crossOriginEmbedderPolicy: false,
|
|
34
|
-
contentSecurityPolicy: {
|
|
35
|
-
directives: {
|
|
36
|
-
imgSrc: [`'self'`, 'data:', 'apollo-server-landing-page.cdn.apollographql.com'],
|
|
37
|
-
scriptSrc: [`'self'`, `https: 'unsafe-inline'`],
|
|
38
|
-
manifestSrc: [`'self'`, 'apollo-server-landing-page.cdn.apollographql.com'],
|
|
39
|
-
frameSrc: [`'self'`, 'sandbox.embed.apollographql.com'],
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
}));
|
|
43
|
-
<%_ } else { -%>
|
|
44
|
-
app.use(helmet());
|
|
45
|
-
<%_ } -%>
|
|
46
|
-
app.use(hpp());
|
|
47
|
-
app.use(cors({ origin: '*', methods: ['GET', 'POST', 'PUT', 'DELETE'] }));
|
|
48
|
-
const limiter = rateLimit({ windowMs: 10 * 60 * 1000, max: 100 });
|
|
49
|
-
app.use(limiter);
|
|
50
|
-
|
|
51
|
-
app.use(express.json());
|
|
52
|
-
app.use(morgan('combined', { stream: { write: (message) => logger.info(message.trim()) } }));
|
|
53
|
-
|
|
54
|
-
<%_ if (communication === 'REST APIs' || communication === 'Kafka') { -%>
|
|
55
|
-
app.use('/api/users', userRoutes);
|
|
56
|
-
<%_ } -%>
|
|
57
|
-
<%_ if (communication === 'REST APIs' || communication === 'Kafka') { -%>
|
|
58
|
-
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpecs));
|
|
59
|
-
<%_ } -%>
|
|
60
|
-
app.use('/health', healthRoutes);
|
|
61
|
-
|
|
62
|
-
// Start Server Logic
|
|
63
|
-
const startServer = async () => {
|
|
64
|
-
<%_ if (communication === 'GraphQL') { -%>
|
|
65
|
-
// GraphQL Setup
|
|
66
|
-
const apolloServer = new ApolloServer<MyContext>({
|
|
67
|
-
typeDefs,
|
|
68
|
-
resolvers,
|
|
69
|
-
plugins: [ApolloServerPluginLandingPageLocalDefault({ embed: true })],
|
|
70
|
-
formatError: (formattedError, error) => {
|
|
71
|
-
const originalError = unwrapResolverError(error);
|
|
72
|
-
if (originalError instanceof ApiError) {
|
|
73
|
-
return {
|
|
74
|
-
...formattedError,
|
|
75
|
-
message: originalError.message,
|
|
76
|
-
extensions: {
|
|
77
|
-
...formattedError.extensions,
|
|
78
|
-
code: originalError.statusCode.toString(),
|
|
79
|
-
}
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
logger.error(`GraphQL Error: ${formattedError.message}`);
|
|
84
|
-
if (originalError instanceof Error && originalError.stack && process.env.NODE_ENV === 'development') {
|
|
85
|
-
logger.error(originalError.stack);
|
|
86
|
-
}
|
|
87
|
-
return formattedError;
|
|
88
|
-
},
|
|
89
|
-
});
|
|
90
|
-
await apolloServer.start();
|
|
91
|
-
app.use('/graphql', expressMiddleware(apolloServer, { context: gqlContext }));
|
|
92
|
-
<%_ } -%>
|
|
93
|
-
app.use(errorMiddleware);
|
|
94
|
-
const server = app.listen(port, () => {
|
|
95
|
-
logger.info(`Server running on port ${port}`);
|
|
96
|
-
<%_ if (communication === 'Kafka') { -%>
|
|
97
|
-
kafkaService.connect()
|
|
98
|
-
.then(async () => {
|
|
99
|
-
logger.info('Kafka connected');
|
|
100
|
-
})
|
|
101
|
-
.catch(err => {
|
|
102
|
-
logger.error('Failed to connect to Kafka after retries:', (err as Error).message);
|
|
103
|
-
});
|
|
104
|
-
<%_ } -%>
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
setupGracefulShutdown(server<% if(communication === 'Kafka') { %>, kafkaService<% } %>);
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
<%_ if (database !== 'None') { -%>
|
|
111
|
-
// Database Sync
|
|
112
|
-
<%_ if (database !== 'None') { -%>
|
|
113
|
-
import <% if (database === 'MongoDB') { %>connectDB<% } else { %>sequelize<% } %> from '@/infrastructure/database/database';
|
|
114
|
-
<%_ } -%>
|
|
115
|
-
|
|
116
|
-
const syncDatabase = async () => {
|
|
117
|
-
let retries = 30;
|
|
118
|
-
while (retries) {
|
|
119
|
-
try {
|
|
120
|
-
<%_ if (database === 'MongoDB') { -%>
|
|
121
|
-
await connectDB();
|
|
122
|
-
<%_ } else { -%>
|
|
123
|
-
await sequelize.sync();
|
|
124
|
-
<%_ } -%>
|
|
125
|
-
logger.info('Database synced');
|
|
126
|
-
await startServer();
|
|
127
|
-
break;
|
|
128
|
-
} catch (error) {
|
|
129
|
-
logger.error('Error syncing database:', error);
|
|
130
|
-
retries -= 1;
|
|
131
|
-
logger.info(`Retries left: ${retries}`);
|
|
132
|
-
await new Promise(res => setTimeout(res, 5000));
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
syncDatabase();
|
|
138
|
-
<%_ } else { -%>
|
|
139
|
-
startServer();
|
|
1
|
+
import { env } from '@/config/env';
|
|
2
|
+
import express from 'express';
|
|
3
|
+
import cors from 'cors';
|
|
4
|
+
import helmet from 'helmet';
|
|
5
|
+
import hpp from 'hpp';
|
|
6
|
+
import rateLimit from 'express-rate-limit';
|
|
7
|
+
import logger from '@/infrastructure/log/logger';
|
|
8
|
+
import morgan from 'morgan';
|
|
9
|
+
import { errorMiddleware } from '@/utils/errorMiddleware';
|
|
10
|
+
import { setupGracefulShutdown } from '@/utils/gracefulShutdown';
|
|
11
|
+
import healthRoutes from '@/interfaces/routes/healthRoute';
|
|
12
|
+
<% if (communication === 'REST APIs' || communication === 'Kafka') { -%>
|
|
13
|
+
import userRoutes from '@/interfaces/routes/userRoutes';
|
|
14
|
+
import swaggerUi from 'swagger-ui-express';
|
|
15
|
+
import swaggerSpecs from '@/config/swagger';<% } %>
|
|
16
|
+
<%_ if (communication === 'Kafka') { -%>import { kafkaService } from '@/infrastructure/messaging/kafkaClient';<%_ } -%>
|
|
17
|
+
<%_ if (communication === 'GraphQL') { -%>
|
|
18
|
+
import { ApolloServer } from '@apollo/server';
|
|
19
|
+
import { expressMiddleware } from '@as-integrations/express4';
|
|
20
|
+
import { ApolloServerPluginLandingPageLocalDefault } from '@apollo/server/plugin/landingPage/default';
|
|
21
|
+
import { unwrapResolverError } from '@apollo/server/errors';
|
|
22
|
+
import { ApiError } from '@/errors/ApiError';
|
|
23
|
+
import { typeDefs, resolvers } from '@/interfaces/graphql';
|
|
24
|
+
import { gqlContext, MyContext } from '@/interfaces/graphql/context';
|
|
25
|
+
<%_ } -%>
|
|
26
|
+
|
|
27
|
+
const app = express();
|
|
28
|
+
const port = env.PORT;
|
|
29
|
+
|
|
30
|
+
// Security Middleware
|
|
31
|
+
<%_ if (communication === 'GraphQL') { -%>
|
|
32
|
+
app.use(helmet({
|
|
33
|
+
crossOriginEmbedderPolicy: false,
|
|
34
|
+
contentSecurityPolicy: {
|
|
35
|
+
directives: {
|
|
36
|
+
imgSrc: [`'self'`, 'data:', 'apollo-server-landing-page.cdn.apollographql.com'],
|
|
37
|
+
scriptSrc: [`'self'`, `https: 'unsafe-inline'`],
|
|
38
|
+
manifestSrc: [`'self'`, 'apollo-server-landing-page.cdn.apollographql.com'],
|
|
39
|
+
frameSrc: [`'self'`, 'sandbox.embed.apollographql.com'],
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
}));
|
|
43
|
+
<%_ } else { -%>
|
|
44
|
+
app.use(helmet());
|
|
45
|
+
<%_ } -%>
|
|
46
|
+
app.use(hpp());
|
|
47
|
+
app.use(cors({ origin: '*', methods: ['GET', 'POST', 'PUT', 'DELETE'] }));
|
|
48
|
+
const limiter = rateLimit({ windowMs: 10 * 60 * 1000, max: 100 });
|
|
49
|
+
app.use(limiter);
|
|
50
|
+
|
|
51
|
+
app.use(express.json());
|
|
52
|
+
app.use(morgan('combined', { stream: { write: (message) => logger.info(message.trim()) } }));
|
|
53
|
+
|
|
54
|
+
<%_ if (communication === 'REST APIs' || communication === 'Kafka') { -%>
|
|
55
|
+
app.use('/api/users', userRoutes);
|
|
56
|
+
<%_ } -%>
|
|
57
|
+
<%_ if (communication === 'REST APIs' || communication === 'Kafka') { -%>
|
|
58
|
+
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpecs));
|
|
59
|
+
<%_ } -%>
|
|
60
|
+
app.use('/health', healthRoutes);
|
|
61
|
+
|
|
62
|
+
// Start Server Logic
|
|
63
|
+
const startServer = async () => {
|
|
64
|
+
<%_ if (communication === 'GraphQL') { -%>
|
|
65
|
+
// GraphQL Setup
|
|
66
|
+
const apolloServer = new ApolloServer<MyContext>({
|
|
67
|
+
typeDefs,
|
|
68
|
+
resolvers,
|
|
69
|
+
plugins: [ApolloServerPluginLandingPageLocalDefault({ embed: true })],
|
|
70
|
+
formatError: (formattedError, error) => {
|
|
71
|
+
const originalError = unwrapResolverError(error);
|
|
72
|
+
if (originalError instanceof ApiError) {
|
|
73
|
+
return {
|
|
74
|
+
...formattedError,
|
|
75
|
+
message: originalError.message,
|
|
76
|
+
extensions: {
|
|
77
|
+
...formattedError.extensions,
|
|
78
|
+
code: originalError.statusCode.toString(),
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
logger.error(`GraphQL Error: ${formattedError.message}`);
|
|
84
|
+
if (originalError instanceof Error && originalError.stack && process.env.NODE_ENV === 'development') {
|
|
85
|
+
logger.error(originalError.stack);
|
|
86
|
+
}
|
|
87
|
+
return formattedError;
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
await apolloServer.start();
|
|
91
|
+
app.use('/graphql', expressMiddleware(apolloServer, { context: gqlContext }));
|
|
92
|
+
<%_ } -%>
|
|
93
|
+
app.use(errorMiddleware);
|
|
94
|
+
const server = app.listen(port, () => {
|
|
95
|
+
logger.info(`Server running on port ${port}`);
|
|
96
|
+
<%_ if (communication === 'Kafka') { -%>
|
|
97
|
+
kafkaService.connect()
|
|
98
|
+
.then(async () => {
|
|
99
|
+
logger.info('Kafka connected');
|
|
100
|
+
})
|
|
101
|
+
.catch(err => {
|
|
102
|
+
logger.error('Failed to connect to Kafka after retries:', (err as Error).message);
|
|
103
|
+
});
|
|
104
|
+
<%_ } -%>
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
setupGracefulShutdown(server<% if(communication === 'Kafka') { %>, kafkaService<% } %>);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
<%_ if (database !== 'None') { -%>
|
|
111
|
+
// Database Sync
|
|
112
|
+
<%_ if (database !== 'None') { -%>
|
|
113
|
+
import <% if (database === 'MongoDB') { %>connectDB<% } else { %>sequelize<% } %> from '@/infrastructure/database/database';
|
|
114
|
+
<%_ } -%>
|
|
115
|
+
|
|
116
|
+
const syncDatabase = async () => {
|
|
117
|
+
let retries = 30;
|
|
118
|
+
while (retries) {
|
|
119
|
+
try {
|
|
120
|
+
<%_ if (database === 'MongoDB') { -%>
|
|
121
|
+
await connectDB();
|
|
122
|
+
<%_ } else { -%>
|
|
123
|
+
await sequelize.sync();
|
|
124
|
+
<%_ } -%>
|
|
125
|
+
logger.info('Database synced');
|
|
126
|
+
await startServer();
|
|
127
|
+
break;
|
|
128
|
+
} catch (error) {
|
|
129
|
+
logger.error('Error syncing database:', error);
|
|
130
|
+
retries -= 1;
|
|
131
|
+
logger.info(`Retries left: ${retries}`);
|
|
132
|
+
await new Promise(res => setTimeout(res, 5000));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
syncDatabase();
|
|
138
|
+
<%_ } else { -%>
|
|
139
|
+
startServer();
|
|
140
140
|
<%_ } -%>
|
|
@@ -1,63 +1,63 @@
|
|
|
1
|
-
jest.mock('winston-daily-rotate-file');
|
|
2
|
-
jest.mock('winston', () => {
|
|
3
|
-
const mockLogger = {
|
|
4
|
-
add: jest.fn(),
|
|
5
|
-
info: jest.fn(),
|
|
6
|
-
error: jest.fn(),
|
|
7
|
-
warn: jest.fn()
|
|
8
|
-
};
|
|
9
|
-
const format = {
|
|
10
|
-
combine: jest.fn(),
|
|
11
|
-
timestamp: jest.fn(),
|
|
12
|
-
json: jest.fn(),
|
|
13
|
-
simple: jest.fn()
|
|
14
|
-
};
|
|
15
|
-
const transports = {
|
|
16
|
-
Console: jest.fn(),
|
|
17
|
-
DailyRotateFile: jest.fn()
|
|
18
|
-
};
|
|
19
|
-
return {
|
|
20
|
-
format,
|
|
21
|
-
transports,
|
|
22
|
-
createLogger: jest.fn().mockReturnValue(mockLogger)
|
|
23
|
-
};
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
<% if (architecture === 'MVC') { -%>
|
|
27
|
-
import logger from '@/utils/logger';
|
|
28
|
-
<% } else { -%>
|
|
29
|
-
import logger from '@/infrastructure/log/logger';
|
|
30
|
-
<% } -%>
|
|
31
|
-
|
|
32
|
-
describe('Logger', () => {
|
|
33
|
-
it('should export a logger instance', () => {
|
|
34
|
-
expect(logger).toBeDefined();
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('should have info method', () => {
|
|
38
|
-
expect(typeof logger.info).toBe('function');
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it('should have error method', () => {
|
|
42
|
-
expect(typeof logger.error).toBe('function');
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('should call info', () => {
|
|
46
|
-
logger.info('test message');
|
|
47
|
-
expect(logger.info).toHaveBeenCalledWith('test message');
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('should call error', () => {
|
|
51
|
-
logger.error('test error');
|
|
52
|
-
expect(logger.error).toHaveBeenCalledWith('test error');
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('should use JSON format in production environment', () => {
|
|
56
|
-
const winston = require('winston');
|
|
57
|
-
jest.resetModules();
|
|
58
|
-
process.env.NODE_ENV = 'production';
|
|
59
|
-
require('<% if (architecture === "MVC") { %>@/utils/logger<% } else { %>@/infrastructure/log/logger<% } %>');
|
|
60
|
-
expect(winston.format.json).toHaveBeenCalled();
|
|
61
|
-
process.env.NODE_ENV = 'test';
|
|
62
|
-
});
|
|
63
|
-
});
|
|
1
|
+
jest.mock('winston-daily-rotate-file');
|
|
2
|
+
jest.mock('winston', () => {
|
|
3
|
+
const mockLogger = {
|
|
4
|
+
add: jest.fn(),
|
|
5
|
+
info: jest.fn(),
|
|
6
|
+
error: jest.fn(),
|
|
7
|
+
warn: jest.fn()
|
|
8
|
+
};
|
|
9
|
+
const format = {
|
|
10
|
+
combine: jest.fn(),
|
|
11
|
+
timestamp: jest.fn(),
|
|
12
|
+
json: jest.fn(),
|
|
13
|
+
simple: jest.fn()
|
|
14
|
+
};
|
|
15
|
+
const transports = {
|
|
16
|
+
Console: jest.fn(),
|
|
17
|
+
DailyRotateFile: jest.fn()
|
|
18
|
+
};
|
|
19
|
+
return {
|
|
20
|
+
format,
|
|
21
|
+
transports,
|
|
22
|
+
createLogger: jest.fn().mockReturnValue(mockLogger)
|
|
23
|
+
};
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
<% if (architecture === 'MVC') { -%>
|
|
27
|
+
import logger from '@/utils/logger';
|
|
28
|
+
<% } else { -%>
|
|
29
|
+
import logger from '@/infrastructure/log/logger';
|
|
30
|
+
<% } -%>
|
|
31
|
+
|
|
32
|
+
describe('Logger', () => {
|
|
33
|
+
it('should export a logger instance', () => {
|
|
34
|
+
expect(logger).toBeDefined();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should have info method', () => {
|
|
38
|
+
expect(typeof logger.info).toBe('function');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should have error method', () => {
|
|
42
|
+
expect(typeof logger.error).toBe('function');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should call info', () => {
|
|
46
|
+
logger.info('test message');
|
|
47
|
+
expect(logger.info).toHaveBeenCalledWith('test message');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should call error', () => {
|
|
51
|
+
logger.error('test error');
|
|
52
|
+
expect(logger.error).toHaveBeenCalledWith('test error');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should use JSON format in production environment', () => {
|
|
56
|
+
const winston = require('winston');
|
|
57
|
+
jest.resetModules();
|
|
58
|
+
process.env.NODE_ENV = 'production';
|
|
59
|
+
require('<% if (architecture === "MVC") { %>@/utils/logger<% } else { %>@/infrastructure/log/logger<% } %>');
|
|
60
|
+
expect(winston.format.json).toHaveBeenCalled();
|
|
61
|
+
process.env.NODE_ENV = 'test';
|
|
62
|
+
});
|
|
63
|
+
});
|
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
import winston from 'winston';
|
|
2
|
-
import 'winston-daily-rotate-file';
|
|
3
|
-
|
|
4
|
-
const logger = winston.createLogger({
|
|
5
|
-
level: 'info',
|
|
6
|
-
format: winston.format.combine(
|
|
7
|
-
winston.format.timestamp(),
|
|
8
|
-
winston.format.json()
|
|
9
|
-
),
|
|
10
|
-
defaultMeta: { service: 'user-service' },
|
|
11
|
-
transports: [
|
|
12
|
-
new winston.transports.DailyRotateFile({
|
|
13
|
-
filename: 'logs/error-%DATE%.log',
|
|
14
|
-
datePattern: 'YYYY-MM-DD',
|
|
15
|
-
zippedArchive: true,
|
|
16
|
-
maxSize: '20m',
|
|
17
|
-
maxFiles: '14d',
|
|
18
|
-
level: 'error',
|
|
19
|
-
}),
|
|
20
|
-
new winston.transports.DailyRotateFile({
|
|
21
|
-
filename: 'logs/combined-%DATE%.log',
|
|
22
|
-
datePattern: 'YYYY-MM-DD',
|
|
23
|
-
zippedArchive: true,
|
|
24
|
-
maxSize: '20m',
|
|
25
|
-
maxFiles: '14d',
|
|
26
|
-
}),
|
|
27
|
-
],
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
logger.add(new winston.transports.Console({
|
|
31
|
-
format: process.env.NODE_ENV !== 'production'
|
|
32
|
-
? winston.format.simple()
|
|
33
|
-
: winston.format.json(),
|
|
34
|
-
}));
|
|
35
|
-
|
|
36
|
-
export default logger;
|
|
1
|
+
import winston from 'winston';
|
|
2
|
+
import 'winston-daily-rotate-file';
|
|
3
|
+
|
|
4
|
+
const logger = winston.createLogger({
|
|
5
|
+
level: 'info',
|
|
6
|
+
format: winston.format.combine(
|
|
7
|
+
winston.format.timestamp(),
|
|
8
|
+
winston.format.json()
|
|
9
|
+
),
|
|
10
|
+
defaultMeta: { service: 'user-service' },
|
|
11
|
+
transports: [
|
|
12
|
+
new winston.transports.DailyRotateFile({
|
|
13
|
+
filename: 'logs/error-%DATE%.log',
|
|
14
|
+
datePattern: 'YYYY-MM-DD',
|
|
15
|
+
zippedArchive: true,
|
|
16
|
+
maxSize: '20m',
|
|
17
|
+
maxFiles: '14d',
|
|
18
|
+
level: 'error',
|
|
19
|
+
}),
|
|
20
|
+
new winston.transports.DailyRotateFile({
|
|
21
|
+
filename: 'logs/combined-%DATE%.log',
|
|
22
|
+
datePattern: 'YYYY-MM-DD',
|
|
23
|
+
zippedArchive: true,
|
|
24
|
+
maxSize: '20m',
|
|
25
|
+
maxFiles: '14d',
|
|
26
|
+
}),
|
|
27
|
+
],
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
logger.add(new winston.transports.Console({
|
|
31
|
+
format: process.env.NODE_ENV !== 'production'
|
|
32
|
+
? winston.format.simple()
|
|
33
|
+
: winston.format.json(),
|
|
34
|
+
}));
|
|
35
|
+
|
|
36
|
+
export default logger;
|
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
import { gqlContext } from '@/interfaces/graphql/context';
|
|
2
|
-
import { Request } from 'express';
|
|
3
|
-
import { resolvers } from '@/interfaces/graphql/resolvers';
|
|
4
|
-
import { typeDefs } from '@/interfaces/graphql/typeDefs';
|
|
5
|
-
|
|
6
|
-
jest.mock('@/infrastructure/repositories/UserRepository');
|
|
7
|
-
|
|
8
|
-
describe('GraphQL Context', () => {
|
|
9
|
-
it('should exercise GraphQL index entry points', () => {
|
|
10
|
-
expect(resolvers).toBeDefined();
|
|
11
|
-
expect(typeDefs).toBeDefined();
|
|
12
|
-
});
|
|
13
|
-
it('should return context with token when authorization header is present', async () => {
|
|
14
|
-
const mockRequest = {
|
|
15
|
-
headers: {
|
|
16
|
-
authorization: 'Bearer token123',
|
|
17
|
-
},
|
|
18
|
-
} as Request;
|
|
19
|
-
|
|
20
|
-
const context = await gqlContext({ req: mockRequest });
|
|
21
|
-
expect(context.token).toBe('Bearer token123');
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it('should return context with empty token when authorization header is missing', async () => {
|
|
25
|
-
const mockRequest = {
|
|
26
|
-
headers: {},
|
|
27
|
-
} as Request;
|
|
28
|
-
|
|
29
|
-
const context = await gqlContext({ req: mockRequest });
|
|
30
|
-
expect(context.token).toBe('');
|
|
31
|
-
});
|
|
32
|
-
});
|
|
1
|
+
import { gqlContext } from '@/interfaces/graphql/context';
|
|
2
|
+
import { Request } from 'express';
|
|
3
|
+
import { resolvers } from '@/interfaces/graphql/resolvers';
|
|
4
|
+
import { typeDefs } from '@/interfaces/graphql/typeDefs';
|
|
5
|
+
|
|
6
|
+
jest.mock('@/infrastructure/repositories/UserRepository');
|
|
7
|
+
|
|
8
|
+
describe('GraphQL Context', () => {
|
|
9
|
+
it('should exercise GraphQL index entry points', () => {
|
|
10
|
+
expect(resolvers).toBeDefined();
|
|
11
|
+
expect(typeDefs).toBeDefined();
|
|
12
|
+
});
|
|
13
|
+
it('should return context with token when authorization header is present', async () => {
|
|
14
|
+
const mockRequest = {
|
|
15
|
+
headers: {
|
|
16
|
+
authorization: 'Bearer token123',
|
|
17
|
+
},
|
|
18
|
+
} as Request;
|
|
19
|
+
|
|
20
|
+
const context = await gqlContext({ req: mockRequest });
|
|
21
|
+
expect(context.token).toBe('Bearer token123');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should return context with empty token when authorization header is missing', async () => {
|
|
25
|
+
const mockRequest = {
|
|
26
|
+
headers: {},
|
|
27
|
+
} as Request;
|
|
28
|
+
|
|
29
|
+
const context = await gqlContext({ req: mockRequest });
|
|
30
|
+
expect(context.token).toBe('');
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { Request } from 'express';
|
|
2
|
-
import { UserRepository } from '@/infrastructure/repositories/UserRepository';
|
|
3
|
-
|
|
4
|
-
export interface MyContext {
|
|
5
|
-
token?: string;
|
|
6
|
-
userRepository: UserRepository;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export const gqlContext = async ({ req }: { req: Request }): Promise<MyContext> => {
|
|
10
|
-
const token = req.headers.authorization || '';
|
|
11
|
-
const userRepository = new UserRepository();
|
|
12
|
-
|
|
13
|
-
return {
|
|
14
|
-
token,
|
|
15
|
-
userRepository
|
|
16
|
-
};
|
|
17
|
-
};
|
|
1
|
+
import { Request } from 'express';
|
|
2
|
+
import { UserRepository } from '@/infrastructure/repositories/UserRepository';
|
|
3
|
+
|
|
4
|
+
export interface MyContext {
|
|
5
|
+
token?: string;
|
|
6
|
+
userRepository: UserRepository;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const gqlContext = async ({ req }: { req: Request }): Promise<MyContext> => {
|
|
10
|
+
const token = req.headers.authorization || '';
|
|
11
|
+
const userRepository = new UserRepository();
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
token,
|
|
15
|
+
userRepository
|
|
16
|
+
};
|
|
17
|
+
};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { typeDefs } from '@/interfaces/graphql/typeDefs';
|
|
2
|
-
export { resolvers } from '@/interfaces/graphql/resolvers';
|
|
3
|
-
export { gqlContext, MyContext } from '@/interfaces/graphql/context';
|
|
1
|
+
export { typeDefs } from '@/interfaces/graphql/typeDefs';
|
|
2
|
+
export { resolvers } from '@/interfaces/graphql/resolvers';
|
|
3
|
+
export { gqlContext, MyContext } from '@/interfaces/graphql/context';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mergeResolvers } from '@graphql-tools/merge';
|
|
2
|
-
import { userResolvers } from '@/interfaces/graphql/resolvers/user.resolvers';
|
|
3
|
-
|
|
4
|
-
export const resolvers = mergeResolvers([userResolvers]);
|
|
1
|
+
import { mergeResolvers } from '@graphql-tools/merge';
|
|
2
|
+
import { userResolvers } from '@/interfaces/graphql/resolvers/user.resolvers';
|
|
3
|
+
|
|
4
|
+
export const resolvers = mergeResolvers([userResolvers]);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mergeTypeDefs } from '@graphql-tools/merge';
|
|
2
|
-
import { userTypes } from './user.types';
|
|
3
|
-
|
|
4
|
-
export const typeDefs = mergeTypeDefs([userTypes]);
|
|
1
|
+
import { mergeTypeDefs } from '@graphql-tools/merge';
|
|
2
|
+
import { userTypes } from './user.types';
|
|
3
|
+
|
|
4
|
+
export const typeDefs = mergeTypeDefs([userTypes]);
|