@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,102 @@
|
|
|
1
|
+
import { describe, it, expect, jest, beforeEach } from '@jest/globals';
|
|
2
|
+
|
|
3
|
+
const withMonitor = jest.fn((_slug: string, callback: () => any) => callback());
|
|
4
|
+
const startSpan = jest.fn((_opts: any, callback: () => any) => callback());
|
|
5
|
+
const captureException = jest.fn();
|
|
6
|
+
const withScope = jest.fn((cb: (scope: any) => void) => {
|
|
7
|
+
const scope = { setTag: jest.fn() };
|
|
8
|
+
cb(scope);
|
|
9
|
+
return scope;
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
jest.mock('@sentry/nestjs', () => ({ withMonitor, startSpan, captureException, withScope }));
|
|
13
|
+
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
15
|
+
const { runMonitoredCron } = require('./monitored-cron');
|
|
16
|
+
|
|
17
|
+
function fakePrisma(locked: boolean, overrides: { transactionThrows?: boolean } = {}) {
|
|
18
|
+
const queryRaw = jest.fn().mockResolvedValue([{ locked }]);
|
|
19
|
+
const transaction = jest.fn(async (fn: any) => {
|
|
20
|
+
if (overrides.transactionThrows) throw new Error('transaction aborted');
|
|
21
|
+
return fn({ $queryRaw: queryRaw });
|
|
22
|
+
});
|
|
23
|
+
return { $transaction: transaction, $queryRaw: queryRaw };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
describe('runMonitoredCron', () => {
|
|
27
|
+
beforeEach(() => {
|
|
28
|
+
jest.clearAllMocks();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('retorna undefined sem executar work() quando outro pod já ganhou o tick', async () => {
|
|
32
|
+
const prisma = fakePrisma(false);
|
|
33
|
+
const work = jest.fn();
|
|
34
|
+
|
|
35
|
+
const result = await runMonitoredCron(prisma, { slug: 'meu-cron', schedule: '* * * * *' }, work);
|
|
36
|
+
|
|
37
|
+
expect(result).toBeUndefined();
|
|
38
|
+
expect(work).not.toHaveBeenCalled();
|
|
39
|
+
expect(withMonitor).not.toHaveBeenCalled();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('executa work() e devolve o resultado quando ganha o tick', async () => {
|
|
43
|
+
const prisma = fakePrisma(true);
|
|
44
|
+
const work = jest.fn().mockResolvedValue({ ok: true });
|
|
45
|
+
|
|
46
|
+
const result = await runMonitoredCron(prisma, { slug: 'meu-cron', schedule: '* * * * *' }, work);
|
|
47
|
+
|
|
48
|
+
expect(work).toHaveBeenCalledTimes(1);
|
|
49
|
+
expect(withMonitor).toHaveBeenCalledWith(
|
|
50
|
+
'meu-cron',
|
|
51
|
+
expect.any(Function),
|
|
52
|
+
expect.objectContaining({ schedule: { type: 'crontab', value: '* * * * *' }, isolateTrace: true }),
|
|
53
|
+
);
|
|
54
|
+
expect(result).toEqual({ ok: true });
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('NUNCA relança quando work() falha — captura no Sentry e retorna undefined', async () => {
|
|
58
|
+
const prisma = fakePrisma(true);
|
|
59
|
+
const boom = new Error('cron falhou');
|
|
60
|
+
const work = jest.fn().mockRejectedValue(boom);
|
|
61
|
+
|
|
62
|
+
await expect(
|
|
63
|
+
runMonitoredCron(prisma, { slug: 'meu-cron', schedule: '* * * * *' }, work),
|
|
64
|
+
).resolves.toBeUndefined();
|
|
65
|
+
|
|
66
|
+
expect(captureException).toHaveBeenCalledWith(
|
|
67
|
+
boom,
|
|
68
|
+
expect.objectContaining({ mechanism: { handled: true, type: 'hedhog.cron' } }),
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('dimensiona o timeout/maxWait da transação a partir de maxRuntimeMinutes', async () => {
|
|
73
|
+
const prisma = fakePrisma(true);
|
|
74
|
+
const work = jest.fn().mockResolvedValue(undefined);
|
|
75
|
+
|
|
76
|
+
await runMonitoredCron(
|
|
77
|
+
prisma,
|
|
78
|
+
{ slug: 'meu-cron', schedule: '* * * * *', maxRuntimeMinutes: 2 },
|
|
79
|
+
work,
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
expect(prisma.$transaction).toHaveBeenCalledWith(
|
|
83
|
+
expect.any(Function),
|
|
84
|
+
{ timeout: 2 * 60_000 + 30_000, maxWait: 20_000 },
|
|
85
|
+
);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('NUNCA relança quando a transação falha (ex.: pool esgotado) — captura no Sentry e retorna undefined', async () => {
|
|
89
|
+
const prisma = fakePrisma(true, { transactionThrows: true });
|
|
90
|
+
const work = jest.fn();
|
|
91
|
+
|
|
92
|
+
await expect(
|
|
93
|
+
runMonitoredCron(prisma, { slug: 'meu-cron', schedule: '* * * * *' }, work),
|
|
94
|
+
).resolves.toBeUndefined();
|
|
95
|
+
|
|
96
|
+
expect(work).not.toHaveBeenCalled();
|
|
97
|
+
expect(captureException).toHaveBeenCalledWith(
|
|
98
|
+
expect.any(Error),
|
|
99
|
+
expect.objectContaining({ mechanism: { handled: true, type: 'hedhog.cron' } }),
|
|
100
|
+
);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import * as Sentry from '@sentry/nestjs';
|
|
2
|
+
|
|
3
|
+
export type MonitoredCronOptions = {
|
|
4
|
+
/** Slug do monitor no Sentry (Crons). */
|
|
5
|
+
slug: string;
|
|
6
|
+
/** Idêntico ao valor passado ao @Cron do método chamador. */
|
|
7
|
+
schedule: string;
|
|
8
|
+
maxRuntimeMinutes?: number;
|
|
9
|
+
checkinMarginMinutes?: number;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/** Fallback só para dimensionar o timeout da transação (ver abaixo) quando um
|
|
13
|
+
* chamador futuro omitir `maxRuntimeMinutes` — os 5 crons atuais sempre informam. */
|
|
14
|
+
const DEFAULT_MAX_RUNTIME_MINUTES = 5;
|
|
15
|
+
|
|
16
|
+
/** Folga sobre `maxRuntimeMinutes` para o `timeout` da transação: cobre a
|
|
17
|
+
* diferença entre o que o Sentry tolera (`maxRuntime`/`checkinMargin`, que só
|
|
18
|
+
* marcam o check-in como perdido) e o que o Prisma de fato aborta. */
|
|
19
|
+
const TRANSACTION_TIMEOUT_SLACK_MS = 30_000;
|
|
20
|
+
|
|
21
|
+
/** Tempo esperando conexão do pool para abrir a transação — alinhado ao
|
|
22
|
+
* `pool_timeout=20s` que packages/api-prisma/src/datasource-url.ts já aplica
|
|
23
|
+
* quando PRISMA_CONNECTION_LIMIT está setado; o default do Prisma (2s) seria
|
|
24
|
+
* mais apertado que esse teto que o projeto já escolheu deliberadamente. */
|
|
25
|
+
const TRANSACTION_MAX_WAIT_MS = 20_000;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Tenta ganhar o tick. Roda DENTRO da transação de `runMonitoredCron` —
|
|
29
|
+
* reaproveita o MESMO padrão de `pg_try_advisory_xact_lock` já usado em
|
|
30
|
+
* cms-scheduled-publisher.service.ts e class-session-reminder.service.ts, mas
|
|
31
|
+
* aqui generalizado: a variante `xact` só é liberada no commit/rollback DESSA
|
|
32
|
+
* transação, então segurá-la aberta pelo tempo de `work()` é o que de fato
|
|
33
|
+
* garante a exclusão mútua entre pods.
|
|
34
|
+
*/
|
|
35
|
+
async function claimCronTick(tx: any, slug: string): Promise<boolean> {
|
|
36
|
+
const rows: { locked: boolean }[] = await tx.$queryRaw`
|
|
37
|
+
SELECT pg_try_advisory_xact_lock(hashtext(${slug})) AS locked
|
|
38
|
+
`;
|
|
39
|
+
return rows[0]?.locked === true;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Envolve o corpo de um método @Cron com Sentry Cron Monitoring (check-in
|
|
44
|
+
* in_progress/ok/error), garantindo que só o pod que ganhou o tick executa
|
|
45
|
+
* `work` e emite o check-in — sem isso, com N pods rodando o mesmo @Cron
|
|
46
|
+
* simultaneamente, o Sentry recebe 2×N check-ins por execução e estoura o
|
|
47
|
+
* rate limit de 6/min por monitor (ver docs/sentry.md).
|
|
48
|
+
*
|
|
49
|
+
* A eleição e `work()` rodam na MESMA transação interativa — é o que faz o
|
|
50
|
+
* `pg_try_advisory_xact_lock` valer como exclusão mútua de verdade. Uma
|
|
51
|
+
* versão anterior comitava a eleição sozinha (fora de transação): o Postgres
|
|
52
|
+
* liberava o lock no instante em que a query retornava, antes do trabalho
|
|
53
|
+
* real começar, e pods suficientemente próximos no tempo (o que é comum,
|
|
54
|
+
* já que o @Cron dispara no mesmo segundo em todos) "ganhavam" o mesmo tick
|
|
55
|
+
* e mandavam check-ins duplicados — foi o que gerou o incidente do
|
|
56
|
+
* sac-sla-sweep. `work()` deliberadamente NÃO recebe `tx` (continua fechando
|
|
57
|
+
* sobre `this.prisma`/outros serviços do chamador): a conexão que segura o
|
|
58
|
+
* lock fica ociosa (idle in transaction) durante o trabalho real, em vez de
|
|
59
|
+
* segurar locks de linha por uma chamada HTTP potencialmente longa (ex.:
|
|
60
|
+
* renovação de assinatura do Graph, até 10 min).
|
|
61
|
+
*
|
|
62
|
+
* Quando outro pod já ganhou o tick, resolve para `undefined` sem chamar
|
|
63
|
+
* `work` — não é uma falha, é a mesma semântica de "outro worker já pegou o
|
|
64
|
+
* job" que a fila customizada já usa.
|
|
65
|
+
*/
|
|
66
|
+
export async function runMonitoredCron<T>(
|
|
67
|
+
prisma: any,
|
|
68
|
+
options: MonitoredCronOptions,
|
|
69
|
+
work: () => Promise<T>,
|
|
70
|
+
): Promise<T | undefined> {
|
|
71
|
+
const timeoutMs =
|
|
72
|
+
(options.maxRuntimeMinutes ?? DEFAULT_MAX_RUNTIME_MINUTES) * 60_000 +
|
|
73
|
+
TRANSACTION_TIMEOUT_SLACK_MS;
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
return await prisma.$transaction(
|
|
77
|
+
async (tx: any) => {
|
|
78
|
+
if (!(await claimCronTick(tx, options.slug))) return undefined;
|
|
79
|
+
|
|
80
|
+
return Sentry.withMonitor(
|
|
81
|
+
options.slug,
|
|
82
|
+
() => Sentry.startSpan({ name: options.slug, op: 'cron.hedhog' }, work),
|
|
83
|
+
{
|
|
84
|
+
schedule: { type: 'crontab', value: options.schedule },
|
|
85
|
+
timezone: 'America/Sao_Paulo',
|
|
86
|
+
...(options.maxRuntimeMinutes ? { maxRuntime: options.maxRuntimeMinutes } : {}),
|
|
87
|
+
...(options.checkinMarginMinutes ? { checkinMargin: options.checkinMarginMinutes } : {}),
|
|
88
|
+
// Distingue cada execução do cron como um trace próprio, em vez de
|
|
89
|
+
// herdar/continuar um trace pendurado do ciclo de poll anterior.
|
|
90
|
+
isolateTrace: true,
|
|
91
|
+
},
|
|
92
|
+
);
|
|
93
|
+
},
|
|
94
|
+
{ timeout: timeoutMs, maxWait: TRANSACTION_MAX_WAIT_MS },
|
|
95
|
+
);
|
|
96
|
+
} catch (error) {
|
|
97
|
+
// withMonitor já registrou o check-in como 'error' antes de relançar (e um
|
|
98
|
+
// erro de work() também derruba a transação por rollback — inofensivo,
|
|
99
|
+
// já que nada é escrito via `tx`, só o lock é liberado). O @Cron do
|
|
100
|
+
// chamador NUNCA deve rejeitar a partir daqui: a lib `cron` por baixo do
|
|
101
|
+
// @nestjs/schedule não garante tratamento de uma rejeição vinda do
|
|
102
|
+
// onTick, e o onUnhandledRejectionIntegration({mode:'strict'}) do
|
|
103
|
+
// instrument.ts derrubaria o processo por um erro que já está capturado.
|
|
104
|
+
Sentry.withScope((scope) => {
|
|
105
|
+
scope.setTag('cron', options.slug);
|
|
106
|
+
Sentry.captureException(error, { mechanism: { handled: true, type: 'hedhog.cron' } });
|
|
107
|
+
});
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { IsNotEmpty, IsString, MaxLength } from 'class-validator';
|
|
2
|
+
|
|
3
|
+
/** Direção B: o aparelho autenticado aprova o navegador que pediu. */
|
|
4
|
+
export class ApproveReversePairingDTO {
|
|
5
|
+
/**
|
|
6
|
+
* Prova de que quem aprova leu mesmo o QR: a pública efêmera precisa bater com
|
|
7
|
+
* a guardada. Sem isso, o `id` sequencial permitiria aprovar (ou espiar) o
|
|
8
|
+
* pareamento de um desconhecido por enumeração.
|
|
9
|
+
*/
|
|
10
|
+
@IsString()
|
|
11
|
+
@IsNotEmpty()
|
|
12
|
+
@MaxLength(255)
|
|
13
|
+
eph_public_key: string;
|
|
14
|
+
|
|
15
|
+
/** Blob OPACO cifrado para a pública efêmera acima. */
|
|
16
|
+
@IsString()
|
|
17
|
+
@IsNotEmpty()
|
|
18
|
+
@MaxLength(8192)
|
|
19
|
+
payload_enc: string;
|
|
20
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { IsNotEmpty, IsString, MaxLength } from 'class-validator';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Direção A: o aparelho que leu o QR reivindica e, depois, resgata.
|
|
5
|
+
*
|
|
6
|
+
* Serve aos dois passos porque a prova é a mesma nos dois: a posse do token que
|
|
7
|
+
* saiu do fragmento do QR. O que muda é só o estado exigido do registro.
|
|
8
|
+
*/
|
|
9
|
+
export class ClaimPairingDTO {
|
|
10
|
+
@IsString()
|
|
11
|
+
@IsNotEmpty()
|
|
12
|
+
@MaxLength(255)
|
|
13
|
+
auth_token: string;
|
|
14
|
+
|
|
15
|
+
@IsString()
|
|
16
|
+
@IsNotEmpty()
|
|
17
|
+
@MaxLength(100)
|
|
18
|
+
client_id: string;
|
|
19
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { IsNotEmpty, IsString, MaxLength } from 'class-validator';
|
|
2
|
+
|
|
3
|
+
/** Direção A: o navegador aprova o aparelho que reivindicou e entrega o blob. */
|
|
4
|
+
export class ConfirmPairingDTO {
|
|
5
|
+
/**
|
|
6
|
+
* Blob OPACO: AES-256-GCM feito no navegador com uma chave derivada do
|
|
7
|
+
* fragmento do QR. O teto de 8 KB existe para a tabela não virar armazenamento
|
|
8
|
+
* de propósito geral — a carga real tem ~600 caracteres.
|
|
9
|
+
*/
|
|
10
|
+
@IsString()
|
|
11
|
+
@IsNotEmpty()
|
|
12
|
+
@MaxLength(8192)
|
|
13
|
+
payload_enc: string;
|
|
14
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { IsNotEmpty, IsString, MaxLength } from 'class-validator';
|
|
2
|
+
|
|
3
|
+
/** Direção A: o navegador destravado abre o pareamento. */
|
|
4
|
+
export class StartPairingDTO {
|
|
5
|
+
/**
|
|
6
|
+
* `sha256(authToken)` em base64url, calculado no cliente. O token cru fica no
|
|
7
|
+
* fragmento do QR e nunca chega aqui — é o que impede um dump do banco de
|
|
8
|
+
* reivindicar pareamentos.
|
|
9
|
+
*/
|
|
10
|
+
@IsString()
|
|
11
|
+
@IsNotEmpty()
|
|
12
|
+
@MaxLength(255)
|
|
13
|
+
auth_token_hash: string;
|
|
14
|
+
|
|
15
|
+
@IsString()
|
|
16
|
+
@IsNotEmpty()
|
|
17
|
+
@MaxLength(100)
|
|
18
|
+
client_id: string;
|
|
19
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import {
|
|
2
|
+
IsBoolean,
|
|
3
|
+
IsNotEmpty,
|
|
4
|
+
IsOptional,
|
|
5
|
+
IsString,
|
|
6
|
+
MaxLength,
|
|
7
|
+
} from 'class-validator';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Direção B: o navegador que QUER acesso abre o pareamento.
|
|
11
|
+
*
|
|
12
|
+
* Público de propósito: o caso principal é um computador novo, onde ainda não há
|
|
13
|
+
* sessão nenhuma. O registro criado aqui não vale nada sozinho — só passa a
|
|
14
|
+
* valer quando um aparelho autenticado o aprova.
|
|
15
|
+
*/
|
|
16
|
+
export class StartReversePairingDTO {
|
|
17
|
+
/**
|
|
18
|
+
* `sha256(pollToken)`. O token de poll NÃO entra no QR: só o `id` entra. Assim
|
|
19
|
+
* quem fotografa a tela não consegue consumir o resultado nem depois de
|
|
20
|
+
* aprovado.
|
|
21
|
+
*/
|
|
22
|
+
@IsString()
|
|
23
|
+
@IsNotEmpty()
|
|
24
|
+
@MaxLength(255)
|
|
25
|
+
poll_hash: string;
|
|
26
|
+
|
|
27
|
+
/** Ponto EC não comprimido (65 bytes) em base64 — a pública efêmera da aba. */
|
|
28
|
+
@IsString()
|
|
29
|
+
@IsNotEmpty()
|
|
30
|
+
@MaxLength(255)
|
|
31
|
+
eph_public_key: string;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* `true` quando o navegador também precisa de sessão (computador novo);
|
|
35
|
+
* `false` quando já está logado e só o cofre está travado.
|
|
36
|
+
*/
|
|
37
|
+
@IsBoolean()
|
|
38
|
+
@IsOptional()
|
|
39
|
+
want_session?: boolean;
|
|
40
|
+
|
|
41
|
+
@IsString()
|
|
42
|
+
@IsNotEmpty()
|
|
43
|
+
@MaxLength(100)
|
|
44
|
+
client_id: string;
|
|
45
|
+
}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { NoRole, Public, User } from '@hed-hog/api';
|
|
2
|
+
import { Locale } from '@hed-hog/api-locale';
|
|
3
|
+
import {
|
|
4
|
+
Body,
|
|
5
|
+
Controller,
|
|
6
|
+
Delete,
|
|
7
|
+
Get,
|
|
8
|
+
Headers,
|
|
9
|
+
Inject,
|
|
10
|
+
Ip,
|
|
11
|
+
Param,
|
|
12
|
+
Post,
|
|
13
|
+
Query,
|
|
14
|
+
Res,
|
|
15
|
+
UseGuards,
|
|
16
|
+
forwardRef,
|
|
17
|
+
} from '@nestjs/common';
|
|
18
|
+
import { Throttle, ThrottlerGuard } from '@nestjs/throttler';
|
|
19
|
+
import { TokenService } from '../token/token.service';
|
|
20
|
+
import { ApproveReversePairingDTO } from './dto/approve-reverse-pairing.dto';
|
|
21
|
+
import { ClaimPairingDTO } from './dto/claim-pairing.dto';
|
|
22
|
+
import { ConfirmPairingDTO } from './dto/confirm-pairing.dto';
|
|
23
|
+
import { PollReversePairingDTO } from './dto/poll-reverse-pairing.dto';
|
|
24
|
+
import { StartPairingDTO } from './dto/start-pairing.dto';
|
|
25
|
+
import { StartReversePairingDTO } from './dto/start-reverse-pairing.dto';
|
|
26
|
+
import { PairingService } from './pairing.service';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Pareamento por QR code. Fica em `/auth` ao lado de `/auth/authorize/*` porque
|
|
30
|
+
* é a mesma família: transferir uma sessão para outro cliente sem manipular
|
|
31
|
+
* senha.
|
|
32
|
+
*
|
|
33
|
+
* Quem chama o quê:
|
|
34
|
+
* - direção A: o NAVEGADOR abre, acompanha e confirma (rotas autenticadas); o
|
|
35
|
+
* CELULAR reivindica e resgata (rotas públicas — ele ainda não tem sessão);
|
|
36
|
+
* - direção B: o NAVEGADOR abre e faz poll (rotas públicas — pode ser um
|
|
37
|
+
* computador onde ninguém está logado); o CELULAR inspeciona e aprova (rotas
|
|
38
|
+
* autenticadas).
|
|
39
|
+
*/
|
|
40
|
+
@Controller('auth')
|
|
41
|
+
export class PairingController {
|
|
42
|
+
constructor(
|
|
43
|
+
@Inject(forwardRef(() => PairingService))
|
|
44
|
+
private readonly service: PairingService,
|
|
45
|
+
@Inject(forwardRef(() => TokenService))
|
|
46
|
+
private readonly token: TokenService,
|
|
47
|
+
) {}
|
|
48
|
+
|
|
49
|
+
/* ---------------------------- Direção A ---------------------------- */
|
|
50
|
+
|
|
51
|
+
@NoRole()
|
|
52
|
+
@UseGuards(ThrottlerGuard)
|
|
53
|
+
@Throttle({ default: { limit: 5, ttl: 60000 } })
|
|
54
|
+
@Post('pairing')
|
|
55
|
+
async start(
|
|
56
|
+
@User() { id },
|
|
57
|
+
@Body() body: StartPairingDTO,
|
|
58
|
+
@Ip() ipAddress: string,
|
|
59
|
+
@Headers('user-agent') userAgent: string,
|
|
60
|
+
) {
|
|
61
|
+
return this.service.start({
|
|
62
|
+
userId: id,
|
|
63
|
+
authTokenHash: body.auth_token_hash,
|
|
64
|
+
clientId: body.client_id,
|
|
65
|
+
ipAddress,
|
|
66
|
+
userAgent,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Poll do navegador enquanto o QR está na tela. Limite alto por isso. */
|
|
71
|
+
@NoRole()
|
|
72
|
+
@UseGuards(ThrottlerGuard)
|
|
73
|
+
@Throttle({ default: { limit: 120, ttl: 60000 } })
|
|
74
|
+
@Get('pairing/:id')
|
|
75
|
+
async status(@User() { id: userId }, @Param('id') id: string) {
|
|
76
|
+
return this.service.status(userId, id);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@NoRole()
|
|
80
|
+
@UseGuards(ThrottlerGuard)
|
|
81
|
+
@Throttle({ default: { limit: 10, ttl: 60000 } })
|
|
82
|
+
@Post('pairing/:id/confirm')
|
|
83
|
+
async confirm(
|
|
84
|
+
@User() { id: userId },
|
|
85
|
+
@Param('id') id: string,
|
|
86
|
+
@Body() body: ConfirmPairingDTO,
|
|
87
|
+
) {
|
|
88
|
+
return this.service.confirm({ userId, id, payloadEnc: body.payload_enc });
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@NoRole()
|
|
92
|
+
@UseGuards(ThrottlerGuard)
|
|
93
|
+
@Throttle({ default: { limit: 20, ttl: 60000 } })
|
|
94
|
+
@Delete('pairing/:id')
|
|
95
|
+
async cancel(@User() { id: userId }, @Param('id') id: string) {
|
|
96
|
+
return this.service.cancel(userId, id);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Público por definição: quem chama é o aparelho que acabou de ler o QR e
|
|
101
|
+
* ainda não tem sessão nenhuma. Quem autentica é a posse do token que saiu do
|
|
102
|
+
* fragmento.
|
|
103
|
+
*/
|
|
104
|
+
@Public()
|
|
105
|
+
@UseGuards(ThrottlerGuard)
|
|
106
|
+
@Throttle({ default: { limit: 10, ttl: 60000 } })
|
|
107
|
+
@Post('pairing/claim')
|
|
108
|
+
async claim(
|
|
109
|
+
@Body() body: ClaimPairingDTO,
|
|
110
|
+
@Ip() ipAddress: string,
|
|
111
|
+
@Headers('user-agent') userAgent: string,
|
|
112
|
+
) {
|
|
113
|
+
return this.service.claim({
|
|
114
|
+
authToken: body.auth_token,
|
|
115
|
+
clientId: body.client_id,
|
|
116
|
+
ipAddress,
|
|
117
|
+
userAgent,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Sem `@Res()` e sem cookie `rt`: quem resgata aqui é um app nativo, para quem
|
|
123
|
+
* um Set-Cookie é ruído. O refreshToken vai no corpo, como em `/auth/token`.
|
|
124
|
+
*/
|
|
125
|
+
@Public()
|
|
126
|
+
@UseGuards(ThrottlerGuard)
|
|
127
|
+
@Throttle({ default: { limit: 60, ttl: 60000 } })
|
|
128
|
+
@Post('pairing/redeem')
|
|
129
|
+
async redeem(
|
|
130
|
+
@Body() body: ClaimPairingDTO,
|
|
131
|
+
@Locale() locale: string,
|
|
132
|
+
@Ip() ipAddress: string,
|
|
133
|
+
@Headers('user-agent') userAgent: string,
|
|
134
|
+
) {
|
|
135
|
+
return this.service.redeem({
|
|
136
|
+
authToken: body.auth_token,
|
|
137
|
+
clientId: body.client_id,
|
|
138
|
+
locale,
|
|
139
|
+
ipAddress,
|
|
140
|
+
userAgent,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/* ---------------------------- Direção B ---------------------------- */
|
|
145
|
+
|
|
146
|
+
@Public()
|
|
147
|
+
@UseGuards(ThrottlerGuard)
|
|
148
|
+
@Throttle({ default: { limit: 10, ttl: 60000 } })
|
|
149
|
+
@Post('pairing/reverse')
|
|
150
|
+
async startReverse(
|
|
151
|
+
@Body() body: StartReversePairingDTO,
|
|
152
|
+
@Ip() ipAddress: string,
|
|
153
|
+
@Headers('user-agent') userAgent: string,
|
|
154
|
+
) {
|
|
155
|
+
return this.service.startReverse({
|
|
156
|
+
pollHash: body.poll_hash,
|
|
157
|
+
ephPublicKey: body.eph_public_key,
|
|
158
|
+
wantSession: body.want_session ?? true,
|
|
159
|
+
clientId: body.client_id,
|
|
160
|
+
ipAddress,
|
|
161
|
+
userAgent,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* O `k` é a pública efêmera lida do QR, e serve de prova de que quem pergunta
|
|
167
|
+
* esteve na frente da tela. Ela é pública — pode aparecer no log de acesso sem
|
|
168
|
+
* problema —, e sem ela o `id` sequencial permitiria varrer os pareamentos
|
|
169
|
+
* alheios.
|
|
170
|
+
*/
|
|
171
|
+
@NoRole()
|
|
172
|
+
@UseGuards(ThrottlerGuard)
|
|
173
|
+
@Throttle({ default: { limit: 30, ttl: 60000 } })
|
|
174
|
+
@Get('pairing/reverse/:id')
|
|
175
|
+
async reverseRequest(@Param('id') id: string, @Query('k') ephPublicKey: string) {
|
|
176
|
+
return this.service.getReverseRequest(id, ephPublicKey ?? '');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
@NoRole()
|
|
180
|
+
@UseGuards(ThrottlerGuard)
|
|
181
|
+
@Throttle({ default: { limit: 10, ttl: 60000 } })
|
|
182
|
+
@Post('pairing/reverse/:id/approve')
|
|
183
|
+
async approveReverse(
|
|
184
|
+
@User() { id: userId },
|
|
185
|
+
@Param('id') id: string,
|
|
186
|
+
@Body() body: ApproveReversePairingDTO,
|
|
187
|
+
) {
|
|
188
|
+
return this.service.approveReverse({
|
|
189
|
+
userId,
|
|
190
|
+
id,
|
|
191
|
+
ephPublicKey: body.eph_public_key,
|
|
192
|
+
payloadEnc: body.payload_enc,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
@NoRole()
|
|
197
|
+
@UseGuards(ThrottlerGuard)
|
|
198
|
+
@Throttle({ default: { limit: 20, ttl: 60000 } })
|
|
199
|
+
@Post('pairing/reverse/:id/reject')
|
|
200
|
+
async rejectReverse(
|
|
201
|
+
@User() { id: userId },
|
|
202
|
+
@Param('id') id: string,
|
|
203
|
+
@Body() body: { eph_public_key?: string },
|
|
204
|
+
) {
|
|
205
|
+
return this.service.rejectReverse({
|
|
206
|
+
userId,
|
|
207
|
+
id,
|
|
208
|
+
ephPublicKey: body?.eph_public_key ?? '',
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Aqui o cookie `rt` FAZ sentido, ao contrário do redeem: quem resgata é um
|
|
214
|
+
* navegador, e é o mesmo formato de sessão que o login normal entrega.
|
|
215
|
+
*/
|
|
216
|
+
@Public()
|
|
217
|
+
@UseGuards(ThrottlerGuard)
|
|
218
|
+
@Throttle({ default: { limit: 60, ttl: 60000 } })
|
|
219
|
+
@Post('pairing/reverse/poll')
|
|
220
|
+
async pollReverse(
|
|
221
|
+
@Body() body: PollReversePairingDTO,
|
|
222
|
+
@Locale() locale: string,
|
|
223
|
+
@Res({ passthrough: true }) res,
|
|
224
|
+
) {
|
|
225
|
+
const result = await this.service.pollReverse({
|
|
226
|
+
pollToken: body.poll_token,
|
|
227
|
+
locale,
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
if ('session' in result && result.session && result.refreshToken) {
|
|
231
|
+
await this.token.setRefreshTokenCookie(
|
|
232
|
+
locale,
|
|
233
|
+
res,
|
|
234
|
+
result.refreshToken,
|
|
235
|
+
result.session.expires_at,
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// A sessão inteira não volta para o cliente: ele precisa dos tokens, não do
|
|
240
|
+
// registro do banco.
|
|
241
|
+
const { session, ...payload } = result as Record<string, unknown> & {
|
|
242
|
+
session?: unknown;
|
|
243
|
+
};
|
|
244
|
+
return payload;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { PrismaModule } from '@hed-hog/api-prisma';
|
|
2
|
+
import { Module, forwardRef } from '@nestjs/common';
|
|
3
|
+
import { AuthModule } from '../auth/auth.module';
|
|
4
|
+
import { SecurityModule } from '../security/security.module';
|
|
5
|
+
import { TokenModule } from '../token/token.module';
|
|
6
|
+
import { PairingController } from './pairing.controller';
|
|
7
|
+
import { PairingService } from './pairing.service';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Separado do AuthorizeModule: lá o HedHog é provedor OAuth para um cliente que
|
|
11
|
+
* conhece de antemão o servidor; aqui o ponto é justamente o aparelho que ainda
|
|
12
|
+
* não sabe com quem falar. O que os dois compartilham — token opaco com hash,
|
|
13
|
+
* TTL curto e consumo atômico — está copiado, não abstraído: uma base comum
|
|
14
|
+
* amarraria dois fluxos de auth que devem poder divergir.
|
|
15
|
+
*/
|
|
16
|
+
@Module({
|
|
17
|
+
imports: [
|
|
18
|
+
forwardRef(() => PrismaModule),
|
|
19
|
+
forwardRef(() => SecurityModule),
|
|
20
|
+
forwardRef(() => AuthModule),
|
|
21
|
+
forwardRef(() => TokenModule),
|
|
22
|
+
],
|
|
23
|
+
controllers: [PairingController],
|
|
24
|
+
providers: [PairingService],
|
|
25
|
+
exports: [PairingService],
|
|
26
|
+
})
|
|
27
|
+
export class PairingModule {}
|