@hed-hog/core 0.0.366 → 0.0.368
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/file/file.service.d.ts +1 -0
- package/dist/file/file.service.d.ts.map +1 -1
- package/dist/file/file.service.js +1 -1
- package/dist/file/file.service.js.map +1 -1
- package/dist/integration/services/outbox-polling.coordinator.d.ts +2 -0
- package/dist/integration/services/outbox-polling.coordinator.d.ts.map +1 -1
- package/dist/integration/services/outbox-polling.coordinator.js +14 -4
- package/dist/integration/services/outbox-polling.coordinator.js.map +1 -1
- package/dist/integration/services/outbox.processor.d.ts.map +1 -1
- package/dist/integration/services/outbox.processor.js +4 -3
- package/dist/integration/services/outbox.processor.js.map +1 -1
- package/dist/locale/locale.controller.d.ts +1 -1
- package/dist/locale/locale.controller.d.ts.map +1 -1
- package/dist/locale/locale.controller.js +4 -3
- package/dist/locale/locale.controller.js.map +1 -1
- package/dist/locale/locale.service.d.ts +1 -1
- package/dist/locale/locale.service.d.ts.map +1 -1
- package/dist/locale/locale.service.js +6 -6
- package/dist/locale/locale.service.js.map +1 -1
- package/dist/notification/events/notification-stale-progress-check.event.d.ts +6 -0
- package/dist/notification/events/notification-stale-progress-check.event.d.ts.map +1 -0
- package/dist/notification/events/notification-stale-progress-check.event.js +11 -0
- package/dist/notification/events/notification-stale-progress-check.event.js.map +1 -0
- package/dist/notification/notification.service.d.ts +4 -1
- package/dist/notification/notification.service.d.ts.map +1 -1
- package/dist/notification/notification.service.js +9 -26
- package/dist/notification/notification.service.js.map +1 -1
- package/dist/task/task.service.d.ts +19 -0
- package/dist/task/task.service.d.ts.map +1 -1
- package/dist/task/task.service.js +46 -0
- package/dist/task/task.service.js.map +1 -1
- package/dist/user/user.controller.d.ts +23 -5
- package/dist/user/user.controller.d.ts.map +1 -1
- package/dist/user/user.controller.js +32 -15
- package/dist/user/user.controller.js.map +1 -1
- package/dist/user/user.service.d.ts +28 -8
- package/dist/user/user.service.d.ts.map +1 -1
- package/dist/user/user.service.js +120 -8
- package/dist/user/user.service.js.map +1 -1
- package/hedhog/data/integration_event_catalog.yaml +118 -118
- package/hedhog/data/integration_provider.yaml +25 -0
- package/hedhog/data/integration_type.yaml +6 -0
- package/hedhog/data/locale.yaml +5 -1
- package/hedhog/data/route.yaml +9 -0
- package/hedhog/data/setting_group.yaml +116 -106
- package/hedhog/data/setting_subgroup.yaml +32 -0
- package/hedhog/frontend/app/integration/profiles/page.tsx.ejs +56 -0
- package/hedhog/frontend/app/roles/page.tsx.ejs +4 -1
- package/hedhog/frontend/app/users/user-logs.tsx.ejs +190 -0
- package/hedhog/frontend/app/users/user-sheet.tsx.ejs +15 -1
- package/hedhog/frontend/messages/en.json +17 -0
- package/hedhog/frontend/messages/pt.json +17 -0
- package/hedhog/table/ai_instruction.yaml +1 -1
- package/hedhog/table/event_webhook.yaml +55 -55
- package/hedhog/table/event_webhook_delivery_log.yaml +66 -66
- package/hedhog/table/event_webhook_event.yaml +26 -26
- package/hedhog/table/integration_event_catalog.yaml +41 -41
- package/hedhog/table/mcp_conversation.yaml +12 -12
- package/hedhog/table/mcp_conversation_message.yaml +23 -23
- package/hedhog/table/mcp_tool_call_log.yaml +22 -22
- package/hedhog/table/route.yaml +33 -33
- package/hedhog/table/user_change_log.yaml +40 -0
- package/hedhog/table/webhook_call_log.yaml +46 -46
- package/hedhog/table/webhook_integration.yaml +43 -43
- package/package.json +4 -4
- package/src/file/file.service.ts +2 -1
- package/src/integration/services/outbox-polling.coordinator.ts +16 -4
- package/src/integration/services/outbox.processor.ts +14 -8
- package/src/integration/types/event.types.ts +4 -4
- package/src/locale/locale.controller.ts +2 -1
- package/src/locale/locale.service.ts +7 -6
- package/src/mcp/decorators/mcp-tool.decorator.ts +7 -7
- package/src/mcp/guards/mcp-permission.guard.ts +51 -51
- package/src/mcp/interfaces/mcp-tool-definition.interface.ts +6 -6
- package/src/mcp/mcp-server.service.ts +87 -87
- package/src/mcp/types/mcp-context.type.ts +6 -6
- package/src/mcp-chat/dto/create-conversation.dto.ts +7 -7
- package/src/mcp-chat/dto/send-message.dto.ts +7 -7
- package/src/notification/events/notification-stale-progress-check.event.ts +5 -0
- package/src/notification/notification.service.ts +12 -25
- package/src/route/dto/create.dto.ts +32 -32
- package/src/route/dto/update.dto.ts +31 -31
- package/src/task/task.service.ts +52 -0
- package/src/user/user.controller.ts +23 -9
- package/src/user/user.service.ts +183 -8
- package/src/webhook-integration/integration-event-catalog.controller.ts +50 -50
- package/src/webhook-integration/integration-event-catalog.service.ts +117 -117
|
@@ -1,46 +1,46 @@
|
|
|
1
|
-
columns:
|
|
2
|
-
- type: pk
|
|
3
|
-
- type: fk
|
|
4
|
-
name: webhook_integration_id
|
|
5
|
-
isNullable: true
|
|
6
|
-
references:
|
|
7
|
-
table: webhook_integration
|
|
8
|
-
column: id
|
|
9
|
-
onDelete: set null
|
|
10
|
-
onUpdate: cascade
|
|
11
|
-
- name: public_uuid
|
|
12
|
-
type: varchar
|
|
13
|
-
length: 36
|
|
14
|
-
- name: status
|
|
15
|
-
type: enum
|
|
16
|
-
enum:
|
|
17
|
-
- success
|
|
18
|
-
- failed
|
|
19
|
-
- name: remote_ip
|
|
20
|
-
type: varchar
|
|
21
|
-
length: 127
|
|
22
|
-
isNullable: true
|
|
23
|
-
- name: duration_ms
|
|
24
|
-
type: int
|
|
25
|
-
default: 0
|
|
26
|
-
- name: error_message
|
|
27
|
-
type: text
|
|
28
|
-
isNullable: true
|
|
29
|
-
- name: payload_summary
|
|
30
|
-
type: json
|
|
31
|
-
isNullable: true
|
|
32
|
-
- name: response_summary
|
|
33
|
-
type: json
|
|
34
|
-
isNullable: true
|
|
35
|
-
- type: created_at
|
|
36
|
-
- type: updated_at
|
|
37
|
-
indices:
|
|
38
|
-
- columns:
|
|
39
|
-
- webhook_integration_id
|
|
40
|
-
- columns:
|
|
41
|
-
- public_uuid
|
|
42
|
-
- columns:
|
|
43
|
-
- status
|
|
44
|
-
- columns:
|
|
45
|
-
- created_at
|
|
46
|
-
|
|
1
|
+
columns:
|
|
2
|
+
- type: pk
|
|
3
|
+
- type: fk
|
|
4
|
+
name: webhook_integration_id
|
|
5
|
+
isNullable: true
|
|
6
|
+
references:
|
|
7
|
+
table: webhook_integration
|
|
8
|
+
column: id
|
|
9
|
+
onDelete: set null
|
|
10
|
+
onUpdate: cascade
|
|
11
|
+
- name: public_uuid
|
|
12
|
+
type: varchar
|
|
13
|
+
length: 36
|
|
14
|
+
- name: status
|
|
15
|
+
type: enum
|
|
16
|
+
enum:
|
|
17
|
+
- success
|
|
18
|
+
- failed
|
|
19
|
+
- name: remote_ip
|
|
20
|
+
type: varchar
|
|
21
|
+
length: 127
|
|
22
|
+
isNullable: true
|
|
23
|
+
- name: duration_ms
|
|
24
|
+
type: int
|
|
25
|
+
default: 0
|
|
26
|
+
- name: error_message
|
|
27
|
+
type: text
|
|
28
|
+
isNullable: true
|
|
29
|
+
- name: payload_summary
|
|
30
|
+
type: json
|
|
31
|
+
isNullable: true
|
|
32
|
+
- name: response_summary
|
|
33
|
+
type: json
|
|
34
|
+
isNullable: true
|
|
35
|
+
- type: created_at
|
|
36
|
+
- type: updated_at
|
|
37
|
+
indices:
|
|
38
|
+
- columns:
|
|
39
|
+
- webhook_integration_id
|
|
40
|
+
- columns:
|
|
41
|
+
- public_uuid
|
|
42
|
+
- columns:
|
|
43
|
+
- status
|
|
44
|
+
- columns:
|
|
45
|
+
- created_at
|
|
46
|
+
|
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
columns:
|
|
2
|
-
- type: pk
|
|
3
|
-
- type: slug
|
|
4
|
-
- name: name
|
|
5
|
-
type: varchar
|
|
6
|
-
length: 127
|
|
7
|
-
- name: description
|
|
8
|
-
type: text
|
|
9
|
-
isNullable: true
|
|
10
|
-
- name: public_uuid
|
|
11
|
-
type: varchar
|
|
12
|
-
length: 36
|
|
13
|
-
- name: status
|
|
14
|
-
type: enum
|
|
15
|
-
enum:
|
|
16
|
-
- active
|
|
17
|
-
- inactive
|
|
18
|
-
default: active
|
|
19
|
-
- name: require_token
|
|
20
|
-
type: boolean
|
|
21
|
-
default: false
|
|
22
|
-
- name: token_hash
|
|
23
|
-
type: varchar
|
|
24
|
-
length: 128
|
|
25
|
-
isNullable: true
|
|
26
|
-
- name: allowed_ips
|
|
27
|
-
type: json
|
|
28
|
-
isNullable: true
|
|
29
|
-
- name: body_schema
|
|
30
|
-
type: json
|
|
31
|
-
isNullable: true
|
|
32
|
-
- type: created_at
|
|
33
|
-
- type: updated_at
|
|
34
|
-
indices:
|
|
35
|
-
- columns:
|
|
36
|
-
- slug
|
|
37
|
-
isUnique: true
|
|
38
|
-
- columns:
|
|
39
|
-
- public_uuid
|
|
40
|
-
isUnique: true
|
|
41
|
-
- columns:
|
|
42
|
-
- status
|
|
43
|
-
|
|
1
|
+
columns:
|
|
2
|
+
- type: pk
|
|
3
|
+
- type: slug
|
|
4
|
+
- name: name
|
|
5
|
+
type: varchar
|
|
6
|
+
length: 127
|
|
7
|
+
- name: description
|
|
8
|
+
type: text
|
|
9
|
+
isNullable: true
|
|
10
|
+
- name: public_uuid
|
|
11
|
+
type: varchar
|
|
12
|
+
length: 36
|
|
13
|
+
- name: status
|
|
14
|
+
type: enum
|
|
15
|
+
enum:
|
|
16
|
+
- active
|
|
17
|
+
- inactive
|
|
18
|
+
default: active
|
|
19
|
+
- name: require_token
|
|
20
|
+
type: boolean
|
|
21
|
+
default: false
|
|
22
|
+
- name: token_hash
|
|
23
|
+
type: varchar
|
|
24
|
+
length: 128
|
|
25
|
+
isNullable: true
|
|
26
|
+
- name: allowed_ips
|
|
27
|
+
type: json
|
|
28
|
+
isNullable: true
|
|
29
|
+
- name: body_schema
|
|
30
|
+
type: json
|
|
31
|
+
isNullable: true
|
|
32
|
+
- type: created_at
|
|
33
|
+
- type: updated_at
|
|
34
|
+
indices:
|
|
35
|
+
- columns:
|
|
36
|
+
- slug
|
|
37
|
+
isUnique: true
|
|
38
|
+
- columns:
|
|
39
|
+
- public_uuid
|
|
40
|
+
isUnique: true
|
|
41
|
+
- columns:
|
|
42
|
+
- status
|
|
43
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hed-hog/core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.368",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"dependencies": {
|
|
@@ -38,12 +38,12 @@
|
|
|
38
38
|
"speakeasy": "^2.0.0",
|
|
39
39
|
"ua-parser-js": "^2.0.10",
|
|
40
40
|
"uuid": "^11.1.0",
|
|
41
|
-
"@hed-hog/api-pagination": "0.0.7",
|
|
42
41
|
"@hed-hog/api": "0.0.8",
|
|
43
42
|
"@hed-hog/api-locale": "0.0.14",
|
|
43
|
+
"@hed-hog/api-pagination": "0.0.7",
|
|
44
44
|
"@hed-hog/api-mail": "0.0.9",
|
|
45
|
-
"@hed-hog/api-
|
|
46
|
-
"@hed-hog/api-
|
|
45
|
+
"@hed-hog/api-types": "0.0.1",
|
|
46
|
+
"@hed-hog/api-prisma": "0.0.6"
|
|
47
47
|
},
|
|
48
48
|
"exports": {
|
|
49
49
|
".": {
|
package/src/file/file.service.ts
CHANGED
|
@@ -490,6 +490,7 @@ export class FileService implements OnModuleInit {
|
|
|
490
490
|
mimetype?: string;
|
|
491
491
|
size?: number;
|
|
492
492
|
encoding?: string;
|
|
493
|
+
skipMimeValidation?: boolean;
|
|
493
494
|
},
|
|
494
495
|
) {
|
|
495
496
|
if (!filePath) {
|
|
@@ -511,7 +512,7 @@ export class FileService implements OnModuleInit {
|
|
|
511
512
|
metadata?.originalname ?? metadata?.filename ?? basename(filePath),
|
|
512
513
|
);
|
|
513
514
|
|
|
514
|
-
if (!(await this.acceptMimetypes(mimetype))) {
|
|
515
|
+
if (!metadata?.skipMimeValidation && !(await this.acceptMimetypes(mimetype))) {
|
|
515
516
|
throw new BadRequestException(`Invalid file type: ${mimetype}`);
|
|
516
517
|
}
|
|
517
518
|
|
|
@@ -15,6 +15,8 @@ export class OutboxPollingCoordinator {
|
|
|
15
15
|
private processingLoopPromise: Promise<void> | null = null;
|
|
16
16
|
private immediateWakeupTriggered = false;
|
|
17
17
|
private shouldStopLoop = false;
|
|
18
|
+
private _currentTimeout: ReturnType<typeof setTimeout> | null = null;
|
|
19
|
+
private _resolveDelay: (() => void) | null = null;
|
|
18
20
|
|
|
19
21
|
constructor(
|
|
20
22
|
private readonly processor: OutboxProcessor,
|
|
@@ -62,6 +64,15 @@ export class OutboxPollingCoordinator {
|
|
|
62
64
|
*/
|
|
63
65
|
triggerImmediateProcessing(): void {
|
|
64
66
|
this.immediateWakeupTriggered = true;
|
|
67
|
+
if (this._currentTimeout !== null) {
|
|
68
|
+
clearTimeout(this._currentTimeout);
|
|
69
|
+
this._currentTimeout = null;
|
|
70
|
+
this.logger.debug('[outbox:wakeup] delay interrupted by new event notification');
|
|
71
|
+
}
|
|
72
|
+
if (this._resolveDelay) {
|
|
73
|
+
this._resolveDelay();
|
|
74
|
+
this._resolveDelay = null;
|
|
75
|
+
}
|
|
65
76
|
}
|
|
66
77
|
|
|
67
78
|
/**
|
|
@@ -88,7 +99,9 @@ export class OutboxPollingCoordinator {
|
|
|
88
99
|
const processorEnabled = settings.outboxProcessorEnabled;
|
|
89
100
|
|
|
90
101
|
if (!isEnabled || !processorEnabled) {
|
|
91
|
-
|
|
102
|
+
this.logger.debug(
|
|
103
|
+
`[outbox:loop] processor disabled (outboxEnabled=${isEnabled} processorEnabled=${processorEnabled}), sleeping ${settings.idlePollingIntervalMs}ms`,
|
|
104
|
+
);
|
|
92
105
|
await this.delay(settings.idlePollingIntervalMs);
|
|
93
106
|
continue;
|
|
94
107
|
}
|
|
@@ -135,9 +148,8 @@ export class OutboxPollingCoordinator {
|
|
|
135
148
|
*/
|
|
136
149
|
private delay(ms: number): Promise<void> {
|
|
137
150
|
return new Promise((resolve) => {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
(this as any)._currentTimeout = timeout;
|
|
151
|
+
this._resolveDelay = resolve;
|
|
152
|
+
this._currentTimeout = setTimeout(resolve, ms);
|
|
141
153
|
});
|
|
142
154
|
}
|
|
143
155
|
}
|
|
@@ -34,7 +34,9 @@ export class OutboxProcessor {
|
|
|
34
34
|
return 0;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
this.logger.debug(
|
|
37
|
+
this.logger.debug(
|
|
38
|
+
`[outbox:batch] processing ${events.length} event(s): ${events.map((e) => `${e.id}(${e.eventName})`).join(', ')}`,
|
|
39
|
+
);
|
|
38
40
|
|
|
39
41
|
let processedCount = 0;
|
|
40
42
|
|
|
@@ -48,7 +50,7 @@ export class OutboxProcessor {
|
|
|
48
50
|
|
|
49
51
|
if (handlers.length === 0) {
|
|
50
52
|
this.logger.warn(
|
|
51
|
-
`
|
|
53
|
+
`[outbox:batch] no handlers registered for eventName=${event.eventName} eventId=${event.id} — marking as processed and skipping`,
|
|
52
54
|
);
|
|
53
55
|
// Mark as processed even though no handlers
|
|
54
56
|
await this.outboxService.updateStatus(
|
|
@@ -80,13 +82,17 @@ export class OutboxProcessor {
|
|
|
80
82
|
|
|
81
83
|
await this.inboxService.markProcessing(inboxEvent.id);
|
|
82
84
|
|
|
85
|
+
this.logger.debug(
|
|
86
|
+
`[outbox:batch] running handler=${handlerDef.consumerName} eventId=${event.id}`,
|
|
87
|
+
);
|
|
88
|
+
|
|
83
89
|
try {
|
|
84
90
|
// Execute handler
|
|
85
|
-
await handlerDef.handler(
|
|
86
|
-
{
|
|
87
|
-
outboxEventId: event.id,
|
|
88
|
-
eventName: event.eventName,
|
|
89
|
-
sourceModule: event.sourceModule,
|
|
91
|
+
await handlerDef.handler(
|
|
92
|
+
{
|
|
93
|
+
outboxEventId: event.id,
|
|
94
|
+
eventName: event.eventName,
|
|
95
|
+
sourceModule: event.sourceModule,
|
|
90
96
|
aggregateType: event.aggregateType,
|
|
91
97
|
aggregateId: event.aggregateId,
|
|
92
98
|
payload: event.payload,
|
|
@@ -107,7 +113,7 @@ export class OutboxProcessor {
|
|
|
107
113
|
} catch (error) {
|
|
108
114
|
anyFailed = true;
|
|
109
115
|
this.logger.error(
|
|
110
|
-
`
|
|
116
|
+
`[outbox:batch] handler=${handlerDef.consumerName} failed for eventId=${event.id}: ${error instanceof Error ? error.message : String(error)}`,
|
|
111
117
|
error,
|
|
112
118
|
);
|
|
113
119
|
await this.inboxService.markFailed(
|
|
@@ -32,10 +32,10 @@ export enum LinkType {
|
|
|
32
32
|
/**
|
|
33
33
|
* Domain event interface that is published and processed
|
|
34
34
|
*/
|
|
35
|
-
export interface DomainEvent {
|
|
36
|
-
outboxEventId?: string;
|
|
37
|
-
eventName: string;
|
|
38
|
-
sourceModule: string;
|
|
35
|
+
export interface DomainEvent {
|
|
36
|
+
outboxEventId?: string;
|
|
37
|
+
eventName: string;
|
|
38
|
+
sourceModule: string;
|
|
39
39
|
aggregateType: string;
|
|
40
40
|
aggregateId: string;
|
|
41
41
|
payload: Record<string, any>;
|
|
@@ -24,8 +24,9 @@ export class LocaleController {
|
|
|
24
24
|
async findAll(
|
|
25
25
|
@Pagination() paginationDto: PaginationDTO,
|
|
26
26
|
@Query('search') search?: string,
|
|
27
|
+
@Query('enabled') enabled?: string,
|
|
27
28
|
) {
|
|
28
|
-
return this.localeService.findAll(paginationDto, search);
|
|
29
|
+
return this.localeService.findAll(paginationDto, search, enabled);
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
@Post()
|
|
@@ -12,7 +12,7 @@ export class LocaleService {
|
|
|
12
12
|
private readonly paginationService: PaginationService,
|
|
13
13
|
) {}
|
|
14
14
|
|
|
15
|
-
async findAll(paginationDto: PaginationDTO, search?: string) {
|
|
15
|
+
async findAll(paginationDto: PaginationDTO, search?: string, enabled?: string) {
|
|
16
16
|
const normalizedSearch = search ?? paginationDto.search;
|
|
17
17
|
const paginationParams: PaginationDTO = {
|
|
18
18
|
...paginationDto,
|
|
@@ -24,14 +24,15 @@ export class LocaleService {
|
|
|
24
24
|
paginationParams,
|
|
25
25
|
);
|
|
26
26
|
|
|
27
|
+
const where: Record<string, any> = { OR };
|
|
28
|
+
if (enabled !== undefined) {
|
|
29
|
+
where.enabled = enabled === 'true';
|
|
30
|
+
}
|
|
31
|
+
|
|
27
32
|
return this.paginationService.paginate(
|
|
28
33
|
this.prismaService.locale,
|
|
29
34
|
paginationParams,
|
|
30
|
-
{
|
|
31
|
-
where: {
|
|
32
|
-
OR,
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
+
{ where },
|
|
35
36
|
);
|
|
36
37
|
}
|
|
37
38
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { SetMetadata } from '@nestjs/common';
|
|
2
|
-
import { McpToolDefinition } from '../interfaces/mcp-tool-definition.interface';
|
|
3
|
-
|
|
4
|
-
export const MCP_TOOL_METADATA = 'mcp:tool';
|
|
5
|
-
|
|
6
|
-
export const McpTool = (definition: McpToolDefinition): MethodDecorator =>
|
|
7
|
-
SetMetadata(MCP_TOOL_METADATA, definition);
|
|
1
|
+
import { SetMetadata } from '@nestjs/common';
|
|
2
|
+
import { McpToolDefinition } from '../interfaces/mcp-tool-definition.interface';
|
|
3
|
+
|
|
4
|
+
export const MCP_TOOL_METADATA = 'mcp:tool';
|
|
5
|
+
|
|
6
|
+
export const McpTool = (definition: McpToolDefinition): MethodDecorator =>
|
|
7
|
+
SetMetadata(MCP_TOOL_METADATA, definition);
|
|
@@ -1,51 +1,51 @@
|
|
|
1
|
-
import { PrismaService } from '@hed-hog/api-prisma';
|
|
2
|
-
import {
|
|
3
|
-
CanActivate,
|
|
4
|
-
ExecutionContext,
|
|
5
|
-
ForbiddenException,
|
|
6
|
-
Injectable,
|
|
7
|
-
} from '@nestjs/common';
|
|
8
|
-
import { Request } from 'express';
|
|
9
|
-
|
|
10
|
-
@Injectable()
|
|
11
|
-
export class McpPermissionGuard implements CanActivate {
|
|
12
|
-
constructor(private readonly prisma: PrismaService) {}
|
|
13
|
-
|
|
14
|
-
async canActivate(context: ExecutionContext): Promise<boolean> {
|
|
15
|
-
const request = context.switchToHttp().getRequest<Request>();
|
|
16
|
-
|
|
17
|
-
// Only POST requests carry tool invocations
|
|
18
|
-
if (request.method !== 'POST') return true;
|
|
19
|
-
|
|
20
|
-
const body = request.body as any;
|
|
21
|
-
const toolName: string | undefined =
|
|
22
|
-
body?.method === 'tools/call' ? body?.params?.name : undefined;
|
|
23
|
-
|
|
24
|
-
// tools/list, initialize, ping, etc. — no per-tool permission check needed
|
|
25
|
-
if (!toolName) return true;
|
|
26
|
-
|
|
27
|
-
const userId = (request as any)?.auth?.sub;
|
|
28
|
-
|
|
29
|
-
const count = await this.prisma.route.count({
|
|
30
|
-
where: {
|
|
31
|
-
type: 'MCP' as any,
|
|
32
|
-
tool_name: toolName,
|
|
33
|
-
role_route: {
|
|
34
|
-
some: {
|
|
35
|
-
role: {
|
|
36
|
-
role_user: {
|
|
37
|
-
some: { user_id: userId },
|
|
38
|
-
},
|
|
39
|
-
},
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
},
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
if (count === 0) {
|
|
46
|
-
throw new ForbiddenException(`Access denied to MCP tool '${toolName}'`);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return true;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
1
|
+
import { PrismaService } from '@hed-hog/api-prisma';
|
|
2
|
+
import {
|
|
3
|
+
CanActivate,
|
|
4
|
+
ExecutionContext,
|
|
5
|
+
ForbiddenException,
|
|
6
|
+
Injectable,
|
|
7
|
+
} from '@nestjs/common';
|
|
8
|
+
import { Request } from 'express';
|
|
9
|
+
|
|
10
|
+
@Injectable()
|
|
11
|
+
export class McpPermissionGuard implements CanActivate {
|
|
12
|
+
constructor(private readonly prisma: PrismaService) {}
|
|
13
|
+
|
|
14
|
+
async canActivate(context: ExecutionContext): Promise<boolean> {
|
|
15
|
+
const request = context.switchToHttp().getRequest<Request>();
|
|
16
|
+
|
|
17
|
+
// Only POST requests carry tool invocations
|
|
18
|
+
if (request.method !== 'POST') return true;
|
|
19
|
+
|
|
20
|
+
const body = request.body as any;
|
|
21
|
+
const toolName: string | undefined =
|
|
22
|
+
body?.method === 'tools/call' ? body?.params?.name : undefined;
|
|
23
|
+
|
|
24
|
+
// tools/list, initialize, ping, etc. — no per-tool permission check needed
|
|
25
|
+
if (!toolName) return true;
|
|
26
|
+
|
|
27
|
+
const userId = (request as any)?.auth?.sub;
|
|
28
|
+
|
|
29
|
+
const count = await this.prisma.route.count({
|
|
30
|
+
where: {
|
|
31
|
+
type: 'MCP' as any,
|
|
32
|
+
tool_name: toolName,
|
|
33
|
+
role_route: {
|
|
34
|
+
some: {
|
|
35
|
+
role: {
|
|
36
|
+
role_user: {
|
|
37
|
+
some: { user_id: userId },
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
if (count === 0) {
|
|
46
|
+
throw new ForbiddenException(`Access denied to MCP tool '${toolName}'`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export interface McpToolDefinition {
|
|
2
|
-
name: string;
|
|
3
|
-
description: string;
|
|
4
|
-
inputSchema?: Record<string, any>;
|
|
5
|
-
readOnly?: boolean;
|
|
6
|
-
}
|
|
1
|
+
export interface McpToolDefinition {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
inputSchema?: Record<string, any>;
|
|
5
|
+
readOnly?: boolean;
|
|
6
|
+
}
|