@hed-hog/core 0.0.371 → 0.0.374

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 (107) hide show
  1. package/README.md +83 -1
  2. package/dist/integration-profile/integration-profile.secrets.d.ts.map +1 -1
  3. package/dist/integration-profile/integration-profile.secrets.js +4 -0
  4. package/dist/integration-profile/integration-profile.secrets.js.map +1 -1
  5. package/dist/integration-profile/integration-profile.utils.d.ts +6 -0
  6. package/dist/integration-profile/integration-profile.utils.d.ts.map +1 -1
  7. package/dist/integration-profile/integration-profile.utils.js +1 -1
  8. package/dist/integration-profile/integration-profile.utils.js.map +1 -1
  9. package/dist/oauth/oauth.controller.d.ts +11 -3
  10. package/dist/oauth/oauth.controller.d.ts.map +1 -1
  11. package/dist/oauth/oauth.controller.js +52 -14
  12. package/dist/oauth/oauth.controller.js.map +1 -1
  13. package/dist/oauth/oauth.module.d.ts.map +1 -1
  14. package/dist/oauth/oauth.module.js +4 -0
  15. package/dist/oauth/oauth.module.js.map +1 -1
  16. package/dist/oauth/oauth.service.d.ts +38 -2
  17. package/dist/oauth/oauth.service.d.ts.map +1 -1
  18. package/dist/oauth/oauth.service.js +122 -5
  19. package/dist/oauth/oauth.service.js.map +1 -1
  20. package/dist/oauth/providers/apple.provider.d.ts +34 -0
  21. package/dist/oauth/providers/apple.provider.d.ts.map +1 -0
  22. package/dist/oauth/providers/apple.provider.js +197 -0
  23. package/dist/oauth/providers/apple.provider.js.map +1 -0
  24. package/dist/oauth/providers/facebook.provider.d.ts +1 -1
  25. package/dist/oauth/providers/facebook.provider.d.ts.map +1 -1
  26. package/dist/oauth/providers/facebook.provider.js +3 -2
  27. package/dist/oauth/providers/facebook.provider.js.map +1 -1
  28. package/dist/oauth/providers/google.provider.d.ts +1 -1
  29. package/dist/oauth/providers/google.provider.d.ts.map +1 -1
  30. package/dist/oauth/providers/google.provider.js +3 -2
  31. package/dist/oauth/providers/google.provider.js.map +1 -1
  32. package/dist/oauth/providers/linkedin.provider.d.ts +25 -0
  33. package/dist/oauth/providers/linkedin.provider.d.ts.map +1 -0
  34. package/dist/oauth/providers/linkedin.provider.js +99 -0
  35. package/dist/oauth/providers/linkedin.provider.js.map +1 -0
  36. package/dist/oauth/providers/microsoft-entra-id.provider.js +1 -1
  37. package/dist/oauth/providers/microsoft-entra-id.provider.js.map +1 -1
  38. package/dist/oauth/providers/microsoft.provider.d.ts +1 -1
  39. package/dist/oauth/providers/microsoft.provider.d.ts.map +1 -1
  40. package/dist/oauth/providers/microsoft.provider.js +3 -2
  41. package/dist/oauth/providers/microsoft.provider.js.map +1 -1
  42. package/dist/pdf/pdf.service.d.ts.map +1 -1
  43. package/dist/pdf/pdf.service.js +19 -2
  44. package/dist/pdf/pdf.service.js.map +1 -1
  45. package/dist/setting/setting.service.d.ts.map +1 -1
  46. package/dist/setting/setting.service.js +4 -1
  47. package/dist/setting/setting.service.js.map +1 -1
  48. package/hedhog/data/integration_provider.yaml +16 -0
  49. package/hedhog/data/setting_group.yaml +98 -0
  50. package/hedhog/data/setting_subgroup.yaml +16 -0
  51. package/hedhog/frontend/app/configurations/[slug]/components/setting-field.tsx.ejs +2 -0
  52. package/hedhog/frontend/app/dashboard/dashboard.css.ejs +11 -11
  53. package/hedhog/frontend/app/integration/profiles/page.tsx.ejs +57 -9
  54. package/hedhog/frontend/app/menu/page.tsx.ejs +1 -1
  55. package/hedhog/frontend/app/users/user-sheet.tsx.ejs +8 -0
  56. package/hedhog/frontend/messages/en.json +35 -0
  57. package/hedhog/frontend/messages/pt.json +35 -0
  58. package/hedhog/table/user_account.yaml +1 -0
  59. package/package.json +4 -4
  60. package/src/access-log/access-log.service.spec.ts +235 -0
  61. package/src/ai-instruction/ai-prompt-builder.service.spec.ts +250 -0
  62. package/src/auth/auth.service.spec.ts +891 -0
  63. package/src/auth/guards/auth.guard.spec.ts +177 -0
  64. package/src/challenge/challenge.service.spec.ts +334 -0
  65. package/src/dashboard/dashboard-core/dashboard-core.service.spec.ts +193 -0
  66. package/src/file/image-optimization.service.spec.ts +283 -0
  67. package/src/file/provider/provider.factory.spec.ts +111 -0
  68. package/src/install/install.service.spec.ts +139 -0
  69. package/src/integration/services/event-subscriber.registry.spec.ts +143 -0
  70. package/src/integration/services/inbox.service.spec.ts +183 -0
  71. package/src/integration/services/integration-link.service.spec.ts +217 -0
  72. package/src/integration/services/outbox.service.spec.ts +290 -0
  73. package/src/integration-profile/integration-credential-crypto.service.spec.ts +289 -0
  74. package/src/integration-profile/integration-profile.secrets.ts +4 -0
  75. package/src/integration-profile/integration-profile.utils.spec.ts +332 -0
  76. package/src/integration-profile/integration-profile.utils.ts +14 -1
  77. package/src/locale/locale.service.spec.ts +78 -0
  78. package/src/mail/mail.service.spec.ts +257 -0
  79. package/src/mcp/guards/mcp-auth.guard.spec.ts +87 -0
  80. package/src/mcp/guards/mcp-permission.guard.spec.ts +64 -0
  81. package/src/mcp/mcp-api-key.service.spec.ts +125 -0
  82. package/src/mcp/mcp-registry.service.spec.ts +96 -0
  83. package/src/mcp/mcp-server.service.spec.ts +187 -0
  84. package/src/menu/menu.service.spec.ts +76 -0
  85. package/src/notification/notification.service.spec.ts +297 -0
  86. package/src/oauth/oauth.controller.ts +53 -10
  87. package/src/oauth/oauth.module.ts +4 -0
  88. package/src/oauth/oauth.service.spec.ts +332 -0
  89. package/src/oauth/oauth.service.ts +156 -1
  90. package/src/oauth/providers/apple.provider.ts +175 -0
  91. package/src/oauth/providers/facebook.provider.ts +3 -2
  92. package/src/oauth/providers/google.provider.ts +3 -2
  93. package/src/oauth/providers/linkedin.provider.ts +96 -0
  94. package/src/oauth/providers/microsoft-entra-id.provider.ts +1 -1
  95. package/src/oauth/providers/microsoft.provider.ts +3 -2
  96. package/src/pdf/pdf.service.ts +20 -2
  97. package/src/role/guards/role.guard.spec.ts +157 -0
  98. package/src/role/role.service.spec.ts +179 -0
  99. package/src/session/session.service.spec.ts +376 -0
  100. package/src/setting/setting.service.spec.ts +316 -0
  101. package/src/setting/setting.service.ts +4 -1
  102. package/src/task/task.service.spec.ts +47 -0
  103. package/src/token/token.service.spec.ts +351 -0
  104. package/src/user/user.service.spec.ts +256 -0
  105. package/src/utils/locale-context.spec.ts +22 -0
  106. package/src/utils/webauthn-origin.spec.ts +73 -0
  107. package/src/webhook-integration/integration-event-catalog.service.spec.ts +149 -0
@@ -0,0 +1,73 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from '@jest/globals';
2
+ import { resolveWebAuthnOrigin } from './webauthn-origin';
3
+
4
+ describe('resolveWebAuthnOrigin', () => {
5
+ const originalEnv = process.env.CORS_ALLOWED_ORIGINS;
6
+
7
+ afterEach(() => {
8
+ if (originalEnv === undefined) {
9
+ delete process.env.CORS_ALLOWED_ORIGINS;
10
+ } else {
11
+ process.env.CORS_ALLOWED_ORIGINS = originalEnv;
12
+ }
13
+ });
14
+
15
+ it('usa fallback padrão quando não há origins configuradas', () => {
16
+ delete process.env.CORS_ALLOWED_ORIGINS;
17
+ const result = resolveWebAuthnOrigin();
18
+ expect(result).toEqual({
19
+ rpID: 'localhost',
20
+ expectedOrigin: 'http://localhost:3200',
21
+ });
22
+ });
23
+
24
+ it('retorna o request origin quando está na allowlist', () => {
25
+ process.env.CORS_ALLOWED_ORIGINS =
26
+ 'https://admin.example.com,https://training.example.com';
27
+ const result = resolveWebAuthnOrigin('https://training.example.com');
28
+ expect(result).toEqual({
29
+ rpID: 'training.example.com',
30
+ expectedOrigin: 'https://training.example.com',
31
+ });
32
+ });
33
+
34
+ it('normaliza trailing slash do request origin antes de comparar', () => {
35
+ process.env.CORS_ALLOWED_ORIGINS = 'https://admin.example.com';
36
+ const result = resolveWebAuthnOrigin('https://admin.example.com/');
37
+ expect(result.expectedOrigin).toBe('https://admin.example.com');
38
+ });
39
+
40
+ it('cai para o primeiro origin permitido quando request origin não está na lista', () => {
41
+ process.env.CORS_ALLOWED_ORIGINS =
42
+ 'https://first.example.com;https://second.example.com';
43
+ const result = resolveWebAuthnOrigin('https://unknown.example.com');
44
+ expect(result.expectedOrigin).toBe('https://first.example.com');
45
+ expect(result.rpID).toBe('first.example.com');
46
+ });
47
+
48
+ it('cai para o primeiro origin permitido quando request origin é inválido', () => {
49
+ process.env.CORS_ALLOWED_ORIGINS = 'https://first.example.com';
50
+ const result = resolveWebAuthnOrigin('not-a-url');
51
+ expect(result.expectedOrigin).toBe('https://first.example.com');
52
+ });
53
+
54
+ it('ignora entradas vazias/inválidas ao construir a allowlist', () => {
55
+ process.env.CORS_ALLOWED_ORIGINS =
56
+ '\n , ; not-a-url ; https://valid.example.com';
57
+ const result = resolveWebAuthnOrigin('https://valid.example.com');
58
+ expect(result.expectedOrigin).toBe('https://valid.example.com');
59
+ });
60
+
61
+ it('usa fallback quando request origin é null e allowlist vazia', () => {
62
+ process.env.CORS_ALLOWED_ORIGINS = '';
63
+ const result = resolveWebAuthnOrigin(null);
64
+ expect(result.expectedOrigin).toBe('http://localhost:3200');
65
+ });
66
+
67
+ it('extrai o origin de uma URL com path e porta', () => {
68
+ process.env.CORS_ALLOWED_ORIGINS = 'http://localhost:3001/app/login';
69
+ const result = resolveWebAuthnOrigin('http://localhost:3001');
70
+ expect(result.expectedOrigin).toBe('http://localhost:3001');
71
+ expect(result.rpID).toBe('localhost');
72
+ });
73
+ });
@@ -0,0 +1,149 @@
1
+ import { describe, it, expect, beforeEach, afterEach, jest } from '@jest/globals';
2
+ import { BadRequestException, NotFoundException } from '@nestjs/common';
3
+ import { IntegrationEventCatalogService } from './integration-event-catalog.service';
4
+
5
+ const makeDeps = () => {
6
+ const catalog = {
7
+ findMany: jest.fn(),
8
+ findUnique: jest.fn(),
9
+ create: jest.fn(),
10
+ update: jest.fn(),
11
+ deleteMany: jest.fn(),
12
+ };
13
+ const prisma = {
14
+ integration_event_catalog: catalog,
15
+ createInsensitiveSearch: jest.fn().mockReturnValue([]),
16
+ };
17
+ const pagination = { paginate: jest.fn().mockResolvedValue({ data: [] }) };
18
+ return { prisma, pagination, catalog };
19
+ };
20
+
21
+ const makeService = (deps = makeDeps()) => ({
22
+ service: new IntegrationEventCatalogService(deps.prisma as any, deps.pagination as any),
23
+ deps,
24
+ });
25
+
26
+ describe('IntegrationEventCatalogService', () => {
27
+ afterEach(() => jest.clearAllMocks());
28
+
29
+ describe('normalize', () => {
30
+ let service: IntegrationEventCatalogService;
31
+ beforeEach(() => {
32
+ service = makeService().service;
33
+ });
34
+ const call = (data: any, partial?: boolean) =>
35
+ (service as any).normalize(data, partial);
36
+
37
+ it('faz trim e default status active', () => {
38
+ const result = call({ slug: ' s ', name: ' N ', module: ' m ' });
39
+ expect(result).toEqual({
40
+ slug: 's',
41
+ name: 'N',
42
+ module: 'm',
43
+ description: null,
44
+ status: 'active',
45
+ });
46
+ });
47
+
48
+ it('status inactive é preservado', () => {
49
+ expect(call({ status: 'inactive' }).status).toBe('inactive');
50
+ });
51
+
52
+ it('description string vira trim ou null', () => {
53
+ expect(call({ description: ' hi ' }).description).toBe('hi');
54
+ expect(call({ description: ' ' }).description).toBeNull();
55
+ });
56
+
57
+ it('partial pula description undefined mas mantém slug/name/module vazios', () => {
58
+ const result = call({ slug: 'x' }, true);
59
+ expect(result).toEqual({ slug: 'x', name: '', module: '', status: 'active' });
60
+ expect(result).not.toHaveProperty('description');
61
+ });
62
+ });
63
+
64
+ describe('get', () => {
65
+ it('lança NotFound quando não existe', async () => {
66
+ const { service, deps } = makeService();
67
+ deps.catalog.findUnique.mockResolvedValue(null);
68
+ await expect(service.get(1)).rejects.toThrow(NotFoundException);
69
+ });
70
+
71
+ it('retorna registro existente', async () => {
72
+ const { service, deps } = makeService();
73
+ deps.catalog.findUnique.mockResolvedValue({ id: 1, slug: 's' });
74
+ await expect(service.get(1)).resolves.toMatchObject({ id: 1 });
75
+ });
76
+ });
77
+
78
+ describe('create', () => {
79
+ it('lança BadRequest quando slug já existe', async () => {
80
+ const { service, deps } = makeService();
81
+ deps.catalog.findUnique.mockResolvedValue({ id: 1 });
82
+ await expect(service.create({ slug: 'dup' })).rejects.toThrow(
83
+ BadRequestException,
84
+ );
85
+ });
86
+
87
+ it('cria com dados normalizados quando slug livre', async () => {
88
+ const { service, deps } = makeService();
89
+ deps.catalog.findUnique.mockResolvedValue(null);
90
+ deps.catalog.create.mockResolvedValue({ id: 2 });
91
+ await service.create({ slug: ' new ', name: 'N', module: 'm' });
92
+ expect(deps.catalog.create).toHaveBeenCalledWith({
93
+ data: expect.objectContaining({ slug: 'new', status: 'active' }),
94
+ });
95
+ });
96
+ });
97
+
98
+ describe('update', () => {
99
+ it('checa disponibilidade só quando slug muda', async () => {
100
+ const { service, deps } = makeService();
101
+ deps.catalog.findUnique
102
+ .mockResolvedValueOnce({ id: 1, slug: 'old' }) // get()
103
+ .mockResolvedValueOnce(null); // ensureSlugAvailable
104
+ deps.catalog.update.mockResolvedValue({ id: 1 });
105
+ await service.update(1, { slug: 'new' });
106
+ expect(deps.catalog.update).toHaveBeenCalledWith({
107
+ where: { id: 1 },
108
+ data: expect.objectContaining({ slug: 'new' }),
109
+ });
110
+ });
111
+
112
+ it('não checa disponibilidade quando slug igual', async () => {
113
+ const { service, deps } = makeService();
114
+ deps.catalog.findUnique.mockResolvedValue({ id: 1, slug: 'same' });
115
+ deps.catalog.update.mockResolvedValue({ id: 1 });
116
+ await service.update(1, { slug: 'same', name: 'X' });
117
+ expect(deps.catalog.findUnique).toHaveBeenCalledTimes(1);
118
+ });
119
+ });
120
+
121
+ describe('delete', () => {
122
+ it('lança BadRequest sem ids', async () => {
123
+ const { service } = makeService();
124
+ await expect(service.delete({ ids: [] } as any)).rejects.toThrow(
125
+ BadRequestException,
126
+ );
127
+ });
128
+
129
+ it('remove por ids', async () => {
130
+ const { service, deps } = makeService();
131
+ deps.catalog.deleteMany.mockResolvedValue({ count: 2 });
132
+ await service.delete({ ids: [1, 2] } as any);
133
+ expect(deps.catalog.deleteMany).toHaveBeenCalledWith({
134
+ where: { id: { in: [1, 2] } },
135
+ });
136
+ });
137
+ });
138
+
139
+ describe('options', () => {
140
+ it('lista apenas ativos', async () => {
141
+ const { service, deps } = makeService();
142
+ deps.catalog.findMany.mockResolvedValue([{ id: 1 }]);
143
+ await service.options();
144
+ expect(deps.catalog.findMany).toHaveBeenCalledWith(
145
+ expect.objectContaining({ where: { status: 'active' } }),
146
+ );
147
+ });
148
+ });
149
+ });