@hed-hog/core 0.0.387 → 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 +24 -9
- 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 +33 -8
- 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 +26 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +30 -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 +9 -1
- package/dist/integration-profile/integration-profile.secrets.js.map +1 -1
- package/dist/integration-profile/integration-profile.service.d.ts +26 -7
- package/dist/integration-profile/integration-profile.service.d.ts.map +1 -1
- package/dist/integration-profile/integration-profile.service.js +94 -91
- package/dist/integration-profile/integration-profile.service.js.map +1 -1
- package/dist/integration-profile/integration-profile.utils.d.ts +13 -1
- package/dist/integration-profile/integration-profile.utils.d.ts.map +1 -1
- package/dist/integration-profile/integration-profile.utils.js +20 -5
- 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 +21 -15
- 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 +30 -18
- 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/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 +153 -84
- 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 +499 -3
- package/hedhog/frontend/messages/pt.json +499 -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/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 +31 -5
- package/src/auth/auth.service.spec.ts +3 -1
- package/src/auth/auth.service.ts +101 -36
- 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 +37 -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 +9 -1
- package/src/integration-profile/integration-profile.service.spec.ts +279 -0
- package/src/integration-profile/integration-profile.service.ts +120 -124
- package/src/integration-profile/integration-profile.utils.spec.ts +22 -0
- package/src/integration-profile/integration-profile.utils.ts +40 -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 +46 -34
- package/src/profile/profile.service.ts +138 -38
- 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/whatsapp-sender/whatsapp-sender.module.ts +10 -0
- package/src/whatsapp-sender/whatsapp-sender.service.ts +313 -0
|
@@ -1,5 +1,5 @@
|
|
|
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';
|
|
@@ -27,10 +27,12 @@ import { UserService } from '../user/user.service';
|
|
|
27
27
|
import { resolveWebAuthnOrigin } from '../utils/webauthn-origin';
|
|
28
28
|
import { ChangeEmailDto } from './dto/change-email.dto';
|
|
29
29
|
import { ChangePasswordDto } from './dto/change-password.dto';
|
|
30
|
+
import { DeleteAccountDto } from './dto/delete-account.dto';
|
|
30
31
|
import { EmailVerificationDto } from './dto/email-verification-confirm.dto';
|
|
31
32
|
import { SendEmailVerificationDto } from './dto/send-email-verification.dto';
|
|
32
33
|
import { SetPreferencesDto } from './dto/set-preferences.dto';
|
|
33
34
|
import { UpdateDto } from './dto/update.dto';
|
|
35
|
+
import { PROFILE_ACCOUNT_DELETED, ProfileAccountDeletedEvent } from './events/profile-account-deleted.event';
|
|
34
36
|
import { PROFILE_AVATAR_UPDATED, ProfileAvatarUpdatedEvent } from './events/profile-avatar-updated.event';
|
|
35
37
|
import { PROFILE_EMAIL_CHANGED, ProfileEmailChangedEvent } from './events/profile-email-changed.event';
|
|
36
38
|
import { PROFILE_UPDATED, ProfileUpdatedEvent } from './events/profile-updated.event';
|
|
@@ -54,7 +56,7 @@ export class ProfileService {
|
|
|
54
56
|
private readonly eventEmitter: EventEmitter2,
|
|
55
57
|
) {}
|
|
56
58
|
|
|
57
|
-
async changeEmail(locale: string, userId: number, { email, password, pin }: ChangeEmailDto) {
|
|
59
|
+
async changeEmail(locale: string, userId: number, { email, password, pin }: ChangeEmailDto, origin?: string) {
|
|
58
60
|
|
|
59
61
|
const checkAlreadyExists = await this.prisma.user_identifier.findFirst({
|
|
60
62
|
where: { type: 'email', value: email, enabled: true, verified_at: { not: null } },
|
|
@@ -127,7 +129,7 @@ export class ProfileService {
|
|
|
127
129
|
});
|
|
128
130
|
|
|
129
131
|
// Create verification challenge for the new email
|
|
130
|
-
await this.challenge.verifyEmail(locale, userId, email);
|
|
132
|
+
await this.challenge.verifyEmail(locale, userId, email, undefined, origin);
|
|
131
133
|
|
|
132
134
|
});
|
|
133
135
|
|
|
@@ -150,7 +152,7 @@ export class ProfileService {
|
|
|
150
152
|
return { success: true };
|
|
151
153
|
}
|
|
152
154
|
|
|
153
|
-
async confirmEmailVerificationMfa(locale: string, userId: number, { pin, challengeId, name }: EmailVerificationDto) {
|
|
155
|
+
async confirmEmailVerificationMfa(locale: string, userId: number, { pin, challengeId, name }: EmailVerificationDto, sessionId: number | null) {
|
|
154
156
|
|
|
155
157
|
const user = await this.prisma.user.findUnique({
|
|
156
158
|
where: { id: userId },
|
|
@@ -223,9 +225,8 @@ export class ProfileService {
|
|
|
223
225
|
|
|
224
226
|
});
|
|
225
227
|
|
|
226
|
-
const
|
|
227
|
-
const
|
|
228
|
-
const newToken = await this.getToken(updatedUser);
|
|
228
|
+
const codes = await this.createMfaRecoveryCodes(userId);
|
|
229
|
+
const newToken = await this.getToken(userId, sessionId);
|
|
229
230
|
return { ...newToken, codes };
|
|
230
231
|
|
|
231
232
|
}
|
|
@@ -302,16 +303,16 @@ export class ProfileService {
|
|
|
302
303
|
return { success: true };
|
|
303
304
|
}
|
|
304
305
|
|
|
305
|
-
async sendEmailVerificationMfa(locale: string, userId: number, { email }: SendEmailVerificationDto) {
|
|
306
|
-
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);
|
|
307
308
|
}
|
|
308
309
|
|
|
309
|
-
async sendEmailVerification(locale: string, userId: number, { email }: SendEmailVerificationDto) {
|
|
310
|
-
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);
|
|
311
312
|
}
|
|
312
313
|
|
|
313
|
-
async sendCodeToRemove(locale, userId, email){
|
|
314
|
-
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)
|
|
315
316
|
}
|
|
316
317
|
|
|
317
318
|
async getEmailIdentifier(userId: number) {
|
|
@@ -359,6 +360,84 @@ export class ProfileService {
|
|
|
359
360
|
return result;
|
|
360
361
|
}
|
|
361
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
|
+
|
|
362
441
|
async updateAvatar(locale: string, userId: number, file: MulterFile) {
|
|
363
442
|
const result = await this.user.changeAvatar(locale, userId, file);
|
|
364
443
|
this.eventEmitter.emit(PROFILE_AVATAR_UPDATED, new ProfileAvatarUpdatedEvent(userId, result?.id ?? null));
|
|
@@ -366,7 +445,7 @@ export class ProfileService {
|
|
|
366
445
|
return result;
|
|
367
446
|
}
|
|
368
447
|
|
|
369
|
-
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) {
|
|
370
449
|
const credential = await this.prisma.user_credential.findFirst({
|
|
371
450
|
where: { user_id: userId, type: 'password' },
|
|
372
451
|
});
|
|
@@ -407,6 +486,7 @@ export class ProfileService {
|
|
|
407
486
|
ipAddress: ipAddress || 'Unknown',
|
|
408
487
|
location: 'Unknown',
|
|
409
488
|
},
|
|
489
|
+
app: await this.challenge.resolveAppKeyFromOrigin(origin),
|
|
410
490
|
}).catch(error => {
|
|
411
491
|
console.error('Failed to send password change notification:', error);
|
|
412
492
|
});
|
|
@@ -448,11 +528,11 @@ export class ProfileService {
|
|
|
448
528
|
return codes;
|
|
449
529
|
}
|
|
450
530
|
|
|
451
|
-
async getToken(
|
|
452
|
-
return { token: this.
|
|
531
|
+
async getToken(userId: number, sessionId: number | null) {
|
|
532
|
+
return { token: await this.token.createAccessToken({ sub: userId, sessionId }) };
|
|
453
533
|
}
|
|
454
534
|
|
|
455
|
-
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) {
|
|
456
536
|
const settings = await this.setting.getSettingValues([
|
|
457
537
|
'mfa-issuer',
|
|
458
538
|
'mfa-window',
|
|
@@ -514,9 +594,8 @@ export class ProfileService {
|
|
|
514
594
|
});
|
|
515
595
|
}
|
|
516
596
|
|
|
517
|
-
const
|
|
518
|
-
const
|
|
519
|
-
const newToken = await this.getToken(updatedUser);
|
|
597
|
+
const codes = await this.createMfaRecoveryCodes(userId);
|
|
598
|
+
const newToken = await this.getToken(userId, sessionId ?? null);
|
|
520
599
|
return { ...newToken, codes };
|
|
521
600
|
}
|
|
522
601
|
|
|
@@ -569,7 +648,7 @@ export class ProfileService {
|
|
|
569
648
|
return { otpauthUrl: secret.otpauth_url, qrCode, secret: encodedSecret };
|
|
570
649
|
}
|
|
571
650
|
|
|
572
|
-
async removeMfaTotp(locale: string, userId: number, mfaId: number, token: string) {
|
|
651
|
+
async removeMfaTotp(locale: string, userId: number, mfaId: number, token: string, sessionId: number | null) {
|
|
573
652
|
try {
|
|
574
653
|
const user = await this.prisma.user.findFirst({
|
|
575
654
|
where: { id: userId },
|
|
@@ -606,26 +685,26 @@ export class ProfileService {
|
|
|
606
685
|
});
|
|
607
686
|
});
|
|
608
687
|
|
|
609
|
-
return this.getUserToken(userId)
|
|
688
|
+
return this.getUserToken(userId, sessionId)
|
|
610
689
|
} catch (error: any) {
|
|
611
690
|
throw new BadRequestException(`Invalid code: ${error?.message}`);
|
|
612
691
|
}
|
|
613
692
|
}
|
|
614
693
|
|
|
615
|
-
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) {
|
|
616
695
|
try {
|
|
617
696
|
await this.challenge.verifyCode(locale, userId, token, providedHash)
|
|
618
697
|
await this.prisma.user_mfa.deleteMany({
|
|
619
698
|
where: { id: mfaId, user_id: userId, type: 'email' },
|
|
620
699
|
});
|
|
621
700
|
|
|
622
|
-
return this.getUserToken(userId)
|
|
701
|
+
return this.getUserToken(userId, sessionId)
|
|
623
702
|
} catch (error: any) {
|
|
624
703
|
throw new BadRequestException(`Invalid code: ${error?.message}`);
|
|
625
704
|
}
|
|
626
705
|
}
|
|
627
706
|
|
|
628
|
-
async removeMfaWithRecoveryCode(locale: string, userId: number, mfaId: number, recoveryCode: string) {
|
|
707
|
+
async removeMfaWithRecoveryCode(locale: string, userId: number, mfaId: number, recoveryCode: string, sessionId: number | null) {
|
|
629
708
|
try {
|
|
630
709
|
const user = await this.prisma.user.findUnique({
|
|
631
710
|
where: { id: userId },
|
|
@@ -659,13 +738,13 @@ export class ProfileService {
|
|
|
659
738
|
where: { id: mfaId, user_id: userId },
|
|
660
739
|
});
|
|
661
740
|
|
|
662
|
-
return this.getUserToken(userId);
|
|
741
|
+
return this.getUserToken(userId, sessionId);
|
|
663
742
|
} catch (error: any) {
|
|
664
743
|
throw new BadRequestException(`Invalid code: ${error?.message}`);
|
|
665
744
|
}
|
|
666
745
|
}
|
|
667
746
|
|
|
668
|
-
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) {
|
|
669
748
|
try {
|
|
670
749
|
const user = await this.prisma.user.findUnique({
|
|
671
750
|
where: { id: userId },
|
|
@@ -786,13 +865,13 @@ export class ProfileService {
|
|
|
786
865
|
where: { id: mfaId, user_id: userId },
|
|
787
866
|
});
|
|
788
867
|
|
|
789
|
-
return this.getUserToken(userId);
|
|
868
|
+
return this.getUserToken(userId, sessionId);
|
|
790
869
|
} catch (error: any) {
|
|
791
870
|
throw new BadRequestException(`Failed to remove MFA: ${error?.message}`);
|
|
792
871
|
}
|
|
793
872
|
}
|
|
794
873
|
|
|
795
|
-
async checkMfaBeforeAdd(locale: string, userId: number) {
|
|
874
|
+
async checkMfaBeforeAdd(locale: string, userId: number, origin?: string) {
|
|
796
875
|
try {
|
|
797
876
|
const mfaMethods = await this.prisma.user_mfa.findMany({
|
|
798
877
|
where: {
|
|
@@ -822,7 +901,7 @@ export class ProfileService {
|
|
|
822
901
|
|
|
823
902
|
if (emailMfas.length > 0) {
|
|
824
903
|
const emails = emailMfas.map(mfa => mfa.user_mfa_email[0].email);
|
|
825
|
-
await this.challenge.sendMfaCodeToMultipleEmails(locale, userId, emails);
|
|
904
|
+
await this.challenge.sendMfaCodeToMultipleEmails(locale, userId, emails, origin);
|
|
826
905
|
availableMethods.push('email');
|
|
827
906
|
}
|
|
828
907
|
|
|
@@ -859,7 +938,7 @@ export class ProfileService {
|
|
|
859
938
|
}
|
|
860
939
|
}
|
|
861
940
|
|
|
862
|
-
async checkMfaBeforeRemove(locale: string, userId: number) {
|
|
941
|
+
async checkMfaBeforeRemove(locale: string, userId: number, origin?: string) {
|
|
863
942
|
try {
|
|
864
943
|
const mfaMethods = await this.prisma.user_mfa.findMany({
|
|
865
944
|
where: {
|
|
@@ -890,7 +969,7 @@ export class ProfileService {
|
|
|
890
969
|
|
|
891
970
|
if (emailMfas.length > 0) {
|
|
892
971
|
const emails = emailMfas.map(mfa => mfa.user_mfa_email[0].email);
|
|
893
|
-
await this.challenge.sendMfaCodeToMultipleEmails(locale, userId, emails);
|
|
972
|
+
await this.challenge.sendMfaCodeToMultipleEmails(locale, userId, emails, origin);
|
|
894
973
|
availableMethods.push('email');
|
|
895
974
|
}
|
|
896
975
|
|
|
@@ -1031,7 +1110,7 @@ export class ProfileService {
|
|
|
1031
1110
|
}
|
|
1032
1111
|
}
|
|
1033
1112
|
|
|
1034
|
-
async sendRecoveryCodesVerification(locale: string, userId: number) {
|
|
1113
|
+
async sendRecoveryCodesVerification(locale: string, userId: number, origin?: string) {
|
|
1035
1114
|
try {
|
|
1036
1115
|
const mfaMethods = await this.prisma.user_mfa.findMany({
|
|
1037
1116
|
where: {
|
|
@@ -1060,7 +1139,7 @@ export class ProfileService {
|
|
|
1060
1139
|
|
|
1061
1140
|
if (emailMfas.length > 0) {
|
|
1062
1141
|
const emails = emailMfas.map(mfa => mfa.user_mfa_email[0].email);
|
|
1063
|
-
await this.challenge.sendMfaCodeToMultipleEmails(locale, userId, emails);
|
|
1142
|
+
await this.challenge.sendMfaCodeToMultipleEmails(locale, userId, emails, origin);
|
|
1064
1143
|
availableMethods.push('email');
|
|
1065
1144
|
}
|
|
1066
1145
|
|
|
@@ -1476,7 +1555,13 @@ export class ProfileService {
|
|
|
1476
1555
|
}
|
|
1477
1556
|
}
|
|
1478
1557
|
|
|
1479
|
-
async removeWebAuthnMfa(
|
|
1558
|
+
async removeWebAuthnMfa(
|
|
1559
|
+
locale: string,
|
|
1560
|
+
userId: number,
|
|
1561
|
+
mfaId: number,
|
|
1562
|
+
assertionResponse: any,
|
|
1563
|
+
requestOrigin?: string,
|
|
1564
|
+
) {
|
|
1480
1565
|
try {
|
|
1481
1566
|
const mfa = await this.prisma.user_mfa.findFirst({
|
|
1482
1567
|
where: {
|
|
@@ -1492,6 +1577,22 @@ export class ProfileService {
|
|
|
1492
1577
|
);
|
|
1493
1578
|
}
|
|
1494
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
|
+
|
|
1495
1596
|
await this.prisma.user_mfa.delete({
|
|
1496
1597
|
where: { id: mfaId },
|
|
1497
1598
|
});
|
|
@@ -1632,8 +1733,7 @@ export class ProfileService {
|
|
|
1632
1733
|
}
|
|
1633
1734
|
}
|
|
1634
1735
|
|
|
1635
|
-
async getUserToken(userId: number){
|
|
1636
|
-
|
|
1637
|
-
return this.getToken(updatedUser);
|
|
1736
|
+
async getUserToken(userId: number, sessionId: number | null){
|
|
1737
|
+
return this.getToken(userId, sessionId);
|
|
1638
1738
|
}
|
|
1639
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
|
+
});
|