@hed-hog/core 0.0.353 → 0.0.354

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 (62) hide show
  1. package/dist/core.module.d.ts.map +1 -1
  2. package/dist/core.module.js +7 -4
  3. package/dist/core.module.js.map +1 -1
  4. package/dist/file/file.controller.d.ts +3 -3
  5. package/dist/file/file.controller.d.ts.map +1 -1
  6. package/dist/file/file.controller.js +7 -3
  7. package/dist/file/file.controller.js.map +1 -1
  8. package/dist/file/file.service.d.ts +1 -0
  9. package/dist/file/file.service.d.ts.map +1 -1
  10. package/dist/file/file.service.js +10 -1
  11. package/dist/file/file.service.js.map +1 -1
  12. package/dist/integration/services/outbox-polling.coordinator.d.ts.map +1 -1
  13. package/dist/integration/services/outbox-polling.coordinator.js +0 -1
  14. package/dist/integration/services/outbox-polling.coordinator.js.map +1 -1
  15. package/dist/locale/dto/create-locale.dto.d.ts +7 -0
  16. package/dist/locale/dto/create-locale.dto.d.ts.map +1 -0
  17. package/dist/locale/dto/create-locale.dto.js +37 -0
  18. package/dist/locale/dto/create-locale.dto.js.map +1 -0
  19. package/dist/locale/locale.controller.d.ts +26 -0
  20. package/dist/locale/locale.controller.d.ts.map +1 -0
  21. package/dist/locale/locale.controller.js +54 -0
  22. package/dist/locale/locale.controller.js.map +1 -0
  23. package/dist/locale/locale.module.d.ts +3 -0
  24. package/dist/locale/locale.module.d.ts.map +1 -0
  25. package/dist/locale/locale.module.js +31 -0
  26. package/dist/locale/locale.module.js.map +1 -0
  27. package/dist/locale/locale.service.d.ts +27 -0
  28. package/dist/locale/locale.service.d.ts.map +1 -0
  29. package/dist/locale/locale.service.js +55 -0
  30. package/dist/locale/locale.service.js.map +1 -0
  31. package/dist/mail-profile/mail-profile.controller.d.ts +3 -3
  32. package/dist/mail-profile/mail-profile.service.d.ts +3 -3
  33. package/dist/mail-sent/mail-sent.controller.d.ts +3 -3
  34. package/dist/mail-sent/mail-sent.service.d.ts +3 -3
  35. package/dist/notification/notification.service.d.ts +1 -0
  36. package/dist/notification/notification.service.d.ts.map +1 -1
  37. package/dist/notification/notification.service.js +36 -4
  38. package/dist/notification/notification.service.js.map +1 -1
  39. package/dist/setting/setting.service.d.ts.map +1 -1
  40. package/dist/setting/setting.service.js +2 -0
  41. package/dist/setting/setting.service.js.map +1 -1
  42. package/hedhog/data/route.yaml +24 -0
  43. package/hedhog/data/setting_group.yaml +20 -2
  44. package/hedhog/frontend/app/configurations/[slug]/components/setting-field.tsx.ejs +22 -22
  45. package/hedhog/frontend/app/dashboard/components/add-widget-selector-dialog.tsx.ejs +3 -3
  46. package/hedhog/frontend/app/integrations-webhooks/page.tsx.ejs +15 -21
  47. package/hedhog/frontend/app/mail/log/page.tsx.ejs +3 -3
  48. package/hedhog/frontend/app/mail/profiles/page.tsx.ejs +3 -6
  49. package/hedhog/frontend/app/mail/template/page.tsx.ejs +3 -6
  50. package/hedhog/frontend/messages/en.json +1 -0
  51. package/hedhog/frontend/messages/pt.json +1 -0
  52. package/package.json +3 -3
  53. package/src/core.module.ts +7 -4
  54. package/src/file/file.controller.ts +11 -6
  55. package/src/file/file.service.ts +15 -1
  56. package/src/integration/services/outbox-polling.coordinator.ts +0 -3
  57. package/src/locale/dto/create-locale.dto.ts +19 -0
  58. package/src/locale/locale.controller.ts +35 -0
  59. package/src/locale/locale.module.ts +18 -0
  60. package/src/locale/locale.service.ts +48 -0
  61. package/src/notification/notification.service.ts +51 -12
  62. package/src/setting/setting.service.ts +8 -6
@@ -32,9 +32,9 @@ import {
32
32
  SelectTrigger,
33
33
  SelectValue,
34
34
  } from '@/components/ui/select';
35
+ import { ResizableSheetContent } from '@/components/ui/resizable-sheet-content';
35
36
  import {
36
37
  Sheet,
37
- SheetContent,
38
38
  SheetDescription,
39
39
  SheetHeader,
40
40
  SheetTitle,
@@ -1707,7 +1707,7 @@ export default function IntegrationsWebhooksPage() {
1707
1707
  </Tabs>
1708
1708
 
1709
1709
  <Sheet open={catalogOpen} onOpenChange={setCatalogOpen}>
1710
- <SheetContent className="w-full overflow-y-auto sm:max-w-2xl">
1710
+ <ResizableSheetContent sheetId="webhooks-catalog-form" defaultWidth={672}>
1711
1711
  <SheetHeader>
1712
1712
  <SheetTitle>
1713
1713
  {catalogEdit ? t('editEvent') : t('newEvent')}
@@ -1764,7 +1764,7 @@ export default function IntegrationsWebhooksPage() {
1764
1764
  t={t}
1765
1765
  />
1766
1766
  </div>
1767
- </SheetContent>
1767
+ </ResizableSheetContent>
1768
1768
  </Sheet>
1769
1769
 
1770
1770
  <Sheet
@@ -1782,7 +1782,7 @@ export default function IntegrationsWebhooksPage() {
1782
1782
  }
1783
1783
  }}
1784
1784
  >
1785
- <SheetContent className="w-full overflow-y-auto sm:max-w-5xl">
1785
+ <ResizableSheetContent sheetId="webhooks-incoming-form" defaultWidth={1024}>
1786
1786
  <SheetHeader>
1787
1787
  <SheetTitle>
1788
1788
  {incomingEdit ? t('editIncoming') : t('newIncoming')}
@@ -2272,10 +2272,7 @@ export default function IntegrationsWebhooksPage() {
2272
2272
  }
2273
2273
  }}
2274
2274
  >
2275
- <SheetContent
2276
- side="right"
2277
- className="w-full overflow-y-auto sm:max-w-3xl"
2278
- >
2275
+ <ResizableSheetContent sheetId="webhooks-action-form" defaultWidth={768}>
2279
2276
  <SheetHeader>
2280
2277
  <SheetTitle>
2281
2278
  {actionEdit ? t('updated') : t('addAction')}
@@ -2659,13 +2656,13 @@ export default function IntegrationsWebhooksPage() {
2659
2656
  t={t}
2660
2657
  />
2661
2658
  </div>
2662
- </SheetContent>
2659
+ </ResizableSheetContent>
2663
2660
  </Sheet>
2664
- </SheetContent>
2661
+ </ResizableSheetContent>
2665
2662
  </Sheet>
2666
2663
 
2667
2664
  <Sheet open={eventWebhookOpen} onOpenChange={setEventWebhookOpen}>
2668
- <SheetContent className="w-full overflow-y-auto sm:max-w-5xl">
2665
+ <ResizableSheetContent sheetId="webhooks-event-webhook-form" defaultWidth={1024}>
2669
2666
  <SheetHeader>
2670
2667
  <SheetTitle>
2671
2668
  {eventWebhookEdit ? t('editEventWebhook') : t('newEventWebhook')}
@@ -2844,7 +2841,7 @@ export default function IntegrationsWebhooksPage() {
2844
2841
  t={t}
2845
2842
  />
2846
2843
  </div>
2847
- </SheetContent>
2844
+ </ResizableSheetContent>
2848
2845
  </Sheet>
2849
2846
 
2850
2847
  <Sheet
@@ -2859,7 +2856,7 @@ export default function IntegrationsWebhooksPage() {
2859
2856
  }
2860
2857
  }}
2861
2858
  >
2862
- <SheetContent className="w-full overflow-y-auto sm:max-w-4xl">
2859
+ <ResizableSheetContent sheetId="webhooks-mail-profile-editor" defaultWidth={896}>
2863
2860
  <SheetHeader>
2864
2861
  <SheetTitle>Editar perfil de e-mail</SheetTitle>
2865
2862
  <SheetDescription>
@@ -3130,7 +3127,7 @@ export default function IntegrationsWebhooksPage() {
3130
3127
  )}
3131
3128
  </div>
3132
3129
  </div>
3133
- </SheetContent>
3130
+ </ResizableSheetContent>
3134
3131
  </Sheet>
3135
3132
 
3136
3133
  <Sheet
@@ -3143,7 +3140,7 @@ export default function IntegrationsWebhooksPage() {
3143
3140
  }
3144
3141
  }}
3145
3142
  >
3146
- <SheetContent className="w-full overflow-y-auto sm:max-w-4xl">
3143
+ <ResizableSheetContent sheetId="webhooks-mail-template-editor" defaultWidth={896}>
3147
3144
  <SheetHeader>
3148
3145
  <SheetTitle>Editar template de e-mail</SheetTitle>
3149
3146
  <SheetDescription>
@@ -3230,17 +3227,14 @@ export default function IntegrationsWebhooksPage() {
3230
3227
  t={t}
3231
3228
  />
3232
3229
  </div>
3233
- </SheetContent>
3230
+ </ResizableSheetContent>
3234
3231
  </Sheet>
3235
3232
 
3236
3233
  <Sheet
3237
3234
  open={Boolean(selectedLog)}
3238
3235
  onOpenChange={(open) => !open && setSelectedLog(null)}
3239
3236
  >
3240
- <SheetContent
3241
- side="right"
3242
- className="w-full overflow-y-auto sm:max-w-3xl"
3243
- >
3237
+ <ResizableSheetContent sheetId="webhooks-log-detail" defaultWidth={768}>
3244
3238
  <SheetHeader>
3245
3239
  <SheetTitle>{t('logDetailTitle')}</SheetTitle>
3246
3240
  <SheetDescription>
@@ -3415,7 +3409,7 @@ export default function IntegrationsWebhooksPage() {
3415
3409
  ) : null}
3416
3410
  </div>
3417
3411
  ) : null}
3418
- </SheetContent>
3412
+ </ResizableSheetContent>
3419
3413
  </Sheet>
3420
3414
 
3421
3415
  <AlertDialog
@@ -14,9 +14,9 @@ import { Card, CardContent } from '@/components/ui/card';
14
14
  import { Input } from '@/components/ui/input';
15
15
  import { KpiCardsGrid, type KpiCardItem } from '@/components/ui/kpi-cards-grid';
16
16
  import { ScrollArea } from '@/components/ui/scroll-area';
17
+ import { ResizableSheetContent } from '@/components/ui/resizable-sheet-content';
17
18
  import {
18
19
  Sheet,
19
- SheetContent,
20
20
  SheetDescription,
21
21
  SheetHeader,
22
22
  SheetTitle,
@@ -564,7 +564,7 @@ export default function MailLogPage() {
564
564
 
565
565
  {selectedLog && (
566
566
  <Sheet open={isDetailSheetOpen} onOpenChange={setIsDetailSheetOpen}>
567
- <SheetContent className="w-full overflow-y-auto sm:max-w-2xl">
567
+ <ResizableSheetContent sheetId="mail-log-detail" defaultWidth={672}>
568
568
  <SheetHeader>
569
569
  <SheetTitle>{t('emailDetails')}</SheetTitle>
570
570
  <SheetDescription>{t('detailsDescription')}</SheetDescription>
@@ -709,7 +709,7 @@ export default function MailLogPage() {
709
709
  </div>
710
710
  </div>
711
711
  </ScrollArea>
712
- </SheetContent>
712
+ </ResizableSheetContent>
713
713
  </Sheet>
714
714
  )}
715
715
  </Page>
@@ -30,9 +30,9 @@ import {
30
30
  SelectTrigger,
31
31
  SelectValue,
32
32
  } from '@/components/ui/select';
33
+ import { ResizableSheetContent } from '@/components/ui/resizable-sheet-content';
33
34
  import {
34
35
  Sheet,
35
- SheetContent,
36
36
  SheetDescription,
37
37
  SheetHeader,
38
38
  SheetTitle,
@@ -462,10 +462,7 @@ export default function MailProfilesPage() {
462
462
  />
463
463
 
464
464
  <Sheet open={isDialogOpen} onOpenChange={setIsDialogOpen}>
465
- <SheetContent
466
- side="right"
467
- className="w-full max-w-[95vw] overflow-y-auto sm:max-w-4xl"
468
- >
465
+ <ResizableSheetContent sheetId="mail-profile-editor" defaultWidth={896}>
469
466
  <SheetHeader>
470
467
  <SheetTitle>
471
468
  {editMode ? t('editProfile') : t('newProfileTitle')}
@@ -795,7 +792,7 @@ export default function MailProfilesPage() {
795
792
  )}
796
793
  </div>
797
794
  </form>
798
- </SheetContent>
795
+ </ResizableSheetContent>
799
796
  </Sheet>
800
797
 
801
798
  <AlertDialog
@@ -39,9 +39,9 @@ import {
39
39
  SelectTrigger,
40
40
  SelectValue,
41
41
  } from '@/components/ui/select';
42
+ import { ResizableSheetContent } from '@/components/ui/resizable-sheet-content';
42
43
  import {
43
44
  Sheet,
44
- SheetContent,
45
45
  SheetDescription,
46
46
  SheetHeader,
47
47
  SheetTitle,
@@ -892,10 +892,7 @@ export default function EmailTemplatesPage() {
892
892
  />
893
893
 
894
894
  <Sheet open={isDialogOpen} onOpenChange={setIsDialogOpen}>
895
- <SheetContent
896
- side="right"
897
- className="w-full max-w-[95vw] overflow-y-auto sm:max-w-6xl"
898
- >
895
+ <ResizableSheetContent sheetId="mail-template-editor" defaultWidth={1152}>
899
896
  <SheetHeader>
900
897
  <SheetTitle>
901
898
  {editMode ? t('editTemplate') : t('newTemplateTitle')}
@@ -1066,7 +1063,7 @@ export default function EmailTemplatesPage() {
1066
1063
  </Button>
1067
1064
  </div>
1068
1065
  </form>
1069
- </SheetContent>
1066
+ </ResizableSheetContent>
1070
1067
  </Sheet>
1071
1068
 
1072
1069
  <Dialog
@@ -1941,6 +1941,7 @@
1941
1941
  "newConversation": "New Conversation",
1942
1942
  "conversations": "Conversations",
1943
1943
  "openConversations": "Open conversations",
1944
+ "closeConversations": "Close conversations",
1944
1945
  "placeholder": "Type your message... (Enter to send, Shift+Enter for new line)",
1945
1946
  "send": "Send",
1946
1947
  "sending": "Sending...",
@@ -1996,6 +1996,7 @@
1996
1996
  "newConversation": "Nova Conversa",
1997
1997
  "conversations": "Conversas",
1998
1998
  "openConversations": "Abrir conversas",
1999
+ "closeConversations": "Fechar conversas",
1999
2000
  "placeholder": "Digite sua mensagem... (Enter para enviar, Shift+Enter para nova linha)",
2000
2001
  "send": "Enviar",
2001
2002
  "sending": "Enviando...",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hed-hog/core",
3
- "version": "0.0.353",
3
+ "version": "0.0.354",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "dependencies": {
@@ -34,10 +34,10 @@
34
34
  "@modelcontextprotocol/sdk": "^1.12.0",
35
35
  "@hed-hog/api-locale": "0.0.14",
36
36
  "@hed-hog/api-mail": "0.0.9",
37
+ "@hed-hog/api-pagination": "0.0.7",
37
38
  "@hed-hog/api-types": "0.0.1",
38
39
  "@hed-hog/api": "0.0.8",
39
- "@hed-hog/api-prisma": "0.0.6",
40
- "@hed-hog/api-pagination": "0.0.7"
40
+ "@hed-hog/api-prisma": "0.0.6"
41
41
  },
42
42
  "exports": {
43
43
  ".": {
@@ -1,8 +1,5 @@
1
1
  import { LogInterceptor } from '@hed-hog/api';
2
2
  import { LocaleModule } from '@hed-hog/api-locale';
3
- import { AccessLogInterceptor } from './access-log/access-log.interceptor';
4
- import { AccessLogModule } from './access-log/access-log.module';
5
- import { AccessLogService } from './access-log/access-log.service';
6
3
  import { MailModule as MailSendModule } from '@hed-hog/api-mail';
7
4
  import { PaginationModule } from '@hed-hog/api-pagination';
8
5
  import { PrismaModule, PrismaService } from '@hed-hog/api-prisma';
@@ -10,6 +7,9 @@ import { HttpModule } from '@nestjs/axios';
10
7
  import { forwardRef, Global, Inject, MiddlewareConsumer, Module, NestModule, OnModuleInit } from '@nestjs/common';
11
8
  import { ConfigModule } from '@nestjs/config';
12
9
  import { APP_INTERCEPTOR, Reflector } from '@nestjs/core';
10
+ import { AccessLogInterceptor } from './access-log/access-log.interceptor';
11
+ import { AccessLogModule } from './access-log/access-log.module';
12
+ import { AccessLogService } from './access-log/access-log.service';
13
13
  import { AiModule } from './ai/ai.module';
14
14
  import { AuthModule } from './auth/auth.module';
15
15
  import { SystemModule } from './core/system.module';
@@ -17,6 +17,7 @@ import { DashboardModule } from './dashboard/dashboard.module';
17
17
  import { FileModule } from './file/file.module';
18
18
  import { InstallModule } from './install/install.module';
19
19
  import { IntegrationModule } from './integration/integration.module';
20
+ import { LocaleModule as CoreLocaleModule } from './locale/locale.module';
20
21
  import { MailProfileModule } from './mail-profile/mail-profile.module';
21
22
  import { MailSentModule } from './mail-sent/mail-sent.module';
22
23
  import { MailModule } from './mail/mail.module';
@@ -24,6 +25,7 @@ import { McpChatModule } from './mcp-chat/mcp-chat.module';
24
25
  import { McpModule } from './mcp/mcp.module';
25
26
  import { MenuModule } from './menu/menu.module';
26
27
  import { LocaleInjectionMiddleware } from './middlewares/locale-injection.middleware';
28
+ import { NotificationModule } from './notification/notification.module';
27
29
  import { OAuthModule } from './oauth/oauth.module';
28
30
  import { PdfModule } from './pdf/pdf.module';
29
31
  import { ProfileModule } from './profile/profile.module';
@@ -40,7 +42,6 @@ import { IsEmailWithSettingsConstraint } from './validators/is-email-with-settin
40
42
  import { IsPinCodeWithSettingConstraint } from './validators/is-pin-code-with-setting.validator';
41
43
  import { IsStrongPasswordWithSettingsConstraint } from './validators/is-strong-password-with-settings.validator';
42
44
  import { ValidatorServiceLocator } from './validators/service-locator';
43
- import { NotificationModule } from './notification/notification.module';
44
45
  import { WebhookIntegrationModule } from './webhook-integration/webhook-integration.module';
45
46
 
46
47
  @Global()
@@ -60,6 +61,7 @@ import { WebhookIntegrationModule } from './webhook-integration/webhook-integrat
60
61
  forwardRef(() => PrismaModule),
61
62
  forwardRef(() => ScreenModule),
62
63
  forwardRef(() => LocaleModule),
64
+ forwardRef(() => CoreLocaleModule),
63
65
  forwardRef(() => UserModule),
64
66
  forwardRef(() => SystemModule),
65
67
  forwardRef(() => SettingModule),
@@ -90,6 +92,7 @@ import { WebhookIntegrationModule } from './webhook-integration/webhook-integrat
90
92
  MenuModule,
91
93
  ScreenModule,
92
94
  LocaleModule,
95
+ CoreLocaleModule,
93
96
  SettingModule,
94
97
  FileModule,
95
98
  SessionModule,
@@ -12,12 +12,13 @@ import {
12
12
  ParseIntPipe,
13
13
  Post,
14
14
  Put,
15
+ Req,
15
16
  Res,
16
17
  UploadedFile,
17
18
  UseInterceptors,
18
19
  } from '@nestjs/common';
19
20
  import { FileInterceptor } from '@nestjs/platform-express';
20
- import { Response } from 'express';
21
+ import { Request, Response } from 'express';
21
22
  import { readFileSync } from 'fs';
22
23
  import Handlebars from 'handlebars';
23
24
  import { join } from 'path';
@@ -53,12 +54,16 @@ export class FileController {
53
54
  }
54
55
 
55
56
  @Put('download/:id')
56
- async getTempURL(@Param('id', ParseIntPipe) id) {
57
-
57
+ async getTempURL(
58
+ @Param('id', ParseIntPipe) id: number,
59
+ @Req() req: Request,
60
+ ) {
61
+ const file = await this.fileService.get(id);
62
+ const token = this.fileService.createDownloadToken(file.path, 3600);
63
+ const origin = `${req.protocol}://${req.get('host')}`;
64
+
58
65
  return {
59
- url: await this.fileService.tempURL(
60
- (await this.fileService.get(id)).path,
61
- ),
66
+ url: `${origin}/file/download/${encodeURIComponent(token)}`,
62
67
  };
63
68
  }
64
69
 
@@ -553,7 +553,7 @@ export class FileService implements OnModuleInit {
553
553
  for (const file of files) {
554
554
  await (await this.getProvider()).delete(file.path);
555
555
 
556
- await this.prismaService.file.delete({
556
+ await this.prismaService.file.deleteMany({
557
557
  where: {
558
558
  id: file.id,
559
559
  },
@@ -702,4 +702,18 @@ export class FileService implements OnModuleInit {
702
702
 
703
703
  return url;
704
704
  }
705
+
706
+ createDownloadToken(filepath: string, expires = 3600) {
707
+ if (!filepath) {
708
+ throw new BadRequestException(`No file path provided`);
709
+ }
710
+
711
+ return this.jwtService.sign(
712
+ { filepath },
713
+ {
714
+ secret: process.env.JWT_SECRET,
715
+ expiresIn: `${Math.max(1, Number(expires) || 3600)}s`,
716
+ },
717
+ );
718
+ }
705
719
  }
@@ -118,9 +118,6 @@ export class OutboxPollingCoordinator {
118
118
  } else {
119
119
  // No items processed - use idle interval
120
120
  nextDelayMs = settings.idlePollingIntervalMs;
121
- this.logger.debug(
122
- `No events processed, idle polling for ${nextDelayMs}ms`,
123
- );
124
121
  }
125
122
 
126
123
  // Wait for next poll, but be ready for immediate wakeup
@@ -0,0 +1,19 @@
1
+ import { IsBoolean, IsOptional, IsString, MaxLength } from 'class-validator';
2
+
3
+ export class CreateLocaleDTO {
4
+ @IsString()
5
+ name: string;
6
+
7
+ @IsString()
8
+ @MaxLength(2)
9
+ code: string;
10
+
11
+ @IsString()
12
+ @MaxLength(2)
13
+ @IsOptional()
14
+ region?: string;
15
+
16
+ @IsBoolean()
17
+ @IsOptional()
18
+ enabled?: boolean;
19
+ }
@@ -0,0 +1,35 @@
1
+ import { Role } from '@hed-hog/api';
2
+ import { Pagination, PaginationDTO } from '@hed-hog/api-pagination';
3
+ import {
4
+ Body,
5
+ Controller,
6
+ Get,
7
+ Inject,
8
+ Post,
9
+ Query,
10
+ forwardRef,
11
+ } from '@nestjs/common';
12
+ import { CreateLocaleDTO } from './dto/create-locale.dto';
13
+ import { LocaleService } from './locale.service';
14
+
15
+ @Role()
16
+ @Controller('core/locales')
17
+ export class LocaleController {
18
+ constructor(
19
+ @Inject(forwardRef(() => LocaleService))
20
+ private readonly localeService: LocaleService,
21
+ ) {}
22
+
23
+ @Get()
24
+ async findAll(
25
+ @Pagination() paginationDto: PaginationDTO,
26
+ @Query('search') search?: string,
27
+ ) {
28
+ return this.localeService.findAll(paginationDto, search);
29
+ }
30
+
31
+ @Post()
32
+ async create(@Body() dto: CreateLocaleDTO) {
33
+ return this.localeService.create(dto);
34
+ }
35
+ }
@@ -0,0 +1,18 @@
1
+ import { PaginationModule } from '@hed-hog/api-pagination';
2
+ import { PrismaModule } from '@hed-hog/api-prisma';
3
+ import { Module, forwardRef } from '@nestjs/common';
4
+ import { AuthModule } from '../auth/auth.module';
5
+ import { LocaleController } from './locale.controller';
6
+ import { LocaleService } from './locale.service';
7
+
8
+ @Module({
9
+ providers: [LocaleService],
10
+ exports: [LocaleService],
11
+ controllers: [LocaleController],
12
+ imports: [
13
+ forwardRef(() => AuthModule),
14
+ forwardRef(() => PrismaModule),
15
+ forwardRef(() => PaginationModule),
16
+ ],
17
+ })
18
+ export class LocaleModule {}
@@ -0,0 +1,48 @@
1
+ import { PaginationDTO, PaginationService } from '@hed-hog/api-pagination';
2
+ import { PrismaService } from '@hed-hog/api-prisma';
3
+ import { Inject, Injectable, forwardRef } from '@nestjs/common';
4
+ import { CreateLocaleDTO } from './dto/create-locale.dto';
5
+
6
+ @Injectable()
7
+ export class LocaleService {
8
+ constructor(
9
+ @Inject(forwardRef(() => PrismaService))
10
+ private readonly prismaService: PrismaService,
11
+ @Inject(forwardRef(() => PaginationService))
12
+ private readonly paginationService: PaginationService,
13
+ ) {}
14
+
15
+ async findAll(paginationDto: PaginationDTO, search?: string) {
16
+ const normalizedSearch = search ?? paginationDto.search;
17
+ const paginationParams: PaginationDTO = {
18
+ ...paginationDto,
19
+ search: normalizedSearch,
20
+ };
21
+ const fields = ['name', 'code'];
22
+ const OR: any[] = this.prismaService.createInsensitiveSearch(
23
+ fields,
24
+ paginationParams,
25
+ );
26
+
27
+ return this.paginationService.paginate(
28
+ this.prismaService.locale,
29
+ paginationParams,
30
+ {
31
+ where: {
32
+ OR,
33
+ },
34
+ },
35
+ );
36
+ }
37
+
38
+ async create(dto: CreateLocaleDTO) {
39
+ return this.prismaService.locale.create({
40
+ data: {
41
+ name: dto.name,
42
+ code: dto.code,
43
+ region: dto.region ?? null,
44
+ enabled: dto.enabled ?? true,
45
+ },
46
+ });
47
+ }
48
+ }
@@ -1,14 +1,15 @@
1
1
  import { PaginationService } from '@hed-hog/api-pagination';
2
2
  import { PrismaService } from '@hed-hog/api-prisma';
3
3
  import {
4
- ForbiddenException,
5
- forwardRef,
6
- Inject,
7
- Injectable,
8
- Logger,
9
- NotFoundException,
10
- OnModuleDestroy,
11
- OnModuleInit,
4
+ BadRequestException,
5
+ ForbiddenException,
6
+ forwardRef,
7
+ Inject,
8
+ Injectable,
9
+ Logger,
10
+ NotFoundException,
11
+ OnModuleDestroy,
12
+ OnModuleInit,
12
13
  } from '@nestjs/common';
13
14
  import type { Request, Response } from 'express';
14
15
  import { randomUUID } from 'node:crypto';
@@ -113,6 +114,9 @@ export class NotificationService implements OnModuleInit, OnModuleDestroy {
113
114
  async markRead(userId: number, id: number) {
114
115
  const notification = await this.assertOwnership(userId, id);
115
116
  if (notification.auto_remove) {
117
+ if (this.isActiveAsyncNotification(notification)) {
118
+ throw new BadRequestException('Active async notifications cannot be deleted');
119
+ }
116
120
  const deleted = await this.prisma.notification.delete({ where: { id } });
117
121
  await this.broadcastNotificationEvent(userId, {
118
122
  type: 'notification.updated',
@@ -133,7 +137,16 @@ export class NotificationService implements OnModuleInit, OnModuleDestroy {
133
137
 
134
138
  async markAllRead(userId: number) {
135
139
  await this.prisma.notification.deleteMany({
136
- where: { user_id: userId, status: 'unread', auto_remove: true },
140
+ where: {
141
+ user_id: userId,
142
+ status: 'unread',
143
+ auto_remove: true,
144
+ OR: [
145
+ { progress: null },
146
+ { progress: { gte: 100 } },
147
+ { finished_at: { not: null } },
148
+ ],
149
+ },
137
150
  });
138
151
  const updated = await this.prisma.notification.updateMany({
139
152
  where: { user_id: userId, status: 'unread' },
@@ -144,7 +157,10 @@ export class NotificationService implements OnModuleInit, OnModuleDestroy {
144
157
  }
145
158
 
146
159
  async delete(userId: number, id: number) {
147
- await this.assertOwnership(userId, id);
160
+ const notification = await this.assertOwnership(userId, id);
161
+ if (this.isActiveAsyncNotification(notification)) {
162
+ throw new BadRequestException('Active async notifications cannot be deleted');
163
+ }
148
164
  const deleted = await this.prisma.notification.delete({ where: { id } });
149
165
  await this.broadcastNotificationEvent(userId, {
150
166
  type: 'notification.updated',
@@ -155,7 +171,14 @@ export class NotificationService implements OnModuleInit, OnModuleDestroy {
155
171
 
156
172
  async deleteAll(userId: number) {
157
173
  const result = await this.prisma.notification.deleteMany({
158
- where: { user_id: userId },
174
+ where: {
175
+ user_id: userId,
176
+ OR: [
177
+ { progress: null },
178
+ { progress: { gte: 100 } },
179
+ { finished_at: { not: null } },
180
+ ],
181
+ },
159
182
  });
160
183
  await this.broadcastNotificationEvent(userId, { type: 'notification.updated' });
161
184
  return result;
@@ -260,13 +283,29 @@ export class NotificationService implements OnModuleInit, OnModuleDestroy {
260
283
  private async assertOwnership(userId: number, id: number) {
261
284
  const notification = await this.prisma.notification.findUnique({
262
285
  where: { id },
263
- select: { user_id: true, auto_remove: true },
286
+ select: {
287
+ user_id: true,
288
+ auto_remove: true,
289
+ progress: true,
290
+ finished_at: true,
291
+ },
264
292
  });
265
293
  if (!notification) throw new NotFoundException();
266
294
  if (notification.user_id !== userId) throw new ForbiddenException();
267
295
  return notification;
268
296
  }
269
297
 
298
+ private isActiveAsyncNotification(notification: {
299
+ progress: number | null;
300
+ finished_at: Date | null;
301
+ }) {
302
+ return (
303
+ notification.progress != null &&
304
+ notification.progress < 100 &&
305
+ notification.finished_at == null
306
+ );
307
+ }
308
+
270
309
  private async broadcastNotificationEvent(
271
310
  userId: number,
272
311
  event: NotificationStreamEvent,
@@ -3,12 +3,12 @@ import { getLocaleText, LocaleService } from '@hed-hog/api-locale';
3
3
  import { PaginationDTO, PaginationService } from '@hed-hog/api-pagination';
4
4
  import { PrismaService, setting_type_0f055cd5ea_enum } from '@hed-hog/api-prisma';
5
5
  import {
6
- BadRequestException,
7
- forwardRef,
8
- Inject,
9
- Injectable,
10
- Logger,
11
- NotFoundException
6
+ BadRequestException,
7
+ forwardRef,
8
+ Inject,
9
+ Injectable,
10
+ Logger,
11
+ NotFoundException
12
12
  } from '@nestjs/common';
13
13
  import * as pako from 'pako';
14
14
  import { IntegrationDeveloperApiService } from '../integration/services/integration-developer-api.service';
@@ -217,6 +217,8 @@ export class SettingService {
217
217
  'disable-authentication-with-email-and-password',
218
218
  'contact-allow-company-registration',
219
219
  'operations.comment-edit-window',
220
+ 'lms-video-conversion-enabled',
221
+ 'lms-video-frame-capture-interval-seconds',
220
222
  ];
221
223
  const setting = await this.getSettingValues(slugs);
222
224
  return { locales, setting };