@hed-hog/core 0.0.390 → 0.0.394
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
|
@@ -4,6 +4,7 @@ import { PrismaModule } from '@hed-hog/api-prisma';
|
|
|
4
4
|
import { Global, Module, forwardRef } from '@nestjs/common';
|
|
5
5
|
import { AuthModule } from '../auth/auth.module';
|
|
6
6
|
import { MailModule } from '../mail/mail.module';
|
|
7
|
+
import { BrandAssetService } from './brand-asset.service';
|
|
7
8
|
import { SettingsController } from './setting.controller';
|
|
8
9
|
import { SettingService } from './setting.service';
|
|
9
10
|
|
|
@@ -17,7 +18,7 @@ import { SettingService } from './setting.service';
|
|
|
17
18
|
forwardRef(() => MailModule),
|
|
18
19
|
],
|
|
19
20
|
controllers: [SettingsController],
|
|
20
|
-
providers: [SettingService],
|
|
21
|
-
exports: [SettingService],
|
|
21
|
+
providers: [SettingService, BrandAssetService],
|
|
22
|
+
exports: [SettingService, BrandAssetService],
|
|
22
23
|
})
|
|
23
24
|
export class SettingModule { }
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
jest,
|
|
8
8
|
} from '@jest/globals';
|
|
9
9
|
import { BadRequestException } from '@nestjs/common';
|
|
10
|
+
import { localeStorage } from '@hed-hog/api-locale';
|
|
10
11
|
import { SettingService } from './setting.service';
|
|
11
12
|
|
|
12
13
|
const makeDeps = () => {
|
|
@@ -16,7 +17,7 @@ const makeDeps = () => {
|
|
|
16
17
|
findFirst: jest.fn(),
|
|
17
18
|
findUnique: jest.fn(),
|
|
18
19
|
},
|
|
19
|
-
setting_user: { findMany: jest.fn() },
|
|
20
|
+
setting_user: { findMany: jest.fn(), findFirst: jest.fn() },
|
|
20
21
|
user: { findUnique: jest.fn() },
|
|
21
22
|
$executeRawUnsafe: jest.fn().mockResolvedValue(undefined),
|
|
22
23
|
$queryRawUnsafe: jest.fn().mockResolvedValue([]),
|
|
@@ -369,4 +370,81 @@ describe('SettingService', () => {
|
|
|
369
370
|
).resolves.toBeUndefined();
|
|
370
371
|
});
|
|
371
372
|
});
|
|
373
|
+
|
|
374
|
+
// Cadeia única de idioma do sistema: e-mail transacional e aviso de idioma de
|
|
375
|
+
// curso precisam responder a mesma pergunta do mesmo jeito.
|
|
376
|
+
describe('resolveUserLocale', () => {
|
|
377
|
+
const arrange = (deps: any, opts: { override?: string | null; system?: string } = {}) => {
|
|
378
|
+
deps.prisma.setting_user.findFirst.mockResolvedValue(
|
|
379
|
+
opts.override ? { value: opts.override } : null,
|
|
380
|
+
);
|
|
381
|
+
deps.prisma.setting.findMany.mockResolvedValue([
|
|
382
|
+
{ id: 1, slug: 'language', value: opts.system ?? 'en', type: 'string', user_override: true },
|
|
383
|
+
]);
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
it('a preferência do usuário vence tudo', async () => {
|
|
387
|
+
const { service, deps } = makeService();
|
|
388
|
+
arrange(deps, { override: 'pt', system: 'en' });
|
|
389
|
+
|
|
390
|
+
const result = await localeStorage.run({ locale: 'en', detected: true }, () =>
|
|
391
|
+
service.resolveUserLocale(42),
|
|
392
|
+
);
|
|
393
|
+
|
|
394
|
+
expect(result).toBe('pt');
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
it('normaliza a preferência gravada com região', async () => {
|
|
398
|
+
const { service, deps } = makeService();
|
|
399
|
+
arrange(deps, { override: 'pt-BR' });
|
|
400
|
+
|
|
401
|
+
await expect(service.resolveUserLocale(42)).resolves.toBe('pt');
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
it('sem preferência, usa o idioma que o cliente declarou', async () => {
|
|
405
|
+
const { service, deps } = makeService();
|
|
406
|
+
arrange(deps, { system: 'en' });
|
|
407
|
+
|
|
408
|
+
const result = await localeStorage.run({ locale: 'pt', detected: true }, () =>
|
|
409
|
+
service.resolveUserLocale(42),
|
|
410
|
+
);
|
|
411
|
+
|
|
412
|
+
expect(result).toBe('pt');
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
// O `en` default do middleware não pode passar por escolha do cliente e
|
|
416
|
+
// atropelar a setting do sistema.
|
|
417
|
+
it('ignora o locale do contexto quando o cliente não declarou idioma', async () => {
|
|
418
|
+
const { service, deps } = makeService();
|
|
419
|
+
arrange(deps, { system: 'pt' });
|
|
420
|
+
|
|
421
|
+
const result = await localeStorage.run({ locale: 'en', detected: false }, () =>
|
|
422
|
+
service.resolveUserLocale(42),
|
|
423
|
+
);
|
|
424
|
+
|
|
425
|
+
expect(result).toBe('pt');
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
it('cai na setting do sistema fora de qualquer requisição', async () => {
|
|
429
|
+
const { service, deps } = makeService();
|
|
430
|
+
arrange(deps, { system: 'pt' });
|
|
431
|
+
|
|
432
|
+
await expect(service.resolveUserLocale(42)).resolves.toBe('pt');
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
it('funciona sem usuário (destinatário que não tem conta)', async () => {
|
|
436
|
+
const { service, deps } = makeService();
|
|
437
|
+
arrange(deps, { system: 'pt' });
|
|
438
|
+
|
|
439
|
+
await expect(service.resolveUserLocale(null)).resolves.toBe('pt');
|
|
440
|
+
expect(deps.prisma.setting_user.findFirst).not.toHaveBeenCalled();
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
it('cai em "en" quando a consulta falha', async () => {
|
|
444
|
+
const { service, deps } = makeService();
|
|
445
|
+
deps.prisma.setting_user.findFirst.mockRejectedValue(new Error('db down'));
|
|
446
|
+
|
|
447
|
+
await expect(service.resolveUserLocale(42)).resolves.toBe('en');
|
|
448
|
+
});
|
|
449
|
+
});
|
|
372
450
|
});
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { itemTranslations } from '@hed-hog/api';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
getLocaleFromContext,
|
|
4
|
+
getLocaleText,
|
|
5
|
+
isLocaleDetectedFromContext,
|
|
6
|
+
LocaleService,
|
|
7
|
+
normalizeLocaleCode,
|
|
8
|
+
} from '@hed-hog/api-locale';
|
|
3
9
|
import { PaginationDTO, PaginationService } from '@hed-hog/api-pagination';
|
|
4
10
|
import { PrismaService, setting_type_0f055cd5ea_enum } from '@hed-hog/api-prisma';
|
|
5
11
|
import {
|
|
@@ -499,6 +505,8 @@ export class SettingService implements OnModuleInit, OnModuleDestroy {
|
|
|
499
505
|
'password-block-leaked',
|
|
500
506
|
'mfa-email-code-length',
|
|
501
507
|
'mcp-enabled',
|
|
508
|
+
'richtext-ai-enabled',
|
|
509
|
+
'richtext-ai-profile-id',
|
|
502
510
|
'date-format',
|
|
503
511
|
'time-format',
|
|
504
512
|
'timezone',
|
|
@@ -1303,6 +1311,106 @@ export class SettingService implements OnModuleInit, OnModuleDestroy {
|
|
|
1303
1311
|
});
|
|
1304
1312
|
}
|
|
1305
1313
|
|
|
1314
|
+
/**
|
|
1315
|
+
* Le SO o override do usuario, sem cair para o valor global.
|
|
1316
|
+
*
|
|
1317
|
+
* `getSettingValues(slug, userId)` nao serve para isso: quando o usuario nao
|
|
1318
|
+
* tem override ele devolve o valor global, indistinguivel de uma preferencia
|
|
1319
|
+
* real. Quem precisa saber se o usuario *escolheu* algo - o idioma do e-mail
|
|
1320
|
+
* e a gravacao unica no login - depende dessa diferenca.
|
|
1321
|
+
*/
|
|
1322
|
+
async getUserSettingValue(
|
|
1323
|
+
userId: number,
|
|
1324
|
+
slug: string,
|
|
1325
|
+
): Promise<string | null> {
|
|
1326
|
+
if (!userId) {
|
|
1327
|
+
return null;
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
const settingUser = await this.prismaService.setting_user.findFirst({
|
|
1331
|
+
where: {
|
|
1332
|
+
user_id: userId,
|
|
1333
|
+
setting: {
|
|
1334
|
+
slug,
|
|
1335
|
+
user_override: true,
|
|
1336
|
+
},
|
|
1337
|
+
},
|
|
1338
|
+
select: {
|
|
1339
|
+
value: true,
|
|
1340
|
+
},
|
|
1341
|
+
});
|
|
1342
|
+
|
|
1343
|
+
const value = String(settingUser?.value ?? '').trim();
|
|
1344
|
+
|
|
1345
|
+
return value || null;
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
/**
|
|
1349
|
+
* Idioma efetivo de um usuario.
|
|
1350
|
+
*
|
|
1351
|
+
* Cadeia unica do sistema, para que e-mail, aviso de idioma de curso e
|
|
1352
|
+
* qualquer outra decisao de idioma respondam a mesma pergunta do mesmo jeito:
|
|
1353
|
+
*
|
|
1354
|
+
* 1. preferencia gravada do usuario - escolha explicita sempre vence;
|
|
1355
|
+
* 2. idioma que o cliente declarou nesta requisicao;
|
|
1356
|
+
* 3. setting global `language`;
|
|
1357
|
+
* 4. `en`.
|
|
1358
|
+
*
|
|
1359
|
+
* O passo 2 exige que o cliente tenha REALMENTE declarado: o `en` que o
|
|
1360
|
+
* middleware usa como default nao pode vencer a setting do sistema.
|
|
1361
|
+
*/
|
|
1362
|
+
async resolveUserLocale(userId?: number | null): Promise<string> {
|
|
1363
|
+
try {
|
|
1364
|
+
if (userId) {
|
|
1365
|
+
const preferred = normalizeLocaleCode(
|
|
1366
|
+
await this.getUserSettingValue(userId, 'language'),
|
|
1367
|
+
);
|
|
1368
|
+
|
|
1369
|
+
if (preferred) {
|
|
1370
|
+
return preferred;
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
const fromRequest = normalizeLocaleCode(getLocaleFromContext());
|
|
1375
|
+
|
|
1376
|
+
if (fromRequest && isLocaleDetectedFromContext()) {
|
|
1377
|
+
return fromRequest;
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
const settings = await this.getSettingValues(['language']);
|
|
1381
|
+
|
|
1382
|
+
return normalizeLocaleCode(settings?.['language']) ?? fromRequest ?? 'en';
|
|
1383
|
+
} catch (error) {
|
|
1384
|
+
this.logger.warn(`Could not resolve locale for user ${userId}: ${error}`);
|
|
1385
|
+
|
|
1386
|
+
return normalizeLocaleCode(getLocaleFromContext()) ?? 'en';
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
/**
|
|
1391
|
+
* Valores aceitos por uma setting de combobox (`setting_list`).
|
|
1392
|
+
*
|
|
1393
|
+
* `setSettingUserValue` valida que a setting existe e permite override, mas
|
|
1394
|
+
* nao valida o *valor*. Sem esta checagem, um navegador em `de-DE` gravaria
|
|
1395
|
+
* `de` como idioma e todo e-mail seguinte procuraria um template que nao
|
|
1396
|
+
* existe.
|
|
1397
|
+
*/
|
|
1398
|
+
async getSettingListValues(slug: string): Promise<string[]> {
|
|
1399
|
+
const setting = await this.prismaService.setting.findFirst({
|
|
1400
|
+
where: { slug },
|
|
1401
|
+
select: {
|
|
1402
|
+
setting_list: {
|
|
1403
|
+
select: { value: true },
|
|
1404
|
+
orderBy: { order: 'asc' },
|
|
1405
|
+
},
|
|
1406
|
+
},
|
|
1407
|
+
});
|
|
1408
|
+
|
|
1409
|
+
return (setting?.setting_list ?? [])
|
|
1410
|
+
.map((item) => String(item?.value ?? '').trim())
|
|
1411
|
+
.filter(Boolean);
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1306
1414
|
async getSettingValues(
|
|
1307
1415
|
slug: string | string[],
|
|
1308
1416
|
userId?: number,
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
jest,
|
|
8
8
|
} from '@jest/globals';
|
|
9
9
|
import {
|
|
10
|
+
BadRequestException,
|
|
10
11
|
ForbiddenException,
|
|
11
12
|
UnauthorizedException,
|
|
12
13
|
} from '@nestjs/common';
|
|
@@ -279,6 +280,21 @@ describe('TokenService', () => {
|
|
|
279
280
|
);
|
|
280
281
|
});
|
|
281
282
|
|
|
283
|
+
it('createAccessToken repassa expiresIn por chamada (sessão simulada)', async () => {
|
|
284
|
+
const { service, deps } = makeService();
|
|
285
|
+
deps.jwt.signAsync.mockResolvedValue('signed');
|
|
286
|
+
|
|
287
|
+
await service.createAccessToken(
|
|
288
|
+
{ sub: 42, sessionId: 555, act: 7, imp: true },
|
|
289
|
+
{ expiresIn: 1800 },
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
expect(deps.jwt.signAsync).toHaveBeenCalledWith(
|
|
293
|
+
{ sub: 42, sessionId: 555, act: 7, imp: true },
|
|
294
|
+
{ secret: 'secret', expiresIn: 1800 },
|
|
295
|
+
);
|
|
296
|
+
});
|
|
297
|
+
|
|
282
298
|
it('createOpaqueToken delega ao security', async () => {
|
|
283
299
|
const { service, deps } = makeService();
|
|
284
300
|
await expect(service.createOpaqueToken(32)).resolves.toBe('opaque-token');
|
|
@@ -328,6 +344,52 @@ describe('TokenService', () => {
|
|
|
328
344
|
);
|
|
329
345
|
});
|
|
330
346
|
|
|
347
|
+
it('createEmailConfirmationToken assina com propósito e validade de 7d', async () => {
|
|
348
|
+
const { service, deps } = makeService();
|
|
349
|
+
deps.jwt.signAsync.mockResolvedValue('confirm-tok');
|
|
350
|
+
|
|
351
|
+
await expect(
|
|
352
|
+
service.createEmailConfirmationToken({ userIdentifierId: 42 }),
|
|
353
|
+
).resolves.toBe('confirm-tok');
|
|
354
|
+
expect(deps.jwt.signAsync).toHaveBeenCalledWith(
|
|
355
|
+
{ userIdentifierId: 42, purpose: 'email-confirmation' },
|
|
356
|
+
expect.objectContaining({ secret: 'secret', expiresIn: '7d' }),
|
|
357
|
+
);
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
it('verifyEmailConfirmationToken retorna o identificador do payload', async () => {
|
|
361
|
+
const { service, deps } = makeService();
|
|
362
|
+
deps.jwt.verifyAsync.mockResolvedValue({
|
|
363
|
+
userIdentifierId: 42,
|
|
364
|
+
purpose: 'email-confirmation',
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
await expect(
|
|
368
|
+
service.verifyEmailConfirmationToken('pt-BR', 'tok'),
|
|
369
|
+
).resolves.toEqual({ userIdentifierId: 42 });
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
// Sem esta checagem, um token de MFA ou de sessão viraria confirmação de
|
|
373
|
+
// e-mail — e, no sentido inverso, o token do link entraria em fluxos que
|
|
374
|
+
// autenticam.
|
|
375
|
+
it('verifyEmailConfirmationToken recusa token de outro propósito', async () => {
|
|
376
|
+
const { service, deps } = makeService();
|
|
377
|
+
deps.jwt.verifyAsync.mockResolvedValue({ userIdentifierId: 42 });
|
|
378
|
+
|
|
379
|
+
await expect(
|
|
380
|
+
service.verifyEmailConfirmationToken('pt-BR', 'tok'),
|
|
381
|
+
).rejects.toThrow(BadRequestException);
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
it('verifyEmailConfirmationToken lança BadRequest em token inválido', async () => {
|
|
385
|
+
const { service, deps } = makeService();
|
|
386
|
+
deps.jwt.verifyAsync.mockRejectedValue(new Error('bad'));
|
|
387
|
+
|
|
388
|
+
await expect(
|
|
389
|
+
service.verifyEmailConfirmationToken('pt-BR', 'tok'),
|
|
390
|
+
).rejects.toThrow(BadRequestException);
|
|
391
|
+
});
|
|
392
|
+
|
|
331
393
|
it('decodeExpiredToken ignora expiração', async () => {
|
|
332
394
|
const { service, deps } = makeService();
|
|
333
395
|
deps.jwt.verifyAsync.mockResolvedValue({ sub: 7 });
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { getLocaleText } from "@hed-hog/api-locale";
|
|
2
2
|
import { PrismaService } from "@hed-hog/api-prisma";
|
|
3
|
-
import { ForbiddenException, forwardRef, Inject, Injectable, Logger, UnauthorizedException } from "@nestjs/common";
|
|
4
|
-
import { JwtService } from "@nestjs/jwt";
|
|
3
|
+
import { BadRequestException, ForbiddenException, forwardRef, Inject, Injectable, Logger, UnauthorizedException } from "@nestjs/common";
|
|
4
|
+
import { JwtService, JwtSignOptions } from "@nestjs/jwt";
|
|
5
5
|
import { SecurityService } from "../security/security.service";
|
|
6
6
|
import { SettingService } from "../setting/setting.service";
|
|
7
7
|
|
|
8
|
+
/** Claim que separa o token do link de confirmação de e-mail dos demais JWTs. */
|
|
9
|
+
const EMAIL_CONFIRMATION_PURPOSE = 'email-confirmation';
|
|
10
|
+
|
|
8
11
|
@Injectable()
|
|
9
12
|
export class TokenService {
|
|
10
13
|
private readonly logger = new Logger(TokenService.name);
|
|
@@ -79,9 +82,16 @@ export class TokenService {
|
|
|
79
82
|
}
|
|
80
83
|
}
|
|
81
84
|
|
|
82
|
-
|
|
85
|
+
/**
|
|
86
|
+
* `options` permite sobrescrever o `expiresIn` global do JwtModule numa
|
|
87
|
+
* chamada especifica — usado pela simulacao de acesso, cujo token precisa
|
|
88
|
+
* morrer junto com a sessao (que nao e renovavel) e nao aos 15 minutos
|
|
89
|
+
* padrao.
|
|
90
|
+
*/
|
|
91
|
+
async createAccessToken(payload: Record<string, any>, options?: JwtSignOptions) {
|
|
83
92
|
return this.jwt.signAsync(payload, {
|
|
84
93
|
secret: this.security.getJwtSecret(),
|
|
94
|
+
...(options ?? {}),
|
|
85
95
|
});
|
|
86
96
|
}
|
|
87
97
|
|
|
@@ -178,6 +188,50 @@ export class TokenService {
|
|
|
178
188
|
}
|
|
179
189
|
}
|
|
180
190
|
|
|
191
|
+
/**
|
|
192
|
+
* Token do link de confirmação de e-mail enviado no cadastro. É um JWT, e não
|
|
193
|
+
* um token opaco em `user_identifier_challenge` como o do reset de senha, por
|
|
194
|
+
* dois motivos: `completeAccount`/`authorizeAccountInvite` aceitam qualquer
|
|
195
|
+
* linha válida daquela tabela, então um token gravado ali transformaria o
|
|
196
|
+
* e-mail de boas-vindas num link de login de 7 dias — o claim `purpose` fecha
|
|
197
|
+
* essa porta; e como nada é consumido, clicar duas vezes (pré-fetch do
|
|
198
|
+
* cliente de e-mail, encaminhamento) continua funcionando.
|
|
199
|
+
*/
|
|
200
|
+
async createEmailConfirmationToken(payload: { userIdentifierId: number }) {
|
|
201
|
+
return this.jwt.signAsync(
|
|
202
|
+
{ ...payload, purpose: EMAIL_CONFIRMATION_PURPOSE },
|
|
203
|
+
{
|
|
204
|
+
secret: this.security.getJwtSecret(),
|
|
205
|
+
expiresIn: '7d',
|
|
206
|
+
},
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async verifyEmailConfirmationToken(
|
|
211
|
+
locale: string,
|
|
212
|
+
token: string,
|
|
213
|
+
): Promise<{ userIdentifierId: number }> {
|
|
214
|
+
try {
|
|
215
|
+
const payload = await this.jwt.verifyAsync(token, {
|
|
216
|
+
secret: this.security.getJwtSecret(),
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
if (payload.purpose !== EMAIL_CONFIRMATION_PURPOSE) {
|
|
220
|
+
throw new Error('Token purpose mismatch');
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return { userIdentifierId: payload.userIdentifierId };
|
|
224
|
+
} catch (error) {
|
|
225
|
+
throw new BadRequestException(
|
|
226
|
+
getLocaleText(
|
|
227
|
+
'confirmEmail.invalidOrExpired',
|
|
228
|
+
locale,
|
|
229
|
+
'This confirmation link is invalid or has expired.',
|
|
230
|
+
),
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
181
235
|
async decodeExpiredToken(token: string): Promise<any> {
|
|
182
236
|
try {
|
|
183
237
|
return await this.jwt.verifyAsync(token, {
|
|
@@ -23,7 +23,7 @@ const makeDeps = () => {
|
|
|
23
23
|
};
|
|
24
24
|
const paginationService = { paginatePrismaModel: jest.fn() };
|
|
25
25
|
const security = { hashArgon2: jest.fn().mockResolvedValue('hashed') };
|
|
26
|
-
const challenge = {
|
|
26
|
+
const challenge = { sendEmailConfirmationLink: jest.fn() };
|
|
27
27
|
const file = { upload: jest.fn(), delete: jest.fn(), getBuffer: jest.fn() };
|
|
28
28
|
const integrationApi = { publishEvent: jest.fn().mockResolvedValue(undefined) };
|
|
29
29
|
const settingService = { getSettingValues: jest.fn().mockResolvedValue({}) };
|
|
@@ -69,7 +69,16 @@ describe('UserService', () => {
|
|
|
69
69
|
deps.prismaService.user_activity.create.mockResolvedValue({ id: 1 });
|
|
70
70
|
await service.registerUserActivity(5, 'login');
|
|
71
71
|
expect(deps.prismaService.user_activity.create).toHaveBeenCalledWith({
|
|
72
|
-
data: { user_id: 5, action: 'login' },
|
|
72
|
+
data: { user_id: 5, action: 'login', actor_user_id: null },
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('grava o ator quem provocou a atividade (simulação de acesso)', async () => {
|
|
77
|
+
const { service, deps } = makeService();
|
|
78
|
+
deps.prismaService.user_activity.create.mockResolvedValue({ id: 1 });
|
|
79
|
+
await service.registerUserActivity(5, 'impersonationStarted', 9);
|
|
80
|
+
expect(deps.prismaService.user_activity.create).toHaveBeenCalledWith({
|
|
81
|
+
data: { user_id: 5, action: 'impersonationStarted', actor_user_id: 9 },
|
|
73
82
|
});
|
|
74
83
|
});
|
|
75
84
|
});
|
|
@@ -403,7 +412,29 @@ describe('UserService', () => {
|
|
|
403
412
|
id: '7',
|
|
404
413
|
action: 'login',
|
|
405
414
|
createdAt: '2026-01-01T00:00:00.000Z',
|
|
415
|
+
actor: null,
|
|
416
|
+
});
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
it('resolve o nome do ator numa atividade de simulação de acesso', async () => {
|
|
420
|
+
const { service, deps } = makeService();
|
|
421
|
+
deps.prismaService.user.findUnique.mockResolvedValue({ id: 1 });
|
|
422
|
+
deps.paginationService.paginatePrismaModel.mockResolvedValue({
|
|
423
|
+
data: [
|
|
424
|
+
{
|
|
425
|
+
id: 8,
|
|
426
|
+
action: 'impersonationStarted',
|
|
427
|
+
actor_user_id: 42,
|
|
428
|
+
created_at: new Date('2026-01-01T00:00:00Z'),
|
|
429
|
+
},
|
|
430
|
+
],
|
|
431
|
+
total: 1,
|
|
406
432
|
});
|
|
433
|
+
deps.prismaService.user.findMany.mockResolvedValue([{ id: 42, name: 'Operadora' }]);
|
|
434
|
+
|
|
435
|
+
const result = await service.listUserActivities('en', 1, {});
|
|
436
|
+
|
|
437
|
+
expect(result.data[0].actor).toEqual({ id: 42, name: 'Operadora' });
|
|
407
438
|
});
|
|
408
439
|
});
|
|
409
440
|
});
|
package/src/user/user.service.ts
CHANGED
|
@@ -186,7 +186,47 @@ export class UserService {
|
|
|
186
186
|
}
|
|
187
187
|
|
|
188
188
|
async get(locale: string, userId: number) {
|
|
189
|
-
|
|
189
|
+
const user = await this.findUserById(locale, userId);
|
|
190
|
+
|
|
191
|
+
// Enriquecimento so aqui, e nao em `findUserById`: aquele metodo tambem
|
|
192
|
+
// serve `GET /auth/verify`, que roda a cada carga de pagina. Quem precisa
|
|
193
|
+
// dos nomes de operador/ator e a ficha do usuario no admin.
|
|
194
|
+
const [user_activity, user_session] = await Promise.all([
|
|
195
|
+
this.attachActivityActors((user as any).user_activity ?? []),
|
|
196
|
+
this.attachSessionImpersonators((user as any).user_session ?? []),
|
|
197
|
+
]);
|
|
198
|
+
|
|
199
|
+
return { ...user, user_activity, user_session };
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** Ver `attachActivityActors`: mesma razao, aplicada a `user_session`. */
|
|
203
|
+
private async attachSessionImpersonators(sessions: any[]) {
|
|
204
|
+
const ids = [
|
|
205
|
+
...new Set(
|
|
206
|
+
sessions
|
|
207
|
+
.map((session) => session.impersonator_user_id)
|
|
208
|
+
.filter((id): id is number => typeof id === 'number'),
|
|
209
|
+
),
|
|
210
|
+
];
|
|
211
|
+
|
|
212
|
+
const users = ids.length
|
|
213
|
+
? await this.prismaService.user.findMany({
|
|
214
|
+
where: { id: { in: ids } },
|
|
215
|
+
select: { id: true, name: true },
|
|
216
|
+
})
|
|
217
|
+
: [];
|
|
218
|
+
const byId = new Map(users.map((user) => [user.id, user]));
|
|
219
|
+
|
|
220
|
+
return sessions.map((session: any) => ({
|
|
221
|
+
...session,
|
|
222
|
+
impersonator:
|
|
223
|
+
typeof session.impersonator_user_id === 'number'
|
|
224
|
+
? (byId.get(session.impersonator_user_id) ?? {
|
|
225
|
+
id: session.impersonator_user_id,
|
|
226
|
+
name: '',
|
|
227
|
+
})
|
|
228
|
+
: null,
|
|
229
|
+
}));
|
|
190
230
|
}
|
|
191
231
|
|
|
192
232
|
async createWithEmailAndPassword(
|
|
@@ -236,7 +276,7 @@ export class UserService {
|
|
|
236
276
|
});
|
|
237
277
|
|
|
238
278
|
try {
|
|
239
|
-
await this.challenge.
|
|
279
|
+
await this.challenge.sendEmailConfirmationLink(locale, user.id, email, app);
|
|
240
280
|
} catch (error: any) {
|
|
241
281
|
this.logger.warn(
|
|
242
282
|
`Signup confirmation email failed for user ${user.id}: ${error?.message ?? error}`
|
|
@@ -253,7 +293,10 @@ export class UserService {
|
|
|
253
293
|
payload: { id: user.id, name: user.name, email },
|
|
254
294
|
}).catch(() => null);
|
|
255
295
|
|
|
256
|
-
|
|
296
|
+
// `hasChallenge` não é decidido aqui: quem chama (AuthService.signupWithEmailAndPassword)
|
|
297
|
+
// é que sabe se `require-email-verification` está ligada e se o cadastro
|
|
298
|
+
// deve ou não já sair com uma sessão emitida.
|
|
299
|
+
return { user };
|
|
257
300
|
}
|
|
258
301
|
|
|
259
302
|
async update(
|
|
@@ -459,6 +502,9 @@ export class UserService {
|
|
|
459
502
|
take: 20,
|
|
460
503
|
},
|
|
461
504
|
user_session: true,
|
|
505
|
+
// Os dispositivos NAO entram aqui: uma conta antiga acumula dezenas
|
|
506
|
+
// deles, e embutir a lista inteira pesaria em todo GET /user/:id. O
|
|
507
|
+
// sheet busca em GET /user-device/user/:userId, que e paginado.
|
|
462
508
|
user_account: true,
|
|
463
509
|
role_user: {
|
|
464
510
|
include: {
|
|
@@ -673,7 +719,15 @@ export class UserService {
|
|
|
673
719
|
// Activity Tracking
|
|
674
720
|
// ==========================================
|
|
675
721
|
|
|
676
|
-
|
|
722
|
+
/**
|
|
723
|
+
* `actorUserId` marca quem provocou a atividade quando nao foi o proprio
|
|
724
|
+
* usuario — hoje, o operador de uma simulacao de acesso.
|
|
725
|
+
*/
|
|
726
|
+
async registerUserActivity(
|
|
727
|
+
userId: number | null | undefined,
|
|
728
|
+
action: string,
|
|
729
|
+
actorUserId?: number | null,
|
|
730
|
+
) {
|
|
677
731
|
if (!userId || !Number.isInteger(userId) || userId <= 0) {
|
|
678
732
|
return null;
|
|
679
733
|
}
|
|
@@ -682,6 +736,7 @@ export class UserService {
|
|
|
682
736
|
data: {
|
|
683
737
|
user_id: userId,
|
|
684
738
|
action,
|
|
739
|
+
actor_user_id: actorUserId ?? null,
|
|
685
740
|
},
|
|
686
741
|
});
|
|
687
742
|
}
|
|
@@ -690,13 +745,18 @@ export class UserService {
|
|
|
690
745
|
// Change Log (administrative audit trail)
|
|
691
746
|
// ==========================================
|
|
692
747
|
|
|
693
|
-
|
|
748
|
+
/**
|
|
749
|
+
* Publico porque a simulacao de acesso (ImpersonationService) tambem precisa
|
|
750
|
+
* gravar na mesma trilha administrativa, com o operador como ator.
|
|
751
|
+
*/
|
|
752
|
+
async recordUserChange(data: {
|
|
694
753
|
targetUserId: number;
|
|
695
754
|
actorUserId?: number | null;
|
|
696
755
|
action: string;
|
|
697
756
|
summary?: string;
|
|
698
757
|
beforeData?: unknown;
|
|
699
758
|
afterData?: unknown;
|
|
759
|
+
ipAddress?: string | null;
|
|
700
760
|
}) {
|
|
701
761
|
return this.prismaService.user_change_log
|
|
702
762
|
.create({
|
|
@@ -705,6 +765,7 @@ export class UserService {
|
|
|
705
765
|
actor_user_id: data.actorUserId ?? null,
|
|
706
766
|
action: data.action,
|
|
707
767
|
summary: data.summary ?? null,
|
|
768
|
+
ip_address: data.ipAddress ?? null,
|
|
708
769
|
before_data:
|
|
709
770
|
data.beforeData === undefined
|
|
710
771
|
? null
|
|
@@ -930,16 +991,57 @@ export class UserService {
|
|
|
930
991
|
},
|
|
931
992
|
);
|
|
932
993
|
|
|
994
|
+
const withActors = await this.attachActivityActors(paginated.data || []);
|
|
995
|
+
|
|
933
996
|
return {
|
|
934
997
|
...paginated,
|
|
935
|
-
data:
|
|
998
|
+
data: withActors.map((activity: any) => ({
|
|
936
999
|
id: String(activity.id),
|
|
937
1000
|
action: activity.action,
|
|
938
1001
|
createdAt: activity.created_at?.toISOString?.() || null,
|
|
1002
|
+
actor: activity.actor,
|
|
939
1003
|
})),
|
|
940
1004
|
};
|
|
941
1005
|
}
|
|
942
1006
|
|
|
1007
|
+
/**
|
|
1008
|
+
* Anexa `actor` as atividades provocadas por outra pessoa (simulacao de
|
|
1009
|
+
* acesso). `actor_user_id` e INT puro, sem FK — uma segunda FK para `user`
|
|
1010
|
+
* renomearia a relacao `user.user_activity` no db pull —, entao o nome vem de
|
|
1011
|
+
* uma consulta separada, como em `listUserChangeLogs`.
|
|
1012
|
+
*
|
|
1013
|
+
* Preserva os campos originais: a ficha do usuario embute `user_activity` cru
|
|
1014
|
+
* e a linha do tempo do admin le `created_at`, nao `createdAt`.
|
|
1015
|
+
*/
|
|
1016
|
+
async attachActivityActors(activities: any[]) {
|
|
1017
|
+
const actorIds = [
|
|
1018
|
+
...new Set(
|
|
1019
|
+
activities
|
|
1020
|
+
.map((activity) => activity.actor_user_id)
|
|
1021
|
+
.filter((id): id is number => typeof id === 'number'),
|
|
1022
|
+
),
|
|
1023
|
+
];
|
|
1024
|
+
|
|
1025
|
+
const actors = actorIds.length
|
|
1026
|
+
? await this.prismaService.user.findMany({
|
|
1027
|
+
where: { id: { in: actorIds } },
|
|
1028
|
+
select: { id: true, name: true },
|
|
1029
|
+
})
|
|
1030
|
+
: [];
|
|
1031
|
+
const actorById = new Map(actors.map((actor) => [actor.id, actor]));
|
|
1032
|
+
|
|
1033
|
+
return activities.map((activity: any) => ({
|
|
1034
|
+
...activity,
|
|
1035
|
+
actor:
|
|
1036
|
+
typeof activity.actor_user_id === 'number'
|
|
1037
|
+
? (actorById.get(activity.actor_user_id) ?? {
|
|
1038
|
+
id: activity.actor_user_id,
|
|
1039
|
+
name: '',
|
|
1040
|
+
})
|
|
1041
|
+
: null,
|
|
1042
|
+
}));
|
|
1043
|
+
}
|
|
1044
|
+
|
|
943
1045
|
// ==========================================
|
|
944
1046
|
// Private Helper Methods
|
|
945
1047
|
// ==========================================
|