@hedhog/admin 0.0.103 → 0.0.105

Sign up to get free protection for your applications and to get access to all the features.
Files changed (186) hide show
  1. package/README.md +141 -141
  2. package/dist/admin.module.js +7 -7
  3. package/dist/admin.module.js.map +1 -1
  4. package/dist/auth/auth.service.d.ts +1 -1
  5. package/dist/auth/auth.service.d.ts.map +1 -1
  6. package/dist/auth/auth.service.js +8 -8
  7. package/dist/auth/auth.service.js.map +1 -1
  8. package/dist/auth/auth.service.spec.js +16 -16
  9. package/dist/auth/auth.service.spec.js.map +1 -1
  10. package/dist/dto/with-locale.dto.d.ts +4 -0
  11. package/dist/dto/with-locale.dto.d.ts.map +1 -0
  12. package/dist/dto/{with-locales.dto.js → with-locale.dto.js} +5 -5
  13. package/dist/dto/with-locale.dto.js.map +1 -0
  14. package/dist/index.d.ts +2 -2
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +2 -2
  17. package/dist/index.js.map +1 -1
  18. package/dist/locale/locale.controller.js +1 -1
  19. package/dist/locale/locale.controller.js.map +1 -1
  20. package/dist/locale/locale.middleware.d.ts +1 -1
  21. package/dist/locale/locale.middleware.d.ts.map +1 -1
  22. package/dist/locale/locale.middleware.js +2 -2
  23. package/dist/locale/locale.middleware.js.map +1 -1
  24. package/dist/locale/locale.service.d.ts +6 -6
  25. package/dist/locale/locale.service.d.ts.map +1 -1
  26. package/dist/locale/locale.service.js +40 -40
  27. package/dist/locale/locale.service.js.map +1 -1
  28. package/dist/locale/locale.service.spec.js +14 -14
  29. package/dist/locale/locale.service.spec.js.map +1 -1
  30. package/dist/menu/menu.controller.js +3 -3
  31. package/dist/menu/menu.controller.js.map +1 -1
  32. package/dist/menu/menu.service.d.ts +3 -3
  33. package/dist/menu/menu.service.d.ts.map +1 -1
  34. package/dist/menu/menu.service.js +35 -35
  35. package/dist/menu/menu.service.js.map +1 -1
  36. package/dist/menu/menu.service.spec.js +33 -37
  37. package/dist/menu/menu.service.spec.js.map +1 -1
  38. package/dist/role/dto/create.dto.d.ts +2 -2
  39. package/dist/role/dto/create.dto.d.ts.map +1 -1
  40. package/dist/role/dto/create.dto.js +2 -2
  41. package/dist/role/dto/create.dto.js.map +1 -1
  42. package/dist/role/guards/role.guard.js +4 -4
  43. package/dist/role/guards/role.guard.js.map +1 -1
  44. package/dist/role/role.controller.js +7 -7
  45. package/dist/role/role.controller.js.map +1 -1
  46. package/dist/role/role.service.d.ts +2 -2
  47. package/dist/role/role.service.d.ts.map +1 -1
  48. package/dist/role/role.service.js +35 -35
  49. package/dist/role/role.service.js.map +1 -1
  50. package/dist/role/role.service.spec.d.ts +0 -1
  51. package/dist/role/role.service.spec.js +400 -340
  52. package/dist/role/role.service.spec.js.map +1 -1
  53. package/dist/route/route.controller.js +3 -3
  54. package/dist/route/route.controller.js.map +1 -1
  55. package/dist/route/route.service.d.ts +2 -2
  56. package/dist/route/route.service.d.ts.map +1 -1
  57. package/dist/route/route.service.js +18 -18
  58. package/dist/route/route.service.js.map +1 -1
  59. package/dist/route/route.service.spec.js +27 -27
  60. package/dist/route/route.service.spec.js.map +1 -1
  61. package/dist/screen/screen.controller.js +4 -4
  62. package/dist/screen/screen.controller.js.map +1 -1
  63. package/dist/screen/screen.service.d.ts +2 -2
  64. package/dist/screen/screen.service.d.ts.map +1 -1
  65. package/dist/screen/screen.service.js +16 -16
  66. package/dist/screen/screen.service.js.map +1 -1
  67. package/dist/screen/screen.service.spec.js +16 -16
  68. package/dist/screen/screen.service.spec.js.map +1 -1
  69. package/dist/setting/dto/setting.dto.d.ts +9 -0
  70. package/dist/setting/dto/setting.dto.d.ts.map +1 -0
  71. package/dist/setting/dto/{settings.dto.js → setting.dto.js} +6 -6
  72. package/dist/setting/dto/setting.dto.js.map +1 -0
  73. package/dist/setting/{settings.controller.d.ts → setting.controller.d.ts} +6 -6
  74. package/dist/setting/setting.controller.d.ts.map +1 -0
  75. package/dist/setting/{settings.controller.js → setting.controller.js} +20 -20
  76. package/dist/setting/setting.controller.js.map +1 -0
  77. package/dist/setting/setting.module.d.ts +3 -0
  78. package/dist/setting/setting.module.d.ts.map +1 -0
  79. package/dist/setting/{settings.module.js → setting.module.js} +6 -6
  80. package/dist/setting/setting.module.js.map +1 -0
  81. package/dist/setting/{settings.service.d.ts → setting.service.d.ts} +4 -4
  82. package/dist/setting/setting.service.d.ts.map +1 -0
  83. package/dist/setting/{settings.service.js → setting.service.js} +42 -42
  84. package/dist/setting/setting.service.js.map +1 -0
  85. package/dist/setting/setting.service.spec.d.ts +2 -0
  86. package/dist/setting/setting.service.spec.d.ts.map +1 -0
  87. package/dist/setting/{settings.service.spec.js → setting.service.spec.js} +17 -17
  88. package/dist/setting/setting.service.spec.js.map +1 -0
  89. package/dist/user/user.controller.js +3 -3
  90. package/dist/user/user.controller.js.map +1 -1
  91. package/dist/user/user.service.d.ts +2 -2
  92. package/dist/user/user.service.d.ts.map +1 -1
  93. package/dist/user/user.service.js +9 -9
  94. package/dist/user/user.service.js.map +1 -1
  95. package/dist/user/user.service.spec.js +24 -24
  96. package/dist/user/user.service.spec.js.map +1 -1
  97. package/package.json +2 -2
  98. package/src/admin.module.ts +36 -0
  99. package/src/auth/auth.controller.ts +48 -0
  100. package/src/auth/auth.module.ts +39 -0
  101. package/src/auth/auth.service.spec.ts +196 -0
  102. package/src/auth/auth.service.ts +175 -0
  103. package/src/auth/decorators/public.decorator.ts +4 -0
  104. package/src/auth/decorators/user.decorator.ts +17 -0
  105. package/src/auth/dto/forget.dto.ts +6 -0
  106. package/src/auth/dto/login.dto.ts +15 -0
  107. package/src/auth/dto/otp.dto.ts +11 -0
  108. package/src/auth/enums/multifactor-type.enum.ts +4 -0
  109. package/src/auth/guards/auth.guard.ts +50 -0
  110. package/src/auth/types/user.type.ts +8 -0
  111. package/src/dto/delete.dto.ts +8 -0
  112. package/src/dto/update-ids.dto.ts +9 -0
  113. package/src/dto/with-locale.dto.ts +8 -0
  114. package/src/index.ts +34 -0
  115. package/src/locale/dto/create.dto.ts +12 -0
  116. package/src/locale/dto/delete.dto.ts +8 -0
  117. package/src/locale/dto/set-enabled.dto.ts +9 -0
  118. package/src/locale/dto/update.dto.ts +15 -0
  119. package/src/locale/index.ts +4 -0
  120. package/src/locale/locale.controller.ts +79 -0
  121. package/src/locale/locale.decorator.ts +8 -0
  122. package/src/locale/locale.middleware.ts +34 -0
  123. package/src/locale/locale.module.ts +23 -0
  124. package/src/locale/locale.service.spec.ts +193 -0
  125. package/src/locale/locale.service.ts +366 -0
  126. package/src/menu/dto/create.dto.ts +25 -0
  127. package/src/menu/dto/order.dto.ts +8 -0
  128. package/src/menu/dto/update.dto.ts +19 -0
  129. package/src/menu/menu.controller.ts +106 -0
  130. package/src/menu/menu.module.ts +18 -0
  131. package/src/menu/menu.service.spec.ts +247 -0
  132. package/src/menu/menu.service.ts +263 -0
  133. package/src/role/decorators/role.decorator.ts +4 -0
  134. package/src/role/dto/create.dto.ts +7 -0
  135. package/src/role/dto/update.dto.ts +4 -0
  136. package/src/role/guards/role.guard.ts +122 -0
  137. package/src/role/role.controller.ts +126 -0
  138. package/src/role/role.module.ts +28 -0
  139. package/src/role/role.service.spec.ts +417 -0
  140. package/src/role/role.service.ts +302 -0
  141. package/src/route/dto/create.dto.ts +13 -0
  142. package/src/route/dto/update.dto.ts +15 -0
  143. package/src/route/route.controller.ts +91 -0
  144. package/src/route/route.module.ts +18 -0
  145. package/src/route/route.service.spec.ts +299 -0
  146. package/src/route/route.service.ts +164 -0
  147. package/src/screen/dto/create.dto.ts +19 -0
  148. package/src/screen/dto/update.dto.ts +19 -0
  149. package/src/screen/screen.controller.ts +93 -0
  150. package/src/screen/screen.module.ts +18 -0
  151. package/src/screen/screen.service.spec.ts +298 -0
  152. package/src/screen/screen.service.ts +181 -0
  153. package/src/setting/dto/create.dto.ts +1 -0
  154. package/src/setting/dto/setting-user.dto.ts +6 -0
  155. package/src/setting/dto/setting.dto.ts +17 -0
  156. package/src/setting/dto/update.dto.ts +3 -0
  157. package/src/setting/setting.controller.ts +100 -0
  158. package/src/setting/setting.module.ts +18 -0
  159. package/src/setting/setting.service.spec.ts +183 -0
  160. package/src/setting/setting.service.ts +346 -0
  161. package/src/types/http-method.ts +8 -0
  162. package/src/user/constants/user.constants.ts +1 -0
  163. package/src/user/dto/create.dto.ts +24 -0
  164. package/src/user/dto/update.dto.ts +41 -0
  165. package/src/user/user.controller.ts +75 -0
  166. package/src/user/user.module.ts +18 -0
  167. package/src/user/user.service.spec.ts +294 -0
  168. package/src/user/user.service.ts +129 -0
  169. package/tsconfig.lib.json +9 -0
  170. package/tsconfig.production.json +20 -0
  171. package/dist/dto/with-locales.dto.d.ts +0 -4
  172. package/dist/dto/with-locales.dto.d.ts.map +0 -1
  173. package/dist/dto/with-locales.dto.js.map +0 -1
  174. package/dist/setting/dto/settings.dto.d.ts +0 -9
  175. package/dist/setting/dto/settings.dto.d.ts.map +0 -1
  176. package/dist/setting/dto/settings.dto.js.map +0 -1
  177. package/dist/setting/settings.controller.d.ts.map +0 -1
  178. package/dist/setting/settings.controller.js.map +0 -1
  179. package/dist/setting/settings.module.d.ts +0 -3
  180. package/dist/setting/settings.module.d.ts.map +0 -1
  181. package/dist/setting/settings.module.js.map +0 -1
  182. package/dist/setting/settings.service.d.ts.map +0 -1
  183. package/dist/setting/settings.service.js.map +0 -1
  184. package/dist/setting/settings.service.spec.d.ts +0 -2
  185. package/dist/setting/settings.service.spec.d.ts.map +0 -1
  186. package/dist/setting/settings.service.spec.js.map +0 -1
@@ -0,0 +1,18 @@
1
+ import { AuthModule } from '../auth/auth.module';
2
+ import { PaginationModule } from '@hedhog/pagination';
3
+ import { PrismaModule } from '@hedhog/prisma';
4
+ import { Module, forwardRef } from '@nestjs/common';
5
+ import { MenuController } from './menu.controller';
6
+ import { MenuService } from './menu.service';
7
+
8
+ @Module({
9
+ providers: [MenuService],
10
+ exports: [MenuService],
11
+ controllers: [MenuController],
12
+ imports: [
13
+ forwardRef(() => AuthModule),
14
+ forwardRef(() => PrismaModule),
15
+ forwardRef(() => PaginationModule),
16
+ ],
17
+ })
18
+ export class MenuModule {}
@@ -0,0 +1,247 @@
1
+ import {
2
+ PageOrderDirection,
3
+ PaginationDTO,
4
+ PaginationService,
5
+ } from '@hedhog/pagination';
6
+ import { PrismaService } from '@hedhog/prisma';
7
+ import { BadRequestException } from '@nestjs/common';
8
+ import { Test, TestingModule } from '@nestjs/testing';
9
+ import { MenuService } from './menu.service';
10
+
11
+ describe('MenuService', () => {
12
+ let menuService: MenuService;
13
+ let prismaService: PrismaService;
14
+ let paginationService: PaginationService;
15
+
16
+ const mockPrismaService = {
17
+ menu: {
18
+ create: jest.fn(),
19
+ update: jest.fn(),
20
+ deleteMany: jest.fn(),
21
+ findUnique: jest.fn(),
22
+ findMany: jest.fn(),
23
+ count: jest.fn(),
24
+ },
25
+ role_menu: {
26
+ deleteMany: jest.fn(),
27
+ createMany: jest.fn(),
28
+ },
29
+ menu_screen: {
30
+ deleteMany: jest.fn(),
31
+ createMany: jest.fn(),
32
+ },
33
+ };
34
+
35
+ const mockPaginationService = {
36
+ paginate: jest.fn(),
37
+ createInsensitiveSearch: jest.fn(),
38
+ };
39
+
40
+ beforeEach(async () => {
41
+ const module: TestingModule = await Test.createTestingModule({
42
+ providers: [
43
+ MenuService,
44
+ { provide: PrismaService, useValue: mockPrismaService },
45
+ { provide: PaginationService, useValue: mockPaginationService },
46
+ ],
47
+ }).compile();
48
+
49
+ menuService = module.get<MenuService>(MenuService);
50
+ prismaService = module.get<PrismaService>(PrismaService);
51
+ paginationService = module.get<PaginationService>(PaginationService);
52
+ });
53
+
54
+ afterEach(() => {
55
+ jest.clearAllMocks();
56
+ });
57
+
58
+ describe('create', () => {
59
+ it('should create a new menu', async () => {
60
+ const createMenuDto = {
61
+ name: 'Test Menu',
62
+ url: '/test',
63
+ icon: 'test-icon',
64
+ order: 1,
65
+ menu_id: undefined,
66
+ };
67
+ mockPrismaService.menu.create.mockResolvedValue(createMenuDto);
68
+
69
+ const result = await menuService.create(createMenuDto);
70
+
71
+ expect(result).toEqual(createMenuDto);
72
+ expect(prismaService.menu.create).toHaveBeenCalledWith({
73
+ data: createMenuDto,
74
+ });
75
+ });
76
+ });
77
+
78
+ describe('update', () => {
79
+ it('should update a menu', async () => {
80
+ const updateDto = { id: 1, data: { name: 'Updated Menu' } };
81
+ mockPrismaService.menu.update.mockResolvedValue(updateDto.data);
82
+
83
+ const result = await menuService.update(updateDto);
84
+
85
+ expect(result).toEqual(updateDto.data);
86
+ expect(prismaService.menu.update).toHaveBeenCalledWith({
87
+ where: { id: updateDto.id },
88
+ data: updateDto.data,
89
+ });
90
+ });
91
+ });
92
+
93
+ describe('delete', () => {
94
+ it('should delete menu', async () => {
95
+ const deleteDto = { ids: [1, 2, 3] };
96
+ mockPrismaService.menu.deleteMany.mockResolvedValue({ count: 3 });
97
+
98
+ const result = await menuService.delete(deleteDto);
99
+
100
+ expect(result).toEqual({ count: 3 });
101
+ expect(prismaService.menu.deleteMany).toHaveBeenCalledWith({
102
+ where: { id: { in: deleteDto.ids } },
103
+ });
104
+ });
105
+
106
+ it('should throw BadRequestException if no ids are provided', async () => {
107
+ await expect(menuService.delete({ ids: null })).rejects.toThrow(
108
+ BadRequestException,
109
+ );
110
+ });
111
+ });
112
+
113
+ describe('listScreens', () => {
114
+ it('should paginate screens', async () => {
115
+ const locale = 'en';
116
+ const menuId = 1;
117
+ const paginationParams: PaginationDTO = {
118
+ page: 1,
119
+ pageSize: 10,
120
+ search: '',
121
+ sortField: '',
122
+ sortOrder: PageOrderDirection.Asc,
123
+ fields: '',
124
+ };
125
+
126
+ const mockScreens = [{ id: 1, name: 'Screen 1' }];
127
+ mockPaginationService.paginate.mockResolvedValue(mockScreens);
128
+
129
+ const result = await menuService.listScreens(
130
+ locale,
131
+ menuId,
132
+ paginationParams,
133
+ );
134
+
135
+ expect(result).toEqual(mockScreens);
136
+ expect(paginationService.paginate).toHaveBeenCalledWith(
137
+ prismaService.screens,
138
+ paginationParams,
139
+ expect.anything(),
140
+ 'screen_locale',
141
+ );
142
+ });
143
+ });
144
+
145
+ describe('updateScreens', () => {
146
+ it('should update screens associated with a menu', async () => {
147
+ const menuId = 1;
148
+ const updateData = { ids: [1, 2, 3] };
149
+
150
+ jest
151
+ .spyOn(prismaService.menu_screen, 'deleteMany')
152
+ .mockResolvedValue(null);
153
+ jest
154
+ .spyOn(prismaService.menu_screen, 'createMany')
155
+ .mockResolvedValue(null);
156
+
157
+ await menuService.updateScreens(menuId, updateData);
158
+
159
+ expect(prismaService.menu_screen.deleteMany).toHaveBeenCalledWith({
160
+ where: { menu_id: menuId },
161
+ });
162
+
163
+ expect(prismaService.menu_screen.createMany).toHaveBeenCalledWith({
164
+ data: updateData.ids.map((screenId) => ({
165
+ menu_id: menuId,
166
+ screen_id: screenId,
167
+ })),
168
+ skipDuplicates: true,
169
+ });
170
+ });
171
+ });
172
+
173
+ describe('updateRoles', () => {
174
+ it('should update role associated with a menu', async () => {
175
+ const menuId = 1;
176
+ const updateData = { ids: [1, 2] };
177
+
178
+ jest.spyOn(prismaService.role_menu, 'deleteMany').mockResolvedValue(null);
179
+ jest.spyOn(prismaService.role_menu, 'createMany').mockResolvedValue(null);
180
+
181
+ await menuService.updateRoles(menuId, updateData);
182
+
183
+ expect(prismaService.role_menu.deleteMany).toHaveBeenCalledWith({
184
+ where: { menu_id: menuId },
185
+ });
186
+
187
+ expect(prismaService.role_menu.createMany).toHaveBeenCalledWith({
188
+ data: updateData.ids.map((roleId) => ({
189
+ menu_id: menuId,
190
+ role_id: roleId,
191
+ })),
192
+ skipDuplicates: true,
193
+ });
194
+ });
195
+ });
196
+
197
+ describe('updateOrder', () => {
198
+ it('should update the order of menu', async () => {
199
+ const orderData = { ids: [1, 2, 3] };
200
+
201
+ jest.spyOn(prismaService.menu, 'count').mockResolvedValue(3);
202
+ jest.spyOn(prismaService.menu, 'update').mockResolvedValue(null);
203
+
204
+ await menuService.updateOrder(orderData);
205
+
206
+ expect(prismaService.menu.count).toHaveBeenCalledWith({
207
+ where: { id: { in: orderData.ids } },
208
+ });
209
+
210
+ expect(prismaService.menu.update).toHaveBeenCalledTimes(
211
+ orderData.ids.length,
212
+ );
213
+
214
+ orderData.ids.forEach((id, index) => {
215
+ expect(prismaService.menu.update).toHaveBeenCalledWith({
216
+ where: { id },
217
+ data: { order: index + 1 },
218
+ });
219
+ });
220
+ });
221
+
222
+ it('should throw BadRequestException if IDs are invalid', async () => {
223
+ const orderData = { ids: [1, 2, 3] };
224
+
225
+ jest.spyOn(prismaService.menu, 'count').mockResolvedValue(2); // IDs não batem com o número esperado
226
+
227
+ await expect(menuService.updateOrder(orderData)).rejects.toThrow(
228
+ BadRequestException,
229
+ );
230
+ });
231
+ });
232
+ /*
233
+ describe('getMenus', () => {
234
+ it('should get menu for a user', async () => {
235
+ const locale = 'en';
236
+ const userId = 1;
237
+ const mockMenus = [{ id: 1, name: 'Menu 1' }];
238
+ mockPrismaService.menu.findMany.mockResolvedValue(mockMenus);
239
+
240
+ const result = await menuService.getMenus(locale, userId);
241
+
242
+ expect(result).toEqual(mockMenus);
243
+ expect(prismaService.menu.findMany).toHaveBeenCalled();
244
+ });
245
+ });
246
+ */
247
+ });
@@ -0,0 +1,263 @@
1
+ import { PaginationDTO, PaginationService } from '@hedhog/pagination';
2
+ import { PrismaService } from '@hedhog/prisma';
3
+ import { itemTranslations } from '@hedhog/utils';
4
+ import {
5
+ BadRequestException,
6
+ Inject,
7
+ Injectable,
8
+ forwardRef,
9
+ } from '@nestjs/common';
10
+ import { DeleteDTO } from '../dto/delete.dto';
11
+ import { UpdateIdsDTO } from '../dto/update-ids.dto';
12
+ import { CreateDTO } from './dto/create.dto';
13
+ import { OrderDTO } from './dto/order.dto';
14
+ import { UpdateDTO } from './dto/update.dto';
15
+
16
+ @Injectable()
17
+ export class MenuService {
18
+ constructor(
19
+ @Inject(forwardRef(() => PrismaService))
20
+ private readonly prismaService: PrismaService,
21
+ @Inject(forwardRef(() => PaginationService))
22
+ private readonly paginationService: PaginationService,
23
+ ) {}
24
+
25
+ async updateScreens(menuId: number, data: UpdateIdsDTO) {
26
+ await this.prismaService.menu_screen.deleteMany({
27
+ where: {
28
+ menu_id: menuId,
29
+ },
30
+ });
31
+
32
+ return this.prismaService.menu_screen.createMany({
33
+ data: data.ids.map((screenId) => ({
34
+ menu_id: menuId,
35
+ screen_id: screenId,
36
+ })),
37
+ skipDuplicates: true,
38
+ });
39
+ }
40
+ async updateRoles(menuId: number, data: UpdateIdsDTO) {
41
+ await this.prismaService.role_menu.deleteMany({
42
+ where: {
43
+ menu_id: menuId,
44
+ },
45
+ });
46
+
47
+ return this.prismaService.role_menu.createMany({
48
+ data: data.ids.map((roleId) => ({
49
+ menu_id: menuId,
50
+ role_id: roleId,
51
+ })),
52
+ skipDuplicates: true,
53
+ });
54
+ }
55
+ async listScreens(
56
+ locale: string,
57
+ menuId: number,
58
+ paginationParams: PaginationDTO,
59
+ ) {
60
+ return this.paginationService.paginate(
61
+ this.prismaService.screen,
62
+ paginationParams,
63
+ {
64
+ include: {
65
+ screen_locale: {
66
+ where: {
67
+ locale: {
68
+ code: locale,
69
+ },
70
+ },
71
+ select: {
72
+ name: true,
73
+ },
74
+ },
75
+ menu_screen: {
76
+ where: {
77
+ menu_id: menuId,
78
+ },
79
+ select: {
80
+ screen_id: true,
81
+ menu_id: true,
82
+ },
83
+ },
84
+ },
85
+ },
86
+ 'screen_locale',
87
+ );
88
+ }
89
+ async listRoles(
90
+ locale: string,
91
+ menuId: number,
92
+ paginationParams: PaginationDTO,
93
+ ) {
94
+ return this.paginationService.paginate(
95
+ this.prismaService.role,
96
+ paginationParams,
97
+ {
98
+ include: {
99
+ role_locale: {
100
+ where: {
101
+ locale: {
102
+ code: locale,
103
+ },
104
+ },
105
+ select: {
106
+ name: true,
107
+ description: true,
108
+ },
109
+ },
110
+ role_menu: {
111
+ where: {
112
+ menu_id: menuId,
113
+ },
114
+ select: {
115
+ role_id: true,
116
+ menu_id: true,
117
+ },
118
+ },
119
+ },
120
+ },
121
+ 'role_locale',
122
+ );
123
+ }
124
+
125
+ async getMenus(locale: string, userId: number, menuId = 0) {
126
+ if (menuId === 0) {
127
+ menuId = null;
128
+ }
129
+
130
+ let menu = (await this.prismaService.menu.findMany({
131
+ where: {
132
+ menu_id: menuId,
133
+ role_menu: {
134
+ some: {
135
+ role: {
136
+ role_user: {
137
+ some: {
138
+ user_id: userId,
139
+ },
140
+ },
141
+ },
142
+ },
143
+ },
144
+ },
145
+ orderBy: {
146
+ order: 'asc',
147
+ },
148
+ include: {
149
+ menu_locale: {
150
+ where: {
151
+ locale: {
152
+ code: locale,
153
+ },
154
+ },
155
+ select: {
156
+ name: true,
157
+ },
158
+ },
159
+ },
160
+ })) as unknown[] as any[];
161
+
162
+ menu = menu.map((m) => itemTranslations('menu_locale', m));
163
+
164
+ for (let i = 0; i < menu.length; i++) {
165
+ menu[i].menu = await this.getMenus(locale, userId, menu[i].id);
166
+ }
167
+
168
+ return menu;
169
+ }
170
+
171
+ async getSystemMenu(locale: string, userId: number) {
172
+ return this.getMenus(locale, userId);
173
+ }
174
+
175
+ async getMenu(locale: string, paginationParams: PaginationDTO) {
176
+ const fields = ['url', 'icon'];
177
+ const OR = this.prismaService.createInsensitiveSearch(
178
+ fields,
179
+ paginationParams,
180
+ );
181
+
182
+ return this.paginationService.paginate(
183
+ this.prismaService.menu,
184
+ paginationParams,
185
+ {
186
+ where: {
187
+ OR,
188
+ },
189
+ include: {
190
+ menu_locale: {
191
+ where: {
192
+ locale: {
193
+ code: locale,
194
+ },
195
+ },
196
+ select: {
197
+ name: true,
198
+ },
199
+ },
200
+ },
201
+ },
202
+ 'menu_locale',
203
+ );
204
+ }
205
+
206
+ async get(menuId: number) {
207
+ return this.prismaService.menu.findUnique({
208
+ where: { id: menuId },
209
+ });
210
+ }
211
+
212
+ async create({ name, url, icon, order, menuId }: CreateDTO) {
213
+ return this.prismaService.menu.create({
214
+ data: { name, url, icon, order, menu_id: menuId },
215
+ });
216
+ }
217
+
218
+ async update({ id, data }: { id: number; data: UpdateDTO }) {
219
+ return this.prismaService.menu.update({
220
+ where: { id },
221
+ data,
222
+ });
223
+ }
224
+
225
+ async delete({ ids }: DeleteDTO) {
226
+ if (ids == undefined || ids == null) {
227
+ throw new BadRequestException(
228
+ `You must select at least one menu to delete.`,
229
+ );
230
+ }
231
+
232
+ return this.prismaService.menu.deleteMany({
233
+ where: {
234
+ id: {
235
+ in: ids,
236
+ },
237
+ },
238
+ });
239
+ }
240
+
241
+ async updateOrder({ ids }: OrderDTO): Promise<void> {
242
+ const count = await this.prismaService.menu.count({
243
+ where: {
244
+ id: {
245
+ in: ids,
246
+ },
247
+ },
248
+ });
249
+
250
+ if (count !== ids.length) {
251
+ throw new BadRequestException('IDs inválidos.');
252
+ }
253
+
254
+ await Promise.all(
255
+ ids.map((id, index) =>
256
+ this.prismaService.menu.update({
257
+ where: { id },
258
+ data: { order: index + 1 },
259
+ }),
260
+ ),
261
+ );
262
+ }
263
+ }
@@ -0,0 +1,4 @@
1
+ import { SetMetadata } from '@nestjs/common';
2
+
3
+ export const WITH_ROLE = 'withRole';
4
+ export const Role = () => SetMetadata(WITH_ROLE, true);
@@ -0,0 +1,7 @@
1
+ import { IsString } from 'class-validator';
2
+ import { WithLocaleDTO } from '../../dto/with-locale.dto';
3
+
4
+ export class CreateDTO extends WithLocaleDTO {
5
+ @IsString()
6
+ slug: string;
7
+ }
@@ -0,0 +1,4 @@
1
+ import { PartialType } from '@nestjs/mapped-types';
2
+ import { CreateDTO } from './create.dto';
3
+
4
+ export class UpdateDTO extends PartialType(CreateDTO) {}
@@ -0,0 +1,122 @@
1
+ import { PrismaService } from '@hedhog/prisma';
2
+ import {
3
+ CanActivate,
4
+ ExecutionContext,
5
+ forwardRef,
6
+ Inject,
7
+ Injectable,
8
+ RequestMethod,
9
+ UnauthorizedException,
10
+ } from '@nestjs/common';
11
+ import { METHOD_METADATA } from '@nestjs/common/constants';
12
+ import { Reflector } from '@nestjs/core';
13
+ import { Request } from 'express';
14
+ import { IS_PUBLIC_KEY } from '../../auth/decorators/public.decorator';
15
+ import { WITH_ROLE } from '../decorators/role.decorator';
16
+
17
+ @Injectable()
18
+ export class RoleGuard implements CanActivate {
19
+ constructor(
20
+ private reflector: Reflector,
21
+ @Inject(forwardRef(() => PrismaService))
22
+ private readonly prisma: PrismaService,
23
+ ) {}
24
+
25
+ async canActivate(context: ExecutionContext): Promise<boolean> {
26
+ const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
27
+ context.getHandler(),
28
+ context.getClass(),
29
+ ]);
30
+
31
+ if (isPublic) {
32
+ return true;
33
+ }
34
+
35
+ const withRole = this.reflector.getAllAndOverride<boolean>(WITH_ROLE, [
36
+ context.getHandler(),
37
+ context.getClass(),
38
+ ]);
39
+
40
+ if (!withRole) {
41
+ return true;
42
+ }
43
+
44
+ const request = context.switchToHttp().getRequest<Request>();
45
+ const controller = context.getClass();
46
+ const handler = context.getHandler();
47
+ const controllerPath = this.reflector.get<string>('path', controller) || '';
48
+ const methodPath = this.reflector.get<string>('path', handler) || '';
49
+
50
+ const requestMethod = this.reflector.get<RequestMethod>(
51
+ METHOD_METADATA,
52
+ handler,
53
+ );
54
+
55
+ let fullPath = `/${controllerPath}/${methodPath}`.replace(/\/+/g, '/');
56
+
57
+ if (fullPath.endsWith('/')) {
58
+ fullPath = fullPath.slice(0, -1);
59
+ }
60
+
61
+ const token = this.extractTokenFromHeader(request);
62
+
63
+ if (!token) {
64
+ throw new UnauthorizedException();
65
+ }
66
+
67
+ const userId = (request as any)?.auth?.user?.id;
68
+
69
+ let httpMethod: any;
70
+ switch (requestMethod) {
71
+ case RequestMethod.GET:
72
+ httpMethod = 'GET';
73
+ break;
74
+ case RequestMethod.POST:
75
+ httpMethod = 'POST';
76
+ break;
77
+ case RequestMethod.PUT:
78
+ httpMethod = 'PUT';
79
+ break;
80
+ case RequestMethod.DELETE:
81
+ httpMethod = 'DELETE';
82
+ break;
83
+ case RequestMethod.PATCH:
84
+ httpMethod = 'PATCH';
85
+ break;
86
+ case RequestMethod.OPTIONS:
87
+ httpMethod = 'OPTIONS';
88
+ break;
89
+ case RequestMethod.HEAD:
90
+ httpMethod = 'HEAD';
91
+ break;
92
+ case RequestMethod.ALL:
93
+ httpMethod = 'ALL';
94
+ break;
95
+ }
96
+
97
+ const route = await this.prisma.route.count({
98
+ where: {
99
+ method: httpMethod,
100
+ url: fullPath,
101
+ role_route: {
102
+ some: {
103
+ role: {
104
+ role_user: {
105
+ some: {
106
+ user_id: Number(userId),
107
+ },
108
+ },
109
+ },
110
+ },
111
+ },
112
+ },
113
+ });
114
+
115
+ return route === 1;
116
+ }
117
+
118
+ private extractTokenFromHeader(request: Request): string | undefined {
119
+ const [type, token] = request.headers.authorization?.split(' ') ?? [];
120
+ return type === 'Bearer' ? token : undefined;
121
+ }
122
+ }