@hed-hog/core 0.0.350 → 0.0.351
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/access-log/access-log.controller.d.ts +16 -0
- package/dist/access-log/access-log.controller.d.ts.map +1 -0
- package/dist/access-log/access-log.controller.js +44 -0
- package/dist/access-log/access-log.controller.js.map +1 -0
- package/dist/access-log/access-log.interceptor.d.ts +9 -0
- package/dist/access-log/access-log.interceptor.d.ts.map +1 -0
- package/dist/access-log/access-log.interceptor.js +46 -0
- package/dist/access-log/access-log.interceptor.js.map +1 -0
- package/dist/access-log/access-log.module.d.ts +3 -0
- package/dist/access-log/access-log.module.d.ts.map +1 -0
- package/dist/access-log/access-log.module.js +29 -0
- package/dist/access-log/access-log.module.js.map +1 -0
- package/dist/access-log/access-log.service.d.ts +47 -0
- package/dist/access-log/access-log.service.d.ts.map +1 -0
- package/dist/access-log/access-log.service.js +113 -0
- package/dist/access-log/access-log.service.js.map +1 -0
- package/dist/access-log/dto/query-access-log.dto.d.ts +8 -0
- package/dist/access-log/dto/query-access-log.dto.d.ts.map +1 -0
- package/dist/access-log/dto/query-access-log.dto.js +42 -0
- package/dist/access-log/dto/query-access-log.dto.js.map +1 -0
- package/dist/auth/auth.controller.d.ts +9 -9
- package/dist/auth/auth.service.d.ts +19 -19
- package/dist/auth/auth.service.d.ts.map +1 -1
- package/dist/auth/auth.service.js +0 -4
- package/dist/auth/auth.service.js.map +1 -1
- package/dist/challenge/challenge.service.d.ts +2 -2
- package/dist/challenge/challenge.service.d.ts.map +1 -1
- package/dist/challenge/challenge.service.js +0 -1
- package/dist/challenge/challenge.service.js.map +1 -1
- package/dist/core/system.module.d.ts.map +1 -1
- package/dist/core/system.module.js +1 -0
- package/dist/core/system.module.js.map +1 -1
- package/dist/core.module.d.ts.map +1 -1
- package/dist/core.module.js +12 -0
- package/dist/core.module.js.map +1 -1
- package/dist/dashboard/dashboard-user/dashboard-user.controller.d.ts +3 -3
- package/dist/dashboard/dashboard-user/dashboard-user.service.d.ts +3 -3
- package/dist/dashboard/dashboard.module.d.ts.map +1 -1
- package/dist/dashboard/dashboard.module.js +4 -1
- package/dist/dashboard/dashboard.module.js.map +1 -1
- package/dist/file/file.controller.js +0 -1
- package/dist/file/file.controller.js.map +1 -1
- package/dist/file/file.service.d.ts +65 -0
- package/dist/file/file.service.d.ts.map +1 -1
- package/dist/file/file.service.js +151 -3
- package/dist/file/file.service.js.map +1 -1
- package/dist/file/provider/abstract.provider.d.ts +8 -0
- package/dist/file/provider/abstract.provider.d.ts.map +1 -1
- package/dist/file/provider/abstract.provider.js.map +1 -1
- package/dist/file/provider/azure.provider.d.ts +4 -0
- package/dist/file/provider/azure.provider.d.ts.map +1 -1
- package/dist/file/provider/azure.provider.js +14 -0
- package/dist/file/provider/azure.provider.js.map +1 -1
- package/dist/file/provider/gcs.provider.d.ts +4 -0
- package/dist/file/provider/gcs.provider.d.ts.map +1 -1
- package/dist/file/provider/gcs.provider.js +14 -0
- package/dist/file/provider/gcs.provider.js.map +1 -1
- package/dist/file/provider/local.provider.d.ts +4 -0
- package/dist/file/provider/local.provider.d.ts.map +1 -1
- package/dist/file/provider/local.provider.js +19 -0
- package/dist/file/provider/local.provider.js.map +1 -1
- package/dist/file/provider/provider.factory.d.ts.map +1 -1
- package/dist/file/provider/provider.factory.js +3 -0
- package/dist/file/provider/provider.factory.js.map +1 -1
- package/dist/file/provider/s3.provider.d.ts +4 -0
- package/dist/file/provider/s3.provider.d.ts.map +1 -1
- package/dist/file/provider/s3.provider.js +17 -0
- package/dist/file/provider/s3.provider.js.map +1 -1
- package/dist/mcp/mcp-audit.service.d.ts +3 -1
- package/dist/mcp/mcp-audit.service.d.ts.map +1 -1
- package/dist/mcp/mcp-audit.service.js +14 -2
- package/dist/mcp/mcp-audit.service.js.map +1 -1
- package/dist/mcp/mcp.controller.d.ts +3 -1
- package/dist/mcp/mcp.controller.d.ts.map +1 -1
- package/dist/mcp/mcp.controller.js +10 -2
- package/dist/mcp/mcp.controller.js.map +1 -1
- package/dist/mcp/mcp.module.d.ts.map +1 -1
- package/dist/mcp/mcp.module.js +72 -0
- package/dist/mcp/mcp.module.js.map +1 -1
- package/dist/mcp/tools/core-ai.mcp-tools.d.ts +45 -0
- package/dist/mcp/tools/core-ai.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-ai.mcp-tools.js +214 -0
- package/dist/mcp/tools/core-ai.mcp-tools.js.map +1 -0
- package/dist/mcp/tools/core-dashboard-items.mcp-tools.d.ts +21 -0
- package/dist/mcp/tools/core-dashboard-items.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-dashboard-items.mcp-tools.js +89 -0
- package/dist/mcp/tools/core-dashboard-items.mcp-tools.js.map +1 -0
- package/dist/mcp/tools/core-dashboard.mcp-tools.d.ts +29 -0
- package/dist/mcp/tools/core-dashboard.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-dashboard.mcp-tools.js +149 -0
- package/dist/mcp/tools/core-dashboard.mcp-tools.js.map +1 -0
- package/dist/mcp/tools/core-event-webhooks.mcp-tools.d.ts +30 -0
- package/dist/mcp/tools/core-event-webhooks.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-event-webhooks.mcp-tools.js +185 -0
- package/dist/mcp/tools/core-event-webhooks.mcp-tools.js.map +1 -0
- package/dist/mcp/tools/core-files.mcp-tools.d.ts +26 -0
- package/dist/mcp/tools/core-files.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-files.mcp-tools.js +140 -0
- package/dist/mcp/tools/core-files.mcp-tools.js.map +1 -0
- package/dist/mcp/tools/core-integration-events.mcp-tools.d.ts +26 -0
- package/dist/mcp/tools/core-integration-events.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-integration-events.mcp-tools.js +168 -0
- package/dist/mcp/tools/core-integration-events.mcp-tools.js.map +1 -0
- package/dist/mcp/tools/core-mail-profile.mcp-tools.d.ts +32 -0
- package/dist/mcp/tools/core-mail-profile.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-mail-profile.mcp-tools.js +178 -0
- package/dist/mcp/tools/core-mail-profile.mcp-tools.js.map +1 -0
- package/dist/mcp/tools/core-mail-sent.mcp-tools.d.ts +14 -0
- package/dist/mcp/tools/core-mail-sent.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-mail-sent.mcp-tools.js +106 -0
- package/dist/mcp/tools/core-mail-sent.mcp-tools.js.map +1 -0
- package/dist/mcp/tools/core-mail.mcp-tools.d.ts +36 -0
- package/dist/mcp/tools/core-mail.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-mail.mcp-tools.js +184 -0
- package/dist/mcp/tools/core-mail.mcp-tools.js.map +1 -0
- package/dist/mcp/tools/core-mcp-chat.mcp-tools.d.ts +21 -0
- package/dist/mcp/tools/core-mcp-chat.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-mcp-chat.mcp-tools.js +118 -0
- package/dist/mcp/tools/core-mcp-chat.mcp-tools.js.map +1 -0
- package/dist/mcp/tools/core-menus.mcp-tools.d.ts +40 -0
- package/dist/mcp/tools/core-menus.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-menus.mcp-tools.js +268 -0
- package/dist/mcp/tools/core-menus.mcp-tools.js.map +1 -0
- package/dist/mcp/tools/core-notifications.mcp-tools.d.ts +31 -0
- package/dist/mcp/tools/core-notifications.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-notifications.mcp-tools.js +178 -0
- package/dist/mcp/tools/core-notifications.mcp-tools.js.map +1 -0
- package/dist/mcp/tools/core-profile.mcp-tools.d.ts +20 -0
- package/dist/mcp/tools/core-profile.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-profile.mcp-tools.js +111 -0
- package/dist/mcp/tools/core-profile.mcp-tools.js.map +1 -0
- package/dist/mcp/tools/core-roles.mcp-tools.d.ts +70 -0
- package/dist/mcp/tools/core-roles.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-roles.mcp-tools.js +355 -0
- package/dist/mcp/tools/core-roles.mcp-tools.js.map +1 -0
- package/dist/mcp/tools/core-routes.mcp-tools.d.ts +52 -0
- package/dist/mcp/tools/core-routes.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-routes.mcp-tools.js +254 -0
- package/dist/mcp/tools/core-routes.mcp-tools.js.map +1 -0
- package/dist/mcp/tools/core-screens.mcp-tools.d.ts +47 -0
- package/dist/mcp/tools/core-screens.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-screens.mcp-tools.js +250 -0
- package/dist/mcp/tools/core-screens.mcp-tools.js.map +1 -0
- package/dist/mcp/tools/core-sessions.mcp-tools.d.ts +20 -0
- package/dist/mcp/tools/core-sessions.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-sessions.mcp-tools.js +128 -0
- package/dist/mcp/tools/core-sessions.mcp-tools.js.map +1 -0
- package/dist/mcp/tools/core-settings.mcp-tools.d.ts +25 -0
- package/dist/mcp/tools/core-settings.mcp-tools.d.ts.map +1 -1
- package/dist/mcp/tools/core-settings.mcp-tools.js +138 -0
- package/dist/mcp/tools/core-settings.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-system.mcp-tools.d.ts +8 -0
- package/dist/mcp/tools/core-system.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-system.mcp-tools.js +40 -0
- package/dist/mcp/tools/core-system.mcp-tools.js.map +1 -0
- package/dist/mcp/tools/core-webhooks.mcp-tools.d.ts +46 -0
- package/dist/mcp/tools/core-webhooks.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-webhooks.mcp-tools.js +266 -0
- package/dist/mcp/tools/core-webhooks.mcp-tools.js.map +1 -0
- package/dist/mcp-chat/mcp-chat.controller.d.ts +1 -1
- package/dist/mcp-chat/mcp-chat.service.d.ts +25 -2
- package/dist/mcp-chat/mcp-chat.service.d.ts.map +1 -1
- package/dist/mcp-chat/mcp-chat.service.js +666 -36
- package/dist/mcp-chat/mcp-chat.service.js.map +1 -1
- package/dist/notification/notification.controller.d.ts +8 -4
- package/dist/notification/notification.controller.d.ts.map +1 -1
- package/dist/notification/notification.controller.js +12 -0
- package/dist/notification/notification.controller.js.map +1 -1
- package/dist/notification/notification.service.d.ts +19 -5
- package/dist/notification/notification.service.d.ts.map +1 -1
- package/dist/notification/notification.service.js +155 -9
- package/dist/notification/notification.service.js.map +1 -1
- package/dist/pdf/pdf.service.js +1 -1
- package/dist/pdf/pdf.service.js.map +1 -1
- package/dist/profile/profile.controller.d.ts +3 -3
- package/dist/profile/profile.controller.js +0 -1
- package/dist/profile/profile.controller.js.map +1 -1
- package/dist/profile/profile.module.d.ts.map +1 -1
- package/dist/profile/profile.module.js +1 -0
- package/dist/profile/profile.module.js.map +1 -1
- package/dist/profile/profile.service.d.ts +3 -3
- package/dist/session/session.controller.d.ts +2 -2
- package/dist/session/session.service.d.ts +6 -6
- package/dist/setting/setting.controller.d.ts +1 -1
- package/dist/setting/setting.service.d.ts +1 -1
- package/dist/setting/setting.service.d.ts.map +1 -1
- package/dist/setting/setting.service.js +1 -4
- package/dist/setting/setting.service.js.map +1 -1
- package/dist/task/task.service.d.ts +1 -0
- package/dist/task/task.service.d.ts.map +1 -1
- package/dist/task/task.service.js +25 -0
- package/dist/task/task.service.js.map +1 -1
- package/dist/token/token.service.d.ts.map +1 -1
- package/dist/token/token.service.js +0 -1
- package/dist/token/token.service.js.map +1 -1
- package/dist/user/user.controller.d.ts +9 -9
- package/dist/user/user.service.d.ts +21 -21
- package/dist/webhook-integration/dto/webhook-action.dto.d.ts +22 -0
- package/dist/webhook-integration/dto/webhook-action.dto.d.ts.map +1 -0
- package/dist/webhook-integration/dto/webhook-action.dto.js +120 -0
- package/dist/webhook-integration/dto/webhook-action.dto.js.map +1 -0
- package/dist/webhook-integration/webhook-integration.controller.d.ts +7 -5
- package/dist/webhook-integration/webhook-integration.controller.d.ts.map +1 -1
- package/dist/webhook-integration/webhook-integration.controller.js +18 -5
- package/dist/webhook-integration/webhook-integration.controller.js.map +1 -1
- package/dist/webhook-integration/webhook-integration.service.d.ts +14 -5
- package/dist/webhook-integration/webhook-integration.service.d.ts.map +1 -1
- package/dist/webhook-integration/webhook-integration.service.js +174 -41
- package/dist/webhook-integration/webhook-integration.service.js.map +1 -1
- package/hedhog/data/menu.yaml +15 -0
- package/hedhog/data/route.yaml +1304 -0
- package/hedhog/data/setting_group.yaml +67 -0
- package/hedhog/data/setting_subgroup.yaml +16 -0
- package/hedhog/frontend/app/access-log/page.tsx.ejs +772 -0
- package/hedhog/frontend/app/ai_agent/page.tsx.ejs +10 -3
- package/hedhog/frontend/app/dashboard/dashboard-home-tabs.tsx.ejs +33 -13
- package/hedhog/frontend/app/dashboard/management/tabs/components-tab.tsx.ejs +12 -5
- package/hedhog/frontend/app/dashboard/management/tabs/dashboards-tab.tsx.ejs +12 -5
- package/hedhog/frontend/app/integrations-webhooks/page.tsx.ejs +1817 -356
- package/hedhog/frontend/app/mcp_chat/chat-components.tsx.ejs +43 -5
- package/hedhog/frontend/app/mcp_chat/page.tsx.ejs +35 -7
- package/hedhog/frontend/app/menu/page.tsx.ejs +9 -3
- package/hedhog/frontend/app/roles/page.tsx.ejs +18 -6
- package/hedhog/frontend/app/routes/page.tsx.ejs +9 -3
- package/hedhog/frontend/app/setting-list/page.tsx.ejs +344 -171
- package/hedhog/frontend/app/users/user-access-sheet.tsx.ejs +310 -304
- package/hedhog/frontend/app/users/user-sheet.tsx.ejs +25 -7
- package/hedhog/frontend/messages/en.json +104 -3
- package/hedhog/frontend/messages/pt.json +104 -3
- package/hedhog/table/access_log.yaml +31 -0
- package/package.json +5 -4
- package/src/access-log/access-log.controller.ts +29 -0
- package/src/access-log/access-log.interceptor.ts +38 -0
- package/src/access-log/access-log.module.ts +16 -0
- package/src/access-log/access-log.service.ts +129 -0
- package/src/access-log/dto/query-access-log.dto.ts +23 -0
- package/src/auth/auth.service.ts +0 -7
- package/src/challenge/challenge.service.ts +0 -2
- package/src/core/system.module.ts +1 -0
- package/src/core.module.ts +12 -0
- package/src/dashboard/dashboard.module.ts +4 -1
- package/src/file/file.controller.ts +14 -14
- package/src/file/file.service.ts +198 -4
- package/src/file/provider/abstract.provider.ts +10 -0
- package/src/file/provider/azure.provider.ts +24 -0
- package/src/file/provider/gcs.provider.ts +20 -0
- package/src/file/provider/local.provider.ts +30 -2
- package/src/file/provider/provider.factory.ts +3 -0
- package/src/file/provider/s3.provider.ts +22 -0
- package/src/mcp/mcp-audit.service.ts +47 -36
- package/src/mcp/mcp.controller.ts +40 -30
- package/src/mcp/mcp.module.ts +72 -0
- package/src/mcp/tools/core-ai.mcp-tools.ts +156 -0
- package/src/mcp/tools/core-dashboard-items.mcp-tools.ts +63 -0
- package/src/mcp/tools/core-dashboard.mcp-tools.ts +103 -0
- package/src/mcp/tools/core-event-webhooks.mcp-tools.ts +134 -0
- package/src/mcp/tools/core-files.mcp-tools.ts +105 -0
- package/src/mcp/tools/core-integration-events.mcp-tools.ts +118 -0
- package/src/mcp/tools/core-mail-profile.mcp-tools.ts +128 -0
- package/src/mcp/tools/core-mail-sent.mcp-tools.ts +76 -0
- package/src/mcp/tools/core-mail.mcp-tools.ts +130 -0
- package/src/mcp/tools/core-mcp-chat.mcp-tools.ts +81 -0
- package/src/mcp/tools/core-menus.mcp-tools.ts +197 -0
- package/src/mcp/tools/core-notifications.mcp-tools.ts +128 -0
- package/src/mcp/tools/core-profile.mcp-tools.ts +74 -0
- package/src/mcp/tools/core-roles.mcp-tools.ts +288 -0
- package/src/mcp/tools/core-routes.mcp-tools.ts +196 -0
- package/src/mcp/tools/core-screens.mcp-tools.ts +186 -0
- package/src/mcp/tools/core-sessions.mcp-tools.ts +97 -0
- package/src/mcp/tools/core-settings.mcp-tools.ts +171 -53
- package/src/mcp/tools/core-system.mcp-tools.ts +19 -0
- package/src/mcp/tools/core-webhooks.mcp-tools.ts +199 -0
- package/src/mcp-chat/mcp-chat.service.ts +887 -38
- package/src/notification/notification.controller.ts +20 -8
- package/src/notification/notification.service.ts +209 -9
- package/src/pdf/pdf.service.ts +1 -1
- package/src/profile/profile.controller.ts +1 -1
- package/src/profile/profile.module.ts +1 -0
- package/src/setting/setting.service.ts +8 -11
- package/src/task/task.service.ts +31 -0
- package/src/token/token.service.ts +0 -2
- package/src/webhook-integration/dto/webhook-action.dto.ts +96 -0
- package/src/webhook-integration/webhook-integration.controller.ts +124 -103
- package/src/webhook-integration/webhook-integration.service.ts +267 -64
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { IsIn, IsNumberString, IsOptional, IsString } from 'class-validator';
|
|
2
|
+
|
|
3
|
+
export class QueryAccessLogDto {
|
|
4
|
+
@IsOptional()
|
|
5
|
+
@IsNumberString()
|
|
6
|
+
userId?: string;
|
|
7
|
+
|
|
8
|
+
@IsOptional()
|
|
9
|
+
@IsString()
|
|
10
|
+
userSearch?: string;
|
|
11
|
+
|
|
12
|
+
@IsOptional()
|
|
13
|
+
@IsString()
|
|
14
|
+
createdAtFrom?: string;
|
|
15
|
+
|
|
16
|
+
@IsOptional()
|
|
17
|
+
@IsString()
|
|
18
|
+
createdAtTo?: string;
|
|
19
|
+
|
|
20
|
+
@IsOptional()
|
|
21
|
+
@IsIn(['http', 'mcp', 'all'])
|
|
22
|
+
type?: 'http' | 'mcp' | 'all';
|
|
23
|
+
}
|
package/src/auth/auth.service.ts
CHANGED
|
@@ -82,8 +82,6 @@ export class AuthService {
|
|
|
82
82
|
|
|
83
83
|
async requiresMfaForLogin(locale: string, email: string, user: any) {
|
|
84
84
|
|
|
85
|
-
console.log('MFA required, setting up email MFA');
|
|
86
|
-
|
|
87
85
|
const settings = await this.setting.getSettingValues([
|
|
88
86
|
'require-mfa',
|
|
89
87
|
'require-email-verification',
|
|
@@ -387,16 +385,11 @@ export class AuthService {
|
|
|
387
385
|
|
|
388
386
|
if (!user) throw new BadRequestException(getLocaleText('accessDenied', locale, 'Access denied.'));
|
|
389
387
|
|
|
390
|
-
console.debug({ user });
|
|
391
|
-
|
|
392
388
|
const credentials = (user as unknown as User).user_credential?.filter((c) => c.type === 'password') || [];
|
|
393
389
|
|
|
394
|
-
console.debug({ credentials });
|
|
395
|
-
|
|
396
390
|
const identifier = user.user_identifier?.find((i) => i.type === 'email' && i.value === email);
|
|
397
391
|
|
|
398
392
|
if (!(await this.security.validatePassword(locale, credentials, password))) {
|
|
399
|
-
console.debug('Invalid password');
|
|
400
393
|
throw new BadRequestException(getLocaleText('accessDenied', locale, 'Access denied.'));
|
|
401
394
|
}
|
|
402
395
|
|
|
@@ -376,8 +376,6 @@ export class ChallengeService {
|
|
|
376
376
|
|
|
377
377
|
async sendMfaCodeToMultipleEmails(locale: string, userId: number, emails: string[]) {
|
|
378
378
|
|
|
379
|
-
console.log('Sending MFA codes to multiple emails:', emails);
|
|
380
|
-
|
|
381
379
|
if (!emails || emails.length === 0) {
|
|
382
380
|
return { success: true };
|
|
383
381
|
}
|
package/src/core.module.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
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';
|
|
3
6
|
import { MailModule as MailSendModule } from '@hed-hog/api-mail';
|
|
4
7
|
import { PaginationModule } from '@hed-hog/api-pagination';
|
|
5
8
|
import { PrismaModule, PrismaService } from '@hed-hog/api-prisma';
|
|
@@ -75,6 +78,7 @@ import { WebhookIntegrationModule } from './webhook-integration/webhook-integrat
|
|
|
75
78
|
forwardRef(() => McpChatModule),
|
|
76
79
|
forwardRef(() => PdfModule),
|
|
77
80
|
forwardRef(() => NotificationModule),
|
|
81
|
+
forwardRef(() => AccessLogModule),
|
|
78
82
|
],
|
|
79
83
|
exports: [
|
|
80
84
|
UserModule,
|
|
@@ -97,6 +101,7 @@ import { WebhookIntegrationModule } from './webhook-integration/webhook-integrat
|
|
|
97
101
|
IsPinCodeWithSettingConstraint,
|
|
98
102
|
PdfModule,
|
|
99
103
|
NotificationModule,
|
|
104
|
+
AccessLogModule,
|
|
100
105
|
],
|
|
101
106
|
providers: [
|
|
102
107
|
Reflector,
|
|
@@ -110,6 +115,13 @@ import { WebhookIntegrationModule } from './webhook-integration/webhook-integrat
|
|
|
110
115
|
},
|
|
111
116
|
inject: [Reflector, PrismaService],
|
|
112
117
|
},
|
|
118
|
+
{
|
|
119
|
+
provide: APP_INTERCEPTOR,
|
|
120
|
+
useFactory: (accessLogService: AccessLogService) => {
|
|
121
|
+
return new AccessLogInterceptor(accessLogService);
|
|
122
|
+
},
|
|
123
|
+
inject: [AccessLogService],
|
|
124
|
+
},
|
|
113
125
|
]
|
|
114
126
|
})
|
|
115
127
|
export class CoreModule implements NestModule, OnModuleInit {
|
|
@@ -23,6 +23,9 @@ import { DashboardCrudModule } from './dashboard/dashboard.module';
|
|
|
23
23
|
],
|
|
24
24
|
controllers: [],
|
|
25
25
|
providers: [],
|
|
26
|
-
exports: [
|
|
26
|
+
exports: [
|
|
27
|
+
forwardRef(() => DashboardCrudModule),
|
|
28
|
+
forwardRef(() => DashboardItemModule),
|
|
29
|
+
],
|
|
27
30
|
})
|
|
28
31
|
export class DashboardModule {}
|
|
@@ -2,19 +2,19 @@ import { Public, Role } from '@hed-hog/api';
|
|
|
2
2
|
import { getLocaleText, Locale } from '@hed-hog/api-locale';
|
|
3
3
|
import { Pagination } from '@hed-hog/api-pagination';
|
|
4
4
|
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
5
|
+
Body,
|
|
6
|
+
Controller,
|
|
7
|
+
Delete,
|
|
8
|
+
forwardRef,
|
|
9
|
+
Get,
|
|
10
|
+
Inject,
|
|
11
|
+
Param,
|
|
12
|
+
ParseIntPipe,
|
|
13
|
+
Post,
|
|
14
|
+
Put,
|
|
15
|
+
Res,
|
|
16
|
+
UploadedFile,
|
|
17
|
+
UseInterceptors,
|
|
18
18
|
} from '@nestjs/common';
|
|
19
19
|
import { FileInterceptor } from '@nestjs/platform-express';
|
|
20
20
|
import { Response } from 'express';
|
|
@@ -54,7 +54,7 @@ export class FileController {
|
|
|
54
54
|
|
|
55
55
|
@Put('download/:id')
|
|
56
56
|
async getTempURL(@Param('id', ParseIntPipe) id) {
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
return {
|
|
59
59
|
url: await this.fileService.tempURL(
|
|
60
60
|
(await this.fileService.get(id)).path,
|
package/src/file/file.service.ts
CHANGED
|
@@ -10,6 +10,9 @@ import {
|
|
|
10
10
|
OnModuleInit
|
|
11
11
|
} from '@nestjs/common';
|
|
12
12
|
import { JwtService } from '@nestjs/jwt';
|
|
13
|
+
import { createWriteStream, promises as fs } from 'fs';
|
|
14
|
+
import { basename, dirname } from 'path';
|
|
15
|
+
import { pipeline } from 'stream/promises';
|
|
13
16
|
import { SettingService } from '../setting/setting.service';
|
|
14
17
|
import { DeleteDTO } from './dto/delete.dto';
|
|
15
18
|
import { AbstractProvider } from './provider/abstract.provider';
|
|
@@ -235,6 +238,113 @@ export class FileService implements OnModuleInit {
|
|
|
235
238
|
return settings['storage'];
|
|
236
239
|
}
|
|
237
240
|
|
|
241
|
+
private getProviderPath(storage: string, filepath: string) {
|
|
242
|
+
if (storage === EnumProvider.LOCAL) {
|
|
243
|
+
return filepath;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
try {
|
|
247
|
+
return new URL(filepath).pathname;
|
|
248
|
+
} catch {
|
|
249
|
+
return filepath;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
private mapStorageNotFoundError(error: any, fileId: number) {
|
|
254
|
+
const missingInStorage =
|
|
255
|
+
error?.name === 'NoSuchKey' ||
|
|
256
|
+
error?.Code === 'NoSuchKey' ||
|
|
257
|
+
error?.$metadata?.httpStatusCode === 404;
|
|
258
|
+
|
|
259
|
+
if (missingInStorage) {
|
|
260
|
+
throw new NotFoundException(`File not found in storage: ${fileId}`);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
throw error;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
async openReadStreamById(fileId: number) {
|
|
267
|
+
if (!fileId) {
|
|
268
|
+
throw new BadRequestException(`No file ID provided`);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const file = await this.prismaService.file.findUnique({
|
|
272
|
+
where: {
|
|
273
|
+
id: fileId,
|
|
274
|
+
},
|
|
275
|
+
include: {
|
|
276
|
+
file_mimetype: true,
|
|
277
|
+
},
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
if (!file) {
|
|
281
|
+
throw new NotFoundException(`File not found: ${fileId}`);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const storage = await this.getStorage();
|
|
285
|
+
const provider = await this.getProvider();
|
|
286
|
+
const providerPath = this.getProviderPath(storage, file.path);
|
|
287
|
+
|
|
288
|
+
let metadata: any;
|
|
289
|
+
try {
|
|
290
|
+
metadata = await provider.metaData(providerPath);
|
|
291
|
+
} catch {
|
|
292
|
+
metadata = undefined;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
try {
|
|
296
|
+
const stream = await provider.readStream(providerPath);
|
|
297
|
+
return {
|
|
298
|
+
file,
|
|
299
|
+
stream,
|
|
300
|
+
size: file.size ?? this.getFileSizeFromMetadata(metadata),
|
|
301
|
+
};
|
|
302
|
+
} catch (error) {
|
|
303
|
+
this.mapStorageNotFoundError(error, fileId);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
async downloadToPath(
|
|
308
|
+
fileId: number,
|
|
309
|
+
destinationPath: string,
|
|
310
|
+
options?: { maxBytes?: number },
|
|
311
|
+
) {
|
|
312
|
+
if (!destinationPath) {
|
|
313
|
+
throw new BadRequestException(`No destination path provided`);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const { file, stream, size } = await this.openReadStreamById(fileId);
|
|
317
|
+
|
|
318
|
+
if (options?.maxBytes && size && size > options.maxBytes) {
|
|
319
|
+
throw new BadRequestException(
|
|
320
|
+
`File too large for streaming download: ${size} bytes (max ${options.maxBytes})`,
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
await fs.mkdir(dirname(destinationPath), { recursive: true });
|
|
325
|
+
|
|
326
|
+
try {
|
|
327
|
+
await pipeline(stream, createWriteStream(destinationPath));
|
|
328
|
+
const stats = await fs.stat(destinationPath);
|
|
329
|
+
|
|
330
|
+
if (options?.maxBytes && stats.size > options.maxBytes) {
|
|
331
|
+
await fs.unlink(destinationPath).catch(() => undefined);
|
|
332
|
+
throw new BadRequestException(
|
|
333
|
+
`File too large for streaming download: ${stats.size} bytes (max ${options.maxBytes})`,
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return {
|
|
338
|
+
file,
|
|
339
|
+
path: destinationPath,
|
|
340
|
+
size: stats.size,
|
|
341
|
+
};
|
|
342
|
+
} catch (error) {
|
|
343
|
+
await fs.unlink(destinationPath).catch(() => undefined);
|
|
344
|
+
throw error;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
238
348
|
async getBuffer(fileId: number) {
|
|
239
349
|
|
|
240
350
|
if (!fileId) {
|
|
@@ -258,8 +368,7 @@ export class FileService implements OnModuleInit {
|
|
|
258
368
|
|
|
259
369
|
const provider = await this.getProvider();
|
|
260
370
|
|
|
261
|
-
const providerPath =
|
|
262
|
-
storage === EnumProvider.LOCAL ? file.path : new URL(file.path).pathname;
|
|
371
|
+
const providerPath = this.getProviderPath(storage, file.path);
|
|
263
372
|
|
|
264
373
|
let providerBuffer: Buffer;
|
|
265
374
|
try {
|
|
@@ -341,6 +450,89 @@ export class FileService implements OnModuleInit {
|
|
|
341
450
|
return file;
|
|
342
451
|
}
|
|
343
452
|
|
|
453
|
+
async uploadFromPath(
|
|
454
|
+
destination: string,
|
|
455
|
+
filePath: string,
|
|
456
|
+
metadata?: {
|
|
457
|
+
originalname?: string;
|
|
458
|
+
filename?: string;
|
|
459
|
+
mimetype?: string;
|
|
460
|
+
size?: number;
|
|
461
|
+
encoding?: string;
|
|
462
|
+
},
|
|
463
|
+
) {
|
|
464
|
+
if (!filePath) {
|
|
465
|
+
throw new BadRequestException(`No file path provided`);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
if (!destination) {
|
|
469
|
+
throw new BadRequestException(`No destination provided`);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
const stats = await fs.stat(filePath).catch(() => null);
|
|
473
|
+
if (!stats || !stats.isFile()) {
|
|
474
|
+
throw new NotFoundException(`File path not found: ${filePath}`);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
const size = metadata?.size ?? stats.size;
|
|
478
|
+
const mimetype = metadata?.mimetype ?? 'application/octet-stream';
|
|
479
|
+
const originalname = this.normalizeUploadedFilename(
|
|
480
|
+
metadata?.originalname ?? metadata?.filename ?? basename(filePath),
|
|
481
|
+
);
|
|
482
|
+
|
|
483
|
+
if (!(await this.acceptMimetypes(mimetype))) {
|
|
484
|
+
throw new BadRequestException(`Invalid file type: ${mimetype}`);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
if (!(await this.maxFileSize(size))) {
|
|
488
|
+
throw new BadRequestException(`File too large: ${size} bytes`);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
const provider = await this.getProvider();
|
|
492
|
+
const providerWithPathUpload = provider as AbstractProvider & {
|
|
493
|
+
uploadFromPath?: (
|
|
494
|
+
destination: string,
|
|
495
|
+
filePath: string,
|
|
496
|
+
options?: { filename?: string; mimetype?: string },
|
|
497
|
+
) => Promise<string>;
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
let url: string;
|
|
501
|
+
if (typeof providerWithPathUpload.uploadFromPath === 'function') {
|
|
502
|
+
url = await providerWithPathUpload.uploadFromPath(destination, filePath, {
|
|
503
|
+
filename: originalname,
|
|
504
|
+
mimetype,
|
|
505
|
+
});
|
|
506
|
+
} else {
|
|
507
|
+
// Fallback explícito para providers legados sem suporte nativo por path.
|
|
508
|
+
const buffer = await fs.readFile(filePath);
|
|
509
|
+
url = await provider.upload(destination, {
|
|
510
|
+
fieldname: 'file',
|
|
511
|
+
originalname,
|
|
512
|
+
encoding: metadata?.encoding ?? '7bit',
|
|
513
|
+
mimetype,
|
|
514
|
+
size,
|
|
515
|
+
destination: '',
|
|
516
|
+
filename: originalname,
|
|
517
|
+
path: filePath,
|
|
518
|
+
buffer,
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
const file = await this.prismaService.file.create({
|
|
523
|
+
data: {
|
|
524
|
+
filename: originalname,
|
|
525
|
+
path: url,
|
|
526
|
+
provider_id: this.providerId,
|
|
527
|
+
location: destination,
|
|
528
|
+
mimetype_id: await this.getMimeType(mimetype),
|
|
529
|
+
size,
|
|
530
|
+
},
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
return file;
|
|
534
|
+
}
|
|
535
|
+
|
|
344
536
|
async delete(locale:string,{ ids }: DeleteDTO) {
|
|
345
537
|
const files = await this.prismaService.file.findMany({
|
|
346
538
|
where: {
|
|
@@ -482,9 +674,11 @@ export class FileService implements OnModuleInit {
|
|
|
482
674
|
);
|
|
483
675
|
}
|
|
484
676
|
|
|
677
|
+
const storage = await this.getStorage();
|
|
485
678
|
const provider = await this.getProvider();
|
|
486
|
-
const
|
|
487
|
-
const
|
|
679
|
+
const providerPath = this.getProviderPath(storage, file.path);
|
|
680
|
+
const metadata = await provider.metaData(providerPath);
|
|
681
|
+
const stream = await provider.readStream(providerPath);
|
|
488
682
|
const size = this.getFileSizeFromMetadata(metadata);
|
|
489
683
|
|
|
490
684
|
return {
|
|
@@ -9,6 +9,11 @@ interface IProvider {
|
|
|
9
9
|
mimetype?: string,
|
|
10
10
|
): Promise<any>;
|
|
11
11
|
upload(destination: string, file: MulterFile): Promise<any>;
|
|
12
|
+
uploadFromPath(
|
|
13
|
+
destination: string,
|
|
14
|
+
filePath: string,
|
|
15
|
+
options?: { filename?: string; mimetype?: string },
|
|
16
|
+
): Promise<any>;
|
|
12
17
|
readStream(filepath: string): Promise<any>;
|
|
13
18
|
delete(filepath: string): Promise<any>;
|
|
14
19
|
metaData(filepath: string): Promise<any>;
|
|
@@ -25,6 +30,11 @@ export abstract class AbstractProvider implements IProvider {
|
|
|
25
30
|
mimetype?: string,
|
|
26
31
|
): Promise<any>;
|
|
27
32
|
abstract upload(destination: string, file: MulterFile): Promise<any>;
|
|
33
|
+
abstract uploadFromPath(
|
|
34
|
+
destination: string,
|
|
35
|
+
filePath: string,
|
|
36
|
+
options?: { filename?: string; mimetype?: string },
|
|
37
|
+
): Promise<any>;
|
|
28
38
|
abstract readStream(filepath: string): Promise<any>;
|
|
29
39
|
abstract delete(filepath: string): Promise<any>;
|
|
30
40
|
abstract metaData(filepath: string): Promise<any>;
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
StorageSharedKeyCredential,
|
|
6
6
|
} from '@azure/storage-blob';
|
|
7
7
|
import { BadRequestException } from '@nestjs/common';
|
|
8
|
+
import { basename } from 'path';
|
|
8
9
|
import { AbstractProvider } from './abstract.provider';
|
|
9
10
|
|
|
10
11
|
export class AzureProvider extends AbstractProvider {
|
|
@@ -92,6 +93,29 @@ export class AzureProvider extends AbstractProvider {
|
|
|
92
93
|
return `https://${this.setting['storage-abs-container']}.azureedge.net/${filepath}`;
|
|
93
94
|
}
|
|
94
95
|
|
|
96
|
+
async uploadFromPath(
|
|
97
|
+
destination: string,
|
|
98
|
+
filePath: string,
|
|
99
|
+
options?: { filename?: string; mimetype?: string },
|
|
100
|
+
): Promise<any> {
|
|
101
|
+
const blobServiceClient = await this.getClient();
|
|
102
|
+
const containerClient = blobServiceClient.getContainerClient(
|
|
103
|
+
this.setting['storage-abs-container'],
|
|
104
|
+
);
|
|
105
|
+
const filepath = [destination, this.getFilename(options?.filename ?? basename(filePath))].join(
|
|
106
|
+
'/',
|
|
107
|
+
);
|
|
108
|
+
const blobClient = containerClient.getBlockBlobClient(filepath);
|
|
109
|
+
|
|
110
|
+
await blobClient.uploadFile(filePath, {
|
|
111
|
+
blobHTTPHeaders: options?.mimetype
|
|
112
|
+
? { blobContentType: options.mimetype }
|
|
113
|
+
: undefined,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
return `https://${this.setting['storage-abs-container']}.azureedge.net/${filepath}`;
|
|
117
|
+
}
|
|
118
|
+
|
|
95
119
|
async delete(filepath: string): Promise<any> {
|
|
96
120
|
const url = new URL(filepath);
|
|
97
121
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Storage } from '@google-cloud/storage';
|
|
2
2
|
import { BadRequestException } from '@nestjs/common';
|
|
3
|
+
import { basename } from 'path';
|
|
3
4
|
import { AbstractProvider } from './abstract.provider';
|
|
4
5
|
|
|
5
6
|
export class GCSProvider extends AbstractProvider {
|
|
@@ -67,6 +68,25 @@ export class GCSProvider extends AbstractProvider {
|
|
|
67
68
|
contentType: file.mimetype,
|
|
68
69
|
});
|
|
69
70
|
}
|
|
71
|
+
|
|
72
|
+
async uploadFromPath(
|
|
73
|
+
destination: string,
|
|
74
|
+
filePath: string,
|
|
75
|
+
options?: { filename?: string; mimetype?: string },
|
|
76
|
+
): Promise<any> {
|
|
77
|
+
const storage = await this.getClient();
|
|
78
|
+
const bucket = storage.bucket(this.setting['storage-gcs-bucket']);
|
|
79
|
+
const objectPath = [destination.replace(/\/$/, ''), this.getFilename(options?.filename ?? basename(filePath))]
|
|
80
|
+
.filter(Boolean)
|
|
81
|
+
.join('/');
|
|
82
|
+
|
|
83
|
+
await bucket.upload(filePath, {
|
|
84
|
+
destination: objectPath,
|
|
85
|
+
contentType: options?.mimetype,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
return objectPath;
|
|
89
|
+
}
|
|
70
90
|
async readStream(filepath: string): Promise<any> {
|
|
71
91
|
const storage = await this.getClient();
|
|
72
92
|
const bucket = storage.bucket(this.setting['storage-gcs-bucket']);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { BadRequestException } from '@nestjs/common';
|
|
2
2
|
import { JwtService } from '@nestjs/jwt';
|
|
3
3
|
import { createReadStream, existsSync, promises as fs } from 'fs';
|
|
4
|
-
import { mkdir, unlink, writeFile } from 'fs/promises';
|
|
4
|
+
import { copyFile, mkdir, stat, unlink, writeFile } from 'fs/promises';
|
|
5
5
|
import * as path from 'path';
|
|
6
|
-
import { join, sep } from 'path';
|
|
6
|
+
import { basename, join, sep } from 'path';
|
|
7
7
|
import { Stream } from 'stream';
|
|
8
8
|
import { AbstractProvider } from './abstract.provider';
|
|
9
9
|
|
|
@@ -99,6 +99,34 @@ export class LocalProvider extends AbstractProvider {
|
|
|
99
99
|
return filePath;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
async uploadFromPath(
|
|
103
|
+
destination: string,
|
|
104
|
+
filePath: string,
|
|
105
|
+
options?: { filename?: string; mimetype?: string },
|
|
106
|
+
): Promise<any> {
|
|
107
|
+
const sourcePath = this.resolveFilepath(filePath);
|
|
108
|
+
if (!existsSync(sourcePath)) {
|
|
109
|
+
throw new BadRequestException(`File not found: ${sourcePath}`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const sourceStats = await stat(sourcePath);
|
|
113
|
+
if (!sourceStats.isFile()) {
|
|
114
|
+
throw new BadRequestException(`Invalid file path: ${sourcePath}`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const storagePath = join(this.setting['storage-local-path'], destination);
|
|
118
|
+
|
|
119
|
+
if (!existsSync(storagePath)) {
|
|
120
|
+
await this.createFolderRecursive(storagePath);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const targetFilename = this.getFilename(options?.filename ?? basename(sourcePath));
|
|
124
|
+
const targetPath = join(storagePath, targetFilename);
|
|
125
|
+
await copyFile(sourcePath, targetPath);
|
|
126
|
+
|
|
127
|
+
return targetPath;
|
|
128
|
+
}
|
|
129
|
+
|
|
102
130
|
async delete(filepath: string): Promise<any> {
|
|
103
131
|
const fullPath = this.resolveFilepath(filepath);
|
|
104
132
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { JwtService } from '@nestjs/jwt';
|
|
2
2
|
import { AbstractProvider } from './abstract.provider';
|
|
3
3
|
import { AzureProvider } from './azure.provider';
|
|
4
|
+
import { GCSProvider } from './gcs.provider';
|
|
4
5
|
import { LocalProvider } from './local.provider';
|
|
5
6
|
import { EnumProvider } from './provider.enum';
|
|
6
7
|
import { S3Provider } from './s3.provider';
|
|
@@ -18,6 +19,8 @@ export class ProviderFactory {
|
|
|
18
19
|
return new LocalProvider(setting, jwtService);
|
|
19
20
|
case EnumProvider.AZURE:
|
|
20
21
|
return new AzureProvider(setting);
|
|
22
|
+
case EnumProvider.GCS:
|
|
23
|
+
return new GCSProvider(setting);
|
|
21
24
|
}
|
|
22
25
|
}
|
|
23
26
|
}
|
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
} from '@aws-sdk/client-s3';
|
|
10
10
|
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
|
11
11
|
import { BadRequestException } from '@nestjs/common';
|
|
12
|
+
import { createReadStream } from 'fs';
|
|
13
|
+
import { basename } from 'path';
|
|
12
14
|
import { Readable } from 'stream';
|
|
13
15
|
import { AbstractProvider } from './abstract.provider';
|
|
14
16
|
|
|
@@ -166,6 +168,26 @@ export class S3Provider extends AbstractProvider {
|
|
|
166
168
|
return `https://${this.setting['storage-s3-bucket']}.s3.${this.setting['storage-s3-region']}.amazonaws.com/${key}`;
|
|
167
169
|
}
|
|
168
170
|
|
|
171
|
+
async uploadFromPath(
|
|
172
|
+
destination: string,
|
|
173
|
+
filePath: string,
|
|
174
|
+
options?: { filename?: string; mimetype?: string },
|
|
175
|
+
): Promise<any> {
|
|
176
|
+
const s3 = await this.getClient();
|
|
177
|
+
const originalName = options?.filename ?? basename(filePath);
|
|
178
|
+
const storedFilename = this.getFilename(originalName);
|
|
179
|
+
const key = [destination, storedFilename].join('/');
|
|
180
|
+
const command = new PutObjectCommand({
|
|
181
|
+
Bucket: this.setting['storage-s3-bucket'],
|
|
182
|
+
Key: key,
|
|
183
|
+
Body: createReadStream(filePath),
|
|
184
|
+
ContentType: options?.mimetype,
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
await s3.send(command);
|
|
188
|
+
return `https://${this.setting['storage-s3-bucket']}.s3.${this.setting['storage-s3-region']}.amazonaws.com/${key}`;
|
|
189
|
+
}
|
|
190
|
+
|
|
169
191
|
async delete(filepath: string): Promise<any> {
|
|
170
192
|
const s3 = await this.getClient();
|
|
171
193
|
const key = this.resolveS3Key(filepath);
|
|
@@ -1,36 +1,47 @@
|
|
|
1
|
-
import { PrismaService } from '@hed-hog/api-prisma';
|
|
2
|
-
import { Injectable, Logger } from '@nestjs/common';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
1
|
+
import { PrismaService } from '@hed-hog/api-prisma';
|
|
2
|
+
import { forwardRef, Inject, Injectable, Logger } from '@nestjs/common';
|
|
3
|
+
import { AccessLogService } from '../access-log/access-log.service';
|
|
4
|
+
|
|
5
|
+
interface AuditRecord {
|
|
6
|
+
userId: number;
|
|
7
|
+
toolName: string;
|
|
8
|
+
input: Record<string, any>;
|
|
9
|
+
success: boolean;
|
|
10
|
+
error?: string;
|
|
11
|
+
durationMs: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
@Injectable()
|
|
15
|
+
export class McpAuditService {
|
|
16
|
+
private readonly logger = new Logger(McpAuditService.name);
|
|
17
|
+
|
|
18
|
+
constructor(
|
|
19
|
+
private readonly prisma: PrismaService,
|
|
20
|
+
@Inject(forwardRef(() => AccessLogService))
|
|
21
|
+
private readonly accessLogService: AccessLogService,
|
|
22
|
+
) {}
|
|
23
|
+
|
|
24
|
+
async record(data: AuditRecord): Promise<void> {
|
|
25
|
+
const sanitized = JSON.stringify(data.input).slice(0, 1000);
|
|
26
|
+
try {
|
|
27
|
+
await this.prisma.mcp_tool_call_log.create({
|
|
28
|
+
data: {
|
|
29
|
+
user_id: data.userId || null,
|
|
30
|
+
tool_name: data.toolName,
|
|
31
|
+
input: sanitized,
|
|
32
|
+
success: data.success,
|
|
33
|
+
error: data.error ?? null,
|
|
34
|
+
duration_ms: data.durationMs,
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
} catch (e: any) {
|
|
38
|
+
this.logger.warn(`Failed to persist MCP audit record: ${e.message}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
this.accessLogService.recordMcp({
|
|
42
|
+
userId: data.userId || undefined,
|
|
43
|
+
mcpTool: data.toolName,
|
|
44
|
+
mcpPrompt: sanitized,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|