@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,246 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tradução única de `?from=yyyy-MM-dd&to=yyyy-MM-dd` para limites comparáveis
|
|
3
|
+
* no banco.
|
|
4
|
+
*
|
|
5
|
+
* A janela é **meio-aberta** — `[from 00:00, to+1d 00:00)` — e nunca inclusiva.
|
|
6
|
+
* `lte new Date('2026-08-31')` compara contra o *instante* `T00:00:00Z` e
|
|
7
|
+
* derruba o dia 31 inteiro; `lte 23:59:59.999` perde o último milissegundo e
|
|
8
|
+
* mente sobre o modelo de dados. A forma meio-aberta é a única que vale igual
|
|
9
|
+
* para coluna `@db.Date` e para coluna `@db.Timestamptz(6)`, sem flag nenhum.
|
|
10
|
+
*
|
|
11
|
+
* Diferenças de propósito para o `resolveRangeWindow` do operations
|
|
12
|
+
* (`libraries/operations/src/services/shared/operations-activity-window.helper.ts`),
|
|
13
|
+
* que serve relatório e não listagem:
|
|
14
|
+
*
|
|
15
|
+
* - sem `from` e sem `to` devolve `null` (= sem recorte), e não o dia de hoje:
|
|
16
|
+
* listagem sem filtro tem de devolver tudo;
|
|
17
|
+
* - **não limita o fim em agora**: reunião de amanhã, contrato que vence em
|
|
18
|
+
* 2027 e assinatura com período à frente são justamente o que essas telas
|
|
19
|
+
* procuram;
|
|
20
|
+
* - aceita janela aberta de um lado só.
|
|
21
|
+
*
|
|
22
|
+
* Os dois hão de convergir, mas depois: aquele alimenta o e-mail do relatório
|
|
23
|
+
* diário e depende do clamp.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
export interface DateRangeBounds {
|
|
27
|
+
gte?: Date;
|
|
28
|
+
lt?: Date;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface DateRange {
|
|
32
|
+
/** Início inclusivo, no fuso pedido. Ausente quando só veio `to`. */
|
|
33
|
+
gte?: Date;
|
|
34
|
+
/** Fim EXCLUSIVO (`to` + 1 dia), no fuso pedido. Ausente quando só veio `from`. */
|
|
35
|
+
lt?: Date;
|
|
36
|
+
/**
|
|
37
|
+
* As mesmas bordas em UTC puro — o que uma coluna `@db.Date` entende.
|
|
38
|
+
*
|
|
39
|
+
* O Prisma manda o parâmetro como `DATE` e o trunca pela leitura **UTC** do
|
|
40
|
+
* `Date`. Uma borda deslocada para fuso a leste derruba um dia inteiro sem
|
|
41
|
+
* erro nenhum: meia-noite de 01/09 em `Asia/Tokyo` é `2026-08-31T15:00Z`,
|
|
42
|
+
* que trunca para `2026-08-31`. Coluna de data pura não tem fuso, então
|
|
43
|
+
* converter fuso nela é sempre errado.
|
|
44
|
+
*/
|
|
45
|
+
dateOnly: DateRangeBounds;
|
|
46
|
+
/** `yyyy-MM-dd` normalizado, já desinvertido. */
|
|
47
|
+
fromIso?: string;
|
|
48
|
+
/** `to + 1 dia`, para o `< $n::date` do SQL cru. */
|
|
49
|
+
toExclusiveIso?: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface DateRangeOptions {
|
|
53
|
+
/**
|
|
54
|
+
* Fuso IANA das bordas civis; o padrão é UTC, que é o fuso dos pods da API.
|
|
55
|
+
* Só afeta coluna com hora — veja `dateOnly`.
|
|
56
|
+
*/
|
|
57
|
+
timezone?: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Tolera ISO completo: só o prefixo `yyyy-MM-dd` interessa. */
|
|
61
|
+
const ISO_DATE_PREFIX = /^(\d{4})-(\d{2})-(\d{2})/;
|
|
62
|
+
|
|
63
|
+
function normalizeTimezone(timezone?: string): string {
|
|
64
|
+
const candidate = String(timezone ?? '').trim();
|
|
65
|
+
if (!candidate) return 'UTC';
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
new Intl.DateTimeFormat('en-US', { timeZone: candidate });
|
|
69
|
+
return candidate;
|
|
70
|
+
} catch {
|
|
71
|
+
// Fuso inventado não pode derrubar uma listagem: cai no fuso dos pods.
|
|
72
|
+
return 'UTC';
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function toIsoDate(value?: string | null): string | null {
|
|
77
|
+
const match = ISO_DATE_PREFIX.exec(String(value ?? '').trim());
|
|
78
|
+
if (!match) return null;
|
|
79
|
+
|
|
80
|
+
const [, yearText, monthText, dayText] = match;
|
|
81
|
+
const year = Number(yearText);
|
|
82
|
+
const month = Number(monthText);
|
|
83
|
+
const day = Number(dayText);
|
|
84
|
+
|
|
85
|
+
// `2026-02-31` casa com o regex e o `Date.UTC` o aceita virando 03/03.
|
|
86
|
+
// Só a volta denuncia.
|
|
87
|
+
const probe = new Date(Date.UTC(year, month - 1, day));
|
|
88
|
+
if (
|
|
89
|
+
probe.getUTCFullYear() !== year ||
|
|
90
|
+
probe.getUTCMonth() !== month - 1 ||
|
|
91
|
+
probe.getUTCDate() !== day
|
|
92
|
+
) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return `${yearText}-${monthText}-${dayText}`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Soma dias de **calendário**. Somar 86400000 ms a um instante zonado erra o
|
|
101
|
+
* dia em toda virada de horário de verão.
|
|
102
|
+
*/
|
|
103
|
+
export function addIsoDays(isoDate: string, days: number): string {
|
|
104
|
+
const [year, month, day] = isoDate.split('-').map(Number);
|
|
105
|
+
|
|
106
|
+
return new Date(Date.UTC(year, month - 1, day + days))
|
|
107
|
+
.toISOString()
|
|
108
|
+
.slice(0, 10);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function utcMidnight(isoDate: string): Date {
|
|
112
|
+
return new Date(`${isoDate}T00:00:00.000Z`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Deslocamento do fuso, em ms, no instante informado. Formatar a data no fuso
|
|
117
|
+
* e reinterpretar as partes como UTC devolve o offset vigente — inclusive em
|
|
118
|
+
* fuso com horário de verão.
|
|
119
|
+
*/
|
|
120
|
+
function timezoneOffsetMs(at: Date, timezone: string): number {
|
|
121
|
+
const parts = new Intl.DateTimeFormat('en-US', {
|
|
122
|
+
timeZone: timezone,
|
|
123
|
+
hour12: false,
|
|
124
|
+
year: 'numeric',
|
|
125
|
+
month: '2-digit',
|
|
126
|
+
day: '2-digit',
|
|
127
|
+
hour: '2-digit',
|
|
128
|
+
minute: '2-digit',
|
|
129
|
+
second: '2-digit',
|
|
130
|
+
}).formatToParts(at);
|
|
131
|
+
|
|
132
|
+
const part = (type: string) =>
|
|
133
|
+
Number(parts.find((item) => item.type === type)?.value ?? 0);
|
|
134
|
+
|
|
135
|
+
const asUtc = Date.UTC(
|
|
136
|
+
part('year'),
|
|
137
|
+
part('month') - 1,
|
|
138
|
+
part('day'),
|
|
139
|
+
// Algumas engines devolvem "24" para a meia-noite em hour12:false.
|
|
140
|
+
part('hour') % 24,
|
|
141
|
+
part('minute'),
|
|
142
|
+
part('second'),
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
return asUtc - at.getTime();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** Meia-noite civil de `isoDate` naquele fuso, como instante UTC. */
|
|
149
|
+
function zonedMidnight(isoDate: string, timezone: string): Date {
|
|
150
|
+
if (timezone === 'UTC') return utcMidnight(isoDate);
|
|
151
|
+
|
|
152
|
+
const naive = Date.parse(`${isoDate}T00:00:00.000Z`);
|
|
153
|
+
|
|
154
|
+
// Duas passadas: a primeira usa o offset do palpite, a segunda o do instante
|
|
155
|
+
// já corrigido — é o que resolve a virada de horário de verão.
|
|
156
|
+
let utc = naive;
|
|
157
|
+
for (let pass = 0; pass < 2; pass += 1) {
|
|
158
|
+
utc = naive - timezoneOffsetMs(new Date(utc), timezone);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return new Date(utc);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Resolve o par `from`/`to` da query.
|
|
166
|
+
*
|
|
167
|
+
* Devolve `null` quando nenhuma das pontas é data utilizável — o que cobre de
|
|
168
|
+
* uma vez "o cliente não filtrou", "o cliente limpou o campo e mandou
|
|
169
|
+
* `?from=`" e "o cliente mandou `31/08/2026`". Filtro ilegível vira "sem
|
|
170
|
+
* filtro", não 500: o `ValidationPipe` barra o que dá para barrar na borda, e
|
|
171
|
+
* aqui dentro a listagem não pode cair.
|
|
172
|
+
*
|
|
173
|
+
* Intervalo invertido é desinvertido: arrastar o calendário de trás para
|
|
174
|
+
* frente não deve devolver lista vazia.
|
|
175
|
+
*/
|
|
176
|
+
export function resolveDateRange(
|
|
177
|
+
from?: string | null,
|
|
178
|
+
to?: string | null,
|
|
179
|
+
options: DateRangeOptions = {},
|
|
180
|
+
): DateRange | null {
|
|
181
|
+
const timezone = normalizeTimezone(options.timezone);
|
|
182
|
+
|
|
183
|
+
let fromIso = toIsoDate(from);
|
|
184
|
+
let toIso = toIsoDate(to);
|
|
185
|
+
|
|
186
|
+
if (!fromIso && !toIso) return null;
|
|
187
|
+
|
|
188
|
+
if (fromIso && toIso && toIso < fromIso) {
|
|
189
|
+
[fromIso, toIso] = [toIso, fromIso];
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const toExclusiveIso = toIso ? addIsoDays(toIso, 1) : undefined;
|
|
193
|
+
|
|
194
|
+
return {
|
|
195
|
+
gte: fromIso ? zonedMidnight(fromIso, timezone) : undefined,
|
|
196
|
+
lt: toExclusiveIso ? zonedMidnight(toExclusiveIso, timezone) : undefined,
|
|
197
|
+
dateOnly: {
|
|
198
|
+
gte: fromIso ? utcMidnight(fromIso) : undefined,
|
|
199
|
+
lt: toExclusiveIso ? utcMidnight(toExclusiveIso) : undefined,
|
|
200
|
+
},
|
|
201
|
+
fromIso: fromIso ?? undefined,
|
|
202
|
+
toExclusiveIso,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export interface DateRangeWhereOptions extends DateRangeOptions {
|
|
207
|
+
/**
|
|
208
|
+
* `true` para coluna `@db.Date`. Usa as bordas UTC puras, que são as únicas
|
|
209
|
+
* que o `DATE` do Postgres trunca para o dia certo.
|
|
210
|
+
*/
|
|
211
|
+
dateOnly?: boolean;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Fragmento pronto para espalhar dentro de um `where` do Prisma:
|
|
216
|
+
*
|
|
217
|
+
* ```ts
|
|
218
|
+
* const where = {
|
|
219
|
+
* deleted_at: null,
|
|
220
|
+
* ...dateRangeWhere('created_at', filters?.from, filters?.to),
|
|
221
|
+
* };
|
|
222
|
+
* ```
|
|
223
|
+
*
|
|
224
|
+
* Devolve `{}` quando não há recorte, então espalhar é sempre seguro e não
|
|
225
|
+
* existe o caminho "montei um `created_at: {}` vazio". As chaves ausentes são
|
|
226
|
+
* omitidas de verdade, não postas como `undefined`.
|
|
227
|
+
*/
|
|
228
|
+
export function dateRangeWhere(
|
|
229
|
+
field: string,
|
|
230
|
+
from?: string | null,
|
|
231
|
+
to?: string | null,
|
|
232
|
+
options: DateRangeWhereOptions = {},
|
|
233
|
+
): Record<string, DateRangeBounds> {
|
|
234
|
+
const range = resolveDateRange(from, to, options);
|
|
235
|
+
if (!range) return {};
|
|
236
|
+
|
|
237
|
+
const source = options.dateOnly
|
|
238
|
+
? range.dateOnly
|
|
239
|
+
: { gte: range.gte, lt: range.lt };
|
|
240
|
+
|
|
241
|
+
const bounds: DateRangeBounds = {};
|
|
242
|
+
if (source.gte) bounds.gte = source.gte;
|
|
243
|
+
if (source.lt) bounds.lt = source.lt;
|
|
244
|
+
|
|
245
|
+
return Object.keys(bounds).length > 0 ? { [field]: bounds } : {};
|
|
246
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { describe, it, expect } from '@jest/globals';
|
|
2
|
+
import { renderTemplate } from './webhook-utils';
|
|
3
|
+
|
|
4
|
+
describe('renderTemplate', () => {
|
|
5
|
+
it('interpola valores simples e aninhados', () => {
|
|
6
|
+
expect(renderTemplate('olá {{name}}', { name: 'João' })).toBe('olá João');
|
|
7
|
+
expect(
|
|
8
|
+
renderTemplate('{{alerts.0.labels.severity}}', {
|
|
9
|
+
alerts: [{ labels: { severity: 'warning' } }],
|
|
10
|
+
}),
|
|
11
|
+
).toBe('warning');
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('percorre listas com each', () => {
|
|
15
|
+
const out = renderTemplate('{{#each alerts}}[{{name}}]{{/each}}', {
|
|
16
|
+
alerts: [{ name: 'a' }, { name: 'b' }],
|
|
17
|
+
});
|
|
18
|
+
expect(out).toBe('[a][b]');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// O motivo dos helpers: o payload do Alertmanager traz status "firing" ou
|
|
22
|
+
// "resolved" e a mensagem tem de ser diferente em cada caso.
|
|
23
|
+
it('escolhe o ramo pelo status com o helper eq', () => {
|
|
24
|
+
const tpl = '{{#if (eq status "firing")}}ALERTA{{else}}NORMALIZOU{{/if}}';
|
|
25
|
+
expect(renderTemplate(tpl, { status: 'firing' })).toBe('ALERTA');
|
|
26
|
+
expect(renderTemplate(tpl, { status: 'resolved' })).toBe('NORMALIZOU');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('compara número e string como equivalentes', () => {
|
|
30
|
+
// O payload vem de JSON externo: quem escreve o template não deve precisar
|
|
31
|
+
// saber se o produtor mandou 200 ou "200".
|
|
32
|
+
expect(renderTemplate('{{#if (eq code 200)}}ok{{/if}}', { code: '200' })).toBe('ok');
|
|
33
|
+
expect(renderTemplate('{{#if (eq code "200")}}ok{{/if}}', { code: 200 })).toBe('ok');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('oferece ne, gt e lt', () => {
|
|
37
|
+
expect(renderTemplate('{{#if (ne a "x")}}dif{{/if}}', { a: 'y' })).toBe('dif');
|
|
38
|
+
expect(renderTemplate('{{#if (gt n 5)}}maior{{/if}}', { n: 6 })).toBe('maior');
|
|
39
|
+
expect(renderTemplate('{{#if (lt n 5)}}menor{{/if}}', { n: 4 })).toBe('menor');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('normaliza caixa com upper e lower', () => {
|
|
43
|
+
expect(renderTemplate('{{upper s}}', { s: 'firing' })).toBe('FIRING');
|
|
44
|
+
expect(renderTemplate('{{lower s}}', { s: 'FIRING' })).toBe('firing');
|
|
45
|
+
// Ausente não pode virar "undefined" no meio da mensagem.
|
|
46
|
+
expect(renderTemplate('{{upper ausente}}', {})).toBe('');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('mantém acentos e símbolos quando usa triple stash', () => {
|
|
50
|
+
// O destino é WhatsApp, não HTML: escapar viraria ' na tela do usuário.
|
|
51
|
+
const ctx = { texto: "capacidade 78% — 'atenção'" };
|
|
52
|
+
expect(renderTemplate('{{{texto}}}', ctx)).toBe("capacidade 78% — 'atenção'");
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('atravessa objetos e listas preservando os tipos que não são string', () => {
|
|
56
|
+
const out = renderTemplate(
|
|
57
|
+
{ titulo: '{{name}}', ativo: true, tentativas: 3, tags: ['{{name}}'] },
|
|
58
|
+
{ name: 'x' },
|
|
59
|
+
);
|
|
60
|
+
expect(out).toEqual({ titulo: 'x', ativo: true, tentativas: 3, tags: ['x'] });
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('devolve null e undefined sem tentar compilar', () => {
|
|
64
|
+
expect(renderTemplate(null, {})).toBeNull();
|
|
65
|
+
expect(renderTemplate(undefined, {})).toBeUndefined();
|
|
66
|
+
});
|
|
67
|
+
});
|
|
@@ -13,6 +13,32 @@ export function hashToken(token: string) {
|
|
|
13
13
|
return createHash('sha256').update(token).digest('hex');
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Handlebars puro não compara valores: `{{#if}}` só testa se algo é vazio. Sem
|
|
18
|
+
* isto um template não consegue reagir ao conteúdo do payload — o caso que
|
|
19
|
+
* motivou os helpers foi o webhook do Alertmanager, onde `status` chega como
|
|
20
|
+
* "firing" ou "resolved" e a mensagem precisa ser diferente em cada caso (mandar
|
|
21
|
+
* o texto de alerta quando o problema acabou é pior do que não mandar nada).
|
|
22
|
+
*
|
|
23
|
+
* Registrados uma vez, no import do módulo: `Handlebars.registerHelper` é global
|
|
24
|
+
* e chamá-lo a cada render só repetiria trabalho.
|
|
25
|
+
*/
|
|
26
|
+
const HELPERS: Record<string, (...args: any[]) => any> = {
|
|
27
|
+
// `{{#if (eq status "firing")}}`. A comparação é frouxa de propósito: o payload
|
|
28
|
+
// vem de JSON externo, onde 200 e "200" são a mesma coisa para quem escreve o
|
|
29
|
+
// template.
|
|
30
|
+
eq: (a: unknown, b: unknown) => String(a) === String(b),
|
|
31
|
+
ne: (a: unknown, b: unknown) => String(a) !== String(b),
|
|
32
|
+
gt: (a: unknown, b: unknown) => Number(a) > Number(b),
|
|
33
|
+
lt: (a: unknown, b: unknown) => Number(a) < Number(b),
|
|
34
|
+
upper: (value: unknown) => String(value ?? '').toUpperCase(),
|
|
35
|
+
lower: (value: unknown) => String(value ?? '').toLowerCase(),
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
for (const [name, fn] of Object.entries(HELPERS)) {
|
|
39
|
+
Handlebars.registerHelper(name, fn);
|
|
40
|
+
}
|
|
41
|
+
|
|
16
42
|
export function renderTemplate(source: unknown, context: Record<string, any>) {
|
|
17
43
|
if (source === undefined || source === null) {
|
|
18
44
|
return source;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { parseWhatsappRecipients, toWhatsappGroupJid } from './whatsapp-recipients';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* O que este parser decide é para ONDE um aviso interno vai. Errar aqui não dá
|
|
5
|
+
* erro: entrega em outro número, ou some em silêncio — os dois modos de falha
|
|
6
|
+
* que o canal existe para evitar.
|
|
7
|
+
*/
|
|
8
|
+
describe('toWhatsappGroupJid', () => {
|
|
9
|
+
it('aceita a forma atual, só dígitos com sufixo', () => {
|
|
10
|
+
expect(toWhatsappGroupJid('120363430651137530@g.us')).toBe(
|
|
11
|
+
'120363430651137530@g.us',
|
|
12
|
+
);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('aceita a forma antiga, derivada de quem criou o grupo', () => {
|
|
16
|
+
expect(toWhatsappGroupJid('5511992662104-1564082267@g.us')).toBe(
|
|
17
|
+
'5511992662104-1564082267@g.us',
|
|
18
|
+
);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('completa o sufixo de um id colado sem ele', () => {
|
|
22
|
+
expect(toWhatsappGroupJid('120363430651137530')).toBe(
|
|
23
|
+
'120363430651137530@g.us',
|
|
24
|
+
);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('não confunde telefone com grupo', () => {
|
|
28
|
+
expect(toWhatsappGroupJid('5511999998888')).toBeNull();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('descarta a colagem de dois telefones grudados', () => {
|
|
32
|
+
expect(toWhatsappGroupJid('5511999998888551199999777')).toBeNull();
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe('parseWhatsappRecipients', () => {
|
|
37
|
+
it('devolve lista vazia para configuração ausente', () => {
|
|
38
|
+
expect(parseWhatsappRecipients(null)).toEqual([]);
|
|
39
|
+
expect(parseWhatsappRecipients('')).toEqual([]);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('completa o DDI 55 de um número nacional com máscara', () => {
|
|
43
|
+
expect(parseWhatsappRecipients('(11) 99999-8888')).toEqual([
|
|
44
|
+
{ to: '5511999998888', targetType: 'phone' },
|
|
45
|
+
]);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('não acrescenta DDI a quem já veio com um', () => {
|
|
49
|
+
expect(parseWhatsappRecipients('5511999998888')).toEqual([
|
|
50
|
+
{ to: '5511999998888', targetType: 'phone' },
|
|
51
|
+
]);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('separa por vírgula, ponto-e-vírgula e quebra de linha, nunca por espaço', () => {
|
|
55
|
+
// Espaço faz parte da máscara: quebrar ali produziria `+55`, `(11)` e
|
|
56
|
+
// `3333-4444`, todos descartados, e a lista sumiria em silêncio.
|
|
57
|
+
expect(
|
|
58
|
+
parseWhatsappRecipients(
|
|
59
|
+
'+55 (11) 3333-4444, 5511999998888;\n120363430651137530@g.us',
|
|
60
|
+
),
|
|
61
|
+
).toEqual([
|
|
62
|
+
{ to: '551133334444', targetType: 'phone' },
|
|
63
|
+
{ to: '5511999998888', targetType: 'phone' },
|
|
64
|
+
{ to: '120363430651137530@g.us', targetType: 'group' },
|
|
65
|
+
]);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('conta uma vez só o mesmo destino escrito de duas formas', () => {
|
|
69
|
+
expect(
|
|
70
|
+
parseWhatsappRecipients('(11) 99999-8888, 5511999998888'),
|
|
71
|
+
).toHaveLength(1);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('descarta o que não é telefone nem grupo', () => {
|
|
75
|
+
// Menos de 10 dígitos não tem DDD, e acima de 20 nem grupo é: a faixa entre
|
|
76
|
+
// 16 e 20 pertence ao id de grupo colado sem o sufixo `@g.us`.
|
|
77
|
+
expect(
|
|
78
|
+
parseWhatsappRecipients('123, abc, 123456789012345678901'),
|
|
79
|
+
).toEqual([]);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('16 a 20 dígitos é id de grupo, não telefone longo demais', () => {
|
|
83
|
+
// O descarte silencioso por "longo demais" é justamente o modo de falha que
|
|
84
|
+
// este canal combate: quem cola o id do painel da Evolution não recebe erro.
|
|
85
|
+
expect(parseWhatsappRecipients('1203634306511375')).toEqual([
|
|
86
|
+
{ to: '1203634306511375@g.us', targetType: 'group' },
|
|
87
|
+
]);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { WhatsappTargetType } from './whatsapp-sender.service';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Destino de um aviso interno por WhatsApp: celular ou grupo.
|
|
5
|
+
*
|
|
6
|
+
* Mora no core, e nao na library que avisa, porque ja sao duas a ler a mesma
|
|
7
|
+
* lista digitada a mao — o SAC, para o grupo do atendimento, e o commerce, para
|
|
8
|
+
* os grupos de venda. Reconhecedor de JID de grupo duplicado e como as duas
|
|
9
|
+
* divergem: uma passa a aceitar o id sem sufixo e a outra nao, e o destino some
|
|
10
|
+
* em silencio no modulo que ficou para tras.
|
|
11
|
+
*/
|
|
12
|
+
export type WhatsappRecipient = {
|
|
13
|
+
to: string;
|
|
14
|
+
targetType: WhatsappTargetType;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Id de grupo do WhatsApp (`...@g.us`), ou null quando o item nao e grupo.
|
|
19
|
+
*
|
|
20
|
+
* Grupo tem duas formas: a atual, so digitos (`120363...@g.us`), e a antiga,
|
|
21
|
+
* derivada de quem criou o grupo (`5511999998888-1564082267@g.us`). As duas
|
|
22
|
+
* saem do painel da Evolution ja com o sufixo.
|
|
23
|
+
*
|
|
24
|
+
* Sem sufixo tambem vale: um id colado do painel tem 18 digitos, e acima do teto
|
|
25
|
+
* do E.164 (15) nao existe telefone — a alternativa seria o descarte silencioso
|
|
26
|
+
* por "longo demais", que e o modo de falha que este canal combate. O corte em
|
|
27
|
+
* 20 digitos deixa de fora a colagem de dois telefones grudados, que nao e
|
|
28
|
+
* destino nenhum.
|
|
29
|
+
*/
|
|
30
|
+
export function toWhatsappGroupJid(value: string): string | null {
|
|
31
|
+
const match = /^(\d{5,}(?:-\d{5,})?)@g\.us$/i.exec(value);
|
|
32
|
+
if (match) return `${match[1]}@g.us`;
|
|
33
|
+
|
|
34
|
+
return /^\d{16,20}$/.test(value) ? `${value}@g.us` : null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Destinos de WhatsApp de uma lista digitada a mao: celulares e grupos.
|
|
39
|
+
*
|
|
40
|
+
* Nao da para reusar um `split` de e-mail: `WhatsappSenderService.normalizeTarget`
|
|
41
|
+
* apenas remove os nao-digitos e **nao acrescenta DDI**. Um `(11) 99999-8888`
|
|
42
|
+
* digitado no admin viraria `11999998888`, um numero que o provedor aceita e
|
|
43
|
+
* entrega em outro lugar — falha silenciosa, que e exatamente o que este canal
|
|
44
|
+
* existe para evitar. Por isso o DDI 55 e completado aqui quando o que sobrou
|
|
45
|
+
* tem cara de numero nacional (DDD + 8 ou 9 digitos).
|
|
46
|
+
*
|
|
47
|
+
* A separacao e so por virgula, ponto-e-virgula e quebra de linha — nao por
|
|
48
|
+
* espaco. Espaco faz parte da mascara de um telefone (`+55 (11) 3333-4444`), e
|
|
49
|
+
* quebrar ali produziria os fragmentos `+55`, `(11)` e `3333-4444`, todos
|
|
50
|
+
* descartados: a lista inteira sumiria em silencio.
|
|
51
|
+
*
|
|
52
|
+
* Menos de 10 digitos nao e telefone com DDD, e mais de 15 nao e telefone nenhum
|
|
53
|
+
* (o teto do E.164) — normalmente sao dois numeros que alguem separou so com
|
|
54
|
+
* espaco. Descartar e melhor que enviar para um numero inventado.
|
|
55
|
+
*/
|
|
56
|
+
export function parseWhatsappRecipients(
|
|
57
|
+
raw: string | null | undefined,
|
|
58
|
+
): WhatsappRecipient[] {
|
|
59
|
+
if (!raw) return [];
|
|
60
|
+
|
|
61
|
+
// Map, e nao Set: o mesmo destino escrito de duas formas continua contando uma
|
|
62
|
+
// vez so, e o tipo precisa vir junto do valor.
|
|
63
|
+
const seen = new Map<string, WhatsappRecipient>();
|
|
64
|
+
|
|
65
|
+
for (const item of raw.split(/[,;\n]+/)) {
|
|
66
|
+
const value = item.trim();
|
|
67
|
+
if (!value) continue;
|
|
68
|
+
|
|
69
|
+
const group = toWhatsappGroupJid(value);
|
|
70
|
+
|
|
71
|
+
if (group) {
|
|
72
|
+
seen.set(group, { to: group, targetType: 'group' });
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const digits = value.replace(/\D+/g, '');
|
|
77
|
+
if (digits.length < 10 || digits.length > 15) continue;
|
|
78
|
+
const phone = digits.length <= 11 ? `55${digits}` : digits;
|
|
79
|
+
seen.set(phone, { to: phone, targetType: 'phone' });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return [...seen.values()];
|
|
83
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import {
|
|
2
|
+
WhatsappSendError,
|
|
3
|
+
WhatsappSenderService,
|
|
4
|
+
} from './whatsapp-sender.service';
|
|
5
|
+
|
|
6
|
+
describe('WhatsappSenderService', () => {
|
|
7
|
+
const buildService = (providerSlug: string) => {
|
|
8
|
+
const prisma = {
|
|
9
|
+
integration_profile: {
|
|
10
|
+
findUnique: jest.fn().mockResolvedValue({
|
|
11
|
+
id: 7,
|
|
12
|
+
slug: 'whatsapp-api-hcode',
|
|
13
|
+
config: { access_token: 'token', phone_number_id: '109371452' },
|
|
14
|
+
integration_provider: { slug: providerSlug },
|
|
15
|
+
integration_type: { slug: 'whatsapp' },
|
|
16
|
+
}),
|
|
17
|
+
findFirst: jest.fn(),
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
return { prisma, service: new WhatsappSenderService(prisma as any) };
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
describe('normalizeTarget', () => {
|
|
25
|
+
it('preserva o sufixo do grupo, que um strip de digitos destruiria', () => {
|
|
26
|
+
const { service } = buildService('evolution-api');
|
|
27
|
+
|
|
28
|
+
expect(
|
|
29
|
+
service.normalizeTarget('120363430651137530@g.us', 'group'),
|
|
30
|
+
).toBe('120363430651137530@g.us');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('reduz o telefone a digitos', () => {
|
|
34
|
+
const { service } = buildService('evolution-api');
|
|
35
|
+
|
|
36
|
+
expect(service.normalizeTarget('+55 (11) 3333-4444')).toBe(
|
|
37
|
+
'551133334444',
|
|
38
|
+
);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// A Cloud API nao tem endpoint de grupo. Sem a guarda, o destino seria
|
|
43
|
+
// reduzido a digitos e a Graph API receberia o id do grupo como telefone.
|
|
44
|
+
describe('grupo no provider oficial', () => {
|
|
45
|
+
let fetchSpy: jest.SpyInstance;
|
|
46
|
+
|
|
47
|
+
beforeEach(() => {
|
|
48
|
+
fetchSpy = jest
|
|
49
|
+
.spyOn(global, 'fetch' as any)
|
|
50
|
+
.mockResolvedValue({ ok: true, json: async () => ({}) } as any);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
afterEach(() => fetchSpy.mockRestore());
|
|
54
|
+
|
|
55
|
+
it('recusa o envio sem chamar a Graph API', async () => {
|
|
56
|
+
const { service } = buildService('whatsapp-official');
|
|
57
|
+
|
|
58
|
+
await expect(
|
|
59
|
+
service.send({
|
|
60
|
+
profileSlug: 'whatsapp-api-hcode',
|
|
61
|
+
to: '120363430651137530@g.us',
|
|
62
|
+
targetType: 'group',
|
|
63
|
+
text: 'Novo chamado',
|
|
64
|
+
}),
|
|
65
|
+
).rejects.toThrow(/não envia mensagem para grupo/);
|
|
66
|
+
|
|
67
|
+
expect(fetchSpy).not.toHaveBeenCalled();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('marca a recusa como permanente, para o job nao insistir', async () => {
|
|
71
|
+
const { service } = buildService('whatsapp-official');
|
|
72
|
+
|
|
73
|
+
const error = await service
|
|
74
|
+
.send({
|
|
75
|
+
profileSlug: 'whatsapp-api-hcode',
|
|
76
|
+
to: '120363430651137530@g.us',
|
|
77
|
+
targetType: 'group',
|
|
78
|
+
text: 'Novo chamado',
|
|
79
|
+
})
|
|
80
|
+
.catch((thrown) => thrown);
|
|
81
|
+
|
|
82
|
+
expect(error).toBeInstanceOf(WhatsappSendError);
|
|
83
|
+
expect((error as WhatsappSendError).retryable).toBe(false);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('a Evolution continua entregando no grupo', async () => {
|
|
87
|
+
const { prisma, service } = buildService('evolution-api');
|
|
88
|
+
prisma.integration_profile.findUnique.mockResolvedValue({
|
|
89
|
+
id: 2,
|
|
90
|
+
slug: 'hcode',
|
|
91
|
+
config: {
|
|
92
|
+
host: 'https://evolution.hcode.com.br',
|
|
93
|
+
token: 'apikey',
|
|
94
|
+
instance_name: 'hcode',
|
|
95
|
+
},
|
|
96
|
+
integration_provider: { slug: 'evolution-api' },
|
|
97
|
+
integration_type: { slug: 'whatsapp' },
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
await service.send({
|
|
101
|
+
profileSlug: 'hcode',
|
|
102
|
+
to: '120363430651137530@g.us',
|
|
103
|
+
targetType: 'group',
|
|
104
|
+
text: 'Novo chamado',
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const [url, init] = fetchSpy.mock.calls[0];
|
|
108
|
+
expect(url).toBe('https://evolution.hcode.com.br/message/sendText/hcode');
|
|
109
|
+
expect(JSON.parse(init.body).number).toBe('120363430651137530@g.us');
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
});
|