lapeeh 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.
Files changed (76) hide show
  1. package/.env.example +14 -0
  2. package/LICENSE +21 -0
  3. package/bin/index.js +934 -0
  4. package/doc/en/ARCHITECTURE_GUIDE.md +79 -0
  5. package/doc/en/CHANGELOG.md +203 -0
  6. package/doc/en/CHEATSHEET.md +90 -0
  7. package/doc/en/CLI.md +111 -0
  8. package/doc/en/CONTRIBUTING.md +119 -0
  9. package/doc/en/DEPLOYMENT.md +171 -0
  10. package/doc/en/FAQ.md +69 -0
  11. package/doc/en/FEATURES.md +99 -0
  12. package/doc/en/GETTING_STARTED.md +84 -0
  13. package/doc/en/INTRODUCTION.md +62 -0
  14. package/doc/en/PACKAGES.md +63 -0
  15. package/doc/en/PERFORMANCE.md +98 -0
  16. package/doc/en/ROADMAP.md +104 -0
  17. package/doc/en/SECURITY.md +95 -0
  18. package/doc/en/STRUCTURE.md +79 -0
  19. package/doc/en/TUTORIAL.md +145 -0
  20. package/doc/id/ARCHITECTURE_GUIDE.md +76 -0
  21. package/doc/id/CHANGELOG.md +203 -0
  22. package/doc/id/CHEATSHEET.md +90 -0
  23. package/doc/id/CLI.md +139 -0
  24. package/doc/id/CONTRIBUTING.md +119 -0
  25. package/doc/id/DEPLOYMENT.md +171 -0
  26. package/doc/id/FAQ.md +69 -0
  27. package/doc/id/FEATURES.md +169 -0
  28. package/doc/id/GETTING_STARTED.md +91 -0
  29. package/doc/id/INTRODUCTION.md +62 -0
  30. package/doc/id/PACKAGES.md +63 -0
  31. package/doc/id/PERFORMANCE.md +100 -0
  32. package/doc/id/ROADMAP.md +107 -0
  33. package/doc/id/SECURITY.md +94 -0
  34. package/doc/id/STRUCTURE.md +79 -0
  35. package/doc/id/TUTORIAL.md +145 -0
  36. package/docker-compose.yml +24 -0
  37. package/ecosystem.config.js +17 -0
  38. package/eslint.config.mjs +26 -0
  39. package/gitignore.template +30 -0
  40. package/lib/bootstrap.ts +210 -0
  41. package/lib/core/realtime.ts +34 -0
  42. package/lib/core/redis.ts +139 -0
  43. package/lib/core/serializer.ts +63 -0
  44. package/lib/core/server.ts +70 -0
  45. package/lib/core/store.ts +116 -0
  46. package/lib/middleware/auth.ts +63 -0
  47. package/lib/middleware/error.ts +50 -0
  48. package/lib/middleware/multipart.ts +13 -0
  49. package/lib/middleware/rateLimit.ts +14 -0
  50. package/lib/middleware/requestLogger.ts +27 -0
  51. package/lib/middleware/visitor.ts +178 -0
  52. package/lib/utils/logger.ts +100 -0
  53. package/lib/utils/pagination.ts +56 -0
  54. package/lib/utils/response.ts +88 -0
  55. package/lib/utils/validator.ts +394 -0
  56. package/nodemon.json +6 -0
  57. package/package.json +126 -0
  58. package/readme.md +357 -0
  59. package/scripts/check-update.js +92 -0
  60. package/scripts/config-clear.js +45 -0
  61. package/scripts/generate-jwt-secret.js +38 -0
  62. package/scripts/init-project.js +84 -0
  63. package/scripts/make-module.js +89 -0
  64. package/scripts/release.js +494 -0
  65. package/scripts/seed-json.js +158 -0
  66. package/scripts/verify-rbac-functional.js +187 -0
  67. package/src/config/app.ts +9 -0
  68. package/src/config/cors.ts +5 -0
  69. package/src/modules/Auth/auth.controller.ts +519 -0
  70. package/src/modules/Rbac/rbac.controller.ts +533 -0
  71. package/src/routes/auth.ts +74 -0
  72. package/src/routes/index.ts +7 -0
  73. package/src/routes/rbac.ts +42 -0
  74. package/storage/logs/.gitkeep +0 -0
  75. package/tsconfig.build.json +12 -0
  76. package/tsconfig.json +30 -0
@@ -0,0 +1,533 @@
1
+ import { Request, Response } from "express";
2
+ import {
3
+ users,
4
+ roles,
5
+ permissions,
6
+ role_permissions,
7
+ user_roles,
8
+ user_permissions,
9
+ generateId,
10
+ Role,
11
+ Permission,
12
+ saveStore,
13
+ } from "@lapeeh/core/store";
14
+ import { sendError, sendFastSuccess } from "@lapeeh/utils/response";
15
+ import { Validator } from "@lapeeh/utils/validator";
16
+ import { z } from "zod";
17
+ import { getSerializer, createResponseSchema } from "@lapeeh/core/serializer";
18
+
19
+ // --- Serializers ---
20
+
21
+ const roleSchema = {
22
+ type: "object",
23
+ properties: {
24
+ id: { type: "string" },
25
+ name: { type: "string" },
26
+ slug: { type: "string" },
27
+ description: { type: "string", nullable: true },
28
+ created_at: { type: "string", format: "date-time", nullable: true },
29
+ updated_at: { type: "string", format: "date-time", nullable: true },
30
+ },
31
+ };
32
+
33
+ const permissionSchema = {
34
+ type: "object",
35
+ properties: {
36
+ id: { type: "string" },
37
+ name: { type: "string" },
38
+ slug: { type: "string" },
39
+ description: { type: "string", nullable: true },
40
+ created_at: { type: "string", format: "date-time", nullable: true },
41
+ updated_at: { type: "string", format: "date-time", nullable: true },
42
+ },
43
+ };
44
+
45
+ const roleSerializer = getSerializer("role", createResponseSchema(roleSchema));
46
+ const roleListSerializer = getSerializer(
47
+ "role-list",
48
+ createResponseSchema({ type: "array", items: roleSchema })
49
+ );
50
+
51
+ const permissionSerializer = getSerializer(
52
+ "permission",
53
+ createResponseSchema(permissionSchema)
54
+ );
55
+ const permissionListSerializer = getSerializer(
56
+ "permission-list",
57
+ createResponseSchema({ type: "array", items: permissionSchema })
58
+ );
59
+
60
+ const voidSerializer = getSerializer(
61
+ "void",
62
+ createResponseSchema({ type: "null" })
63
+ );
64
+
65
+ // --- Controllers ---
66
+
67
+ export async function createRole(req: Request, res: Response) {
68
+ const validator = Validator.make(req.body || {}, {
69
+ name: "required|string|min:1",
70
+ slug: "required|string|min:1|unique:roles,slug",
71
+ description: "string",
72
+ });
73
+
74
+ if (await validator.fails()) {
75
+ sendError(res, 422, "Validation error", validator.errors());
76
+ return;
77
+ }
78
+
79
+ const { name, slug, description } = await validator.validated();
80
+
81
+ // Manual unique check
82
+ if (roles.find((r) => r.slug === slug)) {
83
+ sendError(res, 422, "Validation error", { slug: "Slug already exists" });
84
+ return;
85
+ }
86
+
87
+ const role: Role = {
88
+ id: generateId(),
89
+ name,
90
+ slug,
91
+ description: description || null,
92
+ created_at: new Date(),
93
+ updated_at: new Date(),
94
+ };
95
+ roles.push(role);
96
+ saveStore();
97
+
98
+ sendFastSuccess(res, 201, roleSerializer, {
99
+ status: "success",
100
+ message: "Role created",
101
+ data: { ...role, id: role.id.toString() },
102
+ });
103
+ }
104
+
105
+ export async function listRoles(_req: Request, res: Response) {
106
+ const serialized = roles.map((r: any) => ({ ...r, id: r.id.toString() }));
107
+ sendFastSuccess(res, 200, roleListSerializer, {
108
+ status: "success",
109
+ message: "Roles list",
110
+ data: serialized,
111
+ });
112
+ }
113
+
114
+ export async function updateRole(req: Request, res: Response) {
115
+ const { id } = req.params;
116
+ const roleId = id;
117
+
118
+ const validator = Validator.make(req.body || {}, {
119
+ name: "string",
120
+ slug: `string|unique:roles,slug,${id}`,
121
+ description: "string",
122
+ });
123
+
124
+ if (await validator.fails()) {
125
+ sendError(res, 422, "Validation error", validator.errors());
126
+ return;
127
+ }
128
+ const { name, slug, description } = await validator.validated();
129
+
130
+ const roleIndex = roles.findIndex((r) => r.id === roleId);
131
+ if (roleIndex === -1) {
132
+ sendError(res, 404, "Role not found");
133
+ return;
134
+ }
135
+
136
+ // Manual unique check for update
137
+ if (
138
+ slug &&
139
+ slug !== roles[roleIndex].slug &&
140
+ roles.find((r) => r.slug === slug)
141
+ ) {
142
+ sendError(res, 422, "Validation error", { slug: "Slug already exists" });
143
+ return;
144
+ }
145
+
146
+ roles[roleIndex] = {
147
+ ...roles[roleIndex],
148
+ name: name ?? roles[roleIndex].name,
149
+ slug: slug ?? roles[roleIndex].slug,
150
+ description: description ?? roles[roleIndex].description,
151
+ updated_at: new Date(),
152
+ };
153
+ saveStore();
154
+
155
+ const updated = roles[roleIndex];
156
+ sendFastSuccess(res, 200, roleSerializer, {
157
+ status: "success",
158
+ message: "Role updated",
159
+ data: { ...updated, id: updated.id.toString() },
160
+ });
161
+ }
162
+
163
+ export async function deleteRole(req: Request, res: Response) {
164
+ const { id } = req.params;
165
+ const roleId = id;
166
+ const roleIndex = roles.findIndex((r) => r.id === roleId);
167
+ if (roleIndex === -1) {
168
+ sendError(res, 404, "Role not found");
169
+ return;
170
+ }
171
+
172
+ // Clean up relationships
173
+ // Remove from role_permissions
174
+ for (let i = role_permissions.length - 1; i >= 0; i--) {
175
+ if (role_permissions[i].role_id === roleId) {
176
+ role_permissions.splice(i, 1);
177
+ }
178
+ }
179
+
180
+ // Remove from user_roles
181
+ for (let i = user_roles.length - 1; i >= 0; i--) {
182
+ if (user_roles[i].role_id === roleId) {
183
+ user_roles.splice(i, 1);
184
+ }
185
+ }
186
+
187
+ roles.splice(roleIndex, 1);
188
+
189
+ sendFastSuccess(res, 200, voidSerializer, {
190
+ status: "success",
191
+ message: "Role deleted",
192
+ data: null,
193
+ });
194
+ }
195
+
196
+ export async function createPermission(req: Request, res: Response) {
197
+ const validator = Validator.make(req.body || {}, {
198
+ name: "required|string|min:1",
199
+ slug: "required|string|min:1|unique:permissions,slug",
200
+ description: "string",
201
+ });
202
+
203
+ if (await validator.fails()) {
204
+ sendError(res, 422, "Validation error", validator.errors());
205
+ return;
206
+ }
207
+ const { name, slug, description } = await validator.validated();
208
+
209
+ // Manual unique check
210
+ if (permissions.find((p) => p.slug === slug)) {
211
+ sendError(res, 422, "Validation error", { slug: "Slug already exists" });
212
+ return;
213
+ }
214
+
215
+ const permission: Permission = {
216
+ id: generateId(),
217
+ name,
218
+ slug,
219
+ description: description || null,
220
+ created_at: new Date(),
221
+ updated_at: new Date(),
222
+ };
223
+ permissions.push(permission);
224
+ saveStore();
225
+
226
+ sendFastSuccess(res, 201, permissionSerializer, {
227
+ status: "success",
228
+ message: "Permission created",
229
+ data: { ...permission, id: permission.id.toString() },
230
+ });
231
+ }
232
+
233
+ export async function listPermissions(_req: Request, res: Response) {
234
+ const serialized = permissions.map((p: any) => ({
235
+ ...p,
236
+ id: p.id.toString(),
237
+ }));
238
+ sendFastSuccess(res, 200, permissionListSerializer, {
239
+ status: "success",
240
+ message: "Permissions list",
241
+ data: serialized,
242
+ });
243
+ }
244
+
245
+ export async function updatePermission(req: Request, res: Response) {
246
+ const { id } = req.params;
247
+ const permissionId = id;
248
+
249
+ const validator = Validator.make(req.body || {}, {
250
+ name: "string",
251
+ slug: `string|unique:permissions,slug,${id}`,
252
+ description: "string",
253
+ });
254
+
255
+ if (await validator.fails()) {
256
+ sendError(res, 422, "Validation error", validator.errors());
257
+ return;
258
+ }
259
+ const { name, slug, description } = await validator.validated();
260
+
261
+ const permIndex = permissions.findIndex((p) => p.id === permissionId);
262
+ if (permIndex === -1) {
263
+ sendError(res, 404, "Permission not found");
264
+ return;
265
+ }
266
+
267
+ if (
268
+ slug &&
269
+ slug !== permissions[permIndex].slug &&
270
+ permissions.find((p) => p.slug === slug)
271
+ ) {
272
+ sendError(res, 422, "Validation error", { slug: "Slug already exists" });
273
+ return;
274
+ }
275
+
276
+ permissions[permIndex] = {
277
+ ...permissions[permIndex],
278
+ name: name ?? permissions[permIndex].name,
279
+ slug: slug ?? permissions[permIndex].slug,
280
+ description: description ?? permissions[permIndex].description,
281
+ updated_at: new Date(),
282
+ };
283
+ saveStore();
284
+
285
+ const updated = permissions[permIndex];
286
+
287
+ sendFastSuccess(res, 200, permissionSerializer, {
288
+ status: "success",
289
+ message: "Permission updated",
290
+ data: { ...updated, id: updated.id.toString() },
291
+ });
292
+ }
293
+
294
+ export async function deletePermission(req: Request, res: Response) {
295
+ const { id } = req.params;
296
+ const permissionId = id;
297
+
298
+ const permIndex = permissions.findIndex((p) => p.id === permissionId);
299
+ if (permIndex === -1) {
300
+ sendError(res, 404, "Permission not found");
301
+ return;
302
+ }
303
+
304
+ // Clean up relationships
305
+ for (let i = role_permissions.length - 1; i >= 0; i--) {
306
+ if (role_permissions[i].permission_id === permissionId) {
307
+ role_permissions.splice(i, 1);
308
+ }
309
+ }
310
+
311
+ for (let i = user_permissions.length - 1; i >= 0; i--) {
312
+ if (user_permissions[i].permission_id === permissionId) {
313
+ user_permissions.splice(i, 1);
314
+ }
315
+ }
316
+
317
+ permissions.splice(permIndex, 1);
318
+ saveStore();
319
+
320
+ sendFastSuccess(res, 200, voidSerializer, {
321
+ status: "success",
322
+ message: "Permission deleted",
323
+ data: null,
324
+ });
325
+ }
326
+
327
+ export async function assignRoleToUser(req: Request, res: Response) {
328
+ const validator = Validator.make(req.body || {}, {
329
+ userId: z.string().min(1, "userId wajib diisi"),
330
+ roleId: z.string().min(1, "roleId wajib diisi"),
331
+ });
332
+
333
+ if (await validator.fails()) {
334
+ sendError(res, 422, "Validation error", validator.errors());
335
+ return;
336
+ }
337
+ const { userId, roleId } = await validator.validated();
338
+
339
+ const user = users.find((u) => u.id === userId);
340
+ if (!user) {
341
+ sendError(res, 404, "User not found");
342
+ return;
343
+ }
344
+ const role = roles.find((r) => r.id === roleId);
345
+ if (!role) {
346
+ sendError(res, 404, "Role not found");
347
+ return;
348
+ }
349
+
350
+ const exists = user_roles.find(
351
+ (ur) => ur.user_id === userId && ur.role_id === roleId
352
+ );
353
+ if (!exists) {
354
+ user_roles.push({
355
+ id: generateId(),
356
+ user_id: userId,
357
+ role_id: roleId,
358
+ created_at: new Date(),
359
+ });
360
+ saveStore();
361
+ }
362
+
363
+ sendFastSuccess(res, 200, voidSerializer, {
364
+ status: "success",
365
+ message: "Role assigned to user",
366
+ data: null,
367
+ });
368
+ }
369
+
370
+ export async function removeRoleFromUser(req: Request, res: Response) {
371
+ const validator = Validator.make(req.body || {}, {
372
+ userId: z.string().min(1, "userId wajib diisi"),
373
+ roleId: z.string().min(1, "roleId wajib diisi"),
374
+ });
375
+
376
+ if (await validator.fails()) {
377
+ sendError(res, 422, "Validation error", validator.errors());
378
+ return;
379
+ }
380
+ const { userId, roleId } = await validator.validated();
381
+
382
+ for (let i = user_roles.length - 1; i >= 0; i--) {
383
+ if (user_roles[i].user_id === userId && user_roles[i].role_id === roleId) {
384
+ user_roles.splice(i, 1);
385
+ }
386
+ }
387
+
388
+ sendFastSuccess(res, 200, voidSerializer, {
389
+ status: "success",
390
+ message: "Role removed from user",
391
+ data: null,
392
+ });
393
+ }
394
+
395
+ export async function assignPermissionToRole(req: Request, res: Response) {
396
+ const validator = Validator.make(req.body || {}, {
397
+ roleId: z.string().min(1, "roleId wajib diisi"),
398
+ permissionId: z.string().min(1, "permissionId wajib diisi"),
399
+ });
400
+
401
+ if (await validator.fails()) {
402
+ sendError(res, 422, "Validation error", validator.errors());
403
+ return;
404
+ }
405
+ const { roleId, permissionId } = await validator.validated();
406
+
407
+ const role = roles.find((r) => r.id === roleId);
408
+ if (!role) {
409
+ sendError(res, 404, "Role not found");
410
+ return;
411
+ }
412
+ const permission = permissions.find((p) => p.id === permissionId);
413
+ if (!permission) {
414
+ sendError(res, 404, "Permission not found");
415
+ return;
416
+ }
417
+
418
+ const exists = role_permissions.find(
419
+ (rp) => rp.role_id === roleId && rp.permission_id === permissionId
420
+ );
421
+ if (!exists) {
422
+ role_permissions.push({
423
+ id: generateId(),
424
+ role_id: roleId,
425
+ permission_id: permissionId,
426
+ created_at: new Date(),
427
+ });
428
+ saveStore();
429
+ }
430
+
431
+ sendFastSuccess(res, 200, voidSerializer, {
432
+ status: "success",
433
+ message: "Permission assigned to role",
434
+ data: null,
435
+ });
436
+ }
437
+
438
+ export async function removePermissionFromRole(req: Request, res: Response) {
439
+ const validator = Validator.make(req.body || {}, {
440
+ roleId: z.string().min(1, "roleId wajib diisi"),
441
+ permissionId: z.string().min(1, "permissionId wajib diisi"),
442
+ });
443
+
444
+ if (await validator.fails()) {
445
+ sendError(res, 422, "Validation error", validator.errors());
446
+ return;
447
+ }
448
+ const { roleId, permissionId } = await validator.validated();
449
+
450
+ for (let i = role_permissions.length - 1; i >= 0; i--) {
451
+ if (
452
+ role_permissions[i].role_id === roleId &&
453
+ role_permissions[i].permission_id === permissionId
454
+ ) {
455
+ role_permissions.splice(i, 1);
456
+ }
457
+ }
458
+
459
+ sendFastSuccess(res, 200, voidSerializer, {
460
+ status: "success",
461
+ message: "Permission removed from role",
462
+ data: null,
463
+ });
464
+ }
465
+
466
+ export async function assignPermissionToUser(req: Request, res: Response) {
467
+ const validator = Validator.make(req.body || {}, {
468
+ userId: z.string().min(1, "userId wajib diisi"),
469
+ permissionId: z.string().min(1, "permissionId wajib diisi"),
470
+ });
471
+
472
+ if (await validator.fails()) {
473
+ sendError(res, 422, "Validation error", validator.errors());
474
+ return;
475
+ }
476
+ const { userId, permissionId } = await validator.validated();
477
+
478
+ const user = users.find((u) => u.id === userId);
479
+ if (!user) {
480
+ sendError(res, 404, "User not found");
481
+ return;
482
+ }
483
+ const permission = permissions.find((p) => p.id === permissionId);
484
+ if (!permission) {
485
+ sendError(res, 404, "Permission not found");
486
+ return;
487
+ }
488
+
489
+ const exists = user_permissions.find(
490
+ (up) => up.user_id === userId && up.permission_id === permissionId
491
+ );
492
+ if (!exists) {
493
+ user_permissions.push({
494
+ id: generateId(),
495
+ user_id: userId,
496
+ permission_id: permissionId,
497
+ created_at: new Date(),
498
+ });
499
+ }
500
+
501
+ sendFastSuccess(res, 200, voidSerializer, {
502
+ status: "success",
503
+ message: "Permission assigned to user",
504
+ data: null,
505
+ });
506
+ }
507
+
508
+ export async function removePermissionFromUser(req: Request, res: Response) {
509
+ const validator = Validator.make(req.body || {}, {
510
+ userId: z.string().min(1, "userId wajib diisi"),
511
+ permissionId: z.string().min(1, "permissionId wajib diisi"),
512
+ });
513
+
514
+ if (await validator.fails()) {
515
+ sendError(res, 422, "Validation error", validator.errors());
516
+ return;
517
+ }
518
+ const { userId, permissionId } = await validator.validated();
519
+
520
+ const index = user_permissions.findIndex(
521
+ (up) => up.user_id === userId && up.permission_id === permissionId
522
+ );
523
+ if (index !== -1) {
524
+ user_permissions.splice(index, 1);
525
+ saveStore();
526
+ }
527
+
528
+ sendFastSuccess(res, 200, voidSerializer, {
529
+ status: "success",
530
+ message: "Permission removed from user",
531
+ data: null,
532
+ });
533
+ }
@@ -0,0 +1,74 @@
1
+ import { Router } from "express";
2
+ import rateLimit from "express-rate-limit";
3
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
4
+ const multer = require("multer");
5
+ import path from "path";
6
+ import fs from "fs";
7
+ import {
8
+ register,
9
+ login,
10
+ me,
11
+ logout,
12
+ refreshToken,
13
+ updatePassword,
14
+ updateProfile,
15
+ updateAvatar,
16
+ } from "@/modules/Auth/auth.controller";
17
+ import { requireAuth } from "@lapeeh/middleware/auth";
18
+
19
+ const authLimiter = rateLimit({
20
+ windowMs: 15 * 60 * 1000,
21
+ max: 50,
22
+ standardHeaders: true,
23
+ legacyHeaders: false,
24
+ });
25
+
26
+ const avatarUploadDir = process.env.AVATAR_UPLOAD_DIR || "uploads/avatars";
27
+ if (!fs.existsSync(avatarUploadDir)) {
28
+ fs.mkdirSync(avatarUploadDir, { recursive: true });
29
+ }
30
+
31
+ const storage = (multer as any).diskStorage({
32
+ destination(
33
+ _req: any,
34
+ _file: any,
35
+ cb: (error: Error | null, destination: string) => void
36
+ ) {
37
+ cb(null, avatarUploadDir);
38
+ },
39
+ filename(
40
+ _req: any,
41
+ file: any,
42
+ cb: (error: Error | null, filename: string) => void
43
+ ) {
44
+ const ext = path.extname(file.originalname);
45
+ const base = path.basename(file.originalname, ext);
46
+ const unique = Date.now() + "-" + Math.round(Math.random() * 1e9);
47
+ cb(null, base + "-" + unique + ext);
48
+ },
49
+ });
50
+
51
+ const uploadAvatar = multer({ storage });
52
+
53
+ export const authRouter = Router();
54
+
55
+ authRouter.post("/register", authLimiter, register);
56
+
57
+ authRouter.post("/login", authLimiter, login);
58
+
59
+ authRouter.get("/me", requireAuth, me);
60
+
61
+ authRouter.post("/logout", requireAuth, logout);
62
+
63
+ authRouter.post("/refresh", authLimiter, refreshToken);
64
+
65
+ authRouter.put("/password", requireAuth, updatePassword);
66
+
67
+ authRouter.put("/profile", requireAuth, updateProfile);
68
+
69
+ authRouter.post(
70
+ "/avatar",
71
+ requireAuth,
72
+ uploadAvatar.single("avatar"),
73
+ updateAvatar
74
+ );
@@ -0,0 +1,7 @@
1
+ import { Router } from "express";
2
+ import { authRouter } from "@/routes/auth";
3
+ import { rbacRouter } from "@/routes/rbac";
4
+ export const apiRouter = Router();
5
+
6
+ apiRouter.use("/auth", authRouter);
7
+ apiRouter.use("/rbac", rbacRouter);
@@ -0,0 +1,42 @@
1
+ import { Router } from "express";
2
+ import { requireAdmin, requireAuth } from "@lapeeh/middleware/auth";
3
+ import {
4
+ createRole,
5
+ listRoles,
6
+ updateRole,
7
+ deleteRole,
8
+ createPermission,
9
+ listPermissions,
10
+ updatePermission,
11
+ deletePermission,
12
+ assignRoleToUser,
13
+ removeRoleFromUser,
14
+ assignPermissionToRole,
15
+ removePermissionFromRole,
16
+ assignPermissionToUser,
17
+ removePermissionFromUser,
18
+ } from "@/modules/Rbac/rbac.controller";
19
+
20
+ export const rbacRouter = Router();
21
+
22
+ rbacRouter.use(requireAuth);
23
+ rbacRouter.use(requireAdmin);
24
+
25
+ rbacRouter.post("/roles", createRole);
26
+ rbacRouter.get("/roles", listRoles);
27
+ rbacRouter.put("/roles/:id", updateRole);
28
+ rbacRouter.delete("/roles/:id", deleteRole);
29
+
30
+ rbacRouter.post("/permissions", createPermission);
31
+ rbacRouter.get("/permissions", listPermissions);
32
+ rbacRouter.put("/permissions/:id", updatePermission);
33
+ rbacRouter.delete("/permissions/:id", deletePermission);
34
+
35
+ rbacRouter.post("/users/assign-role", assignRoleToUser);
36
+ rbacRouter.post("/users/remove-role", removeRoleFromUser);
37
+
38
+ rbacRouter.post("/roles/assign-permission", assignPermissionToRole);
39
+ rbacRouter.post("/roles/remove-permission", removePermissionFromRole);
40
+
41
+ rbacRouter.post("/users/assign-permission", assignPermissionToUser);
42
+ rbacRouter.post("/users/remove-permission", removePermissionFromUser);
File without changes
@@ -0,0 +1,12 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "include": [
4
+ "lib",
5
+ "src"
6
+ ],
7
+ "exclude": [
8
+ "node_modules",
9
+ "dist",
10
+ "tests"
11
+ ]
12
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "CommonJS",
5
+ "outDir": "dist",
6
+ "rootDir": ".",
7
+ "declaration": true,
8
+ "declarationMap": true,
9
+ "strict": true,
10
+ "esModuleInterop": true,
11
+ "skipLibCheck": true,
12
+ "forceConsistentCasingInFileNames": true,
13
+ "noUnusedLocals": true,
14
+ "noUnusedParameters": true,
15
+ "paths": {
16
+ "@lapeeh/*": ["./lib/*"],
17
+ "@/*": ["./src/*"]
18
+ }
19
+ },
20
+ "include": [
21
+ "lib",
22
+ "src",
23
+ "generated",
24
+ "tests"
25
+ ],
26
+ "exclude": [
27
+ "node_modules",
28
+ "dist"
29
+ ]
30
+ }