@hed-hog/core 0.0.367 → 0.0.370
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/dist/file/file.service.d.ts +1 -0
- package/dist/file/file.service.d.ts.map +1 -1
- package/dist/file/file.service.js +1 -1
- package/dist/file/file.service.js.map +1 -1
- package/dist/notification/events/notification-stale-progress-check.event.d.ts +6 -0
- package/dist/notification/events/notification-stale-progress-check.event.d.ts.map +1 -0
- package/dist/notification/events/notification-stale-progress-check.event.js +11 -0
- package/dist/notification/events/notification-stale-progress-check.event.js.map +1 -0
- package/dist/notification/notification.service.d.ts +4 -1
- package/dist/notification/notification.service.d.ts.map +1 -1
- package/dist/notification/notification.service.js +9 -26
- package/dist/notification/notification.service.js.map +1 -1
- package/dist/task/task.service.d.ts +19 -0
- package/dist/task/task.service.d.ts.map +1 -1
- package/dist/task/task.service.js +46 -0
- package/dist/task/task.service.js.map +1 -1
- package/dist/user/user.controller.d.ts +23 -5
- package/dist/user/user.controller.d.ts.map +1 -1
- package/dist/user/user.controller.js +32 -15
- package/dist/user/user.controller.js.map +1 -1
- package/dist/user/user.service.d.ts +28 -8
- package/dist/user/user.service.d.ts.map +1 -1
- package/dist/user/user.service.js +120 -8
- package/dist/user/user.service.js.map +1 -1
- package/hedhog/data/integration_event_catalog.yaml +118 -118
- package/hedhog/data/route.yaml +162 -204
- package/hedhog/data/setting_group.yaml +14 -0
- package/hedhog/frontend/app/roles/page.tsx.ejs +4 -1
- package/hedhog/frontend/app/users/user-logs.tsx.ejs +190 -0
- package/hedhog/frontend/app/users/user-sheet.tsx.ejs +15 -1
- package/hedhog/frontend/messages/en.json +13 -0
- package/hedhog/frontend/messages/pt.json +13 -0
- package/hedhog/table/event_webhook.yaml +55 -55
- package/hedhog/table/event_webhook_delivery_log.yaml +66 -66
- package/hedhog/table/event_webhook_event.yaml +26 -26
- package/hedhog/table/integration_event_catalog.yaml +41 -41
- package/hedhog/table/mcp_conversation.yaml +12 -12
- package/hedhog/table/mcp_conversation_message.yaml +23 -23
- package/hedhog/table/mcp_tool_call_log.yaml +22 -22
- package/hedhog/table/route.yaml +33 -33
- package/hedhog/table/user_change_log.yaml +40 -0
- package/hedhog/table/webhook_call_log.yaml +46 -46
- package/hedhog/table/webhook_integration.yaml +43 -43
- package/package.json +5 -5
- package/src/file/file.service.ts +2 -1
- package/src/integration/types/event.types.ts +4 -4
- package/src/mcp/decorators/mcp-tool.decorator.ts +7 -7
- package/src/mcp/guards/mcp-permission.guard.ts +51 -51
- package/src/mcp/interfaces/mcp-tool-definition.interface.ts +6 -6
- package/src/mcp/mcp-server.service.ts +87 -87
- package/src/mcp/types/mcp-context.type.ts +6 -6
- package/src/mcp-chat/dto/create-conversation.dto.ts +7 -7
- package/src/mcp-chat/dto/send-message.dto.ts +7 -7
- package/src/notification/events/notification-stale-progress-check.event.ts +5 -0
- package/src/notification/notification.service.ts +12 -25
- package/src/route/dto/create.dto.ts +32 -32
- package/src/route/dto/update.dto.ts +31 -31
- package/src/task/task.service.ts +52 -0
- package/src/user/user.controller.ts +23 -9
- package/src/user/user.service.ts +183 -8
- package/src/webhook-integration/integration-event-catalog.controller.ts +50 -50
- package/src/webhook-integration/integration-event-catalog.service.ts +117 -117
package/src/user/user.service.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { getLocaleText } from '@hed-hog/api-locale';
|
|
2
2
|
import { PaginationService } from '@hed-hog/api-pagination';
|
|
3
3
|
import { PrismaService } from '@hed-hog/api-prisma';
|
|
4
|
+
import type { Prisma, user_change_log } from '@hed-hog/api-prisma';
|
|
4
5
|
import {
|
|
5
6
|
BadRequestException,
|
|
6
7
|
Inject,
|
|
@@ -55,6 +56,18 @@ const USER_SORT_FIELDS = [
|
|
|
55
56
|
|
|
56
57
|
const USER_SEARCH_FIELDS = ['name'] as const;
|
|
57
58
|
|
|
59
|
+
type UserChangeLog = user_change_log;
|
|
60
|
+
type UserChangeLogActor = Prisma.userGetPayload<{
|
|
61
|
+
select: {
|
|
62
|
+
id: true;
|
|
63
|
+
name: true;
|
|
64
|
+
user_identifier: {
|
|
65
|
+
select: { value: true };
|
|
66
|
+
take: 1;
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
}>;
|
|
70
|
+
|
|
58
71
|
@Injectable()
|
|
59
72
|
export class UserService {
|
|
60
73
|
constructor(
|
|
@@ -213,7 +226,12 @@ export class UserService {
|
|
|
213
226
|
return { user, challenge };
|
|
214
227
|
}
|
|
215
228
|
|
|
216
|
-
async update(
|
|
229
|
+
async update(
|
|
230
|
+
locale: string,
|
|
231
|
+
userId: number,
|
|
232
|
+
{ name }: UpdateDTO,
|
|
233
|
+
actorUserId?: number,
|
|
234
|
+
) {
|
|
217
235
|
const user = await this.findUserById(locale, userId);
|
|
218
236
|
|
|
219
237
|
const result = await this.prismaService.user.update({
|
|
@@ -221,6 +239,17 @@ export class UserService {
|
|
|
221
239
|
data: { name: name ?? user.name },
|
|
222
240
|
});
|
|
223
241
|
|
|
242
|
+
if (result.name !== user.name) {
|
|
243
|
+
await this.recordUserChange({
|
|
244
|
+
targetUserId: userId,
|
|
245
|
+
actorUserId,
|
|
246
|
+
action: 'update',
|
|
247
|
+
summary: `name: "${user.name ?? ''}" → "${result.name ?? ''}"`,
|
|
248
|
+
beforeData: { name: user.name },
|
|
249
|
+
afterData: { name: result.name },
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
|
|
224
253
|
await this.integrationApi.publishEvent({
|
|
225
254
|
eventName: 'core.user.updated',
|
|
226
255
|
sourceModule: 'core',
|
|
@@ -236,6 +265,7 @@ export class UserService {
|
|
|
236
265
|
locale: string,
|
|
237
266
|
userId: number,
|
|
238
267
|
{ password }: ResetPasswordDTO,
|
|
268
|
+
actorUserId?: number,
|
|
239
269
|
) {
|
|
240
270
|
await this.validateUserExists(locale, userId);
|
|
241
271
|
|
|
@@ -266,6 +296,13 @@ export class UserService {
|
|
|
266
296
|
|
|
267
297
|
await this.registerUserActivity(userId, 'resetPassword');
|
|
268
298
|
|
|
299
|
+
await this.recordUserChange({
|
|
300
|
+
targetUserId: userId,
|
|
301
|
+
actorUserId,
|
|
302
|
+
action: 'reset_password',
|
|
303
|
+
summary: 'Password reset and flagged for required change on next login',
|
|
304
|
+
});
|
|
305
|
+
|
|
269
306
|
return {
|
|
270
307
|
password: nextPassword,
|
|
271
308
|
requiresReset: true,
|
|
@@ -305,7 +342,12 @@ export class UserService {
|
|
|
305
342
|
// Avatar Management
|
|
306
343
|
// ==========================================
|
|
307
344
|
|
|
308
|
-
async changeAvatar(
|
|
345
|
+
async changeAvatar(
|
|
346
|
+
locale: string,
|
|
347
|
+
userId: number,
|
|
348
|
+
avatar: MulterFile,
|
|
349
|
+
actorUserId?: number,
|
|
350
|
+
) {
|
|
309
351
|
const user = await this.validateUserExists(locale, userId);
|
|
310
352
|
|
|
311
353
|
const newFile = await this.file.upload(USER_AVATAR_UPLOAD_DESTINATION, avatar);
|
|
@@ -317,6 +359,15 @@ export class UserService {
|
|
|
317
359
|
data: { photo_id: newFile.id },
|
|
318
360
|
});
|
|
319
361
|
|
|
362
|
+
await this.recordUserChange({
|
|
363
|
+
targetUserId: userId,
|
|
364
|
+
actorUserId,
|
|
365
|
+
action: 'avatar_changed',
|
|
366
|
+
summary: 'Avatar updated',
|
|
367
|
+
beforeData: { photo_id: user.photo_id },
|
|
368
|
+
afterData: { photo_id: newFile.id },
|
|
369
|
+
});
|
|
370
|
+
|
|
320
371
|
return {
|
|
321
372
|
id: newFile.id,
|
|
322
373
|
path: newFile.path,
|
|
@@ -518,8 +569,13 @@ export class UserService {
|
|
|
518
569
|
}));
|
|
519
570
|
}
|
|
520
571
|
|
|
521
|
-
async assignRoleToUser(
|
|
522
|
-
|
|
572
|
+
async assignRoleToUser(
|
|
573
|
+
locale: string,
|
|
574
|
+
userId: number,
|
|
575
|
+
roleId: number,
|
|
576
|
+
actorUserId?: number,
|
|
577
|
+
) {
|
|
578
|
+
const [, role] = await Promise.all([
|
|
523
579
|
this.validateUserExists(locale, userId),
|
|
524
580
|
this.validateRoleExists(locale, roleId),
|
|
525
581
|
]);
|
|
@@ -530,16 +586,31 @@ export class UserService {
|
|
|
530
586
|
return existingRoleUser;
|
|
531
587
|
}
|
|
532
588
|
|
|
533
|
-
|
|
589
|
+
const result = await this.prismaService.role_user.create({
|
|
534
590
|
data: {
|
|
535
591
|
user_id: userId,
|
|
536
592
|
role_id: roleId,
|
|
537
593
|
},
|
|
538
594
|
});
|
|
595
|
+
|
|
596
|
+
await this.recordUserChange({
|
|
597
|
+
targetUserId: userId,
|
|
598
|
+
actorUserId,
|
|
599
|
+
action: 'role_assigned',
|
|
600
|
+
summary: `Role assigned: ${role.slug}`,
|
|
601
|
+
afterData: { role_id: roleId, slug: role.slug },
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
return result;
|
|
539
605
|
}
|
|
540
606
|
|
|
541
|
-
async removeRoleFromUser(
|
|
542
|
-
|
|
607
|
+
async removeRoleFromUser(
|
|
608
|
+
locale: string,
|
|
609
|
+
userId: number,
|
|
610
|
+
roleId: number,
|
|
611
|
+
actorUserId?: number,
|
|
612
|
+
) {
|
|
613
|
+
const [, role] = await Promise.all([
|
|
543
614
|
this.validateUserExists(locale, userId),
|
|
544
615
|
this.validateRoleExists(locale, roleId),
|
|
545
616
|
]);
|
|
@@ -554,6 +625,14 @@ export class UserService {
|
|
|
554
625
|
where: { id: roleUser.id },
|
|
555
626
|
});
|
|
556
627
|
|
|
628
|
+
await this.recordUserChange({
|
|
629
|
+
targetUserId: userId,
|
|
630
|
+
actorUserId,
|
|
631
|
+
action: 'role_removed',
|
|
632
|
+
summary: `Role removed: ${role.slug}`,
|
|
633
|
+
beforeData: { role_id: roleId, slug: role.slug },
|
|
634
|
+
});
|
|
635
|
+
|
|
557
636
|
return { count: 1 };
|
|
558
637
|
}
|
|
559
638
|
|
|
@@ -574,6 +653,102 @@ export class UserService {
|
|
|
574
653
|
});
|
|
575
654
|
}
|
|
576
655
|
|
|
656
|
+
// ==========================================
|
|
657
|
+
// Change Log (administrative audit trail)
|
|
658
|
+
// ==========================================
|
|
659
|
+
|
|
660
|
+
private async recordUserChange(data: {
|
|
661
|
+
targetUserId: number;
|
|
662
|
+
actorUserId?: number | null;
|
|
663
|
+
action: string;
|
|
664
|
+
summary?: string;
|
|
665
|
+
beforeData?: unknown;
|
|
666
|
+
afterData?: unknown;
|
|
667
|
+
}) {
|
|
668
|
+
return this.prismaService.user_change_log
|
|
669
|
+
.create({
|
|
670
|
+
data: {
|
|
671
|
+
target_user_id: data.targetUserId,
|
|
672
|
+
actor_user_id: data.actorUserId ?? null,
|
|
673
|
+
action: data.action,
|
|
674
|
+
summary: data.summary ?? null,
|
|
675
|
+
before_data:
|
|
676
|
+
data.beforeData === undefined
|
|
677
|
+
? null
|
|
678
|
+
: JSON.stringify(data.beforeData),
|
|
679
|
+
after_data:
|
|
680
|
+
data.afterData === undefined
|
|
681
|
+
? null
|
|
682
|
+
: JSON.stringify(data.afterData),
|
|
683
|
+
},
|
|
684
|
+
})
|
|
685
|
+
.catch(() => null);
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
async listUserChangeLogs(locale: string, userId: number, paginationParams) {
|
|
689
|
+
await this.validateUserExists(locale, userId);
|
|
690
|
+
|
|
691
|
+
const paginated = await this.paginationService.paginatePrismaModel(
|
|
692
|
+
this.prismaService.user_change_log,
|
|
693
|
+
{
|
|
694
|
+
page: paginationParams?.page,
|
|
695
|
+
pageSize: paginationParams?.pageSize,
|
|
696
|
+
sortField: paginationParams?.sortField || 'created_at',
|
|
697
|
+
sortOrder: paginationParams?.sortOrder || 'desc',
|
|
698
|
+
validSortFields: ['id', 'created_at', 'action'],
|
|
699
|
+
where: { target_user_id: userId },
|
|
700
|
+
},
|
|
701
|
+
);
|
|
702
|
+
|
|
703
|
+
const logs = (paginated.data || []) as UserChangeLog[];
|
|
704
|
+
const actorIds = [
|
|
705
|
+
...new Set(
|
|
706
|
+
logs
|
|
707
|
+
.map((log) => log.actor_user_id)
|
|
708
|
+
.filter((id): id is number => Boolean(id)),
|
|
709
|
+
),
|
|
710
|
+
];
|
|
711
|
+
|
|
712
|
+
const actors: UserChangeLogActor[] = actorIds.length
|
|
713
|
+
? await this.prismaService.user.findMany({
|
|
714
|
+
where: { id: { in: actorIds } },
|
|
715
|
+
select: {
|
|
716
|
+
id: true,
|
|
717
|
+
name: true,
|
|
718
|
+
user_identifier: {
|
|
719
|
+
where: { type: IDENTIFIER_TYPE.EMAIL, enabled: true },
|
|
720
|
+
select: { value: true },
|
|
721
|
+
take: 1,
|
|
722
|
+
},
|
|
723
|
+
},
|
|
724
|
+
})
|
|
725
|
+
: [];
|
|
726
|
+
|
|
727
|
+
const actorMap = new Map(actors.map((actor) => [actor.id, actor]));
|
|
728
|
+
|
|
729
|
+
return {
|
|
730
|
+
...paginated,
|
|
731
|
+
data: logs.map((log) => {
|
|
732
|
+
const actor = log.actor_user_id
|
|
733
|
+
? actorMap.get(log.actor_user_id)
|
|
734
|
+
: null;
|
|
735
|
+
|
|
736
|
+
return {
|
|
737
|
+
id: String(log.id),
|
|
738
|
+
actorUserId: log.actor_user_id ? String(log.actor_user_id) : null,
|
|
739
|
+
actorName: actor?.name ?? null,
|
|
740
|
+
actorEmail: actor?.user_identifier?.[0]?.value ?? null,
|
|
741
|
+
action: log.action,
|
|
742
|
+
summary: log.summary || '',
|
|
743
|
+
beforeData: log.before_data || '',
|
|
744
|
+
afterData: log.after_data || '',
|
|
745
|
+
ipAddress: log.ip_address || '',
|
|
746
|
+
createdAt: log.created_at?.toISOString?.() || null,
|
|
747
|
+
};
|
|
748
|
+
}),
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
|
|
577
752
|
// ==========================================
|
|
578
753
|
// Private Helper Methods
|
|
579
754
|
// ==========================================
|
|
@@ -761,4 +936,4 @@ export class UserService {
|
|
|
761
936
|
},
|
|
762
937
|
});
|
|
763
938
|
}
|
|
764
|
-
}
|
|
939
|
+
}
|
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
import { DeleteDTO, Role } from '@hed-hog/api';
|
|
2
|
-
import { Pagination } from '@hed-hog/api-pagination';
|
|
3
|
-
import {
|
|
4
|
-
Body,
|
|
5
|
-
Controller,
|
|
6
|
-
Delete,
|
|
7
|
-
Get,
|
|
8
|
-
Param,
|
|
9
|
-
ParseIntPipe,
|
|
10
|
-
Patch,
|
|
11
|
-
Post,
|
|
12
|
-
} from '@nestjs/common';
|
|
13
|
-
import { IntegrationEventCatalogService } from './integration-event-catalog.service';
|
|
14
|
-
|
|
15
|
-
@Role()
|
|
16
|
-
@Controller('integration-event-catalog')
|
|
17
|
-
export class IntegrationEventCatalogController {
|
|
18
|
-
constructor(private readonly service: IntegrationEventCatalogService) {}
|
|
19
|
-
|
|
20
|
-
@Get()
|
|
21
|
-
list(@Pagination() paginationParams) {
|
|
22
|
-
return this.service.list(paginationParams);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
@Get('options')
|
|
26
|
-
options() {
|
|
27
|
-
return this.service.options();
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
@Get(':id')
|
|
31
|
-
get(@Param('id', ParseIntPipe) id: number) {
|
|
32
|
-
return this.service.get(id);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
@Post()
|
|
36
|
-
create(@Body() data: any) {
|
|
37
|
-
return this.service.create(data);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
@Patch(':id')
|
|
41
|
-
update(@Param('id', ParseIntPipe) id: number, @Body() data: any) {
|
|
42
|
-
return this.service.update(id, data);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
@Delete()
|
|
46
|
-
delete(@Body() data: DeleteDTO) {
|
|
47
|
-
return this.service.delete(data);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
1
|
+
import { DeleteDTO, Role } from '@hed-hog/api';
|
|
2
|
+
import { Pagination } from '@hed-hog/api-pagination';
|
|
3
|
+
import {
|
|
4
|
+
Body,
|
|
5
|
+
Controller,
|
|
6
|
+
Delete,
|
|
7
|
+
Get,
|
|
8
|
+
Param,
|
|
9
|
+
ParseIntPipe,
|
|
10
|
+
Patch,
|
|
11
|
+
Post,
|
|
12
|
+
} from '@nestjs/common';
|
|
13
|
+
import { IntegrationEventCatalogService } from './integration-event-catalog.service';
|
|
14
|
+
|
|
15
|
+
@Role()
|
|
16
|
+
@Controller('integration-event-catalog')
|
|
17
|
+
export class IntegrationEventCatalogController {
|
|
18
|
+
constructor(private readonly service: IntegrationEventCatalogService) {}
|
|
19
|
+
|
|
20
|
+
@Get()
|
|
21
|
+
list(@Pagination() paginationParams) {
|
|
22
|
+
return this.service.list(paginationParams);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@Get('options')
|
|
26
|
+
options() {
|
|
27
|
+
return this.service.options();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@Get(':id')
|
|
31
|
+
get(@Param('id', ParseIntPipe) id: number) {
|
|
32
|
+
return this.service.get(id);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@Post()
|
|
36
|
+
create(@Body() data: any) {
|
|
37
|
+
return this.service.create(data);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@Patch(':id')
|
|
41
|
+
update(@Param('id', ParseIntPipe) id: number, @Body() data: any) {
|
|
42
|
+
return this.service.update(id, data);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@Delete()
|
|
46
|
+
delete(@Body() data: DeleteDTO) {
|
|
47
|
+
return this.service.delete(data);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
@@ -1,117 +1,117 @@
|
|
|
1
|
-
import { DeleteDTO } from '@hed-hog/api';
|
|
2
|
-
import { PaginationDTO, PaginationService } from '@hed-hog/api-pagination';
|
|
3
|
-
import { PrismaService } from '@hed-hog/api-prisma';
|
|
4
|
-
import {
|
|
5
|
-
BadRequestException,
|
|
6
|
-
Injectable,
|
|
7
|
-
NotFoundException,
|
|
8
|
-
} from '@nestjs/common';
|
|
9
|
-
|
|
10
|
-
@Injectable()
|
|
11
|
-
export class IntegrationEventCatalogService {
|
|
12
|
-
constructor(
|
|
13
|
-
private readonly prisma: PrismaService,
|
|
14
|
-
private readonly pagination: PaginationService,
|
|
15
|
-
) {}
|
|
16
|
-
|
|
17
|
-
private get delegate() {
|
|
18
|
-
return (this.prisma as any).integration_event_catalog;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
async list(paginationParams: PaginationDTO) {
|
|
22
|
-
const fields = ['slug', 'name', 'module', 'description', 'status'];
|
|
23
|
-
const OR = this.prisma.createInsensitiveSearch(fields, paginationParams);
|
|
24
|
-
|
|
25
|
-
return this.pagination.paginate(this.delegate, paginationParams, {
|
|
26
|
-
where: OR.length > 0 ? { OR } : {},
|
|
27
|
-
orderBy: [{ module: 'asc' }, { slug: 'asc' }],
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
async options() {
|
|
32
|
-
return this.delegate.findMany({
|
|
33
|
-
where: { status: 'active' },
|
|
34
|
-
orderBy: [{ module: 'asc' }, { slug: 'asc' }],
|
|
35
|
-
select: {
|
|
36
|
-
id: true,
|
|
37
|
-
slug: true,
|
|
38
|
-
name: true,
|
|
39
|
-
module: true,
|
|
40
|
-
description: true,
|
|
41
|
-
},
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
async get(id: number) {
|
|
46
|
-
const record = await this.delegate.findUnique({ where: { id } });
|
|
47
|
-
if (!record) {
|
|
48
|
-
throw new NotFoundException(`Event catalog item with id ${id} not found.`);
|
|
49
|
-
}
|
|
50
|
-
return record;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
async create(data: any) {
|
|
54
|
-
await this.ensureSlugAvailable(data.slug);
|
|
55
|
-
return this.delegate.create({
|
|
56
|
-
data: this.normalize(data),
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
async update(id: number, data: any) {
|
|
61
|
-
const existing = await this.get(id);
|
|
62
|
-
if (data.slug && data.slug !== existing.slug) {
|
|
63
|
-
await this.ensureSlugAvailable(data.slug);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return this.delegate.update({
|
|
67
|
-
where: { id },
|
|
68
|
-
data: this.normalize(data, true),
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
async delete(data: DeleteDTO) {
|
|
73
|
-
if (!data.ids?.length) {
|
|
74
|
-
throw new BadRequestException('You must select at least one item to delete.');
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return this.delegate.deleteMany({
|
|
78
|
-
where: { id: { in: data.ids } },
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
private async ensureSlugAvailable(slug: string) {
|
|
83
|
-
const existing = await this.delegate.findUnique({
|
|
84
|
-
where: { slug: String(slug || '').trim() },
|
|
85
|
-
select: { id: true },
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
if (existing) {
|
|
89
|
-
throw new BadRequestException(`Event "${slug}" already exists.`);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
private normalize(data: any, partial = false) {
|
|
94
|
-
const normalized: Record<string, any> = {};
|
|
95
|
-
const assign = (key: string, value: any) => {
|
|
96
|
-
if (!partial || value !== undefined) {
|
|
97
|
-
normalized[key] = value;
|
|
98
|
-
}
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
assign('slug', String(data.slug ?? '').trim());
|
|
102
|
-
assign('name', String(data.name ?? '').trim());
|
|
103
|
-
assign('module', String(data.module ?? '').trim());
|
|
104
|
-
assign(
|
|
105
|
-
'description',
|
|
106
|
-
data.description === undefined || data.description === null
|
|
107
|
-
? partial
|
|
108
|
-
? undefined
|
|
109
|
-
: null
|
|
110
|
-
: String(data.description).trim() || null,
|
|
111
|
-
);
|
|
112
|
-
assign('status', data.status === 'inactive' ? 'inactive' : 'active');
|
|
113
|
-
|
|
114
|
-
return normalized;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
1
|
+
import { DeleteDTO } from '@hed-hog/api';
|
|
2
|
+
import { PaginationDTO, PaginationService } from '@hed-hog/api-pagination';
|
|
3
|
+
import { PrismaService } from '@hed-hog/api-prisma';
|
|
4
|
+
import {
|
|
5
|
+
BadRequestException,
|
|
6
|
+
Injectable,
|
|
7
|
+
NotFoundException,
|
|
8
|
+
} from '@nestjs/common';
|
|
9
|
+
|
|
10
|
+
@Injectable()
|
|
11
|
+
export class IntegrationEventCatalogService {
|
|
12
|
+
constructor(
|
|
13
|
+
private readonly prisma: PrismaService,
|
|
14
|
+
private readonly pagination: PaginationService,
|
|
15
|
+
) {}
|
|
16
|
+
|
|
17
|
+
private get delegate() {
|
|
18
|
+
return (this.prisma as any).integration_event_catalog;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async list(paginationParams: PaginationDTO) {
|
|
22
|
+
const fields = ['slug', 'name', 'module', 'description', 'status'];
|
|
23
|
+
const OR = this.prisma.createInsensitiveSearch(fields, paginationParams);
|
|
24
|
+
|
|
25
|
+
return this.pagination.paginate(this.delegate, paginationParams, {
|
|
26
|
+
where: OR.length > 0 ? { OR } : {},
|
|
27
|
+
orderBy: [{ module: 'asc' }, { slug: 'asc' }],
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async options() {
|
|
32
|
+
return this.delegate.findMany({
|
|
33
|
+
where: { status: 'active' },
|
|
34
|
+
orderBy: [{ module: 'asc' }, { slug: 'asc' }],
|
|
35
|
+
select: {
|
|
36
|
+
id: true,
|
|
37
|
+
slug: true,
|
|
38
|
+
name: true,
|
|
39
|
+
module: true,
|
|
40
|
+
description: true,
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async get(id: number) {
|
|
46
|
+
const record = await this.delegate.findUnique({ where: { id } });
|
|
47
|
+
if (!record) {
|
|
48
|
+
throw new NotFoundException(`Event catalog item with id ${id} not found.`);
|
|
49
|
+
}
|
|
50
|
+
return record;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async create(data: any) {
|
|
54
|
+
await this.ensureSlugAvailable(data.slug);
|
|
55
|
+
return this.delegate.create({
|
|
56
|
+
data: this.normalize(data),
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async update(id: number, data: any) {
|
|
61
|
+
const existing = await this.get(id);
|
|
62
|
+
if (data.slug && data.slug !== existing.slug) {
|
|
63
|
+
await this.ensureSlugAvailable(data.slug);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return this.delegate.update({
|
|
67
|
+
where: { id },
|
|
68
|
+
data: this.normalize(data, true),
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async delete(data: DeleteDTO) {
|
|
73
|
+
if (!data.ids?.length) {
|
|
74
|
+
throw new BadRequestException('You must select at least one item to delete.');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return this.delegate.deleteMany({
|
|
78
|
+
where: { id: { in: data.ids } },
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
private async ensureSlugAvailable(slug: string) {
|
|
83
|
+
const existing = await this.delegate.findUnique({
|
|
84
|
+
where: { slug: String(slug || '').trim() },
|
|
85
|
+
select: { id: true },
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
if (existing) {
|
|
89
|
+
throw new BadRequestException(`Event "${slug}" already exists.`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private normalize(data: any, partial = false) {
|
|
94
|
+
const normalized: Record<string, any> = {};
|
|
95
|
+
const assign = (key: string, value: any) => {
|
|
96
|
+
if (!partial || value !== undefined) {
|
|
97
|
+
normalized[key] = value;
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
assign('slug', String(data.slug ?? '').trim());
|
|
102
|
+
assign('name', String(data.name ?? '').trim());
|
|
103
|
+
assign('module', String(data.module ?? '').trim());
|
|
104
|
+
assign(
|
|
105
|
+
'description',
|
|
106
|
+
data.description === undefined || data.description === null
|
|
107
|
+
? partial
|
|
108
|
+
? undefined
|
|
109
|
+
: null
|
|
110
|
+
: String(data.description).trim() || null,
|
|
111
|
+
);
|
|
112
|
+
assign('status', data.status === 'inactive' ? 'inactive' : 'active');
|
|
113
|
+
|
|
114
|
+
return normalized;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|