crypt-express-app 1.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/README.md +1 -0
- package/dist/index.js +20 -0
- package/dist/scripts/generate-app.js +96 -0
- package/dist/scripts/generate-locales.js +29 -0
- package/dist/scripts/generate-middleware.js +165 -0
- package/dist/scripts/generate-module.js +677 -0
- package/dist/scripts/generate-root-files.js +123 -0
- package/dist/scripts/generate-utils.js +191 -0
- package/dist/scripts/middleware.js +165 -0
- package/dist/scripts/setup-prisma.js +21 -0
- package/index.ts +29 -0
- package/package.json +23 -0
- package/scripts/generate-app.ts +104 -0
- package/scripts/generate-locales.ts +36 -0
- package/scripts/generate-middleware.ts +176 -0
- package/scripts/generate-module.ts +715 -0
- package/scripts/generate-root-files.ts +147 -0
- package/scripts/generate-utils.ts +208 -0
- package/scripts/setup-prisma.ts +26 -0
- package/tsconfig.json +10 -0
|
@@ -0,0 +1,677 @@
|
|
|
1
|
+
// generate-module.ts
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
// get module name from command line
|
|
5
|
+
const args = process.argv.slice(2);
|
|
6
|
+
// Check that exactly **one argument** is provided
|
|
7
|
+
if (args.length !== 1) {
|
|
8
|
+
console.error('Invalid module name: provide exactly one module name without spaces.');
|
|
9
|
+
console.error('Example: npm run generate-module clientRole');
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
const moduleInput = args[0];
|
|
13
|
+
// Reject if input contains spaces
|
|
14
|
+
if (/\s/.test(moduleInput)) {
|
|
15
|
+
console.error('Invalid module name: spaces are not allowed. Use camelCase, kebab-case or snake_case.');
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
if (!moduleInput) {
|
|
19
|
+
console.error('Please provide a module name: npm run generate-module <module-name>');
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
// Convert any input to camelCase
|
|
23
|
+
const moduleName = moduleInput.includes('-') || moduleInput.includes('_') || moduleInput.includes(' ')
|
|
24
|
+
? moduleInput
|
|
25
|
+
.toLowerCase()
|
|
26
|
+
.replace(/[-_\s]+(.)/g, (_, c) => c.toUpperCase())
|
|
27
|
+
: moduleInput; // preserve existing camelCase
|
|
28
|
+
console.log("moduleName: ", moduleName);
|
|
29
|
+
// Convert any input (camelCase, PascalCase, snake_case, spaces) to kebab-case
|
|
30
|
+
const fileName = moduleName
|
|
31
|
+
.replace(/([a-z])([A-Z])/g, '$1-$2') // add dash before uppercase letters
|
|
32
|
+
.toLowerCase(); // lowercase everything
|
|
33
|
+
console.log("fileName: ", fileName);
|
|
34
|
+
// const basePath = path.join(__dirname, '..', 'src', 'modules', fileName);
|
|
35
|
+
// const basePath = path.join(__dirname, '..', 'src', 'modules', fileName);
|
|
36
|
+
const projectRoot = process.cwd();
|
|
37
|
+
const modulesRoot = path.join(projectRoot, "src", "modules");
|
|
38
|
+
const basePath = path.join(modulesRoot, fileName);
|
|
39
|
+
// ensure modules directory exists
|
|
40
|
+
if (!fs.existsSync(modulesRoot)) {
|
|
41
|
+
fs.mkdirSync(modulesRoot, { recursive: true });
|
|
42
|
+
}
|
|
43
|
+
// prevent overwrite
|
|
44
|
+
if (fs.existsSync(basePath)) {
|
|
45
|
+
console.error(`❌ Module '${fileName}' already exists.`);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
fs.mkdirSync(basePath, { recursive: true });
|
|
49
|
+
// Create folder
|
|
50
|
+
if (!fs.existsSync(basePath)) {
|
|
51
|
+
fs.mkdirSync(basePath, { recursive: true });
|
|
52
|
+
}
|
|
53
|
+
// Capitalize first letter
|
|
54
|
+
const ModuleClassName = moduleName.charAt(0).toUpperCase() + moduleName.slice(1);
|
|
55
|
+
// Service
|
|
56
|
+
const serviceContent = `import { Create${ModuleClassName}Dto, Update${ModuleClassName}Dto, Create${ModuleClassName}Response, Update${ModuleClassName}Response, Get${ModuleClassName}Response, List${ModuleClassName}Response } from "./${fileName}.dto";
|
|
57
|
+
import { Prisma } from "@prisma/client";
|
|
58
|
+
import prisma from '../../prisma/client';
|
|
59
|
+
import { PaginationRequestDto } from "../common/common.dto";
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
export class ${ModuleClassName}Service {
|
|
63
|
+
constructor() {}
|
|
64
|
+
|
|
65
|
+
async create(payload: Create${ModuleClassName}Dto): Promise<Create${ModuleClassName}Response> {
|
|
66
|
+
try {
|
|
67
|
+
// TODO: create ${ModuleClassName} in database
|
|
68
|
+
return {
|
|
69
|
+
${moduleName}Id: "generated-${moduleName}Id",
|
|
70
|
+
...payload,
|
|
71
|
+
createdAt: new Date().toISOString(),
|
|
72
|
+
updatedAt: new Date().toISOString(),
|
|
73
|
+
};
|
|
74
|
+
} catch (error) {
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async update(${moduleName}Id: string, payload: Update${ModuleClassName}Dto): Promise<Update${ModuleClassName}Response> {
|
|
80
|
+
try {
|
|
81
|
+
// TODO: update ${ModuleClassName} by ${moduleName}Id
|
|
82
|
+
return {
|
|
83
|
+
${moduleName}Id,
|
|
84
|
+
...payload,
|
|
85
|
+
updatedAt: new Date().toISOString(),
|
|
86
|
+
};
|
|
87
|
+
} catch (error) {
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async getById(${moduleName}Id: string): Promise<Get${ModuleClassName}Response> {
|
|
93
|
+
try {
|
|
94
|
+
// TODO: fetch ${ModuleClassName} by ${moduleName}Id
|
|
95
|
+
return {
|
|
96
|
+
${moduleName}Id,
|
|
97
|
+
name: "Sample ${ModuleClassName}",
|
|
98
|
+
createdAt: new Date().toISOString(),
|
|
99
|
+
updatedAt: new Date().toISOString(),
|
|
100
|
+
};
|
|
101
|
+
} catch (error) {
|
|
102
|
+
throw error;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async list({
|
|
107
|
+
offset = 0,
|
|
108
|
+
limit = 20,
|
|
109
|
+
search,
|
|
110
|
+
sortBy = "createdAt",
|
|
111
|
+
sortOrder = "desc",
|
|
112
|
+
}: PaginationRequestDto): Promise<{ items: List${ModuleClassName}Response; total: number }> {
|
|
113
|
+
try {
|
|
114
|
+
// TODO: fetch list of ${ModuleClassName}
|
|
115
|
+
return {
|
|
116
|
+
total: 1,
|
|
117
|
+
items: [
|
|
118
|
+
{
|
|
119
|
+
${moduleName}Id: "1",
|
|
120
|
+
name: "Sample ${ModuleClassName} 1",
|
|
121
|
+
createdAt: new Date().toISOString(),
|
|
122
|
+
updatedAt: new Date().toISOString(),
|
|
123
|
+
},
|
|
124
|
+
]
|
|
125
|
+
}
|
|
126
|
+
} catch (error) {
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async delete(${moduleName}Id: string): Promise<void> {
|
|
132
|
+
try {
|
|
133
|
+
// TODO: delete ${ModuleClassName} by ${moduleName}Id
|
|
134
|
+
return;
|
|
135
|
+
} catch (error) {
|
|
136
|
+
throw error;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export default new ${ModuleClassName}Service()
|
|
142
|
+
`;
|
|
143
|
+
fs.writeFileSync(path.join(basePath, `${fileName}.service.ts`), serviceContent);
|
|
144
|
+
// Controller
|
|
145
|
+
const controllerContent = `
|
|
146
|
+
import { Request, Response } from 'express';
|
|
147
|
+
import { ${ModuleClassName}Service } from './${fileName}.service';
|
|
148
|
+
import {
|
|
149
|
+
Create${ModuleClassName}Dto,
|
|
150
|
+
Update${ModuleClassName}Dto,
|
|
151
|
+
Create${ModuleClassName}ResponseDto,
|
|
152
|
+
Update${ModuleClassName}ResponseDto,
|
|
153
|
+
Get${ModuleClassName}ResponseDto,
|
|
154
|
+
List${ModuleClassName}ResponseDto
|
|
155
|
+
} from './${fileName}.dto';
|
|
156
|
+
import { PaginationRequestDto } from '../common/common.dto';
|
|
157
|
+
|
|
158
|
+
export class ${ModuleClassName}Controller {
|
|
159
|
+
constructor(private ${moduleName}Service: ${ModuleClassName}Service) {}
|
|
160
|
+
|
|
161
|
+
async create(req: Request, res: Response) {
|
|
162
|
+
try {
|
|
163
|
+
const payload: Create${ModuleClassName}Dto = req.body;
|
|
164
|
+
const data = await this.${moduleName}Service.create(payload);
|
|
165
|
+
const response: Create${ModuleClassName}ResponseDto = {
|
|
166
|
+
success: true,
|
|
167
|
+
status: 201,
|
|
168
|
+
message: '${ModuleClassName} created successfully',
|
|
169
|
+
data,
|
|
170
|
+
};
|
|
171
|
+
res.status(201).json(response);
|
|
172
|
+
} catch (error: any) {
|
|
173
|
+
res.status(500).json({ success: false, status: 500, message: error.message });
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async update(req: Request, res: Response) {
|
|
178
|
+
try {
|
|
179
|
+
const ${moduleName}Id = req.params.${moduleName}Id as string;
|
|
180
|
+
const payload: Update${ModuleClassName}Dto = req.body;
|
|
181
|
+
const data = await this.${moduleName}Service.update(${moduleName}Id, payload);
|
|
182
|
+
const response: Update${ModuleClassName}ResponseDto = {
|
|
183
|
+
success: true,
|
|
184
|
+
status: 200,
|
|
185
|
+
message: '${ModuleClassName} updated successfully',
|
|
186
|
+
data,
|
|
187
|
+
};
|
|
188
|
+
res.json(response);
|
|
189
|
+
} catch (error: any) {
|
|
190
|
+
res.status(500).json({ success: false, status: 500, message: error.message });
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
async getById(req: Request, res: Response) {
|
|
195
|
+
try {
|
|
196
|
+
const ${moduleName}Id = req.params.${moduleName}Id as string;
|
|
197
|
+
const data = await this.${moduleName}Service.getById(${moduleName}Id);
|
|
198
|
+
const response: Get${ModuleClassName}ResponseDto = {
|
|
199
|
+
success: true,
|
|
200
|
+
status: 200,
|
|
201
|
+
message: '${ModuleClassName} fetched successfully',
|
|
202
|
+
data,
|
|
203
|
+
};
|
|
204
|
+
res.json(response);
|
|
205
|
+
} catch (error: any) {
|
|
206
|
+
res.status(404).json({ success: false, status: 404, message: error.message });
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async list(req: Request, res: Response) {
|
|
211
|
+
try {
|
|
212
|
+
const query = req.query as PaginationRequestDto;
|
|
213
|
+
|
|
214
|
+
const offset = query.offset ? Number(query.offset) : 0;
|
|
215
|
+
const limit = query.limit ? Number(query.limit) : 20;
|
|
216
|
+
const search = query.search;
|
|
217
|
+
const sortBy = query.sortBy || "createdAt";
|
|
218
|
+
const sortOrder = query.sortOrder || "desc";
|
|
219
|
+
const { items, total } =
|
|
220
|
+
await this.${moduleName}Service.list({
|
|
221
|
+
offset,
|
|
222
|
+
limit,
|
|
223
|
+
search,
|
|
224
|
+
sortBy,
|
|
225
|
+
sortOrder,
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
const response: List${ModuleClassName}ResponseDto = {
|
|
229
|
+
success: true,
|
|
230
|
+
status: 200,
|
|
231
|
+
message: "${ModuleClassName} list fetched successfully",
|
|
232
|
+
data: {
|
|
233
|
+
items,
|
|
234
|
+
total,
|
|
235
|
+
offset,
|
|
236
|
+
limit,
|
|
237
|
+
},
|
|
238
|
+
};
|
|
239
|
+
res.json(response);
|
|
240
|
+
} catch (error: any) {
|
|
241
|
+
res.status(500).json({ success: false, status: 500, message: error.message });
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
async delete(req: Request, res: Response) {
|
|
246
|
+
try {
|
|
247
|
+
const ${moduleName}Id = req.params.${moduleName}Id as string;
|
|
248
|
+
await this.${moduleName}Service.delete(${moduleName}Id);
|
|
249
|
+
res.json({
|
|
250
|
+
success: true,
|
|
251
|
+
status: 200,
|
|
252
|
+
message: '${ModuleClassName} deleted successfully',
|
|
253
|
+
});
|
|
254
|
+
} catch (error: any) {
|
|
255
|
+
res.status(500).json({ success: false, status: 500, message: error.message });
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
`;
|
|
260
|
+
fs.writeFileSync(path.join(basePath, `${fileName}.controller.ts`), controllerContent);
|
|
261
|
+
// Routes
|
|
262
|
+
const routesContent = `
|
|
263
|
+
import express, { Router } from 'express';
|
|
264
|
+
import { ${ModuleClassName}Controller } from './${fileName}.controller';
|
|
265
|
+
import { ${moduleName}Middleware } from './${fileName}.middlewares';
|
|
266
|
+
import { authMiddleware } from '../../middlewares/auth.middleware';
|
|
267
|
+
import ${moduleName}Service from './${fileName}.service';
|
|
268
|
+
import { ActionsType, Resource, ResourceType } from '../../utils/consts';
|
|
269
|
+
import { authorizationMiddleware } from '../../middlewares/authorization.middleware';
|
|
270
|
+
|
|
271
|
+
const router: Router = express.Router();
|
|
272
|
+
const controller = new ${ModuleClassName}Controller(${moduleName}Service);
|
|
273
|
+
router.use(authMiddleware);
|
|
274
|
+
const guard = (actions: ActionsType[]) => authorizationMiddleware({ resource: Resource.COMMON, actions, resourceType: ResourceType.API_ENDPOINT });
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* @openapi
|
|
278
|
+
* tags:
|
|
279
|
+
* - name: ${ModuleClassName}
|
|
280
|
+
* description: ${ModuleClassName} management and operations
|
|
281
|
+
*/
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* @openapi
|
|
285
|
+
* /api/${moduleName}:
|
|
286
|
+
* post:
|
|
287
|
+
* tags: [${ModuleClassName}]
|
|
288
|
+
* summary: Create a new ${ModuleClassName}
|
|
289
|
+
* requestBody:
|
|
290
|
+
* required: true
|
|
291
|
+
* content:
|
|
292
|
+
* application/json:
|
|
293
|
+
* schema:
|
|
294
|
+
* $ref: '#/components/schemas/Create${ModuleClassName}Dto'
|
|
295
|
+
* responses:
|
|
296
|
+
* 201:
|
|
297
|
+
* description: ${ModuleClassName} created successfully
|
|
298
|
+
* content:
|
|
299
|
+
* application/json:
|
|
300
|
+
* schema:
|
|
301
|
+
* $ref: '#/components/schemas/Create${ModuleClassName}ResponseDto'
|
|
302
|
+
*/
|
|
303
|
+
router.post('/', ${moduleName}Middleware, guard([ActionsType.CREATE]), controller.create.bind(controller));
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* @openapi
|
|
307
|
+
* /api/${moduleName}/{${moduleName}Id}:
|
|
308
|
+
* get:
|
|
309
|
+
* tags: [${ModuleClassName}]
|
|
310
|
+
* summary: Get a ${ModuleClassName} by ID
|
|
311
|
+
* parameters:
|
|
312
|
+
* - name: ${moduleName}Id
|
|
313
|
+
* in: path
|
|
314
|
+
* required: true
|
|
315
|
+
* schema:
|
|
316
|
+
* type: string
|
|
317
|
+
* responses:
|
|
318
|
+
* 200:
|
|
319
|
+
* description: ${ModuleClassName} fetched successfully
|
|
320
|
+
* content:
|
|
321
|
+
* application/json:
|
|
322
|
+
* schema:
|
|
323
|
+
* $ref: '#/components/schemas/Get${ModuleClassName}ResponseDto'
|
|
324
|
+
*/
|
|
325
|
+
router.get('/:${moduleName}Id', ${moduleName}Middleware, guard([ActionsType.READ]), controller.getById.bind(controller));
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* @openapi
|
|
329
|
+
* /api/${moduleName}/{${moduleName}Id}:
|
|
330
|
+
* put:
|
|
331
|
+
* tags: [${ModuleClassName}]
|
|
332
|
+
* summary: Update a ${ModuleClassName} by ID
|
|
333
|
+
* parameters:
|
|
334
|
+
* - name: ${moduleName}Id
|
|
335
|
+
* in: path
|
|
336
|
+
* required: true
|
|
337
|
+
* schema:
|
|
338
|
+
* type: string
|
|
339
|
+
* requestBody:
|
|
340
|
+
* required: true
|
|
341
|
+
* content:
|
|
342
|
+
* application/json:
|
|
343
|
+
* schema:
|
|
344
|
+
* $ref: '#/components/schemas/Update${ModuleClassName}Dto'
|
|
345
|
+
* responses:
|
|
346
|
+
* 200:
|
|
347
|
+
* description: ${ModuleClassName} updated successfully
|
|
348
|
+
* content:
|
|
349
|
+
* application/json:
|
|
350
|
+
* schema:
|
|
351
|
+
* $ref: '#/components/schemas/Update${ModuleClassName}ResponseDto'
|
|
352
|
+
*/
|
|
353
|
+
router.put('/:${moduleName}Id', ${moduleName}Middleware, guard([ActionsType.UPDATE]), controller.update.bind(controller));
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* @openapi
|
|
357
|
+
* /api/${moduleName}/{${moduleName}Id}:
|
|
358
|
+
* delete:
|
|
359
|
+
* tags: [${ModuleClassName}]
|
|
360
|
+
* summary: Delete a ${ModuleClassName} by ID
|
|
361
|
+
* parameters:
|
|
362
|
+
* - name: ${moduleName}Id
|
|
363
|
+
* in: path
|
|
364
|
+
* required: true
|
|
365
|
+
* schema:
|
|
366
|
+
* type: string
|
|
367
|
+
* responses:
|
|
368
|
+
* 200:
|
|
369
|
+
* description: ${ModuleClassName} deleted successfully
|
|
370
|
+
* content:
|
|
371
|
+
* application/json:
|
|
372
|
+
* schema:
|
|
373
|
+
* type: object
|
|
374
|
+
* properties:
|
|
375
|
+
* success:
|
|
376
|
+
* type: boolean
|
|
377
|
+
* status:
|
|
378
|
+
* type: integer
|
|
379
|
+
* message:
|
|
380
|
+
* type: string
|
|
381
|
+
*/
|
|
382
|
+
router.delete('/:${moduleName}Id', ${moduleName}Middleware, guard([ActionsType.DELETE]), controller.delete.bind(controller));
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* @openapi
|
|
386
|
+
* /api/${moduleName}:
|
|
387
|
+
* get:
|
|
388
|
+
* tags: [${ModuleClassName}]
|
|
389
|
+
* summary: List all ${ModuleClassName}s with pagination
|
|
390
|
+
* parameters:
|
|
391
|
+
* - in: query
|
|
392
|
+
* name: offset
|
|
393
|
+
* schema:
|
|
394
|
+
* type: integer
|
|
395
|
+
* description: Offset for pagination
|
|
396
|
+
* - in: query
|
|
397
|
+
* name: limit
|
|
398
|
+
* schema:
|
|
399
|
+
* type: integer
|
|
400
|
+
* description: Limit for pagination
|
|
401
|
+
* - in: query
|
|
402
|
+
* name: search
|
|
403
|
+
* schema:
|
|
404
|
+
* type: string
|
|
405
|
+
* description: Search keyword
|
|
406
|
+
* - in: query
|
|
407
|
+
* name: sortBy
|
|
408
|
+
* schema:
|
|
409
|
+
* type: string
|
|
410
|
+
* description: Field to sort by
|
|
411
|
+
* - in: query
|
|
412
|
+
* name: sortOrder
|
|
413
|
+
* schema:
|
|
414
|
+
* type: string
|
|
415
|
+
* enum: [asc, desc]
|
|
416
|
+
* description: Sort order
|
|
417
|
+
* responses:
|
|
418
|
+
* 200:
|
|
419
|
+
* description: List of ${ModuleClassName}s
|
|
420
|
+
* content:
|
|
421
|
+
* application/json:
|
|
422
|
+
* schema:
|
|
423
|
+
* $ref: '#/components/schemas/List${ModuleClassName}ResponseDto'
|
|
424
|
+
*/
|
|
425
|
+
router.get('/', ${moduleName}Middleware, guard([ActionsType.READ_ALL]), controller.list.bind(controller));
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* ======================================================
|
|
429
|
+
* Optional: Health Check
|
|
430
|
+
* ======================================================
|
|
431
|
+
*/
|
|
432
|
+
router.get('/health/check', (_req, res) => {
|
|
433
|
+
res.json({
|
|
434
|
+
success: true,
|
|
435
|
+
status: 200,
|
|
436
|
+
message: "${ModuleClassName} module is healthy",
|
|
437
|
+
});
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
export default router;
|
|
441
|
+
`;
|
|
442
|
+
fs.writeFileSync(path.join(basePath, `${fileName}.routes.ts`), routesContent);
|
|
443
|
+
const dtoContent = `import { ApiResponse, PaginatedData } from "../common/common.dto";
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Main ${ModuleClassName} entity
|
|
447
|
+
*
|
|
448
|
+
* @openapi
|
|
449
|
+
* components:
|
|
450
|
+
* schemas:
|
|
451
|
+
* ${ModuleClassName}:
|
|
452
|
+
* type: object
|
|
453
|
+
* required:
|
|
454
|
+
* - name
|
|
455
|
+
* properties:
|
|
456
|
+
* ${moduleName}Id:
|
|
457
|
+
* type: string
|
|
458
|
+
* example: "c3f2a9b4-8d21-4f3b-a91c-1a2b3c4d5e6f"
|
|
459
|
+
* description: Unique identifier of the ${ModuleClassName}
|
|
460
|
+
* name:
|
|
461
|
+
* type: string
|
|
462
|
+
* example: Sample ${ModuleClassName}
|
|
463
|
+
* description: Name of the ${ModuleClassName}
|
|
464
|
+
* description:
|
|
465
|
+
* type: string
|
|
466
|
+
* example: Optional description
|
|
467
|
+
* nullable: true
|
|
468
|
+
* description: Description of the ${ModuleClassName}
|
|
469
|
+
* createdAt:
|
|
470
|
+
* type: string
|
|
471
|
+
* format: date-time
|
|
472
|
+
* example: "2025-01-01T10:00:00.000Z"
|
|
473
|
+
* description: Creation timestamp
|
|
474
|
+
* updatedAt:
|
|
475
|
+
* type: string
|
|
476
|
+
* format: date-time
|
|
477
|
+
* example: "2025-01-02T12:00:00.000Z"
|
|
478
|
+
* description: Last update timestamp
|
|
479
|
+
*/
|
|
480
|
+
export interface ${ModuleClassName} {
|
|
481
|
+
${moduleName}Id: string;
|
|
482
|
+
name: string;
|
|
483
|
+
description?: string;
|
|
484
|
+
createdAt: string;
|
|
485
|
+
updatedAt: string;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* @openapi
|
|
490
|
+
* components:
|
|
491
|
+
* schemas:
|
|
492
|
+
* Create${ModuleClassName}Dto:
|
|
493
|
+
* type: object
|
|
494
|
+
* required:
|
|
495
|
+
* - name
|
|
496
|
+
* properties:
|
|
497
|
+
* name:
|
|
498
|
+
* type: string
|
|
499
|
+
* example: Sample ${ModuleClassName}
|
|
500
|
+
* description: Name of the ${ModuleClassName}
|
|
501
|
+
* description:
|
|
502
|
+
* type: string
|
|
503
|
+
* example: Sample description
|
|
504
|
+
*/
|
|
505
|
+
export interface Create${ModuleClassName}Dto {
|
|
506
|
+
name: string;
|
|
507
|
+
description?: string;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* @openapi
|
|
512
|
+
* components:
|
|
513
|
+
* schemas:
|
|
514
|
+
* Update${ModuleClassName}Dto:
|
|
515
|
+
* type: object
|
|
516
|
+
* properties:
|
|
517
|
+
* name:
|
|
518
|
+
* type: string
|
|
519
|
+
* example: Updated ${ModuleClassName} Name
|
|
520
|
+
* description:
|
|
521
|
+
* type: string
|
|
522
|
+
* example: Updated description
|
|
523
|
+
*/
|
|
524
|
+
export interface Update${ModuleClassName}Dto {
|
|
525
|
+
name?: string;
|
|
526
|
+
description?: string;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* ======================================================
|
|
531
|
+
* Service return types (data only, controller wraps response)
|
|
532
|
+
* ======================================================
|
|
533
|
+
*/
|
|
534
|
+
|
|
535
|
+
// data returned from service
|
|
536
|
+
export type Create${ModuleClassName}Response = Partial<${ModuleClassName}>;
|
|
537
|
+
export type Update${ModuleClassName}Response = Partial<${ModuleClassName}>;
|
|
538
|
+
export type Get${ModuleClassName}Response = Partial<${ModuleClassName}>;
|
|
539
|
+
export type List${ModuleClassName}Response = Partial<${ModuleClassName}>[];
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* ======================================================
|
|
543
|
+
* API Response DTOs (Swagger documented)
|
|
544
|
+
* ======================================================
|
|
545
|
+
*/
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* @openapi
|
|
549
|
+
* components:
|
|
550
|
+
* schemas:
|
|
551
|
+
* Create${ModuleClassName}ResponseDto:
|
|
552
|
+
* allOf:
|
|
553
|
+
* - $ref: '#/components/schemas/ApiResponse'
|
|
554
|
+
* - type: object
|
|
555
|
+
* properties:
|
|
556
|
+
* data:
|
|
557
|
+
* $ref: '#/components/schemas/${ModuleClassName}'
|
|
558
|
+
*/
|
|
559
|
+
export interface Create${ModuleClassName}ResponseDto
|
|
560
|
+
extends ApiResponse<Partial<${ModuleClassName}>> {}
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* @openapi
|
|
564
|
+
* components:
|
|
565
|
+
* schemas:
|
|
566
|
+
* Update${ModuleClassName}ResponseDto:
|
|
567
|
+
* allOf:
|
|
568
|
+
* - $ref: '#/components/schemas/ApiResponse'
|
|
569
|
+
* - type: object
|
|
570
|
+
* properties:
|
|
571
|
+
* data:
|
|
572
|
+
* $ref: '#/components/schemas/${ModuleClassName}'
|
|
573
|
+
*/
|
|
574
|
+
export interface Update${ModuleClassName}ResponseDto
|
|
575
|
+
extends ApiResponse<Partial<${ModuleClassName}>> {}
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* @openapi
|
|
579
|
+
* components:
|
|
580
|
+
* schemas:
|
|
581
|
+
* Get${ModuleClassName}ResponseDto:
|
|
582
|
+
* allOf:
|
|
583
|
+
* - $ref: '#/components/schemas/ApiResponse'
|
|
584
|
+
* - type: object
|
|
585
|
+
* properties:
|
|
586
|
+
* data:
|
|
587
|
+
* $ref: '#/components/schemas/${ModuleClassName}'
|
|
588
|
+
*/
|
|
589
|
+
export interface Get${ModuleClassName}ResponseDto
|
|
590
|
+
extends ApiResponse<Partial<${ModuleClassName}>> {}
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* @openapi
|
|
594
|
+
* components:
|
|
595
|
+
* schemas:
|
|
596
|
+
* List${ModuleClassName}ResponseDto:
|
|
597
|
+
* allOf:
|
|
598
|
+
* - $ref: '#/components/schemas/ApiResponse'
|
|
599
|
+
* - type: object
|
|
600
|
+
* properties:
|
|
601
|
+
* data:
|
|
602
|
+
* type: object
|
|
603
|
+
* properties:
|
|
604
|
+
* items:
|
|
605
|
+
* type: array
|
|
606
|
+
* items:
|
|
607
|
+
* $ref: '#/components/schemas/${ModuleClassName}'
|
|
608
|
+
* total:
|
|
609
|
+
* type: integer
|
|
610
|
+
* offset:
|
|
611
|
+
* type: integer
|
|
612
|
+
* limit:
|
|
613
|
+
* type: integer
|
|
614
|
+
*/
|
|
615
|
+
export interface List${ModuleClassName}ResponseDto
|
|
616
|
+
extends ApiResponse<PaginatedData<Partial<${ModuleClassName}>>> {}
|
|
617
|
+
`;
|
|
618
|
+
fs.writeFileSync(path.join(basePath, `${fileName}.dto.ts`), dtoContent);
|
|
619
|
+
// Middleware
|
|
620
|
+
const middlewareContent = `
|
|
621
|
+
import { Request, Response, NextFunction } from "express";
|
|
622
|
+
import { cacheService } from "../../utils/redis";
|
|
623
|
+
import { CachedUserProfilePermissionDto } from "../../utils/redis.dto";
|
|
624
|
+
|
|
625
|
+
export async function ${moduleName}Middleware(req: Request, res: Response, next: NextFunction) {
|
|
626
|
+
try {
|
|
627
|
+
const { userId } = req.user!
|
|
628
|
+
const profile = await cacheService.get<CachedUserProfilePermissionDto>(\`user:\${userId}:permissions\`);
|
|
629
|
+
if(!profile) throw new Error("No permission found")
|
|
630
|
+
const userPermissions = profile;
|
|
631
|
+
|
|
632
|
+
const method = req.method;
|
|
633
|
+
const url = req.originalUrl;
|
|
634
|
+
const path = req.path;
|
|
635
|
+
const ${moduleName}Id = req?.params?.${moduleName}Id
|
|
636
|
+
|
|
637
|
+
console.log("---- REALM MIDDLEWARE ----");
|
|
638
|
+
console.log("Method:", method);
|
|
639
|
+
console.log("URL:", url);
|
|
640
|
+
console.log("Path:", path);
|
|
641
|
+
|
|
642
|
+
// Example condition checking
|
|
643
|
+
if (method === "POST" && path === "/") {
|
|
644
|
+
console.log("👉 Creating new realm");
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
if (method === "GET" && path === "/") {
|
|
648
|
+
console.log("👉 Listing realms");
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
if (method === "GET" && ${moduleName}Id) {
|
|
652
|
+
console.log("👉 Getting realm by ID:", ${moduleName}Id);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
if (method === "PUT") {
|
|
656
|
+
console.log("👉 Updating realm");
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
if (method === "DELETE") {
|
|
660
|
+
console.log("👉 Deleting realm");
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
next();
|
|
664
|
+
} catch (error) {
|
|
665
|
+
return res.status(500).json({
|
|
666
|
+
message: "Authorization error",
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
`;
|
|
671
|
+
fs.writeFileSync(path.join(basePath, `${fileName}.middlewares.ts`), middlewareContent);
|
|
672
|
+
const routerIndexPath = path.join(projectRoot, "src", "routes.ts");
|
|
673
|
+
if (fs.existsSync(routerIndexPath)) {
|
|
674
|
+
fs.appendFileSync(routerIndexPath, `\nimport ${moduleName}Routes from "./modules/${fileName}/${fileName}.routes";`);
|
|
675
|
+
fs.appendFileSync(routerIndexPath, `\nrouter.use("/${moduleName}", ${moduleName}Routes);\n`);
|
|
676
|
+
}
|
|
677
|
+
console.log(`Module '${moduleName}' generated successfully at ${basePath}`);
|