@hed-hog/core 0.0.386 → 0.0.390
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 +12 -11
- package/dist/access-log/access-log.interceptor.d.ts.map +1 -1
- package/dist/access-log/access-log.interceptor.js +4 -2
- package/dist/access-log/access-log.interceptor.js.map +1 -1
- package/dist/ai/ai-attachment.fragments.d.ts +23 -0
- package/dist/ai/ai-attachment.fragments.d.ts.map +1 -0
- package/dist/ai/ai-attachment.fragments.js +34 -0
- package/dist/ai/ai-attachment.fragments.js.map +1 -0
- package/dist/ai/ai.controller.d.ts +7 -16
- package/dist/ai/ai.controller.d.ts.map +1 -1
- package/dist/ai/ai.prompts.d.ts +16 -0
- package/dist/ai/ai.prompts.d.ts.map +1 -0
- package/dist/ai/ai.prompts.js +37 -0
- package/dist/ai/ai.prompts.js.map +1 -0
- package/dist/ai/ai.service.d.ts +63 -12
- package/dist/ai/ai.service.d.ts.map +1 -1
- package/dist/ai/ai.service.js +215 -88
- package/dist/ai/ai.service.js.map +1 -1
- package/dist/ai/dto/chat.dto.d.ts +4 -1
- package/dist/ai/dto/chat.dto.d.ts.map +1 -1
- package/dist/ai/dto/chat.dto.js +23 -1
- package/dist/ai/dto/chat.dto.js.map +1 -1
- package/dist/ai/dto/create-agent.dto.d.ts +1 -1
- package/dist/ai/dto/create-agent.dto.d.ts.map +1 -1
- package/dist/ai/dto/create-agent.dto.js +1 -1
- package/dist/ai/dto/create-agent.dto.js.map +1 -1
- package/dist/ai/dto/update-agent.dto.d.ts +1 -1
- package/dist/ai/dto/update-agent.dto.d.ts.map +1 -1
- package/dist/ai/dto/update-agent.dto.js +1 -1
- package/dist/ai/dto/update-agent.dto.js.map +1 -1
- package/dist/ai-instruction/ai-instruction.controller.d.ts +61 -7
- package/dist/ai-instruction/ai-instruction.controller.d.ts.map +1 -1
- package/dist/ai-instruction/ai-instruction.controller.js +63 -20
- package/dist/ai-instruction/ai-instruction.controller.js.map +1 -1
- package/dist/ai-instruction/ai-instruction.module.d.ts.map +1 -1
- package/dist/ai-instruction/ai-instruction.module.js +13 -2
- package/dist/ai-instruction/ai-instruction.module.js.map +1 -1
- package/dist/ai-instruction/ai-instruction.service.d.ts +131 -6
- package/dist/ai-instruction/ai-instruction.service.d.ts.map +1 -1
- package/dist/ai-instruction/ai-instruction.service.js +533 -60
- package/dist/ai-instruction/ai-instruction.service.js.map +1 -1
- package/dist/ai-instruction/ai-prompt-builder.service.d.ts +8 -0
- package/dist/ai-instruction/ai-prompt-builder.service.d.ts.map +1 -1
- package/dist/ai-instruction/ai-prompt-builder.service.js +10 -1
- package/dist/ai-instruction/ai-prompt-builder.service.js.map +1 -1
- package/dist/ai-instruction/ai-prompt-registry.d.ts +45 -0
- package/dist/ai-instruction/ai-prompt-registry.d.ts.map +1 -0
- package/dist/ai-instruction/ai-prompt-registry.js +49 -0
- package/dist/ai-instruction/ai-prompt-registry.js.map +1 -0
- package/dist/ai-instruction/ai-prompt-resolver.service.d.ts +89 -0
- package/dist/ai-instruction/ai-prompt-resolver.service.d.ts.map +1 -0
- package/dist/ai-instruction/ai-prompt-resolver.service.js +222 -0
- package/dist/ai-instruction/ai-prompt-resolver.service.js.map +1 -0
- package/dist/ai-instruction/ai-prompt-template.util.d.ts +70 -0
- package/dist/ai-instruction/ai-prompt-template.util.d.ts.map +1 -0
- package/dist/ai-instruction/ai-prompt-template.util.js +154 -0
- package/dist/ai-instruction/ai-prompt-template.util.js.map +1 -0
- package/dist/ai-instruction/dto/create-ai-instruction.dto.d.ts +5 -0
- package/dist/ai-instruction/dto/create-ai-instruction.dto.d.ts.map +1 -1
- package/dist/ai-instruction/dto/create-ai-instruction.dto.js +27 -1
- package/dist/ai-instruction/dto/create-ai-instruction.dto.js.map +1 -1
- package/dist/ai-instruction/dto/preview-ai-instruction.dto.d.ts +14 -0
- package/dist/ai-instruction/dto/preview-ai-instruction.dto.d.ts.map +1 -0
- package/dist/ai-instruction/dto/preview-ai-instruction.dto.js +42 -0
- package/dist/ai-instruction/dto/preview-ai-instruction.dto.js.map +1 -0
- package/dist/ai-instruction/dto/update-ai-instruction.dto.d.ts +11 -0
- package/dist/ai-instruction/dto/update-ai-instruction.dto.d.ts.map +1 -1
- package/dist/ai-instruction/dto/update-ai-instruction.dto.js +34 -0
- package/dist/ai-instruction/dto/update-ai-instruction.dto.js.map +1 -1
- package/dist/ai-instruction/types/ai-instruction.types.d.ts +13 -1
- package/dist/ai-instruction/types/ai-instruction.types.d.ts.map +1 -1
- package/dist/ai-instruction/types/ai-instruction.types.js +10 -0
- package/dist/ai-instruction/types/ai-instruction.types.js.map +1 -1
- package/dist/auth/auth.controller.d.ts +26 -10
- package/dist/auth/auth.controller.d.ts.map +1 -1
- package/dist/auth/auth.controller.js +49 -21
- package/dist/auth/auth.controller.js.map +1 -1
- package/dist/auth/auth.service.d.ts +35 -9
- package/dist/auth/auth.service.d.ts.map +1 -1
- package/dist/auth/auth.service.js +78 -29
- package/dist/auth/auth.service.js.map +1 -1
- package/dist/captcha/captcha.module.d.ts +3 -0
- package/dist/captcha/captcha.module.d.ts.map +1 -0
- package/dist/captcha/captcha.module.js +23 -0
- package/dist/captcha/captcha.module.js.map +1 -0
- package/dist/captcha/captcha.service.d.ts +38 -0
- package/dist/captcha/captcha.service.d.ts.map +1 -0
- package/dist/captcha/captcha.service.js +144 -0
- package/dist/captcha/captcha.service.js.map +1 -0
- package/dist/challenge/challenge.service.d.ts +16 -7
- package/dist/challenge/challenge.service.d.ts.map +1 -1
- package/dist/challenge/challenge.service.js +64 -16
- package/dist/challenge/challenge.service.js.map +1 -1
- package/dist/core.module.d.ts +3 -1
- package/dist/core.module.d.ts.map +1 -1
- package/dist/core.module.js +29 -2
- package/dist/core.module.js.map +1 -1
- package/dist/dashboard/dashboard/dashboard.controller.d.ts +15 -15
- package/dist/dashboard/dashboard/dashboard.service.d.ts +15 -15
- package/dist/dashboard/dashboard-component/dashboard-component.controller.d.ts +6 -6
- package/dist/dashboard/dashboard-component/dashboard-component.service.d.ts +6 -6
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.controller.d.ts +20 -20
- package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.d.ts +20 -20
- package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts +1 -1
- package/dist/dashboard/dashboard-role/dashboard-role.controller.d.ts +13 -13
- package/dist/dashboard/dashboard-role/dashboard-role.service.d.ts +13 -13
- package/dist/file/file.service.d.ts +6 -0
- package/dist/file/file.service.d.ts.map +1 -1
- package/dist/file/file.service.js +44 -4
- package/dist/file/file.service.js.map +1 -1
- package/dist/incident/incident-notifier.service.d.ts +36 -0
- package/dist/incident/incident-notifier.service.d.ts.map +1 -0
- package/dist/incident/incident-notifier.service.js +208 -0
- package/dist/incident/incident-notifier.service.js.map +1 -0
- package/dist/incident/incident.controller.d.ts +23 -0
- package/dist/incident/incident.controller.d.ts.map +1 -0
- package/dist/incident/incident.controller.js +75 -0
- package/dist/incident/incident.controller.js.map +1 -0
- package/dist/incident/incident.module.d.ts +7 -0
- package/dist/incident/incident.module.d.ts.map +1 -0
- package/dist/incident/incident.module.js +32 -0
- package/dist/incident/incident.module.js.map +1 -0
- package/dist/incident/incident.service.d.ts +72 -0
- package/dist/incident/incident.service.d.ts.map +1 -0
- package/dist/incident/incident.service.js +184 -0
- package/dist/incident/incident.service.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -1
- package/dist/integration-profile/dto/test.dto.d.ts +13 -0
- package/dist/integration-profile/dto/test.dto.d.ts.map +1 -0
- package/dist/integration-profile/dto/test.dto.js +26 -0
- package/dist/integration-profile/dto/test.dto.js.map +1 -0
- package/dist/integration-profile/integration-profile.controller.d.ts +12 -1
- package/dist/integration-profile/integration-profile.controller.d.ts.map +1 -1
- package/dist/integration-profile/integration-profile.controller.js +9 -1
- package/dist/integration-profile/integration-profile.controller.js.map +1 -1
- package/dist/integration-profile/integration-profile.module.d.ts +6 -1
- package/dist/integration-profile/integration-profile.module.d.ts.map +1 -1
- package/dist/integration-profile/integration-profile.module.js +26 -3
- package/dist/integration-profile/integration-profile.module.js.map +1 -1
- package/dist/integration-profile/integration-profile.secrets.d.ts.map +1 -1
- package/dist/integration-profile/integration-profile.secrets.js +12 -2
- package/dist/integration-profile/integration-profile.secrets.js.map +1 -1
- package/dist/integration-profile/integration-profile.service.d.ts +28 -2
- package/dist/integration-profile/integration-profile.service.d.ts.map +1 -1
- package/dist/integration-profile/integration-profile.service.js +105 -31
- package/dist/integration-profile/integration-profile.service.js.map +1 -1
- package/dist/integration-profile/integration-profile.utils.d.ts +31 -1
- package/dist/integration-profile/integration-profile.utils.d.ts.map +1 -1
- package/dist/integration-profile/integration-profile.utils.js +35 -4
- package/dist/integration-profile/integration-profile.utils.js.map +1 -1
- package/dist/integration-profile/testers/ai.testers.d.ts +3 -0
- package/dist/integration-profile/testers/ai.testers.d.ts.map +1 -0
- package/dist/integration-profile/testers/ai.testers.js +125 -0
- package/dist/integration-profile/testers/ai.testers.js.map +1 -0
- package/dist/integration-profile/testers/captcha.testers.d.ts +3 -0
- package/dist/integration-profile/testers/captcha.testers.d.ts.map +1 -0
- package/dist/integration-profile/testers/captcha.testers.js +121 -0
- package/dist/integration-profile/testers/captcha.testers.js.map +1 -0
- package/dist/integration-profile/testers/email.testers.d.ts +3 -0
- package/dist/integration-profile/testers/email.testers.d.ts.map +1 -0
- package/dist/integration-profile/testers/email.testers.js +128 -0
- package/dist/integration-profile/testers/email.testers.js.map +1 -0
- package/dist/integration-profile/testers/http-probe.d.ts +57 -0
- package/dist/integration-profile/testers/http-probe.d.ts.map +1 -0
- package/dist/integration-profile/testers/http-probe.js +140 -0
- package/dist/integration-profile/testers/http-probe.js.map +1 -0
- package/dist/integration-profile/testers/index.d.ts +12 -0
- package/dist/integration-profile/testers/index.d.ts.map +1 -0
- package/dist/integration-profile/testers/index.js +45 -0
- package/dist/integration-profile/testers/index.js.map +1 -0
- package/dist/integration-profile/testers/infrastructure.testers.d.ts +3 -0
- package/dist/integration-profile/testers/infrastructure.testers.d.ts.map +1 -0
- package/dist/integration-profile/testers/infrastructure.testers.js +105 -0
- package/dist/integration-profile/testers/infrastructure.testers.js.map +1 -0
- package/dist/integration-profile/testers/integration-tester.registry.d.ts +17 -0
- package/dist/integration-profile/testers/integration-tester.registry.d.ts.map +1 -0
- package/dist/integration-profile/testers/integration-tester.registry.js +52 -0
- package/dist/integration-profile/testers/integration-tester.registry.js.map +1 -0
- package/dist/integration-profile/testers/integration-tester.types.d.ts +55 -0
- package/dist/integration-profile/testers/integration-tester.types.d.ts.map +1 -0
- package/dist/integration-profile/testers/integration-tester.types.js +16 -0
- package/dist/integration-profile/testers/integration-tester.types.js.map +1 -0
- package/dist/integration-profile/testers/messaging.testers.d.ts +3 -0
- package/dist/integration-profile/testers/messaging.testers.d.ts.map +1 -0
- package/dist/integration-profile/testers/messaging.testers.js +70 -0
- package/dist/integration-profile/testers/messaging.testers.js.map +1 -0
- package/dist/integration-profile/testers/oauth.testers.d.ts +3 -0
- package/dist/integration-profile/testers/oauth.testers.d.ts.map +1 -0
- package/dist/integration-profile/testers/oauth.testers.js +285 -0
- package/dist/integration-profile/testers/oauth.testers.js.map +1 -0
- package/dist/integration-profile/testers/payment.testers.d.ts +3 -0
- package/dist/integration-profile/testers/payment.testers.d.ts.map +1 -0
- package/dist/integration-profile/testers/payment.testers.js +169 -0
- package/dist/integration-profile/testers/payment.testers.js.map +1 -0
- package/dist/integration-profile/testers/storage.testers.d.ts +3 -0
- package/dist/integration-profile/testers/storage.testers.d.ts.map +1 -0
- package/dist/integration-profile/testers/storage.testers.js +208 -0
- package/dist/integration-profile/testers/storage.testers.js.map +1 -0
- package/dist/language/en.json +4 -2
- package/dist/language/pt.json +4 -2
- package/dist/leaked-password/leaked-password.controller.d.ts +24 -0
- package/dist/leaked-password/leaked-password.controller.d.ts.map +1 -0
- package/dist/leaked-password/leaked-password.controller.js +78 -0
- package/dist/leaked-password/leaked-password.controller.js.map +1 -0
- package/dist/leaked-password/leaked-password.module.d.ts +8 -0
- package/dist/leaked-password/leaked-password.module.d.ts.map +1 -0
- package/dist/leaked-password/leaked-password.module.js +31 -0
- package/dist/leaked-password/leaked-password.module.js.map +1 -0
- package/dist/leaked-password/leaked-password.service.d.ts +64 -0
- package/dist/leaked-password/leaked-password.service.d.ts.map +1 -0
- package/dist/leaked-password/leaked-password.service.js +218 -0
- package/dist/leaked-password/leaked-password.service.js.map +1 -0
- package/dist/locale/locale.controller.d.ts +1 -1
- package/dist/locale/locale.service.d.ts +1 -1
- package/dist/mail/dto/preview.dto.d.ts +18 -0
- package/dist/mail/dto/preview.dto.d.ts.map +1 -0
- package/dist/mail/dto/preview.dto.js +66 -0
- package/dist/mail/dto/preview.dto.js.map +1 -0
- package/dist/mail/dto/send.dto.d.ts +7 -0
- 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 +4 -0
- package/dist/mail/mail.controller.d.ts.map +1 -1
- package/dist/mail/mail.controller.js +16 -2
- package/dist/mail/mail.controller.js.map +1 -1
- package/dist/mail/mail.service.d.ts +23 -2
- package/dist/mail/mail.service.d.ts.map +1 -1
- package/dist/mail/mail.service.js +102 -13
- package/dist/mail/mail.service.js.map +1 -1
- package/dist/mail-layout/dto/update.dto.d.ts +17 -0
- package/dist/mail-layout/dto/update.dto.d.ts.map +1 -0
- package/dist/mail-layout/dto/update.dto.js +27 -0
- package/dist/mail-layout/dto/update.dto.js.map +1 -0
- package/dist/mail-layout/mail-layout.controller.d.ts +15 -0
- package/dist/mail-layout/mail-layout.controller.d.ts.map +1 -0
- package/dist/mail-layout/mail-layout.controller.js +71 -0
- package/dist/mail-layout/mail-layout.controller.js.map +1 -0
- package/dist/mail-layout/mail-layout.module.d.ts +3 -0
- package/dist/mail-layout/mail-layout.module.d.ts.map +1 -0
- package/dist/mail-layout/mail-layout.module.js +35 -0
- package/dist/mail-layout/mail-layout.module.js.map +1 -0
- package/dist/mail-layout/mail-layout.service.d.ts +27 -0
- package/dist/mail-layout/mail-layout.service.d.ts.map +1 -0
- package/dist/mail-layout/mail-layout.service.js +80 -0
- package/dist/mail-layout/mail-layout.service.js.map +1 -0
- package/dist/mcp-chat/mcp-chat.controller.d.ts +2 -2
- package/dist/mcp-chat/mcp-chat.service.d.ts +24 -2
- package/dist/mcp-chat/mcp-chat.service.d.ts.map +1 -1
- package/dist/mcp-chat/mcp-chat.service.js +80 -24
- package/dist/mcp-chat/mcp-chat.service.js.map +1 -1
- package/dist/menu/menu.controller.d.ts +3 -3
- package/dist/menu/menu.service.d.ts +3 -3
- package/dist/notification/notification.controller.d.ts +12 -12
- package/dist/notification/notification.service.d.ts +12 -12
- package/dist/oauth/oauth-token-broker.service.d.ts +72 -0
- package/dist/oauth/oauth-token-broker.service.d.ts.map +1 -0
- package/dist/oauth/oauth-token-broker.service.js +258 -0
- package/dist/oauth/oauth-token-broker.service.js.map +1 -0
- package/dist/oauth/oauth.module.d.ts.map +1 -1
- package/dist/oauth/oauth.module.js +3 -1
- package/dist/oauth/oauth.module.js.map +1 -1
- package/dist/oauth/oauth.service.d.ts +2 -0
- package/dist/oauth/oauth.service.d.ts.map +1 -1
- package/dist/oauth/oauth.service.js +6 -3
- package/dist/oauth/oauth.service.js.map +1 -1
- package/dist/pdf/pdf.service.d.ts +8 -0
- package/dist/pdf/pdf.service.d.ts.map +1 -1
- package/dist/pdf/pdf.service.js +52 -0
- package/dist/pdf/pdf.service.js.map +1 -1
- package/dist/presence/presence.controller.d.ts +16 -0
- package/dist/presence/presence.controller.d.ts.map +1 -0
- package/dist/presence/presence.controller.js +60 -0
- package/dist/presence/presence.controller.js.map +1 -0
- package/dist/presence/presence.listener.d.ts +29 -0
- package/dist/presence/presence.listener.d.ts.map +1 -0
- package/dist/presence/presence.listener.js +82 -0
- package/dist/presence/presence.listener.js.map +1 -0
- package/dist/presence/presence.module.d.ts +3 -0
- package/dist/presence/presence.module.d.ts.map +1 -0
- package/dist/presence/presence.module.js +32 -0
- package/dist/presence/presence.module.js.map +1 -0
- package/dist/presence/presence.service.d.ts +42 -0
- package/dist/presence/presence.service.d.ts.map +1 -0
- package/dist/presence/presence.service.js +147 -0
- package/dist/presence/presence.service.js.map +1 -0
- package/dist/profile/dto/delete-account.dto.d.ts +8 -0
- package/dist/profile/dto/delete-account.dto.d.ts.map +1 -0
- package/dist/profile/dto/delete-account.dto.js +22 -0
- package/dist/profile/dto/delete-account.dto.js.map +1 -0
- package/dist/profile/events/profile-account-deleted.event.d.ts +12 -0
- package/dist/profile/events/profile-account-deleted.event.d.ts.map +1 -0
- package/dist/profile/events/profile-account-deleted.event.js +17 -0
- package/dist/profile/events/profile-account-deleted.event.js.map +1 -0
- package/dist/profile/profile.controller.d.ts +24 -17
- package/dist/profile/profile.controller.d.ts.map +1 -1
- package/dist/profile/profile.controller.js +75 -46
- package/dist/profile/profile.controller.js.map +1 -1
- package/dist/profile/profile.service.d.ts +34 -21
- package/dist/profile/profile.service.d.ts.map +1 -1
- package/dist/profile/profile.service.js +113 -37
- package/dist/profile/profile.service.js.map +1 -1
- package/dist/realtime/realtime.gateway.d.ts +48 -0
- package/dist/realtime/realtime.gateway.d.ts.map +1 -0
- package/dist/realtime/realtime.gateway.js +156 -0
- package/dist/realtime/realtime.gateway.js.map +1 -0
- package/dist/realtime/realtime.module.d.ts +8 -0
- package/dist/realtime/realtime.module.d.ts.map +1 -0
- package/dist/realtime/realtime.module.js +29 -0
- package/dist/realtime/realtime.module.js.map +1 -0
- package/dist/realtime/realtime.service.d.ts +51 -0
- package/dist/realtime/realtime.service.d.ts.map +1 -0
- package/dist/realtime/realtime.service.js +177 -0
- package/dist/realtime/realtime.service.js.map +1 -0
- package/dist/redis/redis-pubsub.d.ts +27 -0
- package/dist/redis/redis-pubsub.d.ts.map +1 -0
- package/dist/redis/redis-pubsub.js +140 -0
- package/dist/redis/redis-pubsub.js.map +1 -0
- package/dist/role/role.controller.d.ts +3 -3
- package/dist/role/role.service.d.ts +3 -3
- package/dist/setting/dto/create.dto.d.ts.map +1 -1
- package/dist/setting/dto/create.dto.js +9 -2
- package/dist/setting/dto/create.dto.js.map +1 -1
- package/dist/setting/setting.controller.d.ts +5 -5
- package/dist/setting/setting.service.d.ts +8 -5
- package/dist/setting/setting.service.d.ts.map +1 -1
- package/dist/setting/setting.service.js +25 -1
- package/dist/setting/setting.service.js.map +1 -1
- package/dist/user/dto/create-with-email-and-password.dto.d.ts +2 -0
- package/dist/user/dto/create-with-email-and-password.dto.d.ts.map +1 -1
- package/dist/user/dto/create-with-email-and-password.dto.js +5 -0
- package/dist/user/dto/create-with-email-and-password.dto.js.map +1 -1
- package/dist/user/dto/query-user-mail-list.dto.d.ts +5 -0
- package/dist/user/dto/query-user-mail-list.dto.d.ts.map +1 -0
- package/dist/user/dto/query-user-mail-list.dto.js +25 -0
- package/dist/user/dto/query-user-mail-list.dto.js.map +1 -0
- package/dist/user/user-credential.util.d.ts +19 -0
- package/dist/user/user-credential.util.d.ts.map +1 -0
- package/dist/user/user-credential.util.js +32 -0
- package/dist/user/user-credential.util.js.map +1 -0
- package/dist/user/user.controller.d.ts +17 -5
- package/dist/user/user.controller.d.ts.map +1 -1
- package/dist/user/user.controller.js +18 -3
- package/dist/user/user.controller.js.map +1 -1
- package/dist/user/user.service.d.ts +28 -6
- package/dist/user/user.service.d.ts.map +1 -1
- package/dist/user/user.service.js +204 -13
- package/dist/user/user.service.js.map +1 -1
- package/dist/validators/is-strong-password-with-settings.validator.d.ts.map +1 -1
- package/dist/validators/is-strong-password-with-settings.validator.js +11 -0
- package/dist/validators/is-strong-password-with-settings.validator.js.map +1 -1
- package/dist/validators/service-locator.d.ts +9 -0
- package/dist/validators/service-locator.d.ts.map +1 -1
- package/dist/validators/service-locator.js +12 -0
- package/dist/validators/service-locator.js.map +1 -1
- package/dist/webhook-integration/webhook-integration.controller.d.ts +5 -8
- package/dist/webhook-integration/webhook-integration.controller.d.ts.map +1 -1
- package/dist/webhook-integration/webhook-integration.controller.js +31 -8
- package/dist/webhook-integration/webhook-integration.controller.js.map +1 -1
- package/dist/webhook-integration/webhook-integration.module.d.ts.map +1 -1
- package/dist/webhook-integration/webhook-integration.module.js +5 -0
- package/dist/webhook-integration/webhook-integration.module.js.map +1 -1
- package/dist/webhook-integration/webhook-integration.service.d.ts +33 -1
- package/dist/webhook-integration/webhook-integration.service.d.ts.map +1 -1
- package/dist/webhook-integration/webhook-integration.service.js +109 -4
- package/dist/webhook-integration/webhook-integration.service.js.map +1 -1
- package/dist/webhook-integration/webhook-protocol.registry.d.ts +63 -0
- package/dist/webhook-integration/webhook-protocol.registry.d.ts.map +1 -0
- package/dist/webhook-integration/webhook-protocol.registry.js +51 -0
- package/dist/webhook-integration/webhook-protocol.registry.js.map +1 -0
- package/dist/whatsapp-sender/whatsapp-sender.module.d.ts +3 -0
- package/dist/whatsapp-sender/whatsapp-sender.module.d.ts.map +1 -0
- package/dist/whatsapp-sender/whatsapp-sender.module.js +23 -0
- package/dist/whatsapp-sender/whatsapp-sender.module.js.map +1 -0
- package/dist/whatsapp-sender/whatsapp-sender.service.d.ts +50 -0
- package/dist/whatsapp-sender/whatsapp-sender.service.d.ts.map +1 -0
- package/dist/whatsapp-sender/whatsapp-sender.service.js +224 -0
- package/dist/whatsapp-sender/whatsapp-sender.service.js.map +1 -0
- package/hedhog/data/integration_provider.yaml +16 -0
- package/hedhog/data/mail.yaml +88 -61
- package/hedhog/data/mail_layout.yaml +188 -0
- package/hedhog/data/menu.yaml +67 -3
- package/hedhog/data/route.yaml +135 -0
- package/hedhog/data/setting_group.yaml +139 -1
- package/hedhog/frontend/app/account/components/mfa-setup-dialog.tsx.ejs +36 -0
- package/hedhog/frontend/app/account/components/two-factor-auth.tsx.ejs +2 -0
- package/hedhog/frontend/app/account/hooks/use-mfa-setup.ts.ejs +10 -7
- package/hedhog/frontend/app/ai_agent/page.tsx.ejs +7 -4
- package/hedhog/frontend/app/ai_instruction/_components/ai-instruction-form.tsx.ejs +580 -0
- package/hedhog/frontend/app/ai_instruction/_components/ai-instruction-preview.tsx.ejs +150 -0
- package/hedhog/frontend/app/ai_instruction/_components/ai-instruction-versions.tsx.ejs +199 -0
- package/hedhog/frontend/app/ai_instruction/_components/prompt-diff.tsx.ejs +63 -0
- package/hedhog/frontend/app/ai_instruction/_lib/line-diff.spec.ts.ejs +59 -0
- package/hedhog/frontend/app/ai_instruction/_lib/line-diff.ts.ejs +72 -0
- package/hedhog/frontend/app/ai_instruction/_lib/prompt-template.spec.ts.ejs +170 -0
- package/hedhog/frontend/app/ai_instruction/_lib/prompt-template.ts.ejs +167 -0
- package/hedhog/frontend/app/ai_instruction/_lib/types.ts.ejs +99 -0
- package/hedhog/frontend/app/ai_instruction/page.tsx.ejs +422 -0
- package/hedhog/frontend/app/configurations/[slug]/components/setting-field.tsx.ejs +51 -4
- package/hedhog/frontend/app/incidents/page.tsx.ejs +400 -0
- package/hedhog/frontend/app/integration/profiles/page.tsx.ejs +169 -92
- package/hedhog/frontend/app/mail/layout/page.tsx.ejs +344 -0
- package/hedhog/frontend/app/mail/template/page.tsx.ejs +155 -121
- package/hedhog/frontend/app/mcp_chat/page.tsx.ejs +129 -17
- package/hedhog/frontend/app/pages/_components/page-delete-dialog.tsx.ejs +343 -0
- package/hedhog/frontend/app/pages/_components/page-detail-sheet.tsx.ejs +438 -0
- package/hedhog/frontend/app/pages/_components/page-wizard-sheet.tsx.ejs +1009 -0
- package/hedhog/frontend/app/pages/_components/system-page-types.ts.ejs +56 -0
- package/hedhog/frontend/app/pages/_components/use-system-pages.ts.ejs +171 -0
- package/hedhog/frontend/app/pages/page.tsx.ejs +514 -0
- package/hedhog/frontend/app/users/page.tsx.ejs +119 -13
- package/hedhog/frontend/app/users/user-activity-timeline.tsx.ejs +231 -0
- package/hedhog/frontend/app/users/user-mails.tsx.ejs +399 -0
- package/hedhog/frontend/app/users/user-sheet.tsx.ejs +60 -75
- package/hedhog/frontend/messages/en.json +521 -3
- package/hedhog/frontend/messages/pt.json +521 -3
- package/hedhog/table/ai_agent.yaml +1 -0
- package/hedhog/table/ai_instruction.yaml +15 -0
- package/hedhog/table/leaked_password.yaml +18 -0
- package/hedhog/table/mail_layout.yaml +35 -0
- package/hedhog/table/mail_var.yaml +4 -0
- package/hedhog/table/setting.yaml +1 -0
- package/hedhog/table/system_incident.yaml +59 -0
- package/hedhog/table/user_presence.yaml +36 -0
- package/hedhog/table/webhook_integration.yaml +64 -43
- package/package.json +8 -4
- package/src/access-log/access-log.interceptor.ts +4 -2
- package/src/ai/ai-attachment.fragments.spec.ts +319 -0
- package/src/ai/ai-attachment.fragments.ts +35 -0
- package/src/ai/ai.prompts.ts +38 -0
- package/src/ai/ai.service.spec.ts +280 -0
- package/src/ai/ai.service.ts +338 -82
- package/src/ai/dto/chat.dto.ts +33 -3
- package/src/ai/dto/create-agent.dto.ts +2 -2
- package/src/ai/dto/update-agent.dto.ts +2 -2
- package/src/ai-instruction/ai-instruction.controller.ts +40 -9
- package/src/ai-instruction/ai-instruction.module.ts +13 -2
- package/src/ai-instruction/ai-instruction.service.spec.ts +658 -0
- package/src/ai-instruction/ai-instruction.service.ts +661 -62
- package/src/ai-instruction/ai-prompt-builder.service.ts +10 -1
- package/src/ai-instruction/ai-prompt-registry.ts +87 -0
- package/src/ai-instruction/ai-prompt-resolver.service.spec.ts +375 -0
- package/src/ai-instruction/ai-prompt-resolver.service.ts +295 -0
- package/src/ai-instruction/ai-prompt-template.util.spec.ts +162 -0
- package/src/ai-instruction/ai-prompt-template.util.ts +199 -0
- package/src/ai-instruction/dto/create-ai-instruction.dto.ts +39 -3
- package/src/ai-instruction/dto/preview-ai-instruction.dto.ts +26 -0
- package/src/ai-instruction/dto/update-ai-instruction.dto.ts +42 -1
- package/src/ai-instruction/types/ai-instruction.types.ts +24 -1
- package/src/auth/auth.controller.ts +33 -6
- package/src/auth/auth.service.spec.ts +3 -1
- package/src/auth/auth.service.ts +103 -37
- package/src/captcha/captcha.module.ts +10 -0
- package/src/captcha/captcha.service.spec.ts +210 -0
- package/src/captcha/captcha.service.ts +191 -0
- package/src/challenge/challenge.service.spec.ts +39 -4
- package/src/challenge/challenge.service.ts +72 -17
- package/src/core.module.ts +27 -1
- package/src/file/file.service.spec.ts +105 -0
- package/src/file/file.service.ts +60 -3
- package/src/incident/incident-notifier.service.spec.ts +205 -0
- package/src/incident/incident-notifier.service.ts +241 -0
- package/src/incident/incident.controller.ts +38 -0
- package/src/incident/incident.module.ts +19 -0
- package/src/incident/incident.service.spec.ts +150 -0
- package/src/incident/incident.service.ts +209 -0
- package/src/index.ts +38 -0
- package/src/integration-profile/dto/test.dto.ts +19 -0
- package/src/integration-profile/integration-profile.controller.ts +10 -1
- package/src/integration-profile/integration-profile.module.spec.ts +51 -0
- package/src/integration-profile/integration-profile.module.ts +23 -4
- package/src/integration-profile/integration-profile.secrets.ts +12 -2
- package/src/integration-profile/integration-profile.service.spec.ts +279 -0
- package/src/integration-profile/integration-profile.service.ts +132 -29
- package/src/integration-profile/integration-profile.utils.spec.ts +97 -0
- package/src/integration-profile/integration-profile.utils.ts +79 -3
- package/src/integration-profile/testers/ai.testers.ts +165 -0
- package/src/integration-profile/testers/captcha.testers.spec.ts +92 -0
- package/src/integration-profile/testers/captcha.testers.ts +139 -0
- package/src/integration-profile/testers/email.testers.ts +161 -0
- package/src/integration-profile/testers/http-probe.spec.ts +153 -0
- package/src/integration-profile/testers/http-probe.ts +171 -0
- package/src/integration-profile/testers/index.ts +30 -0
- package/src/integration-profile/testers/infrastructure.testers.ts +122 -0
- package/src/integration-profile/testers/integration-tester.registry.spec.ts +75 -0
- package/src/integration-profile/testers/integration-tester.registry.ts +43 -0
- package/src/integration-profile/testers/integration-tester.types.ts +61 -0
- package/src/integration-profile/testers/messaging.testers.ts +102 -0
- package/src/integration-profile/testers/oauth.testers.spec.ts +220 -0
- package/src/integration-profile/testers/oauth.testers.ts +305 -0
- package/src/integration-profile/testers/payment.testers.ts +185 -0
- package/src/integration-profile/testers/remaining.testers.spec.ts +401 -0
- package/src/integration-profile/testers/storage.testers.spec.ts +185 -0
- package/src/integration-profile/testers/storage.testers.ts +226 -0
- package/src/integration-profile/testers/tester-coverage.spec.ts +58 -0
- package/src/language/en.json +4 -2
- package/src/language/pt.json +4 -2
- package/src/leaked-password/README.md +83 -0
- package/src/leaked-password/leaked-password.controller.ts +52 -0
- package/src/leaked-password/leaked-password.module.ts +18 -0
- package/src/leaked-password/leaked-password.service.ts +242 -0
- package/src/leaked-password/seed/build-seed-sql.mjs +180 -0
- package/src/leaked-password/seed/common-passwords.txt +335 -0
- package/src/mail/dto/preview.dto.ts +50 -0
- package/src/mail/dto/send.dto.ts +14 -1
- package/src/mail/mail-template.spec.ts +197 -0
- package/src/mail/mail.controller.ts +9 -2
- package/src/mail/mail.service.spec.ts +116 -1
- package/src/mail/mail.service.ts +118 -12
- package/src/mail-layout/dto/update.dto.ts +19 -0
- package/src/mail-layout/mail-layout.controller.ts +47 -0
- package/src/mail-layout/mail-layout.module.ts +22 -0
- package/src/mail-layout/mail-layout.service.ts +94 -0
- package/src/mcp-chat/mcp-chat.service.spec.ts +236 -0
- package/src/mcp-chat/mcp-chat.service.ts +122 -26
- package/src/oauth/oauth-token-broker.service.spec.ts +172 -0
- package/src/oauth/oauth-token-broker.service.ts +363 -0
- package/src/oauth/oauth.module.ts +3 -1
- package/src/oauth/oauth.service.ts +8 -2
- package/src/pdf/pdf.service.ts +75 -0
- package/src/presence/presence.controller.ts +29 -0
- package/src/presence/presence.listener.ts +53 -0
- package/src/presence/presence.module.ts +19 -0
- package/src/presence/presence.service.spec.ts +97 -0
- package/src/presence/presence.service.ts +160 -0
- package/src/profile/dto/delete-account.dto.ts +11 -0
- package/src/profile/events/profile-account-deleted.event.ts +13 -0
- package/src/profile/profile.controller.ts +52 -36
- package/src/profile/profile.service.ts +143 -41
- package/src/realtime/realtime.gateway.ts +138 -0
- package/src/realtime/realtime.module.ts +16 -0
- package/src/realtime/realtime.service.spec.ts +50 -0
- package/src/realtime/realtime.service.ts +187 -0
- package/src/redis/redis-pubsub.ts +179 -0
- package/src/setting/dto/create.dto.ts +13 -2
- package/src/setting/setting.service.spec.ts +56 -0
- package/src/setting/setting.service.ts +29 -1
- package/src/user/dto/create-with-email-and-password.dto.ts +6 -1
- package/src/user/dto/query-user-mail-list.dto.ts +10 -0
- package/src/user/user-credential.util.ts +35 -0
- package/src/user/user.controller.ts +17 -2
- package/src/user/user.service.spec.ts +153 -0
- package/src/user/user.service.ts +255 -12
- package/src/validators/is-strong-password-with-settings.validator.ts +12 -0
- package/src/validators/service-locator.ts +15 -0
- package/src/webhook-integration/webhook-integration.controller.ts +40 -2
- package/src/webhook-integration/webhook-integration.module.ts +5 -0
- package/src/webhook-integration/webhook-integration.service.ts +133 -0
- package/src/webhook-integration/webhook-protocol.registry.spec.ts +70 -0
- package/src/webhook-integration/webhook-protocol.registry.ts +93 -0
- package/src/whatsapp-sender/whatsapp-sender.module.ts +10 -0
- package/src/whatsapp-sender/whatsapp-sender.service.ts +313 -0
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { getLocaleText } from '@hed-hog/api-locale';
|
|
2
|
-
import { PrismaService
|
|
2
|
+
import { PrismaService } from '@hed-hog/api-prisma';
|
|
3
3
|
import { User } from '@hed-hog/api-types';
|
|
4
4
|
import { BadRequestException, forwardRef, Inject, Injectable, NotFoundException } from '@nestjs/common';
|
|
5
5
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
|
6
6
|
import { JwtService } from '@nestjs/jwt';
|
|
7
7
|
import type {
|
|
8
|
+
PublicKeyCredentialCreationOptionsJSON,
|
|
9
|
+
PublicKeyCredentialRequestOptionsJSON,
|
|
8
10
|
VerifiedAuthenticationResponse,
|
|
9
11
|
VerifiedRegistrationResponse,
|
|
10
12
|
} from '@simplewebauthn/server';
|
|
@@ -25,10 +27,12 @@ import { UserService } from '../user/user.service';
|
|
|
25
27
|
import { resolveWebAuthnOrigin } from '../utils/webauthn-origin';
|
|
26
28
|
import { ChangeEmailDto } from './dto/change-email.dto';
|
|
27
29
|
import { ChangePasswordDto } from './dto/change-password.dto';
|
|
30
|
+
import { DeleteAccountDto } from './dto/delete-account.dto';
|
|
28
31
|
import { EmailVerificationDto } from './dto/email-verification-confirm.dto';
|
|
29
32
|
import { SendEmailVerificationDto } from './dto/send-email-verification.dto';
|
|
30
33
|
import { SetPreferencesDto } from './dto/set-preferences.dto';
|
|
31
34
|
import { UpdateDto } from './dto/update.dto';
|
|
35
|
+
import { PROFILE_ACCOUNT_DELETED, ProfileAccountDeletedEvent } from './events/profile-account-deleted.event';
|
|
32
36
|
import { PROFILE_AVATAR_UPDATED, ProfileAvatarUpdatedEvent } from './events/profile-avatar-updated.event';
|
|
33
37
|
import { PROFILE_EMAIL_CHANGED, ProfileEmailChangedEvent } from './events/profile-email-changed.event';
|
|
34
38
|
import { PROFILE_UPDATED, ProfileUpdatedEvent } from './events/profile-updated.event';
|
|
@@ -52,7 +56,7 @@ export class ProfileService {
|
|
|
52
56
|
private readonly eventEmitter: EventEmitter2,
|
|
53
57
|
) {}
|
|
54
58
|
|
|
55
|
-
async changeEmail(locale: string, userId: number, { email, password, pin }: ChangeEmailDto) {
|
|
59
|
+
async changeEmail(locale: string, userId: number, { email, password, pin }: ChangeEmailDto, origin?: string) {
|
|
56
60
|
|
|
57
61
|
const checkAlreadyExists = await this.prisma.user_identifier.findFirst({
|
|
58
62
|
where: { type: 'email', value: email, enabled: true, verified_at: { not: null } },
|
|
@@ -125,7 +129,7 @@ export class ProfileService {
|
|
|
125
129
|
});
|
|
126
130
|
|
|
127
131
|
// Create verification challenge for the new email
|
|
128
|
-
await this.challenge.verifyEmail(locale, userId, email);
|
|
132
|
+
await this.challenge.verifyEmail(locale, userId, email, undefined, origin);
|
|
129
133
|
|
|
130
134
|
});
|
|
131
135
|
|
|
@@ -148,7 +152,7 @@ export class ProfileService {
|
|
|
148
152
|
return { success: true };
|
|
149
153
|
}
|
|
150
154
|
|
|
151
|
-
async confirmEmailVerificationMfa(locale: string, userId: number, { pin, challengeId, name }: EmailVerificationDto) {
|
|
155
|
+
async confirmEmailVerificationMfa(locale: string, userId: number, { pin, challengeId, name }: EmailVerificationDto, sessionId: number | null) {
|
|
152
156
|
|
|
153
157
|
const user = await this.prisma.user.findUnique({
|
|
154
158
|
where: { id: userId },
|
|
@@ -221,9 +225,8 @@ export class ProfileService {
|
|
|
221
225
|
|
|
222
226
|
});
|
|
223
227
|
|
|
224
|
-
const
|
|
225
|
-
const
|
|
226
|
-
const newToken = await this.getToken(updatedUser);
|
|
228
|
+
const codes = await this.createMfaRecoveryCodes(userId);
|
|
229
|
+
const newToken = await this.getToken(userId, sessionId);
|
|
227
230
|
return { ...newToken, codes };
|
|
228
231
|
|
|
229
232
|
}
|
|
@@ -300,16 +303,16 @@ export class ProfileService {
|
|
|
300
303
|
return { success: true };
|
|
301
304
|
}
|
|
302
305
|
|
|
303
|
-
async sendEmailVerificationMfa(locale: string, userId: number, { email }: SendEmailVerificationDto) {
|
|
304
|
-
return this.challenge.verifyMfaEmail(locale, userId, email);
|
|
306
|
+
async sendEmailVerificationMfa(locale: string, userId: number, { email }: SendEmailVerificationDto, origin?: string) {
|
|
307
|
+
return this.challenge.verifyMfaEmail(locale, userId, email, origin);
|
|
305
308
|
}
|
|
306
309
|
|
|
307
|
-
async sendEmailVerification(locale: string, userId: number, { email }: SendEmailVerificationDto) {
|
|
308
|
-
return this.challenge.verifyEmail(locale, userId, email);
|
|
310
|
+
async sendEmailVerification(locale: string, userId: number, { email }: SendEmailVerificationDto, origin?: string) {
|
|
311
|
+
return this.challenge.verifyEmail(locale, userId, email, undefined, origin);
|
|
309
312
|
}
|
|
310
313
|
|
|
311
|
-
async sendCodeToRemove(locale, userId, email){
|
|
312
|
-
return this.challenge.verifyEmail(locale, userId, email)
|
|
314
|
+
async sendCodeToRemove(locale, userId, email, origin?: string){
|
|
315
|
+
return this.challenge.verifyEmail(locale, userId, email, undefined, origin)
|
|
313
316
|
}
|
|
314
317
|
|
|
315
318
|
async getEmailIdentifier(userId: number) {
|
|
@@ -357,6 +360,84 @@ export class ProfileService {
|
|
|
357
360
|
return result;
|
|
358
361
|
}
|
|
359
362
|
|
|
363
|
+
/**
|
|
364
|
+
* Exclusão de conta pelo próprio usuário (exigida pelas lojas de aplicativos).
|
|
365
|
+
*
|
|
366
|
+
* Anonimiza em vez de apagar: os dados pessoais somem e o acesso é revogado
|
|
367
|
+
* em definitivo, mas as linhas continuam existindo para não quebrar registros
|
|
368
|
+
* fiscais e integridade referencial. Os identificadores (e-mail, CPF...) são
|
|
369
|
+
* embaralhados, o que libera o e-mail original para uma conta nova no futuro.
|
|
370
|
+
*/
|
|
371
|
+
async deleteAccount(locale: string, userId: number, { password }: DeleteAccountDto) {
|
|
372
|
+
const user = await this.prisma.user.findUnique({ where: { id: userId } });
|
|
373
|
+
|
|
374
|
+
if (!user) {
|
|
375
|
+
throw new NotFoundException(getLocaleText('userNotFound', locale, 'User not found.'));
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if (user.deactivated_at) {
|
|
379
|
+
throw new BadRequestException(getLocaleText('accessDenied', locale, 'Access denied.'));
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const credentials = await this.prisma.user_credential.findMany({
|
|
383
|
+
where: { user_id: userId, type: 'password' },
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
// Conta com senha exige a senha; conta só de login social não tem o que pedir.
|
|
387
|
+
if (credentials.length > 0) {
|
|
388
|
+
if (!password || !(await this.security.validatePassword(locale, credentials, password))) {
|
|
389
|
+
throw new BadRequestException(getLocaleText('accessDenied', locale, 'Access denied.'));
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const deletedAt = new Date();
|
|
394
|
+
const suffix = `${userId}-${deletedAt.getTime()}`;
|
|
395
|
+
|
|
396
|
+
await this.prisma.$transaction(async (tx) => {
|
|
397
|
+
// Identificadores primeiro: libera e-mail/CPF para reuso e impede o login,
|
|
398
|
+
// que resolve o usuário justamente por essa tabela.
|
|
399
|
+
const identifiers = await tx.user_identifier.findMany({
|
|
400
|
+
where: { user_id: userId },
|
|
401
|
+
select: { id: true, type: true },
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
for (const identifier of identifiers) {
|
|
405
|
+
await tx.user_identifier.update({
|
|
406
|
+
where: { id: identifier.id },
|
|
407
|
+
data: {
|
|
408
|
+
value: `deleted-${identifier.type}-${suffix}`,
|
|
409
|
+
enabled: false,
|
|
410
|
+
verified_at: null,
|
|
411
|
+
},
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Credenciais, sessões e segundos fatores: nada aqui pode sobreviver,
|
|
416
|
+
// ou restaria um caminho de acesso à conta anonimizada.
|
|
417
|
+
await tx.user_identifier_challenge.deleteMany({ where: { user_identifier: { user_id: userId } } });
|
|
418
|
+
await tx.user_credential.deleteMany({ where: { user_id: userId } });
|
|
419
|
+
await tx.user_session.deleteMany({ where: { user_id: userId } });
|
|
420
|
+
await tx.user_mfa_challenge.deleteMany({ where: { user_mfa: { user_id: userId } } });
|
|
421
|
+
await tx.user_mfa.deleteMany({ where: { user_id: userId } });
|
|
422
|
+
await tx.user_recovery_code.deleteMany({ where: { user_id: userId } });
|
|
423
|
+
await tx.user_account.deleteMany({ where: { user_id: userId } });
|
|
424
|
+
|
|
425
|
+
await tx.user.update({
|
|
426
|
+
where: { id: userId },
|
|
427
|
+
data: {
|
|
428
|
+
name: 'Usuário removido',
|
|
429
|
+
photo_id: null,
|
|
430
|
+
deactivated_at: deletedAt,
|
|
431
|
+
},
|
|
432
|
+
});
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
// Cada library cuida dos seus próprios dados (assinatura, progresso, mensagens).
|
|
436
|
+
this.eventEmitter.emit(PROFILE_ACCOUNT_DELETED, new ProfileAccountDeletedEvent(userId, deletedAt));
|
|
437
|
+
|
|
438
|
+
return { success: true };
|
|
439
|
+
}
|
|
440
|
+
|
|
360
441
|
async updateAvatar(locale: string, userId: number, file: MulterFile) {
|
|
361
442
|
const result = await this.user.changeAvatar(locale, userId, file);
|
|
362
443
|
this.eventEmitter.emit(PROFILE_AVATAR_UPDATED, new ProfileAvatarUpdatedEvent(userId, result?.id ?? null));
|
|
@@ -364,7 +445,7 @@ export class ProfileService {
|
|
|
364
445
|
return result;
|
|
365
446
|
}
|
|
366
447
|
|
|
367
|
-
async changePassword(locale:string, userId: number, {currentPassword, newPassword}: ChangePasswordDto, ipAddress?: string, userAgent?: string) {
|
|
448
|
+
async changePassword(locale:string, userId: number, {currentPassword, newPassword}: ChangePasswordDto, ipAddress?: string, userAgent?: string, origin?: string) {
|
|
368
449
|
const credential = await this.prisma.user_credential.findFirst({
|
|
369
450
|
where: { user_id: userId, type: 'password' },
|
|
370
451
|
});
|
|
@@ -405,6 +486,7 @@ export class ProfileService {
|
|
|
405
486
|
ipAddress: ipAddress || 'Unknown',
|
|
406
487
|
location: 'Unknown',
|
|
407
488
|
},
|
|
489
|
+
app: await this.challenge.resolveAppKeyFromOrigin(origin),
|
|
408
490
|
}).catch(error => {
|
|
409
491
|
console.error('Failed to send password change notification:', error);
|
|
410
492
|
});
|
|
@@ -446,11 +528,11 @@ export class ProfileService {
|
|
|
446
528
|
return codes;
|
|
447
529
|
}
|
|
448
530
|
|
|
449
|
-
async getToken(
|
|
450
|
-
return { token: this.
|
|
531
|
+
async getToken(userId: number, sessionId: number | null) {
|
|
532
|
+
return { token: await this.token.createAccessToken({ sub: userId, sessionId }) };
|
|
451
533
|
}
|
|
452
534
|
|
|
453
|
-
async verifyMfaTotp(locale: string, userId: number, token: string, secret: string, name?: string) {
|
|
535
|
+
async verifyMfaTotp(locale: string, userId: number, token: string, secret: string, name?: string, sessionId?: number | null) {
|
|
454
536
|
const settings = await this.setting.getSettingValues([
|
|
455
537
|
'mfa-issuer',
|
|
456
538
|
'mfa-window',
|
|
@@ -512,9 +594,8 @@ export class ProfileService {
|
|
|
512
594
|
});
|
|
513
595
|
}
|
|
514
596
|
|
|
515
|
-
const
|
|
516
|
-
const
|
|
517
|
-
const newToken = await this.getToken(updatedUser);
|
|
597
|
+
const codes = await this.createMfaRecoveryCodes(userId);
|
|
598
|
+
const newToken = await this.getToken(userId, sessionId ?? null);
|
|
518
599
|
return { ...newToken, codes };
|
|
519
600
|
}
|
|
520
601
|
|
|
@@ -567,7 +648,7 @@ export class ProfileService {
|
|
|
567
648
|
return { otpauthUrl: secret.otpauth_url, qrCode, secret: encodedSecret };
|
|
568
649
|
}
|
|
569
650
|
|
|
570
|
-
async removeMfaTotp(locale: string, userId: number, mfaId: number, token: string) {
|
|
651
|
+
async removeMfaTotp(locale: string, userId: number, mfaId: number, token: string, sessionId: number | null) {
|
|
571
652
|
try {
|
|
572
653
|
const user = await this.prisma.user.findFirst({
|
|
573
654
|
where: { id: userId },
|
|
@@ -604,26 +685,26 @@ export class ProfileService {
|
|
|
604
685
|
});
|
|
605
686
|
});
|
|
606
687
|
|
|
607
|
-
return this.getUserToken(userId)
|
|
688
|
+
return this.getUserToken(userId, sessionId)
|
|
608
689
|
} catch (error: any) {
|
|
609
690
|
throw new BadRequestException(`Invalid code: ${error?.message}`);
|
|
610
691
|
}
|
|
611
692
|
}
|
|
612
693
|
|
|
613
|
-
async removeMfaEmail(locale: string, userId: number, mfaId: number, token: string, providedHash: string) {
|
|
694
|
+
async removeMfaEmail(locale: string, userId: number, mfaId: number, token: string, providedHash: string, sessionId: number | null) {
|
|
614
695
|
try {
|
|
615
696
|
await this.challenge.verifyCode(locale, userId, token, providedHash)
|
|
616
697
|
await this.prisma.user_mfa.deleteMany({
|
|
617
698
|
where: { id: mfaId, user_id: userId, type: 'email' },
|
|
618
699
|
});
|
|
619
700
|
|
|
620
|
-
return this.getUserToken(userId)
|
|
701
|
+
return this.getUserToken(userId, sessionId)
|
|
621
702
|
} catch (error: any) {
|
|
622
703
|
throw new BadRequestException(`Invalid code: ${error?.message}`);
|
|
623
704
|
}
|
|
624
705
|
}
|
|
625
706
|
|
|
626
|
-
async removeMfaWithRecoveryCode(locale: string, userId: number, mfaId: number, recoveryCode: string) {
|
|
707
|
+
async removeMfaWithRecoveryCode(locale: string, userId: number, mfaId: number, recoveryCode: string, sessionId: number | null) {
|
|
627
708
|
try {
|
|
628
709
|
const user = await this.prisma.user.findUnique({
|
|
629
710
|
where: { id: userId },
|
|
@@ -657,13 +738,13 @@ export class ProfileService {
|
|
|
657
738
|
where: { id: mfaId, user_id: userId },
|
|
658
739
|
});
|
|
659
740
|
|
|
660
|
-
return this.getUserToken(userId);
|
|
741
|
+
return this.getUserToken(userId, sessionId);
|
|
661
742
|
} catch (error: any) {
|
|
662
743
|
throw new BadRequestException(`Invalid code: ${error?.message}`);
|
|
663
744
|
}
|
|
664
745
|
}
|
|
665
746
|
|
|
666
|
-
async removeMfa(locale: string, userId: number, mfaId: number, data: { token?: string; hash?: string; verificationType?: 'totp' | 'email' | 'recovery' | 'webauthn'; assertionResponse?: any }, requestOrigin
|
|
747
|
+
async removeMfa(locale: string, userId: number, mfaId: number, data: { token?: string; hash?: string; verificationType?: 'totp' | 'email' | 'recovery' | 'webauthn'; assertionResponse?: any }, requestOrigin: string | undefined, sessionId: number | null) {
|
|
667
748
|
try {
|
|
668
749
|
const user = await this.prisma.user.findUnique({
|
|
669
750
|
where: { id: userId },
|
|
@@ -784,13 +865,13 @@ export class ProfileService {
|
|
|
784
865
|
where: { id: mfaId, user_id: userId },
|
|
785
866
|
});
|
|
786
867
|
|
|
787
|
-
return this.getUserToken(userId);
|
|
868
|
+
return this.getUserToken(userId, sessionId);
|
|
788
869
|
} catch (error: any) {
|
|
789
870
|
throw new BadRequestException(`Failed to remove MFA: ${error?.message}`);
|
|
790
871
|
}
|
|
791
872
|
}
|
|
792
873
|
|
|
793
|
-
async checkMfaBeforeAdd(locale: string, userId: number) {
|
|
874
|
+
async checkMfaBeforeAdd(locale: string, userId: number, origin?: string) {
|
|
794
875
|
try {
|
|
795
876
|
const mfaMethods = await this.prisma.user_mfa.findMany({
|
|
796
877
|
where: {
|
|
@@ -820,7 +901,7 @@ export class ProfileService {
|
|
|
820
901
|
|
|
821
902
|
if (emailMfas.length > 0) {
|
|
822
903
|
const emails = emailMfas.map(mfa => mfa.user_mfa_email[0].email);
|
|
823
|
-
await this.challenge.sendMfaCodeToMultipleEmails(locale, userId, emails);
|
|
904
|
+
await this.challenge.sendMfaCodeToMultipleEmails(locale, userId, emails, origin);
|
|
824
905
|
availableMethods.push('email');
|
|
825
906
|
}
|
|
826
907
|
|
|
@@ -857,7 +938,7 @@ export class ProfileService {
|
|
|
857
938
|
}
|
|
858
939
|
}
|
|
859
940
|
|
|
860
|
-
async checkMfaBeforeRemove(locale: string, userId: number) {
|
|
941
|
+
async checkMfaBeforeRemove(locale: string, userId: number, origin?: string) {
|
|
861
942
|
try {
|
|
862
943
|
const mfaMethods = await this.prisma.user_mfa.findMany({
|
|
863
944
|
where: {
|
|
@@ -888,7 +969,7 @@ export class ProfileService {
|
|
|
888
969
|
|
|
889
970
|
if (emailMfas.length > 0) {
|
|
890
971
|
const emails = emailMfas.map(mfa => mfa.user_mfa_email[0].email);
|
|
891
|
-
await this.challenge.sendMfaCodeToMultipleEmails(locale, userId, emails);
|
|
972
|
+
await this.challenge.sendMfaCodeToMultipleEmails(locale, userId, emails, origin);
|
|
892
973
|
availableMethods.push('email');
|
|
893
974
|
}
|
|
894
975
|
|
|
@@ -1029,7 +1110,7 @@ export class ProfileService {
|
|
|
1029
1110
|
}
|
|
1030
1111
|
}
|
|
1031
1112
|
|
|
1032
|
-
async sendRecoveryCodesVerification(locale: string, userId: number) {
|
|
1113
|
+
async sendRecoveryCodesVerification(locale: string, userId: number, origin?: string) {
|
|
1033
1114
|
try {
|
|
1034
1115
|
const mfaMethods = await this.prisma.user_mfa.findMany({
|
|
1035
1116
|
where: {
|
|
@@ -1058,7 +1139,7 @@ export class ProfileService {
|
|
|
1058
1139
|
|
|
1059
1140
|
if (emailMfas.length > 0) {
|
|
1060
1141
|
const emails = emailMfas.map(mfa => mfa.user_mfa_email[0].email);
|
|
1061
|
-
await this.challenge.sendMfaCodeToMultipleEmails(locale, userId, emails);
|
|
1142
|
+
await this.challenge.sendMfaCodeToMultipleEmails(locale, userId, emails, origin);
|
|
1062
1143
|
availableMethods.push('email');
|
|
1063
1144
|
}
|
|
1064
1145
|
|
|
@@ -1221,7 +1302,7 @@ export class ProfileService {
|
|
|
1221
1302
|
}
|
|
1222
1303
|
}
|
|
1223
1304
|
|
|
1224
|
-
async generateWebAuthnRegistrationOptions(locale: string, userId: number, name: string, requestOrigin?: string) {
|
|
1305
|
+
async generateWebAuthnRegistrationOptions(locale: string, userId: number, name: string, requestOrigin?: string): Promise<PublicKeyCredentialCreationOptionsJSON> {
|
|
1225
1306
|
try {
|
|
1226
1307
|
const user = await this.prisma.user.findFirst({
|
|
1227
1308
|
where: { id: userId },
|
|
@@ -1367,7 +1448,7 @@ export class ProfileService {
|
|
|
1367
1448
|
}
|
|
1368
1449
|
}
|
|
1369
1450
|
|
|
1370
|
-
async generateWebAuthnAuthenticationOptions(locale: string, userId: number, requestOrigin?: string) {
|
|
1451
|
+
async generateWebAuthnAuthenticationOptions(locale: string, userId: number, requestOrigin?: string): Promise<PublicKeyCredentialRequestOptionsJSON> {
|
|
1371
1452
|
try {
|
|
1372
1453
|
const user = await this.prisma.user.findFirst({
|
|
1373
1454
|
where: { id: userId },
|
|
@@ -1474,7 +1555,13 @@ export class ProfileService {
|
|
|
1474
1555
|
}
|
|
1475
1556
|
}
|
|
1476
1557
|
|
|
1477
|
-
async removeWebAuthnMfa(
|
|
1558
|
+
async removeWebAuthnMfa(
|
|
1559
|
+
locale: string,
|
|
1560
|
+
userId: number,
|
|
1561
|
+
mfaId: number,
|
|
1562
|
+
assertionResponse: any,
|
|
1563
|
+
requestOrigin?: string,
|
|
1564
|
+
) {
|
|
1478
1565
|
try {
|
|
1479
1566
|
const mfa = await this.prisma.user_mfa.findFirst({
|
|
1480
1567
|
where: {
|
|
@@ -1490,6 +1577,22 @@ export class ProfileService {
|
|
|
1490
1577
|
);
|
|
1491
1578
|
}
|
|
1492
1579
|
|
|
1580
|
+
// Reautenticacao obrigatoria: sem isso, um token de acesso vazado bastava
|
|
1581
|
+
// para remover a chave de seguranca da conta sem provar posse dela.
|
|
1582
|
+
if (!assertionResponse) {
|
|
1583
|
+
throw new BadRequestException(
|
|
1584
|
+
getLocaleText('assertionResponseRequired', locale, 'Assertion response is required for WebAuthn.')
|
|
1585
|
+
);
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
try {
|
|
1589
|
+
await this.verifyWebAuthnAuthentication(userId, assertionResponse, requestOrigin);
|
|
1590
|
+
} catch (error) {
|
|
1591
|
+
throw new BadRequestException(
|
|
1592
|
+
getLocaleText('invalidWebAuthn', locale, 'WebAuthn verification failed.')
|
|
1593
|
+
);
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1493
1596
|
await this.prisma.user_mfa.delete({
|
|
1494
1597
|
where: { id: mfaId },
|
|
1495
1598
|
});
|
|
@@ -1500,7 +1603,7 @@ export class ProfileService {
|
|
|
1500
1603
|
}
|
|
1501
1604
|
}
|
|
1502
1605
|
|
|
1503
|
-
async generateWebAuthnAuthenticationOptionsForLogin(locale: string, mfaToken: string) {
|
|
1606
|
+
async generateWebAuthnAuthenticationOptionsForLogin(locale: string, mfaToken: string): Promise<PublicKeyCredentialRequestOptionsJSON> {
|
|
1504
1607
|
try {
|
|
1505
1608
|
const decoded = this.jwt.decode(mfaToken) as any;
|
|
1506
1609
|
if (!decoded || !decoded.userId) {
|
|
@@ -1630,8 +1733,7 @@ export class ProfileService {
|
|
|
1630
1733
|
}
|
|
1631
1734
|
}
|
|
1632
1735
|
|
|
1633
|
-
async getUserToken(userId: number){
|
|
1634
|
-
|
|
1635
|
-
return this.getToken(updatedUser);
|
|
1736
|
+
async getUserToken(userId: number, sessionId: number | null){
|
|
1737
|
+
return this.getToken(userId, sessionId);
|
|
1636
1738
|
}
|
|
1637
1739
|
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { Logger } from '@nestjs/common';
|
|
2
|
+
import { EventEmitter2 } from '@nestjs/event-emitter';
|
|
3
|
+
import {
|
|
4
|
+
ConnectedSocket,
|
|
5
|
+
MessageBody,
|
|
6
|
+
OnGatewayConnection,
|
|
7
|
+
OnGatewayDisconnect,
|
|
8
|
+
OnGatewayInit,
|
|
9
|
+
SubscribeMessage,
|
|
10
|
+
WebSocketGateway,
|
|
11
|
+
WebSocketServer,
|
|
12
|
+
} from '@nestjs/websockets';
|
|
13
|
+
import type { Server, Socket } from 'socket.io';
|
|
14
|
+
import { TokenService } from '../token/token.service';
|
|
15
|
+
import { RealtimeService } from './realtime.service';
|
|
16
|
+
|
|
17
|
+
/** Events other modules can listen to via `@OnEvent(...)`. */
|
|
18
|
+
export const REALTIME_EVENTS = {
|
|
19
|
+
userConnected: 'realtime.user.connected',
|
|
20
|
+
userDisconnected: 'realtime.user.disconnected',
|
|
21
|
+
} as const;
|
|
22
|
+
|
|
23
|
+
function corsOrigin(): string[] | boolean {
|
|
24
|
+
const raw = String(process.env.CORS_ALLOWED_ORIGINS || '').trim();
|
|
25
|
+
if (!raw) return true; // permissive handshake — the JWT is the real gate
|
|
26
|
+
return raw
|
|
27
|
+
.split(',')
|
|
28
|
+
.map((s) => s.trim())
|
|
29
|
+
.filter(Boolean);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Single Socket.IO gateway (`/realtime`) on the same HTTP server/port. It only
|
|
34
|
+
* handles transport + auth + room membership; the reusable emit API lives in
|
|
35
|
+
* {@link RealtimeService}. Auth reuses {@link TokenService.verify} (same JWT as
|
|
36
|
+
* the HTTP `AuthGuard`); connection lifecycle is broadcast via EventEmitter2 so
|
|
37
|
+
* features (e.g. presence) react without this layer knowing about them.
|
|
38
|
+
*/
|
|
39
|
+
@WebSocketGateway({
|
|
40
|
+
namespace: '/realtime',
|
|
41
|
+
cors: { origin: corsOrigin(), credentials: true },
|
|
42
|
+
})
|
|
43
|
+
export class RealtimeGateway
|
|
44
|
+
implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect
|
|
45
|
+
{
|
|
46
|
+
private readonly logger = new Logger(RealtimeGateway.name);
|
|
47
|
+
|
|
48
|
+
@WebSocketServer()
|
|
49
|
+
server!: Server;
|
|
50
|
+
|
|
51
|
+
constructor(
|
|
52
|
+
private readonly realtime: RealtimeService,
|
|
53
|
+
private readonly token: TokenService,
|
|
54
|
+
private readonly events: EventEmitter2,
|
|
55
|
+
) {}
|
|
56
|
+
|
|
57
|
+
afterInit(server: Server) {
|
|
58
|
+
this.realtime.setServer(server);
|
|
59
|
+
this.logger.log('Realtime gateway initialized on /realtime');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private extractToken(socket: Socket): string | undefined {
|
|
63
|
+
const authToken = (socket.handshake.auth as { token?: string } | undefined)?.token;
|
|
64
|
+
if (authToken) return authToken;
|
|
65
|
+
const header = socket.handshake.headers.authorization;
|
|
66
|
+
const [type, value] = String(header ?? '').split(' ');
|
|
67
|
+
return type === 'Bearer' ? value : undefined;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async handleConnection(socket: Socket) {
|
|
71
|
+
const token = this.extractToken(socket);
|
|
72
|
+
if (!token) {
|
|
73
|
+
socket.disconnect(true);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
const payload = await this.token.verify('en', token);
|
|
78
|
+
const userId = Number(payload?.sub);
|
|
79
|
+
if (!Number.isInteger(userId) || userId <= 0) {
|
|
80
|
+
socket.disconnect(true);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
socket.data.userId = userId;
|
|
84
|
+
socket.join(`user:${userId}`);
|
|
85
|
+
const localCount = this.realtime.trackConnect(userId);
|
|
86
|
+
this.events.emit(REALTIME_EVENTS.userConnected, {
|
|
87
|
+
userId,
|
|
88
|
+
firstConnection: localCount === 1,
|
|
89
|
+
});
|
|
90
|
+
} catch {
|
|
91
|
+
socket.disconnect(true);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
handleDisconnect(socket: Socket) {
|
|
96
|
+
const userId = socket.data?.userId as number | undefined;
|
|
97
|
+
if (!userId) return;
|
|
98
|
+
const remaining = this.realtime.trackDisconnect(userId);
|
|
99
|
+
this.events.emit(REALTIME_EVENTS.userDisconnected, {
|
|
100
|
+
userId,
|
|
101
|
+
remainingLocalSockets: remaining,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Joins the socket to the given channels (rooms `channel:{name}`). */
|
|
106
|
+
@SubscribeMessage('subscribe')
|
|
107
|
+
onSubscribe(
|
|
108
|
+
@ConnectedSocket() socket: Socket,
|
|
109
|
+
@MessageBody() body: { channels?: string[] },
|
|
110
|
+
) {
|
|
111
|
+
const channels = Array.isArray(body?.channels) ? body.channels : [];
|
|
112
|
+
for (const ch of channels) {
|
|
113
|
+
if (this.isAllowed(socket, String(ch))) socket.join(`channel:${ch}`);
|
|
114
|
+
}
|
|
115
|
+
return { ok: true, subscribed: channels };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
@SubscribeMessage('unsubscribe')
|
|
119
|
+
onUnsubscribe(
|
|
120
|
+
@ConnectedSocket() socket: Socket,
|
|
121
|
+
@MessageBody() body: { channels?: string[] },
|
|
122
|
+
) {
|
|
123
|
+
const channels = Array.isArray(body?.channels) ? body.channels : [];
|
|
124
|
+
for (const ch of channels) socket.leave(`channel:${ch}`);
|
|
125
|
+
return { ok: true };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Channel-subscription authorization. `presence:{id}` is readable by any
|
|
130
|
+
* authenticated user (presence is coarse/non-sensitive). Modules adding
|
|
131
|
+
* sensitive channels should extend this with per-channel checks.
|
|
132
|
+
*/
|
|
133
|
+
private isAllowed(_socket: Socket, channel: string): boolean {
|
|
134
|
+
if (channel.startsWith('presence:')) return true;
|
|
135
|
+
// Default-allow other channels for now; tighten per feature as needed.
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { forwardRef, Module } from '@nestjs/common';
|
|
2
|
+
import { TokenModule } from '../token/token.module';
|
|
3
|
+
import { RealtimeGateway } from './realtime.gateway';
|
|
4
|
+
import { RealtimeService } from './realtime.service';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Generic real-time (WebSocket) layer for the whole app. Any module can inject
|
|
8
|
+
* {@link RealtimeService} to push events to users/channels, fanned out across
|
|
9
|
+
* instances via Redis. EventEmitter2 (global) delivers connection lifecycle.
|
|
10
|
+
*/
|
|
11
|
+
@Module({
|
|
12
|
+
imports: [forwardRef(() => TokenModule)],
|
|
13
|
+
providers: [RealtimeGateway, RealtimeService],
|
|
14
|
+
exports: [RealtimeService],
|
|
15
|
+
})
|
|
16
|
+
export class RealtimeModule {}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, jest } from '@jest/globals';
|
|
2
|
+
import { RealtimeService } from './realtime.service';
|
|
3
|
+
|
|
4
|
+
const makeServer = () => {
|
|
5
|
+
const emit = jest.fn();
|
|
6
|
+
const to = jest.fn(() => ({ emit }));
|
|
7
|
+
return { to, emit: emit as any, rootEmit: jest.fn(), server: { to, emit: jest.fn() } };
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
describe('RealtimeService', () => {
|
|
11
|
+
let service: RealtimeService;
|
|
12
|
+
let server: ReturnType<typeof makeServer>;
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
service = new RealtimeService();
|
|
16
|
+
server = makeServer();
|
|
17
|
+
service.setServer(server.server as any);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('emits to the user room', () => {
|
|
21
|
+
service.emitToUser(7, 'presence:update', { online: true });
|
|
22
|
+
expect(server.to).toHaveBeenCalledWith('user:7');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('emits to the channel room', () => {
|
|
26
|
+
service.emitToChannel('presence:7', 'presence:update', { online: true });
|
|
27
|
+
expect(server.to).toHaveBeenCalledWith('channel:presence:7');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('broadcasts to everyone', () => {
|
|
31
|
+
service.broadcast('ping', {});
|
|
32
|
+
expect((server.server as any).emit).toHaveBeenCalledWith('ping', {});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('does not throw when no server is bound yet', () => {
|
|
36
|
+
const bare = new RealtimeService();
|
|
37
|
+
expect(() => bare.emitToUser(1, 'x', {})).not.toThrow();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('tracks local connections per user', () => {
|
|
41
|
+
expect(service.trackConnect(1)).toBe(1);
|
|
42
|
+
expect(service.trackConnect(1)).toBe(2);
|
|
43
|
+
expect(service.isLocallyConnected(1)).toBe(true);
|
|
44
|
+
expect(service.getLocallyConnectedUserIds()).toEqual([1]);
|
|
45
|
+
expect(service.trackDisconnect(1)).toBe(1);
|
|
46
|
+
expect(service.trackDisconnect(1)).toBe(0);
|
|
47
|
+
expect(service.isLocallyConnected(1)).toBe(false);
|
|
48
|
+
expect(service.getLocallyConnectedUserIds()).toEqual([]);
|
|
49
|
+
});
|
|
50
|
+
});
|