@hed-hog/core 0.0.399 → 0.0.401
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/ai/ai-agent.sort.d.ts +6 -0
- package/dist/ai/ai-agent.sort.d.ts.map +1 -0
- package/dist/ai/ai-agent.sort.js +21 -0
- package/dist/ai/ai-agent.sort.js.map +1 -0
- package/dist/ai/ai.controller.d.ts +6 -4
- package/dist/ai/ai.controller.d.ts.map +1 -1
- package/dist/ai/ai.service.d.ts +121 -6
- package/dist/ai/ai.service.d.ts.map +1 -1
- package/dist/ai/ai.service.js +355 -160
- package/dist/ai/ai.service.js.map +1 -1
- package/dist/ai-instruction/ai-instruction.service.d.ts.map +1 -1
- package/dist/ai-instruction/ai-instruction.service.js +26 -23
- package/dist/ai-instruction/ai-instruction.service.js.map +1 -1
- package/dist/ai-instruction/ai-instruction.sort.d.ts +16 -0
- package/dist/ai-instruction/ai-instruction.sort.d.ts.map +1 -0
- package/dist/ai-instruction/ai-instruction.sort.js +32 -0
- package/dist/ai-instruction/ai-instruction.sort.js.map +1 -0
- package/dist/ai-instruction/ai-prompt-resolver.service.d.ts +12 -0
- package/dist/ai-instruction/ai-prompt-resolver.service.d.ts.map +1 -1
- package/dist/ai-instruction/ai-prompt-resolver.service.js +26 -0
- package/dist/ai-instruction/ai-prompt-resolver.service.js.map +1 -1
- package/dist/auth/auth.controller.d.ts +24 -8
- package/dist/auth/auth.controller.d.ts.map +1 -1
- package/dist/auth/auth.module.d.ts.map +1 -1
- package/dist/auth/auth.module.js +8 -1
- package/dist/auth/auth.module.js.map +1 -1
- package/dist/auth/auth.service.d.ts +72 -25
- package/dist/auth/auth.service.d.ts.map +1 -1
- package/dist/auth/auth.service.js +41 -4
- package/dist/auth/auth.service.js.map +1 -1
- package/dist/auth/guards/auth.guard.d.ts +3 -1
- package/dist/auth/guards/auth.guard.d.ts.map +1 -1
- package/dist/auth/guards/auth.guard.js +12 -3
- package/dist/auth/guards/auth.guard.js.map +1 -1
- package/dist/authorize/authorize.controller.d.ts +29 -5
- package/dist/authorize/authorize.controller.d.ts.map +1 -1
- package/dist/authorize/authorize.controller.js +120 -13
- package/dist/authorize/authorize.controller.js.map +1 -1
- package/dist/authorize/authorize.errors.d.ts +34 -0
- package/dist/authorize/authorize.errors.d.ts.map +1 -0
- package/dist/authorize/authorize.errors.js +37 -0
- package/dist/authorize/authorize.errors.js.map +1 -0
- package/dist/authorize/authorize.module.d.ts.map +1 -1
- package/dist/authorize/authorize.module.js +6 -1
- package/dist/authorize/authorize.module.js.map +1 -1
- package/dist/authorize/authorize.service.d.ts +77 -3
- package/dist/authorize/authorize.service.d.ts.map +1 -1
- package/dist/authorize/authorize.service.js +212 -21
- package/dist/authorize/authorize.service.js.map +1 -1
- package/dist/authorize/dto/authorize-info.dto.d.ts +10 -1
- package/dist/authorize/dto/authorize-info.dto.d.ts.map +1 -1
- package/dist/authorize/dto/authorize-info.dto.js +16 -1
- package/dist/authorize/dto/authorize-info.dto.js.map +1 -1
- package/dist/authorize/dto/consent.dto.d.ts +10 -1
- package/dist/authorize/dto/consent.dto.d.ts.map +1 -1
- package/dist/authorize/dto/consent.dto.js +16 -1
- package/dist/authorize/dto/consent.dto.js.map +1 -1
- package/dist/authorize/dto/token.dto.d.ts +12 -0
- package/dist/authorize/dto/token.dto.d.ts.map +1 -1
- package/dist/authorize/dto/token.dto.js +21 -1
- package/dist/authorize/dto/token.dto.js.map +1 -1
- package/dist/authorize/oauth-exception.filter.d.ts +17 -0
- package/dist/authorize/oauth-exception.filter.d.ts.map +1 -0
- package/dist/authorize/oauth-exception.filter.js +54 -0
- package/dist/authorize/oauth-exception.filter.js.map +1 -0
- package/dist/authorize/oauth-metadata.service.d.ts +39 -0
- package/dist/authorize/oauth-metadata.service.d.ts.map +1 -0
- package/dist/authorize/oauth-metadata.service.js +81 -0
- package/dist/authorize/oauth-metadata.service.js.map +1 -0
- package/dist/authorize/well-known.controller.d.ts +67 -0
- package/dist/authorize/well-known.controller.d.ts.map +1 -0
- package/dist/authorize/well-known.controller.js +126 -0
- package/dist/authorize/well-known.controller.js.map +1 -0
- package/dist/captcha/captcha.service.d.ts +32 -2
- package/dist/captcha/captcha.service.d.ts.map +1 -1
- package/dist/captcha/captcha.service.js +85 -12
- package/dist/captcha/captcha.service.js.map +1 -1
- package/dist/challenge/challenge.service.d.ts +2 -2
- package/dist/core.module.d.ts.map +1 -1
- package/dist/core.module.js +9 -0
- package/dist/core.module.js.map +1 -1
- package/dist/dashboard/dashboard/dashboard.controller.d.ts +9 -9
- package/dist/dashboard/dashboard/dashboard.service.d.ts +9 -9
- package/dist/dashboard/dashboard-component/dashboard-component.controller.d.ts +3 -3
- package/dist/dashboard/dashboard-component/dashboard-component.service.d.ts +3 -3
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.controller.d.ts +3 -3
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.d.ts +3 -3
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.d.ts.map +1 -1
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.js +2 -4
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.js.map +1 -1
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.sort.d.ts +8 -0
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.sort.d.ts.map +1 -0
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.sort.js +18 -0
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.sort.js.map +1 -0
- package/dist/dashboard/dashboard-item/dashboard-item.service.d.ts.map +1 -1
- package/dist/dashboard/dashboard-item/dashboard-item.service.js +2 -4
- package/dist/dashboard/dashboard-item/dashboard-item.service.js.map +1 -1
- package/dist/dashboard/dashboard-item/dashboard-item.sort.d.ts +9 -0
- package/dist/dashboard/dashboard-item/dashboard-item.sort.d.ts.map +1 -0
- package/dist/dashboard/dashboard-item/dashboard-item.sort.js +19 -0
- package/dist/dashboard/dashboard-item/dashboard-item.sort.js.map +1 -0
- package/dist/dashboard/dashboard-role/dashboard-role.controller.d.ts +3 -3
- package/dist/dashboard/dashboard-role/dashboard-role.service.d.ts +3 -3
- package/dist/dashboard/dashboard-role/dashboard-role.service.d.ts.map +1 -1
- package/dist/dashboard/dashboard-role/dashboard-role.service.js +2 -4
- package/dist/dashboard/dashboard-role/dashboard-role.service.js.map +1 -1
- package/dist/dashboard/dashboard-role/dashboard-role.sort.d.ts +8 -0
- package/dist/dashboard/dashboard-role/dashboard-role.sort.d.ts.map +1 -0
- package/dist/dashboard/dashboard-role/dashboard-role.sort.js +16 -0
- package/dist/dashboard/dashboard-role/dashboard-role.sort.js.map +1 -0
- package/dist/document-kit/document-kit.module.d.ts +3 -0
- package/dist/document-kit/document-kit.module.d.ts.map +1 -0
- package/dist/document-kit/document-kit.module.js +24 -0
- package/dist/document-kit/document-kit.module.js.map +1 -0
- package/dist/document-kit/document-kit.registry.d.ts +55 -0
- package/dist/document-kit/document-kit.registry.d.ts.map +1 -0
- package/dist/document-kit/document-kit.registry.js +91 -0
- package/dist/document-kit/document-kit.registry.js.map +1 -0
- package/dist/document-kit/document-kit.service.d.ts +49 -0
- package/dist/document-kit/document-kit.service.d.ts.map +1 -0
- package/dist/document-kit/document-kit.service.js +135 -0
- package/dist/document-kit/document-kit.service.js.map +1 -0
- package/dist/file/file-upload-ticket.service.d.ts +160 -0
- package/dist/file/file-upload-ticket.service.d.ts.map +1 -0
- package/dist/file/file-upload-ticket.service.js +382 -0
- package/dist/file/file-upload-ticket.service.js.map +1 -0
- package/dist/file/file.controller.d.ts +24 -1
- package/dist/file/file.controller.d.ts.map +1 -1
- package/dist/file/file.controller.js +63 -2
- package/dist/file/file.controller.js.map +1 -1
- package/dist/file/file.module.d.ts.map +1 -1
- package/dist/file/file.module.js +14 -2
- package/dist/file/file.module.js.map +1 -1
- package/dist/file/file.service.d.ts +197 -7
- package/dist/file/file.service.d.ts.map +1 -1
- package/dist/file/file.service.js +438 -78
- package/dist/file/file.service.js.map +1 -1
- package/dist/file/filename-encoding.util.d.ts +24 -0
- package/dist/file/filename-encoding.util.d.ts.map +1 -0
- package/dist/file/filename-encoding.util.js +63 -0
- package/dist/file/filename-encoding.util.js.map +1 -0
- package/dist/file/image-optimization.service.d.ts +27 -0
- package/dist/file/image-optimization.service.d.ts.map +1 -1
- package/dist/file/image-optimization.service.js +63 -14
- package/dist/file/image-optimization.service.js.map +1 -1
- package/dist/file/mcp-file-source.schema.d.ts +33 -0
- package/dist/file/mcp-file-source.schema.d.ts.map +1 -0
- package/dist/file/mcp-file-source.schema.js +78 -0
- package/dist/file/mcp-file-source.schema.js.map +1 -0
- package/dist/file/mcp-file-source.service.d.ts +124 -0
- package/dist/file/mcp-file-source.service.d.ts.map +1 -0
- package/dist/file/mcp-file-source.service.js +262 -0
- package/dist/file/mcp-file-source.service.js.map +1 -0
- package/dist/file/mimetype-sniff.util.d.ts +62 -0
- package/dist/file/mimetype-sniff.util.d.ts.map +1 -0
- package/dist/file/mimetype-sniff.util.js +226 -0
- package/dist/file/mimetype-sniff.util.js.map +1 -0
- package/dist/file/remote-fetch.util.d.ts +35 -0
- package/dist/file/remote-fetch.util.d.ts.map +1 -0
- package/dist/file/remote-fetch.util.js +236 -0
- package/dist/file/remote-fetch.util.js.map +1 -0
- package/dist/impersonation/dto/start-impersonation.dto.d.ts +4 -1
- package/dist/impersonation/dto/start-impersonation.dto.d.ts.map +1 -1
- package/dist/impersonation/dto/start-impersonation.dto.js +1 -1
- package/dist/impersonation/dto/start-impersonation.dto.js.map +1 -1
- package/dist/impersonation/impersonation.service.d.ts +8 -3
- package/dist/impersonation/impersonation.service.d.ts.map +1 -1
- package/dist/impersonation/impersonation.service.js +14 -4
- package/dist/impersonation/impersonation.service.js.map +1 -1
- package/dist/incident/incident.controller.d.ts +2 -1
- package/dist/incident/incident.controller.d.ts.map +1 -1
- package/dist/incident/incident.controller.js +10 -6
- package/dist/incident/incident.controller.js.map +1 -1
- package/dist/incident/incident.service.d.ts +2 -0
- package/dist/incident/incident.service.d.ts.map +1 -1
- package/dist/incident/incident.service.js +4 -0
- package/dist/incident/incident.service.js.map +1 -1
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -1
- package/dist/integration-profile/integration-profile.secrets.d.ts.map +1 -1
- package/dist/integration-profile/integration-profile.secrets.js +28 -0
- package/dist/integration-profile/integration-profile.secrets.js.map +1 -1
- package/dist/integration-profile/integration-profile.service.d.ts +0 -13
- package/dist/integration-profile/integration-profile.service.d.ts.map +1 -1
- package/dist/integration-profile/integration-profile.service.js +5 -40
- package/dist/integration-profile/integration-profile.service.js.map +1 -1
- package/dist/integration-profile/integration-profile.utils.js +5 -5
- package/dist/integration-profile/integration-profile.utils.js.map +1 -1
- package/dist/integration-profile/testers/ai.testers.d.ts.map +1 -1
- package/dist/integration-profile/testers/ai.testers.js +46 -8
- package/dist/integration-profile/testers/ai.testers.js.map +1 -1
- package/dist/integration-profile/testers/index.d.ts.map +1 -1
- package/dist/integration-profile/testers/index.js +2 -0
- package/dist/integration-profile/testers/index.js.map +1 -1
- package/dist/integration-profile/testers/shipping.testers.d.ts +3 -0
- package/dist/integration-profile/testers/shipping.testers.d.ts.map +1 -0
- package/dist/integration-profile/testers/shipping.testers.js +71 -0
- package/dist/integration-profile/testers/shipping.testers.js.map +1 -0
- package/dist/locale/locale.controller.d.ts +1 -1
- package/dist/locale/locale.service.d.ts +1 -1
- package/dist/mail/dto/send.dto.d.ts +6 -1
- package/dist/mail/dto/send.dto.d.ts.map +1 -1
- package/dist/mail/dto/send.dto.js.map +1 -1
- package/dist/mail/mail.service.d.ts +9 -0
- package/dist/mail/mail.service.d.ts.map +1 -1
- package/dist/mail/mail.service.js +34 -14
- package/dist/mail/mail.service.js.map +1 -1
- package/dist/mail-sent/mail-sent.controller.d.ts +1 -1
- package/dist/mail-sent/mail-sent.service.d.ts +1 -1
- package/dist/mail-sent/mail-sent.service.d.ts.map +1 -1
- package/dist/mail-sent/mail-sent.service.js +2 -1
- package/dist/mail-sent/mail-sent.service.js.map +1 -1
- package/dist/mail-sent/mail-sent.sort.d.ts +9 -0
- package/dist/mail-sent/mail-sent.sort.d.ts.map +1 -0
- package/dist/mail-sent/mail-sent.sort.js +19 -0
- package/dist/mail-sent/mail-sent.sort.js.map +1 -0
- package/dist/mcp/filters/mcp-challenge.filter.d.ts +20 -0
- package/dist/mcp/filters/mcp-challenge.filter.d.ts.map +1 -0
- package/dist/mcp/filters/mcp-challenge.filter.js +69 -0
- package/dist/mcp/filters/mcp-challenge.filter.js.map +1 -0
- package/dist/mcp/guards/mcp-auth.guard.d.ts +4 -1
- package/dist/mcp/guards/mcp-auth.guard.d.ts.map +1 -1
- package/dist/mcp/guards/mcp-auth.guard.js +19 -5
- package/dist/mcp/guards/mcp-auth.guard.js.map +1 -1
- package/dist/mcp/guards/mcp-permission.guard.d.ts +25 -3
- package/dist/mcp/guards/mcp-permission.guard.d.ts.map +1 -1
- package/dist/mcp/guards/mcp-permission.guard.js +46 -26
- package/dist/mcp/guards/mcp-permission.guard.js.map +1 -1
- package/dist/mcp/mcp-audit.service.d.ts +17 -0
- package/dist/mcp/mcp-audit.service.d.ts.map +1 -1
- package/dist/mcp/mcp-audit.service.js +32 -3
- package/dist/mcp/mcp-audit.service.js.map +1 -1
- package/dist/mcp/mcp-permission.service.d.ts +64 -0
- package/dist/mcp/mcp-permission.service.d.ts.map +1 -0
- package/dist/mcp/mcp-permission.service.js +178 -0
- package/dist/mcp/mcp-permission.service.js.map +1 -0
- package/dist/mcp/mcp-server.service.d.ts +3 -1
- package/dist/mcp/mcp-server.service.d.ts.map +1 -1
- package/dist/mcp/mcp-server.service.js +36 -15
- package/dist/mcp/mcp-server.service.js.map +1 -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 +7 -1
- package/dist/mcp/mcp.module.js.map +1 -1
- package/dist/mcp/tools/core-access-log.mcp-tools.d.ts +17 -0
- package/dist/mcp/tools/core-access-log.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-access-log.mcp-tools.js +74 -0
- package/dist/mcp/tools/core-access-log.mcp-tools.js.map +1 -0
- package/dist/mcp/tools/core-ai.mcp-tools.js +2 -9
- package/dist/mcp/tools/core-ai.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-dashboard-items.mcp-tools.js +1 -1
- package/dist/mcp/tools/core-dashboard-items.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-dashboard.mcp-tools.js +2 -9
- package/dist/mcp/tools/core-dashboard.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-event-webhooks.mcp-tools.d.ts.map +1 -1
- package/dist/mcp/tools/core-event-webhooks.mcp-tools.js +2 -10
- package/dist/mcp/tools/core-event-webhooks.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-files.mcp-tools.d.ts +17 -5
- package/dist/mcp/tools/core-files.mcp-tools.d.ts.map +1 -1
- package/dist/mcp/tools/core-files.mcp-tools.js +186 -24
- package/dist/mcp/tools/core-files.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-integration-events.mcp-tools.js +2 -9
- package/dist/mcp/tools/core-integration-events.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-mail-sent.mcp-tools.js +2 -14
- package/dist/mcp/tools/core-mail-sent.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-mail.mcp-tools.js +2 -9
- package/dist/mcp/tools/core-mail.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-menus.mcp-tools.d.ts.map +1 -1
- package/dist/mcp/tools/core-menus.mcp-tools.js +2 -10
- package/dist/mcp/tools/core-menus.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-notifications.mcp-tools.d.ts.map +1 -1
- package/dist/mcp/tools/core-notifications.mcp-tools.js +0 -1
- package/dist/mcp/tools/core-notifications.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-roles.mcp-tools.d.ts.map +1 -1
- package/dist/mcp/tools/core-roles.mcp-tools.js +2 -13
- package/dist/mcp/tools/core-roles.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-routes.mcp-tools.d.ts.map +1 -1
- package/dist/mcp/tools/core-routes.mcp-tools.js +2 -11
- package/dist/mcp/tools/core-routes.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-screens.mcp-tools.d.ts.map +1 -1
- package/dist/mcp/tools/core-screens.mcp-tools.js +2 -11
- package/dist/mcp/tools/core-screens.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-sessions.mcp-tools.d.ts.map +1 -1
- package/dist/mcp/tools/core-sessions.mcp-tools.js +0 -2
- package/dist/mcp/tools/core-sessions.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-settings.mcp-tools.d.ts.map +1 -1
- package/dist/mcp/tools/core-settings.mcp-tools.js +2 -10
- package/dist/mcp/tools/core-settings.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-users.mcp-tools.js +2 -9
- package/dist/mcp/tools/core-users.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-webhooks.mcp-tools.d.ts.map +1 -1
- package/dist/mcp/tools/core-webhooks.mcp-tools.js +2 -10
- package/dist/mcp/tools/core-webhooks.mcp-tools.js.map +1 -1
- package/dist/mcp/types/mcp-context.type.d.ts +9 -0
- package/dist/mcp/types/mcp-context.type.d.ts.map +1 -1
- package/dist/mcp-chat/mcp-chat.service.d.ts.map +1 -1
- package/dist/mcp-chat/mcp-chat.service.js +8 -0
- package/dist/mcp-chat/mcp-chat.service.js.map +1 -1
- package/dist/meeting/meeting-provider.registry.d.ts +187 -0
- package/dist/meeting/meeting-provider.registry.d.ts.map +1 -1
- package/dist/meeting/meeting-provider.registry.js.map +1 -1
- package/dist/meeting/meeting.constants.d.ts +70 -0
- package/dist/meeting/meeting.constants.d.ts.map +1 -1
- package/dist/meeting/meeting.constants.js +9 -1
- package/dist/meeting/meeting.constants.js.map +1 -1
- package/dist/menu/menu.service.d.ts.map +1 -1
- package/dist/menu/menu.service.js +29 -9
- package/dist/menu/menu.service.js.map +1 -1
- package/dist/message-template/dto/update-message-template.dto.d.ts +18 -0
- package/dist/message-template/dto/update-message-template.dto.d.ts.map +1 -0
- package/dist/message-template/dto/update-message-template.dto.js +38 -0
- package/dist/message-template/dto/update-message-template.dto.js.map +1 -0
- package/dist/message-template/message-template-admin.service.d.ts +70 -0
- package/dist/message-template/message-template-admin.service.d.ts.map +1 -0
- package/dist/message-template/message-template-admin.service.js +226 -0
- package/dist/message-template/message-template-admin.service.js.map +1 -0
- package/dist/message-template/message-template.controller.d.ts +29 -0
- package/dist/message-template/message-template.controller.d.ts.map +1 -0
- package/dist/message-template/message-template.controller.js +101 -0
- package/dist/message-template/message-template.controller.js.map +1 -0
- package/dist/message-template/message-template.module.d.ts +10 -0
- package/dist/message-template/message-template.module.d.ts.map +1 -0
- package/dist/message-template/message-template.module.js +33 -0
- package/dist/message-template/message-template.module.js.map +1 -0
- package/dist/message-template/message-template.registry.d.ts +55 -0
- package/dist/message-template/message-template.registry.d.ts.map +1 -0
- package/dist/message-template/message-template.registry.js +64 -0
- package/dist/message-template/message-template.registry.js.map +1 -0
- package/dist/message-template/message-template.service.d.ts +59 -0
- package/dist/message-template/message-template.service.d.ts.map +1 -0
- package/dist/message-template/message-template.service.js +166 -0
- package/dist/message-template/message-template.service.js.map +1 -0
- package/dist/notification/notification.constants.d.ts +11 -0
- package/dist/notification/notification.constants.d.ts.map +1 -0
- package/dist/notification/notification.constants.js +14 -0
- package/dist/notification/notification.constants.js.map +1 -0
- package/dist/notification/notification.controller.d.ts +20 -20
- package/dist/notification/notification.module.d.ts.map +1 -1
- package/dist/notification/notification.module.js +3 -0
- package/dist/notification/notification.module.js.map +1 -1
- package/dist/notification/notification.service.d.ts +33 -21
- package/dist/notification/notification.service.d.ts.map +1 -1
- package/dist/notification/notification.service.js +36 -2
- package/dist/notification/notification.service.js.map +1 -1
- package/dist/oauth/oauth-token-broker.service.d.ts +50 -1
- package/dist/oauth/oauth-token-broker.service.d.ts.map +1 -1
- package/dist/oauth/oauth-token-broker.service.js +105 -3
- package/dist/oauth/oauth-token-broker.service.js.map +1 -1
- package/dist/oauth/oauth.controller.d.ts +4 -6
- package/dist/oauth/oauth.controller.d.ts.map +1 -1
- package/dist/oauth/oauth.controller.js +0 -1
- package/dist/oauth/oauth.controller.js.map +1 -1
- package/dist/oauth/oauth.errors.d.ts +15 -1
- package/dist/oauth/oauth.errors.d.ts.map +1 -1
- package/dist/oauth/oauth.errors.js +9 -1
- package/dist/oauth/oauth.errors.js.map +1 -1
- package/dist/oauth/oauth.module.d.ts.map +1 -1
- package/dist/oauth/oauth.module.js +6 -1
- package/dist/oauth/oauth.module.js.map +1 -1
- package/dist/oauth/oauth.service.d.ts +25 -5
- package/dist/oauth/oauth.service.d.ts.map +1 -1
- package/dist/oauth/oauth.service.js +63 -27
- package/dist/oauth/oauth.service.js.map +1 -1
- package/dist/oauth/providers/abstract.provider.d.ts +10 -0
- package/dist/oauth/providers/abstract.provider.d.ts.map +1 -1
- package/dist/oauth/providers/abstract.provider.js +39 -12
- package/dist/oauth/providers/abstract.provider.js.map +1 -1
- package/dist/oauth/providers/github.provider.d.ts +1 -1
- package/dist/oauth/providers/github.provider.d.ts.map +1 -1
- package/dist/oauth/providers/github.provider.js +48 -22
- package/dist/oauth/providers/github.provider.js.map +1 -1
- package/dist/oauth/providers/microsoft-entra-id.provider.d.ts +6 -0
- package/dist/oauth/providers/microsoft-entra-id.provider.d.ts.map +1 -1
- package/dist/oauth/providers/microsoft-entra-id.provider.js +36 -4
- package/dist/oauth/providers/microsoft-entra-id.provider.js.map +1 -1
- package/dist/observability/monitored-cron.d.ts +29 -12
- package/dist/observability/monitored-cron.d.ts.map +1 -1
- package/dist/observability/monitored-cron.js +131 -46
- package/dist/observability/monitored-cron.js.map +1 -1
- package/dist/pairing/pairing.service.d.ts +6 -2
- package/dist/pairing/pairing.service.d.ts.map +1 -1
- package/dist/pdf/document/charts/chart-bar.d.ts +24 -0
- package/dist/pdf/document/charts/chart-bar.d.ts.map +1 -0
- package/dist/pdf/document/charts/chart-bar.js +117 -0
- package/dist/pdf/document/charts/chart-bar.js.map +1 -0
- package/dist/pdf/document/charts/chart-donut.d.ts +16 -0
- package/dist/pdf/document/charts/chart-donut.d.ts.map +1 -0
- package/dist/pdf/document/charts/chart-donut.js +89 -0
- package/dist/pdf/document/charts/chart-donut.js.map +1 -0
- package/dist/pdf/document/charts/chart-gauge.d.ts +15 -0
- package/dist/pdf/document/charts/chart-gauge.d.ts.map +1 -0
- package/dist/pdf/document/charts/chart-gauge.js +60 -0
- package/dist/pdf/document/charts/chart-gauge.js.map +1 -0
- package/dist/pdf/document/charts/chart-line.d.ts +29 -0
- package/dist/pdf/document/charts/chart-line.d.ts.map +1 -0
- package/dist/pdf/document/charts/chart-line.js +89 -0
- package/dist/pdf/document/charts/chart-line.js.map +1 -0
- package/dist/pdf/document/charts/chart-primitives.d.ts +64 -0
- package/dist/pdf/document/charts/chart-primitives.d.ts.map +1 -0
- package/dist/pdf/document/charts/chart-primitives.js +110 -0
- package/dist/pdf/document/charts/chart-primitives.js.map +1 -0
- package/dist/pdf/document/charts/chart-radar.d.ts +13 -0
- package/dist/pdf/document/charts/chart-radar.d.ts.map +1 -0
- package/dist/pdf/document/charts/chart-radar.js +83 -0
- package/dist/pdf/document/charts/chart-radar.js.map +1 -0
- package/dist/pdf/document/charts/chart-scale.d.ts +39 -0
- package/dist/pdf/document/charts/chart-scale.d.ts.map +1 -0
- package/dist/pdf/document/charts/chart-scale.js +118 -0
- package/dist/pdf/document/charts/chart-scale.js.map +1 -0
- package/dist/pdf/document/charts/index.d.ts +8 -0
- package/dist/pdf/document/charts/index.d.ts.map +1 -0
- package/dist/pdf/document/charts/index.js +24 -0
- package/dist/pdf/document/charts/index.js.map +1 -0
- package/dist/pdf/document/index.d.ts +8 -0
- package/dist/pdf/document/index.d.ts.map +1 -0
- package/dist/pdf/document/index.js +24 -0
- package/dist/pdf/document/index.js.map +1 -0
- package/dist/pdf/document/pdf-document.chrome.d.ts +20 -0
- package/dist/pdf/document/pdf-document.chrome.d.ts.map +1 -0
- package/dist/pdf/document/pdf-document.chrome.js +36 -0
- package/dist/pdf/document/pdf-document.chrome.js.map +1 -0
- package/dist/pdf/document/pdf-document.css.d.ts +20 -0
- package/dist/pdf/document/pdf-document.css.d.ts.map +1 -0
- package/dist/pdf/document/pdf-document.css.js +239 -0
- package/dist/pdf/document/pdf-document.css.js.map +1 -0
- package/dist/pdf/document/pdf-document.renderer.d.ts +10 -0
- package/dist/pdf/document/pdf-document.renderer.d.ts.map +1 -0
- package/dist/pdf/document/pdf-document.renderer.js +153 -0
- package/dist/pdf/document/pdf-document.renderer.js.map +1 -0
- package/dist/pdf/document/pdf-document.service.d.ts +27 -0
- package/dist/pdf/document/pdf-document.service.d.ts.map +1 -0
- package/dist/pdf/document/pdf-document.service.js +81 -0
- package/dist/pdf/document/pdf-document.service.js.map +1 -0
- package/dist/pdf/document/pdf-document.theme.d.ts +41 -0
- package/dist/pdf/document/pdf-document.theme.d.ts.map +1 -0
- package/dist/pdf/document/pdf-document.theme.js +105 -0
- package/dist/pdf/document/pdf-document.theme.js.map +1 -0
- package/dist/pdf/document/pdf-document.types.d.ts +108 -0
- package/dist/pdf/document/pdf-document.types.d.ts.map +1 -0
- package/dist/pdf/document/pdf-document.types.js +18 -0
- package/dist/pdf/document/pdf-document.types.js.map +1 -0
- package/dist/pdf/pdf.module.d.ts.map +1 -1
- package/dist/pdf/pdf.module.js +3 -2
- package/dist/pdf/pdf.module.js.map +1 -1
- package/dist/pdf/pdf.service.d.ts +69 -1
- package/dist/pdf/pdf.service.d.ts.map +1 -1
- package/dist/pdf/pdf.service.js +164 -63
- package/dist/pdf/pdf.service.js.map +1 -1
- package/dist/pdf/templates/commercial-proposal.template.d.ts +96 -0
- package/dist/pdf/templates/commercial-proposal.template.d.ts.map +1 -0
- package/dist/pdf/templates/commercial-proposal.template.js +276 -0
- package/dist/pdf/templates/commercial-proposal.template.js.map +1 -0
- package/dist/profile/dto/change-password.dto.d.ts.map +1 -1
- package/dist/profile/dto/change-password.dto.js +4 -6
- package/dist/profile/dto/change-password.dto.js.map +1 -1
- package/dist/profile/profile.controller.d.ts +1 -1
- package/dist/profile/profile.service.d.ts +1 -1
- package/dist/profile/profile.service.d.ts.map +1 -1
- package/dist/profile/profile.service.js +9 -3
- package/dist/profile/profile.service.js.map +1 -1
- package/dist/realtime/realtime-tick.coalescer.d.ts +34 -0
- package/dist/realtime/realtime-tick.coalescer.d.ts.map +1 -0
- package/dist/realtime/realtime-tick.coalescer.js +60 -0
- package/dist/realtime/realtime-tick.coalescer.js.map +1 -0
- package/dist/realtime/realtime.service.d.ts +2 -0
- package/dist/realtime/realtime.service.d.ts.map +1 -1
- package/dist/realtime/realtime.service.js +22 -2
- package/dist/realtime/realtime.service.js.map +1 -1
- package/dist/redis/redis-kv.service.d.ts +90 -0
- package/dist/redis/redis-kv.service.d.ts.map +1 -0
- package/dist/redis/redis-kv.service.js +247 -0
- package/dist/redis/redis-kv.service.js.map +1 -0
- package/dist/redis/redis-master.d.ts +29 -0
- package/dist/redis/redis-master.d.ts.map +1 -0
- package/dist/redis/redis-master.js +81 -0
- package/dist/redis/redis-master.js.map +1 -0
- package/dist/redis/redis-pubsub.d.ts +12 -0
- package/dist/redis/redis-pubsub.d.ts.map +1 -1
- package/dist/redis/redis-pubsub.js +25 -2
- package/dist/redis/redis-pubsub.js.map +1 -1
- package/dist/redis/redis.module.d.ts +11 -0
- package/dist/redis/redis.module.d.ts.map +1 -0
- package/dist/redis/redis.module.js +30 -0
- package/dist/redis/redis.module.js.map +1 -0
- package/dist/session/session.controller.d.ts +6 -2
- package/dist/session/session.controller.d.ts.map +1 -1
- package/dist/session/session.service.d.ts +118 -15
- package/dist/session/session.service.d.ts.map +1 -1
- package/dist/session/session.service.js +251 -77
- package/dist/session/session.service.js.map +1 -1
- package/dist/setting/favicon.controller.d.ts +26 -0
- package/dist/setting/favicon.controller.d.ts.map +1 -0
- package/dist/setting/favicon.controller.js +130 -0
- package/dist/setting/favicon.controller.js.map +1 -0
- package/dist/setting/setting.controller.d.ts +5 -0
- package/dist/setting/setting.controller.d.ts.map +1 -1
- package/dist/setting/setting.module.d.ts.map +1 -1
- package/dist/setting/setting.module.js +2 -1
- package/dist/setting/setting.module.js.map +1 -1
- package/dist/setting/setting.service.d.ts +7 -0
- package/dist/setting/setting.service.d.ts.map +1 -1
- package/dist/setting/setting.service.js +38 -3
- package/dist/setting/setting.service.js.map +1 -1
- package/dist/task/task.service.d.ts +17 -0
- package/dist/task/task.service.d.ts.map +1 -1
- package/dist/task/task.service.js +48 -13
- package/dist/task/task.service.js.map +1 -1
- package/dist/token/token.service.d.ts +19 -1
- package/dist/token/token.service.d.ts.map +1 -1
- package/dist/token/token.service.js +39 -2
- package/dist/token/token.service.js.map +1 -1
- package/dist/user/user-credential.util.d.ts +10 -0
- package/dist/user/user-credential.util.d.ts.map +1 -1
- package/dist/user/user-credential.util.js +14 -1
- package/dist/user/user-credential.util.js.map +1 -1
- package/dist/user/user.service.d.ts +12 -4
- package/dist/user/user.service.d.ts.map +1 -1
- package/dist/user/user.service.js +2 -11
- package/dist/user/user.service.js.map +1 -1
- package/dist/user/user.sort.d.ts +16 -0
- package/dist/user/user.sort.d.ts.map +1 -0
- package/dist/user/user.sort.js +25 -0
- package/dist/user/user.sort.js.map +1 -0
- package/dist/utils/abbreviate-name.d.ts +13 -0
- package/dist/utils/abbreviate-name.d.ts.map +1 -0
- package/dist/utils/abbreviate-name.js +42 -0
- package/dist/utils/abbreviate-name.js.map +1 -0
- package/dist/utils/date-range-query.dto.d.ts +11 -0
- package/dist/utils/date-range-query.dto.d.ts.map +1 -0
- package/dist/utils/date-range-query.dto.js +63 -0
- package/dist/utils/date-range-query.dto.js.map +1 -0
- package/dist/utils/date-range.d.ts +96 -0
- package/dist/utils/date-range.d.ts.map +1 -0
- package/dist/utils/date-range.js +172 -0
- package/dist/utils/date-range.js.map +1 -0
- package/dist/webhook-integration/webhook-utils.d.ts.map +1 -1
- package/dist/webhook-integration/webhook-utils.js +24 -0
- package/dist/webhook-integration/webhook-utils.js.map +1 -1
- package/dist/whatsapp-sender/whatsapp-recipients.d.ts +49 -0
- package/dist/whatsapp-sender/whatsapp-recipients.d.ts.map +1 -0
- package/dist/whatsapp-sender/whatsapp-recipients.js +66 -0
- package/dist/whatsapp-sender/whatsapp-recipients.js.map +1 -0
- package/dist/whatsapp-sender/whatsapp-sender.service.d.ts +25 -0
- package/dist/whatsapp-sender/whatsapp-sender.service.d.ts.map +1 -1
- package/dist/whatsapp-sender/whatsapp-sender.service.js +71 -7
- package/dist/whatsapp-sender/whatsapp-sender.service.js.map +1 -1
- package/hedhog/data/integration_provider.yaml +9 -0
- package/hedhog/data/integration_type.yaml +12 -0
- package/hedhog/data/locale.yaml +0 -1
- package/hedhog/data/mail.yaml +2 -2
- package/hedhog/data/mail_layout.yaml +6 -3
- package/hedhog/data/menu.yaml +25 -5
- package/hedhog/data/route.yaml +148 -0
- package/hedhog/data/setting_group.yaml +189 -3
- package/hedhog/frontend/app/access-log/page.tsx.ejs +47 -53
- package/hedhog/frontend/app/account/components/active-sessions.tsx.ejs +33 -8
- package/hedhog/frontend/app/account/components/mcp-tokens.tsx.ejs +54 -13
- package/hedhog/frontend/app/account/components/mfa-method-card.tsx.ejs +7 -1
- package/hedhog/frontend/app/account/components/recovery-codes-dialog.tsx.ejs +1 -0
- package/hedhog/frontend/app/account/components/tokens.tsx.ejs +47 -17
- package/hedhog/frontend/app/ai_agent/page.tsx.ejs +46 -5
- package/hedhog/frontend/app/ai_instruction/_components/ai-instruction-form.tsx.ejs +101 -30
- package/hedhog/frontend/app/ai_instruction/_components/ai-instruction-versions.tsx.ejs +24 -8
- package/hedhog/frontend/app/ai_instruction/page.tsx.ejs +80 -15
- package/hedhog/frontend/app/configurations/[slug]/components/image-optimization-action.tsx.ejs +166 -0
- package/hedhog/frontend/app/configurations/[slug]/components/setting-field.test.tsx.ejs +145 -45
- package/hedhog/frontend/app/configurations/[slug]/components/setting-field.tsx.ejs +99 -219
- package/hedhog/frontend/app/configurations/[slug]/page.tsx.ejs +19 -1
- package/hedhog/frontend/app/configurations/layout.tsx.ejs +19 -5
- package/hedhog/frontend/app/dashboard/[slug]/widget-renderer.tsx.ejs +2 -8
- package/hedhog/frontend/app/dashboard/components/dynamic-widget.tsx.ejs +3 -0
- package/hedhog/frontend/app/dashboard/components/widget-wrapper.tsx.ejs +4 -0
- package/hedhog/frontend/app/dashboard/dashboard-home-tabs.tsx.ejs +43 -44
- package/hedhog/frontend/app/dashboard/management/tabs/component-roles-tab.tsx.ejs +41 -3
- package/hedhog/frontend/app/dashboard/management/tabs/components-tab.tsx.ejs +42 -2
- package/hedhog/frontend/app/dashboard/management/tabs/dashboard-roles-tab.tsx.ejs +41 -3
- package/hedhog/frontend/app/dashboard/management/tabs/dashboards-tab.tsx.ejs +39 -4
- package/hedhog/frontend/app/dashboard/management/tabs/items-tab.tsx.ejs +42 -3
- package/hedhog/frontend/app/incidents/page.tsx.ejs +60 -31
- package/hedhog/frontend/app/integration/profiles/page.tsx.ejs +45 -5
- package/hedhog/frontend/app/integrations-webhooks/page.tsx.ejs +92 -15
- package/hedhog/frontend/app/mail/log/page.tsx.ejs +109 -61
- package/hedhog/frontend/app/mail/template/page.tsx.ejs +125 -23
- package/hedhog/frontend/app/mcp_chat/chat-components.tsx.ejs +13 -15
- package/hedhog/frontend/app/mcp_chat/page.tsx.ejs +116 -110
- package/hedhog/frontend/app/message-templates/_components/message-template-sheet.tsx.ejs +245 -0
- package/hedhog/frontend/app/message-templates/_lib/types.ts.ejs +24 -0
- package/hedhog/frontend/app/message-templates/page.tsx.ejs +182 -0
- package/hedhog/frontend/app/pages/_components/page-wizard-sheet.tsx.ejs +1 -0
- package/hedhog/frontend/app/pages/page.tsx.ejs +94 -9
- package/hedhog/frontend/app/roles/menus.tsx.ejs +4 -4
- package/hedhog/frontend/app/roles/routes.tsx.ejs +4 -4
- package/hedhog/frontend/app/roles/users.tsx.ejs +4 -4
- package/hedhog/frontend/app/setting-list/page.tsx.ejs +71 -9
- package/hedhog/frontend/app/users/page.tsx.ejs +77 -67
- package/hedhog/frontend/messages/en.json +237 -37
- package/hedhog/frontend/messages/pt.json +237 -37
- package/hedhog/table/cron_lock.yaml +17 -0
- package/hedhog/table/file.yaml +13 -1
- package/hedhog/table/file_upload_chunk.yaml +23 -0
- package/hedhog/table/file_upload_ticket.yaml +67 -0
- package/hedhog/table/mcp_tool_call_log.yaml +30 -22
- package/hedhog/table/message_template.yaml +33 -0
- package/hedhog/table/setting.yaml +8 -0
- package/hedhog/table/user_session.yaml +36 -1
- package/package.json +5 -3
- package/src/ai/ai-agent.sort.ts +21 -0
- package/src/ai/ai-attachment.fragments.spec.ts +56 -3
- package/src/ai/ai.service.responses.spec.ts +429 -0
- package/src/ai/ai.service.spec.ts +173 -1
- package/src/ai/ai.service.ts +473 -220
- package/src/ai-instruction/ai-instruction.service.ts +32 -28
- package/src/ai-instruction/ai-instruction.sort.ts +32 -0
- package/src/ai-instruction/ai-prompt-resolver.service.spec.ts +44 -0
- package/src/ai-instruction/ai-prompt-resolver.service.ts +29 -0
- package/src/auth/auth.module.ts +8 -1
- package/src/auth/auth.service.spec.ts +108 -1
- package/src/auth/auth.service.ts +59 -5
- package/src/auth/guards/auth.guard.ts +9 -1
- package/src/authorize/authorize.controller.ts +126 -11
- package/src/authorize/authorize.errors.ts +55 -0
- package/src/authorize/authorize.module.ts +6 -1
- package/src/authorize/authorize.service.spec.ts +379 -7
- package/src/authorize/authorize.service.ts +309 -23
- package/src/authorize/dto/authorize-info.dto.ts +21 -3
- package/src/authorize/dto/consent.dto.ts +21 -3
- package/src/authorize/dto/token.dto.ts +24 -2
- package/src/authorize/oauth-exception.filter.ts +58 -0
- package/src/authorize/oauth-metadata.service.ts +67 -0
- package/src/authorize/well-known.controller.spec.ts +84 -0
- package/src/authorize/well-known.controller.ts +96 -0
- package/src/captcha/captcha.service.spec.ts +211 -4
- package/src/captcha/captcha.service.ts +127 -10
- package/src/core.module.ts +9 -0
- package/src/dashboard/dashboard-component-role/dashboard-component-role.service.ts +2 -3
- package/src/dashboard/dashboard-component-role/dashboard-component-role.sort.ts +18 -0
- package/src/dashboard/dashboard-item/dashboard-item.service.ts +2 -3
- package/src/dashboard/dashboard-item/dashboard-item.sort.ts +19 -0
- package/src/dashboard/dashboard-role/dashboard-role.service.ts +2 -3
- package/src/dashboard/dashboard-role/dashboard-role.sort.ts +16 -0
- package/src/document-kit/document-kit.module.ts +12 -0
- package/src/document-kit/document-kit.registry.ts +125 -0
- package/src/document-kit/document-kit.service.spec.ts +156 -0
- package/src/document-kit/document-kit.service.ts +173 -0
- package/src/file/file-upload-ticket.service.spec.ts +422 -0
- package/src/file/file-upload-ticket.service.ts +513 -0
- package/src/file/file.controller.ts +65 -0
- package/src/file/file.module.ts +14 -2
- package/src/file/file.service.spec.ts +262 -1
- package/src/file/file.service.ts +505 -87
- package/src/file/filename-encoding.util.spec.ts +54 -0
- package/src/file/filename-encoding.util.ts +67 -0
- package/src/file/image-optimization.service.spec.ts +156 -19
- package/src/file/image-optimization.service.ts +106 -21
- package/src/file/mcp-file-source.schema.ts +86 -0
- package/src/file/mcp-file-source.service.spec.ts +473 -0
- package/src/file/mcp-file-source.service.ts +445 -0
- package/src/file/mimetype-sniff.util.spec.ts +126 -0
- package/src/file/mimetype-sniff.util.ts +265 -0
- package/src/file/remote-fetch.util.spec.ts +117 -0
- package/src/file/remote-fetch.util.ts +330 -0
- package/src/impersonation/dto/start-impersonation.dto.ts +5 -2
- package/src/impersonation/impersonation.service.spec.ts +24 -1
- package/src/impersonation/impersonation.service.ts +14 -4
- package/src/incident/incident.controller.ts +9 -1
- package/src/incident/incident.service.ts +7 -0
- package/src/index.ts +23 -1
- package/src/integration-profile/integration-profile.secrets.ts +28 -0
- package/src/integration-profile/integration-profile.service.ts +7 -64
- package/src/integration-profile/integration-profile.utils.spec.ts +9 -0
- package/src/integration-profile/integration-profile.utils.ts +5 -5
- package/src/integration-profile/testers/ai.testers.ts +70 -5
- package/src/integration-profile/testers/index.ts +2 -0
- package/src/integration-profile/testers/remaining.testers.spec.ts +144 -1
- package/src/integration-profile/testers/shipping.testers.ts +94 -0
- package/src/integration-profile/testers/tester-coverage.spec.ts +10 -0
- package/src/mail/dto/send.dto.ts +6 -1
- package/src/mail/mail-template.spec.ts +58 -1
- package/src/mail/mail.service.spec.ts +112 -0
- package/src/mail/mail.service.ts +42 -4
- package/src/mail-sent/mail-sent.service.ts +3 -0
- package/src/mail-sent/mail-sent.sort.ts +19 -0
- package/src/mcp/filters/mcp-challenge.filter.spec.ts +76 -0
- package/src/mcp/filters/mcp-challenge.filter.ts +70 -0
- package/src/mcp/guards/mcp-auth.guard.spec.ts +51 -2
- package/src/mcp/guards/mcp-auth.guard.ts +24 -1
- package/src/mcp/guards/mcp-permission.guard.spec.ts +109 -15
- package/src/mcp/guards/mcp-permission.guard.ts +76 -51
- package/src/mcp/mcp-audit.service.spec.ts +107 -0
- package/src/mcp/mcp-audit.service.ts +37 -1
- package/src/mcp/mcp-permission.service.spec.ts +197 -0
- package/src/mcp/mcp-permission.service.ts +184 -0
- package/src/mcp/mcp-server.service.spec.ts +84 -2
- package/src/mcp/mcp-server.service.ts +111 -87
- package/src/mcp/mcp.controller.ts +8 -1
- package/src/mcp/mcp.module.ts +7 -1
- package/src/mcp/tools/core-access-log.mcp-tools.ts +66 -0
- package/src/mcp/tools/core-ai.mcp-tools.ts +1 -1
- package/src/mcp/tools/core-dashboard-items.mcp-tools.ts +1 -1
- package/src/mcp/tools/core-dashboard.mcp-tools.ts +1 -1
- package/src/mcp/tools/core-event-webhooks.mcp-tools.ts +1 -2
- package/src/mcp/tools/core-files.mcp-tools.ts +202 -22
- package/src/mcp/tools/core-integration-events.mcp-tools.ts +1 -1
- package/src/mcp/tools/core-mail-sent.mcp-tools.ts +1 -1
- package/src/mcp/tools/core-mail.mcp-tools.ts +1 -1
- package/src/mcp/tools/core-menus.mcp-tools.ts +1 -2
- package/src/mcp/tools/core-notifications.mcp-tools.ts +0 -1
- package/src/mcp/tools/core-roles.mcp-tools.ts +1 -5
- package/src/mcp/tools/core-routes.mcp-tools.ts +1 -3
- package/src/mcp/tools/core-screens.mcp-tools.ts +1 -3
- package/src/mcp/tools/core-sessions.mcp-tools.ts +0 -2
- package/src/mcp/tools/core-settings.mcp-tools.ts +1 -2
- package/src/mcp/tools/core-users.mcp-tools.ts +1 -1
- package/src/mcp/tools/core-webhooks.mcp-tools.ts +1 -2
- package/src/mcp/types/mcp-context.type.ts +15 -6
- package/src/mcp-chat/mcp-chat.service.ts +8 -0
- package/src/meeting/meeting-provider.registry.ts +201 -0
- package/src/meeting/meeting.constants.ts +74 -0
- package/src/menu/menu.service.spec.ts +89 -0
- package/src/menu/menu.service.ts +29 -11
- package/src/message-template/dto/update-message-template.dto.ts +22 -0
- package/src/message-template/message-template-admin.service.spec.ts +163 -0
- package/src/message-template/message-template-admin.service.ts +239 -0
- package/src/message-template/message-template.controller.ts +52 -0
- package/src/message-template/message-template.module.ts +21 -0
- package/src/message-template/message-template.registry.spec.ts +62 -0
- package/src/message-template/message-template.registry.ts +125 -0
- package/src/message-template/message-template.service.spec.ts +149 -0
- package/src/message-template/message-template.service.ts +149 -0
- package/src/notification/notification.constants.ts +10 -0
- package/src/notification/notification.module.ts +3 -0
- package/src/notification/notification.service.spec.ts +75 -1
- package/src/notification/notification.service.ts +39 -1
- package/src/oauth/oauth-token-broker.service.spec.ts +108 -0
- package/src/oauth/oauth-token-broker.service.ts +129 -2
- package/src/oauth/oauth.controller.ts +0 -1
- package/src/oauth/oauth.errors.ts +7 -0
- package/src/oauth/oauth.module.ts +6 -1
- package/src/oauth/oauth.service.spec.ts +58 -7
- package/src/oauth/oauth.service.ts +71 -32
- package/src/oauth/providers/abstract.provider.spec.ts +192 -0
- package/src/oauth/providers/abstract.provider.ts +46 -14
- package/src/oauth/providers/github.provider.ts +57 -34
- package/src/oauth/providers/microsoft-entra-id.provider.spec.ts +133 -0
- package/src/oauth/providers/microsoft-entra-id.provider.ts +37 -3
- package/src/observability/monitored-cron.spec.ts +164 -31
- package/src/observability/monitored-cron.ts +154 -62
- package/src/pdf/document/charts/chart-bar.ts +217 -0
- package/src/pdf/document/charts/chart-donut.ts +163 -0
- package/src/pdf/document/charts/chart-gauge.ts +94 -0
- package/src/pdf/document/charts/chart-line.ts +159 -0
- package/src/pdf/document/charts/chart-primitives.ts +188 -0
- package/src/pdf/document/charts/chart-radar.ts +136 -0
- package/src/pdf/document/charts/chart-scale.spec.ts +93 -0
- package/src/pdf/document/charts/chart-scale.ts +141 -0
- package/src/pdf/document/charts/charts.spec.ts +157 -0
- package/src/pdf/document/charts/index.ts +7 -0
- package/src/pdf/document/index.ts +7 -0
- package/src/pdf/document/pdf-document.chrome.ts +38 -0
- package/src/pdf/document/pdf-document.css.ts +238 -0
- package/src/pdf/document/pdf-document.renderer.spec.ts +129 -0
- package/src/pdf/document/pdf-document.renderer.ts +215 -0
- package/src/pdf/document/pdf-document.service.ts +77 -0
- package/src/pdf/document/pdf-document.theme.ts +146 -0
- package/src/pdf/document/pdf-document.types.ts +97 -0
- package/src/pdf/pdf.module.ts +3 -2
- package/src/pdf/pdf.service.spec.ts +131 -0
- package/src/pdf/pdf.service.ts +245 -82
- package/src/pdf/templates/commercial-proposal.template.spec.ts +177 -0
- package/src/pdf/templates/commercial-proposal.template.ts +400 -0
- package/src/profile/dto/change-password.dto.ts +25 -7
- package/src/profile/profile.service.spec.ts +97 -0
- package/src/profile/profile.service.ts +9 -3
- package/src/realtime/realtime-tick.coalescer.spec.ts +92 -0
- package/src/realtime/realtime-tick.coalescer.ts +60 -0
- package/src/realtime/realtime.service.spec.ts +33 -0
- package/src/realtime/realtime.service.ts +28 -2
- package/src/redis/redis-kv.service.ts +261 -0
- package/src/redis/redis-master.spec.ts +111 -0
- package/src/redis/redis-master.ts +86 -0
- package/src/redis/redis-pubsub.ts +27 -3
- package/src/redis/redis.module.ts +17 -0
- package/src/session/session.service.spec.ts +121 -42
- package/src/session/session.service.ts +279 -82
- package/src/setting/favicon.controller.spec.ts +92 -0
- package/src/setting/favicon.controller.ts +135 -0
- package/src/setting/setting.module.ts +2 -1
- package/src/setting/setting.service.ts +44 -3
- package/src/task/task.service.spec.ts +41 -0
- package/src/task/task.service.ts +53 -13
- package/src/token/token.service.spec.ts +90 -0
- package/src/token/token.service.ts +48 -2
- package/src/user/user-credential.util.ts +14 -5
- package/src/user/user.service.ts +2 -12
- package/src/user/user.sort.ts +25 -0
- package/src/utils/abbreviate-name.spec.ts +50 -0
- package/src/utils/abbreviate-name.ts +44 -0
- package/src/utils/date-range-query.dto.ts +56 -0
- package/src/utils/date-range.spec.ts +175 -0
- package/src/utils/date-range.ts +246 -0
- package/src/webhook-integration/webhook-utils.spec.ts +67 -0
- package/src/webhook-integration/webhook-utils.ts +26 -0
- package/src/whatsapp-sender/whatsapp-recipients.spec.ts +89 -0
- package/src/whatsapp-sender/whatsapp-recipients.ts +83 -0
- package/src/whatsapp-sender/whatsapp-sender.service.spec.ts +185 -0
- package/src/whatsapp-sender/whatsapp-sender.service.ts +90 -7
- package/hedhog/query/device_pairing_request.sql +0 -46
- package/hedhog/query/impersonation_grant.sql +0 -71
- package/hedhog/query/oauth_authorize_pkce.sql +0 -58
- package/hedhog/query/oauth_mobile_state_token.sql +0 -30
- package/hedhog/query/oauth_web_ref_token.sql +0 -25
package/src/file/file.service.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
GetBucketNotificationConfigurationCommand,
|
|
3
|
+
GetObjectCommand,
|
|
3
4
|
HeadBucketCommand,
|
|
4
5
|
HeadObjectCommand,
|
|
5
6
|
PutBucketNotificationConfigurationCommand,
|
|
6
7
|
S3Client,
|
|
7
8
|
} from '@aws-sdk/client-s3';
|
|
9
|
+
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
|
8
10
|
import {
|
|
9
11
|
AddPermissionCommand,
|
|
10
12
|
CreateFunctionCommand,
|
|
@@ -23,14 +25,17 @@ import { PaginationDTO, PaginationService } from '@hed-hog/api-pagination';
|
|
|
23
25
|
import { PrismaService } from '@hed-hog/api-prisma';
|
|
24
26
|
import {
|
|
25
27
|
BadRequestException,
|
|
28
|
+
ForbiddenException,
|
|
26
29
|
forwardRef,
|
|
27
30
|
Inject,
|
|
28
31
|
Injectable,
|
|
29
32
|
Logger,
|
|
30
33
|
NotFoundException,
|
|
31
|
-
OnModuleInit
|
|
34
|
+
OnModuleInit,
|
|
35
|
+
ServiceUnavailableException
|
|
32
36
|
} from '@nestjs/common';
|
|
33
37
|
import { JwtService } from '@nestjs/jwt';
|
|
38
|
+
import { createHash } from 'crypto';
|
|
34
39
|
import type { Request } from 'express';
|
|
35
40
|
import { createWriteStream, promises as fs } from 'fs';
|
|
36
41
|
import { tmpdir } from 'os';
|
|
@@ -44,6 +49,8 @@ import {
|
|
|
44
49
|
} from '../integration-profile/integration-profile.utils';
|
|
45
50
|
import { SettingService } from '../setting/setting.service';
|
|
46
51
|
import { DeleteDTO } from './dto/delete.dto';
|
|
52
|
+
import { fixMojibake } from './filename-encoding.util';
|
|
53
|
+
import { MIMETYPE_SNIFF_BYTES } from './mimetype-sniff.util';
|
|
47
54
|
import {
|
|
48
55
|
ImageOptimizationService,
|
|
49
56
|
ImageVariantFormat,
|
|
@@ -54,6 +61,9 @@ import {
|
|
|
54
61
|
} from './provider/abstract.provider';
|
|
55
62
|
import { ProviderFactory } from './provider/provider.factory';
|
|
56
63
|
|
|
64
|
+
/** Códigos de erro do S3/Spaces que indicam indisponibilidade passageira, não arquivo ruim. */
|
|
65
|
+
const STORAGE_UNAVAILABLE_CODES = ['SlowDown', 'ServiceUnavailable', 'InternalError', 'RequestTimeout'];
|
|
66
|
+
|
|
57
67
|
type ResolvedProvider = {
|
|
58
68
|
provider: AbstractProvider;
|
|
59
69
|
providerId: number;
|
|
@@ -96,21 +106,13 @@ export class FileService implements OnModuleInit {
|
|
|
96
106
|
return `${file.location}/${file.path}`;
|
|
97
107
|
}
|
|
98
108
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
109
|
+
/**
|
|
110
|
+
* Nome que chegou com o encoding trocado (busboy entrega `originalname` em
|
|
111
|
+
* latin1, e a base ainda recebe nome importado com o mesmo defeito). A lógica
|
|
112
|
+
* mora no util porque a migration de backfill a reproduz em plpgsql.
|
|
113
|
+
*/
|
|
103
114
|
private normalizeUploadedFilename(filename: string): string {
|
|
104
|
-
|
|
105
|
-
return filename;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (!this.hasMojibake(filename)) {
|
|
109
|
-
return filename;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const decoded = Buffer.from(filename, 'latin1').toString('utf8');
|
|
113
|
-
return this.hasMojibake(decoded) ? filename : decoded;
|
|
115
|
+
return filename ? fixMojibake(filename) : filename;
|
|
114
116
|
}
|
|
115
117
|
|
|
116
118
|
constructor(
|
|
@@ -306,16 +308,37 @@ export class FileService implements OnModuleInit {
|
|
|
306
308
|
return file;
|
|
307
309
|
}
|
|
308
310
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
311
|
+
/**
|
|
312
|
+
* Traduz a falha do provider de storage para a resposta HTTP certa.
|
|
313
|
+
*
|
|
314
|
+
* 5xx e throttle do S3/Spaces são indisponibilidade passageira, não defeito:
|
|
315
|
+
* viram 503 com `code: STORAGE_UNAVAILABLE`, que o HttpExceptionFilter da API
|
|
316
|
+
* responde com `Retry-After` e reporta deduplicado. Sem isso, um 503 do Spaces
|
|
317
|
+
* com corpo que não é XML (ex.: `upstream connect error` do balanceador)
|
|
318
|
+
* estourava no parse do SDK como `char 'u' is not expected` e virava 500.
|
|
319
|
+
*
|
|
320
|
+
* O status vem de `$metadata.httpStatusCode`, que o SDK carimba mesmo quando a
|
|
321
|
+
* desserialização do erro falha; `$response.statusCode` cobre o resto.
|
|
322
|
+
*/
|
|
323
|
+
private mapStorageError(error: any, fileId: number): never {
|
|
324
|
+
const status = error?.$metadata?.httpStatusCode ?? error?.$response?.statusCode;
|
|
325
|
+
const codes = [error?.name, error?.Code];
|
|
314
326
|
|
|
315
|
-
if (
|
|
327
|
+
if (codes.includes('NoSuchKey') || status === 404) {
|
|
316
328
|
throw new NotFoundException(`File not found in storage: ${fileId}`);
|
|
317
329
|
}
|
|
318
330
|
|
|
331
|
+
const unavailable =
|
|
332
|
+
(typeof status === 'number' && status >= 500) ||
|
|
333
|
+
codes.some((c) => STORAGE_UNAVAILABLE_CODES.includes(c));
|
|
334
|
+
|
|
335
|
+
if (unavailable) {
|
|
336
|
+
throw new ServiceUnavailableException(
|
|
337
|
+
{ message: 'Storage temporarily unavailable', code: 'STORAGE_UNAVAILABLE' },
|
|
338
|
+
{ cause: error },
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
|
|
319
342
|
throw error;
|
|
320
343
|
}
|
|
321
344
|
|
|
@@ -340,11 +363,15 @@ export class FileService implements OnModuleInit {
|
|
|
340
363
|
const provider = await this.getProvider();
|
|
341
364
|
const providerPath = this.resolveProviderPath(file);
|
|
342
365
|
|
|
366
|
+
// O HEAD só serve para descobrir o tamanho quando a tabela não o tem. Feito
|
|
367
|
+
// sempre, dobrava as requisições ao storage em cada segmento HLS.
|
|
343
368
|
let metadata: any;
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
369
|
+
if (file.size == null) {
|
|
370
|
+
try {
|
|
371
|
+
metadata = await provider.metaData(providerPath);
|
|
372
|
+
} catch {
|
|
373
|
+
metadata = undefined;
|
|
374
|
+
}
|
|
348
375
|
}
|
|
349
376
|
|
|
350
377
|
try {
|
|
@@ -355,7 +382,7 @@ export class FileService implements OnModuleInit {
|
|
|
355
382
|
size: file.size ?? this.getFileSizeFromMetadata(metadata),
|
|
356
383
|
};
|
|
357
384
|
} catch (error) {
|
|
358
|
-
this.
|
|
385
|
+
this.mapStorageError(error, fileId);
|
|
359
386
|
}
|
|
360
387
|
}
|
|
361
388
|
|
|
@@ -377,10 +404,15 @@ export class FileService implements OnModuleInit {
|
|
|
377
404
|
const resolvedEnd = end !== undefined ? end : totalSize > 0 ? totalSize - 1 : undefined;
|
|
378
405
|
const hasRange = start !== undefined;
|
|
379
406
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
407
|
+
let stream: Awaited<ReturnType<AbstractProvider['readStream']>>;
|
|
408
|
+
try {
|
|
409
|
+
stream = await provider.readStream(
|
|
410
|
+
providerPath,
|
|
411
|
+
hasRange ? { start: resolvedStart, end: resolvedEnd } : undefined,
|
|
412
|
+
);
|
|
413
|
+
} catch (error) {
|
|
414
|
+
this.mapStorageError(error, fileId);
|
|
415
|
+
}
|
|
384
416
|
|
|
385
417
|
const contentLength =
|
|
386
418
|
resolvedEnd !== undefined ? resolvedEnd - resolvedStart + 1 : totalSize;
|
|
@@ -463,16 +495,7 @@ export class FileService implements OnModuleInit {
|
|
|
463
495
|
try {
|
|
464
496
|
providerBuffer = await provider.buffer(providerPath);
|
|
465
497
|
} catch (error: any) {
|
|
466
|
-
|
|
467
|
-
error?.name === 'NoSuchKey' ||
|
|
468
|
-
error?.Code === 'NoSuchKey' ||
|
|
469
|
-
error?.$metadata?.httpStatusCode === 404;
|
|
470
|
-
|
|
471
|
-
if (missingInStorage) {
|
|
472
|
-
throw new NotFoundException(`File not found in storage: ${fileId}`);
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
throw error;
|
|
498
|
+
this.mapStorageError(error, fileId);
|
|
476
499
|
}
|
|
477
500
|
|
|
478
501
|
const buffer = Buffer.from(providerBuffer);
|
|
@@ -548,6 +571,13 @@ export class FileService implements OnModuleInit {
|
|
|
548
571
|
skipMimeValidation?: boolean;
|
|
549
572
|
skipSizeValidation?: boolean;
|
|
550
573
|
isPrivate?: boolean;
|
|
574
|
+
// Quem enviou. É o que permite recusar um `fileId` de outro usuário nas tools MCP
|
|
575
|
+
// (ver `assertFileUsable`); fica nulo nos caminhos internos sem usuário.
|
|
576
|
+
createdByUserId?: number | null;
|
|
577
|
+
// Grava a imagem byte a byte, sem a conversão para WebP nem o redimensionamento.
|
|
578
|
+
// Para quem entrega o arquivo a um consumidor externo que exige o formato
|
|
579
|
+
// original — o Instagram recusa WebP. O chamador já normalizou o arquivo.
|
|
580
|
+
skipImageOptimization?: boolean;
|
|
551
581
|
},
|
|
552
582
|
) {
|
|
553
583
|
|
|
@@ -584,12 +614,18 @@ export class FileService implements OnModuleInit {
|
|
|
584
614
|
fileBuffer.buffer,
|
|
585
615
|
);
|
|
586
616
|
|
|
587
|
-
const optimized =
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
617
|
+
const optimized = options?.skipImageOptimization
|
|
618
|
+
? {
|
|
619
|
+
buffer: fileBuffer.buffer,
|
|
620
|
+
mimetype: effectiveMimetype,
|
|
621
|
+
originalname: normalizedFilename,
|
|
622
|
+
}
|
|
623
|
+
: await this.imageOptimizationService.optimizeBuffer(
|
|
624
|
+
fileBuffer.buffer,
|
|
625
|
+
effectiveMimetype,
|
|
626
|
+
normalizedFilename,
|
|
627
|
+
{ maxWidth: resize?.maxWidth, maxHeight: resize?.maxHeight },
|
|
628
|
+
);
|
|
593
629
|
|
|
594
630
|
const normalizedFile = {
|
|
595
631
|
...fileBuffer,
|
|
@@ -610,6 +646,7 @@ export class FileService implements OnModuleInit {
|
|
|
610
646
|
mimetype_id: await this.getMimeType(optimized.mimetype),
|
|
611
647
|
size: optimized.buffer.length,
|
|
612
648
|
is_private: options?.isPrivate ?? false,
|
|
649
|
+
created_by_user_id: options?.createdByUserId ?? null,
|
|
613
650
|
},
|
|
614
651
|
});
|
|
615
652
|
|
|
@@ -629,8 +666,12 @@ export class FileService implements OnModuleInit {
|
|
|
629
666
|
skipSizeValidation?: boolean;
|
|
630
667
|
// Tira o arquivo das rotas que servem por id sem autenticação.
|
|
631
668
|
isPrivate?: boolean;
|
|
669
|
+
// Quem enviou. Ver a nota do mesmo campo em `upload`.
|
|
670
|
+
createdByUserId?: number | null;
|
|
632
671
|
// Mesmo par maxWidth/maxHeight que `upload` aceita — só tem efeito em image/*.
|
|
633
672
|
resize?: { maxWidth?: number; maxHeight?: number };
|
|
673
|
+
// Mesma opção de `upload`: a imagem segue o caminho de streaming, intacta.
|
|
674
|
+
skipImageOptimization?: boolean;
|
|
634
675
|
},
|
|
635
676
|
) {
|
|
636
677
|
if (!filePath) {
|
|
@@ -652,9 +693,18 @@ export class FileService implements OnModuleInit {
|
|
|
652
693
|
);
|
|
653
694
|
// Recupera o tipo real quando o caller não informou o mimetype (default antigo era
|
|
654
695
|
// application/octet-stream, que fazia imagens caírem fora da otimização e do serve).
|
|
696
|
+
// Diferente de `upload`, aqui só existe um caminho no disco — e ler o arquivo inteiro
|
|
697
|
+
// é justamente o que o branch de streaming abaixo evita para SCORM/vídeo de vários GB.
|
|
698
|
+
// Por isso o sniffing recebe só o cabeçalho, e apenas quando o caller não declarou um
|
|
699
|
+
// tipo concreto: sem isso, uma imagem enviada como application/octet-stream sem
|
|
700
|
+
// extensão no nome escapava da otimização e era gravada byte a byte.
|
|
701
|
+
const declaredMimetype = metadata?.mimetype;
|
|
702
|
+
const isGenericMimetype =
|
|
703
|
+
!declaredMimetype || declaredMimetype === 'application/octet-stream';
|
|
655
704
|
const mimetype = await this.resolveEffectiveMimetype(
|
|
656
|
-
|
|
705
|
+
declaredMimetype,
|
|
657
706
|
originalname,
|
|
707
|
+
isGenericMimetype ? await this.readFileHeader(filePath) : undefined,
|
|
658
708
|
);
|
|
659
709
|
|
|
660
710
|
if (!metadata?.skipMimeValidation && !(await this.acceptMimetypes(mimetype))) {
|
|
@@ -671,8 +721,9 @@ export class FileService implements OnModuleInit {
|
|
|
671
721
|
// streaming direto do disco via provider.uploadFromPath. Isso evita carregar
|
|
672
722
|
// o arquivo inteiro na RAM (fs.readFile estoura o limite de Buffer / causa OOM)
|
|
673
723
|
// e habilita upload multipart no S3. A otimização de imagem só se aplica a
|
|
674
|
-
// image/*, então não há perda nesse caminho.
|
|
675
|
-
|
|
724
|
+
// image/*, então não há perda nesse caminho. `skipImageOptimization` manda a
|
|
725
|
+
// imagem pelo mesmo caminho: o chamador quer os bytes exatamente como estão.
|
|
726
|
+
if (!mimetype.startsWith('image/') || metadata?.skipImageOptimization) {
|
|
676
727
|
const url = await provider.uploadFromPath(destination, filePath, {
|
|
677
728
|
filename: originalname,
|
|
678
729
|
mimetype,
|
|
@@ -687,6 +738,7 @@ export class FileService implements OnModuleInit {
|
|
|
687
738
|
mimetype_id: await this.getMimeType(mimetype),
|
|
688
739
|
size,
|
|
689
740
|
is_private: metadata?.isPrivate ?? false,
|
|
741
|
+
created_by_user_id: metadata?.createdByUserId ?? null,
|
|
690
742
|
},
|
|
691
743
|
});
|
|
692
744
|
}
|
|
@@ -724,20 +776,102 @@ export class FileService implements OnModuleInit {
|
|
|
724
776
|
mimetype_id: await this.getMimeType(optimized.mimetype),
|
|
725
777
|
size: optimized.buffer.length,
|
|
726
778
|
is_private: metadata?.isPrivate ?? false,
|
|
779
|
+
created_by_user_id: metadata?.createdByUserId ?? null,
|
|
727
780
|
},
|
|
728
781
|
});
|
|
729
782
|
|
|
730
783
|
return file;
|
|
731
784
|
}
|
|
732
785
|
|
|
786
|
+
/**
|
|
787
|
+
* Mimetypes de imagem que `optimizeBuffer` devolve intactos por decisão de projeto
|
|
788
|
+
* (vetor não vira bitmap, GIF animado não vira quadro único). Enfileirá-los para
|
|
789
|
+
* reotimização seria custo de download e reprocessamento com resultado garantidamente
|
|
790
|
+
* nulo, então ficam fora da varredura.
|
|
791
|
+
*/
|
|
792
|
+
private static readonly NON_OPTIMIZABLE_IMAGE_MIMETYPES = [
|
|
793
|
+
'image/svg+xml',
|
|
794
|
+
'image/gif',
|
|
795
|
+
];
|
|
796
|
+
|
|
797
|
+
/**
|
|
798
|
+
* Lista as imagens armazenadas que valem uma passada por `reoptimizeFile`, para a
|
|
799
|
+
* operação em massa de reprocessamento do acervo.
|
|
800
|
+
*
|
|
801
|
+
* Devolve junto o total e a soma de bytes do filtro inteiro (não só da página), que é
|
|
802
|
+
* o que permite mostrar uma prévia antes de enfileirar dezenas de milhares de jobs.
|
|
803
|
+
* O core não conhece a fila — quem enfileira é a library `queue`, que depende deste
|
|
804
|
+
* pacote e não o contrário.
|
|
805
|
+
*/
|
|
806
|
+
async findOptimizableImages(params?: {
|
|
807
|
+
/** Piso de tamanho em bytes; abaixo disso o ganho não paga o reprocessamento. */
|
|
808
|
+
minSize?: number;
|
|
809
|
+
/** Restringe a `location` exatas (ex.: `lms/xp/skills`), para rollout em ondas. */
|
|
810
|
+
locations?: string[];
|
|
811
|
+
offset?: number;
|
|
812
|
+
limit?: number;
|
|
813
|
+
}): Promise<{ ids: number[]; total: number; totalBytes: number }> {
|
|
814
|
+
const minSize = Math.max(0, Math.trunc(Number(params?.minSize) || 0));
|
|
815
|
+
const locations = (params?.locations ?? [])
|
|
816
|
+
.map((location) => String(location).trim())
|
|
817
|
+
.filter(Boolean);
|
|
818
|
+
|
|
819
|
+
const where: any = {
|
|
820
|
+
size: { gte: minSize },
|
|
821
|
+
file_mimetype: {
|
|
822
|
+
name: {
|
|
823
|
+
startsWith: 'image/',
|
|
824
|
+
notIn: FileService.NON_OPTIMIZABLE_IMAGE_MIMETYPES,
|
|
825
|
+
},
|
|
826
|
+
},
|
|
827
|
+
};
|
|
828
|
+
|
|
829
|
+
if (locations.length) {
|
|
830
|
+
where.location = { in: locations };
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
const [aggregate, rows] = await Promise.all([
|
|
834
|
+
this.prismaService.file.aggregate({
|
|
835
|
+
where,
|
|
836
|
+
_count: { _all: true },
|
|
837
|
+
_sum: { size: true },
|
|
838
|
+
}),
|
|
839
|
+
this.prismaService.file.findMany({
|
|
840
|
+
where,
|
|
841
|
+
select: { id: true },
|
|
842
|
+
// Ordem estável para que o enfileiramento paginado não pule nem repita linhas.
|
|
843
|
+
orderBy: { id: 'asc' },
|
|
844
|
+
skip: Math.max(0, Math.trunc(Number(params?.offset) || 0)),
|
|
845
|
+
take: Math.min(5000, Math.max(1, Math.trunc(Number(params?.limit) || 1000))),
|
|
846
|
+
}),
|
|
847
|
+
]);
|
|
848
|
+
|
|
849
|
+
return {
|
|
850
|
+
ids: rows.map((row) => row.id),
|
|
851
|
+
total: aggregate._count._all ?? 0,
|
|
852
|
+
totalBytes: Number(aggregate._sum.size ?? 0),
|
|
853
|
+
};
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
/**
|
|
857
|
+
* Ganho mínimo para valer a regravação de um arquivo que já está no formato final.
|
|
858
|
+
*
|
|
859
|
+
* Recomprimir WebP sobre WebP sempre devolve alguns bytes a menos, então um limiar de
|
|
860
|
+
* "estritamente menor" nunca converge: cada passada da varredura trocaria o arquivo
|
|
861
|
+
* por um ganho irrisório e mais uma geração de perda de qualidade. Medido aqui, três
|
|
862
|
+
* passadas seguidas rendiam 10926 → 10664 → 10662 bytes — a última custou uma
|
|
863
|
+
* recompressão inteira para economizar 2 bytes.
|
|
864
|
+
*/
|
|
865
|
+
private static readonly REOPTIMIZE_MIN_GAIN_RATIO = 0.1;
|
|
866
|
+
|
|
733
867
|
/**
|
|
734
868
|
* Re-applies the current image-optimization settings to an already-stored file,
|
|
735
869
|
* rewriting it in place: the `file` record keeps the same id, so every row that
|
|
736
870
|
* references it (course_image, course_lesson_video_frame, ...) stays valid without
|
|
737
|
-
* any FK juggling. Only replaces the stored object when the
|
|
738
|
-
*
|
|
739
|
-
* operation idempotent and avoids repeated
|
|
740
|
-
* left untouched.
|
|
871
|
+
* any FK juggling. Only replaces the stored object when the mimetype changed
|
|
872
|
+
* (e.g. PNG → WebP) or the result is smaller by a margin that justifies the rewrite —
|
|
873
|
+
* that margin is what actually makes the operation idempotent and avoids repeated
|
|
874
|
+
* lossy recompression. Non-image files are left untouched.
|
|
741
875
|
*/
|
|
742
876
|
async reoptimizeFile(fileId: number): Promise<{
|
|
743
877
|
changed: boolean;
|
|
@@ -760,9 +894,15 @@ export class FileService implements OnModuleInit {
|
|
|
760
894
|
);
|
|
761
895
|
|
|
762
896
|
const mimetypeChanged = optimized.mimetype !== currentMimetype;
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
897
|
+
// Mudança de formato é o ganho principal (PNG → WebP) e vale por si; sem ela, só
|
|
898
|
+
// regrava quando a economia passa do limiar. `optimizeBuffer` já garante que o
|
|
899
|
+
// resultado nunca é maior que a entrada neste caminho (sem crop de alvo exato).
|
|
900
|
+
const worthRewriting =
|
|
901
|
+
oldSize > 0 &&
|
|
902
|
+
(oldSize - optimized.buffer.length) / oldSize >=
|
|
903
|
+
FileService.REOPTIMIZE_MIN_GAIN_RATIO;
|
|
904
|
+
|
|
905
|
+
if (!mimetypeChanged && !worthRewriting) {
|
|
766
906
|
return {
|
|
767
907
|
changed: false,
|
|
768
908
|
oldSize,
|
|
@@ -850,7 +990,130 @@ export class FileService implements OnModuleInit {
|
|
|
850
990
|
return files.map((file) => file.id);
|
|
851
991
|
}
|
|
852
992
|
|
|
853
|
-
|
|
993
|
+
/**
|
|
994
|
+
* `ownerUserId` restringe a listagem aos arquivos enviados por aquele usuário.
|
|
995
|
+
*
|
|
996
|
+
* Existe para as tools MCP: sem ele, `core.files.list` é um índice de tudo o que
|
|
997
|
+
* qualquer empresa já subiu, incluindo o que está gravado como `is_private`. O
|
|
998
|
+
* controller REST continua chamando sem o parâmetro, porque ali a rota já é `admin`
|
|
999
|
+
* e a tela de arquivos é justamente a visão global.
|
|
1000
|
+
*/
|
|
1001
|
+
/**
|
|
1002
|
+
* Grava um stream em disco medindo, hasheando e guardando o cabeçalho de uma vez só.
|
|
1003
|
+
*
|
|
1004
|
+
* O upload assinado recebe o binário cru no corpo do PUT, e nada garante que venha com
|
|
1005
|
+
* `Content-Length` — sem o contador abaixo, um corpo aberto encheria o disco do pod. O
|
|
1006
|
+
* corte é feito em pleno voo, antes de o excedente chegar ao arquivo.
|
|
1007
|
+
*
|
|
1008
|
+
* Passa o cabeçalho adiante porque conferir o tipo real é obrigatório nesse caminho, e
|
|
1009
|
+
* reler o arquivo depois seria uma segunda passada por nada.
|
|
1010
|
+
*/
|
|
1011
|
+
async writeStreamToPath(
|
|
1012
|
+
source: NodeJS.ReadableStream,
|
|
1013
|
+
targetPath: string,
|
|
1014
|
+
maxBytes: number,
|
|
1015
|
+
): Promise<{ bytes: number; sha256: string; header: Buffer }> {
|
|
1016
|
+
const hash = createHash('sha256');
|
|
1017
|
+
const headerChunks: Buffer[] = [];
|
|
1018
|
+
let headerLength = 0;
|
|
1019
|
+
let bytes = 0;
|
|
1020
|
+
|
|
1021
|
+
const target = createWriteStream(targetPath);
|
|
1022
|
+
|
|
1023
|
+
await pipeline(
|
|
1024
|
+
source as any,
|
|
1025
|
+
async function* (chunks: AsyncIterable<Buffer>) {
|
|
1026
|
+
for await (const chunk of chunks) {
|
|
1027
|
+
const buffer = Buffer.from(chunk);
|
|
1028
|
+
bytes += buffer.length;
|
|
1029
|
+
|
|
1030
|
+
if (bytes > maxBytes) {
|
|
1031
|
+
throw new BadRequestException(
|
|
1032
|
+
`Upload exceeds the declared size of ${maxBytes} bytes`,
|
|
1033
|
+
);
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
hash.update(buffer);
|
|
1037
|
+
|
|
1038
|
+
if (headerLength < MIMETYPE_SNIFF_BYTES) {
|
|
1039
|
+
const slice = buffer.subarray(0, MIMETYPE_SNIFF_BYTES - headerLength);
|
|
1040
|
+
headerChunks.push(slice);
|
|
1041
|
+
headerLength += slice.length;
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
yield buffer;
|
|
1045
|
+
}
|
|
1046
|
+
},
|
|
1047
|
+
target,
|
|
1048
|
+
);
|
|
1049
|
+
|
|
1050
|
+
return {
|
|
1051
|
+
bytes,
|
|
1052
|
+
sha256: hash.digest('hex'),
|
|
1053
|
+
header: Buffer.concat(headerChunks),
|
|
1054
|
+
};
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
/**
|
|
1058
|
+
* Conteúdo de um arquivo já armazenado, em memória.
|
|
1059
|
+
*
|
|
1060
|
+
* Existe para as tools que consomem os bytes sem persistir nada (importar extrato,
|
|
1061
|
+
* extrair dados de uma nota com IA): elas recebem um `fileId` e precisam do arquivo,
|
|
1062
|
+
* não de um stream. O teto evita que um `fileId` apontando para um SCORM de vários GB
|
|
1063
|
+
* derrube o pod — quem serve arquivo grande usa `openReadStream` e não passa por aqui.
|
|
1064
|
+
*/
|
|
1065
|
+
async bufferById(fileId: number, maxBytes: number): Promise<Buffer> {
|
|
1066
|
+
const { stream } = await this.openReadStreamById(fileId);
|
|
1067
|
+
const chunks: Buffer[] = [];
|
|
1068
|
+
let total = 0;
|
|
1069
|
+
|
|
1070
|
+
for await (const chunk of stream as AsyncIterable<Buffer>) {
|
|
1071
|
+
total += chunk.length;
|
|
1072
|
+
|
|
1073
|
+
if (total > maxBytes) {
|
|
1074
|
+
(stream as any).destroy?.();
|
|
1075
|
+
throw new BadRequestException(
|
|
1076
|
+
`File ${fileId} is larger than the ${maxBytes} bytes this operation can load`,
|
|
1077
|
+
);
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
chunks.push(Buffer.from(chunk));
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
return Buffer.concat(chunks);
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
/**
|
|
1087
|
+
* Nome do mimetype de um arquivo.
|
|
1088
|
+
*
|
|
1089
|
+
* `get()` e `upload()` devolvem a linha crua, sem a relação, e cada chamador que
|
|
1090
|
+
* precisava do tipo vinha refazendo o `include` por conta. Como o cache de
|
|
1091
|
+
* `getMimeType` é id-por-nome, este é o inverso dele e sai do mesmo mapa quando já
|
|
1092
|
+
* tiver sido consultado.
|
|
1093
|
+
*/
|
|
1094
|
+
async mimetypeNameById(mimetypeId: number): Promise<string> {
|
|
1095
|
+
const cached = Object.entries(this.mimetypes).find(
|
|
1096
|
+
([, id]) => id === mimetypeId,
|
|
1097
|
+
);
|
|
1098
|
+
|
|
1099
|
+
if (cached) {
|
|
1100
|
+
return cached[0];
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
const row = await this.prismaService.file_mimetype.findUnique({
|
|
1104
|
+
where: { id: mimetypeId },
|
|
1105
|
+
select: { name: true },
|
|
1106
|
+
});
|
|
1107
|
+
|
|
1108
|
+
if (row?.name) {
|
|
1109
|
+
this.mimetypes[row.name] = mimetypeId;
|
|
1110
|
+
return row.name;
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
return 'application/octet-stream';
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
async getFiles(paginationParams: PaginationDTO, ownerUserId?: number) {
|
|
854
1117
|
const fields = ['filename', 'path'];
|
|
855
1118
|
const OR = this.prismaService.createInsensitiveSearch(
|
|
856
1119
|
fields,
|
|
@@ -863,11 +1126,84 @@ export class FileService implements OnModuleInit {
|
|
|
863
1126
|
{
|
|
864
1127
|
where: {
|
|
865
1128
|
OR,
|
|
1129
|
+
...(ownerUserId ? { created_by_user_id: ownerUserId } : {}),
|
|
866
1130
|
},
|
|
867
1131
|
},
|
|
868
1132
|
);
|
|
869
1133
|
}
|
|
870
1134
|
|
|
1135
|
+
/**
|
|
1136
|
+
* Papéis que enxergam qualquer arquivo. Os mesmos que já governam as rotas de arquivo
|
|
1137
|
+
* em `route.yaml` — quem administra o acervo pela tela também o administra pelo MCP.
|
|
1138
|
+
*/
|
|
1139
|
+
private static readonly FILE_ADMIN_ROLES = ['admin', 'admin-access'];
|
|
1140
|
+
|
|
1141
|
+
/** Cache curto de papéis, no mesmo espírito do McpPermissionService. */
|
|
1142
|
+
private readonly fileAdminCache = new Map<number, { value: boolean; at: number }>();
|
|
1143
|
+
private static readonly FILE_ADMIN_CACHE_TTL_MS = 30_000;
|
|
1144
|
+
|
|
1145
|
+
async isFileAdmin(userId: number): Promise<boolean> {
|
|
1146
|
+
const cached = this.fileAdminCache.get(userId);
|
|
1147
|
+
|
|
1148
|
+
if (cached && Date.now() - cached.at < FileService.FILE_ADMIN_CACHE_TTL_MS) {
|
|
1149
|
+
return cached.value;
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
const found = await this.prismaService.role_user.findFirst({
|
|
1153
|
+
where: {
|
|
1154
|
+
user_id: userId,
|
|
1155
|
+
role: { slug: { in: FileService.FILE_ADMIN_ROLES } },
|
|
1156
|
+
},
|
|
1157
|
+
select: { role_id: true },
|
|
1158
|
+
});
|
|
1159
|
+
|
|
1160
|
+
const value = Boolean(found);
|
|
1161
|
+
this.fileAdminCache.set(userId, { value, at: Date.now() });
|
|
1162
|
+
|
|
1163
|
+
return value;
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
/**
|
|
1167
|
+
* Porteiro do `fileId` vindo de fora: só o dono usa um arquivo, e quem administra o
|
|
1168
|
+
* acervo usa qualquer um.
|
|
1169
|
+
*
|
|
1170
|
+
* Sem isto, apontar um `fileId` numa tool seria enumerar — id de arquivo é sequencial,
|
|
1171
|
+
* exatamente o motivo pelo qual `is_private` existe. Arquivo anterior a esta coluna
|
|
1172
|
+
* (ou gravado por cron/seed/webhook) fica com dono nulo e só o admin alcança: não dá
|
|
1173
|
+
* para provar de quem ele é, e adivinhar a favor do chamador seria abrir a porta que
|
|
1174
|
+
* este método fecha.
|
|
1175
|
+
*/
|
|
1176
|
+
async assertFileUsable(fileId: number, userId: number) {
|
|
1177
|
+
if (!Number.isInteger(fileId) || fileId <= 0) {
|
|
1178
|
+
throw new BadRequestException(`Invalid file id: ${fileId}`);
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
const file = await this.prismaService.file.findUnique({
|
|
1182
|
+
where: { id: fileId },
|
|
1183
|
+
include: { file_mimetype: true },
|
|
1184
|
+
});
|
|
1185
|
+
|
|
1186
|
+
// Mesma resposta para inexistente e para alheio seria o ideal contra enumeração, mas
|
|
1187
|
+
// `get()` já responde 404 com o id, e divergir aqui só confundiria quem integra.
|
|
1188
|
+
if (!file) {
|
|
1189
|
+
throw new NotFoundException(`File not found: ${fileId}`);
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
if (file.created_by_user_id === userId) {
|
|
1193
|
+
return file;
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
if (await this.isFileAdmin(userId)) {
|
|
1197
|
+
return file;
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
throw new ForbiddenException(
|
|
1201
|
+
file.created_by_user_id === null
|
|
1202
|
+
? `File ${fileId} has no recorded owner and can only be used by an administrator`
|
|
1203
|
+
: `File ${fileId} belongs to another user`,
|
|
1204
|
+
);
|
|
1205
|
+
}
|
|
1206
|
+
|
|
871
1207
|
async get(fileId: number) {
|
|
872
1208
|
const file = await this.prismaService.file.findUnique({
|
|
873
1209
|
where: { id: fileId },
|
|
@@ -1028,19 +1364,45 @@ export class FileService implements OnModuleInit {
|
|
|
1028
1364
|
return undefined;
|
|
1029
1365
|
}
|
|
1030
1366
|
|
|
1367
|
+
/**
|
|
1368
|
+
* Resolve o `file` cujo `resolveProviderPath` (location + path, ou path já combinado) bate com
|
|
1369
|
+
* o `filepath` embutido no token de download — é o inverso de `resolveProviderPath`.
|
|
1370
|
+
*
|
|
1371
|
+
* Bug corrigido (relatado como "Token de download inválido ou expirado" no storage local):
|
|
1372
|
+
* `tempURLById` assina o token com `resolveProviderPath(file)` (ex.:
|
|
1373
|
+
* "class-assignment-tasks/abc123.pdf", já com o prefixo de `location`), mas o upload local/S3
|
|
1374
|
+
* grava só o basename em `file.path` ("abc123.pdf") e o `location` separado em outra coluna.
|
|
1375
|
+
* Um `findFirst({ where: { path: filepath } })` direto nunca batia para esses arquivos — sempre
|
|
1376
|
+
* caía no `catch` abaixo e mostrava a mensagem genérica de token inválido, mesmo com o token
|
|
1377
|
+
* (e o arquivo) perfeitamente válidos. S3 mascarava isso porque `S3Provider.tempURL` devolve uma
|
|
1378
|
+
* URL assinada direto do bucket, sem passar por este método.
|
|
1379
|
+
*/
|
|
1380
|
+
private async findFileByResolvedPath(filepath: string) {
|
|
1381
|
+
const bySameValue = await this.prismaService.file.findFirst({
|
|
1382
|
+
where: { path: filepath },
|
|
1383
|
+
include: { file_mimetype: true },
|
|
1384
|
+
});
|
|
1385
|
+
if (bySameValue) return bySameValue;
|
|
1386
|
+
|
|
1387
|
+
const slashIndex = filepath.lastIndexOf('/');
|
|
1388
|
+
if (slashIndex === -1) return null;
|
|
1389
|
+
|
|
1390
|
+
return this.prismaService.file.findFirst({
|
|
1391
|
+
where: {
|
|
1392
|
+
location: filepath.slice(0, slashIndex),
|
|
1393
|
+
path: filepath.slice(slashIndex + 1),
|
|
1394
|
+
},
|
|
1395
|
+
include: { file_mimetype: true },
|
|
1396
|
+
});
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1031
1399
|
async download(locale:string, token: string) {
|
|
1032
1400
|
try {
|
|
1033
1401
|
const { filepath } = this.jwtService.verify(token, {
|
|
1034
1402
|
secret: process.env.JWT_SECRET,
|
|
1035
1403
|
});
|
|
1036
1404
|
|
|
1037
|
-
|
|
1038
|
-
const file = await this.prismaService.file.findFirst({
|
|
1039
|
-
where: { path: filepath },
|
|
1040
|
-
include: {
|
|
1041
|
-
file_mimetype: true,
|
|
1042
|
-
},
|
|
1043
|
-
});
|
|
1405
|
+
const file = await this.findFileByResolvedPath(filepath);
|
|
1044
1406
|
|
|
1045
1407
|
if (!file) {
|
|
1046
1408
|
throw new NotFoundException(getLocaleText('file.download.not_found', locale, `Not found: ${filepath}`));
|
|
@@ -1186,6 +1548,30 @@ export class FileService implements OnModuleInit {
|
|
|
1186
1548
|
return map[ext] ?? null;
|
|
1187
1549
|
}
|
|
1188
1550
|
|
|
1551
|
+
/**
|
|
1552
|
+
* Lê só o começo do arquivo, o bastante para o sharp reconhecer o formato pelo
|
|
1553
|
+
* cabeçalho. Existe para o sniffing de `uploadFromPath`, que não pode chamar
|
|
1554
|
+
* `fs.readFile`: o mesmo caminho recebe pacotes SCORM e vídeos de vários GB, e
|
|
1555
|
+
* carregá-los inteiros estoura o limite de Buffer. Devolve `undefined` em qualquer
|
|
1556
|
+
* falha de leitura — quem chama já trata a ausência do buffer.
|
|
1557
|
+
*/
|
|
1558
|
+
private async readFileHeader(
|
|
1559
|
+
filePath: string,
|
|
1560
|
+
bytes = 64 * 1024,
|
|
1561
|
+
): Promise<Buffer | undefined> {
|
|
1562
|
+
let handle: fs.FileHandle | undefined;
|
|
1563
|
+
try {
|
|
1564
|
+
handle = await fs.open(filePath, 'r');
|
|
1565
|
+
const buffer = Buffer.alloc(bytes);
|
|
1566
|
+
const { bytesRead } = await handle.read(buffer, 0, bytes, 0);
|
|
1567
|
+
return bytesRead > 0 ? buffer.subarray(0, bytesRead) : undefined;
|
|
1568
|
+
} catch {
|
|
1569
|
+
return undefined;
|
|
1570
|
+
} finally {
|
|
1571
|
+
await handle?.close().catch(() => undefined);
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1189
1575
|
/**
|
|
1190
1576
|
* Resolves the MIME type to persist for an upload. When the incoming mimetype is
|
|
1191
1577
|
* generic (application/octet-stream or empty), recovers the real image type from
|
|
@@ -1197,29 +1583,7 @@ export class FileService implements OnModuleInit {
|
|
|
1197
1583
|
name: string,
|
|
1198
1584
|
buffer?: Buffer,
|
|
1199
1585
|
): Promise<string> {
|
|
1200
|
-
if (mimetype && mimetype.startsWith('image/'))
|
|
1201
|
-
// O mimetype que chega do navegador vem da EXTENSÃO do arquivo, não do
|
|
1202
|
-
// conteúdo: um PNG renomeado para `.svg` é anunciado como `image/svg+xml`.
|
|
1203
|
-
// Gravar isso faz o endpoint de imagem servir bytes PNG sob Content-Type
|
|
1204
|
-
// de SVG e, com o `nosniff` que a API envia, o navegador não corrige
|
|
1205
|
-
// sozinho — a imagem simplesmente não renderiza, sem erro nenhum.
|
|
1206
|
-
// Com os bytes em mãos, quem decide é a assinatura do arquivo.
|
|
1207
|
-
if (buffer) {
|
|
1208
|
-
const sniffed =
|
|
1209
|
-
await this.imageOptimizationService.detectImageMimetype(buffer);
|
|
1210
|
-
// Só sobrepõe quando o sniffing reconheceu o formato: um `null` significa
|
|
1211
|
-
// que o sharp não entendeu o buffer, e aí o declarado ainda é o melhor
|
|
1212
|
-
// palpite disponível.
|
|
1213
|
-
if (sniffed && sniffed !== mimetype) {
|
|
1214
|
-
this.logger.warn(
|
|
1215
|
-
`Upload "${name}" foi anunciado como ${mimetype}, mas os bytes são ${sniffed}. Persistindo o tipo real.`,
|
|
1216
|
-
);
|
|
1217
|
-
return sniffed;
|
|
1218
|
-
}
|
|
1219
|
-
}
|
|
1220
|
-
|
|
1221
|
-
return mimetype;
|
|
1222
|
-
}
|
|
1586
|
+
if (mimetype && mimetype.startsWith('image/')) return mimetype;
|
|
1223
1587
|
// Tipos concretos não-imagem (video, pdf, ...) são mantidos como estão.
|
|
1224
1588
|
if (mimetype && mimetype !== 'application/octet-stream') return mimetype;
|
|
1225
1589
|
|
|
@@ -1514,6 +1878,17 @@ export class FileService implements OnModuleInit {
|
|
|
1514
1878
|
return url;
|
|
1515
1879
|
}
|
|
1516
1880
|
|
|
1881
|
+
/** Par de `tempURLById`, pela mesma razão: `path` cru resolve para NoSuchKey no S3. */
|
|
1882
|
+
async tempOpenURLById(fileId: number, expires = 3600) {
|
|
1883
|
+
const url = await this.tempURLById(fileId, expires);
|
|
1884
|
+
|
|
1885
|
+
if (typeof url === 'string') {
|
|
1886
|
+
return url.replace('/file/download/', '/file/open/');
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1889
|
+
return url;
|
|
1890
|
+
}
|
|
1891
|
+
|
|
1517
1892
|
async getTemporaryCredentials(params: {
|
|
1518
1893
|
accessKeyId: string;
|
|
1519
1894
|
secretAccessKey: string;
|
|
@@ -1596,6 +1971,49 @@ export class FileService implements OnModuleInit {
|
|
|
1596
1971
|
};
|
|
1597
1972
|
}
|
|
1598
1973
|
|
|
1974
|
+
/**
|
|
1975
|
+
* URL assinada de um objeto em bucket avulso.
|
|
1976
|
+
*
|
|
1977
|
+
* O `getFileUrl` do provider resolve arquivos do acervo, que vivem no destino
|
|
1978
|
+
* ativo do core. Este metodo serve para os buckets que outras libraries
|
|
1979
|
+
* configuram por conta propria — o Studio grava no perfil de armazenamento
|
|
1980
|
+
* dele, que pode nao ser o mesmo — e por isso recebe as credenciais e o bucket
|
|
1981
|
+
* de fora, como o `headS3Object` ao lado.
|
|
1982
|
+
*/
|
|
1983
|
+
async signS3Object(params: {
|
|
1984
|
+
accessKeyId: string;
|
|
1985
|
+
secretAccessKey: string;
|
|
1986
|
+
sessionToken?: string;
|
|
1987
|
+
region: string;
|
|
1988
|
+
bucket: string;
|
|
1989
|
+
key: string;
|
|
1990
|
+
expiresInSeconds?: number;
|
|
1991
|
+
downloadFileName?: string;
|
|
1992
|
+
}): Promise<string> {
|
|
1993
|
+
const s3 = new S3Client({
|
|
1994
|
+
region: params.region,
|
|
1995
|
+
credentials: {
|
|
1996
|
+
accessKeyId: params.accessKeyId,
|
|
1997
|
+
secretAccessKey: params.secretAccessKey,
|
|
1998
|
+
...(params.sessionToken ? { sessionToken: params.sessionToken } : {}),
|
|
1999
|
+
},
|
|
2000
|
+
});
|
|
2001
|
+
|
|
2002
|
+
const command = new GetObjectCommand({
|
|
2003
|
+
Bucket: params.bucket,
|
|
2004
|
+
Key: params.key,
|
|
2005
|
+
...(params.downloadFileName
|
|
2006
|
+
? {
|
|
2007
|
+
ResponseContentDisposition: `attachment; filename="${params.downloadFileName.replace(/"/g, '')}"`,
|
|
2008
|
+
}
|
|
2009
|
+
: {}),
|
|
2010
|
+
});
|
|
2011
|
+
|
|
2012
|
+
return getSignedUrl(s3 as any, command, {
|
|
2013
|
+
expiresIn: params.expiresInSeconds ?? 900,
|
|
2014
|
+
});
|
|
2015
|
+
}
|
|
2016
|
+
|
|
1599
2017
|
async headS3Bucket(params: {
|
|
1600
2018
|
accessKeyId: string;
|
|
1601
2019
|
secretAccessKey: string;
|