@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,614 @@
|
|
|
1
|
+
// `Prisma` vem de @hed-hog/api-prisma, não de '@prisma/client': o tsconfig do
|
|
2
|
+
// monorepo mapeia '@prisma/client' para o client gerado (.prisma/client), que
|
|
3
|
+
// não expõe o helper `Prisma.sql`. É a convenção do repo inteiro.
|
|
4
|
+
import { Prisma, PrismaService } from '@hed-hog/api-prisma';
|
|
5
|
+
import {
|
|
6
|
+
BadRequestException,
|
|
7
|
+
Inject,
|
|
8
|
+
Injectable,
|
|
9
|
+
Logger,
|
|
10
|
+
forwardRef,
|
|
11
|
+
} from '@nestjs/common';
|
|
12
|
+
import { Cron, CronExpression } from '@nestjs/schedule';
|
|
13
|
+
import { timingSafeEqual } from 'crypto';
|
|
14
|
+
import { AuthService } from '../auth/auth.service';
|
|
15
|
+
import { SecurityService } from '../security/security.service';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Pareamento de aparelhos por QR code, nas duas direções.
|
|
19
|
+
*
|
|
20
|
+
* Modelado no `AuthorizeService`: token opaco de que só o hash é persistido, TTL
|
|
21
|
+
* curto, e toda transição de estado decidida pelo banco com
|
|
22
|
+
* `UPDATE ... WHERE ... IS NULL RETURNING`. Dois requests concorrentes — o caso
|
|
23
|
+
* de replay — só podem ter um vencedor, e quem decide é o Postgres.
|
|
24
|
+
*
|
|
25
|
+
* ## O que este serviço NÃO faz
|
|
26
|
+
*
|
|
27
|
+
* Ele nunca decifra `payload_enc`. Aquele blob é AES-256-GCM feito no cliente,
|
|
28
|
+
* com uma chave que jamais chega até aqui: na direção A ela sai do fragmento do
|
|
29
|
+
* QR (que o navegador não transmite), na direção B da chave privada efêmera que
|
|
30
|
+
* vive só na memória da aba. Chamar `security.decrypt` nele não daria erro de
|
|
31
|
+
* compilação — daria um cofre a menos de zero-knowledge. O spec cobre isso.
|
|
32
|
+
*
|
|
33
|
+
* ## Por que a direção A tem duas fases
|
|
34
|
+
*
|
|
35
|
+
* Se o navegador subisse o blob junto com o QR, quem fotografasse a tela nos 2
|
|
36
|
+
* minutos de validade levaria a sessão e as chaves. Com claim → confirm →
|
|
37
|
+
* redeem, o aparelho que reivindica não recebe nada; o blob só sobe depois que a
|
|
38
|
+
* pessoa aprovou, olhando o IP e o user-agent de quem pediu. Um QR
|
|
39
|
+
* fotografado vira um alerta recusável na tela da própria vítima.
|
|
40
|
+
*/
|
|
41
|
+
@Injectable()
|
|
42
|
+
export class PairingService {
|
|
43
|
+
private readonly logger = new Logger(PairingService.name);
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Curto de propósito: o QR fica visível numa tela, e o único trabalho dele é
|
|
47
|
+
* sobreviver ao tempo de pegar o celular e apontar a câmera.
|
|
48
|
+
*/
|
|
49
|
+
private readonly pairingTtlMs = 120_000;
|
|
50
|
+
|
|
51
|
+
static readonly BROWSER_TO_DEVICE = 'browser-to-device';
|
|
52
|
+
static readonly DEVICE_TO_BROWSER = 'device-to-browser';
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Uma mensagem só para "não existe", "expirou", "já foi usado" e "está no
|
|
56
|
+
* estado errado". Distinguir esses casos contaria a um atacante exatamente
|
|
57
|
+
* onde ele está no fluxo.
|
|
58
|
+
*/
|
|
59
|
+
private static readonly GENERIC_ERROR =
|
|
60
|
+
'Pareamento inválido, expirado ou já utilizado. Gere um novo QR code.';
|
|
61
|
+
|
|
62
|
+
constructor(
|
|
63
|
+
@Inject(forwardRef(() => PrismaService))
|
|
64
|
+
private readonly prisma: PrismaService,
|
|
65
|
+
@Inject(forwardRef(() => SecurityService))
|
|
66
|
+
private readonly security: SecurityService,
|
|
67
|
+
@Inject(forwardRef(() => AuthService))
|
|
68
|
+
private readonly auth: AuthService,
|
|
69
|
+
) {}
|
|
70
|
+
|
|
71
|
+
/* ------------------------------------------------------------------ */
|
|
72
|
+
/* Comum */
|
|
73
|
+
/* ------------------------------------------------------------------ */
|
|
74
|
+
|
|
75
|
+
private invalid(): BadRequestException {
|
|
76
|
+
return new BadRequestException(PairingService.GENERIC_ERROR);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
private async assertClient(clientId: string) {
|
|
80
|
+
const client = await this.prisma.oauth_app_client.findUnique({
|
|
81
|
+
where: { client_id: clientId },
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
if (!client || !client.enabled) {
|
|
85
|
+
throw new BadRequestException('Aplicativo desconhecido ou desabilitado.');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return client;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private expiresAt(): Date {
|
|
92
|
+
return new Date(Date.now() + this.pairingTtlMs);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private parseId(id: string): bigint {
|
|
96
|
+
try {
|
|
97
|
+
const parsed = BigInt(id);
|
|
98
|
+
// BigInt(0) e não 0n: o literal exige target ES2020, e as libraries
|
|
99
|
+
// compilam com es2017 no tsconfig.production.json — ver
|
|
100
|
+
// libraries/fiscal/src/document/fiscal-document.totals.ts.
|
|
101
|
+
if (parsed <= BigInt(0)) throw new Error('id inválido');
|
|
102
|
+
return parsed;
|
|
103
|
+
} catch {
|
|
104
|
+
throw this.invalid();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Mesma tabela que `UserService.registerUserActivity` escreve. O insert é
|
|
110
|
+
* direto para não arrastar o UserModule inteiro — com todas as suas
|
|
111
|
+
* dependências — para dentro de um módulo de auth por causa de uma linha.
|
|
112
|
+
*/
|
|
113
|
+
private async logActivity(userId: number | null, action: string) {
|
|
114
|
+
if (!userId) return;
|
|
115
|
+
await this.prisma.user_activity
|
|
116
|
+
.create({ data: { user_id: userId, action } })
|
|
117
|
+
.catch(() => null);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Comparação em tempo constante de duas strings base64. Usada na prova de
|
|
122
|
+
* posse da chave efêmera, que é pública mas cujo desencontro não deve vazar
|
|
123
|
+
* quantos bytes bateram.
|
|
124
|
+
*/
|
|
125
|
+
private safeEquals(a: string, b: string): boolean {
|
|
126
|
+
const left = Buffer.from(a ?? '');
|
|
127
|
+
const right = Buffer.from(b ?? '');
|
|
128
|
+
if (left.length !== right.length) return false;
|
|
129
|
+
try {
|
|
130
|
+
return timingSafeEqual(left, right);
|
|
131
|
+
} catch {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/* ------------------------------------------------------------------ */
|
|
137
|
+
/* Direção A — navegador destravado → celular */
|
|
138
|
+
/* ------------------------------------------------------------------ */
|
|
139
|
+
|
|
140
|
+
/** Abre o pareamento. Chamado pelo navegador, que já está autenticado. */
|
|
141
|
+
async start(params: {
|
|
142
|
+
userId: number;
|
|
143
|
+
authTokenHash: string;
|
|
144
|
+
clientId: string;
|
|
145
|
+
ipAddress: string;
|
|
146
|
+
userAgent: string;
|
|
147
|
+
}) {
|
|
148
|
+
await this.assertClient(params.clientId);
|
|
149
|
+
|
|
150
|
+
const expiresAt = this.expiresAt();
|
|
151
|
+
const row = await this.prisma.device_pairing_request.create({
|
|
152
|
+
data: {
|
|
153
|
+
direction: PairingService.BROWSER_TO_DEVICE,
|
|
154
|
+
auth_hash: params.authTokenHash,
|
|
155
|
+
user_id: params.userId,
|
|
156
|
+
client_id: params.clientId,
|
|
157
|
+
issued_ip: params.ipAddress,
|
|
158
|
+
issued_user_agent: params.userAgent,
|
|
159
|
+
expires_at: expiresAt,
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
await this.logActivity(params.userId, 'device-pairing-started');
|
|
164
|
+
this.logger.debug(
|
|
165
|
+
`[PairingService] pareamento aberto id=${row.id} userId=${params.userId}`,
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
return { id: String(row.id), expiresAt: expiresAt.toISOString() };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** Estado para a tela do navegador, incluindo quem reivindicou. */
|
|
172
|
+
async status(userId: number, id: string) {
|
|
173
|
+
const row = await this.prisma.device_pairing_request.findFirst({
|
|
174
|
+
where: {
|
|
175
|
+
id: this.parseId(id),
|
|
176
|
+
user_id: userId,
|
|
177
|
+
direction: PairingService.BROWSER_TO_DEVICE,
|
|
178
|
+
},
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
if (!row) throw this.invalid();
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
status: this.deriveStatus(row),
|
|
185
|
+
claim: row.claimed_at
|
|
186
|
+
? {
|
|
187
|
+
ip: row.claim_ip,
|
|
188
|
+
userAgent: row.claim_user_agent,
|
|
189
|
+
at: row.claimed_at.toISOString(),
|
|
190
|
+
}
|
|
191
|
+
: null,
|
|
192
|
+
expiresAt: row.expires_at.toISOString(),
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
private deriveStatus(row: {
|
|
197
|
+
cancelled_at: Date | null;
|
|
198
|
+
consumed_at: Date | null;
|
|
199
|
+
confirmed_at: Date | null;
|
|
200
|
+
claimed_at: Date | null;
|
|
201
|
+
expires_at: Date;
|
|
202
|
+
}): string {
|
|
203
|
+
if (row.cancelled_at) return 'cancelled';
|
|
204
|
+
if (row.consumed_at) return 'consumed';
|
|
205
|
+
if (row.expires_at.getTime() <= Date.now()) return 'expired';
|
|
206
|
+
if (row.confirmed_at) return 'confirmed';
|
|
207
|
+
if (row.claimed_at) return 'claimed';
|
|
208
|
+
return 'pending';
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* O aparelho que leu o QR se anuncia. Não recebe nada além de "aguarde": é
|
|
213
|
+
* este passo que transforma uma foto do QR num alerta na tela da vítima em vez
|
|
214
|
+
* de um comprometimento silencioso.
|
|
215
|
+
*/
|
|
216
|
+
async claim(params: {
|
|
217
|
+
authToken: string;
|
|
218
|
+
clientId: string;
|
|
219
|
+
ipAddress: string;
|
|
220
|
+
userAgent: string;
|
|
221
|
+
}) {
|
|
222
|
+
const authHash = this.security.hashSha256(params.authToken);
|
|
223
|
+
|
|
224
|
+
const rows = await this.prisma.$queryRaw<
|
|
225
|
+
Array<{ id: bigint; user_id: number; expires_at: Date }>
|
|
226
|
+
>(
|
|
227
|
+
Prisma.sql`
|
|
228
|
+
UPDATE device_pairing_request
|
|
229
|
+
SET claimed_at = NOW(),
|
|
230
|
+
claim_ip = ${params.ipAddress},
|
|
231
|
+
claim_user_agent = ${params.userAgent}
|
|
232
|
+
WHERE auth_hash = ${authHash}
|
|
233
|
+
AND client_id = ${params.clientId}
|
|
234
|
+
AND direction = ${PairingService.BROWSER_TO_DEVICE}
|
|
235
|
+
AND expires_at > NOW()
|
|
236
|
+
AND claimed_at IS NULL
|
|
237
|
+
AND cancelled_at IS NULL
|
|
238
|
+
AND consumed_at IS NULL
|
|
239
|
+
RETURNING id, user_id, expires_at
|
|
240
|
+
`,
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
const row = rows[0];
|
|
244
|
+
if (!row) {
|
|
245
|
+
// Um segundo leitor do mesmo QR perde a corrida aqui, decidida pelo banco.
|
|
246
|
+
this.logger.warn('[PairingService] claim recusado (hash inválido ou já reivindicado)');
|
|
247
|
+
throw this.invalid();
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
await this.logActivity(row.user_id, 'device-pairing-claimed');
|
|
251
|
+
|
|
252
|
+
return {
|
|
253
|
+
status: 'claimed',
|
|
254
|
+
expiresAt: row.expires_at.toISOString(),
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/** O navegador aprova o aparelho e só então entrega o blob cifrado. */
|
|
259
|
+
async confirm(params: { userId: number; id: string; payloadEnc: string }) {
|
|
260
|
+
const rows = await this.prisma.$queryRaw<Array<{ id: bigint }>>(
|
|
261
|
+
Prisma.sql`
|
|
262
|
+
UPDATE device_pairing_request
|
|
263
|
+
SET confirmed_at = NOW(),
|
|
264
|
+
payload_enc = ${params.payloadEnc}
|
|
265
|
+
WHERE id = ${this.parseId(params.id)}
|
|
266
|
+
AND user_id = ${params.userId}
|
|
267
|
+
AND direction = ${PairingService.BROWSER_TO_DEVICE}
|
|
268
|
+
AND expires_at > NOW()
|
|
269
|
+
AND claimed_at IS NOT NULL
|
|
270
|
+
AND confirmed_at IS NULL
|
|
271
|
+
AND cancelled_at IS NULL
|
|
272
|
+
AND consumed_at IS NULL
|
|
273
|
+
RETURNING id
|
|
274
|
+
`,
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
if (!rows[0]) {
|
|
278
|
+
this.logger.warn(
|
|
279
|
+
`[PairingService] confirm recusado id=${params.id} userId=${params.userId}`,
|
|
280
|
+
);
|
|
281
|
+
throw this.invalid();
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
await this.logActivity(params.userId, 'device-pairing-confirmed');
|
|
285
|
+
return { status: 'confirmed' };
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/** "Rejeitar", fechar o diálogo, ou regerar o QR. */
|
|
289
|
+
async cancel(userId: number, id: string) {
|
|
290
|
+
await this.prisma.device_pairing_request.updateMany({
|
|
291
|
+
where: { id: this.parseId(id), user_id: userId, consumed_at: null },
|
|
292
|
+
data: { cancelled_at: new Date() },
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
await this.logActivity(userId, 'device-pairing-cancelled');
|
|
296
|
+
return { status: 'cancelled' };
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* O aparelho resgata. Antes de confirmado devolve `pending` SEM consumir — do
|
|
301
|
+
* contrário o primeiro poll queimaria o registro e a aprovação chegaria tarde
|
|
302
|
+
* demais.
|
|
303
|
+
*/
|
|
304
|
+
async redeem(params: {
|
|
305
|
+
authToken: string;
|
|
306
|
+
clientId: string;
|
|
307
|
+
locale: string;
|
|
308
|
+
ipAddress: string;
|
|
309
|
+
userAgent: string;
|
|
310
|
+
}) {
|
|
311
|
+
const authHash = this.security.hashSha256(params.authToken);
|
|
312
|
+
|
|
313
|
+
const pending = await this.prisma.device_pairing_request.findFirst({
|
|
314
|
+
where: {
|
|
315
|
+
auth_hash: authHash,
|
|
316
|
+
client_id: params.clientId,
|
|
317
|
+
direction: PairingService.BROWSER_TO_DEVICE,
|
|
318
|
+
},
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
if (
|
|
322
|
+
!pending ||
|
|
323
|
+
pending.cancelled_at ||
|
|
324
|
+
pending.consumed_at ||
|
|
325
|
+
pending.expires_at.getTime() <= Date.now()
|
|
326
|
+
) {
|
|
327
|
+
throw this.invalid();
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (!pending.confirmed_at) {
|
|
331
|
+
return { status: 'pending' as const };
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const rows = await this.prisma.$queryRaw<
|
|
335
|
+
Array<{ user_id: number; payload_enc: string }>
|
|
336
|
+
>(
|
|
337
|
+
Prisma.sql`
|
|
338
|
+
UPDATE device_pairing_request
|
|
339
|
+
SET consumed_at = NOW()
|
|
340
|
+
WHERE auth_hash = ${authHash}
|
|
341
|
+
AND client_id = ${params.clientId}
|
|
342
|
+
AND direction = ${PairingService.BROWSER_TO_DEVICE}
|
|
343
|
+
AND confirmed_at IS NOT NULL
|
|
344
|
+
AND consumed_at IS NULL
|
|
345
|
+
AND cancelled_at IS NULL
|
|
346
|
+
AND expires_at > NOW()
|
|
347
|
+
RETURNING user_id, payload_enc
|
|
348
|
+
`,
|
|
349
|
+
);
|
|
350
|
+
|
|
351
|
+
const row = rows[0];
|
|
352
|
+
if (!row) {
|
|
353
|
+
this.logger.warn('[PairingService] redeem após consumo ou expiração');
|
|
354
|
+
throw this.invalid();
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const { accessToken, refreshToken } = await this.auth.getAuthenticationPayload(
|
|
358
|
+
params.locale,
|
|
359
|
+
row.user_id,
|
|
360
|
+
params.ipAddress,
|
|
361
|
+
params.userAgent,
|
|
362
|
+
);
|
|
363
|
+
|
|
364
|
+
await this.logActivity(row.user_id, 'device-pairing-consumed');
|
|
365
|
+
|
|
366
|
+
return {
|
|
367
|
+
status: 'confirmed' as const,
|
|
368
|
+
accessToken,
|
|
369
|
+
refreshToken,
|
|
370
|
+
tokenType: 'Bearer',
|
|
371
|
+
// Informativo para o cliente agendar refresh; a verdade é o exp do JWT.
|
|
372
|
+
expiresIn: 3600,
|
|
373
|
+
// Devolvido intacto: este serviço não sabe (nem pode saber) o que há dentro.
|
|
374
|
+
payload: row.payload_enc,
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/* ------------------------------------------------------------------ */
|
|
379
|
+
/* Direção B — celular destravado → navegador */
|
|
380
|
+
/* ------------------------------------------------------------------ */
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* O navegador que QUER acesso abre o pareamento. Público: o caso principal é
|
|
384
|
+
* um computador onde ninguém está logado. O registro nasce sem dono.
|
|
385
|
+
*/
|
|
386
|
+
async startReverse(params: {
|
|
387
|
+
pollHash: string;
|
|
388
|
+
ephPublicKey: string;
|
|
389
|
+
wantSession: boolean;
|
|
390
|
+
clientId: string;
|
|
391
|
+
ipAddress: string;
|
|
392
|
+
userAgent: string;
|
|
393
|
+
}) {
|
|
394
|
+
await this.assertClient(params.clientId);
|
|
395
|
+
|
|
396
|
+
const expiresAt = this.expiresAt();
|
|
397
|
+
const row = await this.prisma.device_pairing_request.create({
|
|
398
|
+
data: {
|
|
399
|
+
direction: PairingService.DEVICE_TO_BROWSER,
|
|
400
|
+
auth_hash: params.pollHash,
|
|
401
|
+
client_id: params.clientId,
|
|
402
|
+
eph_public_key: params.ephPublicKey,
|
|
403
|
+
want_session: params.wantSession,
|
|
404
|
+
issued_ip: params.ipAddress,
|
|
405
|
+
issued_user_agent: params.userAgent,
|
|
406
|
+
expires_at: expiresAt,
|
|
407
|
+
},
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
return { id: String(row.id), expiresAt: expiresAt.toISOString() };
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Metadados do navegador que pediu, para o aparelho decidir se aprova.
|
|
415
|
+
*
|
|
416
|
+
* Exige a pública efêmera como prova de que quem pergunta leu mesmo o QR: o
|
|
417
|
+
* `id` é sequencial, e sem essa prova qualquer usuário autenticado poderia
|
|
418
|
+
* varrer os ids e descobrir de que IP alguém está tentando entrar.
|
|
419
|
+
*/
|
|
420
|
+
async getReverseRequest(id: string, ephPublicKey: string) {
|
|
421
|
+
const row = await this.prisma.device_pairing_request.findFirst({
|
|
422
|
+
where: {
|
|
423
|
+
id: this.parseId(id),
|
|
424
|
+
direction: PairingService.DEVICE_TO_BROWSER,
|
|
425
|
+
},
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
if (
|
|
429
|
+
!row ||
|
|
430
|
+
!row.eph_public_key ||
|
|
431
|
+
!this.safeEquals(row.eph_public_key, ephPublicKey) ||
|
|
432
|
+
row.cancelled_at ||
|
|
433
|
+
row.consumed_at ||
|
|
434
|
+
row.confirmed_at ||
|
|
435
|
+
row.expires_at.getTime() <= Date.now()
|
|
436
|
+
) {
|
|
437
|
+
this.logger.warn(`[PairingService] inspeção recusada id=${id}`);
|
|
438
|
+
throw this.invalid();
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// Registrar a leitura deixa o navegador mostrar "celular conectado,
|
|
442
|
+
// aguardando confirmação" em vez de um QR parado.
|
|
443
|
+
await this.prisma.device_pairing_request.updateMany({
|
|
444
|
+
where: { id: row.id, claimed_at: null },
|
|
445
|
+
data: { claimed_at: new Date() },
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
return {
|
|
449
|
+
issuedIp: row.issued_ip,
|
|
450
|
+
issuedUserAgent: row.issued_user_agent,
|
|
451
|
+
ephPublicKey: row.eph_public_key,
|
|
452
|
+
wantSession: row.want_session,
|
|
453
|
+
expiresAt: row.expires_at.toISOString(),
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/** O aparelho autenticado aprova e entrega o blob cifrado para a efêmera. */
|
|
458
|
+
async approveReverse(params: {
|
|
459
|
+
userId: number;
|
|
460
|
+
id: string;
|
|
461
|
+
ephPublicKey: string;
|
|
462
|
+
payloadEnc: string;
|
|
463
|
+
}) {
|
|
464
|
+
const rows = await this.prisma.$queryRaw<Array<{ id: bigint }>>(
|
|
465
|
+
Prisma.sql`
|
|
466
|
+
UPDATE device_pairing_request
|
|
467
|
+
SET confirmed_at = NOW(),
|
|
468
|
+
user_id = ${params.userId},
|
|
469
|
+
payload_enc = ${params.payloadEnc}
|
|
470
|
+
WHERE id = ${this.parseId(params.id)}
|
|
471
|
+
AND direction = ${PairingService.DEVICE_TO_BROWSER}
|
|
472
|
+
AND eph_public_key = ${params.ephPublicKey}
|
|
473
|
+
AND expires_at > NOW()
|
|
474
|
+
AND confirmed_at IS NULL
|
|
475
|
+
AND cancelled_at IS NULL
|
|
476
|
+
AND consumed_at IS NULL
|
|
477
|
+
RETURNING id
|
|
478
|
+
`,
|
|
479
|
+
);
|
|
480
|
+
|
|
481
|
+
if (!rows[0]) {
|
|
482
|
+
this.logger.warn(
|
|
483
|
+
`[PairingService] aprovação recusada id=${params.id} userId=${params.userId}`,
|
|
484
|
+
);
|
|
485
|
+
throw this.invalid();
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
await this.logActivity(params.userId, 'device-pairing-approved');
|
|
489
|
+
return { status: 'confirmed' };
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
async rejectReverse(params: {
|
|
493
|
+
userId: number;
|
|
494
|
+
id: string;
|
|
495
|
+
ephPublicKey: string;
|
|
496
|
+
}) {
|
|
497
|
+
await this.prisma.device_pairing_request.updateMany({
|
|
498
|
+
where: {
|
|
499
|
+
id: this.parseId(params.id),
|
|
500
|
+
direction: PairingService.DEVICE_TO_BROWSER,
|
|
501
|
+
eph_public_key: params.ephPublicKey,
|
|
502
|
+
consumed_at: null,
|
|
503
|
+
},
|
|
504
|
+
data: { cancelled_at: new Date() },
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
await this.logActivity(params.userId, 'device-pairing-rejected');
|
|
508
|
+
return { status: 'cancelled' };
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* O navegador pergunta se já foi aprovado. Enquanto não, devolve `pending` sem
|
|
513
|
+
* consumir. A sessão, quando pedida, nasce com o IP e o user-agent do
|
|
514
|
+
* NAVEGADOR — é ele quem vai usá-la, não o celular que aprovou.
|
|
515
|
+
*/
|
|
516
|
+
async pollReverse(params: { pollToken: string; locale: string }) {
|
|
517
|
+
const pollHash = this.security.hashSha256(params.pollToken);
|
|
518
|
+
|
|
519
|
+
const pending = await this.prisma.device_pairing_request.findFirst({
|
|
520
|
+
where: {
|
|
521
|
+
auth_hash: pollHash,
|
|
522
|
+
direction: PairingService.DEVICE_TO_BROWSER,
|
|
523
|
+
},
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
if (
|
|
527
|
+
!pending ||
|
|
528
|
+
pending.cancelled_at ||
|
|
529
|
+
pending.consumed_at ||
|
|
530
|
+
pending.expires_at.getTime() <= Date.now()
|
|
531
|
+
) {
|
|
532
|
+
throw this.invalid();
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
if (!pending.confirmed_at) {
|
|
536
|
+
return {
|
|
537
|
+
status: 'pending' as const,
|
|
538
|
+
claimed: Boolean(pending.claimed_at),
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
const rows = await this.prisma.$queryRaw<
|
|
543
|
+
Array<{
|
|
544
|
+
user_id: number;
|
|
545
|
+
payload_enc: string;
|
|
546
|
+
want_session: boolean;
|
|
547
|
+
issued_ip: string | null;
|
|
548
|
+
issued_user_agent: string | null;
|
|
549
|
+
}>
|
|
550
|
+
>(
|
|
551
|
+
Prisma.sql`
|
|
552
|
+
UPDATE device_pairing_request
|
|
553
|
+
SET consumed_at = NOW()
|
|
554
|
+
WHERE auth_hash = ${pollHash}
|
|
555
|
+
AND direction = ${PairingService.DEVICE_TO_BROWSER}
|
|
556
|
+
AND confirmed_at IS NOT NULL
|
|
557
|
+
AND consumed_at IS NULL
|
|
558
|
+
AND cancelled_at IS NULL
|
|
559
|
+
AND expires_at > NOW()
|
|
560
|
+
RETURNING user_id, payload_enc, want_session, issued_ip, issued_user_agent
|
|
561
|
+
`,
|
|
562
|
+
);
|
|
563
|
+
|
|
564
|
+
const row = rows[0];
|
|
565
|
+
if (!row) {
|
|
566
|
+
this.logger.warn('[PairingService] poll após consumo ou expiração');
|
|
567
|
+
throw this.invalid();
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
await this.logActivity(row.user_id, 'device-pairing-consumed');
|
|
571
|
+
|
|
572
|
+
if (!row.want_session) {
|
|
573
|
+
return { status: 'confirmed' as const, payload: row.payload_enc };
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
const { accessToken, refreshToken, session } =
|
|
577
|
+
await this.auth.getAuthenticationPayload(
|
|
578
|
+
params.locale,
|
|
579
|
+
row.user_id,
|
|
580
|
+
row.issued_ip ?? '',
|
|
581
|
+
row.issued_user_agent ?? '',
|
|
582
|
+
);
|
|
583
|
+
|
|
584
|
+
return {
|
|
585
|
+
status: 'confirmed' as const,
|
|
586
|
+
payload: row.payload_enc,
|
|
587
|
+
accessToken,
|
|
588
|
+
refreshToken,
|
|
589
|
+
session,
|
|
590
|
+
tokenType: 'Bearer',
|
|
591
|
+
expiresIn: 3600,
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/* ------------------------------------------------------------------ */
|
|
596
|
+
/* Limpeza */
|
|
597
|
+
/* ------------------------------------------------------------------ */
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* Os registros são minúsculos e de vida curta, mas nada os apagava — mesmo
|
|
601
|
+
* buraco que `oauth_authorization_code` tem. Sem isto, ciphertext aprovado e
|
|
602
|
+
* nunca resgatado ficaria no banco para sempre.
|
|
603
|
+
*/
|
|
604
|
+
@Cron(CronExpression.EVERY_DAY_AT_4AM)
|
|
605
|
+
async purgeExpired() {
|
|
606
|
+
const { count } = await this.prisma.device_pairing_request.deleteMany({
|
|
607
|
+
where: { expires_at: { lt: new Date(Date.now() - 24 * 60 * 60 * 1000) } },
|
|
608
|
+
});
|
|
609
|
+
|
|
610
|
+
if (count > 0) {
|
|
611
|
+
this.logger.log(`[PairingService] ${count} pareamentos expirados removidos`);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
}
|
package/src/pdf/pdf.service.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Inject, Injectable, InternalServerErrorException, Logger, forwardRef } from '@nestjs/common';
|
|
2
2
|
import { ConfigService } from '@nestjs/config';
|
|
3
3
|
|
|
4
|
+
import { BrandAssetService } from '../setting/brand-asset.service';
|
|
4
5
|
import { SettingService } from '../setting/setting.service';
|
|
5
6
|
import { PdfAreaType, PdfThemeConfig } from './interfaces/pdf-theme-config.interface';
|
|
6
7
|
import { hcodeInstitutionalTemplate } from './templates/hcode-institutional.template';
|
|
@@ -43,6 +44,8 @@ export class PdfService {
|
|
|
43
44
|
constructor(
|
|
44
45
|
@Inject(forwardRef(() => SettingService))
|
|
45
46
|
private readonly settingService: SettingService,
|
|
47
|
+
@Inject(forwardRef(() => BrandAssetService))
|
|
48
|
+
private readonly brandAsset: BrandAssetService,
|
|
46
49
|
private readonly configService: ConfigService,
|
|
47
50
|
) {}
|
|
48
51
|
|
|
@@ -87,7 +90,9 @@ export class PdfService {
|
|
|
87
90
|
(values['pdf-logo'] as string) ||
|
|
88
91
|
(values['image-url'] as string) ||
|
|
89
92
|
'';
|
|
90
|
-
const logoOverride = rawLogoUrl
|
|
93
|
+
const logoOverride = rawLogoUrl
|
|
94
|
+
? await this.brandAsset.resolveAssetUrl(rawLogoUrl)
|
|
95
|
+
: '';
|
|
91
96
|
|
|
92
97
|
return {
|
|
93
98
|
area,
|
|
@@ -115,29 +120,6 @@ export class PdfService {
|
|
|
115
120
|
}
|
|
116
121
|
}
|
|
117
122
|
|
|
118
|
-
/**
|
|
119
|
-
* Resolves a potentially relative URL (e.g. /logo.svg) to an absolute URL
|
|
120
|
-
* using the API base URL derived from environment variables.
|
|
121
|
-
*/
|
|
122
|
-
private resolveUrl(url: string): string {
|
|
123
|
-
if (!url) return '';
|
|
124
|
-
if (
|
|
125
|
-
url.startsWith('data:') ||
|
|
126
|
-
url.startsWith('http://') ||
|
|
127
|
-
url.startsWith('https://')
|
|
128
|
-
) {
|
|
129
|
-
return url;
|
|
130
|
-
}
|
|
131
|
-
const port = this.configService.get<string | number>('PORT') ?? 3100;
|
|
132
|
-
const baseUrl =
|
|
133
|
-
this.configService.get<string>('API_URL') ??
|
|
134
|
-
`http://localhost:${port}`;
|
|
135
|
-
return (
|
|
136
|
-
baseUrl.replace(/\/$/, '') +
|
|
137
|
-
(url.startsWith('/') ? url : '/' + url)
|
|
138
|
-
);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
123
|
// ── HTML → PDF ────────────────────────────────────────────────────────────
|
|
142
124
|
|
|
143
125
|
/**
|