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