@hed-hog/core 0.0.399 → 0.0.401
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai/ai-agent.sort.d.ts +6 -0
- package/dist/ai/ai-agent.sort.d.ts.map +1 -0
- package/dist/ai/ai-agent.sort.js +21 -0
- package/dist/ai/ai-agent.sort.js.map +1 -0
- package/dist/ai/ai.controller.d.ts +6 -4
- package/dist/ai/ai.controller.d.ts.map +1 -1
- package/dist/ai/ai.service.d.ts +121 -6
- package/dist/ai/ai.service.d.ts.map +1 -1
- package/dist/ai/ai.service.js +355 -160
- package/dist/ai/ai.service.js.map +1 -1
- package/dist/ai-instruction/ai-instruction.service.d.ts.map +1 -1
- package/dist/ai-instruction/ai-instruction.service.js +26 -23
- package/dist/ai-instruction/ai-instruction.service.js.map +1 -1
- package/dist/ai-instruction/ai-instruction.sort.d.ts +16 -0
- package/dist/ai-instruction/ai-instruction.sort.d.ts.map +1 -0
- package/dist/ai-instruction/ai-instruction.sort.js +32 -0
- package/dist/ai-instruction/ai-instruction.sort.js.map +1 -0
- package/dist/ai-instruction/ai-prompt-resolver.service.d.ts +12 -0
- package/dist/ai-instruction/ai-prompt-resolver.service.d.ts.map +1 -1
- package/dist/ai-instruction/ai-prompt-resolver.service.js +26 -0
- package/dist/ai-instruction/ai-prompt-resolver.service.js.map +1 -1
- package/dist/auth/auth.controller.d.ts +24 -8
- package/dist/auth/auth.controller.d.ts.map +1 -1
- package/dist/auth/auth.module.d.ts.map +1 -1
- package/dist/auth/auth.module.js +8 -1
- package/dist/auth/auth.module.js.map +1 -1
- package/dist/auth/auth.service.d.ts +72 -25
- package/dist/auth/auth.service.d.ts.map +1 -1
- package/dist/auth/auth.service.js +41 -4
- package/dist/auth/auth.service.js.map +1 -1
- package/dist/auth/guards/auth.guard.d.ts +3 -1
- package/dist/auth/guards/auth.guard.d.ts.map +1 -1
- package/dist/auth/guards/auth.guard.js +12 -3
- package/dist/auth/guards/auth.guard.js.map +1 -1
- package/dist/authorize/authorize.controller.d.ts +29 -5
- package/dist/authorize/authorize.controller.d.ts.map +1 -1
- package/dist/authorize/authorize.controller.js +120 -13
- package/dist/authorize/authorize.controller.js.map +1 -1
- package/dist/authorize/authorize.errors.d.ts +34 -0
- package/dist/authorize/authorize.errors.d.ts.map +1 -0
- package/dist/authorize/authorize.errors.js +37 -0
- package/dist/authorize/authorize.errors.js.map +1 -0
- package/dist/authorize/authorize.module.d.ts.map +1 -1
- package/dist/authorize/authorize.module.js +6 -1
- package/dist/authorize/authorize.module.js.map +1 -1
- package/dist/authorize/authorize.service.d.ts +77 -3
- package/dist/authorize/authorize.service.d.ts.map +1 -1
- package/dist/authorize/authorize.service.js +212 -21
- package/dist/authorize/authorize.service.js.map +1 -1
- package/dist/authorize/dto/authorize-info.dto.d.ts +10 -1
- package/dist/authorize/dto/authorize-info.dto.d.ts.map +1 -1
- package/dist/authorize/dto/authorize-info.dto.js +16 -1
- package/dist/authorize/dto/authorize-info.dto.js.map +1 -1
- package/dist/authorize/dto/consent.dto.d.ts +10 -1
- package/dist/authorize/dto/consent.dto.d.ts.map +1 -1
- package/dist/authorize/dto/consent.dto.js +16 -1
- package/dist/authorize/dto/consent.dto.js.map +1 -1
- package/dist/authorize/dto/token.dto.d.ts +12 -0
- package/dist/authorize/dto/token.dto.d.ts.map +1 -1
- package/dist/authorize/dto/token.dto.js +21 -1
- package/dist/authorize/dto/token.dto.js.map +1 -1
- package/dist/authorize/oauth-exception.filter.d.ts +17 -0
- package/dist/authorize/oauth-exception.filter.d.ts.map +1 -0
- package/dist/authorize/oauth-exception.filter.js +54 -0
- package/dist/authorize/oauth-exception.filter.js.map +1 -0
- package/dist/authorize/oauth-metadata.service.d.ts +39 -0
- package/dist/authorize/oauth-metadata.service.d.ts.map +1 -0
- package/dist/authorize/oauth-metadata.service.js +81 -0
- package/dist/authorize/oauth-metadata.service.js.map +1 -0
- package/dist/authorize/well-known.controller.d.ts +67 -0
- package/dist/authorize/well-known.controller.d.ts.map +1 -0
- package/dist/authorize/well-known.controller.js +126 -0
- package/dist/authorize/well-known.controller.js.map +1 -0
- package/dist/captcha/captcha.service.d.ts +32 -2
- package/dist/captcha/captcha.service.d.ts.map +1 -1
- package/dist/captcha/captcha.service.js +85 -12
- package/dist/captcha/captcha.service.js.map +1 -1
- package/dist/challenge/challenge.service.d.ts +2 -2
- package/dist/core.module.d.ts.map +1 -1
- package/dist/core.module.js +9 -0
- package/dist/core.module.js.map +1 -1
- package/dist/dashboard/dashboard/dashboard.controller.d.ts +9 -9
- package/dist/dashboard/dashboard/dashboard.service.d.ts +9 -9
- package/dist/dashboard/dashboard-component/dashboard-component.controller.d.ts +3 -3
- package/dist/dashboard/dashboard-component/dashboard-component.service.d.ts +3 -3
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.controller.d.ts +3 -3
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.d.ts +3 -3
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.d.ts.map +1 -1
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.js +2 -4
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.js.map +1 -1
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.sort.d.ts +8 -0
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.sort.d.ts.map +1 -0
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.sort.js +18 -0
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.sort.js.map +1 -0
- package/dist/dashboard/dashboard-item/dashboard-item.service.d.ts.map +1 -1
- package/dist/dashboard/dashboard-item/dashboard-item.service.js +2 -4
- package/dist/dashboard/dashboard-item/dashboard-item.service.js.map +1 -1
- package/dist/dashboard/dashboard-item/dashboard-item.sort.d.ts +9 -0
- package/dist/dashboard/dashboard-item/dashboard-item.sort.d.ts.map +1 -0
- package/dist/dashboard/dashboard-item/dashboard-item.sort.js +19 -0
- package/dist/dashboard/dashboard-item/dashboard-item.sort.js.map +1 -0
- package/dist/dashboard/dashboard-role/dashboard-role.controller.d.ts +3 -3
- package/dist/dashboard/dashboard-role/dashboard-role.service.d.ts +3 -3
- package/dist/dashboard/dashboard-role/dashboard-role.service.d.ts.map +1 -1
- package/dist/dashboard/dashboard-role/dashboard-role.service.js +2 -4
- package/dist/dashboard/dashboard-role/dashboard-role.service.js.map +1 -1
- package/dist/dashboard/dashboard-role/dashboard-role.sort.d.ts +8 -0
- package/dist/dashboard/dashboard-role/dashboard-role.sort.d.ts.map +1 -0
- package/dist/dashboard/dashboard-role/dashboard-role.sort.js +16 -0
- package/dist/dashboard/dashboard-role/dashboard-role.sort.js.map +1 -0
- package/dist/document-kit/document-kit.module.d.ts +3 -0
- package/dist/document-kit/document-kit.module.d.ts.map +1 -0
- package/dist/document-kit/document-kit.module.js +24 -0
- package/dist/document-kit/document-kit.module.js.map +1 -0
- package/dist/document-kit/document-kit.registry.d.ts +55 -0
- package/dist/document-kit/document-kit.registry.d.ts.map +1 -0
- package/dist/document-kit/document-kit.registry.js +91 -0
- package/dist/document-kit/document-kit.registry.js.map +1 -0
- package/dist/document-kit/document-kit.service.d.ts +49 -0
- package/dist/document-kit/document-kit.service.d.ts.map +1 -0
- package/dist/document-kit/document-kit.service.js +135 -0
- package/dist/document-kit/document-kit.service.js.map +1 -0
- package/dist/file/file-upload-ticket.service.d.ts +160 -0
- package/dist/file/file-upload-ticket.service.d.ts.map +1 -0
- package/dist/file/file-upload-ticket.service.js +382 -0
- package/dist/file/file-upload-ticket.service.js.map +1 -0
- package/dist/file/file.controller.d.ts +24 -1
- package/dist/file/file.controller.d.ts.map +1 -1
- package/dist/file/file.controller.js +63 -2
- package/dist/file/file.controller.js.map +1 -1
- package/dist/file/file.module.d.ts.map +1 -1
- package/dist/file/file.module.js +14 -2
- package/dist/file/file.module.js.map +1 -1
- package/dist/file/file.service.d.ts +197 -7
- package/dist/file/file.service.d.ts.map +1 -1
- package/dist/file/file.service.js +438 -78
- package/dist/file/file.service.js.map +1 -1
- package/dist/file/filename-encoding.util.d.ts +24 -0
- package/dist/file/filename-encoding.util.d.ts.map +1 -0
- package/dist/file/filename-encoding.util.js +63 -0
- package/dist/file/filename-encoding.util.js.map +1 -0
- package/dist/file/image-optimization.service.d.ts +27 -0
- package/dist/file/image-optimization.service.d.ts.map +1 -1
- package/dist/file/image-optimization.service.js +63 -14
- package/dist/file/image-optimization.service.js.map +1 -1
- package/dist/file/mcp-file-source.schema.d.ts +33 -0
- package/dist/file/mcp-file-source.schema.d.ts.map +1 -0
- package/dist/file/mcp-file-source.schema.js +78 -0
- package/dist/file/mcp-file-source.schema.js.map +1 -0
- package/dist/file/mcp-file-source.service.d.ts +124 -0
- package/dist/file/mcp-file-source.service.d.ts.map +1 -0
- package/dist/file/mcp-file-source.service.js +262 -0
- package/dist/file/mcp-file-source.service.js.map +1 -0
- package/dist/file/mimetype-sniff.util.d.ts +62 -0
- package/dist/file/mimetype-sniff.util.d.ts.map +1 -0
- package/dist/file/mimetype-sniff.util.js +226 -0
- package/dist/file/mimetype-sniff.util.js.map +1 -0
- package/dist/file/remote-fetch.util.d.ts +35 -0
- package/dist/file/remote-fetch.util.d.ts.map +1 -0
- package/dist/file/remote-fetch.util.js +236 -0
- package/dist/file/remote-fetch.util.js.map +1 -0
- package/dist/impersonation/dto/start-impersonation.dto.d.ts +4 -1
- package/dist/impersonation/dto/start-impersonation.dto.d.ts.map +1 -1
- package/dist/impersonation/dto/start-impersonation.dto.js +1 -1
- package/dist/impersonation/dto/start-impersonation.dto.js.map +1 -1
- package/dist/impersonation/impersonation.service.d.ts +8 -3
- package/dist/impersonation/impersonation.service.d.ts.map +1 -1
- package/dist/impersonation/impersonation.service.js +14 -4
- package/dist/impersonation/impersonation.service.js.map +1 -1
- package/dist/incident/incident.controller.d.ts +2 -1
- package/dist/incident/incident.controller.d.ts.map +1 -1
- package/dist/incident/incident.controller.js +10 -6
- package/dist/incident/incident.controller.js.map +1 -1
- package/dist/incident/incident.service.d.ts +2 -0
- package/dist/incident/incident.service.d.ts.map +1 -1
- package/dist/incident/incident.service.js +4 -0
- package/dist/incident/incident.service.js.map +1 -1
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -1
- package/dist/integration-profile/integration-profile.secrets.d.ts.map +1 -1
- package/dist/integration-profile/integration-profile.secrets.js +28 -0
- package/dist/integration-profile/integration-profile.secrets.js.map +1 -1
- package/dist/integration-profile/integration-profile.service.d.ts +0 -13
- package/dist/integration-profile/integration-profile.service.d.ts.map +1 -1
- package/dist/integration-profile/integration-profile.service.js +5 -40
- package/dist/integration-profile/integration-profile.service.js.map +1 -1
- package/dist/integration-profile/integration-profile.utils.js +5 -5
- package/dist/integration-profile/integration-profile.utils.js.map +1 -1
- package/dist/integration-profile/testers/ai.testers.d.ts.map +1 -1
- package/dist/integration-profile/testers/ai.testers.js +46 -8
- package/dist/integration-profile/testers/ai.testers.js.map +1 -1
- package/dist/integration-profile/testers/index.d.ts.map +1 -1
- package/dist/integration-profile/testers/index.js +2 -0
- package/dist/integration-profile/testers/index.js.map +1 -1
- package/dist/integration-profile/testers/shipping.testers.d.ts +3 -0
- package/dist/integration-profile/testers/shipping.testers.d.ts.map +1 -0
- package/dist/integration-profile/testers/shipping.testers.js +71 -0
- package/dist/integration-profile/testers/shipping.testers.js.map +1 -0
- package/dist/locale/locale.controller.d.ts +1 -1
- package/dist/locale/locale.service.d.ts +1 -1
- package/dist/mail/dto/send.dto.d.ts +6 -1
- package/dist/mail/dto/send.dto.d.ts.map +1 -1
- package/dist/mail/dto/send.dto.js.map +1 -1
- package/dist/mail/mail.service.d.ts +9 -0
- package/dist/mail/mail.service.d.ts.map +1 -1
- package/dist/mail/mail.service.js +34 -14
- package/dist/mail/mail.service.js.map +1 -1
- package/dist/mail-sent/mail-sent.controller.d.ts +1 -1
- package/dist/mail-sent/mail-sent.service.d.ts +1 -1
- package/dist/mail-sent/mail-sent.service.d.ts.map +1 -1
- package/dist/mail-sent/mail-sent.service.js +2 -1
- package/dist/mail-sent/mail-sent.service.js.map +1 -1
- package/dist/mail-sent/mail-sent.sort.d.ts +9 -0
- package/dist/mail-sent/mail-sent.sort.d.ts.map +1 -0
- package/dist/mail-sent/mail-sent.sort.js +19 -0
- package/dist/mail-sent/mail-sent.sort.js.map +1 -0
- package/dist/mcp/filters/mcp-challenge.filter.d.ts +20 -0
- package/dist/mcp/filters/mcp-challenge.filter.d.ts.map +1 -0
- package/dist/mcp/filters/mcp-challenge.filter.js +69 -0
- package/dist/mcp/filters/mcp-challenge.filter.js.map +1 -0
- package/dist/mcp/guards/mcp-auth.guard.d.ts +4 -1
- package/dist/mcp/guards/mcp-auth.guard.d.ts.map +1 -1
- package/dist/mcp/guards/mcp-auth.guard.js +19 -5
- package/dist/mcp/guards/mcp-auth.guard.js.map +1 -1
- package/dist/mcp/guards/mcp-permission.guard.d.ts +25 -3
- package/dist/mcp/guards/mcp-permission.guard.d.ts.map +1 -1
- package/dist/mcp/guards/mcp-permission.guard.js +46 -26
- package/dist/mcp/guards/mcp-permission.guard.js.map +1 -1
- package/dist/mcp/mcp-audit.service.d.ts +17 -0
- package/dist/mcp/mcp-audit.service.d.ts.map +1 -1
- package/dist/mcp/mcp-audit.service.js +32 -3
- package/dist/mcp/mcp-audit.service.js.map +1 -1
- package/dist/mcp/mcp-permission.service.d.ts +64 -0
- package/dist/mcp/mcp-permission.service.d.ts.map +1 -0
- package/dist/mcp/mcp-permission.service.js +178 -0
- package/dist/mcp/mcp-permission.service.js.map +1 -0
- package/dist/mcp/mcp-server.service.d.ts +3 -1
- package/dist/mcp/mcp-server.service.d.ts.map +1 -1
- package/dist/mcp/mcp-server.service.js +36 -15
- package/dist/mcp/mcp-server.service.js.map +1 -1
- package/dist/mcp/mcp.controller.d.ts.map +1 -1
- package/dist/mcp/mcp.controller.js +10 -2
- package/dist/mcp/mcp.controller.js.map +1 -1
- package/dist/mcp/mcp.module.d.ts.map +1 -1
- package/dist/mcp/mcp.module.js +7 -1
- package/dist/mcp/mcp.module.js.map +1 -1
- package/dist/mcp/tools/core-access-log.mcp-tools.d.ts +17 -0
- package/dist/mcp/tools/core-access-log.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-access-log.mcp-tools.js +74 -0
- package/dist/mcp/tools/core-access-log.mcp-tools.js.map +1 -0
- package/dist/mcp/tools/core-ai.mcp-tools.js +2 -9
- package/dist/mcp/tools/core-ai.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-dashboard-items.mcp-tools.js +1 -1
- package/dist/mcp/tools/core-dashboard-items.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-dashboard.mcp-tools.js +2 -9
- package/dist/mcp/tools/core-dashboard.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-event-webhooks.mcp-tools.d.ts.map +1 -1
- package/dist/mcp/tools/core-event-webhooks.mcp-tools.js +2 -10
- package/dist/mcp/tools/core-event-webhooks.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-files.mcp-tools.d.ts +17 -5
- package/dist/mcp/tools/core-files.mcp-tools.d.ts.map +1 -1
- package/dist/mcp/tools/core-files.mcp-tools.js +186 -24
- package/dist/mcp/tools/core-files.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-integration-events.mcp-tools.js +2 -9
- package/dist/mcp/tools/core-integration-events.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-mail-sent.mcp-tools.js +2 -14
- package/dist/mcp/tools/core-mail-sent.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-mail.mcp-tools.js +2 -9
- package/dist/mcp/tools/core-mail.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-menus.mcp-tools.d.ts.map +1 -1
- package/dist/mcp/tools/core-menus.mcp-tools.js +2 -10
- package/dist/mcp/tools/core-menus.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-notifications.mcp-tools.d.ts.map +1 -1
- package/dist/mcp/tools/core-notifications.mcp-tools.js +0 -1
- package/dist/mcp/tools/core-notifications.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-roles.mcp-tools.d.ts.map +1 -1
- package/dist/mcp/tools/core-roles.mcp-tools.js +2 -13
- package/dist/mcp/tools/core-roles.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-routes.mcp-tools.d.ts.map +1 -1
- package/dist/mcp/tools/core-routes.mcp-tools.js +2 -11
- package/dist/mcp/tools/core-routes.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-screens.mcp-tools.d.ts.map +1 -1
- package/dist/mcp/tools/core-screens.mcp-tools.js +2 -11
- package/dist/mcp/tools/core-screens.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-sessions.mcp-tools.d.ts.map +1 -1
- package/dist/mcp/tools/core-sessions.mcp-tools.js +0 -2
- package/dist/mcp/tools/core-sessions.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-settings.mcp-tools.d.ts.map +1 -1
- package/dist/mcp/tools/core-settings.mcp-tools.js +2 -10
- package/dist/mcp/tools/core-settings.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-users.mcp-tools.js +2 -9
- package/dist/mcp/tools/core-users.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-webhooks.mcp-tools.d.ts.map +1 -1
- package/dist/mcp/tools/core-webhooks.mcp-tools.js +2 -10
- package/dist/mcp/tools/core-webhooks.mcp-tools.js.map +1 -1
- package/dist/mcp/types/mcp-context.type.d.ts +9 -0
- package/dist/mcp/types/mcp-context.type.d.ts.map +1 -1
- package/dist/mcp-chat/mcp-chat.service.d.ts.map +1 -1
- package/dist/mcp-chat/mcp-chat.service.js +8 -0
- package/dist/mcp-chat/mcp-chat.service.js.map +1 -1
- package/dist/meeting/meeting-provider.registry.d.ts +187 -0
- package/dist/meeting/meeting-provider.registry.d.ts.map +1 -1
- package/dist/meeting/meeting-provider.registry.js.map +1 -1
- package/dist/meeting/meeting.constants.d.ts +70 -0
- package/dist/meeting/meeting.constants.d.ts.map +1 -1
- package/dist/meeting/meeting.constants.js +9 -1
- package/dist/meeting/meeting.constants.js.map +1 -1
- package/dist/menu/menu.service.d.ts.map +1 -1
- package/dist/menu/menu.service.js +29 -9
- package/dist/menu/menu.service.js.map +1 -1
- package/dist/message-template/dto/update-message-template.dto.d.ts +18 -0
- package/dist/message-template/dto/update-message-template.dto.d.ts.map +1 -0
- package/dist/message-template/dto/update-message-template.dto.js +38 -0
- package/dist/message-template/dto/update-message-template.dto.js.map +1 -0
- package/dist/message-template/message-template-admin.service.d.ts +70 -0
- package/dist/message-template/message-template-admin.service.d.ts.map +1 -0
- package/dist/message-template/message-template-admin.service.js +226 -0
- package/dist/message-template/message-template-admin.service.js.map +1 -0
- package/dist/message-template/message-template.controller.d.ts +29 -0
- package/dist/message-template/message-template.controller.d.ts.map +1 -0
- package/dist/message-template/message-template.controller.js +101 -0
- package/dist/message-template/message-template.controller.js.map +1 -0
- package/dist/message-template/message-template.module.d.ts +10 -0
- package/dist/message-template/message-template.module.d.ts.map +1 -0
- package/dist/message-template/message-template.module.js +33 -0
- package/dist/message-template/message-template.module.js.map +1 -0
- package/dist/message-template/message-template.registry.d.ts +55 -0
- package/dist/message-template/message-template.registry.d.ts.map +1 -0
- package/dist/message-template/message-template.registry.js +64 -0
- package/dist/message-template/message-template.registry.js.map +1 -0
- package/dist/message-template/message-template.service.d.ts +59 -0
- package/dist/message-template/message-template.service.d.ts.map +1 -0
- package/dist/message-template/message-template.service.js +166 -0
- package/dist/message-template/message-template.service.js.map +1 -0
- package/dist/notification/notification.constants.d.ts +11 -0
- package/dist/notification/notification.constants.d.ts.map +1 -0
- package/dist/notification/notification.constants.js +14 -0
- package/dist/notification/notification.constants.js.map +1 -0
- package/dist/notification/notification.controller.d.ts +20 -20
- package/dist/notification/notification.module.d.ts.map +1 -1
- package/dist/notification/notification.module.js +3 -0
- package/dist/notification/notification.module.js.map +1 -1
- package/dist/notification/notification.service.d.ts +33 -21
- package/dist/notification/notification.service.d.ts.map +1 -1
- package/dist/notification/notification.service.js +36 -2
- package/dist/notification/notification.service.js.map +1 -1
- package/dist/oauth/oauth-token-broker.service.d.ts +50 -1
- package/dist/oauth/oauth-token-broker.service.d.ts.map +1 -1
- package/dist/oauth/oauth-token-broker.service.js +105 -3
- package/dist/oauth/oauth-token-broker.service.js.map +1 -1
- package/dist/oauth/oauth.controller.d.ts +4 -6
- package/dist/oauth/oauth.controller.d.ts.map +1 -1
- package/dist/oauth/oauth.controller.js +0 -1
- package/dist/oauth/oauth.controller.js.map +1 -1
- package/dist/oauth/oauth.errors.d.ts +15 -1
- package/dist/oauth/oauth.errors.d.ts.map +1 -1
- package/dist/oauth/oauth.errors.js +9 -1
- package/dist/oauth/oauth.errors.js.map +1 -1
- package/dist/oauth/oauth.module.d.ts.map +1 -1
- package/dist/oauth/oauth.module.js +6 -1
- package/dist/oauth/oauth.module.js.map +1 -1
- package/dist/oauth/oauth.service.d.ts +25 -5
- package/dist/oauth/oauth.service.d.ts.map +1 -1
- package/dist/oauth/oauth.service.js +63 -27
- package/dist/oauth/oauth.service.js.map +1 -1
- package/dist/oauth/providers/abstract.provider.d.ts +10 -0
- package/dist/oauth/providers/abstract.provider.d.ts.map +1 -1
- package/dist/oauth/providers/abstract.provider.js +39 -12
- package/dist/oauth/providers/abstract.provider.js.map +1 -1
- package/dist/oauth/providers/github.provider.d.ts +1 -1
- package/dist/oauth/providers/github.provider.d.ts.map +1 -1
- package/dist/oauth/providers/github.provider.js +48 -22
- package/dist/oauth/providers/github.provider.js.map +1 -1
- package/dist/oauth/providers/microsoft-entra-id.provider.d.ts +6 -0
- package/dist/oauth/providers/microsoft-entra-id.provider.d.ts.map +1 -1
- package/dist/oauth/providers/microsoft-entra-id.provider.js +36 -4
- package/dist/oauth/providers/microsoft-entra-id.provider.js.map +1 -1
- package/dist/observability/monitored-cron.d.ts +29 -12
- package/dist/observability/monitored-cron.d.ts.map +1 -1
- package/dist/observability/monitored-cron.js +131 -46
- package/dist/observability/monitored-cron.js.map +1 -1
- package/dist/pairing/pairing.service.d.ts +6 -2
- package/dist/pairing/pairing.service.d.ts.map +1 -1
- package/dist/pdf/document/charts/chart-bar.d.ts +24 -0
- package/dist/pdf/document/charts/chart-bar.d.ts.map +1 -0
- package/dist/pdf/document/charts/chart-bar.js +117 -0
- package/dist/pdf/document/charts/chart-bar.js.map +1 -0
- package/dist/pdf/document/charts/chart-donut.d.ts +16 -0
- package/dist/pdf/document/charts/chart-donut.d.ts.map +1 -0
- package/dist/pdf/document/charts/chart-donut.js +89 -0
- package/dist/pdf/document/charts/chart-donut.js.map +1 -0
- package/dist/pdf/document/charts/chart-gauge.d.ts +15 -0
- package/dist/pdf/document/charts/chart-gauge.d.ts.map +1 -0
- package/dist/pdf/document/charts/chart-gauge.js +60 -0
- package/dist/pdf/document/charts/chart-gauge.js.map +1 -0
- package/dist/pdf/document/charts/chart-line.d.ts +29 -0
- package/dist/pdf/document/charts/chart-line.d.ts.map +1 -0
- package/dist/pdf/document/charts/chart-line.js +89 -0
- package/dist/pdf/document/charts/chart-line.js.map +1 -0
- package/dist/pdf/document/charts/chart-primitives.d.ts +64 -0
- package/dist/pdf/document/charts/chart-primitives.d.ts.map +1 -0
- package/dist/pdf/document/charts/chart-primitives.js +110 -0
- package/dist/pdf/document/charts/chart-primitives.js.map +1 -0
- package/dist/pdf/document/charts/chart-radar.d.ts +13 -0
- package/dist/pdf/document/charts/chart-radar.d.ts.map +1 -0
- package/dist/pdf/document/charts/chart-radar.js +83 -0
- package/dist/pdf/document/charts/chart-radar.js.map +1 -0
- package/dist/pdf/document/charts/chart-scale.d.ts +39 -0
- package/dist/pdf/document/charts/chart-scale.d.ts.map +1 -0
- package/dist/pdf/document/charts/chart-scale.js +118 -0
- package/dist/pdf/document/charts/chart-scale.js.map +1 -0
- package/dist/pdf/document/charts/index.d.ts +8 -0
- package/dist/pdf/document/charts/index.d.ts.map +1 -0
- package/dist/pdf/document/charts/index.js +24 -0
- package/dist/pdf/document/charts/index.js.map +1 -0
- package/dist/pdf/document/index.d.ts +8 -0
- package/dist/pdf/document/index.d.ts.map +1 -0
- package/dist/pdf/document/index.js +24 -0
- package/dist/pdf/document/index.js.map +1 -0
- package/dist/pdf/document/pdf-document.chrome.d.ts +20 -0
- package/dist/pdf/document/pdf-document.chrome.d.ts.map +1 -0
- package/dist/pdf/document/pdf-document.chrome.js +36 -0
- package/dist/pdf/document/pdf-document.chrome.js.map +1 -0
- package/dist/pdf/document/pdf-document.css.d.ts +20 -0
- package/dist/pdf/document/pdf-document.css.d.ts.map +1 -0
- package/dist/pdf/document/pdf-document.css.js +239 -0
- package/dist/pdf/document/pdf-document.css.js.map +1 -0
- package/dist/pdf/document/pdf-document.renderer.d.ts +10 -0
- package/dist/pdf/document/pdf-document.renderer.d.ts.map +1 -0
- package/dist/pdf/document/pdf-document.renderer.js +153 -0
- package/dist/pdf/document/pdf-document.renderer.js.map +1 -0
- package/dist/pdf/document/pdf-document.service.d.ts +27 -0
- package/dist/pdf/document/pdf-document.service.d.ts.map +1 -0
- package/dist/pdf/document/pdf-document.service.js +81 -0
- package/dist/pdf/document/pdf-document.service.js.map +1 -0
- package/dist/pdf/document/pdf-document.theme.d.ts +41 -0
- package/dist/pdf/document/pdf-document.theme.d.ts.map +1 -0
- package/dist/pdf/document/pdf-document.theme.js +105 -0
- package/dist/pdf/document/pdf-document.theme.js.map +1 -0
- package/dist/pdf/document/pdf-document.types.d.ts +108 -0
- package/dist/pdf/document/pdf-document.types.d.ts.map +1 -0
- package/dist/pdf/document/pdf-document.types.js +18 -0
- package/dist/pdf/document/pdf-document.types.js.map +1 -0
- package/dist/pdf/pdf.module.d.ts.map +1 -1
- package/dist/pdf/pdf.module.js +3 -2
- package/dist/pdf/pdf.module.js.map +1 -1
- package/dist/pdf/pdf.service.d.ts +69 -1
- package/dist/pdf/pdf.service.d.ts.map +1 -1
- package/dist/pdf/pdf.service.js +164 -63
- package/dist/pdf/pdf.service.js.map +1 -1
- package/dist/pdf/templates/commercial-proposal.template.d.ts +96 -0
- package/dist/pdf/templates/commercial-proposal.template.d.ts.map +1 -0
- package/dist/pdf/templates/commercial-proposal.template.js +276 -0
- package/dist/pdf/templates/commercial-proposal.template.js.map +1 -0
- package/dist/profile/dto/change-password.dto.d.ts.map +1 -1
- package/dist/profile/dto/change-password.dto.js +4 -6
- package/dist/profile/dto/change-password.dto.js.map +1 -1
- package/dist/profile/profile.controller.d.ts +1 -1
- package/dist/profile/profile.service.d.ts +1 -1
- package/dist/profile/profile.service.d.ts.map +1 -1
- package/dist/profile/profile.service.js +9 -3
- package/dist/profile/profile.service.js.map +1 -1
- package/dist/realtime/realtime-tick.coalescer.d.ts +34 -0
- package/dist/realtime/realtime-tick.coalescer.d.ts.map +1 -0
- package/dist/realtime/realtime-tick.coalescer.js +60 -0
- package/dist/realtime/realtime-tick.coalescer.js.map +1 -0
- package/dist/realtime/realtime.service.d.ts +2 -0
- package/dist/realtime/realtime.service.d.ts.map +1 -1
- package/dist/realtime/realtime.service.js +22 -2
- package/dist/realtime/realtime.service.js.map +1 -1
- package/dist/redis/redis-kv.service.d.ts +90 -0
- package/dist/redis/redis-kv.service.d.ts.map +1 -0
- package/dist/redis/redis-kv.service.js +247 -0
- package/dist/redis/redis-kv.service.js.map +1 -0
- package/dist/redis/redis-master.d.ts +29 -0
- package/dist/redis/redis-master.d.ts.map +1 -0
- package/dist/redis/redis-master.js +81 -0
- package/dist/redis/redis-master.js.map +1 -0
- package/dist/redis/redis-pubsub.d.ts +12 -0
- package/dist/redis/redis-pubsub.d.ts.map +1 -1
- package/dist/redis/redis-pubsub.js +25 -2
- package/dist/redis/redis-pubsub.js.map +1 -1
- package/dist/redis/redis.module.d.ts +11 -0
- package/dist/redis/redis.module.d.ts.map +1 -0
- package/dist/redis/redis.module.js +30 -0
- package/dist/redis/redis.module.js.map +1 -0
- package/dist/session/session.controller.d.ts +6 -2
- package/dist/session/session.controller.d.ts.map +1 -1
- package/dist/session/session.service.d.ts +118 -15
- package/dist/session/session.service.d.ts.map +1 -1
- package/dist/session/session.service.js +251 -77
- package/dist/session/session.service.js.map +1 -1
- package/dist/setting/favicon.controller.d.ts +26 -0
- package/dist/setting/favicon.controller.d.ts.map +1 -0
- package/dist/setting/favicon.controller.js +130 -0
- package/dist/setting/favicon.controller.js.map +1 -0
- package/dist/setting/setting.controller.d.ts +5 -0
- package/dist/setting/setting.controller.d.ts.map +1 -1
- package/dist/setting/setting.module.d.ts.map +1 -1
- package/dist/setting/setting.module.js +2 -1
- package/dist/setting/setting.module.js.map +1 -1
- package/dist/setting/setting.service.d.ts +7 -0
- package/dist/setting/setting.service.d.ts.map +1 -1
- package/dist/setting/setting.service.js +38 -3
- package/dist/setting/setting.service.js.map +1 -1
- package/dist/task/task.service.d.ts +17 -0
- package/dist/task/task.service.d.ts.map +1 -1
- package/dist/task/task.service.js +48 -13
- package/dist/task/task.service.js.map +1 -1
- package/dist/token/token.service.d.ts +19 -1
- package/dist/token/token.service.d.ts.map +1 -1
- package/dist/token/token.service.js +39 -2
- package/dist/token/token.service.js.map +1 -1
- package/dist/user/user-credential.util.d.ts +10 -0
- package/dist/user/user-credential.util.d.ts.map +1 -1
- package/dist/user/user-credential.util.js +14 -1
- package/dist/user/user-credential.util.js.map +1 -1
- package/dist/user/user.service.d.ts +12 -4
- package/dist/user/user.service.d.ts.map +1 -1
- package/dist/user/user.service.js +2 -11
- package/dist/user/user.service.js.map +1 -1
- package/dist/user/user.sort.d.ts +16 -0
- package/dist/user/user.sort.d.ts.map +1 -0
- package/dist/user/user.sort.js +25 -0
- package/dist/user/user.sort.js.map +1 -0
- package/dist/utils/abbreviate-name.d.ts +13 -0
- package/dist/utils/abbreviate-name.d.ts.map +1 -0
- package/dist/utils/abbreviate-name.js +42 -0
- package/dist/utils/abbreviate-name.js.map +1 -0
- package/dist/utils/date-range-query.dto.d.ts +11 -0
- package/dist/utils/date-range-query.dto.d.ts.map +1 -0
- package/dist/utils/date-range-query.dto.js +63 -0
- package/dist/utils/date-range-query.dto.js.map +1 -0
- package/dist/utils/date-range.d.ts +96 -0
- package/dist/utils/date-range.d.ts.map +1 -0
- package/dist/utils/date-range.js +172 -0
- package/dist/utils/date-range.js.map +1 -0
- package/dist/webhook-integration/webhook-utils.d.ts.map +1 -1
- package/dist/webhook-integration/webhook-utils.js +24 -0
- package/dist/webhook-integration/webhook-utils.js.map +1 -1
- package/dist/whatsapp-sender/whatsapp-recipients.d.ts +49 -0
- package/dist/whatsapp-sender/whatsapp-recipients.d.ts.map +1 -0
- package/dist/whatsapp-sender/whatsapp-recipients.js +66 -0
- package/dist/whatsapp-sender/whatsapp-recipients.js.map +1 -0
- package/dist/whatsapp-sender/whatsapp-sender.service.d.ts +25 -0
- package/dist/whatsapp-sender/whatsapp-sender.service.d.ts.map +1 -1
- package/dist/whatsapp-sender/whatsapp-sender.service.js +71 -7
- package/dist/whatsapp-sender/whatsapp-sender.service.js.map +1 -1
- package/hedhog/data/integration_provider.yaml +9 -0
- package/hedhog/data/integration_type.yaml +12 -0
- package/hedhog/data/locale.yaml +0 -1
- package/hedhog/data/mail.yaml +2 -2
- package/hedhog/data/mail_layout.yaml +6 -3
- package/hedhog/data/menu.yaml +25 -5
- package/hedhog/data/route.yaml +148 -0
- package/hedhog/data/setting_group.yaml +189 -3
- package/hedhog/frontend/app/access-log/page.tsx.ejs +47 -53
- package/hedhog/frontend/app/account/components/active-sessions.tsx.ejs +33 -8
- package/hedhog/frontend/app/account/components/mcp-tokens.tsx.ejs +54 -13
- package/hedhog/frontend/app/account/components/mfa-method-card.tsx.ejs +7 -1
- package/hedhog/frontend/app/account/components/recovery-codes-dialog.tsx.ejs +1 -0
- package/hedhog/frontend/app/account/components/tokens.tsx.ejs +47 -17
- package/hedhog/frontend/app/ai_agent/page.tsx.ejs +46 -5
- package/hedhog/frontend/app/ai_instruction/_components/ai-instruction-form.tsx.ejs +101 -30
- package/hedhog/frontend/app/ai_instruction/_components/ai-instruction-versions.tsx.ejs +24 -8
- package/hedhog/frontend/app/ai_instruction/page.tsx.ejs +80 -15
- package/hedhog/frontend/app/configurations/[slug]/components/image-optimization-action.tsx.ejs +166 -0
- package/hedhog/frontend/app/configurations/[slug]/components/setting-field.test.tsx.ejs +145 -45
- package/hedhog/frontend/app/configurations/[slug]/components/setting-field.tsx.ejs +99 -219
- package/hedhog/frontend/app/configurations/[slug]/page.tsx.ejs +19 -1
- package/hedhog/frontend/app/configurations/layout.tsx.ejs +19 -5
- package/hedhog/frontend/app/dashboard/[slug]/widget-renderer.tsx.ejs +2 -8
- package/hedhog/frontend/app/dashboard/components/dynamic-widget.tsx.ejs +3 -0
- package/hedhog/frontend/app/dashboard/components/widget-wrapper.tsx.ejs +4 -0
- package/hedhog/frontend/app/dashboard/dashboard-home-tabs.tsx.ejs +43 -44
- package/hedhog/frontend/app/dashboard/management/tabs/component-roles-tab.tsx.ejs +41 -3
- package/hedhog/frontend/app/dashboard/management/tabs/components-tab.tsx.ejs +42 -2
- package/hedhog/frontend/app/dashboard/management/tabs/dashboard-roles-tab.tsx.ejs +41 -3
- package/hedhog/frontend/app/dashboard/management/tabs/dashboards-tab.tsx.ejs +39 -4
- package/hedhog/frontend/app/dashboard/management/tabs/items-tab.tsx.ejs +42 -3
- package/hedhog/frontend/app/incidents/page.tsx.ejs +60 -31
- package/hedhog/frontend/app/integration/profiles/page.tsx.ejs +45 -5
- package/hedhog/frontend/app/integrations-webhooks/page.tsx.ejs +92 -15
- package/hedhog/frontend/app/mail/log/page.tsx.ejs +109 -61
- package/hedhog/frontend/app/mail/template/page.tsx.ejs +125 -23
- package/hedhog/frontend/app/mcp_chat/chat-components.tsx.ejs +13 -15
- package/hedhog/frontend/app/mcp_chat/page.tsx.ejs +116 -110
- package/hedhog/frontend/app/message-templates/_components/message-template-sheet.tsx.ejs +245 -0
- package/hedhog/frontend/app/message-templates/_lib/types.ts.ejs +24 -0
- package/hedhog/frontend/app/message-templates/page.tsx.ejs +182 -0
- package/hedhog/frontend/app/pages/_components/page-wizard-sheet.tsx.ejs +1 -0
- package/hedhog/frontend/app/pages/page.tsx.ejs +94 -9
- package/hedhog/frontend/app/roles/menus.tsx.ejs +4 -4
- package/hedhog/frontend/app/roles/routes.tsx.ejs +4 -4
- package/hedhog/frontend/app/roles/users.tsx.ejs +4 -4
- package/hedhog/frontend/app/setting-list/page.tsx.ejs +71 -9
- package/hedhog/frontend/app/users/page.tsx.ejs +77 -67
- package/hedhog/frontend/messages/en.json +237 -37
- package/hedhog/frontend/messages/pt.json +237 -37
- package/hedhog/table/cron_lock.yaml +17 -0
- package/hedhog/table/file.yaml +13 -1
- package/hedhog/table/file_upload_chunk.yaml +23 -0
- package/hedhog/table/file_upload_ticket.yaml +67 -0
- package/hedhog/table/mcp_tool_call_log.yaml +30 -22
- package/hedhog/table/message_template.yaml +33 -0
- package/hedhog/table/setting.yaml +8 -0
- package/hedhog/table/user_session.yaml +36 -1
- package/package.json +5 -3
- package/src/ai/ai-agent.sort.ts +21 -0
- package/src/ai/ai-attachment.fragments.spec.ts +56 -3
- package/src/ai/ai.service.responses.spec.ts +429 -0
- package/src/ai/ai.service.spec.ts +173 -1
- package/src/ai/ai.service.ts +473 -220
- package/src/ai-instruction/ai-instruction.service.ts +32 -28
- package/src/ai-instruction/ai-instruction.sort.ts +32 -0
- package/src/ai-instruction/ai-prompt-resolver.service.spec.ts +44 -0
- package/src/ai-instruction/ai-prompt-resolver.service.ts +29 -0
- package/src/auth/auth.module.ts +8 -1
- package/src/auth/auth.service.spec.ts +108 -1
- package/src/auth/auth.service.ts +59 -5
- package/src/auth/guards/auth.guard.ts +9 -1
- package/src/authorize/authorize.controller.ts +126 -11
- package/src/authorize/authorize.errors.ts +55 -0
- package/src/authorize/authorize.module.ts +6 -1
- package/src/authorize/authorize.service.spec.ts +379 -7
- package/src/authorize/authorize.service.ts +309 -23
- package/src/authorize/dto/authorize-info.dto.ts +21 -3
- package/src/authorize/dto/consent.dto.ts +21 -3
- package/src/authorize/dto/token.dto.ts +24 -2
- package/src/authorize/oauth-exception.filter.ts +58 -0
- package/src/authorize/oauth-metadata.service.ts +67 -0
- package/src/authorize/well-known.controller.spec.ts +84 -0
- package/src/authorize/well-known.controller.ts +96 -0
- package/src/captcha/captcha.service.spec.ts +211 -4
- package/src/captcha/captcha.service.ts +127 -10
- package/src/core.module.ts +9 -0
- package/src/dashboard/dashboard-component-role/dashboard-component-role.service.ts +2 -3
- package/src/dashboard/dashboard-component-role/dashboard-component-role.sort.ts +18 -0
- package/src/dashboard/dashboard-item/dashboard-item.service.ts +2 -3
- package/src/dashboard/dashboard-item/dashboard-item.sort.ts +19 -0
- package/src/dashboard/dashboard-role/dashboard-role.service.ts +2 -3
- package/src/dashboard/dashboard-role/dashboard-role.sort.ts +16 -0
- package/src/document-kit/document-kit.module.ts +12 -0
- package/src/document-kit/document-kit.registry.ts +125 -0
- package/src/document-kit/document-kit.service.spec.ts +156 -0
- package/src/document-kit/document-kit.service.ts +173 -0
- package/src/file/file-upload-ticket.service.spec.ts +422 -0
- package/src/file/file-upload-ticket.service.ts +513 -0
- package/src/file/file.controller.ts +65 -0
- package/src/file/file.module.ts +14 -2
- package/src/file/file.service.spec.ts +262 -1
- package/src/file/file.service.ts +505 -87
- package/src/file/filename-encoding.util.spec.ts +54 -0
- package/src/file/filename-encoding.util.ts +67 -0
- package/src/file/image-optimization.service.spec.ts +156 -19
- package/src/file/image-optimization.service.ts +106 -21
- package/src/file/mcp-file-source.schema.ts +86 -0
- package/src/file/mcp-file-source.service.spec.ts +473 -0
- package/src/file/mcp-file-source.service.ts +445 -0
- package/src/file/mimetype-sniff.util.spec.ts +126 -0
- package/src/file/mimetype-sniff.util.ts +265 -0
- package/src/file/remote-fetch.util.spec.ts +117 -0
- package/src/file/remote-fetch.util.ts +330 -0
- package/src/impersonation/dto/start-impersonation.dto.ts +5 -2
- package/src/impersonation/impersonation.service.spec.ts +24 -1
- package/src/impersonation/impersonation.service.ts +14 -4
- package/src/incident/incident.controller.ts +9 -1
- package/src/incident/incident.service.ts +7 -0
- package/src/index.ts +23 -1
- package/src/integration-profile/integration-profile.secrets.ts +28 -0
- package/src/integration-profile/integration-profile.service.ts +7 -64
- package/src/integration-profile/integration-profile.utils.spec.ts +9 -0
- package/src/integration-profile/integration-profile.utils.ts +5 -5
- package/src/integration-profile/testers/ai.testers.ts +70 -5
- package/src/integration-profile/testers/index.ts +2 -0
- package/src/integration-profile/testers/remaining.testers.spec.ts +144 -1
- package/src/integration-profile/testers/shipping.testers.ts +94 -0
- package/src/integration-profile/testers/tester-coverage.spec.ts +10 -0
- package/src/mail/dto/send.dto.ts +6 -1
- package/src/mail/mail-template.spec.ts +58 -1
- package/src/mail/mail.service.spec.ts +112 -0
- package/src/mail/mail.service.ts +42 -4
- package/src/mail-sent/mail-sent.service.ts +3 -0
- package/src/mail-sent/mail-sent.sort.ts +19 -0
- package/src/mcp/filters/mcp-challenge.filter.spec.ts +76 -0
- package/src/mcp/filters/mcp-challenge.filter.ts +70 -0
- package/src/mcp/guards/mcp-auth.guard.spec.ts +51 -2
- package/src/mcp/guards/mcp-auth.guard.ts +24 -1
- package/src/mcp/guards/mcp-permission.guard.spec.ts +109 -15
- package/src/mcp/guards/mcp-permission.guard.ts +76 -51
- package/src/mcp/mcp-audit.service.spec.ts +107 -0
- package/src/mcp/mcp-audit.service.ts +37 -1
- package/src/mcp/mcp-permission.service.spec.ts +197 -0
- package/src/mcp/mcp-permission.service.ts +184 -0
- package/src/mcp/mcp-server.service.spec.ts +84 -2
- package/src/mcp/mcp-server.service.ts +111 -87
- package/src/mcp/mcp.controller.ts +8 -1
- package/src/mcp/mcp.module.ts +7 -1
- package/src/mcp/tools/core-access-log.mcp-tools.ts +66 -0
- package/src/mcp/tools/core-ai.mcp-tools.ts +1 -1
- package/src/mcp/tools/core-dashboard-items.mcp-tools.ts +1 -1
- package/src/mcp/tools/core-dashboard.mcp-tools.ts +1 -1
- package/src/mcp/tools/core-event-webhooks.mcp-tools.ts +1 -2
- package/src/mcp/tools/core-files.mcp-tools.ts +202 -22
- package/src/mcp/tools/core-integration-events.mcp-tools.ts +1 -1
- package/src/mcp/tools/core-mail-sent.mcp-tools.ts +1 -1
- package/src/mcp/tools/core-mail.mcp-tools.ts +1 -1
- package/src/mcp/tools/core-menus.mcp-tools.ts +1 -2
- package/src/mcp/tools/core-notifications.mcp-tools.ts +0 -1
- package/src/mcp/tools/core-roles.mcp-tools.ts +1 -5
- package/src/mcp/tools/core-routes.mcp-tools.ts +1 -3
- package/src/mcp/tools/core-screens.mcp-tools.ts +1 -3
- package/src/mcp/tools/core-sessions.mcp-tools.ts +0 -2
- package/src/mcp/tools/core-settings.mcp-tools.ts +1 -2
- package/src/mcp/tools/core-users.mcp-tools.ts +1 -1
- package/src/mcp/tools/core-webhooks.mcp-tools.ts +1 -2
- package/src/mcp/types/mcp-context.type.ts +15 -6
- package/src/mcp-chat/mcp-chat.service.ts +8 -0
- package/src/meeting/meeting-provider.registry.ts +201 -0
- package/src/meeting/meeting.constants.ts +74 -0
- package/src/menu/menu.service.spec.ts +89 -0
- package/src/menu/menu.service.ts +29 -11
- package/src/message-template/dto/update-message-template.dto.ts +22 -0
- package/src/message-template/message-template-admin.service.spec.ts +163 -0
- package/src/message-template/message-template-admin.service.ts +239 -0
- package/src/message-template/message-template.controller.ts +52 -0
- package/src/message-template/message-template.module.ts +21 -0
- package/src/message-template/message-template.registry.spec.ts +62 -0
- package/src/message-template/message-template.registry.ts +125 -0
- package/src/message-template/message-template.service.spec.ts +149 -0
- package/src/message-template/message-template.service.ts +149 -0
- package/src/notification/notification.constants.ts +10 -0
- package/src/notification/notification.module.ts +3 -0
- package/src/notification/notification.service.spec.ts +75 -1
- package/src/notification/notification.service.ts +39 -1
- package/src/oauth/oauth-token-broker.service.spec.ts +108 -0
- package/src/oauth/oauth-token-broker.service.ts +129 -2
- package/src/oauth/oauth.controller.ts +0 -1
- package/src/oauth/oauth.errors.ts +7 -0
- package/src/oauth/oauth.module.ts +6 -1
- package/src/oauth/oauth.service.spec.ts +58 -7
- package/src/oauth/oauth.service.ts +71 -32
- package/src/oauth/providers/abstract.provider.spec.ts +192 -0
- package/src/oauth/providers/abstract.provider.ts +46 -14
- package/src/oauth/providers/github.provider.ts +57 -34
- package/src/oauth/providers/microsoft-entra-id.provider.spec.ts +133 -0
- package/src/oauth/providers/microsoft-entra-id.provider.ts +37 -3
- package/src/observability/monitored-cron.spec.ts +164 -31
- package/src/observability/monitored-cron.ts +154 -62
- package/src/pdf/document/charts/chart-bar.ts +217 -0
- package/src/pdf/document/charts/chart-donut.ts +163 -0
- package/src/pdf/document/charts/chart-gauge.ts +94 -0
- package/src/pdf/document/charts/chart-line.ts +159 -0
- package/src/pdf/document/charts/chart-primitives.ts +188 -0
- package/src/pdf/document/charts/chart-radar.ts +136 -0
- package/src/pdf/document/charts/chart-scale.spec.ts +93 -0
- package/src/pdf/document/charts/chart-scale.ts +141 -0
- package/src/pdf/document/charts/charts.spec.ts +157 -0
- package/src/pdf/document/charts/index.ts +7 -0
- package/src/pdf/document/index.ts +7 -0
- package/src/pdf/document/pdf-document.chrome.ts +38 -0
- package/src/pdf/document/pdf-document.css.ts +238 -0
- package/src/pdf/document/pdf-document.renderer.spec.ts +129 -0
- package/src/pdf/document/pdf-document.renderer.ts +215 -0
- package/src/pdf/document/pdf-document.service.ts +77 -0
- package/src/pdf/document/pdf-document.theme.ts +146 -0
- package/src/pdf/document/pdf-document.types.ts +97 -0
- package/src/pdf/pdf.module.ts +3 -2
- package/src/pdf/pdf.service.spec.ts +131 -0
- package/src/pdf/pdf.service.ts +245 -82
- package/src/pdf/templates/commercial-proposal.template.spec.ts +177 -0
- package/src/pdf/templates/commercial-proposal.template.ts +400 -0
- package/src/profile/dto/change-password.dto.ts +25 -7
- package/src/profile/profile.service.spec.ts +97 -0
- package/src/profile/profile.service.ts +9 -3
- package/src/realtime/realtime-tick.coalescer.spec.ts +92 -0
- package/src/realtime/realtime-tick.coalescer.ts +60 -0
- package/src/realtime/realtime.service.spec.ts +33 -0
- package/src/realtime/realtime.service.ts +28 -2
- package/src/redis/redis-kv.service.ts +261 -0
- package/src/redis/redis-master.spec.ts +111 -0
- package/src/redis/redis-master.ts +86 -0
- package/src/redis/redis-pubsub.ts +27 -3
- package/src/redis/redis.module.ts +17 -0
- package/src/session/session.service.spec.ts +121 -42
- package/src/session/session.service.ts +279 -82
- package/src/setting/favicon.controller.spec.ts +92 -0
- package/src/setting/favicon.controller.ts +135 -0
- package/src/setting/setting.module.ts +2 -1
- package/src/setting/setting.service.ts +44 -3
- package/src/task/task.service.spec.ts +41 -0
- package/src/task/task.service.ts +53 -13
- package/src/token/token.service.spec.ts +90 -0
- package/src/token/token.service.ts +48 -2
- package/src/user/user-credential.util.ts +14 -5
- package/src/user/user.service.ts +2 -12
- package/src/user/user.sort.ts +25 -0
- package/src/utils/abbreviate-name.spec.ts +50 -0
- package/src/utils/abbreviate-name.ts +44 -0
- package/src/utils/date-range-query.dto.ts +56 -0
- package/src/utils/date-range.spec.ts +175 -0
- package/src/utils/date-range.ts +246 -0
- package/src/webhook-integration/webhook-utils.spec.ts +67 -0
- package/src/webhook-integration/webhook-utils.ts +26 -0
- package/src/whatsapp-sender/whatsapp-recipients.spec.ts +89 -0
- package/src/whatsapp-sender/whatsapp-recipients.ts +83 -0
- package/src/whatsapp-sender/whatsapp-sender.service.spec.ts +185 -0
- package/src/whatsapp-sender/whatsapp-sender.service.ts +90 -7
- package/hedhog/query/device_pairing_request.sql +0 -46
- package/hedhog/query/impersonation_grant.sql +0 -71
- package/hedhog/query/oauth_authorize_pkce.sql +0 -58
- package/hedhog/query/oauth_mobile_state_token.sql +0 -30
- package/hedhog/query/oauth_web_ref_token.sql +0 -25
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { PrismaService } from '@hed-hog/api-prisma';
|
|
2
|
+
import { Inject, Injectable, Logger, forwardRef } from '@nestjs/common';
|
|
3
|
+
import * as Handlebars from 'handlebars';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
MessageTemplateDefinition,
|
|
7
|
+
getMessageTemplateDefinition,
|
|
8
|
+
} from './message-template.registry';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Idioma em que a plataforma escreve para a própria equipe.
|
|
12
|
+
*
|
|
13
|
+
* O aviso interno não é mensagem ao cliente: quem lê é quem trabalha aqui, e o
|
|
14
|
+
* fato gerador pode ter chegado em qualquer idioma. Por isso o corpo sai no
|
|
15
|
+
* idioma do painel, e não no de quem comprou ou abriu o chamado.
|
|
16
|
+
*/
|
|
17
|
+
export const DEFAULT_MESSAGE_LOCALE = 'pt';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Renderiza o texto das mensagens que saem por canal que não é e-mail.
|
|
21
|
+
*
|
|
22
|
+
* O corpo é texto puro com o markdown do WhatsApp, e não HTML: não dá para
|
|
23
|
+
* reusar o `MailService`, cujo render troca quebra de linha por `<br/>` — o que
|
|
24
|
+
* no WhatsApp apareceria literal na tela.
|
|
25
|
+
*
|
|
26
|
+
* **Nunca devolve nulo para um slug registrado.** Linha ausente, `is_active`
|
|
27
|
+
* desligado, corpo em branco, sintaxe quebrada ou banco fora do ar caem no texto
|
|
28
|
+
* canônico do registry. Isso é o que permite ao chamador parar de manter um
|
|
29
|
+
* construtor de texto em código só como rede: a rede é o `fallback`.
|
|
30
|
+
*/
|
|
31
|
+
@Injectable()
|
|
32
|
+
export class MessageTemplateService {
|
|
33
|
+
private readonly logger = new Logger(MessageTemplateService.name);
|
|
34
|
+
|
|
35
|
+
constructor(
|
|
36
|
+
@Inject(forwardRef(() => PrismaService))
|
|
37
|
+
private readonly prisma: PrismaService,
|
|
38
|
+
) {}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* O texto pronto, ou `null` quando o slug não está registrado.
|
|
42
|
+
*
|
|
43
|
+
* Nulo aqui é erro de programação (slug digitado errado, library não
|
|
44
|
+
* carregada), não configuração — daí ser o único caso.
|
|
45
|
+
*/
|
|
46
|
+
async render(
|
|
47
|
+
slug: string,
|
|
48
|
+
variables: Record<string, unknown>,
|
|
49
|
+
locale = DEFAULT_MESSAGE_LOCALE,
|
|
50
|
+
): Promise<string | null> {
|
|
51
|
+
const definition = getMessageTemplateDefinition(slug);
|
|
52
|
+
|
|
53
|
+
if (!definition) {
|
|
54
|
+
this.logger.error(
|
|
55
|
+
`Modelo de mensagem "${slug}" não está registrado; nada a renderizar.`,
|
|
56
|
+
);
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const edited = await this.readEditedBody(slug, locale);
|
|
61
|
+
|
|
62
|
+
if (edited) {
|
|
63
|
+
const rendered = MessageTemplateService.compile(edited, variables);
|
|
64
|
+
if (rendered) return rendered;
|
|
65
|
+
|
|
66
|
+
// Texto editado que não compila ou renderiza vazio não pode calar o aviso.
|
|
67
|
+
this.logger.warn(
|
|
68
|
+
`O texto editado do modelo "${slug}" não pôde ser usado; caindo no padrão.`,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return MessageTemplateService.renderFallback(definition, variables, locale);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Renderiza o texto canônico do registry.
|
|
77
|
+
*
|
|
78
|
+
* Estático e público porque o teste que varre o registry inteiro o usa para
|
|
79
|
+
* afirmar que todo modelo registrado compila com as suas amostras — é ele que
|
|
80
|
+
* substitui a manutenção do texto em dois lugares.
|
|
81
|
+
*/
|
|
82
|
+
static renderFallback(
|
|
83
|
+
definition: MessageTemplateDefinition,
|
|
84
|
+
variables: Record<string, unknown>,
|
|
85
|
+
locale = DEFAULT_MESSAGE_LOCALE,
|
|
86
|
+
): string | null {
|
|
87
|
+
const body =
|
|
88
|
+
definition.fallback[locale] ?? definition.fallback[DEFAULT_MESSAGE_LOCALE];
|
|
89
|
+
|
|
90
|
+
return body ? MessageTemplateService.compile(body, variables) : null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Compila e renderiza, ou `null` em qualquer desfecho ruim.
|
|
95
|
+
*
|
|
96
|
+
* `noEscape` de propósito: escapar encheria o WhatsApp de `'`. Template
|
|
97
|
+
* preenchido só com blocos que não casaram renderiza vazio, e mandar mensagem
|
|
98
|
+
* em branco para o grupo é pior que mandar o texto padrão.
|
|
99
|
+
*/
|
|
100
|
+
private static compile(
|
|
101
|
+
body: string,
|
|
102
|
+
variables: Record<string, unknown>,
|
|
103
|
+
): string | null {
|
|
104
|
+
try {
|
|
105
|
+
const rendered = Handlebars.compile(body, { noEscape: true })(variables).trim();
|
|
106
|
+
return rendered || null;
|
|
107
|
+
} catch {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* O corpo editado no painel, ou `null` quando não há nada utilizável.
|
|
114
|
+
*
|
|
115
|
+
* Sem linha no idioma pedido, usa a primeira que existir: um texto escrito só
|
|
116
|
+
* em português continua valendo para quem abriu o painel em inglês — o
|
|
117
|
+
* contrário seria a equipe receber o padrão por causa do idioma da sessão.
|
|
118
|
+
*/
|
|
119
|
+
private async readEditedBody(
|
|
120
|
+
slug: string,
|
|
121
|
+
locale: string,
|
|
122
|
+
): Promise<string | null> {
|
|
123
|
+
try {
|
|
124
|
+
const template = await (this.prisma as any).message_template.findUnique({
|
|
125
|
+
where: { slug },
|
|
126
|
+
select: {
|
|
127
|
+
is_active: true,
|
|
128
|
+
message_template_locale: {
|
|
129
|
+
select: { body: true, locale: { select: { code: true } } },
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
if (!template?.is_active) return null;
|
|
135
|
+
|
|
136
|
+
const rows = template.message_template_locale ?? [];
|
|
137
|
+
const match = rows.find((row: any) => row.locale?.code === locale) ?? rows[0];
|
|
138
|
+
|
|
139
|
+
return String(match?.body ?? '').trim() || null;
|
|
140
|
+
} catch (error) {
|
|
141
|
+
this.logger.warn(
|
|
142
|
+
`Não foi possível ler o modelo "${slug}": ${
|
|
143
|
+
(error as Error)?.message ?? 'erro desconhecido'
|
|
144
|
+
}`,
|
|
145
|
+
);
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nome do evento de notificação no WebSocket (`/realtime`).
|
|
3
|
+
*
|
|
4
|
+
* Segue a convenção `dominio:evento` dos outros produtores (`presence:update`,
|
|
5
|
+
* `lms:waiting-room:tick`). O payload é o mesmo `NotificationStreamEvent` que o
|
|
6
|
+
* SSE já entrega — os dois transportes convivem por tempo indeterminado (o
|
|
7
|
+
* class-app publicado nas lojas só fala SSE), e transporte que carrega dado
|
|
8
|
+
* diferente faz o comportamento do cliente depender de qual dos dois está vivo.
|
|
9
|
+
*/
|
|
10
|
+
export const NOTIFICATION_REALTIME_EVENT = 'notification:event';
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { PaginationModule } from '@hed-hog/api-pagination';
|
|
2
2
|
import { PrismaModule } from '@hed-hog/api-prisma';
|
|
3
3
|
import { forwardRef, Module } from '@nestjs/common';
|
|
4
|
+
import { RealtimeModule } from '../realtime/realtime.module';
|
|
4
5
|
import { NotificationController } from './notification.controller';
|
|
5
6
|
import { NotificationService } from './notification.service';
|
|
6
7
|
|
|
@@ -8,6 +9,8 @@ import { NotificationService } from './notification.service';
|
|
|
8
9
|
imports: [
|
|
9
10
|
forwardRef(() => PrismaModule),
|
|
10
11
|
forwardRef(() => PaginationModule),
|
|
12
|
+
// O funil de eventos empurra a notificação pelo WebSocket além do SSE.
|
|
13
|
+
forwardRef(() => RealtimeModule),
|
|
11
14
|
],
|
|
12
15
|
controllers: [NotificationController],
|
|
13
16
|
providers: [NotificationService],
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
ForbiddenException,
|
|
5
5
|
NotFoundException,
|
|
6
6
|
} from '@nestjs/common';
|
|
7
|
+
import { NOTIFICATION_REALTIME_EVENT } from './notification.constants';
|
|
7
8
|
import { NotificationService } from './notification.service';
|
|
8
9
|
|
|
9
10
|
const makeDeps = () => {
|
|
@@ -22,7 +23,8 @@ const makeDeps = () => {
|
|
|
22
23
|
};
|
|
23
24
|
const pagination = { paginate: jest.fn().mockResolvedValue({ data: [] }) };
|
|
24
25
|
const eventEmitter = { emitAsync: jest.fn().mockResolvedValue([]) };
|
|
25
|
-
|
|
26
|
+
const realtime = { emitToUser: jest.fn() };
|
|
27
|
+
return { prisma, pagination, eventEmitter, realtime };
|
|
26
28
|
};
|
|
27
29
|
|
|
28
30
|
const makeService = (deps = makeDeps()) => ({
|
|
@@ -30,6 +32,7 @@ const makeService = (deps = makeDeps()) => ({
|
|
|
30
32
|
deps.prisma as any,
|
|
31
33
|
deps.pagination as any,
|
|
32
34
|
deps.eventEmitter as any,
|
|
35
|
+
deps.realtime as any,
|
|
33
36
|
),
|
|
34
37
|
deps,
|
|
35
38
|
});
|
|
@@ -240,6 +243,77 @@ describe('NotificationService', () => {
|
|
|
240
243
|
});
|
|
241
244
|
expect(received).toHaveLength(1);
|
|
242
245
|
});
|
|
246
|
+
|
|
247
|
+
// Os dois transportes convivem: o SSE (EventEmitter + pg_notify) ainda
|
|
248
|
+
// serve o class-app publicado, e o socket serve os apps web. Este teste
|
|
249
|
+
// trava a coexistência — derrubar um dos três é o jeito mais fácil de
|
|
250
|
+
// quebrar notificação em produção sem nenhum erro aparecer.
|
|
251
|
+
it('entrega nos três canais: local, socket e pg_notify', async () => {
|
|
252
|
+
const { service, deps } = makeService();
|
|
253
|
+
const received: any[] = [];
|
|
254
|
+
(service as any).notificationEvents.on('notification:7', (e: any) =>
|
|
255
|
+
received.push(e),
|
|
256
|
+
);
|
|
257
|
+
const event = { type: 'notification.created', notificationId: 3 };
|
|
258
|
+
|
|
259
|
+
await (service as any).broadcastNotificationEvent(7, event);
|
|
260
|
+
|
|
261
|
+
expect(received).toEqual([event]);
|
|
262
|
+
expect(deps.realtime.emitToUser).toHaveBeenCalledWith(
|
|
263
|
+
7,
|
|
264
|
+
NOTIFICATION_REALTIME_EVENT,
|
|
265
|
+
event,
|
|
266
|
+
);
|
|
267
|
+
expect(deps.prisma.$executeRaw).toHaveBeenCalled();
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('emit do socket falhando não impede o pg_notify', async () => {
|
|
271
|
+
const { service, deps } = makeService();
|
|
272
|
+
deps.realtime.emitToUser.mockImplementation(() => {
|
|
273
|
+
throw new Error('socket down');
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
await expect(
|
|
277
|
+
(service as any).broadcastNotificationEvent(7, {
|
|
278
|
+
type: 'notification.updated',
|
|
279
|
+
}),
|
|
280
|
+
).resolves.toBeUndefined();
|
|
281
|
+
expect(deps.prisma.$executeRaw).toHaveBeenCalled();
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
describe('notifyRewardGranted', () => {
|
|
286
|
+
it('emite sinal sem payload para o usuário', async () => {
|
|
287
|
+
const { service } = makeService();
|
|
288
|
+
const received: any[] = [];
|
|
289
|
+
(service as any).notificationEvents.on('notification:9', (e: any) =>
|
|
290
|
+
received.push(e),
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
await service.notifyRewardGranted(9);
|
|
294
|
+
|
|
295
|
+
// Só o tipo: a recompensa mora em `lms_student_reward`, e quem ouve refaz
|
|
296
|
+
// o GET da própria fila. Mandar o conteúdo aqui abriria caminho para
|
|
297
|
+
// comemorar duas vezes entre a web e o app.
|
|
298
|
+
expect(received).toEqual([{ type: 'reward.granted' }]);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
it('atravessa o cluster pelo pg_notify', async () => {
|
|
302
|
+
const { service, deps } = makeService();
|
|
303
|
+
|
|
304
|
+
await service.notifyRewardGranted(9);
|
|
305
|
+
|
|
306
|
+
// O desbloqueio acontece no worker de fila, um processo separado de quem
|
|
307
|
+
// segura a conexão SSE — sem esta publicação o aviso morria no pod errado.
|
|
308
|
+
expect(deps.prisma.$executeRaw).toHaveBeenCalled();
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
it('não derruba o chamador quando o broadcast falha', async () => {
|
|
312
|
+
const { service, deps } = makeService();
|
|
313
|
+
deps.prisma.$executeRaw.mockRejectedValue(new Error('down'));
|
|
314
|
+
|
|
315
|
+
await expect(service.notifyRewardGranted(9)).resolves.toBeUndefined();
|
|
316
|
+
});
|
|
243
317
|
});
|
|
244
318
|
|
|
245
319
|
describe('handleClusterNotification', () => {
|
|
@@ -16,8 +16,10 @@ import type { Request, Response } from 'express';
|
|
|
16
16
|
import { randomUUID } from 'node:crypto';
|
|
17
17
|
import { EventEmitter } from 'node:events';
|
|
18
18
|
import { Client, type Notification as PgNotification } from 'pg';
|
|
19
|
+
import { RealtimeService } from '../realtime/realtime.service';
|
|
19
20
|
import { CreateNotificationDto } from './dto/create-notification.dto';
|
|
20
21
|
import { UpdateProgressDto } from './dto/update-progress.dto';
|
|
22
|
+
import { NOTIFICATION_REALTIME_EVENT } from './notification.constants';
|
|
21
23
|
import {
|
|
22
24
|
NOTIFICATION_STALE_PROGRESS_CHECK,
|
|
23
25
|
NotificationStaleProgressCheckEvent,
|
|
@@ -26,7 +28,11 @@ import {
|
|
|
26
28
|
type NotificationStreamEventType =
|
|
27
29
|
| 'ready'
|
|
28
30
|
| 'notification.created'
|
|
29
|
-
| 'notification.updated'
|
|
31
|
+
| 'notification.updated'
|
|
32
|
+
// Sinal de "acorda e busca", sem relação com a lista de notificações: quem
|
|
33
|
+
// ouve refaz o GET da própria fila. Anda por aqui porque este é o único
|
|
34
|
+
// stream que os apps do aluno já mantêm aberto e que alcança todos os pods.
|
|
35
|
+
| 'reward.granted';
|
|
30
36
|
|
|
31
37
|
type NotificationStreamEvent = {
|
|
32
38
|
type: NotificationStreamEventType;
|
|
@@ -53,6 +59,8 @@ export class NotificationService implements OnModuleInit, OnModuleDestroy {
|
|
|
53
59
|
@Inject(forwardRef(() => PaginationService))
|
|
54
60
|
private readonly pagination: PaginationService,
|
|
55
61
|
private readonly eventEmitter: EventEmitter2,
|
|
62
|
+
@Inject(forwardRef(() => RealtimeService))
|
|
63
|
+
private readonly realtime: RealtimeService,
|
|
56
64
|
) {
|
|
57
65
|
this.notificationEvents.setMaxListeners(0);
|
|
58
66
|
}
|
|
@@ -122,6 +130,19 @@ export class NotificationService implements OnModuleInit, OnModuleDestroy {
|
|
|
122
130
|
await this.broadcastNotificationEvent(userId, { type: 'notification.updated' });
|
|
123
131
|
}
|
|
124
132
|
|
|
133
|
+
/**
|
|
134
|
+
* Avisa que o usuário ganhou uma recompensa de gamificação, para o cliente
|
|
135
|
+
* buscar a fila dele.
|
|
136
|
+
*
|
|
137
|
+
* Sem payload de propósito: a recompensa mora em `lms_student_reward` com o
|
|
138
|
+
* `seen_at` como fonte da verdade — mandar o conteúdo aqui abriria caminho
|
|
139
|
+
* para comemorar duas vezes entre a web e o app, e engordaria o `pg_notify`,
|
|
140
|
+
* cujo teto de 8000 bytes falha calado (ver `broadcastNotificationEvent`).
|
|
141
|
+
*/
|
|
142
|
+
async notifyRewardGranted(userId: number) {
|
|
143
|
+
await this.broadcastNotificationEvent(userId, { type: 'reward.granted' });
|
|
144
|
+
}
|
|
145
|
+
|
|
125
146
|
async unreadCount(userId: number) {
|
|
126
147
|
const count = await this.prisma.notification.count({
|
|
127
148
|
where: { user_id: userId, status: 'unread' },
|
|
@@ -330,6 +351,23 @@ export class NotificationService implements OnModuleInit, OnModuleDestroy {
|
|
|
330
351
|
) {
|
|
331
352
|
this.emitLocalNotificationEvent(userId, event);
|
|
332
353
|
|
|
354
|
+
// WebSocket, antes do `pg_notify` abaixo: o emit é síncrono e o
|
|
355
|
+
// `$executeRaw` é aguardado, então empurrar primeiro tira o socket da
|
|
356
|
+
// latência do Postgres. Vai por `emitToUser` — a sala `user:{id}` só é
|
|
357
|
+
// entrada com o `sub` do JWT verificado, e o `subscribe` do gateway monta
|
|
358
|
+
// salas com o prefixo literal `channel:`, então ela não é assinável de
|
|
359
|
+
// fora. É o que torna seguro mandar a notificação inteira aqui; por
|
|
360
|
+
// `emitToChannel` o título e o corpo vazariam para qualquer conta.
|
|
361
|
+
//
|
|
362
|
+
// O try/catch existe porque este é o primeiro call-site de `emitToUser` em
|
|
363
|
+
// produção: um throw derrubaria `create`, `markRead` e o resto do funil, e
|
|
364
|
+
// a entrega pelo SSE (acima) já aconteceu.
|
|
365
|
+
try {
|
|
366
|
+
this.realtime.emitToUser(userId, NOTIFICATION_REALTIME_EVENT, event);
|
|
367
|
+
} catch (error) {
|
|
368
|
+
this.logger.warn('Failed to push notification over realtime', error as any);
|
|
369
|
+
}
|
|
370
|
+
|
|
333
371
|
const payload: ClusterNotificationPayload = {
|
|
334
372
|
...event,
|
|
335
373
|
userId,
|
|
@@ -70,6 +70,32 @@ describe('OAuthTokenBrokerService', () => {
|
|
|
70
70
|
token_expires_at: new Date('2020-01-01T00:00:00Z'),
|
|
71
71
|
});
|
|
72
72
|
|
|
73
|
+
/**
|
|
74
|
+
* O Prisma 6 devolve coluna `Bytes` como `Uint8Array`, não como `Buffer`. Os
|
|
75
|
+
* mocks daqui usavam `Buffer`, então os testes passavam enquanto produção
|
|
76
|
+
* falhava em TODA conta: `Uint8Array.toString()` imprime os bytes em decimal
|
|
77
|
+
* ("115,97,...") em vez de decodificar utf8, o decrypt não achava as 4 partes
|
|
78
|
+
* e o usuário via "reconecte a conta" — que reconectar não resolvia.
|
|
79
|
+
*/
|
|
80
|
+
it('decrypts a Bytes column that chega como Uint8Array (Prisma 6)', async () => {
|
|
81
|
+
const encrypted = deps.security.encrypt('refresh-original');
|
|
82
|
+
deps.prisma.user_account.findFirst.mockResolvedValue({
|
|
83
|
+
id: 42,
|
|
84
|
+
refresh_token: new Uint8Array(Buffer.from(encrypted)),
|
|
85
|
+
scopes: 'openid,offline_access',
|
|
86
|
+
token_expires_at: new Date('2020-01-01T00:00:00Z'),
|
|
87
|
+
} as any);
|
|
88
|
+
deps.http.post.mockReturnValue(
|
|
89
|
+
of({ data: { access_token: 'ACCESS-1', expires_in: 3600 } }) as any,
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const result = await service.getAccessToken(1, PROVIDER);
|
|
93
|
+
|
|
94
|
+
expect(result.accessToken).toBe('ACCESS-1');
|
|
95
|
+
const [, body] = deps.http.post.mock.calls[0] as [string, string, unknown];
|
|
96
|
+
expect(body).toContain('refresh_token=refresh-original');
|
|
97
|
+
});
|
|
98
|
+
|
|
73
99
|
it('decrypts the stored refresh token, exchanges it, and returns an access token', async () => {
|
|
74
100
|
deps.prisma.user_account.findFirst.mockResolvedValue(
|
|
75
101
|
storedRefreshTokenRow(deps.security, 'refresh-original') as any,
|
|
@@ -161,6 +187,88 @@ describe('OAuthTokenBrokerService', () => {
|
|
|
161
187
|
});
|
|
162
188
|
});
|
|
163
189
|
|
|
190
|
+
/**
|
|
191
|
+
* A Microsoft rotaciona o refresh token a cada troca e invalida o anterior.
|
|
192
|
+
* Com várias réplicas disparando o mesmo tick de cron, a segunda a chegar
|
|
193
|
+
* recebe `invalid_grant` por uma corrida, não por revogação — e reportar
|
|
194
|
+
* "reconecte a conta" ali é enganoso: reconectar não resolve, a corrida se
|
|
195
|
+
* repete no ciclo seguinte.
|
|
196
|
+
*/
|
|
197
|
+
describe('rotação concorrente', () => {
|
|
198
|
+
it('refaz a troca com o token que outro processo gravou', async () => {
|
|
199
|
+
deps.prisma.user_account.findFirst
|
|
200
|
+
// leitura inicial: o token que vai falhar
|
|
201
|
+
.mockResolvedValueOnce(
|
|
202
|
+
storedRefreshTokenRow(deps.security, 'refresh-antigo') as any,
|
|
203
|
+
)
|
|
204
|
+
// releitura após o invalid_grant: outro processo já rotacionou
|
|
205
|
+
.mockResolvedValueOnce(
|
|
206
|
+
storedRefreshTokenRow(deps.security, 'refresh-novo') as any,
|
|
207
|
+
)
|
|
208
|
+
// a retentativa carrega a conta de novo
|
|
209
|
+
.mockResolvedValueOnce(
|
|
210
|
+
storedRefreshTokenRow(deps.security, 'refresh-novo') as any,
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
deps.http.post
|
|
214
|
+
.mockReturnValueOnce(
|
|
215
|
+
throwError(() => ({
|
|
216
|
+
response: { data: { error: 'invalid_grant' }, status: 400 },
|
|
217
|
+
})) as any,
|
|
218
|
+
)
|
|
219
|
+
.mockReturnValueOnce(
|
|
220
|
+
of({ data: { access_token: 'token-bom', expires_in: 3600 } }) as any,
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
const result = await service.getAccessToken(1, PROVIDER);
|
|
224
|
+
|
|
225
|
+
expect(result.accessToken).toBe('token-bom');
|
|
226
|
+
expect(deps.http.post).toHaveBeenCalledTimes(2);
|
|
227
|
+
// A segunda troca tem que usar o token NOVO, não repetir o que falhou.
|
|
228
|
+
const segundaChamada = String((deps.http.post.mock.calls[1] as any[])[1]);
|
|
229
|
+
expect(segundaChamada).toContain('refresh-novo');
|
|
230
|
+
expect(segundaChamada).not.toContain('refresh-antigo');
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it('ainda reporta revogação quando o token no banco não mudou', async () => {
|
|
234
|
+
// Sem essa distinção, uma conta de fato desconectada entraria em laço.
|
|
235
|
+
deps.prisma.user_account.findFirst.mockResolvedValue(
|
|
236
|
+
storedRefreshTokenRow(deps.security, 'refresh-original') as any,
|
|
237
|
+
);
|
|
238
|
+
deps.http.post.mockReturnValue(
|
|
239
|
+
throwError(() => ({
|
|
240
|
+
response: { data: { error: 'invalid_grant' }, status: 400 },
|
|
241
|
+
})) as any,
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
await expect(service.getAccessToken(1, PROVIDER)).rejects.toMatchObject({
|
|
245
|
+
reason: 'refresh_revoked',
|
|
246
|
+
});
|
|
247
|
+
// Uma tentativa só: não adianta insistir contra revogação real.
|
|
248
|
+
expect(deps.http.post).toHaveBeenCalledTimes(1);
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it('não persegue rotações indefinidamente', async () => {
|
|
252
|
+
// Cada releitura devolve um token diferente, simulando réplicas
|
|
253
|
+
// rotacionando sem parar. O teto tem que encerrar.
|
|
254
|
+
let n = 0;
|
|
255
|
+
deps.prisma.user_account.findFirst.mockImplementation(
|
|
256
|
+
async () =>
|
|
257
|
+
storedRefreshTokenRow(deps.security, `refresh-${n++}`) as any,
|
|
258
|
+
);
|
|
259
|
+
deps.http.post.mockReturnValue(
|
|
260
|
+
throwError(() => ({
|
|
261
|
+
response: { data: { error: 'invalid_grant' }, status: 400 },
|
|
262
|
+
})) as any,
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
await expect(service.getAccessToken(1, PROVIDER)).rejects.toMatchObject({
|
|
266
|
+
reason: 'refresh_revoked',
|
|
267
|
+
});
|
|
268
|
+
expect(deps.http.post.mock.calls.length).toBeLessThanOrEqual(3);
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
|
|
164
272
|
it('parses granted scopes from the stored account', async () => {
|
|
165
273
|
deps.prisma.user_account.findFirst.mockResolvedValue({
|
|
166
274
|
scopes: 'openid, Calendars.Read offline_access',
|
|
@@ -78,6 +78,16 @@ export class OAuthTokenBrokerService {
|
|
|
78
78
|
/** Refresh a bit early so an in-flight request never rides an about-to-expire token. */
|
|
79
79
|
private static readonly EXPIRY_SKEW_MS = 60 * 1000;
|
|
80
80
|
|
|
81
|
+
/**
|
|
82
|
+
* Quantas vezes seguir um refresh token que outro processo rotacionou.
|
|
83
|
+
*
|
|
84
|
+
* Duas bastam na prática: a corrida acontece quando várias réplicas disparam
|
|
85
|
+
* o mesmo tick de cron, e a segunda tentativa já parte do token que a
|
|
86
|
+
* vencedora gravou. O teto existe para o caso patológico de rotações
|
|
87
|
+
* encadeadas não virar laço.
|
|
88
|
+
*/
|
|
89
|
+
private static readonly MAX_ROTATION_RETRIES = 2;
|
|
90
|
+
|
|
81
91
|
constructor(
|
|
82
92
|
private readonly http: HttpService,
|
|
83
93
|
@Inject(forwardRef(() => SettingService))
|
|
@@ -169,6 +179,70 @@ export class OAuthTokenBrokerService {
|
|
|
169
179
|
return { tokenUrl, clientId, clientSecret };
|
|
170
180
|
}
|
|
171
181
|
|
|
182
|
+
/**
|
|
183
|
+
* Lê uma coluna `Bytes` que guarda texto cifrado.
|
|
184
|
+
*
|
|
185
|
+
* O Prisma 6 passou a devolver `Bytes` como `Uint8Array`, e não mais como
|
|
186
|
+
* `Buffer`. Os dois têm `.toString()`, mas com significados opostos: o do
|
|
187
|
+
* Buffer decodifica os bytes como utf8 ("salt:iv:tag:cifra"), o do
|
|
188
|
+
* Uint8Array imprime os bytes em decimal separados por vírgula
|
|
189
|
+
* ("115,97,108,116,..."). O `SecurityService.decrypt` então não achava as 4
|
|
190
|
+
* partes separadas por ":" e lançava — e como toda conta caía nesse mesmo
|
|
191
|
+
* erro, a mensagem final era "reconecte a conta", que nenhuma reconexão
|
|
192
|
+
* resolvia. `Buffer.from` normaliza os dois casos.
|
|
193
|
+
*/
|
|
194
|
+
private readEncryptedColumn(value: Uint8Array | Buffer): string {
|
|
195
|
+
return Buffer.from(value).toString();
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Resumo do erro do provedor para o log. Usa só `error` e `error_description`
|
|
200
|
+
* — campos que a Microsoft e o Google preenchem com código e explicação
|
|
201
|
+
* (AADSTS…), nunca com credencial. O corpo inteiro não vai para o log: a
|
|
202
|
+
* requisição que falhou carrega o client_secret e o refresh token.
|
|
203
|
+
*/
|
|
204
|
+
private describeGrantError(error: any): string {
|
|
205
|
+
const data = error?.response?.data;
|
|
206
|
+
const code = data?.error ?? 'unknown';
|
|
207
|
+
const description = String(data?.error_description ?? '')
|
|
208
|
+
.replace(/\s+/g, ' ')
|
|
209
|
+
.trim();
|
|
210
|
+
return description ? `${code}: ${description.slice(0, 300)}` : code;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Relê o refresh token do banco para ver se outro processo já o rotacionou.
|
|
215
|
+
*
|
|
216
|
+
* Devolve `true` só quando o valor guardado é DIFERENTE do que acabou de
|
|
217
|
+
* falhar — é isso que distingue uma corrida entre réplicas (vale tentar de
|
|
218
|
+
* novo, agora com o token novo) de uma revogação de verdade (não adianta
|
|
219
|
+
* insistir). Comparar os valores importa: sem essa checagem, uma conta
|
|
220
|
+
* realmente desconectada entraria em laço de tentativas.
|
|
221
|
+
*
|
|
222
|
+
* Nunca lança: qualquer problema aqui vira "não rotacionou", e quem chamou
|
|
223
|
+
* segue para o erro original, que é a informação correta a dar ao usuário.
|
|
224
|
+
*/
|
|
225
|
+
private async reloadRotatedRefreshToken(
|
|
226
|
+
userId: number,
|
|
227
|
+
provider: string,
|
|
228
|
+
usedRefreshToken: string,
|
|
229
|
+
): Promise<boolean> {
|
|
230
|
+
try {
|
|
231
|
+
const account = await this.prisma.user_account.findFirst({
|
|
232
|
+
where: { user_id: userId, provider: this.toAccountProvider(provider) },
|
|
233
|
+
select: { refresh_token: true },
|
|
234
|
+
});
|
|
235
|
+
if (!account?.refresh_token) return false;
|
|
236
|
+
|
|
237
|
+
const stored = this.security.decrypt(
|
|
238
|
+
this.readEncryptedColumn(account.refresh_token),
|
|
239
|
+
);
|
|
240
|
+
return Boolean(stored) && stored !== usedRefreshToken;
|
|
241
|
+
} catch {
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
172
246
|
private async loadUserAccount(userId: number, provider: string) {
|
|
173
247
|
const account = await this.prisma.user_account.findFirst({
|
|
174
248
|
where: {
|
|
@@ -232,6 +306,13 @@ export class OAuthTokenBrokerService {
|
|
|
232
306
|
async getAccessToken(
|
|
233
307
|
userId: number,
|
|
234
308
|
provider: string,
|
|
309
|
+
/**
|
|
310
|
+
* Profundidade da retentativa após uma rotação concorrente. Existe para a
|
|
311
|
+
* recursão ter fim: numa corrida entre muitas réplicas, cada uma pode
|
|
312
|
+
* rotacionar de novo entre a nossa falha e a releitura, e sem teto duas
|
|
313
|
+
* réplicas azaradas ficariam se revezando indefinidamente.
|
|
314
|
+
*/
|
|
315
|
+
attempt = 0,
|
|
235
316
|
): Promise<BrokeredAccessToken> {
|
|
236
317
|
const key = this.cacheKey(userId, provider);
|
|
237
318
|
const cached = this.cache.get(key);
|
|
@@ -249,8 +330,23 @@ export class OAuthTokenBrokerService {
|
|
|
249
330
|
|
|
250
331
|
let decryptedRefreshToken: string;
|
|
251
332
|
try {
|
|
252
|
-
decryptedRefreshToken = this.security.decrypt(
|
|
333
|
+
decryptedRefreshToken = this.security.decrypt(
|
|
334
|
+
this.readEncryptedColumn(account.refresh_token),
|
|
335
|
+
);
|
|
253
336
|
} catch (error) {
|
|
337
|
+
// Este caminho e o `invalid_grant` lá embaixo chegam ao usuário com a
|
|
338
|
+
// MESMA frase ("reconecte a conta"), e as causas não têm nada a ver uma
|
|
339
|
+
// com a outra: aqui o valor guardado não abre, lá é a Microsoft recusando
|
|
340
|
+
// o grant. Sem distinguir no log, não há como saber qual dos dois ocorreu.
|
|
341
|
+
// A mensagem do decrypt separa os dois motivos possíveis daqui — formato
|
|
342
|
+
// inválido (o valor lido não é o cifrado) e falha de autenticação (chave
|
|
343
|
+
// diferente da que cifrou) —, e é truncada porque ela ecoa o começo do
|
|
344
|
+
// valor que tentou decifrar.
|
|
345
|
+
this.logger.warn(
|
|
346
|
+
`Stored refresh token could not be decrypted: provider=${provider} userId=${userId} detail=${
|
|
347
|
+
error instanceof Error ? error.message.split('Value:')[0].trim() : 'unknown'
|
|
348
|
+
}`,
|
|
349
|
+
);
|
|
254
350
|
throw new OAuthTokenBrokerError(
|
|
255
351
|
'refresh_revoked',
|
|
256
352
|
`Failed to decrypt stored refresh token for "${provider}".`,
|
|
@@ -290,8 +386,39 @@ export class OAuthTokenBrokerService {
|
|
|
290
386
|
} catch (error: any) {
|
|
291
387
|
const providerError = error?.response?.data?.error;
|
|
292
388
|
if (providerError === 'invalid_grant') {
|
|
293
|
-
// Refresh token revoked/expired — the user must reconnect.
|
|
294
389
|
this.cache.delete(key);
|
|
390
|
+
|
|
391
|
+
// Antes de declarar a conta desconectada, checa se outro processo
|
|
392
|
+
// rotacionou o token enquanto este fazia a troca.
|
|
393
|
+
//
|
|
394
|
+
// A Microsoft emite um refresh token novo a cada troca e invalida o
|
|
395
|
+
// anterior, então basta duas réplicas usarem o mesmo token ao mesmo
|
|
396
|
+
// tempo para a segunda levar `invalid_grant` — e este serviço roda em
|
|
397
|
+
// seis. Sem esta releitura, uma corrida entre réplicas era reportada ao
|
|
398
|
+
// usuário como "reconecte a conta", e reconectar não resolvia nada,
|
|
399
|
+
// porque a corrida se repetia no ciclo seguinte.
|
|
400
|
+
const rotated =
|
|
401
|
+
attempt < OAuthTokenBrokerService.MAX_ROTATION_RETRIES &&
|
|
402
|
+
(await this.reloadRotatedRefreshToken(
|
|
403
|
+
userId,
|
|
404
|
+
provider,
|
|
405
|
+
decryptedRefreshToken,
|
|
406
|
+
));
|
|
407
|
+
if (rotated) {
|
|
408
|
+
this.logger.log(
|
|
409
|
+
`Refresh token was rotated by another process; retrying: provider=${provider} userId=${userId} attempt=${attempt + 1}`,
|
|
410
|
+
);
|
|
411
|
+
return this.getAccessToken(userId, provider, attempt + 1);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// Nada mudou no banco: o grant foi mesmo recusado. O `error_description`
|
|
415
|
+
// do provedor é a única coisa que diz POR QUÊ (na Microsoft, o código
|
|
416
|
+
// AADSTS: token expirado, consentimento faltando, acesso condicional).
|
|
417
|
+
// Sem ele no log, "reconecte a conta" é tudo que sobra para diagnosticar
|
|
418
|
+
// — e reconectar não resolve a maioria desses casos.
|
|
419
|
+
this.logger.warn(
|
|
420
|
+
`Refresh grant rejected: provider=${provider} userId=${userId} scopesSent="${scopeParam.join(' ')}" detail=${this.describeGrantError(error)}`,
|
|
421
|
+
);
|
|
295
422
|
throw new OAuthTokenBrokerError(
|
|
296
423
|
'refresh_revoked',
|
|
297
424
|
`Refresh token for "${provider}" is no longer valid. Reconnect the account.`,
|
|
@@ -11,6 +11,13 @@ export class OAuthProviderException extends Error {
|
|
|
11
11
|
public readonly status?: number,
|
|
12
12
|
public readonly providerError?: string,
|
|
13
13
|
public readonly providerErrorCode?: string,
|
|
14
|
+
/**
|
|
15
|
+
* Erro original do provider (tipicamente um AxiosError). Declarado como
|
|
16
|
+
* propriedade em vez de `super(message, { cause })` porque a library compila
|
|
17
|
+
* com `lib: es2021`, onde `ErrorOptions` ainda não existe — o que importa é a
|
|
18
|
+
* propriedade em runtime, que é o que o `linkedErrors` do Sentry encadeia.
|
|
19
|
+
*/
|
|
20
|
+
public readonly cause?: unknown,
|
|
14
21
|
) {
|
|
15
22
|
super(message);
|
|
16
23
|
this.name = 'OAuthProviderException';
|
|
@@ -23,7 +23,12 @@ import { MicrosoftProvider } from './providers/microsoft.provider';
|
|
|
23
23
|
|
|
24
24
|
@Module({
|
|
25
25
|
imports: [
|
|
26
|
-
|
|
26
|
+
// O default do axios é `timeout: 0` — espera indefinidamente. Como a troca do
|
|
27
|
+
// código roda dentro da transação de 60s do OAuthCallbackCoordinatorService,
|
|
28
|
+
// uma conexão pendurada com o provider prendia uma conexão do pool até a
|
|
29
|
+
// transação estourar. 10s é folgado para um endpoint de token e falha como
|
|
30
|
+
// ECONNABORTED, que o mapProviderError classifica.
|
|
31
|
+
HttpModule.register({ timeout: 10_000, maxRedirects: 5 }),
|
|
27
32
|
forwardRef(() => PrismaModule),
|
|
28
33
|
forwardRef(() => MailSendModule),
|
|
29
34
|
forwardRef(() => ConfigModule),
|