@hed-hog/core 0.0.368 → 0.0.371

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 (271) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +2 -0
  3. package/dist/ai/ai.service.d.ts.map +1 -1
  4. package/dist/ai/ai.service.js +10 -7
  5. package/dist/ai/ai.service.js.map +1 -1
  6. package/dist/app-command/app-command.module.d.ts +3 -0
  7. package/dist/app-command/app-command.module.d.ts.map +1 -0
  8. package/dist/app-command/app-command.module.js +22 -0
  9. package/dist/app-command/app-command.module.js.map +1 -0
  10. package/dist/app-command/app-command.registry.d.ts +22 -0
  11. package/dist/app-command/app-command.registry.d.ts.map +1 -0
  12. package/dist/{webhook-integration/webhook-command.registry.js → app-command/app-command.registry.js} +6 -6
  13. package/dist/app-command/app-command.registry.js.map +1 -0
  14. package/dist/auth/auth.controller.d.ts +2 -2
  15. package/dist/auth/auth.controller.d.ts.map +1 -1
  16. package/dist/auth/auth.controller.js +20 -7
  17. package/dist/auth/auth.controller.js.map +1 -1
  18. package/dist/auth/auth.service.d.ts +3 -3
  19. package/dist/auth/auth.service.d.ts.map +1 -1
  20. package/dist/auth/auth.service.js +10 -36
  21. package/dist/auth/auth.service.js.map +1 -1
  22. package/dist/auth/dto/forget.dto.d.ts +1 -0
  23. package/dist/auth/dto/forget.dto.d.ts.map +1 -1
  24. package/dist/auth/dto/forget.dto.js +5 -0
  25. package/dist/auth/dto/forget.dto.js.map +1 -1
  26. package/dist/auth/guards/auth.guard.d.ts.map +1 -1
  27. package/dist/auth/guards/auth.guard.js +4 -1
  28. package/dist/auth/guards/auth.guard.js.map +1 -1
  29. package/dist/challenge/challenge.service.d.ts +8 -7
  30. package/dist/challenge/challenge.service.d.ts.map +1 -1
  31. package/dist/challenge/challenge.service.js +27 -19
  32. package/dist/challenge/challenge.service.js.map +1 -1
  33. package/dist/file/dto/upload.dto.d.ts +3 -0
  34. package/dist/file/dto/upload.dto.d.ts.map +1 -1
  35. package/dist/file/dto/upload.dto.js +22 -0
  36. package/dist/file/dto/upload.dto.js.map +1 -1
  37. package/dist/file/file.controller.d.ts +2 -1
  38. package/dist/file/file.controller.d.ts.map +1 -1
  39. package/dist/file/file.controller.js +29 -16
  40. package/dist/file/file.controller.js.map +1 -1
  41. package/dist/file/file.service.d.ts +51 -1
  42. package/dist/file/file.service.d.ts.map +1 -1
  43. package/dist/file/file.service.js +250 -18
  44. package/dist/file/file.service.js.map +1 -1
  45. package/dist/file/image-optimization.service.d.ts +17 -4
  46. package/dist/file/image-optimization.service.d.ts.map +1 -1
  47. package/dist/file/image-optimization.service.js +101 -46
  48. package/dist/file/image-optimization.service.js.map +1 -1
  49. package/dist/file/provider/s3.provider.d.ts.map +1 -1
  50. package/dist/file/provider/s3.provider.js +15 -7
  51. package/dist/file/provider/s3.provider.js.map +1 -1
  52. package/dist/file/provider/spaces.provider.d.ts.map +1 -1
  53. package/dist/file/provider/spaces.provider.js +18 -8
  54. package/dist/file/provider/spaces.provider.js.map +1 -1
  55. package/dist/index.d.ts +4 -1
  56. package/dist/index.d.ts.map +1 -1
  57. package/dist/index.js +4 -1
  58. package/dist/index.js.map +1 -1
  59. package/dist/integration/services/event-subscriber.registry.d.ts +5 -0
  60. package/dist/integration/services/event-subscriber.registry.d.ts.map +1 -1
  61. package/dist/integration/services/event-subscriber.registry.js +15 -0
  62. package/dist/integration/services/event-subscriber.registry.js.map +1 -1
  63. package/dist/integration/services/outbox.processor.d.ts.map +1 -1
  64. package/dist/integration/services/outbox.processor.js +2 -4
  65. package/dist/integration/services/outbox.processor.js.map +1 -1
  66. package/dist/integration/services/outbox.service.d.ts +4 -2
  67. package/dist/integration/services/outbox.service.d.ts.map +1 -1
  68. package/dist/integration/services/outbox.service.js +26 -15
  69. package/dist/integration/services/outbox.service.js.map +1 -1
  70. package/dist/integration-profile/integration-credential-crypto.service.d.ts +43 -0
  71. package/dist/integration-profile/integration-credential-crypto.service.d.ts.map +1 -0
  72. package/dist/integration-profile/integration-credential-crypto.service.js +142 -0
  73. package/dist/integration-profile/integration-credential-crypto.service.js.map +1 -0
  74. package/dist/integration-profile/integration-profile.controller.d.ts +6 -7
  75. package/dist/integration-profile/integration-profile.controller.d.ts.map +1 -1
  76. package/dist/integration-profile/integration-profile.controller.js +4 -3
  77. package/dist/integration-profile/integration-profile.controller.js.map +1 -1
  78. package/dist/integration-profile/integration-profile.module.d.ts.map +1 -1
  79. package/dist/integration-profile/integration-profile.module.js +5 -2
  80. package/dist/integration-profile/integration-profile.module.js.map +1 -1
  81. package/dist/integration-profile/integration-profile.secrets.d.ts +16 -0
  82. package/dist/integration-profile/integration-profile.secrets.d.ts.map +1 -0
  83. package/dist/integration-profile/integration-profile.secrets.js +56 -0
  84. package/dist/integration-profile/integration-profile.secrets.js.map +1 -0
  85. package/dist/integration-profile/integration-profile.service.d.ts +125 -8
  86. package/dist/integration-profile/integration-profile.service.d.ts.map +1 -1
  87. package/dist/integration-profile/integration-profile.service.js +257 -68
  88. package/dist/integration-profile/integration-profile.service.js.map +1 -1
  89. package/dist/integration-profile/integration-profile.utils.d.ts +17 -0
  90. package/dist/integration-profile/integration-profile.utils.d.ts.map +1 -1
  91. package/dist/integration-profile/integration-profile.utils.js +31 -0
  92. package/dist/integration-profile/integration-profile.utils.js.map +1 -1
  93. package/dist/language/en.json +178 -0
  94. package/dist/language/pt.json +178 -0
  95. package/dist/mail/mail.controller.d.ts.map +1 -1
  96. package/dist/mail/mail.controller.js +5 -28
  97. package/dist/mail/mail.controller.js.map +1 -1
  98. package/dist/mail/mail.module.js +18 -18
  99. package/dist/mail/mail.module.js.map +1 -1
  100. package/dist/mail/mail.service.d.ts +13 -2
  101. package/dist/mail/mail.service.d.ts.map +1 -1
  102. package/dist/mail/mail.service.js +25 -15
  103. package/dist/mail/mail.service.js.map +1 -1
  104. package/dist/mcp-chat/mcp-chat.service.js +6 -5
  105. package/dist/mcp-chat/mcp-chat.service.js.map +1 -1
  106. package/dist/oauth/providers/facebook.provider.js +5 -5
  107. package/dist/oauth/providers/facebook.provider.js.map +1 -1
  108. package/dist/oauth/providers/github.provider.js +5 -5
  109. package/dist/oauth/providers/github.provider.js.map +1 -1
  110. package/dist/oauth/providers/google.provider.js +5 -5
  111. package/dist/oauth/providers/google.provider.js.map +1 -1
  112. package/dist/oauth/providers/microsoft-entra-id.provider.js +5 -5
  113. package/dist/oauth/providers/microsoft-entra-id.provider.js.map +1 -1
  114. package/dist/oauth/providers/microsoft.provider.js +5 -5
  115. package/dist/oauth/providers/microsoft.provider.js.map +1 -1
  116. package/dist/pdf/pdf.service.d.ts +4 -0
  117. package/dist/pdf/pdf.service.d.ts.map +1 -1
  118. package/dist/pdf/pdf.service.js +16 -9
  119. package/dist/pdf/pdf.service.js.map +1 -1
  120. package/dist/profile/profile.controller.d.ts +7 -7
  121. package/dist/profile/profile.controller.d.ts.map +1 -1
  122. package/dist/profile/profile.controller.js +28 -21
  123. package/dist/profile/profile.controller.js.map +1 -1
  124. package/dist/profile/profile.service.d.ts +7 -7
  125. package/dist/profile/profile.service.d.ts.map +1 -1
  126. package/dist/profile/profile.service.js +21 -25
  127. package/dist/profile/profile.service.js.map +1 -1
  128. package/dist/setting/setting.service.d.ts +1 -0
  129. package/dist/setting/setting.service.d.ts.map +1 -1
  130. package/dist/setting/setting.service.js +15 -2
  131. package/dist/setting/setting.service.js.map +1 -1
  132. package/dist/token/token.service.d.ts.map +1 -1
  133. package/dist/token/token.service.js +2 -1
  134. package/dist/token/token.service.js.map +1 -1
  135. package/dist/user/user.controller.d.ts +9 -0
  136. package/dist/user/user.controller.d.ts.map +1 -1
  137. package/dist/user/user.controller.js +13 -0
  138. package/dist/user/user.controller.js.map +1 -1
  139. package/dist/user/user.service.d.ts +7 -0
  140. package/dist/user/user.service.d.ts.map +1 -1
  141. package/dist/user/user.service.js +19 -0
  142. package/dist/user/user.service.js.map +1 -1
  143. package/dist/utils/webauthn-origin.d.ts +11 -0
  144. package/dist/utils/webauthn-origin.d.ts.map +1 -0
  145. package/dist/utils/webauthn-origin.js +37 -0
  146. package/dist/utils/webauthn-origin.js.map +1 -0
  147. package/dist/webhook-integration/webhook-integration.controller.d.ts +13 -0
  148. package/dist/webhook-integration/webhook-integration.controller.d.ts.map +1 -1
  149. package/dist/webhook-integration/webhook-integration.controller.js +29 -0
  150. package/dist/webhook-integration/webhook-integration.controller.js.map +1 -1
  151. package/dist/webhook-integration/webhook-integration.module.js +3 -3
  152. package/dist/webhook-integration/webhook-integration.module.js.map +1 -1
  153. package/dist/webhook-integration/webhook-integration.service.d.ts +18 -2
  154. package/dist/webhook-integration/webhook-integration.service.d.ts.map +1 -1
  155. package/dist/webhook-integration/webhook-integration.service.js +110 -6
  156. package/dist/webhook-integration/webhook-integration.service.js.map +1 -1
  157. package/hedhog/data/integration_event_catalog.yaml +118 -118
  158. package/hedhog/data/integration_provider.yaml +9 -0
  159. package/hedhog/data/integration_type.yaml +6 -0
  160. package/hedhog/data/menu.yaml +6 -0
  161. package/hedhog/data/role.yaml +7 -0
  162. package/hedhog/data/route.yaml +192 -207
  163. package/hedhog/data/setting_group.yaml +119 -13
  164. package/hedhog/frontend/app/access-log/page.tsx.ejs +35 -3
  165. package/hedhog/frontend/app/account/components/active-sessions.tsx.ejs +36 -3
  166. package/hedhog/frontend/app/account/components/mcp-tokens.tsx.ejs +39 -4
  167. package/hedhog/frontend/app/account/components/tokens.tsx.ejs +39 -4
  168. package/hedhog/frontend/app/ai_agent/page.tsx.ejs +35 -2
  169. package/hedhog/frontend/app/configurations/[slug]/components/byte-size-input.tsx.ejs +160 -0
  170. package/hedhog/frontend/app/configurations/[slug]/components/duration-input.tsx.ejs +157 -0
  171. package/hedhog/frontend/app/configurations/[slug]/components/setting-field.tsx.ejs +244 -63
  172. package/hedhog/frontend/app/dashboard/[slug]/dashboard-content.tsx.ejs +33 -1
  173. package/hedhog/frontend/app/dashboard/management/tabs/component-roles-tab.tsx.ejs +35 -3
  174. package/hedhog/frontend/app/dashboard/management/tabs/components-tab.tsx.ejs +36 -4
  175. package/hedhog/frontend/app/dashboard/management/tabs/dashboard-roles-tab.tsx.ejs +35 -3
  176. package/hedhog/frontend/app/dashboard/management/tabs/dashboards-tab.tsx.ejs +36 -4
  177. package/hedhog/frontend/app/dashboard/management/tabs/items-tab.tsx.ejs +35 -3
  178. package/hedhog/frontend/app/integration/profiles/page.tsx.ejs +141 -29
  179. package/hedhog/frontend/app/integrations-webhooks/page.tsx.ejs +118 -11
  180. package/hedhog/frontend/app/mail/log/page.tsx.ejs +37 -4
  181. package/hedhog/frontend/app/menu/page.tsx.ejs +34 -2
  182. package/hedhog/frontend/app/preferences/page.tsx.ejs +77 -0
  183. package/hedhog/frontend/app/roles/menus.tsx.ejs +35 -3
  184. package/hedhog/frontend/app/roles/page.tsx.ejs +34 -2
  185. package/hedhog/frontend/app/roles/routes.tsx.ejs +36 -4
  186. package/hedhog/frontend/app/roles/users.tsx.ejs +36 -4
  187. package/hedhog/frontend/app/routes/page.tsx.ejs +34 -2
  188. package/hedhog/frontend/app/setting-list/page.tsx.ejs +34 -2
  189. package/hedhog/frontend/app/users/page.tsx.ejs +51 -21
  190. package/hedhog/frontend/messages/en.json +69 -4
  191. package/hedhog/frontend/messages/pt.json +69 -4
  192. package/hedhog/table/event_webhook.yaml +55 -55
  193. package/hedhog/table/event_webhook_delivery_log.yaml +66 -66
  194. package/hedhog/table/event_webhook_event.yaml +26 -26
  195. package/hedhog/table/integration_event_catalog.yaml +41 -41
  196. package/hedhog/table/mcp_conversation.yaml +12 -12
  197. package/hedhog/table/mcp_conversation_message.yaml +23 -23
  198. package/hedhog/table/mcp_tool_call_log.yaml +22 -22
  199. package/hedhog/table/route.yaml +33 -33
  200. package/hedhog/table/setting.yaml +2 -0
  201. package/hedhog/table/webhook_call_log.yaml +3 -0
  202. package/hedhog/table/webhook_integration.yaml +43 -43
  203. package/package.json +9 -4
  204. package/src/ai/ai.service.ts +9 -6
  205. package/src/app-command/app-command.module.ts +9 -0
  206. package/src/{webhook-integration/webhook-command.registry.ts → app-command/app-command.registry.ts} +9 -9
  207. package/src/auth/auth.controller.ts +20 -4
  208. package/src/auth/auth.service.ts +10 -36
  209. package/src/auth/dto/forget.dto.ts +5 -1
  210. package/src/auth/guards/auth.guard.ts +4 -1
  211. package/src/challenge/challenge.service.ts +30 -19
  212. package/src/file/dto/upload.dto.ts +25 -1
  213. package/src/file/file.controller.ts +31 -18
  214. package/src/file/file.service.ts +341 -16
  215. package/src/file/image-optimization.service.ts +111 -46
  216. package/src/file/provider/s3.provider.ts +16 -7
  217. package/src/file/provider/spaces.provider.ts +15 -5
  218. package/src/index.ts +4 -1
  219. package/src/integration/services/event-subscriber.registry.ts +15 -0
  220. package/src/integration/services/outbox.processor.ts +2 -5
  221. package/src/integration/services/outbox.service.ts +35 -17
  222. package/src/integration/types/event.types.ts +4 -4
  223. package/src/integration-profile/integration-credential-crypto.service.ts +146 -0
  224. package/src/integration-profile/integration-profile.controller.ts +2 -0
  225. package/src/integration-profile/integration-profile.module.ts +5 -2
  226. package/src/integration-profile/integration-profile.secrets.ts +51 -0
  227. package/src/integration-profile/integration-profile.service.ts +335 -54
  228. package/src/integration-profile/integration-profile.utils.ts +56 -0
  229. package/src/language/en.json +5 -1
  230. package/src/language/pt.json +5 -1
  231. package/src/mail/mail.controller.ts +5 -29
  232. package/src/mail/mail.module.ts +5 -5
  233. package/src/mail/mail.service.ts +37 -13
  234. package/src/mcp/decorators/mcp-tool.decorator.ts +7 -7
  235. package/src/mcp/guards/mcp-permission.guard.ts +51 -51
  236. package/src/mcp/interfaces/mcp-tool-definition.interface.ts +6 -6
  237. package/src/mcp/mcp-server.service.ts +87 -87
  238. package/src/mcp/types/mcp-context.type.ts +6 -6
  239. package/src/mcp-chat/dto/create-conversation.dto.ts +7 -7
  240. package/src/mcp-chat/dto/send-message.dto.ts +7 -7
  241. package/src/mcp-chat/mcp-chat.service.ts +5 -5
  242. package/src/notification/events/notification-stale-progress-check.event.ts +5 -5
  243. package/src/oauth/oauth-callback-coordinator.service.spec.ts +159 -0
  244. package/src/oauth/providers/facebook.provider.ts +3 -3
  245. package/src/oauth/providers/github.provider.ts +3 -3
  246. package/src/oauth/providers/google.provider.ts +3 -3
  247. package/src/oauth/providers/microsoft-entra-id.provider.ts +3 -3
  248. package/src/oauth/providers/microsoft.provider.ts +3 -3
  249. package/src/pdf/pdf.service.ts +19 -6
  250. package/src/profile/profile.controller.ts +21 -14
  251. package/src/profile/profile.service.ts +23 -20
  252. package/src/route/dto/create.dto.ts +32 -32
  253. package/src/route/dto/update.dto.ts +31 -31
  254. package/src/security/security.service.spec.ts +222 -0
  255. package/src/setting/setting.service.ts +15 -2
  256. package/src/token/token.service.ts +2 -1
  257. package/src/user/user.controller.ts +26 -16
  258. package/src/user/user.service.ts +25 -0
  259. package/src/utils/webauthn-origin.ts +38 -0
  260. package/src/webhook-integration/integration-event-catalog.controller.ts +50 -50
  261. package/src/webhook-integration/integration-event-catalog.service.ts +117 -117
  262. package/src/webhook-integration/webhook-integration.controller.ts +23 -0
  263. package/src/webhook-integration/webhook-integration.module.ts +3 -3
  264. package/src/webhook-integration/webhook-integration.service.ts +115 -5
  265. package/dist/webhook-integration/webhook-command.registry.d.ts +0 -22
  266. package/dist/webhook-integration/webhook-command.registry.d.ts.map +0 -1
  267. package/dist/webhook-integration/webhook-command.registry.js.map +0 -1
  268. package/src/menu/menu.service.spec.ts +0 -247
  269. package/src/route/route.service.spec.ts +0 -300
  270. package/src/screen/screen.service.spec.ts +0 -298
  271. package/src/user/user.service.spec.ts +0 -294
@@ -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
+
@@ -129,6 +129,14 @@ export class WebhookIntegrationController {
129
129
  ) {
130
130
  return this.service.streamLogs(id, res);
131
131
  }
132
+
133
+ @Post(':id/log/:logId/retry')
134
+ retryLog(
135
+ @Param('id', ParseIntPipe) id: number,
136
+ @Param('logId', ParseIntPipe) logId: number,
137
+ ) {
138
+ return this.service.retryCall(id, logId);
139
+ }
132
140
  }
133
141
 
134
142
  @Public()
@@ -145,5 +153,20 @@ export class PublicWebhookController {
145
153
  ) {
146
154
  return this.service.executePublic(uuid, body, headers, req);
147
155
  }
156
+
157
+ /**
158
+ * GET variant so a webhook can be triggered by a plain clickable link (e.g.
159
+ * approve/reject links sent by email/WhatsApp). Query params are used as the
160
+ * payload for action templates (`{{body.decision}}` ← `?decision=approve`).
161
+ */
162
+ @Get(':uuid')
163
+ executeGet(
164
+ @Param('uuid') uuid: string,
165
+ @Query() query: any,
166
+ @Headers() headers: any,
167
+ @Req() req: Request,
168
+ ) {
169
+ return this.service.executePublic(uuid, query ?? {}, headers, req);
170
+ }
148
171
  }
149
172
 
@@ -3,6 +3,7 @@ import { PaginationModule } from '@hed-hog/api-pagination';
3
3
  import { PrismaModule } from '@hed-hog/api-prisma';
4
4
  import { HttpModule } from '@nestjs/axios';
5
5
  import { Module, forwardRef } from '@nestjs/common';
6
+ import { AppCommandModule } from '../app-command/app-command.module';
6
7
  import { IntegrationModule } from '../integration/integration.module';
7
8
  import { MailModule } from '../mail/mail.module';
8
9
  import { McpModule } from '../mcp/mcp.module';
@@ -10,7 +11,6 @@ import { EventWebhookController } from './event-webhook.controller';
10
11
  import { EventWebhookService } from './event-webhook.service';
11
12
  import { IntegrationEventCatalogController } from './integration-event-catalog.controller';
12
13
  import { IntegrationEventCatalogService } from './integration-event-catalog.service';
13
- import { WebhookCommandRegistry } from './webhook-command.registry';
14
14
  import {
15
15
  PublicWebhookController,
16
16
  WebhookIntegrationController,
@@ -26,6 +26,7 @@ import { WebhookIntegrationService } from './webhook-integration.service';
26
26
  forwardRef(() => MailModule),
27
27
  forwardRef(() => MailMainModule),
28
28
  forwardRef(() => McpModule),
29
+ AppCommandModule,
29
30
  ],
30
31
  controllers: [
31
32
  IntegrationEventCatalogController,
@@ -35,13 +36,12 @@ import { WebhookIntegrationService } from './webhook-integration.service';
35
36
  ],
36
37
  providers: [
37
38
  IntegrationEventCatalogService,
38
- WebhookCommandRegistry,
39
39
  WebhookIntegrationService,
40
40
  EventWebhookService,
41
41
  ],
42
42
  exports: [
43
43
  IntegrationEventCatalogService,
44
- WebhookCommandRegistry,
44
+ AppCommandModule,
45
45
  WebhookIntegrationService,
46
46
  EventWebhookService,
47
47
  ],
@@ -24,7 +24,7 @@ import {
24
24
  CreateWebhookActionDTO,
25
25
  UpdateWebhookActionDTO,
26
26
  } from './dto/webhook-action.dto';
27
- import { WebhookCommandRegistry } from './webhook-command.registry';
27
+ import { AppCommandRegistry } from '../app-command/app-command.registry';
28
28
  import {
29
29
  createPlainToken,
30
30
  createPublicUuid,
@@ -103,7 +103,7 @@ export class WebhookIntegrationService {
103
103
  private readonly mailMainService: MailMainService,
104
104
  private readonly coreMailService: MailService,
105
105
  private readonly settingService: SettingService,
106
- private readonly commandRegistry: WebhookCommandRegistry,
106
+ private readonly commandRegistry: AppCommandRegistry,
107
107
  private readonly mcpApiKeyService: McpApiKeyService,
108
108
  ) {}
109
109
 
@@ -495,7 +495,8 @@ export class WebhookIntegrationService {
495
495
  pageSize,
496
496
  prev: page > 1 ? page - 1 : null,
497
497
  next: page < lastPage ? page + 1 : null,
498
- data,
498
+ // Never expose the raw replay payload (may contain secrets) to clients.
499
+ data: (data || []).map(({ payload_raw, ...rest }: any) => rest),
499
500
  };
500
501
 
501
502
  const logIds = (result.data || [])
@@ -553,7 +554,6 @@ export class WebhookIntegrationService {
553
554
  async executePublic(uuid: string, body: any, headers: any, req: Request) {
554
555
  const startedAt = Date.now();
555
556
  const remoteIp = this.getRemoteIp(req);
556
- const actionLogs: ActionExecutionLog[] = [];
557
557
  let integration: any | null = null;
558
558
 
559
559
  try {
@@ -575,14 +575,70 @@ export class WebhookIntegrationService {
575
575
  this.validateToken(integration, headers);
576
576
  this.validateIp(integration, remoteIp);
577
577
  this.validateBodySchema(integration, body);
578
+ } catch (error) {
579
+ const callLog = await this.createCallLog({
580
+ integration,
581
+ uuid,
582
+ status: 'failed',
583
+ remoteIp,
584
+ durationMs: Date.now() - startedAt,
585
+ payload: body,
586
+ response: {
587
+ success: false,
588
+ actions: [],
589
+ },
590
+ error,
591
+ }).catch(() => null);
592
+
593
+ if (integration?.id && callLog?.id) {
594
+ this.logEmitter.emit(`log:${integration.id}`, { id: callLog.id });
595
+ }
596
+
597
+ if (error instanceof HttpException) {
598
+ throw error;
599
+ }
600
+
601
+ return {
602
+ success: false,
603
+ actions: [],
604
+ };
605
+ }
606
+
607
+ return this.runActionsAndLog({
608
+ integration,
609
+ uuid,
610
+ body,
611
+ remoteIp,
612
+ webhookHeaders: normalizeJsonObject(headers) || {},
613
+ startedAt,
614
+ });
615
+ }
578
616
 
617
+ /**
618
+ * Runs every active action of an integration against `body`, persists a new
619
+ * call log + action logs, and emits the SSE event. Shared by the public
620
+ * webhook entrypoint and by the admin-triggered retry of a failed call.
621
+ */
622
+ private async runActionsAndLog(input: {
623
+ integration: any;
624
+ uuid: string;
625
+ body: any;
626
+ remoteIp: string | null;
627
+ webhookHeaders: Record<string, unknown>;
628
+ startedAt: number;
629
+ }) {
630
+ const { integration, uuid, body, remoteIp, webhookHeaders, startedAt } =
631
+ input;
632
+ const actionLogs: ActionExecutionLog[] = [];
633
+
634
+ try {
579
635
  const actionResults = [];
580
636
  for (const action of integration.webhook_action || []) {
581
637
  const actionStartedAt = Date.now();
582
638
 
583
639
  try {
584
640
  const result = await this.executeAction(action, body, {
585
- webhookHeaders: normalizeJsonObject(headers) || {},
641
+ webhookHeaders,
586
642
  remoteIp,
587
643
  publicUuid: uuid,
588
644
  integrationId: integration.id,
@@ -700,6 +756,56 @@ export class WebhookIntegrationService {
700
756
  }
701
757
  }
702
758
 
759
+ /**
760
+ * Replays a previously failed webhook call from the Logs tab. Re-executes the
761
+ * whole call (all active actions) using the original stored payload, without
762
+ * re-validating token/IP (the retry is triggered by an authenticated admin).
763
+ * Produces a brand-new call log so the retry is itself auditable.
764
+ */
765
+ async retryCall(integrationId: number, logId: number) {
766
+ const log = await this.logs?.findFirst({
767
+ where: { id: logId, webhook_integration_id: integrationId },
768
+ });
769
+
770
+ if (!log) {
771
+ throw new NotFoundException('Webhook call log not found.');
772
+ }
773
+
774
+ if (log.status !== 'failed') {
775
+ throw new BadRequestException('Only failed webhook calls can be retried.');
776
+ }
777
+
778
+ if (log.payload_raw === null || log.payload_raw === undefined) {
779
+ throw new BadRequestException(
780
+ 'This webhook call cannot be retried because its original payload was not stored.',
781
+ );
782
+ }
783
+
784
+ const integration = await this.integrations.findUnique({
785
+ where: { id: integrationId },
786
+ include: {
787
+ webhook_action: {
788
+ select: this.webhookActionSelect,
789
+ where: { status: 'active' },
790
+ orderBy: [{ order: 'asc' }, { id: 'asc' }],
791
+ },
792
+ },
793
+ });
794
+
795
+ if (!integration) {
796
+ throw new NotFoundException('Webhook integration not found.');
797
+ }
798
+
799
+ return this.runActionsAndLog({
800
+ integration,
801
+ uuid: integration.public_uuid ?? log.public_uuid,
802
+ body: log.payload_raw,
803
+ remoteIp: null,
804
+ webhookHeaders: {},
805
+ startedAt: Date.now(),
806
+ });
807
+ }
808
+
703
809
  private async executeAction(
704
810
  action: any,
705
811
  body: any,
@@ -1300,6 +1406,10 @@ export class WebhookIntegrationService {
1300
1406
  duration_ms: input.durationMs,
1301
1407
  error_message: input.error ? this.extractErrorMessage(input.error) : null,
1302
1408
  payload_summary: summarizePayload(sanitizeSensitiveData(input.payload)),
1409
+ // Store the full, unsanitized payload only for failed calls so they can
1410
+ // be replayed faithfully from the Logs tab. Successful calls keep just
1411
+ // the sanitized summary to limit secret exposure and storage.
1412
+ payload_raw: input.status === 'failed' ? (input.payload ?? null) : null,
1303
1413
  response_summary: summarizePayload(sanitizeSensitiveData(input.response)),
1304
1414
  },
1305
1415
  select: {
@@ -1,22 +0,0 @@
1
- export interface WebhookCommandContext extends Record<string, unknown> {
2
- webhookBody?: unknown;
3
- webhookHeaders?: Record<string, unknown>;
4
- remoteIp?: string | null;
5
- publicUuid?: string;
6
- integrationId?: number;
7
- }
8
- export interface WebhookCommand {
9
- slug: string;
10
- name: string;
11
- description: string;
12
- inputSchema: Record<string, unknown>;
13
- handler: (params: Record<string, unknown>, context?: WebhookCommandContext) => Promise<unknown>;
14
- }
15
- export declare class WebhookCommandRegistry {
16
- private readonly commands;
17
- register(command: WebhookCommand): void;
18
- find(slug: string): WebhookCommand | undefined;
19
- list(): WebhookCommand[];
20
- dispatch(slug: string, params: Record<string, unknown>, context?: WebhookCommandContext): Promise<unknown>;
21
- }
22
- //# sourceMappingURL=webhook-command.registry.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"webhook-command.registry.d.ts","sourceRoot":"","sources":["../../src/webhook-integration/webhook-command.registry.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,qBAAsB,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACpE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,OAAO,EAAE,CACP,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,OAAO,CAAC,EAAE,qBAAqB,KAC5B,OAAO,CAAC,OAAO,CAAC,CAAC;CACvB;AAED,qBACa,sBAAsB;IACjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqC;IAE9D,QAAQ,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;IAIvC,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAI9C,IAAI,IAAI,cAAc,EAAE;IAIlB,QAAQ,CACZ,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,OAAO,CAAC;CAQpB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"webhook-command.registry.js","sourceRoot":"","sources":["../../src/webhook-integration/webhook-command.registry.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAA4C;AAsBrC,IAAM,sBAAsB,GAA5B,MAAM,sBAAsB;IAA5B;QACY,aAAQ,GAAG,IAAI,GAAG,EAA0B,CAAC;IA0BhE,CAAC;IAxBC,QAAQ,CAAC,OAAuB;QAC9B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC3C,CAAC;IAED,IAAI,CAAC,IAAY;QACf,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IAED,IAAI;QACF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5C,CAAC;IAED,KAAK,CAAC,QAAQ,CACZ,IAAY,EACZ,MAA+B,EAC/B,OAA+B;QAE/B,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,6BAA6B,IAAI,EAAE,CAAC,CAAC;QACvD,CAAC;QAED,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1C,CAAC;CACF,CAAA;AA3BY,wDAAsB;iCAAtB,sBAAsB;IADlC,IAAA,mBAAU,GAAE;GACA,sBAAsB,CA2BlC"}