@hed-hog/core 0.0.300 → 0.0.302
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/ai/ai.service.d.ts +13 -2
- package/dist/ai/ai.service.d.ts.map +1 -1
- package/dist/ai/ai.service.js +104 -2
- package/dist/ai/ai.service.js.map +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts +26 -9
- package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts.map +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.controller.js +11 -5
- package/dist/dashboard/dashboard-core/dashboard-core.controller.js.map +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts +34 -10
- package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts.map +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.service.js +196 -69
- package/dist/dashboard/dashboard-core/dashboard-core.service.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/integration/services/integration-link.service.d.ts +5 -1
- package/dist/integration/services/integration-link.service.d.ts.map +1 -1
- package/dist/integration/services/integration-link.service.js +141 -53
- package/dist/integration/services/integration-link.service.js.map +1 -1
- package/dist/mail/mail.service.d.ts +9 -2
- package/dist/mail/mail.service.d.ts.map +1 -1
- package/dist/mail/mail.service.js +56 -4
- package/dist/mail/mail.service.js.map +1 -1
- package/dist/setting/setting.service.d.ts +6 -1
- package/dist/setting/setting.service.d.ts.map +1 -1
- package/dist/setting/setting.service.js +188 -15
- package/dist/setting/setting.service.js.map +1 -1
- package/hedhog/data/setting_group.yaml +28 -0
- package/hedhog/frontend/app/dashboard/dashboard-home-tabs.tsx.ejs +305 -75
- package/hedhog/frontend/messages/en.json +15 -3
- package/hedhog/frontend/messages/pt.json +15 -3
- package/package.json +5 -5
- package/src/ai/ai.service.ts +129 -1
- package/src/dashboard/dashboard-core/dashboard-core.controller.ts +9 -2
- package/src/dashboard/dashboard-core/dashboard-core.service.ts +276 -75
- package/src/index.ts +7 -6
- package/src/integration/services/integration-link.service.ts +190 -55
- package/src/mail/mail.service.ts +67 -3
- package/src/setting/setting.service.ts +222 -15
|
@@ -11,11 +11,21 @@ import {
|
|
|
11
11
|
NotFoundException
|
|
12
12
|
} from '@nestjs/common';
|
|
13
13
|
import * as pako from 'pako';
|
|
14
|
+
import { IntegrationDeveloperApiService } from '../integration/services/integration-developer-api.service';
|
|
14
15
|
import { CreateDTO } from './dto/create.dto';
|
|
15
16
|
import { DeleteDTO } from './dto/delete.dto';
|
|
16
17
|
import { SettingDTO } from './dto/setting.dto';
|
|
17
18
|
import { UpdateDTO } from './dto/update.dto';
|
|
18
19
|
|
|
20
|
+
type SettingChangeEventInput = {
|
|
21
|
+
settingId?: number | null;
|
|
22
|
+
slug: string;
|
|
23
|
+
type?: string | null;
|
|
24
|
+
oldValue?: any;
|
|
25
|
+
newValue?: any;
|
|
26
|
+
source: string;
|
|
27
|
+
};
|
|
28
|
+
|
|
19
29
|
@Injectable()
|
|
20
30
|
export class SettingService {
|
|
21
31
|
|
|
@@ -29,6 +39,8 @@ export class SettingService {
|
|
|
29
39
|
private readonly paginationService: PaginationService,
|
|
30
40
|
@Inject(forwardRef(() => LocaleService))
|
|
31
41
|
private readonly localeService: LocaleService,
|
|
42
|
+
@Inject(forwardRef(() => IntegrationDeveloperApiService))
|
|
43
|
+
private readonly integrationApi: IntegrationDeveloperApiService,
|
|
32
44
|
) { }
|
|
33
45
|
|
|
34
46
|
async exportSettings(includeSecrets = false): Promise<Buffer> {
|
|
@@ -108,32 +120,47 @@ export class SettingService {
|
|
|
108
120
|
id: true,
|
|
109
121
|
slug: true,
|
|
110
122
|
type: true,
|
|
123
|
+
value: true,
|
|
111
124
|
},
|
|
112
125
|
});
|
|
113
126
|
|
|
114
127
|
const transaction = [];
|
|
128
|
+
const changedSettings: SettingChangeEventInput[] = [];
|
|
129
|
+
|
|
115
130
|
for (const setting of settings) {
|
|
116
131
|
const existingSetting = existingSettings.find((s) => s.slug === setting.slug);
|
|
117
132
|
if (existingSetting) {
|
|
133
|
+
const nextValue = this.setValueFormattedByType(existingSetting.type, setting.value);
|
|
134
|
+
|
|
118
135
|
transaction.push(
|
|
119
136
|
this.prismaService.setting.update({
|
|
120
137
|
where: {
|
|
121
138
|
id: existingSetting.id,
|
|
122
139
|
},
|
|
123
140
|
data: {
|
|
124
|
-
value:
|
|
141
|
+
value: nextValue,
|
|
125
142
|
},
|
|
126
143
|
}),
|
|
127
144
|
);
|
|
145
|
+
|
|
146
|
+
changedSettings.push({
|
|
147
|
+
settingId: existingSetting.id,
|
|
148
|
+
slug: existingSetting.slug,
|
|
149
|
+
type: existingSetting.type,
|
|
150
|
+
oldValue: existingSetting.value,
|
|
151
|
+
newValue: nextValue,
|
|
152
|
+
source: 'confirmImport',
|
|
153
|
+
});
|
|
128
154
|
}
|
|
129
155
|
}
|
|
130
156
|
|
|
131
157
|
if (transaction.length > 0) {
|
|
132
158
|
await this.prismaService.$transaction(transaction);
|
|
159
|
+
await this.emitSettingChangedEvents(changedSettings);
|
|
133
160
|
}
|
|
134
161
|
|
|
135
|
-
return {
|
|
136
|
-
success: true,
|
|
162
|
+
return {
|
|
163
|
+
success: true,
|
|
137
164
|
updatedCount: transaction.length,
|
|
138
165
|
};
|
|
139
166
|
}
|
|
@@ -178,6 +205,8 @@ export class SettingService {
|
|
|
178
205
|
'password-min-uppercase',
|
|
179
206
|
'password-min-length',
|
|
180
207
|
'mfa-email-code-length',
|
|
208
|
+
'ai-openai-api-key-enabled',
|
|
209
|
+
'ai-gemini-api-key-enabled',
|
|
181
210
|
'date-format',
|
|
182
211
|
'time-format',
|
|
183
212
|
'timezone',
|
|
@@ -233,21 +262,52 @@ export class SettingService {
|
|
|
233
262
|
async setManySettings(data: SettingDTO) {
|
|
234
263
|
this.clearCache();
|
|
235
264
|
const transaction = [];
|
|
265
|
+
const existingSettings = await this.prismaService.setting.findMany({
|
|
266
|
+
where: {
|
|
267
|
+
slug: {
|
|
268
|
+
in: data.setting.map((item) => item.slug),
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
select: {
|
|
272
|
+
id: true,
|
|
273
|
+
slug: true,
|
|
274
|
+
type: true,
|
|
275
|
+
value: true,
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
const changedSettings: SettingChangeEventInput[] = [];
|
|
236
279
|
|
|
237
280
|
for (const { slug, value } of data.setting) {
|
|
281
|
+
const existingSetting = existingSettings.find((item) => item.slug === slug);
|
|
282
|
+
if (!existingSetting) {
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const nextValue = this.setValueFormattedByType(existingSetting.type, value);
|
|
287
|
+
|
|
238
288
|
transaction.push(
|
|
239
289
|
this.prismaService.setting.updateMany({
|
|
240
290
|
where: {
|
|
241
291
|
slug,
|
|
242
292
|
},
|
|
243
293
|
data: {
|
|
244
|
-
value,
|
|
294
|
+
value: nextValue,
|
|
245
295
|
},
|
|
246
296
|
}),
|
|
247
297
|
);
|
|
298
|
+
|
|
299
|
+
changedSettings.push({
|
|
300
|
+
settingId: existingSetting.id,
|
|
301
|
+
slug: existingSetting.slug,
|
|
302
|
+
type: existingSetting.type,
|
|
303
|
+
oldValue: existingSetting.value,
|
|
304
|
+
newValue: nextValue,
|
|
305
|
+
source: 'setManySettings',
|
|
306
|
+
});
|
|
248
307
|
}
|
|
249
308
|
|
|
250
309
|
await this.prismaService.$transaction(transaction);
|
|
310
|
+
await this.emitSettingChangedEvents(changedSettings);
|
|
251
311
|
return { success: true };
|
|
252
312
|
}
|
|
253
313
|
|
|
@@ -477,7 +537,7 @@ export class SettingService {
|
|
|
477
537
|
|
|
478
538
|
async create(data: CreateDTO, _locale: string) {
|
|
479
539
|
this.clearCache();
|
|
480
|
-
|
|
540
|
+
const createdSetting = await this.prismaService.setting.create({
|
|
481
541
|
data: {
|
|
482
542
|
slug: data.slug,
|
|
483
543
|
type: data.type as any,
|
|
@@ -490,21 +550,42 @@ export class SettingService {
|
|
|
490
550
|
},
|
|
491
551
|
},
|
|
492
552
|
});
|
|
553
|
+
|
|
554
|
+
await this.emitSettingChangedEvents([
|
|
555
|
+
{
|
|
556
|
+
settingId: createdSetting.id,
|
|
557
|
+
slug: createdSetting.slug,
|
|
558
|
+
type: createdSetting.type,
|
|
559
|
+
oldValue: undefined,
|
|
560
|
+
newValue: createdSetting.value,
|
|
561
|
+
source: 'create',
|
|
562
|
+
},
|
|
563
|
+
]);
|
|
564
|
+
|
|
565
|
+
return createdSetting;
|
|
493
566
|
}
|
|
494
567
|
|
|
495
568
|
async update({ id, data }: { id: number; data: UpdateDTO }) {
|
|
496
569
|
this.clearCache();
|
|
497
570
|
|
|
498
|
-
const
|
|
571
|
+
const currentSetting = await this.prismaService.setting.findFirst({
|
|
499
572
|
where: {
|
|
500
573
|
id,
|
|
501
574
|
},
|
|
575
|
+
select: {
|
|
576
|
+
id: true,
|
|
577
|
+
slug: true,
|
|
578
|
+
type: true,
|
|
579
|
+
value: true,
|
|
580
|
+
},
|
|
502
581
|
});
|
|
503
582
|
|
|
504
|
-
if (!type) {
|
|
583
|
+
if (!currentSetting?.type) {
|
|
505
584
|
throw new NotFoundException(`Setting not found.`);
|
|
506
585
|
}
|
|
507
586
|
|
|
587
|
+
const { type } = currentSetting;
|
|
588
|
+
|
|
508
589
|
// Build update data object with only provided fields
|
|
509
590
|
const updateData: any = {};
|
|
510
591
|
|
|
@@ -541,6 +622,17 @@ export class SettingService {
|
|
|
541
622
|
data: updateData,
|
|
542
623
|
});
|
|
543
624
|
|
|
625
|
+
await this.emitSettingChangedEvents([
|
|
626
|
+
{
|
|
627
|
+
settingId: updatedSetting.id,
|
|
628
|
+
slug: updatedSetting.slug,
|
|
629
|
+
type: updatedSetting.type,
|
|
630
|
+
oldValue: currentSetting.value,
|
|
631
|
+
newValue: updatedSetting.value,
|
|
632
|
+
source: 'update',
|
|
633
|
+
},
|
|
634
|
+
]);
|
|
635
|
+
|
|
544
636
|
// Garantir que o value sobrescreve o valor original
|
|
545
637
|
const result = { ...updatedSetting };
|
|
546
638
|
result.value = this.getValueFormattedByType(updatedSetting.type, updatedSetting.value);
|
|
@@ -549,28 +641,123 @@ export class SettingService {
|
|
|
549
641
|
|
|
550
642
|
async updateFromSlug(slug: string, data: UpdateDTO) {
|
|
551
643
|
this.clearCache();
|
|
552
|
-
const
|
|
644
|
+
const currentSetting = await this.prismaService.setting.findFirst({
|
|
553
645
|
where: {
|
|
554
646
|
slug,
|
|
555
647
|
},
|
|
648
|
+
select: {
|
|
649
|
+
id: true,
|
|
650
|
+
slug: true,
|
|
651
|
+
type: true,
|
|
652
|
+
value: true,
|
|
653
|
+
},
|
|
556
654
|
});
|
|
557
655
|
|
|
558
|
-
if (!id) {
|
|
656
|
+
if (!currentSetting?.id) {
|
|
559
657
|
throw new NotFoundException(`Setting with slug ${slug} not found.`);
|
|
560
658
|
}
|
|
561
659
|
|
|
660
|
+
const nextValue = this.setValueFormattedByType(currentSetting.type, data.value);
|
|
661
|
+
|
|
562
662
|
const updatedSetting = await this.prismaService.setting.update({
|
|
563
|
-
where: { id },
|
|
663
|
+
where: { id: currentSetting.id },
|
|
564
664
|
data: {
|
|
565
|
-
value:
|
|
665
|
+
value: nextValue,
|
|
566
666
|
},
|
|
567
667
|
});
|
|
568
668
|
|
|
669
|
+
await this.emitSettingChangedEvents([
|
|
670
|
+
{
|
|
671
|
+
settingId: updatedSetting.id,
|
|
672
|
+
slug: updatedSetting.slug,
|
|
673
|
+
type: updatedSetting.type,
|
|
674
|
+
oldValue: currentSetting.value,
|
|
675
|
+
newValue: updatedSetting.value,
|
|
676
|
+
source: 'updateFromSlug',
|
|
677
|
+
},
|
|
678
|
+
]);
|
|
679
|
+
|
|
569
680
|
const result = { ...updatedSetting };
|
|
570
681
|
result.value = this.getValueFormattedByType(updatedSetting.type, updatedSetting.value);
|
|
571
682
|
return result;
|
|
572
683
|
}
|
|
573
684
|
|
|
685
|
+
private normalizeValueForComparison(value: any) {
|
|
686
|
+
if (value === undefined) {
|
|
687
|
+
return '__undefined__';
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
if (value === null) {
|
|
691
|
+
return null;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
if (typeof value === 'string') {
|
|
695
|
+
return value.trim();
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
try {
|
|
699
|
+
return JSON.stringify(value);
|
|
700
|
+
} catch {
|
|
701
|
+
return String(value);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
private isValueDefined(value: any) {
|
|
706
|
+
if (value === undefined || value === null) {
|
|
707
|
+
return false;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
if (typeof value === 'string') {
|
|
711
|
+
return value.trim().length > 0;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
if (Array.isArray(value)) {
|
|
715
|
+
return value.length > 0;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
return true;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
private async emitSettingChangedEvents(changes: SettingChangeEventInput[]) {
|
|
722
|
+
const events = changes
|
|
723
|
+
.filter((change) => Boolean(change.slug))
|
|
724
|
+
.filter(
|
|
725
|
+
(change) =>
|
|
726
|
+
this.normalizeValueForComparison(change.oldValue) !==
|
|
727
|
+
this.normalizeValueForComparison(change.newValue),
|
|
728
|
+
)
|
|
729
|
+
.map((change) => ({
|
|
730
|
+
eventName: 'core.setting.changed',
|
|
731
|
+
sourceModule: 'core',
|
|
732
|
+
aggregateType: 'setting',
|
|
733
|
+
aggregateId: String(change.settingId ?? change.slug),
|
|
734
|
+
payload: {
|
|
735
|
+
settingId: change.settingId ?? null,
|
|
736
|
+
slug: change.slug,
|
|
737
|
+
type: change.type ?? null,
|
|
738
|
+
source: change.source,
|
|
739
|
+
hasValue: this.isValueDefined(change.newValue),
|
|
740
|
+
},
|
|
741
|
+
metadata: {
|
|
742
|
+
settingSlug: change.slug,
|
|
743
|
+
source: change.source,
|
|
744
|
+
},
|
|
745
|
+
}));
|
|
746
|
+
|
|
747
|
+
if (events.length === 0) {
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
try {
|
|
752
|
+
await this.integrationApi.publishEvents(events);
|
|
753
|
+
} catch (error) {
|
|
754
|
+
this.logger.error(
|
|
755
|
+
`Failed to publish ${events.length} setting change event(s).`,
|
|
756
|
+
error instanceof Error ? error.stack : String(error),
|
|
757
|
+
);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
|
|
574
761
|
setValueFormattedByType(type: string, value: any) {
|
|
575
762
|
switch (type) {
|
|
576
763
|
case 'boolean':
|
|
@@ -829,23 +1016,43 @@ export class SettingService {
|
|
|
829
1016
|
async setValue(slug: string, value: string) {
|
|
830
1017
|
this.clearCache();
|
|
831
1018
|
|
|
832
|
-
const
|
|
1019
|
+
const currentSetting = await this.prismaService.setting.findFirst({
|
|
833
1020
|
where: {
|
|
834
1021
|
slug,
|
|
835
1022
|
},
|
|
1023
|
+
select: {
|
|
1024
|
+
id: true,
|
|
1025
|
+
slug: true,
|
|
1026
|
+
type: true,
|
|
1027
|
+
value: true,
|
|
1028
|
+
},
|
|
836
1029
|
});
|
|
837
1030
|
|
|
838
|
-
if (!id) {
|
|
1031
|
+
if (!currentSetting?.id) {
|
|
839
1032
|
throw new NotFoundException(`Setting with slug ${slug} not found.`);
|
|
840
1033
|
}
|
|
841
1034
|
|
|
842
|
-
|
|
1035
|
+
const nextValue = this.setValueFormattedByType(currentSetting.type, value);
|
|
1036
|
+
const result = await this.prismaService.setting.updateMany({
|
|
843
1037
|
where: {
|
|
844
1038
|
slug,
|
|
845
1039
|
},
|
|
846
1040
|
data: {
|
|
847
|
-
value,
|
|
1041
|
+
value: nextValue,
|
|
848
1042
|
},
|
|
849
1043
|
});
|
|
1044
|
+
|
|
1045
|
+
await this.emitSettingChangedEvents([
|
|
1046
|
+
{
|
|
1047
|
+
settingId: currentSetting.id,
|
|
1048
|
+
slug: currentSetting.slug,
|
|
1049
|
+
type: currentSetting.type,
|
|
1050
|
+
oldValue: currentSetting.value,
|
|
1051
|
+
newValue: nextValue,
|
|
1052
|
+
source: 'setValue',
|
|
1053
|
+
},
|
|
1054
|
+
]);
|
|
1055
|
+
|
|
1056
|
+
return result;
|
|
850
1057
|
}
|
|
851
1058
|
}
|