@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,97 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach, jest } from '@jest/globals';
|
|
2
|
+
import { NotFoundException } from '@nestjs/common';
|
|
3
|
+
|
|
4
|
+
import { ProfileService } from './profile.service';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* O que estes casos protegem é uma frase só: "esta conta tem senha" precisa
|
|
8
|
+
* significar a mesma coisa aqui e no resto do core.
|
|
9
|
+
*
|
|
10
|
+
* `hasPasswordCredential` (usado pelo "esqueci minha senha") sempre exigiu
|
|
11
|
+
* credencial **ativa**, e a revogação é o que o convite de acesso enterprise usa
|
|
12
|
+
* para trocar "autorize o acesso" por "crie sua conta e defina a senha". Estas
|
|
13
|
+
* duas leituras do perfil ignoravam `revoked_at`: `changePassword` aceitava a
|
|
14
|
+
* senha de uma credencial revogada e regravava o hash nela, e o `hasPassword` do
|
|
15
|
+
* `GET /profile` — que o app usa para decidir se pede senha — dizia que sim.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const makeDeps = () => ({
|
|
19
|
+
prisma: {
|
|
20
|
+
user: { findUnique: jest.fn() },
|
|
21
|
+
user_credential: { findFirst: jest.fn(), update: jest.fn() },
|
|
22
|
+
},
|
|
23
|
+
user: { registerUserActivity: jest.fn() },
|
|
24
|
+
jwt: {},
|
|
25
|
+
setting: {},
|
|
26
|
+
security: { validatePassword: jest.fn(), hashArgon2: jest.fn() },
|
|
27
|
+
challenge: { resolveAppKeyFromOrigin: jest.fn() },
|
|
28
|
+
token: {},
|
|
29
|
+
mail: { resolveRecipientLocale: jest.fn(), sendTemplatedMail: jest.fn() },
|
|
30
|
+
eventEmitter: { emit: jest.fn() },
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const makeService = (deps = makeDeps()) => ({
|
|
34
|
+
service: new ProfileService(
|
|
35
|
+
deps.prisma as any,
|
|
36
|
+
deps.user as any,
|
|
37
|
+
deps.jwt as any,
|
|
38
|
+
deps.setting as any,
|
|
39
|
+
deps.security as any,
|
|
40
|
+
deps.challenge as any,
|
|
41
|
+
deps.token as any,
|
|
42
|
+
deps.mail as any,
|
|
43
|
+
deps.eventEmitter as any,
|
|
44
|
+
),
|
|
45
|
+
deps,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe('ProfileService', () => {
|
|
49
|
+
afterEach(() => jest.clearAllMocks());
|
|
50
|
+
|
|
51
|
+
describe('changePassword', () => {
|
|
52
|
+
it('procura apenas credencial de senha não revogada', async () => {
|
|
53
|
+
const { service, deps } = makeService();
|
|
54
|
+
deps.prisma.user_credential.findFirst.mockResolvedValue(null);
|
|
55
|
+
|
|
56
|
+
await expect(
|
|
57
|
+
service.changePassword('pt-BR', 7, {
|
|
58
|
+
currentPassword: 'antiga',
|
|
59
|
+
newPassword: 'nova-senha-forte',
|
|
60
|
+
} as any),
|
|
61
|
+
).rejects.toThrow(NotFoundException);
|
|
62
|
+
|
|
63
|
+
expect(deps.prisma.user_credential.findFirst).toHaveBeenCalledWith({
|
|
64
|
+
where: { user_id: 7, type: 'password', revoked_at: null },
|
|
65
|
+
});
|
|
66
|
+
// Nada de validar hash revogado: sem credencial ativa o fluxo para antes.
|
|
67
|
+
expect(deps.security.validatePassword).not.toHaveBeenCalled();
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe('getProfile', () => {
|
|
72
|
+
it('conta apenas credencial de senha não revogada em hasPassword', async () => {
|
|
73
|
+
const { service, deps } = makeService();
|
|
74
|
+
deps.prisma.user.findUnique.mockResolvedValue({ id: 7, name: 'Aluno' });
|
|
75
|
+
deps.prisma.user_credential.findFirst.mockResolvedValue(null);
|
|
76
|
+
const count = jest.fn().mockResolvedValue(0);
|
|
77
|
+
(deps.prisma.user_credential as any).count = count;
|
|
78
|
+
|
|
79
|
+
const result = await service.getProfile(7);
|
|
80
|
+
|
|
81
|
+
expect(count).toHaveBeenCalledWith({
|
|
82
|
+
where: { user_id: 7, type: 'password', revoked_at: null },
|
|
83
|
+
});
|
|
84
|
+
expect(result).toEqual({ id: 7, name: 'Aluno', hasPassword: false });
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('devolve hasPassword true quando existe credencial ativa', async () => {
|
|
88
|
+
const { service, deps } = makeService();
|
|
89
|
+
deps.prisma.user.findUnique.mockResolvedValue({ id: 7, name: 'Aluno' });
|
|
90
|
+
(deps.prisma.user_credential as any).count = jest.fn().mockResolvedValue(1);
|
|
91
|
+
|
|
92
|
+
await expect(service.getProfile(7)).resolves.toMatchObject({
|
|
93
|
+
hasPassword: true,
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
});
|
|
@@ -24,6 +24,7 @@ import { SecurityService } from '../security/security.service';
|
|
|
24
24
|
import { SettingService } from '../setting/setting.service';
|
|
25
25
|
import { TokenService } from '../token/token.service';
|
|
26
26
|
import { UserService } from '../user/user.service';
|
|
27
|
+
import { isActivePasswordCredential } from '../user/user-credential.util';
|
|
27
28
|
import { buildSecurityEventVars } from '../utils/security-event-vars';
|
|
28
29
|
import { resolveWebAuthnOrigin } from '../utils/webauthn-origin';
|
|
29
30
|
import { ChangeEmailDto } from './dto/change-email.dto';
|
|
@@ -101,7 +102,7 @@ export class ProfileService {
|
|
|
101
102
|
throw new NotFoundException(getLocaleText('userNotFound', locale, 'User not found.'));
|
|
102
103
|
}
|
|
103
104
|
|
|
104
|
-
const credentials = (user as unknown as User).user_credential?.filter(
|
|
105
|
+
const credentials = (user as unknown as User).user_credential?.filter(isActivePasswordCredential) || [];
|
|
105
106
|
|
|
106
107
|
if (!await this.security.validatePassword(locale, credentials, password)) {
|
|
107
108
|
throw new BadRequestException(getLocaleText('accessDenied', locale, 'Access denied.'));
|
|
@@ -325,7 +326,9 @@ export class ProfileService {
|
|
|
325
326
|
async getProfile(userId: number) {
|
|
326
327
|
const [user, passwordCredentials] = await Promise.all([
|
|
327
328
|
this.prisma.user.findUnique({ where: { id: userId } }),
|
|
328
|
-
this.prisma.user_credential.count({
|
|
329
|
+
this.prisma.user_credential.count({
|
|
330
|
+
where: { user_id: userId, type: 'password', revoked_at: null },
|
|
331
|
+
}),
|
|
329
332
|
]);
|
|
330
333
|
|
|
331
334
|
if (!user) return user;
|
|
@@ -457,7 +460,10 @@ export class ProfileService {
|
|
|
457
460
|
|
|
458
461
|
async changePassword(locale:string, userId: number, {currentPassword, newPassword}: ChangePasswordDto, ipAddress?: string, userAgent?: string, origin?: string) {
|
|
459
462
|
const credential = await this.prisma.user_credential.findFirst({
|
|
460
|
-
|
|
463
|
+
// Credencial revogada não é senha atual: sem este filtro, `changePassword`
|
|
464
|
+
// aceitava a senha antiga de uma credencial revogada e a reativava por via
|
|
465
|
+
// indireta, gravando um hash novo na mesma linha.
|
|
466
|
+
where: { user_id: userId, type: 'password', revoked_at: null },
|
|
461
467
|
});
|
|
462
468
|
|
|
463
469
|
if (!credential) {
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/// <reference types="jest" />
|
|
2
|
+
|
|
3
|
+
import { afterEach, beforeEach, describe, expect, it, jest } from '@jest/globals';
|
|
4
|
+
import { RealtimeTickCoalescer } from './realtime-tick.coalescer';
|
|
5
|
+
|
|
6
|
+
describe('RealtimeTickCoalescer', () => {
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
jest.useFakeTimers();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
jest.useRealTimers();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('junta uma rajada num único disparo', () => {
|
|
16
|
+
// O heartbeat bate a cada 15s por aluno: com 200 ao vivo seriam ~13
|
|
17
|
+
// eventos/s. É esta compressão que torna o push viável.
|
|
18
|
+
const onFlush = jest.fn();
|
|
19
|
+
const coalescer = new RealtimeTickCoalescer(2000, onFlush);
|
|
20
|
+
|
|
21
|
+
for (let i = 0; i < 200; i += 1) coalescer.signal();
|
|
22
|
+
expect(onFlush).not.toHaveBeenCalled();
|
|
23
|
+
|
|
24
|
+
jest.advanceTimersByTime(2000);
|
|
25
|
+
|
|
26
|
+
expect(onFlush).toHaveBeenCalledTimes(1);
|
|
27
|
+
expect(onFlush).toHaveBeenCalledWith({ seq: 1, coalesced: 200 });
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('não dispara sozinho quando não houve sinal', () => {
|
|
31
|
+
const onFlush = jest.fn();
|
|
32
|
+
new RealtimeTickCoalescer(2000, onFlush);
|
|
33
|
+
|
|
34
|
+
jest.advanceTimersByTime(10_000);
|
|
35
|
+
|
|
36
|
+
expect(onFlush).not.toHaveBeenCalled();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('abre uma janela nova depois do disparo', () => {
|
|
40
|
+
const onFlush = jest.fn();
|
|
41
|
+
const coalescer = new RealtimeTickCoalescer(2000, onFlush);
|
|
42
|
+
|
|
43
|
+
coalescer.signal();
|
|
44
|
+
jest.advanceTimersByTime(2000);
|
|
45
|
+
expect(onFlush).toHaveBeenCalledTimes(1);
|
|
46
|
+
|
|
47
|
+
coalescer.signal();
|
|
48
|
+
jest.advanceTimersByTime(2000);
|
|
49
|
+
|
|
50
|
+
expect(onFlush).toHaveBeenCalledTimes(2);
|
|
51
|
+
expect(onFlush).toHaveBeenLastCalledWith({ seq: 2, coalesced: 1 });
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('não se auto-perpetua depois de esvaziar', () => {
|
|
55
|
+
const onFlush = jest.fn();
|
|
56
|
+
const coalescer = new RealtimeTickCoalescer(2000, onFlush);
|
|
57
|
+
|
|
58
|
+
coalescer.signal();
|
|
59
|
+
jest.advanceTimersByTime(2000);
|
|
60
|
+
jest.advanceTimersByTime(60_000);
|
|
61
|
+
|
|
62
|
+
expect(onFlush).toHaveBeenCalledTimes(1);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('dispose cancela o disparo pendente', () => {
|
|
66
|
+
// Um timer vivo no encerramento segura o processo e atrasa o drain do pod.
|
|
67
|
+
const onFlush = jest.fn();
|
|
68
|
+
const coalescer = new RealtimeTickCoalescer(2000, onFlush);
|
|
69
|
+
|
|
70
|
+
coalescer.signal();
|
|
71
|
+
coalescer.dispose();
|
|
72
|
+
jest.advanceTimersByTime(10_000);
|
|
73
|
+
|
|
74
|
+
expect(onFlush).not.toHaveBeenCalled();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('flush manual entrega o que estiver pendente', () => {
|
|
78
|
+
const onFlush = jest.fn();
|
|
79
|
+
const coalescer = new RealtimeTickCoalescer(2000, onFlush);
|
|
80
|
+
|
|
81
|
+
coalescer.signal();
|
|
82
|
+
coalescer.signal();
|
|
83
|
+
coalescer.flush();
|
|
84
|
+
|
|
85
|
+
expect(onFlush).toHaveBeenCalledWith({ seq: 1, coalesced: 2 });
|
|
86
|
+
|
|
87
|
+
// Depois do flush manual, o timer que já estava agendado não deve emitir de
|
|
88
|
+
// novo com a fila vazia.
|
|
89
|
+
jest.advanceTimersByTime(2000);
|
|
90
|
+
expect(onFlush).toHaveBeenCalledTimes(1);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Junta uma rajada de sinais num único disparo.
|
|
3
|
+
*
|
|
4
|
+
* Nasceu no LMS e vive aqui porque o problema não é de lá: qualquer feature com
|
|
5
|
+
* uma tela de acompanhamento tem a mesma rajada. No LMS é o heartbeat do player,
|
|
6
|
+
* que bate a cada 15s por aluno — com 200 alunos ao vivo são ~13 sinais por
|
|
7
|
+
* segundo, e um evento de realtime por batida faria cada admin com a tela aberta
|
|
8
|
+
* refazer a consulta 13x/s. Na loja é o beacon de navegação dos visitantes. O
|
|
9
|
+
* coalescer transforma isso num disparo a cada `windowMs`.
|
|
10
|
+
*
|
|
11
|
+
* A borda escolhida é a de saída (trailing): o primeiro sinal agenda o disparo
|
|
12
|
+
* para daqui a `windowMs` e os que chegarem no meio são absorvidos. Isso limita
|
|
13
|
+
* o pior caso a um disparo por janela, ao custo de até `windowMs` de latência —
|
|
14
|
+
* aceitável para uma tela de acompanhamento, e o número de sinais absorvidos vai
|
|
15
|
+
* junto no disparo para quem quiser instrumentar.
|
|
16
|
+
*/
|
|
17
|
+
export class RealtimeTickCoalescer {
|
|
18
|
+
private timer: ReturnType<typeof setTimeout> | null = null;
|
|
19
|
+
private pending = 0;
|
|
20
|
+
private sequence = 0;
|
|
21
|
+
|
|
22
|
+
constructor(
|
|
23
|
+
private readonly windowMs: number,
|
|
24
|
+
private readonly onFlush: (info: { seq: number; coalesced: number }) => void,
|
|
25
|
+
) {}
|
|
26
|
+
|
|
27
|
+
/** Registra um sinal. Só o primeiro da janela agenda o disparo. */
|
|
28
|
+
signal(): void {
|
|
29
|
+
this.pending += 1;
|
|
30
|
+
if (this.timer !== null) return;
|
|
31
|
+
|
|
32
|
+
this.timer = setTimeout(() => {
|
|
33
|
+
this.timer = null;
|
|
34
|
+
this.flush();
|
|
35
|
+
}, this.windowMs);
|
|
36
|
+
|
|
37
|
+
// Um timer pendente não deve segurar o processo no shutdown do worker.
|
|
38
|
+
this.timer.unref?.();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Dispara agora o que estiver pendente, se houver. */
|
|
42
|
+
flush(): void {
|
|
43
|
+
if (this.pending === 0) return;
|
|
44
|
+
|
|
45
|
+
const coalesced = this.pending;
|
|
46
|
+
this.pending = 0;
|
|
47
|
+
this.sequence += 1;
|
|
48
|
+
|
|
49
|
+
this.onFlush({ seq: this.sequence, coalesced });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Cancela o disparo pendente. Usado no encerramento do módulo. */
|
|
53
|
+
dispose(): void {
|
|
54
|
+
if (this.timer !== null) {
|
|
55
|
+
clearTimeout(this.timer);
|
|
56
|
+
this.timer = null;
|
|
57
|
+
}
|
|
58
|
+
this.pending = 0;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -37,6 +37,39 @@ describe('RealtimeService', () => {
|
|
|
37
37
|
expect(() => bare.emitToUser(1, 'x', {})).not.toThrow();
|
|
38
38
|
});
|
|
39
39
|
|
|
40
|
+
// Caminho que só existe com mais de um pod: quem cria a notificação quase
|
|
41
|
+
// nunca é o pod que segura o socket do usuário (4 réplicas, sem sticky
|
|
42
|
+
// session), então a entrega real depende deste re-emit a partir do Redis.
|
|
43
|
+
describe('handleRedisMessage', () => {
|
|
44
|
+
const message = (over: Record<string, unknown> = {}) =>
|
|
45
|
+
JSON.stringify({
|
|
46
|
+
target: { type: 'user', target: '7' },
|
|
47
|
+
event: 'notification:event',
|
|
48
|
+
payload: { type: 'notification.created' },
|
|
49
|
+
originInstanceId: 'outro-pod',
|
|
50
|
+
...over,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('re-emite localmente o que veio de outro pod', () => {
|
|
54
|
+
(service as any).handleRedisMessage(message());
|
|
55
|
+
expect(server.to).toHaveBeenCalledWith('user:7');
|
|
56
|
+
expect(server.emit).toHaveBeenCalledWith('notification:event', {
|
|
57
|
+
type: 'notification.created',
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('ignora a própria mensagem, já emitida localmente', () => {
|
|
62
|
+
const ownId = (service as any).instanceId;
|
|
63
|
+
(service as any).handleRedisMessage(message({ originInstanceId: ownId }));
|
|
64
|
+
expect(server.to).not.toHaveBeenCalled();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('ignora payload malformado', () => {
|
|
68
|
+
expect(() => (service as any).handleRedisMessage('{')).not.toThrow();
|
|
69
|
+
expect(server.to).not.toHaveBeenCalled();
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
40
73
|
it('tracks local connections per user', () => {
|
|
41
74
|
expect(service.trackConnect(1)).toBe(1);
|
|
42
75
|
expect(service.trackConnect(1)).toBe(2);
|
|
@@ -36,6 +36,15 @@ export class RealtimeService implements OnModuleInit, OnModuleDestroy {
|
|
|
36
36
|
private server: Server | null = null;
|
|
37
37
|
private redisPublisher: RedisClientType | null = null;
|
|
38
38
|
private redisSubscriber: RedisClientType | null = null;
|
|
39
|
+
// A `reconnectStrategy` que nunca devolve `false` faz `client.connect()` ficar
|
|
40
|
+
// retentando pra sempre quando o Redis está indisponível: a Promise nunca
|
|
41
|
+
// resolve nem rejeita, e `onModuleInit` trava o boot inteiro da API esperando
|
|
42
|
+
// por ela. Este teto bound só a tentativa inicial; a reconexão de um cliente
|
|
43
|
+
// já conectado continua indefinida.
|
|
44
|
+
private readonly redisConnectTimeoutMs = Math.max(
|
|
45
|
+
1000,
|
|
46
|
+
Number(process.env.REDIS_CONNECT_TIMEOUT_MS || 3000),
|
|
47
|
+
);
|
|
39
48
|
|
|
40
49
|
/** Local (this-instance) socket count per user id, for presence/liveness. */
|
|
41
50
|
private readonly localConnections = new Map<number, number>();
|
|
@@ -162,8 +171,8 @@ export class RealtimeService implements OnModuleInit, OnModuleDestroy {
|
|
|
162
171
|
this.redisSubscriber = createClient(opts);
|
|
163
172
|
this.redisPublisher.on('error', (e) => this.logger.warn(`Realtime Redis publisher error: ${e?.message ?? e}`));
|
|
164
173
|
this.redisSubscriber.on('error', (e) => this.logger.warn(`Realtime Redis subscriber error: ${e?.message ?? e}`));
|
|
165
|
-
await this.redisPublisher.connect();
|
|
166
|
-
await this.redisSubscriber.connect();
|
|
174
|
+
await this.withConnectTimeout(this.redisPublisher.connect(), this.redisConnectTimeoutMs);
|
|
175
|
+
await this.withConnectTimeout(this.redisSubscriber.connect(), this.redisConnectTimeoutMs);
|
|
167
176
|
await this.redisSubscriber.subscribe(this.channel, (msg) => this.handleRedisMessage(msg));
|
|
168
177
|
this.logger.log(`Realtime Redis fan-out enabled on channel ${this.channel}`);
|
|
169
178
|
} catch (error) {
|
|
@@ -184,4 +193,21 @@ export class RealtimeService implements OnModuleInit, OnModuleDestroy {
|
|
|
184
193
|
/* ignore */
|
|
185
194
|
}
|
|
186
195
|
}
|
|
196
|
+
|
|
197
|
+
private async withConnectTimeout<T>(promise: Promise<T>, timeoutMs: number): Promise<T> {
|
|
198
|
+
let timer: NodeJS.Timeout;
|
|
199
|
+
try {
|
|
200
|
+
return await Promise.race([
|
|
201
|
+
promise,
|
|
202
|
+
new Promise<never>((_, reject) => {
|
|
203
|
+
timer = setTimeout(
|
|
204
|
+
() => reject(new Error(`Redis connection timed out after ${timeoutMs}ms`)),
|
|
205
|
+
timeoutMs,
|
|
206
|
+
);
|
|
207
|
+
}),
|
|
208
|
+
]);
|
|
209
|
+
} finally {
|
|
210
|
+
clearTimeout(timer!);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
187
213
|
}
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { Injectable, Logger, OnModuleDestroy, OnModuleInit } from '@nestjs/common';
|
|
2
|
+
import { createClient, type RedisClientType } from 'redis';
|
|
3
|
+
import { extractRedisErrorMessage, withConnectTimeout } from './redis-pubsub';
|
|
4
|
+
import { isReadOnlyReplicaError, resolveMasterUrl } from './redis-master';
|
|
5
|
+
|
|
6
|
+
/** Uma entrada do índice ordenado por tempo, já decodificada. */
|
|
7
|
+
export type RedisScoredMember = {
|
|
8
|
+
member: string;
|
|
9
|
+
score: number;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Chave/valor com TTL sobre o Redis, para estado efêmero compartilhado entre as
|
|
14
|
+
* réplicas da API.
|
|
15
|
+
*
|
|
16
|
+
* Existe ao lado do `redis-pubsub.ts` — que só publica e assina — porque
|
|
17
|
+
* algumas features precisam GUARDAR estado por um tempo curto, não só avisar
|
|
18
|
+
* que algo mudou. O caso que motivou: o painel de visitantes ao vivo da loja,
|
|
19
|
+
* onde gravar cada navegação no Postgres colocaria carga de escrita no banco
|
|
20
|
+
* que serve a própria loja, exatamente o que o recurso não pode fazer. Aqui o
|
|
21
|
+
* dado vive na memória do Redis e expira sozinho, sem job de limpeza.
|
|
22
|
+
*
|
|
23
|
+
* **Toda falha degrada para inerte.** Mesma postura do pub/sub: sem
|
|
24
|
+
* `REDIS_URL`, ou com o Redis fora, cada método vira no-op (ou devolve vazio) e
|
|
25
|
+
* quem chamou segue seu caminho. Um recurso de observação nunca pode derrubar o
|
|
26
|
+
* fluxo que ele observa — daí nenhum método propagar erro.
|
|
27
|
+
*/
|
|
28
|
+
@Injectable()
|
|
29
|
+
export class RedisKvService implements OnModuleInit, OnModuleDestroy {
|
|
30
|
+
private readonly logger = new Logger(RedisKvService.name);
|
|
31
|
+
private readonly url = String(process.env.REDIS_URL || '').trim();
|
|
32
|
+
private readonly connectTimeoutMs = Math.max(
|
|
33
|
+
1000,
|
|
34
|
+
Number(process.env.REDIS_CONNECT_TIMEOUT_MS || 3000),
|
|
35
|
+
);
|
|
36
|
+
/** Uma queda do Redis dispara `error` a cada tentativa de reconexão. */
|
|
37
|
+
private readonly errorLogCooldownMs = 30000;
|
|
38
|
+
private lastErrorLogAt = 0;
|
|
39
|
+
|
|
40
|
+
private client: RedisClientType | null = null;
|
|
41
|
+
/** Reconexão em andamento, para não abrir uma por escrita durante o failover. */
|
|
42
|
+
private reconnecting: Promise<void> | null = null;
|
|
43
|
+
|
|
44
|
+
/** `false` = sem Redis; todo método é no-op e quem chama não precisa saber. */
|
|
45
|
+
get enabled(): boolean {
|
|
46
|
+
return this.client !== null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async onModuleInit(): Promise<void> {
|
|
50
|
+
if (!this.url) {
|
|
51
|
+
this.logger.log('Redis key-value disabled because REDIS_URL is not configured.');
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
this.client = await this.connectToMaster();
|
|
56
|
+
if (this.client) this.logger.log('Redis key-value enabled.');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Conecta no master, e não em quem o Service devolver.
|
|
61
|
+
*
|
|
62
|
+
* `REDIS_URL` aponta para um Service que balanceia entre master e réplica
|
|
63
|
+
* (veja `resolveMasterUrl`), então conectar direto na URL deixa metade dos
|
|
64
|
+
* processos escrevendo numa réplica e falhando com `READONLY`.
|
|
65
|
+
*/
|
|
66
|
+
private async connectToMaster(): Promise<RedisClientType | null> {
|
|
67
|
+
try {
|
|
68
|
+
const url = await resolveMasterUrl(
|
|
69
|
+
this.url,
|
|
70
|
+
this.logger,
|
|
71
|
+
this.connectTimeoutMs,
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const client = createClient({
|
|
75
|
+
url,
|
|
76
|
+
socket: {
|
|
77
|
+
reconnectStrategy: (retries: number) => Math.min(retries * 250, 5000),
|
|
78
|
+
},
|
|
79
|
+
}) as RedisClientType;
|
|
80
|
+
|
|
81
|
+
// Registrado antes do connect: uma falha no handshake vira erro tratado
|
|
82
|
+
// em vez de um evento 'error' sem dono derrubando o processo.
|
|
83
|
+
client.on('error', (error: unknown) => this.logClientError(error));
|
|
84
|
+
|
|
85
|
+
await withConnectTimeout(client.connect(), this.connectTimeoutMs);
|
|
86
|
+
return client;
|
|
87
|
+
} catch (error) {
|
|
88
|
+
this.logger.warn(
|
|
89
|
+
`Redis key-value disabled: ${extractRedisErrorMessage(error)}`,
|
|
90
|
+
);
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Refaz a conexão depois de um `READONLY`.
|
|
97
|
+
*
|
|
98
|
+
* Um failover troca quem é o master, e o client fica preso no nó que virou
|
|
99
|
+
* réplica — daí em diante toda escrita falha. Reconectar redescobre o master.
|
|
100
|
+
* O guard serializa as tentativas: sem ele, um pico de escritas em cima do
|
|
101
|
+
* failover abriria uma conexão nova por requisição.
|
|
102
|
+
*/
|
|
103
|
+
private async reconnectToMaster(): Promise<void> {
|
|
104
|
+
if (this.reconnecting) return this.reconnecting;
|
|
105
|
+
|
|
106
|
+
this.reconnecting = (async () => {
|
|
107
|
+
const stale = this.client;
|
|
108
|
+
this.client = null;
|
|
109
|
+
if (stale) {
|
|
110
|
+
try {
|
|
111
|
+
await stale.quit();
|
|
112
|
+
} catch {
|
|
113
|
+
// O nó virou réplica ou caiu; encerrando de qualquer forma.
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
this.client = await this.connectToMaster();
|
|
117
|
+
})().finally(() => {
|
|
118
|
+
this.reconnecting = null;
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
return this.reconnecting;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async onModuleDestroy(): Promise<void> {
|
|
125
|
+
const client = this.client;
|
|
126
|
+
this.client = null;
|
|
127
|
+
if (!client) return;
|
|
128
|
+
try {
|
|
129
|
+
await client.quit();
|
|
130
|
+
} catch {
|
|
131
|
+
// Encerrando de qualquer forma.
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Grava um objeto como JSON com expiração em segundos. */
|
|
136
|
+
async setJson(key: string, value: unknown, ttlSeconds: number): Promise<void> {
|
|
137
|
+
await this.run(async (client) => {
|
|
138
|
+
await client.set(key, JSON.stringify(value), { EX: Math.max(1, ttlSeconds) });
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Lê e desserializa. Devolve `null` se não existe, expirou ou está corrompido. */
|
|
143
|
+
async getJson<T>(key: string): Promise<T | null> {
|
|
144
|
+
return (
|
|
145
|
+
(await this.run(async (client) => {
|
|
146
|
+
const raw = await client.get(key);
|
|
147
|
+
if (raw == null) return null;
|
|
148
|
+
try {
|
|
149
|
+
return JSON.parse(raw) as T;
|
|
150
|
+
} catch {
|
|
151
|
+
// Valor gravado por uma versão anterior com outro formato: tratar como
|
|
152
|
+
// ausente é melhor do que estourar na leitura do painel.
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
})) ?? null
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Lê várias chaves numa ida só.
|
|
161
|
+
*
|
|
162
|
+
* `MGET` em vez de um `get` por chave: com dezenas de visitantes ao vivo, uma
|
|
163
|
+
* chamada por chave multiplicaria o round-trip pelo número de sessões a cada
|
|
164
|
+
* atualização do painel. Devolve na MESMA ordem das chaves pedidas, com `null`
|
|
165
|
+
* onde não havia valor.
|
|
166
|
+
*/
|
|
167
|
+
async getJsonMany<T>(keys: string[]): Promise<Array<T | null>> {
|
|
168
|
+
if (!keys.length) return [];
|
|
169
|
+
const values = await this.run(async (client) => client.mGet(keys));
|
|
170
|
+
if (!values) return keys.map(() => null);
|
|
171
|
+
|
|
172
|
+
return values.map((raw) => {
|
|
173
|
+
if (raw == null) return null;
|
|
174
|
+
try {
|
|
175
|
+
return JSON.parse(raw) as T;
|
|
176
|
+
} catch {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async delete(key: string): Promise<void> {
|
|
183
|
+
await this.run(async (client) => {
|
|
184
|
+
await client.del(key);
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** Põe (ou repontua) um membro no índice ordenado. */
|
|
189
|
+
async zadd(key: string, member: string, score: number): Promise<void> {
|
|
190
|
+
await this.run(async (client) => {
|
|
191
|
+
await client.zAdd(key, [{ value: member, score }]);
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** Membros com score dentro do intervalo, do mais antigo para o mais recente. */
|
|
196
|
+
async zrangeByScore(
|
|
197
|
+
key: string,
|
|
198
|
+
min: number,
|
|
199
|
+
max: number,
|
|
200
|
+
): Promise<RedisScoredMember[]> {
|
|
201
|
+
const rows = await this.run(async (client) =>
|
|
202
|
+
client.zRangeByScoreWithScores(key, min, max),
|
|
203
|
+
);
|
|
204
|
+
return (rows ?? []).map((row) => ({ member: row.value, score: row.score }));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Remove do índice o que caiu fora da janela.
|
|
209
|
+
*
|
|
210
|
+
* O TTL cuida das chaves individuais, mas não do índice: sem esta poda o
|
|
211
|
+
* sorted set acumularia para sempre todo visitante que já passou pela loja, e
|
|
212
|
+
* cada leitura do painel ficaria mais cara que a anterior.
|
|
213
|
+
*/
|
|
214
|
+
async zremRangeByScore(key: string, min: number, max: number): Promise<void> {
|
|
215
|
+
await this.run(async (client) => {
|
|
216
|
+
await client.zRemRangeByScore(key, min, max);
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Executa contra o client, engolindo qualquer falha.
|
|
222
|
+
*
|
|
223
|
+
* É aqui que mora a promessa de degradar para inerte: quem chama recebe
|
|
224
|
+
* `undefined` e trata como "sem dado", em vez de precisar de try/catch em
|
|
225
|
+
* cada ponto de uso.
|
|
226
|
+
*/
|
|
227
|
+
private async run<T>(fn: (client: RedisClientType) => Promise<T>): Promise<T | undefined> {
|
|
228
|
+
const client = this.client;
|
|
229
|
+
if (!client) return undefined;
|
|
230
|
+
try {
|
|
231
|
+
return await fn(client);
|
|
232
|
+
} catch (error) {
|
|
233
|
+
// `READONLY` quer dizer que este client está falando com uma réplica —
|
|
234
|
+
// um failover trocou o master debaixo dele. Reconectar redescobre quem é
|
|
235
|
+
// o master e a operação é refeita uma vez; qualquer outro erro segue a
|
|
236
|
+
// regra da casa e degrada para inerte.
|
|
237
|
+
if (isReadOnlyReplicaError(error)) {
|
|
238
|
+
await this.reconnectToMaster();
|
|
239
|
+
const reconnected = this.client;
|
|
240
|
+
if (reconnected) {
|
|
241
|
+
try {
|
|
242
|
+
return await fn(reconnected);
|
|
243
|
+
} catch (retryError) {
|
|
244
|
+
this.logClientError(retryError);
|
|
245
|
+
return undefined;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
this.logClientError(error);
|
|
251
|
+
return undefined;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
private logClientError(error: unknown): void {
|
|
256
|
+
const now = Date.now();
|
|
257
|
+
if (now - this.lastErrorLogAt < this.errorLogCooldownMs) return;
|
|
258
|
+
this.lastErrorLogAt = now;
|
|
259
|
+
this.logger.warn(`Redis key-value error: ${extractRedisErrorMessage(error)}`);
|
|
260
|
+
}
|
|
261
|
+
}
|