@hed-hog/core 0.0.399 → 0.0.400
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 +4 -4
- package/dist/ai/ai.service.d.ts +35 -4
- package/dist/ai/ai.service.d.ts.map +1 -1
- package/dist/ai/ai.service.js +51 -137
- 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/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 +6 -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/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 +182 -6
- package/dist/file/file.service.d.ts.map +1 -1
- package/dist/file/file.service.js +386 -54
- 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 +75 -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 +17 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -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 +7 -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/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 +5 -1
- package/dist/mcp/mcp.module.js.map +1 -1
- 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 +115 -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.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.map +1 -1
- package/dist/oauth/providers/microsoft-entra-id.provider.js +6 -0
- 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/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/setting.controller.d.ts +5 -0
- package/dist/setting/setting.controller.d.ts.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 +46 -5
- 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 +6 -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 +140 -0
- package/hedhog/data/setting_group.yaml +137 -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 +183 -36
- package/hedhog/frontend/messages/pt.json +183 -36
- 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 +7 -5
- package/src/ai/ai-agent.sort.ts +21 -0
- package/src/ai/ai.service.spec.ts +173 -1
- package/src/ai/ai.service.ts +64 -193
- package/src/ai-instruction/ai-instruction.service.ts +32 -28
- package/src/ai-instruction/ai-instruction.sort.ts +32 -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 +6 -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/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 +94 -0
- package/src/file/file.service.ts +433 -53
- 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 +83 -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 +17 -0
- package/src/integration-profile/integration-profile.secrets.ts +7 -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/index.ts +2 -0
- package/src/integration-profile/testers/remaining.testers.spec.ts +79 -0
- package/src/integration-profile/testers/shipping.testers.ts +94 -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 +5 -1
- 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 +121 -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.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.ts +8 -1
- package/src/observability/monitored-cron.spec.ts +164 -31
- package/src/observability/monitored-cron.ts +154 -62
- 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/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 +112 -0
- package/src/whatsapp-sender/whatsapp-sender.service.ts +58 -5
- 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
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nome de arquivo que chegou com o encoding trocado ("mojibake").
|
|
3
|
+
*
|
|
4
|
+
* O caso clássico é o byte a byte de UTF-8 lido como Latin-1: "Introdução" vira
|
|
5
|
+
* "Introdução". O multipart (busboy) entrega `originalname` assim, e a base
|
|
6
|
+
* ainda carrega nomes importados com o mesmo defeito — inclusive em duas camadas,
|
|
7
|
+
* quando o nome já quebrado passou pela conversão de novo ("IntroduÃ\u0083§Ã\u0083£o").
|
|
8
|
+
*
|
|
9
|
+
* A migration `20261103000000_core_fix_mojibake_filenames` reproduz esta mesma
|
|
10
|
+
* lógica em plpgsql para consertar o que já está gravado; mudanças aqui e lá
|
|
11
|
+
* andam juntas.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** Quantas camadas de conversão desfazer antes de desistir. */
|
|
15
|
+
const MAX_DECODE_PASSES = 3;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Marca de nome quebrado. `Ã`/`Â` seguidos de qualquer coisa é o rastro do byte
|
|
19
|
+
* de continuação lido como caractere; `â` seguido de um byte alto é o começo de
|
|
20
|
+
* um caractere de 3 bytes (travessão, aspas curvas) quebrado do mesmo jeito.
|
|
21
|
+
*/
|
|
22
|
+
const MOJIBAKE_PATTERN = /Ã.|Â.|â[\u0080-\u00BF]/;
|
|
23
|
+
|
|
24
|
+
export function hasMojibake(value: string): boolean {
|
|
25
|
+
return MOJIBAKE_PATTERN.test(value);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Desfaz as camadas de mojibake de `value`, ou devolve o original quando não dá
|
|
30
|
+
* para desfazer com segurança.
|
|
31
|
+
*
|
|
32
|
+
* Só aceita o resultado de uma passada se ele for UTF-8 válido: é isso que
|
|
33
|
+
* protege o nome legítimo que casa o padrão por acaso. "MANUTENÇÃO.pdf" tem
|
|
34
|
+
* "ÃO", mas os bytes Latin-1 correspondentes (`C7 C3 4F`) não formam UTF-8, o
|
|
35
|
+
* `Buffer` devolve `U+FFFD` e o nome sai intacto.
|
|
36
|
+
*/
|
|
37
|
+
export function fixMojibake(value: string): string {
|
|
38
|
+
if (!value || !hasMojibake(value)) {
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
let current = value;
|
|
43
|
+
|
|
44
|
+
for (let pass = 0; pass < MAX_DECODE_PASSES; pass += 1) {
|
|
45
|
+
let decoded: string;
|
|
46
|
+
try {
|
|
47
|
+
decoded = Buffer.from(current, 'latin1').toString('utf8');
|
|
48
|
+
} catch {
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// `\uFFFD` é o que o decoder põe no lugar de byte inválido: o palpite estava
|
|
53
|
+
// errado e a passada anterior é o melhor que temos.
|
|
54
|
+
if (decoded === current || decoded.includes('\uFFFD')) {
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
current = decoded;
|
|
59
|
+
|
|
60
|
+
if (!hasMojibake(current)) {
|
|
61
|
+
return current;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Nenhuma passada chegou a um nome limpo: na dúvida, não estraga o que existe.
|
|
66
|
+
return hasMojibake(current) ? value : current;
|
|
67
|
+
}
|
|
@@ -20,16 +20,25 @@ import { ImageOptimizationService } from './image-optimization.service';
|
|
|
20
20
|
|
|
21
21
|
const mockSharp = sharp as unknown as jest.Mock & { __instance: any };
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
// O `SettingService` real converte o valor pelo `type` da setting antes de devolver:
|
|
24
|
+
// `boolean` vira boolean, `array` vira array JS. O mock precisa refletir isso, senão o
|
|
25
|
+
// spec valida um contrato que não existe — foi exatamente assim que a leitura por
|
|
26
|
+
// string passou despercebida e deixou compressão e WebP desligados em produção.
|
|
27
|
+
const DEFAULT_SETTINGS: Record<string, unknown> = {
|
|
28
|
+
'image-compression-enabled': false,
|
|
25
29
|
'image-compression-quality': undefined,
|
|
26
30
|
'image-compression-types': undefined,
|
|
27
|
-
'image-webp-conversion-enabled':
|
|
31
|
+
'image-webp-conversion-enabled': false,
|
|
28
32
|
'image-webp-convert-types': undefined,
|
|
29
33
|
'image-max-dimension': undefined,
|
|
30
34
|
};
|
|
31
35
|
|
|
32
|
-
|
|
36
|
+
// Entrada grande o bastante para que qualquer saída simulada conte como ganho: o
|
|
37
|
+
// serviço recusa um resultado que não ficou menor, então um buffer de 3 bytes como
|
|
38
|
+
// entrada faria todo teste cair no fallback para o original.
|
|
39
|
+
const INPUT = Buffer.alloc(4096, 1);
|
|
40
|
+
|
|
41
|
+
const makeService = (settings: Record<string, unknown> = {}) => {
|
|
33
42
|
const merged = { ...DEFAULT_SETTINGS, ...settings };
|
|
34
43
|
const settingService = {
|
|
35
44
|
getSettingValues: jest.fn(async () => merged),
|
|
@@ -135,12 +144,64 @@ describe('ImageOptimizationService', () => {
|
|
|
135
144
|
|
|
136
145
|
it('retorna o original quando não há resize, compressão nem conversão', async () => {
|
|
137
146
|
const { service } = makeService();
|
|
138
|
-
const buffer =
|
|
147
|
+
const buffer = INPUT;
|
|
139
148
|
const result = await service.optimizeBuffer(buffer, 'image/jpeg', 'a.jpg');
|
|
140
149
|
expect(result).toEqual({ buffer, mimetype: 'image/jpeg', originalname: 'a.jpg' });
|
|
141
150
|
expect(mockSharp).not.toHaveBeenCalled();
|
|
142
151
|
});
|
|
143
152
|
|
|
153
|
+
// Regressão: o SettingService devolve boolean e array já convertidos pelo `type` da
|
|
154
|
+
// setting. Ler isso como string desligava compressão e WebP mesmo com os toggles
|
|
155
|
+
// ligados na tela, e era o motivo de PNGs de vários MB serem gravados intactos.
|
|
156
|
+
it('converte para webp com as settings tipadas, como o SettingService entrega', async () => {
|
|
157
|
+
const { service } = makeService({
|
|
158
|
+
'image-webp-conversion-enabled': true,
|
|
159
|
+
'image-webp-convert-types': ['image/jpeg', 'image/png'],
|
|
160
|
+
'image-compression-quality': 80,
|
|
161
|
+
});
|
|
162
|
+
const out = Buffer.from('webp-bytes');
|
|
163
|
+
mockSharp.__instance.toBuffer.mockResolvedValue(out);
|
|
164
|
+
|
|
165
|
+
const result = await service.optimizeBuffer(INPUT, 'image/png', 'skill.png');
|
|
166
|
+
|
|
167
|
+
expect(result).toEqual({
|
|
168
|
+
buffer: out,
|
|
169
|
+
mimetype: 'image/webp',
|
|
170
|
+
originalname: 'skill.webp',
|
|
171
|
+
});
|
|
172
|
+
expect(mockSharp.__instance.webp).toHaveBeenCalledWith({ quality: 80 });
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('comprime com as settings tipadas quando a conversão para webp está desligada', async () => {
|
|
176
|
+
const { service } = makeService({
|
|
177
|
+
'image-compression-enabled': true,
|
|
178
|
+
'image-compression-types': ['image/jpeg'],
|
|
179
|
+
'image-compression-quality': 60,
|
|
180
|
+
});
|
|
181
|
+
const out = Buffer.from('jpeg-small');
|
|
182
|
+
mockSharp.__instance.toBuffer.mockResolvedValue(out);
|
|
183
|
+
|
|
184
|
+
const result = await service.optimizeBuffer(INPUT, 'image/jpeg', 'a.jpg');
|
|
185
|
+
|
|
186
|
+
expect(result).toEqual({ buffer: out, mimetype: 'image/jpeg', originalname: 'a.jpg' });
|
|
187
|
+
expect(mockSharp.__instance.jpeg).toHaveBeenCalledWith({ quality: 60 });
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('mantém o original quando o tipo tipado não está em nenhuma das listas', async () => {
|
|
191
|
+
const { service } = makeService({
|
|
192
|
+
'image-compression-enabled': true,
|
|
193
|
+
'image-compression-types': ['image/jpeg'],
|
|
194
|
+
'image-webp-conversion-enabled': true,
|
|
195
|
+
'image-webp-convert-types': ['image/jpeg'],
|
|
196
|
+
});
|
|
197
|
+
const buffer = Buffer.from('png-bytes');
|
|
198
|
+
|
|
199
|
+
const result = await service.optimizeBuffer(buffer, 'image/png', 'a.png');
|
|
200
|
+
|
|
201
|
+
expect(result).toEqual({ buffer, mimetype: 'image/png', originalname: 'a.png' });
|
|
202
|
+
expect(mockSharp).not.toHaveBeenCalled();
|
|
203
|
+
});
|
|
204
|
+
|
|
144
205
|
it('converte para webp quando habilitado e o tipo está na lista', async () => {
|
|
145
206
|
const { service } = makeService({
|
|
146
207
|
'image-webp-conversion-enabled': 'true',
|
|
@@ -150,7 +211,7 @@ describe('ImageOptimizationService', () => {
|
|
|
150
211
|
const out = Buffer.from('webp-bytes');
|
|
151
212
|
mockSharp.__instance.toBuffer.mockResolvedValue(out);
|
|
152
213
|
|
|
153
|
-
const result = await service.optimizeBuffer(
|
|
214
|
+
const result = await service.optimizeBuffer(INPUT, 'image/jpeg', 'photo.jpg');
|
|
154
215
|
|
|
155
216
|
expect(result).toEqual({
|
|
156
217
|
buffer: out,
|
|
@@ -169,7 +230,7 @@ describe('ImageOptimizationService', () => {
|
|
|
169
230
|
const out = Buffer.from('jpeg-small');
|
|
170
231
|
mockSharp.__instance.toBuffer.mockResolvedValue(out);
|
|
171
232
|
|
|
172
|
-
const result = await service.optimizeBuffer(
|
|
233
|
+
const result = await service.optimizeBuffer(INPUT, 'image/jpeg', 'a.jpg');
|
|
173
234
|
|
|
174
235
|
expect(result).toEqual({ buffer: out, mimetype: 'image/jpeg', originalname: 'a.jpg' });
|
|
175
236
|
expect(mockSharp.__instance.jpeg).toHaveBeenCalledWith({ quality: 60 });
|
|
@@ -184,7 +245,7 @@ describe('ImageOptimizationService', () => {
|
|
|
184
245
|
const out = Buffer.from('png-small');
|
|
185
246
|
mockSharp.__instance.toBuffer.mockResolvedValue(out);
|
|
186
247
|
|
|
187
|
-
const result = await service.optimizeBuffer(
|
|
248
|
+
const result = await service.optimizeBuffer(INPUT, 'image/png', 'a.png');
|
|
188
249
|
|
|
189
250
|
expect(result.mimetype).toBe('image/png');
|
|
190
251
|
// quality 80 -> compressionLevel 2
|
|
@@ -199,7 +260,7 @@ describe('ImageOptimizationService', () => {
|
|
|
199
260
|
const out = Buffer.from('png-from-tiff');
|
|
200
261
|
mockSharp.__instance.toBuffer.mockResolvedValue(out);
|
|
201
262
|
|
|
202
|
-
const result = await service.optimizeBuffer(
|
|
263
|
+
const result = await service.optimizeBuffer(INPUT, 'image/tiff', 'scan.tiff');
|
|
203
264
|
|
|
204
265
|
expect(result).toEqual({
|
|
205
266
|
buffer: out,
|
|
@@ -213,7 +274,7 @@ describe('ImageOptimizationService', () => {
|
|
|
213
274
|
const out = Buffer.from('resized');
|
|
214
275
|
mockSharp.__instance.toBuffer.mockResolvedValue(out);
|
|
215
276
|
|
|
216
|
-
const result = await service.optimizeBuffer(
|
|
277
|
+
const result = await service.optimizeBuffer(INPUT, 'image/jpeg', 'a.jpg');
|
|
217
278
|
|
|
218
279
|
expect(mockSharp.__instance.resize).toHaveBeenCalledWith({
|
|
219
280
|
width: 1024,
|
|
@@ -228,7 +289,7 @@ describe('ImageOptimizationService', () => {
|
|
|
228
289
|
const { service } = makeService({ 'image-max-dimension': '1024' });
|
|
229
290
|
mockSharp.__instance.toBuffer.mockResolvedValue(Buffer.from('cropped'));
|
|
230
291
|
|
|
231
|
-
await service.optimizeBuffer(
|
|
292
|
+
await service.optimizeBuffer(INPUT, 'image/jpeg', 'a.jpg', {
|
|
232
293
|
maxWidth: 200,
|
|
233
294
|
maxHeight: 100,
|
|
234
295
|
});
|
|
@@ -245,13 +306,89 @@ describe('ImageOptimizationService', () => {
|
|
|
245
306
|
const out = Buffer.from('reencoded');
|
|
246
307
|
mockSharp.__instance.toBuffer.mockResolvedValue(out);
|
|
247
308
|
|
|
248
|
-
const result = await service.optimizeBuffer(
|
|
309
|
+
const result = await service.optimizeBuffer(INPUT, 'image/heic', 'a.heic');
|
|
249
310
|
|
|
250
|
-
expect(result).toEqual({ buffer: out, mimetype: 'image/
|
|
311
|
+
expect(result).toEqual({ buffer: out, mimetype: 'image/heic', originalname: 'a.heic' });
|
|
251
312
|
expect(mockSharp.__instance.jpeg).not.toHaveBeenCalled();
|
|
252
313
|
expect(mockSharp.__instance.png).not.toHaveBeenCalled();
|
|
253
314
|
});
|
|
254
315
|
|
|
316
|
+
it('não toca em SVG mesmo com max-dimension configurado (sharp não tem encoder de SVG)', async () => {
|
|
317
|
+
const { service } = makeService({ 'image-max-dimension': '500' });
|
|
318
|
+
const buffer = Buffer.from('<svg></svg>');
|
|
319
|
+
|
|
320
|
+
const result = await service.optimizeBuffer(buffer, 'image/svg+xml', 'logo.svg');
|
|
321
|
+
|
|
322
|
+
expect(result).toEqual({ buffer, mimetype: 'image/svg+xml', originalname: 'logo.svg' });
|
|
323
|
+
expect(mockSharp).not.toHaveBeenCalled();
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
it('não toca em GIF mesmo com max-dimension configurado (preserva a animação)', async () => {
|
|
327
|
+
const { service } = makeService({ 'image-max-dimension': '500' });
|
|
328
|
+
const buffer = Buffer.from('gif-bytes');
|
|
329
|
+
|
|
330
|
+
const result = await service.optimizeBuffer(buffer, 'image/gif', 'a.gif');
|
|
331
|
+
|
|
332
|
+
expect(result).toEqual({ buffer, mimetype: 'image/gif', originalname: 'a.gif' });
|
|
333
|
+
expect(mockSharp).not.toHaveBeenCalled();
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
// Recomprimir nem sempre encolhe: ilustração chapada e captura de tela com poucas
|
|
337
|
+
// cores costumam sair maiores em WebP com perda do que no PNG original. Gravar esse
|
|
338
|
+
// resultado inflaria justamente os arquivos que a feature deveria aliviar.
|
|
339
|
+
it('mantém o original quando o webp fica maior que a entrada', async () => {
|
|
340
|
+
const { service } = makeService({
|
|
341
|
+
'image-webp-conversion-enabled': true,
|
|
342
|
+
'image-webp-convert-types': ['image/png'],
|
|
343
|
+
});
|
|
344
|
+
const small = Buffer.alloc(64, 1);
|
|
345
|
+
mockSharp.__instance.toBuffer.mockResolvedValue(Buffer.alloc(4096, 2));
|
|
346
|
+
|
|
347
|
+
const result = await service.optimizeBuffer(small, 'image/png', 'flat-art.png');
|
|
348
|
+
|
|
349
|
+
expect(result).toEqual({
|
|
350
|
+
buffer: small,
|
|
351
|
+
mimetype: 'image/png',
|
|
352
|
+
originalname: 'flat-art.png',
|
|
353
|
+
});
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
it('mantém o original quando a recompressão no mesmo formato fica maior', async () => {
|
|
357
|
+
const { service } = makeService({
|
|
358
|
+
'image-compression-enabled': true,
|
|
359
|
+
'image-compression-types': ['image/jpeg'],
|
|
360
|
+
});
|
|
361
|
+
const small = Buffer.alloc(64, 1);
|
|
362
|
+
mockSharp.__instance.toBuffer.mockResolvedValue(Buffer.alloc(4096, 2));
|
|
363
|
+
|
|
364
|
+
const result = await service.optimizeBuffer(small, 'image/jpeg', 'a.jpg');
|
|
365
|
+
|
|
366
|
+
expect(result).toEqual({ buffer: small, mimetype: 'image/jpeg', originalname: 'a.jpg' });
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
// O recorte com dimensões exatas é a razão da chamada (capa de curso), não uma
|
|
370
|
+
// tentativa de encolher: devolver o original ali entregaria a proporção errada.
|
|
371
|
+
it('aceita resultado maior quando o alvo exato foi pedido (crop)', async () => {
|
|
372
|
+
const { service } = makeService({
|
|
373
|
+
'image-webp-conversion-enabled': true,
|
|
374
|
+
'image-webp-convert-types': ['image/png'],
|
|
375
|
+
});
|
|
376
|
+
const small = Buffer.alloc(64, 1);
|
|
377
|
+
const bigger = Buffer.alloc(4096, 2);
|
|
378
|
+
mockSharp.__instance.toBuffer.mockResolvedValue(bigger);
|
|
379
|
+
|
|
380
|
+
const result = await service.optimizeBuffer(small, 'image/png', 'banner.png', {
|
|
381
|
+
maxWidth: 1200,
|
|
382
|
+
maxHeight: 630,
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
expect(result).toEqual({
|
|
386
|
+
buffer: bigger,
|
|
387
|
+
mimetype: 'image/webp',
|
|
388
|
+
originalname: 'banner.webp',
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
|
|
255
392
|
it('faz fallback para o original quando o sharp lança', async () => {
|
|
256
393
|
const { service } = makeService({
|
|
257
394
|
'image-compression-enabled': 'true',
|
|
@@ -261,7 +398,7 @@ describe('ImageOptimizationService', () => {
|
|
|
261
398
|
const warnSpy = jest
|
|
262
399
|
.spyOn((service as any).logger, 'warn')
|
|
263
400
|
.mockImplementation(() => undefined);
|
|
264
|
-
const buffer =
|
|
401
|
+
const buffer = INPUT;
|
|
265
402
|
|
|
266
403
|
const result = await service.optimizeBuffer(buffer, 'image/jpeg', 'a.jpg');
|
|
267
404
|
|
|
@@ -277,7 +414,7 @@ describe('ImageOptimizationService', () => {
|
|
|
277
414
|
});
|
|
278
415
|
mockSharp.__instance.toBuffer.mockResolvedValue(Buffer.from('x'));
|
|
279
416
|
|
|
280
|
-
await service.optimizeBuffer(
|
|
417
|
+
await service.optimizeBuffer(INPUT, 'image/jpeg', 'a.jpg');
|
|
281
418
|
|
|
282
419
|
expect(mockSharp.__instance.jpeg).toHaveBeenCalledWith({ quality: 80 });
|
|
283
420
|
});
|
|
@@ -289,7 +426,7 @@ describe('ImageOptimizationService', () => {
|
|
|
289
426
|
mockSharp.__instance.webp.mockReturnValue(mockSharp.__instance);
|
|
290
427
|
mockSharp.__instance.toBuffer.mockResolvedValue(Buffer.from('out'));
|
|
291
428
|
|
|
292
|
-
const result = await service.resizeBuffer(
|
|
429
|
+
const result = await service.resizeBuffer(INPUT, 'image/jpeg', {
|
|
293
430
|
width: 640,
|
|
294
431
|
format: 'webp',
|
|
295
432
|
quality: 82,
|
|
@@ -312,7 +449,7 @@ describe('ImageOptimizationService', () => {
|
|
|
312
449
|
const { service } = makeService();
|
|
313
450
|
mockSharp.__instance.toBuffer.mockResolvedValue(Buffer.from('out'));
|
|
314
451
|
|
|
315
|
-
const result = await service.resizeBuffer(
|
|
452
|
+
const result = await service.resizeBuffer(INPUT, 'image/png', {
|
|
316
453
|
width: 320,
|
|
317
454
|
});
|
|
318
455
|
|
|
@@ -352,7 +489,7 @@ describe('ImageOptimizationService', () => {
|
|
|
352
489
|
.mockImplementation(() => undefined);
|
|
353
490
|
mockSharp.__instance.toBuffer.mockRejectedValue(new Error('sharp boom'));
|
|
354
491
|
|
|
355
|
-
const result = await service.resizeBuffer(
|
|
492
|
+
const result = await service.resizeBuffer(INPUT, 'image/jpeg', {
|
|
356
493
|
width: 640,
|
|
357
494
|
});
|
|
358
495
|
|
|
@@ -364,7 +501,7 @@ describe('ImageOptimizationService', () => {
|
|
|
364
501
|
const { service } = makeService();
|
|
365
502
|
mockSharp.__instance.toBuffer.mockResolvedValue(Buffer.from('out'));
|
|
366
503
|
|
|
367
|
-
await service.resizeBuffer(
|
|
504
|
+
await service.resizeBuffer(INPUT, 'image/jpeg', {
|
|
368
505
|
width: 640,
|
|
369
506
|
quality: 0,
|
|
370
507
|
});
|
|
@@ -43,16 +43,36 @@ export class ImageOptimizationService {
|
|
|
43
43
|
private readonly settingService: SettingService,
|
|
44
44
|
) {}
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
/**
|
|
47
|
+
* `SettingService.getSettingValues` já converte o valor pelo `type` da setting: uma
|
|
48
|
+
* de tipo `array` chega aqui como array JS, não como a string do banco. Ler só a
|
|
49
|
+
* string fazia `JSON.parse` receber o array coagido para `"image/jpeg,image/png"`,
|
|
50
|
+
* lançar, e o catch devolver `[]` — o que desligava compressão e WebP em silêncio,
|
|
51
|
+
* por mais que os tipos estivessem marcados na tela. Aceitar as duas formas mantém
|
|
52
|
+
* o serviço utilizável por quem monta as settings à mão (specs, chamadores diretos).
|
|
53
|
+
*/
|
|
54
|
+
private parseArraySetting(raw: unknown): string[] {
|
|
55
|
+
if (Array.isArray(raw)) {
|
|
56
|
+
return raw.map((item) => String(item));
|
|
57
|
+
}
|
|
58
|
+
if (typeof raw !== 'string' || !raw) return [];
|
|
48
59
|
try {
|
|
49
60
|
const parsed = JSON.parse(raw);
|
|
50
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
61
|
+
return Array.isArray(parsed) ? parsed.map((item) => String(item)) : [];
|
|
51
62
|
} catch {
|
|
52
63
|
return [];
|
|
53
64
|
}
|
|
54
65
|
}
|
|
55
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Mesmo motivo do `parseArraySetting`: uma setting `boolean` chega como `true`, e
|
|
69
|
+
* comparar com a string `'true'` dava sempre `false`. É a mesma leitura tolerante
|
|
70
|
+
* que o `OAuthService` já faz.
|
|
71
|
+
*/
|
|
72
|
+
private parseBooleanSetting(raw: unknown): boolean {
|
|
73
|
+
return raw === true || raw === 'true';
|
|
74
|
+
}
|
|
75
|
+
|
|
56
76
|
private replaceExtension(filename: string, newExt: string): string {
|
|
57
77
|
const base = basename(filename, extname(filename));
|
|
58
78
|
return `${base}.${newExt}`;
|
|
@@ -88,6 +108,29 @@ export class ImageOptimizationService {
|
|
|
88
108
|
}
|
|
89
109
|
}
|
|
90
110
|
|
|
111
|
+
/**
|
|
112
|
+
* Recusa um resultado que não ficou menor que a entrada.
|
|
113
|
+
*
|
|
114
|
+
* Recomprimir nem sempre encolhe: ilustração chapada e captura de tela com poucas
|
|
115
|
+
* cores são casos em que o PNG original já é menor do que qualquer WebP com perda, e
|
|
116
|
+
* gravar o resultado maior contraria o próprio motivo de a feature existir. Pior, o
|
|
117
|
+
* `reoptimizeFile` autoriza a troca só pela mudança de mimetype — sem esta guarda, a
|
|
118
|
+
* varredura do acervo poderia inflar justamente os arquivos que ela deveria aliviar.
|
|
119
|
+
*
|
|
120
|
+
* Não vale quando o chamador pediu dimensões exatas (`maxWidth` + `maxHeight`): ali o
|
|
121
|
+
* recorte é a razão da chamada, e devolver o original entregaria a proporção errada.
|
|
122
|
+
*/
|
|
123
|
+
private keepSmaller(
|
|
124
|
+
original: OptimizedImage,
|
|
125
|
+
optimized: OptimizedImage,
|
|
126
|
+
isExactCrop: boolean,
|
|
127
|
+
): OptimizedImage {
|
|
128
|
+
if (isExactCrop || optimized.buffer.length < original.buffer.length) {
|
|
129
|
+
return optimized;
|
|
130
|
+
}
|
|
131
|
+
return original;
|
|
132
|
+
}
|
|
133
|
+
|
|
91
134
|
/**
|
|
92
135
|
* Applies optional resize, compression and/or WebP conversion to the given
|
|
93
136
|
* buffer based on the current settings. Returns the original data unchanged
|
|
@@ -104,6 +147,17 @@ export class ImageOptimizationService {
|
|
|
104
147
|
return { buffer, mimetype, originalname };
|
|
105
148
|
}
|
|
106
149
|
|
|
150
|
+
// Vectors must stay vectors, and resizing an animated GIF would flatten it to a
|
|
151
|
+
// single frame — same reasoning as resizeBuffer() below. Sharp also has no SVG
|
|
152
|
+
// encoder: falling through to the generic "unknown format" branch re-encodes the
|
|
153
|
+
// rasterized result as PNG bytes while this function kept reporting the original
|
|
154
|
+
// `image/svg+xml` mimetype, so the stored file ended up with a Content-Type that
|
|
155
|
+
// didn't match its actual bytes and every consumer (the settings preview, the
|
|
156
|
+
// dynamic favicon, <img> tags) rendered a broken image.
|
|
157
|
+
if (mimetype === 'image/svg+xml' || mimetype === 'image/gif') {
|
|
158
|
+
return { buffer, mimetype, originalname };
|
|
159
|
+
}
|
|
160
|
+
|
|
107
161
|
const settings = await this.settingService.getSettingValues([
|
|
108
162
|
'image-compression-enabled',
|
|
109
163
|
'image-compression-quality',
|
|
@@ -113,8 +167,12 @@ export class ImageOptimizationService {
|
|
|
113
167
|
'image-max-dimension',
|
|
114
168
|
]);
|
|
115
169
|
|
|
116
|
-
const compressionEnabled =
|
|
117
|
-
|
|
170
|
+
const compressionEnabled = this.parseBooleanSetting(
|
|
171
|
+
settings['image-compression-enabled'],
|
|
172
|
+
);
|
|
173
|
+
const webpEnabled = this.parseBooleanSetting(
|
|
174
|
+
settings['image-webp-conversion-enabled'],
|
|
175
|
+
);
|
|
118
176
|
|
|
119
177
|
const quality = Math.min(
|
|
120
178
|
100,
|
|
@@ -149,6 +207,9 @@ export class ImageOptimizationService {
|
|
|
149
207
|
return { buffer, mimetype, originalname };
|
|
150
208
|
}
|
|
151
209
|
|
|
210
|
+
const original: OptimizedImage = { buffer, mimetype, originalname };
|
|
211
|
+
const isExactCrop = targetWidth > 0 && targetHeight > 0;
|
|
212
|
+
|
|
152
213
|
try {
|
|
153
214
|
let instance = sharp(buffer);
|
|
154
215
|
if (resize) {
|
|
@@ -158,50 +219,74 @@ export class ImageOptimizationService {
|
|
|
158
219
|
// WebP conversion has priority over plain compression.
|
|
159
220
|
if (willConvertWebp) {
|
|
160
221
|
const optimized = await instance.webp({ quality }).toBuffer();
|
|
161
|
-
return
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
222
|
+
return this.keepSmaller(
|
|
223
|
+
original,
|
|
224
|
+
{
|
|
225
|
+
buffer: optimized,
|
|
226
|
+
mimetype: 'image/webp',
|
|
227
|
+
originalname: this.replaceExtension(originalname, 'webp'),
|
|
228
|
+
},
|
|
229
|
+
isExactCrop,
|
|
230
|
+
);
|
|
166
231
|
}
|
|
167
232
|
|
|
168
233
|
// Re-encode keeping the original format. Reached when compressing, or when
|
|
169
234
|
// only resizing (a resize always requires re-encoding the buffer).
|
|
170
235
|
if (mimetype === 'image/jpeg') {
|
|
171
236
|
const optimized = await instance.jpeg({ quality }).toBuffer();
|
|
172
|
-
return
|
|
237
|
+
return this.keepSmaller(
|
|
238
|
+
original,
|
|
239
|
+
{ buffer: optimized, mimetype, originalname },
|
|
240
|
+
isExactCrop,
|
|
241
|
+
);
|
|
173
242
|
} else if (mimetype === 'image/png') {
|
|
174
243
|
const optimized = await instance
|
|
175
244
|
.png({ compressionLevel: this.pngCompressionLevel(quality) })
|
|
176
245
|
.toBuffer();
|
|
177
|
-
return
|
|
246
|
+
return this.keepSmaller(
|
|
247
|
+
original,
|
|
248
|
+
{ buffer: optimized, mimetype, originalname },
|
|
249
|
+
isExactCrop,
|
|
250
|
+
);
|
|
178
251
|
} else if (mimetype === 'image/webp') {
|
|
179
252
|
const optimized = await instance.webp({ quality }).toBuffer();
|
|
180
|
-
return
|
|
253
|
+
return this.keepSmaller(
|
|
254
|
+
original,
|
|
255
|
+
{ buffer: optimized, mimetype, originalname },
|
|
256
|
+
isExactCrop,
|
|
257
|
+
);
|
|
181
258
|
} else if (mimetype === 'image/bmp' || mimetype === 'image/tiff') {
|
|
182
259
|
// Convert to PNG as a compressed lossless alternative.
|
|
183
260
|
const optimized = await instance
|
|
184
261
|
.png({ compressionLevel: this.pngCompressionLevel(quality) })
|
|
185
262
|
.toBuffer();
|
|
186
|
-
return
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
263
|
+
return this.keepSmaller(
|
|
264
|
+
original,
|
|
265
|
+
{
|
|
266
|
+
buffer: optimized,
|
|
267
|
+
mimetype: 'image/png',
|
|
268
|
+
originalname: this.replaceExtension(originalname, 'png'),
|
|
269
|
+
},
|
|
270
|
+
isExactCrop,
|
|
271
|
+
);
|
|
191
272
|
}
|
|
192
273
|
|
|
193
274
|
// Unknown image format: only re-encode if a resize was requested,
|
|
194
275
|
// otherwise leave it untouched.
|
|
195
276
|
if (resize) {
|
|
196
277
|
const optimized = await instance.toBuffer();
|
|
197
|
-
return
|
|
278
|
+
return this.keepSmaller(
|
|
279
|
+
original,
|
|
280
|
+
{ buffer: optimized, mimetype, originalname },
|
|
281
|
+
isExactCrop,
|
|
282
|
+
);
|
|
198
283
|
}
|
|
199
|
-
return
|
|
284
|
+
return original;
|
|
200
285
|
} catch (err) {
|
|
201
286
|
this.logger.warn(
|
|
202
287
|
`Image optimization failed for "${originalname}" (${mimetype}): ${err instanceof Error ? err.message : String(err)}. Falling back to original.`,
|
|
203
288
|
);
|
|
204
|
-
return
|
|
289
|
+
return original;
|
|
205
290
|
}
|
|
206
291
|
}
|
|
207
292
|
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fragmento de schema e texto de descrição que toda tool MCP que recebe arquivo espalha
|
|
3
|
+
* no seu `inputSchema`.
|
|
4
|
+
*
|
|
5
|
+
* Fica num lugar só porque é ele que o modelo lê para decidir o caminho: se cada tool
|
|
6
|
+
* descrevesse as modalidades com palavras próprias, o agente escolheria diferente em cada
|
|
7
|
+
* uma, e a mais barata (`fileId`) perderia para a mais cara (`fileBase64`) por acidente
|
|
8
|
+
* de redação.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/** As quatro origens, em ordem de preferência. */
|
|
12
|
+
export const FILE_SOURCE_FIELDS = [
|
|
13
|
+
'fileId',
|
|
14
|
+
'uploadId',
|
|
15
|
+
'fileUrl',
|
|
16
|
+
'fileBase64',
|
|
17
|
+
] as const;
|
|
18
|
+
|
|
19
|
+
export type FileSourceModality = (typeof FILE_SOURCE_FIELDS)[number];
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Propriedades para espalhar no `inputSchema` da tool.
|
|
23
|
+
*
|
|
24
|
+
* Nenhuma delas entra em `required`: a exigência é "exatamente uma", que JSON Schema
|
|
25
|
+
* expressaria com `oneOf` mas que nem todo cliente MCP avalia — a checagem de verdade
|
|
26
|
+
* está em `McpFileSourceService.detectModality`, e a descrição abaixo é o que faz o
|
|
27
|
+
* modelo acertar antes de chamar.
|
|
28
|
+
*/
|
|
29
|
+
export const FILE_SOURCE_SCHEMA_PROPERTIES: Record<string, any> = {
|
|
30
|
+
fileId: {
|
|
31
|
+
type: 'number',
|
|
32
|
+
description:
|
|
33
|
+
'Preferred. ID of a file already in storage (from core.files.list, an attachments list, or core.files.upload.complete). Must have been uploaded by the caller, unless the caller is an administrator.',
|
|
34
|
+
},
|
|
35
|
+
uploadId: {
|
|
36
|
+
type: 'number',
|
|
37
|
+
description:
|
|
38
|
+
'ID returned by core.files.create-upload-url or core.files.upload.init, after the binary has been uploaded. Use this to send a file that is on disk without putting its bytes in the conversation.',
|
|
39
|
+
},
|
|
40
|
+
fileUrl: {
|
|
41
|
+
type: 'string',
|
|
42
|
+
description:
|
|
43
|
+
'Public http(s) URL the server downloads by itself. Private, loopback, link-local and cloud metadata addresses are rejected, redirects are limited, and the content type is checked against the actual bytes.',
|
|
44
|
+
},
|
|
45
|
+
fileBase64: {
|
|
46
|
+
type: 'string',
|
|
47
|
+
description:
|
|
48
|
+
'Base64-encoded content, sent inline. Only for small files (about 256kb): base64 of anything larger overflows the model output limit. Requires fileName and mimeType.',
|
|
49
|
+
},
|
|
50
|
+
fileName: {
|
|
51
|
+
type: 'string',
|
|
52
|
+
description: 'Original file name. Required with fileBase64; optional with fileUrl.',
|
|
53
|
+
},
|
|
54
|
+
mimeType: {
|
|
55
|
+
type: 'string',
|
|
56
|
+
description:
|
|
57
|
+
'MIME type, e.g. application/pdf. Required with fileBase64; optional with fileUrl. Must be allowed by the storage-accept-mimetype setting, and is verified against the real content.',
|
|
58
|
+
},
|
|
59
|
+
sha256: {
|
|
60
|
+
type: 'string',
|
|
61
|
+
description:
|
|
62
|
+
'Optional hex sha256 of the file. When present, content that does not hash to it is rejected.',
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Bloco anexado à descrição de cada tool.
|
|
68
|
+
*
|
|
69
|
+
* Diz o teto real — `storage-max-size`, 10 MB por padrão. A descrição antiga de
|
|
70
|
+
* `finance.titles.attachments.add` anunciava ~37 MB, número derivado do
|
|
71
|
+
* `api-body-size-limit`; ele nunca valeu, porque quem barra primeiro é o
|
|
72
|
+
* `storage-max-size`, e é ele que aparece na mensagem de erro.
|
|
73
|
+
*/
|
|
74
|
+
export const FILE_SOURCE_DESCRIPTION = [
|
|
75
|
+
'Exactly one file source is required: fileId, uploadId, fileUrl or fileBase64.',
|
|
76
|
+
'Prefer fileId; use fileBase64 only for files of a few hundred kilobytes, since the encoded content has to fit in a single tool call.',
|
|
77
|
+
'To send a local file without putting it in the conversation, call core.files.create-upload-url, PUT the binary to the URL it returns, call core.files.upload.complete, and pass the resulting fileId here.',
|
|
78
|
+
'Every path enforces the storage-accept-mimetype and storage-max-size settings (10mb by default).',
|
|
79
|
+
].join(' ');
|
|
80
|
+
|
|
81
|
+
/** Junta a descrição própria da tool com o bloco comum. */
|
|
82
|
+
export const withFileSourceDescription = (description: string): string =>
|
|
83
|
+
`${description.trim()} ${FILE_SOURCE_DESCRIPTION}`;
|