@hed-hog/core 0.0.390 → 0.0.393
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/README.md +39 -0
- package/dist/access-log/access-log.controller.d.ts.map +1 -1
- package/dist/access-log/access-log.controller.js +1 -1
- package/dist/access-log/access-log.controller.js.map +1 -1
- package/dist/access-log/access-log.interceptor.d.ts.map +1 -1
- package/dist/access-log/access-log.interceptor.js +8 -5
- package/dist/access-log/access-log.interceptor.js.map +1 -1
- package/dist/access-log/access-log.service.d.ts +4 -0
- package/dist/access-log/access-log.service.d.ts.map +1 -1
- package/dist/access-log/access-log.service.js +31 -4
- package/dist/access-log/access-log.service.js.map +1 -1
- package/dist/access-log/dto/query-access-log.dto.d.ts +2 -0
- package/dist/access-log/dto/query-access-log.dto.d.ts.map +1 -1
- package/dist/access-log/dto/query-access-log.dto.js +5 -0
- package/dist/access-log/dto/query-access-log.dto.js.map +1 -1
- package/dist/ai/ai.controller.d.ts +12 -8
- package/dist/ai/ai.controller.d.ts.map +1 -1
- package/dist/ai/ai.controller.js +17 -2
- package/dist/ai/ai.controller.js.map +1 -1
- package/dist/ai/ai.module.d.ts.map +1 -1
- package/dist/ai/ai.module.js +3 -2
- package/dist/ai/ai.module.js.map +1 -1
- package/dist/ai/ai.service.d.ts +30 -1
- package/dist/ai/ai.service.d.ts.map +1 -1
- package/dist/ai/ai.service.js +97 -3
- package/dist/ai/ai.service.js.map +1 -1
- package/dist/ai/dto/richtext-edit-html.dto.d.ts +5 -0
- package/dist/ai/dto/richtext-edit-html.dto.d.ts.map +1 -0
- package/dist/ai/dto/richtext-edit-html.dto.js +27 -0
- package/dist/ai/dto/richtext-edit-html.dto.js.map +1 -0
- package/dist/ai/richtext-ai.prompts.d.ts +5 -0
- package/dist/ai/richtext-ai.prompts.d.ts.map +1 -0
- package/dist/ai/richtext-ai.prompts.js +57 -0
- package/dist/ai/richtext-ai.prompts.js.map +1 -0
- package/dist/ai/richtext-ai.service.d.ts +34 -0
- package/dist/ai/richtext-ai.service.d.ts.map +1 -0
- package/dist/ai/richtext-ai.service.js +95 -0
- package/dist/ai/richtext-ai.service.js.map +1 -0
- package/dist/ai-instruction/ai-instruction.controller.d.ts +5 -5
- package/dist/auth/auth.controller.d.ts +66 -7
- package/dist/auth/auth.controller.d.ts.map +1 -1
- package/dist/auth/auth.controller.js +67 -23
- package/dist/auth/auth.controller.js.map +1 -1
- package/dist/auth/auth.module.d.ts.map +1 -1
- package/dist/auth/auth.module.js +2 -0
- package/dist/auth/auth.module.js.map +1 -1
- package/dist/auth/auth.service.d.ts +136 -4
- package/dist/auth/auth.service.d.ts.map +1 -1
- package/dist/auth/auth.service.js +246 -48
- package/dist/auth/auth.service.js.map +1 -1
- package/dist/auth/dto/confirm-email.dto.d.ts +4 -0
- package/dist/auth/dto/confirm-email.dto.d.ts.map +1 -0
- package/dist/auth/dto/confirm-email.dto.js +22 -0
- package/dist/auth/dto/confirm-email.dto.js.map +1 -0
- package/dist/auth/dto/resend-confirmation.dto.d.ts +8 -0
- package/dist/auth/dto/resend-confirmation.dto.d.ts.map +1 -0
- package/dist/auth/dto/resend-confirmation.dto.js +36 -0
- package/dist/auth/dto/resend-confirmation.dto.js.map +1 -0
- package/dist/authorize/authorize.controller.d.ts +1 -1
- package/dist/authorize/authorize.controller.d.ts.map +1 -1
- package/dist/authorize/authorize.controller.js +4 -2
- package/dist/authorize/authorize.controller.js.map +1 -1
- package/dist/authorize/authorize.service.d.ts +7 -0
- package/dist/authorize/authorize.service.d.ts.map +1 -1
- package/dist/authorize/authorize.service.js +1 -1
- package/dist/authorize/authorize.service.js.map +1 -1
- package/dist/challenge/challenge.service.d.ts +18 -5
- package/dist/challenge/challenge.service.d.ts.map +1 -1
- package/dist/challenge/challenge.service.js +50 -3
- package/dist/challenge/challenge.service.js.map +1 -1
- package/dist/core.module.d.ts.map +1 -1
- package/dist/core.module.js +17 -0
- package/dist/core.module.js.map +1 -1
- package/dist/dashboard/dashboard/dashboard.controller.d.ts +6 -6
- package/dist/dashboard/dashboard/dashboard.service.d.ts +6 -6
- package/dist/dashboard/dashboard-component/dashboard-component.controller.d.ts +4 -4
- package/dist/dashboard/dashboard-component/dashboard-component.service.d.ts +4 -4
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.controller.d.ts +1 -1
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.d.ts +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts +2 -2
- package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts +2 -2
- package/dist/dashboard/dashboard-item/dashboard-item.controller.d.ts +2 -2
- package/dist/dashboard/dashboard-item/dashboard-item.service.d.ts +2 -2
- package/dist/file/file.controller.d.ts +8 -1
- package/dist/file/file.controller.d.ts.map +1 -1
- package/dist/file/file.controller.js +45 -7
- package/dist/file/file.controller.js.map +1 -1
- package/dist/file/file.service.d.ts +128 -1
- package/dist/file/file.service.d.ts.map +1 -1
- package/dist/file/file.service.js +319 -8
- package/dist/file/file.service.js.map +1 -1
- package/dist/file/image-optimization.service.d.ts +43 -0
- package/dist/file/image-optimization.service.d.ts.map +1 -1
- package/dist/file/image-optimization.service.js +154 -0
- package/dist/file/image-optimization.service.js.map +1 -1
- package/dist/file/provider/abstract.provider.d.ts +16 -0
- package/dist/file/provider/abstract.provider.d.ts.map +1 -1
- package/dist/file/provider/abstract.provider.js +4 -0
- package/dist/file/provider/abstract.provider.js.map +1 -1
- package/dist/file/provider/gcs.provider.d.ts +2 -0
- package/dist/file/provider/gcs.provider.d.ts.map +1 -1
- package/dist/file/provider/gcs.provider.js +10 -0
- package/dist/file/provider/gcs.provider.js.map +1 -1
- package/dist/file/provider/local.provider.d.ts +1 -0
- package/dist/file/provider/local.provider.d.ts.map +1 -1
- package/dist/file/provider/local.provider.js +13 -0
- package/dist/file/provider/local.provider.js.map +1 -1
- package/dist/file/provider/s3.provider.d.ts +5 -0
- package/dist/file/provider/s3.provider.d.ts.map +1 -1
- package/dist/file/provider/s3.provider.js +15 -0
- package/dist/file/provider/s3.provider.js.map +1 -1
- package/dist/file/provider/spaces.provider.d.ts +5 -0
- package/dist/file/provider/spaces.provider.d.ts.map +1 -1
- package/dist/file/provider/spaces.provider.js +16 -0
- package/dist/file/provider/spaces.provider.js.map +1 -1
- package/dist/impersonation/dto/redeem-impersonation.dto.d.ts +4 -0
- package/dist/impersonation/dto/redeem-impersonation.dto.d.ts.map +1 -0
- package/dist/impersonation/dto/redeem-impersonation.dto.js +23 -0
- package/dist/impersonation/dto/redeem-impersonation.dto.js.map +1 -0
- package/dist/impersonation/dto/start-impersonation.dto.d.ts +7 -0
- package/dist/impersonation/dto/start-impersonation.dto.d.ts.map +1 -0
- package/dist/impersonation/dto/start-impersonation.dto.js +27 -0
- package/dist/impersonation/dto/start-impersonation.dto.js.map +1 -0
- package/dist/impersonation/impersonation-restrictions.d.ts +26 -0
- package/dist/impersonation/impersonation-restrictions.d.ts.map +1 -0
- package/dist/impersonation/impersonation-restrictions.js +76 -0
- package/dist/impersonation/impersonation-restrictions.js.map +1 -0
- package/dist/impersonation/impersonation.controller.d.ts +90 -0
- package/dist/impersonation/impersonation.controller.d.ts.map +1 -0
- package/dist/impersonation/impersonation.controller.js +135 -0
- package/dist/impersonation/impersonation.controller.js.map +1 -0
- package/dist/impersonation/impersonation.module.d.ts +8 -0
- package/dist/impersonation/impersonation.module.d.ts.map +1 -0
- package/dist/impersonation/impersonation.module.js +44 -0
- package/dist/impersonation/impersonation.module.js.map +1 -0
- package/dist/impersonation/impersonation.service.d.ts +164 -0
- package/dist/impersonation/impersonation.service.d.ts.map +1 -0
- package/dist/impersonation/impersonation.service.js +408 -0
- package/dist/impersonation/impersonation.service.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -1
- package/dist/integration/services/outbox-polling.coordinator.d.ts +2 -0
- package/dist/integration/services/outbox-polling.coordinator.d.ts.map +1 -1
- package/dist/integration/services/outbox-polling.coordinator.js +50 -30
- package/dist/integration/services/outbox-polling.coordinator.js.map +1 -1
- package/dist/integration/services/outbox.service.d.ts.map +1 -1
- package/dist/integration/services/outbox.service.js +5 -1
- package/dist/integration/services/outbox.service.js.map +1 -1
- package/dist/integration-profile/integration-profile.secrets.d.ts.map +1 -1
- package/dist/integration-profile/integration-profile.secrets.js +3 -0
- package/dist/integration-profile/integration-profile.secrets.js.map +1 -1
- package/dist/integration-profile/integration-profile.utils.d.ts +14 -0
- package/dist/integration-profile/integration-profile.utils.d.ts.map +1 -1
- package/dist/integration-profile/integration-profile.utils.js +26 -0
- package/dist/integration-profile/integration-profile.utils.js.map +1 -1
- package/dist/integration-profile/testers/email.testers.d.ts.map +1 -1
- package/dist/integration-profile/testers/email.testers.js +28 -0
- package/dist/integration-profile/testers/email.testers.js.map +1 -1
- package/dist/language/en.json +11 -0
- package/dist/language/pt.json +11 -0
- package/dist/mail/dto/send.dto.d.ts +38 -1
- package/dist/mail/dto/send.dto.d.ts.map +1 -1
- package/dist/mail/dto/send.dto.js +10 -0
- package/dist/mail/dto/send.dto.js.map +1 -1
- package/dist/mail/mail.controller.d.ts +1 -0
- package/dist/mail/mail.controller.d.ts.map +1 -1
- package/dist/mail/mail.module.d.ts.map +1 -1
- package/dist/mail/mail.module.js +2 -0
- package/dist/mail/mail.module.js.map +1 -1
- package/dist/mail/mail.service.d.ts +48 -4
- package/dist/mail/mail.service.d.ts.map +1 -1
- package/dist/mail/mail.service.js +187 -35
- package/dist/mail/mail.service.js.map +1 -1
- package/dist/mail-delivery/mail-delivery-provision.service.d.ts +21 -0
- package/dist/mail-delivery/mail-delivery-provision.service.d.ts.map +1 -0
- package/dist/mail-delivery/mail-delivery-provision.service.js +64 -0
- package/dist/mail-delivery/mail-delivery-provision.service.js.map +1 -0
- package/dist/mail-delivery/mail-delivery.constants.d.ts +17 -0
- package/dist/mail-delivery/mail-delivery.constants.d.ts.map +1 -0
- package/dist/mail-delivery/mail-delivery.constants.js +31 -0
- package/dist/mail-delivery/mail-delivery.constants.js.map +1 -0
- package/dist/mail-delivery/mail-delivery.controller.d.ts +19 -0
- package/dist/mail-delivery/mail-delivery.controller.d.ts.map +1 -0
- package/dist/mail-delivery/mail-delivery.controller.js +53 -0
- package/dist/mail-delivery/mail-delivery.controller.js.map +1 -0
- package/dist/mail-delivery/mail-delivery.module.d.ts +7 -0
- package/dist/mail-delivery/mail-delivery.module.d.ts.map +1 -0
- package/dist/mail-delivery/mail-delivery.module.js +36 -0
- package/dist/mail-delivery/mail-delivery.module.js.map +1 -0
- package/dist/mail-delivery/mail-delivery.service.d.ts +38 -0
- package/dist/mail-delivery/mail-delivery.service.d.ts.map +1 -0
- package/dist/mail-delivery/mail-delivery.service.js +140 -0
- package/dist/mail-delivery/mail-delivery.service.js.map +1 -0
- package/dist/mail-delivery/ses-event.parser.d.ts +27 -0
- package/dist/mail-delivery/ses-event.parser.d.ts.map +1 -0
- package/dist/mail-delivery/ses-event.parser.js +131 -0
- package/dist/mail-delivery/ses-event.parser.js.map +1 -0
- package/dist/mail-delivery/ses-webhook.service.d.ts +58 -0
- package/dist/mail-delivery/ses-webhook.service.d.ts.map +1 -0
- package/dist/mail-delivery/ses-webhook.service.js +214 -0
- package/dist/mail-delivery/ses-webhook.service.js.map +1 -0
- package/dist/mail-delivery/sns-message.util.d.ts +38 -0
- package/dist/mail-delivery/sns-message.util.d.ts.map +1 -0
- package/dist/mail-delivery/sns-message.util.js +109 -0
- package/dist/mail-delivery/sns-message.util.js.map +1 -0
- package/dist/mail-sent/dto/query-mail-sent-list.dto.d.ts +5 -0
- package/dist/mail-sent/dto/query-mail-sent-list.dto.d.ts.map +1 -1
- package/dist/mail-sent/dto/query-mail-sent-list.dto.js +6 -0
- package/dist/mail-sent/dto/query-mail-sent-list.dto.js.map +1 -1
- package/dist/mail-sent/mail-sent.controller.d.ts +39 -0
- package/dist/mail-sent/mail-sent.controller.d.ts.map +1 -1
- package/dist/mail-sent/mail-sent.service.d.ts +39 -0
- package/dist/mail-sent/mail-sent.service.d.ts.map +1 -1
- package/dist/mail-sent/mail-sent.service.js +45 -4
- package/dist/mail-sent/mail-sent.service.js.map +1 -1
- package/dist/mailbox/mailbox-fetcher.registry.d.ts +100 -0
- package/dist/mailbox/mailbox-fetcher.registry.d.ts.map +1 -0
- package/dist/mailbox/mailbox-fetcher.registry.js +59 -0
- package/dist/mailbox/mailbox-fetcher.registry.js.map +1 -0
- package/dist/mailbox/mailbox.module.d.ts +8 -0
- package/dist/mailbox/mailbox.module.d.ts.map +1 -0
- package/dist/mailbox/mailbox.module.js +27 -0
- package/dist/mailbox/mailbox.module.js.map +1 -0
- package/dist/meeting/meeting-provider.registry.d.ts +155 -0
- package/dist/meeting/meeting-provider.registry.d.ts.map +1 -0
- package/dist/meeting/meeting-provider.registry.js +53 -0
- package/dist/meeting/meeting-provider.registry.js.map +1 -0
- package/dist/meeting/meeting.constants.d.ts +47 -0
- package/dist/meeting/meeting.constants.d.ts.map +1 -0
- package/dist/meeting/meeting.constants.js +21 -0
- package/dist/meeting/meeting.constants.js.map +1 -0
- package/dist/meeting/meeting.module.d.ts +7 -0
- package/dist/meeting/meeting.module.d.ts.map +1 -0
- package/dist/meeting/meeting.module.js +26 -0
- package/dist/meeting/meeting.module.js.map +1 -0
- package/dist/middlewares/locale-injection.middleware.d.ts +12 -0
- package/dist/middlewares/locale-injection.middleware.d.ts.map +1 -1
- package/dist/middlewares/locale-injection.middleware.js +20 -4
- package/dist/middlewares/locale-injection.middleware.js.map +1 -1
- package/dist/oauth/providers/google.provider.d.ts +7 -0
- package/dist/oauth/providers/google.provider.d.ts.map +1 -1
- package/dist/oauth/providers/google.provider.js +43 -13
- package/dist/oauth/providers/google.provider.js.map +1 -1
- package/dist/observability/monitored-cron.d.ts +34 -0
- package/dist/observability/monitored-cron.d.ts.map +1 -0
- package/dist/observability/monitored-cron.js +118 -0
- package/dist/observability/monitored-cron.js.map +1 -0
- package/dist/pairing/dto/approve-reverse-pairing.dto.d.ts +12 -0
- package/dist/pairing/dto/approve-reverse-pairing.dto.d.ts.map +1 -0
- package/dist/pairing/dto/approve-reverse-pairing.dto.js +30 -0
- package/dist/pairing/dto/approve-reverse-pairing.dto.js.map +1 -0
- package/dist/pairing/dto/claim-pairing.dto.d.ts +11 -0
- package/dist/pairing/dto/claim-pairing.dto.d.ts.map +1 -0
- package/dist/pairing/dto/claim-pairing.dto.js +35 -0
- package/dist/pairing/dto/claim-pairing.dto.js.map +1 -0
- package/dist/pairing/dto/confirm-pairing.dto.d.ts +10 -0
- package/dist/pairing/dto/confirm-pairing.dto.d.ts.map +1 -0
- package/dist/pairing/dto/confirm-pairing.dto.js +24 -0
- package/dist/pairing/dto/confirm-pairing.dto.js.map +1 -0
- package/dist/pairing/dto/poll-reverse-pairing.dto.d.ts +5 -0
- package/dist/pairing/dto/poll-reverse-pairing.dto.d.ts.map +1 -0
- package/dist/pairing/dto/poll-reverse-pairing.dto.js +24 -0
- package/dist/pairing/dto/poll-reverse-pairing.dto.js.map +1 -0
- package/dist/pairing/dto/start-pairing.dto.d.ts +11 -0
- package/dist/pairing/dto/start-pairing.dto.d.ts.map +1 -0
- package/dist/pairing/dto/start-pairing.dto.js +30 -0
- package/dist/pairing/dto/start-pairing.dto.js.map +1 -0
- package/dist/pairing/dto/start-reverse-pairing.dto.d.ts +24 -0
- package/dist/pairing/dto/start-reverse-pairing.dto.d.ts.map +1 -0
- package/dist/pairing/dto/start-reverse-pairing.dto.js +47 -0
- package/dist/pairing/dto/start-reverse-pairing.dto.js.map +1 -0
- package/dist/pairing/pairing.controller.d.ts +118 -0
- package/dist/pairing/pairing.controller.d.ts.map +1 -0
- package/dist/pairing/pairing.controller.js +296 -0
- package/dist/pairing/pairing.controller.js.map +1 -0
- package/dist/pairing/pairing.module.d.ts +10 -0
- package/dist/pairing/pairing.module.d.ts.map +1 -0
- package/dist/pairing/pairing.module.js +40 -0
- package/dist/pairing/pairing.module.js.map +1 -0
- package/dist/pairing/pairing.service.d.ts +238 -0
- package/dist/pairing/pairing.service.d.ts.map +1 -0
- package/dist/pairing/pairing.service.js +495 -0
- package/dist/pairing/pairing.service.js.map +1 -0
- package/dist/pdf/pdf.service.d.ts +3 -6
- package/dist/pdf/pdf.service.d.ts.map +1 -1
- package/dist/pdf/pdf.service.js +8 -20
- package/dist/pdf/pdf.service.js.map +1 -1
- package/dist/profile/profile.controller.d.ts +11 -0
- package/dist/profile/profile.controller.d.ts.map +1 -1
- package/dist/profile/profile.service.d.ts +11 -0
- package/dist/profile/profile.service.d.ts.map +1 -1
- package/dist/profile/profile.service.js +20 -11
- package/dist/profile/profile.service.js.map +1 -1
- package/dist/redis/redis-pubsub.d.ts.map +1 -1
- package/dist/redis/redis-pubsub.js +44 -0
- package/dist/redis/redis-pubsub.js.map +1 -1
- package/dist/role/guards/role.guard.d.ts +5 -0
- package/dist/role/guards/role.guard.d.ts.map +1 -1
- package/dist/role/guards/role.guard.js +41 -39
- package/dist/role/guards/role.guard.js.map +1 -1
- package/dist/session/session.controller.d.ts +2 -0
- package/dist/session/session.controller.d.ts.map +1 -1
- package/dist/session/session.service.d.ts +39 -0
- package/dist/session/session.service.d.ts.map +1 -1
- package/dist/session/session.service.js +91 -8
- package/dist/session/session.service.js.map +1 -1
- package/dist/setting/brand-asset.service.d.ts +48 -0
- package/dist/setting/brand-asset.service.d.ts.map +1 -0
- package/dist/setting/brand-asset.service.js +130 -0
- package/dist/setting/brand-asset.service.js.map +1 -0
- package/dist/setting/setting.module.d.ts.map +1 -1
- package/dist/setting/setting.module.js +3 -2
- package/dist/setting/setting.module.js.map +1 -1
- package/dist/setting/setting.service.d.ts +33 -0
- package/dist/setting/setting.service.d.ts.map +1 -1
- package/dist/setting/setting.service.js +88 -0
- package/dist/setting/setting.service.js.map +1 -1
- package/dist/token/token.service.d.ts +23 -2
- package/dist/token/token.service.d.ts.map +1 -1
- package/dist/token/token.service.js +39 -4
- package/dist/token/token.service.js.map +1 -1
- package/dist/user/user.controller.d.ts +8 -22
- package/dist/user/user.controller.d.ts.map +1 -1
- package/dist/user/user.service.d.ts +44 -24
- package/dist/user/user.service.d.ts.map +1 -1
- package/dist/user/user.service.js +89 -6
- package/dist/user/user.service.js.map +1 -1
- package/dist/user-device/user-device.controller.d.ts +54 -0
- package/dist/user-device/user-device.controller.d.ts.map +1 -0
- package/dist/user-device/user-device.controller.js +103 -0
- package/dist/user-device/user-device.controller.js.map +1 -0
- package/dist/user-device/user-device.module.d.ts +3 -0
- package/dist/user-device/user-device.module.d.ts.map +1 -0
- package/dist/user-device/user-device.module.js +26 -0
- package/dist/user-device/user-device.module.js.map +1 -0
- package/dist/user-device/user-device.service.d.ts +71 -0
- package/dist/user-device/user-device.service.d.ts.map +1 -0
- package/dist/user-device/user-device.service.js +158 -0
- package/dist/user-device/user-device.service.js.map +1 -0
- package/dist/utils/device-key.d.ts +23 -0
- package/dist/utils/device-key.d.ts.map +1 -0
- package/dist/utils/device-key.js +31 -0
- package/dist/utils/device-key.js.map +1 -0
- package/dist/utils/geo-ip.d.ts +38 -0
- package/dist/utils/geo-ip.d.ts.map +1 -0
- package/dist/utils/geo-ip.js +122 -0
- package/dist/utils/geo-ip.js.map +1 -0
- package/dist/utils/locale-context.d.ts +10 -7
- package/dist/utils/locale-context.d.ts.map +1 -1
- package/dist/utils/locale-context.js +14 -8
- package/dist/utils/locale-context.js.map +1 -1
- package/dist/utils/security-event-vars.d.ts +56 -0
- package/dist/utils/security-event-vars.d.ts.map +1 -0
- package/dist/utils/security-event-vars.js +109 -0
- package/dist/utils/security-event-vars.js.map +1 -0
- package/dist/utils/user-agent.d.ts +19 -0
- package/dist/utils/user-agent.d.ts.map +1 -0
- package/dist/utils/user-agent.js +36 -0
- package/dist/utils/user-agent.js.map +1 -0
- package/dist/webhook-integration/webhook-integration.service.d.ts +5 -0
- package/dist/webhook-integration/webhook-integration.service.d.ts.map +1 -1
- package/dist/webhook-integration/webhook-integration.service.js +50 -4
- package/dist/webhook-integration/webhook-integration.service.js.map +1 -1
- package/dist/webhook-integration/webhook-protocol.registry.d.ts +6 -0
- package/dist/webhook-integration/webhook-protocol.registry.d.ts.map +1 -1
- package/dist/webhook-integration/webhook-protocol.registry.js.map +1 -1
- package/hedhog/data/integration_provider.yaml +9 -0
- package/hedhog/data/mail.yaml +198 -62
- package/hedhog/data/mail_layout.yaml +43 -33
- package/hedhog/data/menu.yaml +2 -0
- package/hedhog/data/role.yaml +16 -0
- package/hedhog/data/route.yaml +172 -3
- package/hedhog/data/setting_group.yaml +178 -2
- package/hedhog/data/setting_subgroup.yaml +8 -0
- package/hedhog/frontend/app/_lib/hooks/use-core-access.test.tsx.ejs +125 -0
- package/hedhog/frontend/app/_lib/hooks/use-core-access.ts.ejs +29 -0
- package/hedhog/frontend/app/_lib/hooks/use-impersonation-access.test.ts.ejs +71 -0
- package/hedhog/frontend/app/_lib/hooks/use-impersonation-access.ts.ejs +32 -0
- package/hedhog/frontend/app/access-log/page.tsx.ejs +96 -22
- package/hedhog/frontend/app/account/components/active-sessions.tsx.ejs +17 -1
- package/hedhog/frontend/app/account/components/known-devices.tsx.ejs +89 -0
- package/hedhog/frontend/app/account/components/profile-form.tsx.ejs +2 -1
- package/hedhog/frontend/app/account/components/user-devices-list.tsx.ejs +186 -0
- package/hedhog/frontend/app/account/devices/page.tsx.ejs +5 -0
- package/hedhog/frontend/app/account/layout.tsx.ejs +2 -0
- package/hedhog/frontend/app/configurations/[slug]/components/setting-field.test.tsx.ejs +298 -0
- package/hedhog/frontend/app/configurations/[slug]/components/setting-field.tsx.ejs +376 -1236
- package/hedhog/frontend/app/mail/log/page.tsx.ejs +210 -3
- package/hedhog/frontend/app/mail/template/page.tsx.ejs +190 -132
- package/hedhog/frontend/app/users/active-session.tsx.ejs +58 -11
- package/hedhog/frontend/app/users/page.tsx.ejs +69 -10
- package/hedhog/frontend/app/users/simulate-access-dialog.test.tsx.ejs +202 -0
- package/hedhog/frontend/app/users/simulate-access-dialog.tsx.ejs +170 -0
- package/hedhog/frontend/app/users/user-activity-timeline.tsx.ejs +19 -0
- package/hedhog/frontend/app/users/user-devices.tsx.ejs +89 -0
- package/hedhog/frontend/app/users/user-logs.tsx.ejs +13 -1
- package/hedhog/frontend/app/users/user-sheet.tsx.ejs +46 -24
- package/hedhog/frontend/messages/en.json +146 -48
- package/hedhog/frontend/messages/pt.json +146 -48
- package/hedhog/table/access_log.yaml +7 -0
- package/hedhog/table/file.yaml +8 -0
- package/hedhog/table/mail_delivery_event.yaml +76 -0
- package/hedhog/table/mail_sent.yaml +23 -0
- package/hedhog/table/user_activity.yaml +9 -0
- package/hedhog/table/user_device.yaml +52 -0
- package/hedhog/table/user_session.yaml +20 -1
- package/package.json +6 -5
- package/src/access-log/access-log.controller.ts +1 -0
- package/src/access-log/access-log.interceptor.ts +5 -1
- package/src/access-log/access-log.service.spec.ts +33 -2
- package/src/access-log/access-log.service.ts +38 -0
- package/src/access-log/dto/query-access-log.dto.ts +5 -0
- package/src/ai/ai.controller.ts +9 -0
- package/src/ai/ai.module.ts +3 -2
- package/src/ai/ai.service.ts +150 -13
- package/src/ai/dto/richtext-edit-html.dto.ts +11 -0
- package/src/ai/richtext-ai.prompts.ts +57 -0
- package/src/ai/richtext-ai.service.ts +109 -0
- package/src/auth/auth.controller.ts +47 -8
- package/src/auth/auth.module.ts +2 -0
- package/src/auth/auth.service.spec.ts +383 -6
- package/src/auth/auth.service.ts +345 -56
- package/src/auth/dto/confirm-email.dto.ts +7 -0
- package/src/auth/dto/resend-confirmation.dto.ts +20 -0
- package/src/authorize/authorize.controller.ts +2 -0
- package/src/authorize/authorize.service.spec.ts +23 -0
- package/src/authorize/authorize.service.ts +8 -0
- package/src/challenge/challenge.service.spec.ts +73 -0
- package/src/challenge/challenge.service.ts +62 -3
- package/src/core.module.ts +19 -0
- package/src/file/file.controller.ts +54 -4
- package/src/file/file.service.spec.ts +275 -105
- package/src/file/file.service.ts +409 -14
- package/src/file/image-optimization.service.spec.ts +92 -0
- package/src/file/image-optimization.service.ts +202 -0
- package/src/file/provider/abstract.provider.ts +23 -0
- package/src/file/provider/gcs.provider.ts +17 -0
- package/src/file/provider/local.provider.ts +21 -0
- package/src/file/provider/s3.provider.ts +25 -0
- package/src/file/provider/spaces.provider.ts +26 -0
- package/src/impersonation/dto/redeem-impersonation.dto.ts +8 -0
- package/src/impersonation/dto/start-impersonation.dto.ts +13 -0
- package/src/impersonation/impersonation-restrictions.ts +84 -0
- package/src/impersonation/impersonation.controller.ts +109 -0
- package/src/impersonation/impersonation.module.ts +31 -0
- package/src/impersonation/impersonation.service.spec.ts +427 -0
- package/src/impersonation/impersonation.service.ts +577 -0
- package/src/index.ts +21 -0
- package/src/integration/services/outbox-polling.coordinator.spec.ts +90 -0
- package/src/integration/services/outbox-polling.coordinator.ts +62 -40
- package/src/integration/services/outbox.service.spec.ts +8 -0
- package/src/integration/services/outbox.service.ts +19 -13
- package/src/integration-profile/integration-profile.secrets.ts +3 -0
- package/src/integration-profile/integration-profile.utils.ts +38 -0
- package/src/integration-profile/testers/email.testers.ts +34 -1
- package/src/language/en.json +11 -0
- package/src/language/pt.json +11 -0
- package/src/mail/dto/send.dto.ts +46 -2
- package/src/mail/mail-template.spec.ts +79 -2
- package/src/mail/mail.module.ts +2 -0
- package/src/mail/mail.service.spec.ts +307 -6
- package/src/mail/mail.service.ts +227 -30
- package/src/mail-delivery/mail-delivery-provision.service.ts +66 -0
- package/src/mail-delivery/mail-delivery.constants.ts +31 -0
- package/src/mail-delivery/mail-delivery.controller.ts +38 -0
- package/src/mail-delivery/mail-delivery.module.ts +23 -0
- package/src/mail-delivery/mail-delivery.service.spec.ts +136 -0
- package/src/mail-delivery/mail-delivery.service.ts +152 -0
- package/src/mail-delivery/ses-event.parser.spec.ts +140 -0
- package/src/mail-delivery/ses-event.parser.ts +191 -0
- package/src/mail-delivery/ses-webhook.service.spec.ts +170 -0
- package/src/mail-delivery/ses-webhook.service.ts +240 -0
- package/src/mail-delivery/sns-message.util.spec.ts +163 -0
- package/src/mail-delivery/sns-message.util.ts +125 -0
- package/src/mail-sent/dto/query-mail-sent-list.dto.ts +15 -0
- package/src/mail-sent/mail-sent.service.ts +46 -2
- package/src/mailbox/mailbox-fetcher.registry.ts +132 -0
- package/src/mailbox/mailbox.module.ts +14 -0
- package/src/meeting/meeting-provider.registry.ts +193 -0
- package/src/meeting/meeting.constants.ts +53 -0
- package/src/meeting/meeting.module.ts +13 -0
- package/src/middlewares/locale-injection.middleware.ts +25 -6
- package/src/oauth/providers/google.provider.ts +49 -15
- package/src/observability/monitored-cron.spec.ts +102 -0
- package/src/observability/monitored-cron.ts +110 -0
- package/src/pairing/dto/approve-reverse-pairing.dto.ts +20 -0
- package/src/pairing/dto/claim-pairing.dto.ts +19 -0
- package/src/pairing/dto/confirm-pairing.dto.ts +14 -0
- package/src/pairing/dto/poll-reverse-pairing.dto.ts +9 -0
- package/src/pairing/dto/start-pairing.dto.ts +19 -0
- package/src/pairing/dto/start-reverse-pairing.dto.ts +45 -0
- package/src/pairing/pairing.controller.ts +246 -0
- package/src/pairing/pairing.module.ts +27 -0
- package/src/pairing/pairing.service.spec.ts +462 -0
- package/src/pairing/pairing.service.ts +614 -0
- package/src/pdf/pdf.service.ts +6 -24
- package/src/profile/profile.service.ts +26 -8
- package/src/redis/redis-pubsub.ts +11 -0
- package/src/role/guards/role.guard.spec.ts +75 -0
- package/src/role/guards/role.guard.ts +64 -46
- package/src/session/session.service.spec.ts +81 -0
- package/src/session/session.service.ts +93 -6
- package/src/setting/brand-asset.service.ts +137 -0
- package/src/setting/setting.module.ts +3 -2
- package/src/setting/setting.service.spec.ts +79 -1
- package/src/setting/setting.service.ts +109 -1
- package/src/token/token.service.spec.ts +62 -0
- package/src/token/token.service.ts +57 -3
- package/src/user/user.service.spec.ts +33 -2
- package/src/user/user.service.ts +108 -6
- package/src/user-device/user-device.controller.ts +77 -0
- package/src/user-device/user-device.module.ts +13 -0
- package/src/user-device/user-device.service.spec.ts +234 -0
- package/src/user-device/user-device.service.ts +168 -0
- package/src/utils/device-key.spec.ts +57 -0
- package/src/utils/device-key.ts +32 -0
- package/src/utils/geo-ip.spec.ts +134 -0
- package/src/utils/geo-ip.ts +93 -0
- package/src/utils/locale-context.spec.ts +27 -1
- package/src/utils/locale-context.ts +15 -12
- package/src/utils/security-event-vars.spec.ts +161 -0
- package/src/utils/security-event-vars.ts +153 -0
- package/src/utils/user-agent.spec.ts +37 -0
- package/src/utils/user-agent.ts +46 -0
- package/src/webhook-integration/webhook-integration.service.ts +57 -3
- package/src/webhook-integration/webhook-protocol.registry.ts +6 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, jest } from '@jest/globals';
|
|
2
|
+
import { OutboxPollingCoordinator } from './outbox-polling.coordinator';
|
|
3
|
+
|
|
4
|
+
const makeSettings = (over: Partial<Record<string, any>> = {}) => ({
|
|
5
|
+
outboxEnabled: true,
|
|
6
|
+
outboxProcessorEnabled: true,
|
|
7
|
+
pollingIntervalMs: 1000,
|
|
8
|
+
idlePollingIntervalMs: 2000,
|
|
9
|
+
batchSize: 10,
|
|
10
|
+
startupDrainBatchSize: 50,
|
|
11
|
+
maxAttempts: 3,
|
|
12
|
+
processingLeaseMs: 30000,
|
|
13
|
+
retryBaseDelayMs: 1000,
|
|
14
|
+
deadLetterEnabled: true,
|
|
15
|
+
...over,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const makeDeps = () => {
|
|
19
|
+
const processor = { processBatch: jest.fn<any>() };
|
|
20
|
+
const notifier = { subscribe: jest.fn(), unsubscribe: jest.fn() };
|
|
21
|
+
const integrationSettingsService = { getRuntimeSettings: jest.fn<any>() };
|
|
22
|
+
return { processor, notifier, integrationSettingsService };
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
describe('OutboxPollingCoordinator', () => {
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
jest.useFakeTimers();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
afterEach(async () => {
|
|
31
|
+
jest.clearAllMocks();
|
|
32
|
+
jest.useRealTimers();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('não deixa uma falha de uma iteração derrubar o loop (sem unhandled rejection)', async () => {
|
|
36
|
+
const deps = makeDeps();
|
|
37
|
+
deps.integrationSettingsService.getRuntimeSettings.mockResolvedValue(makeSettings());
|
|
38
|
+
deps.processor.processBatch
|
|
39
|
+
.mockRejectedValueOnce(
|
|
40
|
+
new Error('Transaction API error: Unable to start a transaction in the given time.'),
|
|
41
|
+
)
|
|
42
|
+
.mockResolvedValue(0);
|
|
43
|
+
|
|
44
|
+
const coordinator = new OutboxPollingCoordinator(
|
|
45
|
+
deps.processor as any,
|
|
46
|
+
deps.notifier as any,
|
|
47
|
+
deps.integrationSettingsService as any,
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
await coordinator.startProcessingLoop();
|
|
51
|
+
|
|
52
|
+
// 1st iteration throws -> caught, backs off ERROR_BACKOFF_MS (5000ms) instead of crashing
|
|
53
|
+
await jest.advanceTimersByTimeAsync(5000);
|
|
54
|
+
|
|
55
|
+
expect(deps.processor.processBatch).toHaveBeenCalledTimes(2);
|
|
56
|
+
// The loop must still be running — a caught iteration error must not stop/crash it
|
|
57
|
+
expect(coordinator.isRunning()).toBe(true);
|
|
58
|
+
|
|
59
|
+
// Ask it to stop (flips shouldStopLoop synchronously), then let the in-flight idle delay
|
|
60
|
+
// from the 2nd (successful) iteration resolve so the loop can observe the flag and exit
|
|
61
|
+
// cleanly, without running a 3rd iteration.
|
|
62
|
+
const stopPromise = coordinator.stopProcessingLoop();
|
|
63
|
+
await jest.advanceTimersByTimeAsync(2000);
|
|
64
|
+
await stopPromise;
|
|
65
|
+
|
|
66
|
+
expect(coordinator.isRunning()).toBe(false);
|
|
67
|
+
expect(deps.processor.processBatch).toHaveBeenCalledTimes(2);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('não fica preso em "running" para sempre se o loop rejeitar de forma inesperada', async () => {
|
|
71
|
+
const deps = makeDeps();
|
|
72
|
+
// Simulate a failure before the inner try/catch can even run (e.g. notifier.subscribe throwing)
|
|
73
|
+
deps.notifier.subscribe.mockImplementation(() => {
|
|
74
|
+
throw new Error('boom');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const coordinator = new OutboxPollingCoordinator(
|
|
78
|
+
deps.processor as any,
|
|
79
|
+
deps.notifier as any,
|
|
80
|
+
deps.integrationSettingsService as any,
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
await coordinator.startProcessingLoop();
|
|
84
|
+
// Let the rejected promise's .catch() handler run
|
|
85
|
+
await Promise.resolve();
|
|
86
|
+
await Promise.resolve();
|
|
87
|
+
|
|
88
|
+
expect(coordinator.isRunning()).toBe(false);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
@@ -11,6 +11,8 @@ import { OutboxProcessor } from './outbox.processor';
|
|
|
11
11
|
@Injectable()
|
|
12
12
|
export class OutboxPollingCoordinator {
|
|
13
13
|
private readonly logger = new Logger(OutboxPollingCoordinator.name);
|
|
14
|
+
/** Fallback backoff used when a loop iteration throws before settings can be trusted */
|
|
15
|
+
private static readonly ERROR_BACKOFF_MS = 5000;
|
|
14
16
|
private processingLoopRunning = false;
|
|
15
17
|
private processingLoopPromise: Promise<void> | null = null;
|
|
16
18
|
private immediateWakeupTriggered = false;
|
|
@@ -38,7 +40,14 @@ export class OutboxPollingCoordinator {
|
|
|
38
40
|
this.shouldStopLoop = false;
|
|
39
41
|
this.logger.log('Starting outbox hybrid processing loop');
|
|
40
42
|
|
|
41
|
-
|
|
43
|
+
// Defense-in-depth: runProcessingLoop() catches errors internally and should never reject,
|
|
44
|
+
// but if it somehow does, don't let it become an unhandled rejection (which kills the pod —
|
|
45
|
+
// see Sentry.onUnhandledRejectionIntegration({ mode: 'strict' }) in apps/api/src/instrument.ts)
|
|
46
|
+
// and don't leave the flag stuck as "running" with no way to recover.
|
|
47
|
+
this.processingLoopPromise = this.runProcessingLoop().catch((error) => {
|
|
48
|
+
this.logger.error('[outbox:loop] processing loop crashed unexpectedly', error);
|
|
49
|
+
this.processingLoopRunning = false;
|
|
50
|
+
});
|
|
42
51
|
}
|
|
43
52
|
|
|
44
53
|
/**
|
|
@@ -94,47 +103,60 @@ export class OutboxPollingCoordinator {
|
|
|
94
103
|
|
|
95
104
|
try {
|
|
96
105
|
while (!this.shouldStopLoop) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
106
|
+
try {
|
|
107
|
+
const settings = await this.integrationSettingsService.getRuntimeSettings();
|
|
108
|
+
const isEnabled = settings.outboxEnabled;
|
|
109
|
+
const processorEnabled = settings.outboxProcessorEnabled;
|
|
110
|
+
|
|
111
|
+
if (!isEnabled || !processorEnabled) {
|
|
112
|
+
this.logger.debug(
|
|
113
|
+
`[outbox:loop] processor disabled (outboxEnabled=${isEnabled} processorEnabled=${processorEnabled}), sleeping ${settings.idlePollingIntervalMs}ms`,
|
|
114
|
+
);
|
|
115
|
+
await this.delay(settings.idlePollingIntervalMs);
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Check if we should process immediately (woken by notification)
|
|
120
|
+
const shouldProcessImmediately = this.immediateWakeupTriggered;
|
|
121
|
+
this.immediateWakeupTriggered = false;
|
|
122
|
+
|
|
123
|
+
// Process batch
|
|
124
|
+
const processedCount = await this.processor.processBatch();
|
|
125
|
+
|
|
126
|
+
// Determine next polling interval
|
|
127
|
+
let nextDelayMs: number;
|
|
128
|
+
|
|
129
|
+
if (shouldProcessImmediately && processedCount > 0) {
|
|
130
|
+
// We processed items from immediate wakeup - use active polling interval
|
|
131
|
+
nextDelayMs = settings.pollingIntervalMs;
|
|
132
|
+
this.logger.debug(
|
|
133
|
+
`Processed ${processedCount} events from immediate wakeup, next poll in ${nextDelayMs}ms`,
|
|
134
|
+
);
|
|
135
|
+
} else if (processedCount > 0) {
|
|
136
|
+
// Processed items from periodic polling - use active interval
|
|
137
|
+
nextDelayMs = settings.pollingIntervalMs;
|
|
138
|
+
this.logger.debug(
|
|
139
|
+
`Processed ${processedCount} events, next poll in ${nextDelayMs}ms`,
|
|
140
|
+
);
|
|
141
|
+
} else {
|
|
142
|
+
// No items processed - use idle interval
|
|
143
|
+
nextDelayMs = settings.idlePollingIntervalMs;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Wait for next poll, but be ready for immediate wakeup
|
|
147
|
+
await this.delay(nextDelayMs);
|
|
148
|
+
} catch (error) {
|
|
149
|
+
// A single iteration failing (e.g. a transient DB/transaction error) must never escape
|
|
150
|
+
// the loop — that would reject processingLoopPromise as an unhandled rejection and, per
|
|
151
|
+
// Sentry's strict unhandled-rejection mode, take down the whole pod. Log, back off, and
|
|
152
|
+
// keep polling instead. Don't trust `settings` here — the error may have come from
|
|
153
|
+
// fetching it — so use a fixed fallback delay.
|
|
154
|
+
this.logger.error(
|
|
155
|
+
'[outbox:loop] iteration failed, backing off before retrying',
|
|
156
|
+
error,
|
|
124
157
|
);
|
|
125
|
-
|
|
126
|
-
// Processed items from periodic polling - use active interval
|
|
127
|
-
nextDelayMs = settings.pollingIntervalMs;
|
|
128
|
-
this.logger.debug(
|
|
129
|
-
`Processed ${processedCount} events, next poll in ${nextDelayMs}ms`,
|
|
130
|
-
);
|
|
131
|
-
} else {
|
|
132
|
-
// No items processed - use idle interval
|
|
133
|
-
nextDelayMs = settings.idlePollingIntervalMs;
|
|
158
|
+
await this.delay(OutboxPollingCoordinator.ERROR_BACKOFF_MS);
|
|
134
159
|
}
|
|
135
|
-
|
|
136
|
-
// Wait for next poll, but be ready for immediate wakeup
|
|
137
|
-
await this.delay(nextDelayMs);
|
|
138
160
|
}
|
|
139
161
|
} finally {
|
|
140
162
|
// Cleanup: unsubscribe from notifications
|
|
@@ -156,6 +156,10 @@ describe('OutboxService', () => {
|
|
|
156
156
|
|
|
157
157
|
expect(result).toEqual([]);
|
|
158
158
|
expect(tx.$executeRaw).not.toHaveBeenCalled();
|
|
159
|
+
expect(deps.prisma.$transaction).toHaveBeenCalledWith(
|
|
160
|
+
expect.any(Function),
|
|
161
|
+
{ maxWait: 5000, timeout: 5000 },
|
|
162
|
+
);
|
|
159
163
|
});
|
|
160
164
|
|
|
161
165
|
it('bloqueia linhas encontradas e marca como PROCESSING', async () => {
|
|
@@ -173,6 +177,10 @@ describe('OutboxService', () => {
|
|
|
173
177
|
expect(result).toHaveLength(2);
|
|
174
178
|
expect(result[0].status).toBe(OutboxEventStatus.PROCESSING);
|
|
175
179
|
expect(result[1].status).toBe(OutboxEventStatus.PROCESSING);
|
|
180
|
+
expect(deps.prisma.$transaction).toHaveBeenCalledWith(
|
|
181
|
+
expect.any(Function),
|
|
182
|
+
{ maxWait: 5000, timeout: 5000 },
|
|
183
|
+
);
|
|
176
184
|
});
|
|
177
185
|
});
|
|
178
186
|
|
|
@@ -114,8 +114,9 @@ export class OutboxService {
|
|
|
114
114
|
const leaseThreshold = new Date(now.getTime() - leaseMs);
|
|
115
115
|
const leaseUntil = new Date(now.getTime() + leaseMs);
|
|
116
116
|
|
|
117
|
-
return this.prisma.$transaction(
|
|
118
|
-
|
|
117
|
+
return this.prisma.$transaction(
|
|
118
|
+
async (tx) => {
|
|
119
|
+
const rows = await tx.$queryRaw<OutboxEventRecord[]>`
|
|
119
120
|
SELECT * FROM outbox_event
|
|
120
121
|
WHERE status = 'pending'::"outbox_event_status_e5d55ba10d_enum"
|
|
121
122
|
OR (status = 'processing'::"outbox_event_status_e5d55ba10d_enum"
|
|
@@ -125,10 +126,10 @@ export class OutboxService {
|
|
|
125
126
|
FOR UPDATE SKIP LOCKED
|
|
126
127
|
`;
|
|
127
128
|
|
|
128
|
-
|
|
129
|
+
if (rows.length === 0) return [];
|
|
129
130
|
|
|
130
|
-
|
|
131
|
-
|
|
131
|
+
const ids = rows.map((r) => r.id);
|
|
132
|
+
await tx.$executeRaw`
|
|
132
133
|
UPDATE outbox_event
|
|
133
134
|
SET status = 'processing'::"outbox_event_status_e5d55ba10d_enum",
|
|
134
135
|
available_at = ${leaseUntil},
|
|
@@ -136,14 +137,19 @@ export class OutboxService {
|
|
|
136
137
|
WHERE id = ANY(${ids}::int[])
|
|
137
138
|
`;
|
|
138
139
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
140
|
+
return rows.map((r) =>
|
|
141
|
+
this.toDomainEvent({
|
|
142
|
+
...r,
|
|
143
|
+
status: OutboxEventStatus.PROCESSING,
|
|
144
|
+
available_at: leaseUntil,
|
|
145
|
+
}),
|
|
146
|
+
);
|
|
147
|
+
},
|
|
148
|
+
// Default maxWait (2s) is too tight when the pool is briefly under contention from other
|
|
149
|
+
// long-lived transactions elsewhere in the app; give this short, index-backed query a bit
|
|
150
|
+
// more slack to acquire a connection before giving up.
|
|
151
|
+
{ maxWait: 5000, timeout: 5000 },
|
|
152
|
+
);
|
|
147
153
|
}
|
|
148
154
|
|
|
149
155
|
/**
|
|
@@ -20,6 +20,9 @@ export const INTEGRATION_SECRET_KEYS: Record<string, string[]> = {
|
|
|
20
20
|
gmail: ['client_secret', 'refresh_token'],
|
|
21
21
|
ses: ['secret_access_key'],
|
|
22
22
|
resend: ['api_token', 'webhook_secret'],
|
|
23
|
+
// IMAP e um perfil de RECEBIMENTO: a senha (ou senha de app, no Google
|
|
24
|
+
// Workspace e no M365) da acesso de leitura a caixa inteira.
|
|
25
|
+
imap: ['password'],
|
|
23
26
|
// Mensageria
|
|
24
27
|
'evolution-api': ['token'],
|
|
25
28
|
// app_secret assina o HMAC do webhook (X-Hub-Signature-256) e verify_token é
|
|
@@ -50,11 +50,49 @@ export function buildMailConfigFromIntegration(
|
|
|
50
50
|
region: String(cfg.region ?? ''),
|
|
51
51
|
from: String(cfg.from_email ?? ''),
|
|
52
52
|
};
|
|
53
|
+
case 'imap':
|
|
54
|
+
// Sem este caso o operador que apontasse o perfil de envio para uma caixa
|
|
55
|
+
// IMAP receberia "Unsupported mail provider: imap" e ficaria procurando um
|
|
56
|
+
// provedor que na verdade existe — so nao serve para enviar.
|
|
57
|
+
throw new BadRequestException(
|
|
58
|
+
'IMAP profiles are inbound-only and cannot be used to send mail. ' +
|
|
59
|
+
'Choose an SMTP, Gmail, SES or Resend profile instead.',
|
|
60
|
+
);
|
|
53
61
|
default:
|
|
54
62
|
throw new BadRequestException(`Unsupported mail provider: ${providerSlug}`);
|
|
55
63
|
}
|
|
56
64
|
}
|
|
57
65
|
|
|
66
|
+
export type ImapIntegrationConfig = {
|
|
67
|
+
host: string;
|
|
68
|
+
port: number;
|
|
69
|
+
secure: boolean;
|
|
70
|
+
username: string;
|
|
71
|
+
password: string;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Mapeia o config de um perfil `imap` para as credenciais de leitura da caixa.
|
|
76
|
+
*
|
|
77
|
+
* `secure: true` e IMAPS direto na 993 (o normal); `false` e conexao limpa na
|
|
78
|
+
* 143 com STARTTLS. O padrao 993/true cobre Gmail, Microsoft 365, Zoho e cPanel.
|
|
79
|
+
*/
|
|
80
|
+
export function buildImapConfigFromIntegration(
|
|
81
|
+
config: unknown,
|
|
82
|
+
): ImapIntegrationConfig {
|
|
83
|
+
const cfg = (config as Cfg) ?? {};
|
|
84
|
+
const port = Number(cfg.port ?? 993);
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
host: String(cfg.host ?? '').trim(),
|
|
88
|
+
port: Number.isFinite(port) && port > 0 ? port : 993,
|
|
89
|
+
// Só quem explicitamente marcou 143/STARTTLS sai do TLS direto.
|
|
90
|
+
secure: cfg.secure === undefined ? true : Boolean(cfg.secure),
|
|
91
|
+
username: String(cfg.username ?? '').trim(),
|
|
92
|
+
password: String(cfg.password ?? ''),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
58
96
|
export type ResendIntegrationConfig = {
|
|
59
97
|
apiToken: string;
|
|
60
98
|
domain: string;
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
verifyImapConnection,
|
|
3
|
+
verifySesCredentials,
|
|
4
|
+
verifySmtpConnection,
|
|
5
|
+
} from '@hed-hog/api-mail';
|
|
2
6
|
import {
|
|
3
7
|
describeHttpError,
|
|
4
8
|
fail,
|
|
@@ -153,9 +157,38 @@ const resendTester: IntegrationTester = {
|
|
|
153
157
|
},
|
|
154
158
|
};
|
|
155
159
|
|
|
160
|
+
/**
|
|
161
|
+
* IMAP é o único provider de e-mail que não envia — ele lê uma caixa. Por isso
|
|
162
|
+
* é o único que não exige `from_email`.
|
|
163
|
+
*/
|
|
164
|
+
const imapTester: IntegrationTester = {
|
|
165
|
+
providerSlug: 'imap',
|
|
166
|
+
test: async (config) => {
|
|
167
|
+
const missing = requireFields(config, [
|
|
168
|
+
['host', 'IMAP host'],
|
|
169
|
+
['username', 'Username'],
|
|
170
|
+
['password', 'Password'],
|
|
171
|
+
]);
|
|
172
|
+
if (missing) return missing;
|
|
173
|
+
|
|
174
|
+
const result = await verifyImapConnection({
|
|
175
|
+
host: str(config, 'host'),
|
|
176
|
+
port: Number(config.port ?? 993),
|
|
177
|
+
secure: config.secure === undefined ? true : Boolean(config.secure),
|
|
178
|
+
username: str(config, 'username'),
|
|
179
|
+
password: String(config.password ?? ''),
|
|
180
|
+
folder: str(config, 'folder') || 'INBOX',
|
|
181
|
+
timeoutMs: 10_000,
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
return result.ok ? pass(result.info) : fail(result.error!);
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
|
|
156
188
|
export const EMAIL_TESTERS: IntegrationTester[] = [
|
|
157
189
|
smtpTester,
|
|
158
190
|
sesTester,
|
|
159
191
|
gmailTester,
|
|
160
192
|
resendTester,
|
|
193
|
+
imapTester,
|
|
161
194
|
];
|
package/src/language/en.json
CHANGED
|
@@ -171,6 +171,17 @@
|
|
|
171
171
|
"not_found": "Mail template with id {id} not found."
|
|
172
172
|
}
|
|
173
173
|
},
|
|
174
|
+
"oauth": {
|
|
175
|
+
"callback": {
|
|
176
|
+
"codeRequired": "Authorization code is required to complete OAuth authentication.",
|
|
177
|
+
"invalidState": "OAuth callback state is invalid or expired. Start the sign-in flow again.",
|
|
178
|
+
"invalidType": "Unsupported OAuth callback type.",
|
|
179
|
+
"invalid": "OAuth callback is invalid or expired. Start the sign-in flow again.",
|
|
180
|
+
"alreadyProcessed": "This OAuth callback was already processed. Start the sign-in flow again.",
|
|
181
|
+
"inProgress": "This OAuth callback is already being processed. Wait a moment and try again.",
|
|
182
|
+
"unavailable": "Unable to complete OAuth authentication right now. Please try again."
|
|
183
|
+
}
|
|
184
|
+
},
|
|
174
185
|
"mail_sent_not_found": "Mail sent record not found.",
|
|
175
186
|
"validationSslugMustBeString": "Slug must be a string.",
|
|
176
187
|
"sessionRevoked": "Session has been revoked or expired.",
|
package/src/language/pt.json
CHANGED
|
@@ -171,6 +171,17 @@
|
|
|
171
171
|
"not_found": "Modelo de e-mail com id {id} não encontrado."
|
|
172
172
|
}
|
|
173
173
|
},
|
|
174
|
+
"oauth": {
|
|
175
|
+
"callback": {
|
|
176
|
+
"codeRequired": "O código de autorização é obrigatório para concluir a autenticação.",
|
|
177
|
+
"invalidState": "O retorno da autenticação é inválido ou expirou. Inicie o login novamente.",
|
|
178
|
+
"invalidType": "Tipo de retorno de autenticação não suportado.",
|
|
179
|
+
"invalid": "O retorno da autenticação é inválido ou expirou. Inicie o login novamente.",
|
|
180
|
+
"alreadyProcessed": "Este retorno de autenticação já foi processado. Inicie o login novamente.",
|
|
181
|
+
"inProgress": "Este retorno de autenticação já está sendo processado. Aguarde um instante e tente novamente.",
|
|
182
|
+
"unavailable": "Não foi possível concluir a autenticação agora. Tente novamente."
|
|
183
|
+
}
|
|
184
|
+
},
|
|
174
185
|
"mail_sent_not_found": "Registro de e-mail enviado não encontrado.",
|
|
175
186
|
"validationSslugMustBeString": "O slug deve ser uma string.",
|
|
176
187
|
"sessionRevoked": "Sessão foi revogada ou expirou.",
|
package/src/mail/dto/send.dto.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getLocaleText } from '@hed-hog/api-locale';
|
|
2
|
-
import { IsNotEmpty, IsObject, IsOptional, IsString } from 'class-validator';
|
|
2
|
+
import { IsNotEmpty, IsNumber, IsObject, IsOptional, IsString } from 'class-validator';
|
|
3
3
|
|
|
4
4
|
export class SendTemplatedMailDTO {
|
|
5
5
|
@IsString({ message: (args) => getLocaleText('validation.stringRequired', args.value) })
|
|
@@ -21,8 +21,52 @@ export class SendTemplatedMailDTO {
|
|
|
21
21
|
@IsString({ message: (args) => getLocaleText('validation.stringRequired', args.value) })
|
|
22
22
|
profileSlug?: string;
|
|
23
23
|
|
|
24
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* Endereco para onde a resposta do destinatario deve voltar.
|
|
26
|
+
*
|
|
27
|
+
* Sem isto a resposta cai no `from` do perfil, que nao carrega nenhuma
|
|
28
|
+
* identificacao do assunto tratado. E o que permite ao SAC endereçar cada
|
|
29
|
+
* chamado (`<protocolo>@dominio-de-resposta`) e reconhecer a resposta na
|
|
30
|
+
* entrada.
|
|
31
|
+
*/
|
|
32
|
+
@IsOptional()
|
|
33
|
+
@IsString({ message: (args) => getLocaleText('validation.stringRequired', args.value) })
|
|
34
|
+
replyTo?: string;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Chave do app de origem (ex.: "class"). Nao afeta mais a marca —
|
|
38
|
+
* `{{brandName}}` vem sempre de `system-name`.
|
|
39
|
+
*/
|
|
25
40
|
@IsOptional()
|
|
26
41
|
@IsString({ message: (args) => getLocaleText('validation.stringRequired', args.value) })
|
|
27
42
|
app?: string;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Destinatario, quando o chamador ja o conhece.
|
|
46
|
+
*
|
|
47
|
+
* Serve so para pular a busca do usuario pelo e-mail na hora de descobrir o
|
|
48
|
+
* idioma de preferencia; omitir nao muda o resultado.
|
|
49
|
+
*/
|
|
50
|
+
@IsOptional()
|
|
51
|
+
@IsNumber({}, { message: (args) => getLocaleText('validation.numberRequired', args.value) })
|
|
52
|
+
userId?: number;
|
|
28
53
|
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Resultado de `MailService.sendTemplatedMail`.
|
|
57
|
+
*
|
|
58
|
+
* `sent: false` cobre tanto o aborto por configuração ausente quanto qualquer
|
|
59
|
+
* falha do provedor: os dois são tratados internamente e não lançam, então o
|
|
60
|
+
* retorno é a única forma de o chamador saber que nada saiu.
|
|
61
|
+
*
|
|
62
|
+
* Deliberadamente um objeto único em vez de união discriminada: as libraries
|
|
63
|
+
* compilam com `strictNullChecks: false`, e nessa configuração o `if (r.sent)`
|
|
64
|
+
* não estreita a união no ponto de uso.
|
|
65
|
+
*/
|
|
66
|
+
export type SendTemplatedMailResult = {
|
|
67
|
+
sent: boolean;
|
|
68
|
+
/** Id devolvido pelo provedor quando `sent` é `true`. */
|
|
69
|
+
providerMessageId?: string | null;
|
|
70
|
+
reason?: 'mail-not-configured' | 'send-failed' | 'template-locale-missing';
|
|
71
|
+
error?: string;
|
|
72
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect } from '@jest/globals';
|
|
2
|
-
import { DEFAULT_MAIL_LAYOUT, renderMailTemplate } from '@hed-hog/api-mail';
|
|
2
|
+
import { DEFAULT_MAIL_LAYOUT, renderMailTemplate, resolveMailAssetUrl } from '@hed-hog/api-mail';
|
|
3
3
|
|
|
4
4
|
// Testes do renderizador de e-mail moram aqui (e nao em packages/api-mail)
|
|
5
5
|
// porque o api-mail nao tem jest configurado; o jest do core resolve o pacote.
|
|
@@ -192,6 +192,83 @@ describe('renderMailTemplate', () => {
|
|
|
192
192
|
expect(DEFAULT_MAIL_LAYOUT.wrapper).toContain('{{{content}}}');
|
|
193
193
|
expect(DEFAULT_MAIL_LAYOUT.wrapper).toContain('{{{header}}}');
|
|
194
194
|
expect(DEFAULT_MAIL_LAYOUT.wrapper).toContain('{{{footer}}}');
|
|
195
|
-
expect(DEFAULT_MAIL_LAYOUT.header).toContain('{{
|
|
195
|
+
expect(DEFAULT_MAIL_LAYOUT.header).toContain('{{{brandMark}}}');
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it('usa o selo de texto com a cor de marca quando nao ha logo', () => {
|
|
199
|
+
const html = renderMailTemplate({
|
|
200
|
+
subject: 'Bem-vindo',
|
|
201
|
+
body: '<p>Ola</p>',
|
|
202
|
+
brandName: 'Acme',
|
|
203
|
+
primaryColor: '#ff760c',
|
|
204
|
+
primaryForeground: '#ffffff',
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
expect(html).toContain('class="brand"');
|
|
208
|
+
expect(html).toContain('background:#ff760c');
|
|
209
|
+
expect(html).toContain('color:#ffffff');
|
|
210
|
+
expect(html).toContain('>Acme<');
|
|
211
|
+
// A cor tambem tempera o acento do painel e os links do corpo.
|
|
212
|
+
expect(html).toContain('background:#ff760c;"></div>');
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('usa a imagem do logo quando configurado, no lugar do selo de texto', () => {
|
|
216
|
+
const html = renderMailTemplate({
|
|
217
|
+
subject: 'Bem-vindo',
|
|
218
|
+
body: '<p>Ola</p>',
|
|
219
|
+
brandName: 'Acme',
|
|
220
|
+
logoUrl: 'https://cdn.example.com/logo.svg',
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
expect(html).toContain('<img src="https://cdn.example.com/logo.svg"');
|
|
224
|
+
expect(html).toContain('alt="Acme"');
|
|
225
|
+
expect(html).not.toContain('class="brand"');
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it('ignora o logo quando a URL nao e publica, caindo no selo de texto', () => {
|
|
229
|
+
// Regressao do bug: com API_URL ausente, `image-url` = `/logo.svg` virava
|
|
230
|
+
// `http://localhost:3100/logo.svg` e o destinatario recebia um <img>
|
|
231
|
+
// quebrado, mostrando so o alt.
|
|
232
|
+
const html = renderMailTemplate({
|
|
233
|
+
subject: 'Bem-vindo',
|
|
234
|
+
body: '<p>Ola</p>',
|
|
235
|
+
brandName: 'Acme',
|
|
236
|
+
logoUrl: 'http://localhost:3100/logo.svg',
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
expect(html).not.toContain('<img');
|
|
240
|
+
expect(html).toContain('class="brand"');
|
|
241
|
+
expect(html).toContain('>Acme<');
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it('cai para as cores padrao quando nenhuma e informada', () => {
|
|
245
|
+
const html = renderMailTemplate({ subject: 'x', body: 'corpo' });
|
|
246
|
+
|
|
247
|
+
expect(html).toContain('#111827');
|
|
248
|
+
expect(html).toContain('#ffffff');
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
describe('resolveMailAssetUrl', () => {
|
|
253
|
+
it('mantem URLs ja absolutas ou data: intactas', () => {
|
|
254
|
+
expect(resolveMailAssetUrl('https://cdn.example.com/logo.svg', 'http://localhost:3100')).toBe(
|
|
255
|
+
'https://cdn.example.com/logo.svg',
|
|
256
|
+
);
|
|
257
|
+
expect(resolveMailAssetUrl('data:image/png;base64,abc', 'http://localhost:3100')).toBe(
|
|
258
|
+
'data:image/png;base64,abc',
|
|
259
|
+
);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it('resolve caminhos relativos contra a base informada', () => {
|
|
263
|
+
expect(resolveMailAssetUrl('/logo.svg', 'http://localhost:3100')).toBe(
|
|
264
|
+
'http://localhost:3100/logo.svg',
|
|
265
|
+
);
|
|
266
|
+
expect(resolveMailAssetUrl('/logo.svg', 'http://localhost:3100/')).toBe(
|
|
267
|
+
'http://localhost:3100/logo.svg',
|
|
268
|
+
);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it('devolve string vazia quando a URL esta vazia', () => {
|
|
272
|
+
expect(resolveMailAssetUrl('', 'http://localhost:3100')).toBe('');
|
|
196
273
|
});
|
|
197
274
|
});
|
package/src/mail/mail.module.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { MailModule as MailMainModule, MailModuleOptions } from '@hed-hog/api-ma
|
|
|
3
3
|
import { PaginationModule } from '@hed-hog/api-pagination';
|
|
4
4
|
import { PrismaModule, PrismaService } from '@hed-hog/api-prisma';
|
|
5
5
|
import { forwardRef, Module } from '@nestjs/common';
|
|
6
|
+
import { ConfigModule } from '@nestjs/config';
|
|
6
7
|
import { MailSentModule } from '../mail-sent/mail-sent.module';
|
|
7
8
|
import { SettingModule } from '../setting/setting.module';
|
|
8
9
|
import { MailController } from './mail.controller';
|
|
@@ -95,6 +96,7 @@ import { MailService } from './mail.service';
|
|
|
95
96
|
forwardRef(() => PaginationModule),
|
|
96
97
|
forwardRef(() => LocaleModule),
|
|
97
98
|
forwardRef(() => SettingModule),
|
|
99
|
+
ConfigModule,
|
|
98
100
|
],
|
|
99
101
|
controllers: [MailController],
|
|
100
102
|
providers: [MailService],
|