@omnizap-system/omnizap 2.5.12
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/.clusterfuzzlite/Dockerfile +10 -0
- package/.env.example +907 -0
- package/.github/codeql/codeql-config.yml +10 -0
- package/.github/dependabot.yml +35 -0
- package/.github/workflows/ci.yml +73 -0
- package/.github/workflows/codeql.yml +106 -0
- package/.github/workflows/db-migration-check.yml +98 -0
- package/.github/workflows/dependency-review.yml +22 -0
- package/.github/workflows/deploy.yml +95 -0
- package/.github/workflows/release.yml +106 -0
- package/.github/workflows/security-attest-provenance.yml +51 -0
- package/.github/workflows/security-gitleaks.yml +34 -0
- package/.github/workflows/security-runner-hardening.yml +31 -0
- package/.github/workflows/security-scorecard.yml +44 -0
- package/.github/workflows/security-zap-baseline.yml +44 -0
- package/.github/workflows/security-zap-full-scan.yml +43 -0
- package/.github/workflows/security-zizmor.yml +36 -0
- package/.github/workflows/wiki-sync.yml +44 -0
- package/.gitleaks.toml +15 -0
- package/.prettierrc +34 -0
- package/CODE_OF_CONDUCT.md +114 -0
- package/LICENSE +56 -0
- package/README.md +110 -0
- package/SECURITY.md +110 -0
- package/app/config/index.js +4 -0
- package/app/configParts/adminIdentity.js +92 -0
- package/app/configParts/baileysConfig.js +1818 -0
- package/app/configParts/groupUtils.js +692 -0
- package/app/configParts/loggerConfig.js +394 -0
- package/app/configParts/messagePersistenceService.js +305 -0
- package/app/connection/baileysCompatibility.test.js +40 -0
- package/app/connection/baileysDbAuthState.js +344 -0
- package/app/connection/socketController.js +2243 -0
- package/app/controllers/messageController.js +7 -0
- package/app/controllers/messagePipeline/commandMiddleware.js +146 -0
- package/app/controllers/messagePipeline/conversationMiddleware.js +183 -0
- package/app/controllers/messagePipeline/messagePipelineMiddlewares.test.js +522 -0
- package/app/controllers/messagePipeline/postProcessingMiddleware.js +41 -0
- package/app/controllers/messagePipeline/preProcessingMiddlewares.js +166 -0
- package/app/controllers/messageProcessingPipeline.js +699 -0
- package/app/modules/adminModule/AGENT.md +4056 -0
- package/app/modules/adminModule/adminAiHelpService.js +56 -0
- package/app/modules/adminModule/adminConfigRuntime.js +177 -0
- package/app/modules/adminModule/commandConfig.json +7122 -0
- package/app/modules/adminModule/groupCommandHandlers.js +1823 -0
- package/app/modules/adminModule/groupCommandHandlers.test.js +350 -0
- package/app/modules/adminModule/groupEventHandlers.js +399 -0
- package/app/modules/aiModule/AGENT.md +547 -0
- package/app/modules/aiModule/aiAiHelpService.js +14 -0
- package/app/modules/aiModule/aiConfigRuntime.js +135 -0
- package/app/modules/aiModule/catCommand.js +967 -0
- package/app/modules/aiModule/commandConfig.json +981 -0
- package/app/modules/analyticsModule/messageAnalysisEventRepository.js +83 -0
- package/app/modules/gameModule/AGENT.md +196 -0
- package/app/modules/gameModule/commandConfig.json +366 -0
- package/app/modules/gameModule/diceCommand.js +42 -0
- package/app/modules/gameModule/gameAiHelpService.js +14 -0
- package/app/modules/gameModule/gameConfigRuntime.js +68 -0
- package/app/modules/menuModule/AGENT.md +205 -0
- package/app/modules/menuModule/commandConfig.json +366 -0
- package/app/modules/menuModule/common.js +316 -0
- package/app/modules/menuModule/menuAiHelpService.js +14 -0
- package/app/modules/menuModule/menuConfigRuntime.js +68 -0
- package/app/modules/menuModule/menus.js +66 -0
- package/app/modules/playModule/AGENT.md +321 -0
- package/app/modules/playModule/commandConfig.json +584 -0
- package/app/modules/playModule/playAiHelpService.js +14 -0
- package/app/modules/playModule/playCommand.js +1417 -0
- package/app/modules/playModule/playConfigRuntime.js +68 -0
- package/app/modules/quoteModule/AGENT.md +199 -0
- package/app/modules/quoteModule/commandConfig.json +366 -0
- package/app/modules/quoteModule/quoteAiHelpService.js +14 -0
- package/app/modules/quoteModule/quoteCommand.js +842 -0
- package/app/modules/quoteModule/quoteConfigRuntime.js +68 -0
- package/app/modules/rpgPokemonModule/AGENT.md +229 -0
- package/app/modules/rpgPokemonModule/commandConfig.json +386 -0
- package/app/modules/rpgPokemonModule/rpgBattleCanvasRenderer.js +795 -0
- package/app/modules/rpgPokemonModule/rpgBattleService.js +2110 -0
- package/app/modules/rpgPokemonModule/rpgBattleService.test.js +770 -0
- package/app/modules/rpgPokemonModule/rpgEvolutionUtils.js +22 -0
- package/app/modules/rpgPokemonModule/rpgPokemonAiHelpService.js +14 -0
- package/app/modules/rpgPokemonModule/rpgPokemonCommand.js +174 -0
- package/app/modules/rpgPokemonModule/rpgPokemonConfigRuntime.js +68 -0
- package/app/modules/rpgPokemonModule/rpgPokemonDomain.js +192 -0
- package/app/modules/rpgPokemonModule/rpgPokemonDomain.test.js +93 -0
- package/app/modules/rpgPokemonModule/rpgPokemonEvolution.test.js +46 -0
- package/app/modules/rpgPokemonModule/rpgPokemonMessages.js +746 -0
- package/app/modules/rpgPokemonModule/rpgPokemonRepository.js +1847 -0
- package/app/modules/rpgPokemonModule/rpgPokemonService.js +6839 -0
- package/app/modules/rpgPokemonModule/rpgProfileCanvasRenderer.js +354 -0
- package/app/modules/statsModule/AGENT.md +320 -0
- package/app/modules/statsModule/commandConfig.json +540 -0
- package/app/modules/statsModule/globalRankingCommand.js +64 -0
- package/app/modules/statsModule/rankingCommand.js +41 -0
- package/app/modules/statsModule/rankingCommon.js +1305 -0
- package/app/modules/statsModule/statsAiHelpService.js +14 -0
- package/app/modules/statsModule/statsConfigRuntime.js +68 -0
- package/app/modules/stickerModule/AGENT.md +692 -0
- package/app/modules/stickerModule/addStickerMetadata.js +239 -0
- package/app/modules/stickerModule/commandConfig.json +1216 -0
- package/app/modules/stickerModule/convertToWebp.js +367 -0
- package/app/modules/stickerModule/stickerAiHelpService.js +14 -0
- package/app/modules/stickerModule/stickerCommand.js +446 -0
- package/app/modules/stickerModule/stickerConfigRuntime.js +68 -0
- package/app/modules/stickerModule/stickerConvertCommand.js +159 -0
- package/app/modules/stickerModule/stickerTextCommand.js +653 -0
- package/app/modules/stickerPackModule/AGENT.md +215 -0
- package/app/modules/stickerPackModule/autoPackCollectorRuntime.js +20 -0
- package/app/modules/stickerPackModule/autoPackCollectorService.js +357 -0
- package/app/modules/stickerPackModule/commandConfig.json +387 -0
- package/app/modules/stickerPackModule/domainEventOutboxRepository.js +227 -0
- package/app/modules/stickerPackModule/domainEvents.js +52 -0
- package/app/modules/stickerPackModule/semanticReclassificationEngine.js +429 -0
- package/app/modules/stickerPackModule/semanticReclassificationEngine.test.js +75 -0
- package/app/modules/stickerPackModule/semanticThemeClusterService.js +544 -0
- package/app/modules/stickerPackModule/stickerAssetClassificationRepository.js +400 -0
- package/app/modules/stickerPackModule/stickerAssetRepository.js +400 -0
- package/app/modules/stickerPackModule/stickerAssetReprocessQueueRepository.js +175 -0
- package/app/modules/stickerPackModule/stickerAutoPackByTagsRuntime.js +3702 -0
- package/app/modules/stickerPackModule/stickerClassificationBackgroundRuntime.js +559 -0
- package/app/modules/stickerPackModule/stickerClassificationService.js +557 -0
- package/app/modules/stickerPackModule/stickerDedicatedTaskWorkerRuntime.js +249 -0
- package/app/modules/stickerPackModule/stickerDomainEventBus.js +65 -0
- package/app/modules/stickerPackModule/stickerDomainEventConsumerRuntime.js +208 -0
- package/app/modules/stickerPackModule/stickerMarketplaceDriftService.js +99 -0
- package/app/modules/stickerPackModule/stickerObjectStorageService.js +285 -0
- package/app/modules/stickerPackModule/stickerPackAiHelpService.js +14 -0
- package/app/modules/stickerPackModule/stickerPackCommandHandlers.js +1148 -0
- package/app/modules/stickerPackModule/stickerPackConfigRuntime.js +68 -0
- package/app/modules/stickerPackModule/stickerPackEngagementRepository.js +152 -0
- package/app/modules/stickerPackModule/stickerPackErrors.js +30 -0
- package/app/modules/stickerPackModule/stickerPackInteractionEventRepository.js +101 -0
- package/app/modules/stickerPackModule/stickerPackItemRepository.js +432 -0
- package/app/modules/stickerPackModule/stickerPackMarketplaceService.js +313 -0
- package/app/modules/stickerPackModule/stickerPackMessageService.js +268 -0
- package/app/modules/stickerPackModule/stickerPackRepository.js +450 -0
- package/app/modules/stickerPackModule/stickerPackScoreSnapshotRepository.js +179 -0
- package/app/modules/stickerPackModule/stickerPackScoreSnapshotRuntime.js +271 -0
- package/app/modules/stickerPackModule/stickerPackService.js +733 -0
- package/app/modules/stickerPackModule/stickerPackServiceRuntime.js +32 -0
- package/app/modules/stickerPackModule/stickerPackUtils.js +107 -0
- package/app/modules/stickerPackModule/stickerStorageService.js +559 -0
- package/app/modules/stickerPackModule/stickerWorkerPipelineRuntime.js +242 -0
- package/app/modules/stickerPackModule/stickerWorkerTaskQueueRepository.js +242 -0
- package/app/modules/systemMetricsModule/AGENT.md +193 -0
- package/app/modules/systemMetricsModule/commandConfig.json +344 -0
- package/app/modules/systemMetricsModule/pingCommand.js +399 -0
- package/app/modules/systemMetricsModule/systemMetricsAiHelpService.js +14 -0
- package/app/modules/systemMetricsModule/systemMetricsConfigRuntime.js +68 -0
- package/app/modules/tiktokModule/AGENT.md +196 -0
- package/app/modules/tiktokModule/commandConfig.json +366 -0
- package/app/modules/tiktokModule/tiktokAiHelpService.js +14 -0
- package/app/modules/tiktokModule/tiktokCommand.js +716 -0
- package/app/modules/tiktokModule/tiktokConfigRuntime.js +68 -0
- package/app/modules/userModule/AGENT.md +200 -0
- package/app/modules/userModule/commandConfig.json +386 -0
- package/app/modules/userModule/userAiHelpService.js +14 -0
- package/app/modules/userModule/userCommand.js +1155 -0
- package/app/modules/userModule/userConfigRuntime.js +68 -0
- package/app/modules/waifuPicsModule/AGENT.md +431 -0
- package/app/modules/waifuPicsModule/commandConfig.json +780 -0
- package/app/modules/waifuPicsModule/waifuPicsAiHelpService.js +14 -0
- package/app/modules/waifuPicsModule/waifuPicsCommand.js +586 -0
- package/app/modules/waifuPicsModule/waifuPicsConfigRuntime.js +68 -0
- package/app/observability/metrics.js +766 -0
- package/app/services/ai/aiHelpResponseCacheRepository.js +280 -0
- package/app/services/ai/aiLearningRepository.js +400 -0
- package/app/services/ai/commandConfigEnrichmentRepository.js +769 -0
- package/app/services/ai/commandConfigEnrichmentService.js +452 -0
- package/app/services/ai/commandConfigValidationService.js +443 -0
- package/app/services/ai/commandToolBuilderService.js +192 -0
- package/app/services/ai/conversationRouterService.js +516 -0
- package/app/services/ai/geminiService.js +115 -0
- package/app/services/ai/geminiService.test.js +87 -0
- package/app/services/ai/globalModuleAiHelpService.js +1412 -0
- package/app/services/ai/globalToolCallingService.js +203 -0
- package/app/services/ai/messageCommandExecutionService.js +391 -0
- package/app/services/ai/moduleAiHelpCoreService.js +1099 -0
- package/app/services/ai/moduleAiHelpWrapperFactory.js +65 -0
- package/app/services/ai/moduleCommandConfigRuntimeService.js +113 -0
- package/app/services/ai/moduleToolExecutorService.js +464 -0
- package/app/services/ai/moduleToolRegistryService.js +178 -0
- package/app/services/ai/toolCandidateSelectorService.js +781 -0
- package/app/services/auth/googleWebLinkService.js +80 -0
- package/app/services/auth/whatsappLoginLinkService.js +230 -0
- package/app/services/external/pokeApiService.js +398 -0
- package/app/services/group/groupMetadataService.js +311 -0
- package/app/services/infra/dbWriteQueue.js +874 -0
- package/app/services/infra/featureFlagService.js +131 -0
- package/app/services/infra/queueUtils.js +55 -0
- package/app/services/messaging/captchaService.js +491 -0
- package/app/services/messaging/messagePersistenceService.js +1 -0
- package/app/services/messaging/newsBroadcastService.js +347 -0
- package/app/services/sticker/stickerFocusService.js +347 -0
- package/app/services/sticker/stickerFocusService.test.js +43 -0
- package/app/store/aiPromptStore.js +38 -0
- package/app/store/conversationSessionStore.js +131 -0
- package/app/store/groupConfigStore.js +58 -0
- package/app/store/premiumUserStore.js +54 -0
- package/app/utils/antiLink/antiLinkModule.js +700 -0
- package/app/utils/http/getImageBufferModule.js +18 -0
- package/app/utils/json/jsonSanitizer.js +113 -0
- package/app/utils/json/jsonSanitizer.test.js +40 -0
- package/app/utils/systemMetrics/systemMetricsModule.js +88 -0
- package/app/workers/aiLearningWorker.js +605 -0
- package/app/workers/commandConfigEnrichmentWorker.js +242 -0
- package/database/index.js +2075 -0
- package/database/init.js +151 -0
- package/database/migrations/.gitkeep +0 -0
- package/database/migrations/20260307_d0_hardening_down.sql +64 -0
- package/database/migrations/20260307_d0_hardening_up.sql +79 -0
- package/database/migrations/20260307_d1_terms_acceptance_down.sql +11 -0
- package/database/migrations/20260307_d1_terms_acceptance_up.sql +37 -0
- package/database/migrations/20260307_d2_auth_hardening_down.sql +75 -0
- package/database/migrations/20260307_d2_auth_hardening_up.sql +100 -0
- package/database/migrations/20260314_d7_canonical_sender_down.sql +53 -0
- package/database/migrations/20260314_d7_canonical_sender_up.sql +114 -0
- package/database/migrations/20260406_d30_security_analytics_down.sql +95 -0
- package/database/migrations/20260406_d30_security_analytics_up.sql +292 -0
- package/database/migrations/20260407_d31_web_google_session_token_hardening_down.sql +2 -0
- package/database/migrations/20260407_d31_web_google_session_token_hardening_up.sql +17 -0
- package/database/migrations/20260408_d32_ai_help_response_cache_down.sql +1 -0
- package/database/migrations/20260408_d32_ai_help_response_cache_up.sql +22 -0
- package/database/migrations/20260409_d33_ai_learning_tables_down.sql +4 -0
- package/database/migrations/20260409_d33_ai_learning_tables_up.sql +52 -0
- package/database/migrations/20260410_d34_command_config_enrichment_down.sql +3 -0
- package/database/migrations/20260410_d34_command_config_enrichment_up.sql +48 -0
- package/database/schema.sql +1186 -0
- package/docker-compose.yml +104 -0
- package/docs/audits/stickerCatalogController-out-of-scope.md +103 -0
- package/docs/audits/stickerCatalogController-symbols.md +58 -0
- package/docs/compliance/acceptable-use-policy-2026-03-07.md +35 -0
- package/docs/compliance/dpa-b2b-standard-2026-03-07.md +80 -0
- package/docs/compliance/monthly-compliance-checklist-2026-03-07.md +88 -0
- package/docs/compliance/notice-and-takedown-policy-2026-03-07.md +34 -0
- package/docs/compliance/privacy-policy-2026-03-07.md +75 -0
- package/docs/compliance/subprocessors-inventory-2026-03-07.md +16 -0
- package/docs/database/production-db-evolution-runbook-2026q1.md +365 -0
- package/docs/security/dsar-lgpd-runbook-2026-03-07.md +86 -0
- package/docs/security/incident-response-lgpd-anpd-runbook-2026-03-07.md +77 -0
- package/docs/security/network-hardening-runbook-2026-03-07.md +137 -0
- package/docs/seo/omnizap-seo-playbook-br-2026-02-28.md +238 -0
- package/docs/seo/satellite-page-template.md +116 -0
- package/docs/seo/satellite-pages-phase1.json +364 -0
- package/docs/wiki/Home.md +120 -0
- package/docs/wiki/pair-extraordinaire-2026-03-08.md +3 -0
- package/docs/wiki/recent-changes-2026-03-08.md +47 -0
- package/ecosystem.prod.config.cjs +135 -0
- package/eslint.config.js +89 -0
- package/index.js +488 -0
- package/ml/clip_classifier/Dockerfile +18 -0
- package/ml/clip_classifier/README.md +118 -0
- package/ml/clip_classifier/adaptive_scoring.py +40 -0
- package/ml/clip_classifier/classifier.py +654 -0
- package/ml/clip_classifier/embedding_store.py +481 -0
- package/ml/clip_classifier/env_loader.py +15 -0
- package/ml/clip_classifier/llm_label_expander.py +144 -0
- package/ml/clip_classifier/main.py +213 -0
- package/ml/clip_classifier/requirements.txt +10 -0
- package/ml/clip_classifier/similarity_engine.py +74 -0
- package/new-logo.png +0 -0
- package/observability/alert-rules.yml +60 -0
- package/observability/grafana/dashboards/omnizap-mysql.json +136 -0
- package/observability/grafana/dashboards/omnizap-overview.json +170 -0
- package/observability/grafana/provisioning/dashboards/dashboards.yml +11 -0
- package/observability/grafana/provisioning/datasources/datasources.yml +15 -0
- package/observability/loki-config.yml +38 -0
- package/observability/mysql-setup.sql +46 -0
- package/observability/prometheus.yml +35 -0
- package/observability/promtail-config.yml +84 -0
- package/observability/sticker-catalog-slo.md +83 -0
- package/observability/sticker-scale-hardening-rollout.md +128 -0
- package/package.json +144 -0
- package/public/apple-touch-icon.png +0 -0
- package/public/assets/css/commands-react.input.css +71 -0
- package/public/assets/css/create-pack-react.input.css +31 -0
- package/public/assets/css/home-react.input.css +106 -0
- package/public/assets/css/login-react.input.css +58 -0
- package/public/assets/css/stickers-react.input.css +18 -0
- package/public/assets/css/terms-react.input.css +115 -0
- package/public/assets/css/user-react.input.css +57 -0
- package/public/assets/images/brand-icon-192.png +0 -0
- package/public/assets/images/brand-logo-128.webp +0 -0
- package/public/assets/images/hero-banner-1280.jpg +0 -0
- package/public/comandos/commands-catalog.json +4517 -0
- package/public/css/api-docs.css +161 -0
- package/public/css/stickers-admin.css +1288 -0
- package/public/css/styles.css +679 -0
- package/public/css/systemadm/admin.css +474 -0
- package/public/css/systemadm/base.css +73 -0
- package/public/css/systemadm/components.css +662 -0
- package/public/css/systemadm/layout.css +229 -0
- package/public/css/systemadm/tokens.css +56 -0
- package/public/favicon-16x16.png +0 -0
- package/public/favicon-32x32.png +0 -0
- package/public/favicon.ico +0 -0
- package/public/js/apps/apiDocsApp.js +235 -0
- package/public/js/apps/commandsReactApp.js +528 -0
- package/public/js/apps/createPackApp.js +1646 -0
- package/public/js/apps/homeReactApp.js +942 -0
- package/public/js/apps/loginReactApp.js +496 -0
- package/public/js/apps/stickersAdminApp.js +1753 -0
- package/public/js/apps/stickersApp.js +3797 -0
- package/public/js/apps/termsReactApp.js +528 -0
- package/public/js/apps/userApp.js +2540 -0
- package/public/js/apps/userProfile/actions.js +66 -0
- package/public/js/apps/userReactApp.js +547 -0
- package/public/js/catalog.js +950 -0
- package/public/pages/api-docs.html +40 -0
- package/public/pages/aup.html +158 -0
- package/public/pages/comandos.html +41 -0
- package/public/pages/dpa.html +227 -0
- package/public/pages/home.html +45 -0
- package/public/pages/licenca.html +182 -0
- package/public/pages/login.html +40 -0
- package/public/pages/notice-and-takedown.html +234 -0
- package/public/pages/politica-de-privacidade.html +251 -0
- package/public/pages/seo-bot-whatsapp-para-grupo.html +350 -0
- package/public/pages/seo-bot-whatsapp-sem-programar.html +350 -0
- package/public/pages/seo-como-automatizar-avisos-no-whatsapp.html +350 -0
- package/public/pages/seo-como-criar-comandos-whatsapp.html +350 -0
- package/public/pages/seo-como-evitar-spam-no-whatsapp.html +350 -0
- package/public/pages/seo-como-moderar-grupo-whatsapp.html +350 -0
- package/public/pages/seo-como-organizar-comunidade-whatsapp.html +350 -0
- package/public/pages/seo-melhor-bot-whatsapp-para-grupos.html +350 -0
- package/public/pages/stickers-admin.html +31 -0
- package/public/pages/stickers-create.html +41 -0
- package/public/pages/stickers.html +45 -0
- package/public/pages/suboperadores.html +237 -0
- package/public/pages/termos-de-uso-texto-integral.html +241 -0
- package/public/pages/termos-de-uso.html +41 -0
- package/public/pages/user-password-reset.html +32 -0
- package/public/pages/user-systemadm.html +508 -0
- package/public/pages/user.html +39 -0
- package/public/robots.txt +9 -0
- package/public/site.webmanifest +24 -0
- package/public/sitemap.xml +98 -0
- package/schemas/command-config.schema.json +582 -0
- package/scripts/baileys-compat-smoke.mjs +12 -0
- package/scripts/cache-bust.mjs +142 -0
- package/scripts/deploy.sh +916 -0
- package/scripts/email-broadcast-terms-update.mjs +170 -0
- package/scripts/enrich-command-discovery-fields.mjs +286 -0
- package/scripts/generate-command-config-schema.mjs +273 -0
- package/scripts/generate-commands-catalog.mjs +308 -0
- package/scripts/generate-module-agents.mjs +631 -0
- package/scripts/generate-seo-satellite-pages.mjs +400 -0
- package/scripts/github-deploy-notify.mjs +174 -0
- package/scripts/github-release-notify.mjs +219 -0
- package/scripts/release.sh +599 -0
- package/scripts/run-codeql-local.sh +116 -0
- package/scripts/run-prettier-all.mjs +25 -0
- package/scripts/security-smoketest.mjs +581 -0
- package/scripts/sticker-catalog-loadtest.mjs +210 -0
- package/scripts/sticker-worker-task.mjs +119 -0
- package/scripts/sync-readme-snapshot.mjs +133 -0
- package/scripts/validate-command-config-schema.mjs +130 -0
- package/scripts/validate-command-configs.mjs +15 -0
- package/scripts/wiki-sync.sh +191 -0
- package/server/auth/googleWebAuth/googleWebAuthRuntime.js +62 -0
- package/server/auth/googleWebAuth/googleWebAuthService.js +807 -0
- package/server/auth/jwt/webJwtService.js +147 -0
- package/server/auth/stickerCatalogAuthContext.js +165 -0
- package/server/auth/termsAcceptance/termsAcceptanceHandler.js +189 -0
- package/server/auth/userPassword/index.js +14 -0
- package/server/auth/userPassword/userPasswordAuthService.js +422 -0
- package/server/auth/userPassword/userPasswordCrypto.js +199 -0
- package/server/auth/userPassword/userPasswordCrypto.test.js +76 -0
- package/server/auth/userPassword/userPasswordRecoveryService.js +728 -0
- package/server/auth/validation/authSchemas.js +236 -0
- package/server/auth/webAccount/webAccountHandlers.js +1434 -0
- package/server/controllers/admin/adminBanService.js +138 -0
- package/server/controllers/admin/adminPanelHandlers.js +2083 -0
- package/server/controllers/admin/stickerCatalogAdminContext.js +17 -0
- package/server/controllers/admin/systemAdminController.js +201 -0
- package/server/controllers/email/emailAutomationController.js +239 -0
- package/server/controllers/metricsController.js +21 -0
- package/server/controllers/seo/stickerCatalogSeoContext.js +514 -0
- package/server/controllers/sticker/nonCatalogHandlers.js +303 -0
- package/server/controllers/sticker/stickerCatalogController.js +4700 -0
- package/server/controllers/system/contactController.js +115 -0
- package/server/controllers/system/githubController.js +137 -0
- package/server/controllers/system/stickerCatalogSystemContext.js +758 -0
- package/server/controllers/system/storageController.js +154 -0
- package/server/controllers/system/systemController.js +135 -0
- package/server/controllers/system/systemMetricsController.js +156 -0
- package/server/controllers/system/visitController.js +90 -0
- package/server/controllers/userController.js +145 -0
- package/server/email/emailAutomationRuntime.js +225 -0
- package/server/email/emailAutomationService.js +125 -0
- package/server/email/emailOutboxRepository.js +282 -0
- package/server/email/emailTemplateService.js +480 -0
- package/server/email/emailTransportService.js +156 -0
- package/server/http/clientIp.js +95 -0
- package/server/http/httpRequestUtils.js +262 -0
- package/server/http/httpRequestUtils.test.js +80 -0
- package/server/http/httpServer.js +180 -0
- package/server/http/requestContext.js +20 -0
- package/server/http/siteRoutingUtils.js +87 -0
- package/server/index.js +1 -0
- package/server/middleware/cachePolicy.js +26 -0
- package/server/middleware/cachePolicyHelpers.js +1 -0
- package/server/middleware/endpointRateLimit.js +181 -0
- package/server/middleware/rateLimit.js +70 -0
- package/server/middleware/requireAdminAuth.js +48 -0
- package/server/middleware/securityHeaders.js +97 -0
- package/server/routes/admin/systemAdminRouter.js +64 -0
- package/server/routes/email/emailAutomationRouter.js +46 -0
- package/server/routes/health/healthRouter.js +41 -0
- package/server/routes/indexRouter.js +234 -0
- package/server/routes/metrics/metricsRouter.js +58 -0
- package/server/routes/static/staticPageRouter.js +134 -0
- package/server/routes/sticker/catalogHandlers/catalogAdminHttp.js +105 -0
- package/server/routes/sticker/catalogHandlers/catalogAuthHttp.js +77 -0
- package/server/routes/sticker/catalogHandlers/catalogPublicHttp.js +120 -0
- package/server/routes/sticker/catalogHandlers/catalogUploadHttp.js +83 -0
- package/server/routes/sticker/catalogRouter.js +77 -0
- package/server/routes/sticker/stickerApiRouter.js +84 -0
- package/server/routes/sticker/stickerDataRouter.js +145 -0
- package/server/routes/sticker/stickerSiteRouter.js +43 -0
- package/server/routes/user/userApiPaths.js +66 -0
- package/server/routes/user/userRouter.js +65 -0
- package/server/utils/safePath.js +26 -0
- package/utils/logger/loggerModule.js +35 -0
- package/vite.config.mjs +38 -0
|
@@ -0,0 +1,807 @@
|
|
|
1
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
+
import axios from 'axios';
|
|
3
|
+
|
|
4
|
+
import { signWebAuthJwt, verifyWebAuthJwt, extractBearerTokenFromRequest } from '../jwt/webJwtService.js';
|
|
5
|
+
import { parseGoogleAuthSessionPayload } from '../validation/authSchemas.js';
|
|
6
|
+
import { resolveClientIp } from '../../http/clientIp.js';
|
|
7
|
+
|
|
8
|
+
const GOOGLE_TOKENINFO_URL = 'https://oauth2.googleapis.com/tokeninfo';
|
|
9
|
+
const GOOGLE_WEB_SESSION_COOKIE_NAME = 'omnizap_google_session';
|
|
10
|
+
|
|
11
|
+
const hashSessionToken = (value) => {
|
|
12
|
+
const token = String(value || '').trim();
|
|
13
|
+
if (!token) return null;
|
|
14
|
+
return createHash('sha256').update(token).digest();
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const buildSessionRowKeyFromHash = (hashValue) => {
|
|
18
|
+
if (!Buffer.isBuffer(hashValue) || !hashValue.length) return '';
|
|
19
|
+
return hashValue.toString('hex').slice(0, 36);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const normalizeCookiePath = (value, fallback = '/') => {
|
|
23
|
+
const raw = String(value || '').trim();
|
|
24
|
+
const base = raw || fallback;
|
|
25
|
+
const withSlash = base.startsWith('/') ? base : `/${base}`;
|
|
26
|
+
if (withSlash.length > 1 && withSlash.endsWith('/')) return withSlash.slice(0, -1);
|
|
27
|
+
return withSlash || '/';
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const createGoogleWebAuthService = ({ executeQuery, runSqlTransaction, tables, logger, sendJson, readJsonBody, parseCookies, getCookieValuesFromRequest, appendSetCookie, buildCookieString, normalizeGoogleSubject, normalizeEmail, normalizeJid, sanitizeText, toIsoOrNull, toWhatsAppPhoneDigits, resolveWhatsAppOwnerJidFromLoginPayload, buildGoogleOwnerJid, assertGoogleIdentityNotBanned, googleClientId, sessionTtlMs, sessionDbTouchIntervalMs, sessionDbPruneIntervalMs, notAllowedErrorCode, onGoogleWebSessionCreated = null, sessionCookiePath = '/', legacyCookiePaths = [] }) => {
|
|
31
|
+
const webGoogleSessionMap = new Map();
|
|
32
|
+
let googleWebSessionDbPruneAt = 0;
|
|
33
|
+
const normalizedSessionCookiePath = normalizeCookiePath(sessionCookiePath, '/');
|
|
34
|
+
const normalizedLegacyCookiePaths = Array.from(new Set(['/', normalizedSessionCookiePath, ...legacyCookiePaths].map((pathValue) => normalizeCookiePath(pathValue, '/')).filter(Boolean)));
|
|
35
|
+
const USER_PASSWORD_TABLE = String(tables.STICKER_WEB_USER_PASSWORD || 'web_user_password').trim() || 'web_user_password';
|
|
36
|
+
|
|
37
|
+
const pruneExpiredGoogleSessions = () => {
|
|
38
|
+
const now = Date.now();
|
|
39
|
+
for (const [token, session] of webGoogleSessionMap.entries()) {
|
|
40
|
+
if (!session || Number(session.expiresAt || 0) <= now) {
|
|
41
|
+
webGoogleSessionMap.delete(token);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const verifyGoogleIdToken = async (idToken) => {
|
|
47
|
+
const token = String(idToken || '').trim();
|
|
48
|
+
if (!token) {
|
|
49
|
+
const error = new Error('Token Google ausente.');
|
|
50
|
+
error.statusCode = 401;
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
let response;
|
|
55
|
+
try {
|
|
56
|
+
response = await axios.get(GOOGLE_TOKENINFO_URL, {
|
|
57
|
+
params: { id_token: token },
|
|
58
|
+
timeout: 5000,
|
|
59
|
+
validateStatus: () => true,
|
|
60
|
+
});
|
|
61
|
+
} catch (error) {
|
|
62
|
+
const wrapped = new Error('Falha ao validar login Google.');
|
|
63
|
+
wrapped.statusCode = 502;
|
|
64
|
+
wrapped.cause = error;
|
|
65
|
+
throw wrapped;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (response.status < 200 || response.status >= 300) {
|
|
69
|
+
const reason = String(response?.data?.error_description || response?.data?.error || '').trim();
|
|
70
|
+
const error = new Error(reason || 'Token Google inválido.');
|
|
71
|
+
error.statusCode = 401;
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const claims = response?.data && typeof response.data === 'object' ? response.data : {};
|
|
76
|
+
const aud = String(claims.aud || '').trim();
|
|
77
|
+
const iss = String(claims.iss || '').trim();
|
|
78
|
+
const sub = normalizeGoogleSubject(claims.sub);
|
|
79
|
+
const email = String(claims.email || '')
|
|
80
|
+
.trim()
|
|
81
|
+
.toLowerCase();
|
|
82
|
+
const emailVerified = String(claims.email_verified || '')
|
|
83
|
+
.trim()
|
|
84
|
+
.toLowerCase();
|
|
85
|
+
|
|
86
|
+
if (googleClientId && aud !== googleClientId) {
|
|
87
|
+
const error = new Error('Login Google não pertence a este aplicativo.');
|
|
88
|
+
error.statusCode = 403;
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
if (iss && !['accounts.google.com', 'https://accounts.google.com'].includes(iss)) {
|
|
92
|
+
const error = new Error('Emissor do token Google inválido.');
|
|
93
|
+
error.statusCode = 401;
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
if (!sub) {
|
|
97
|
+
const error = new Error('Token Google sem identificador de usuário.');
|
|
98
|
+
error.statusCode = 401;
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
if (email && emailVerified && !['true', '1'].includes(emailVerified)) {
|
|
102
|
+
const error = new Error('Conta Google sem e-mail verificado.');
|
|
103
|
+
error.statusCode = 403;
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
sub,
|
|
109
|
+
email: email || null,
|
|
110
|
+
name: sanitizeText(claims.name || claims.given_name || '', 120, { allowEmpty: true }) || null,
|
|
111
|
+
picture: String(claims.picture || '').trim() || null,
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const getGoogleWebSessionTokensFromRequest = (req) => {
|
|
116
|
+
const direct = getCookieValuesFromRequest(req, GOOGLE_WEB_SESSION_COOKIE_NAME);
|
|
117
|
+
if (direct.length > 0) return direct;
|
|
118
|
+
const cookies = parseCookies(req);
|
|
119
|
+
const fallback = String(cookies[GOOGLE_WEB_SESSION_COOKIE_NAME] || '').trim();
|
|
120
|
+
return fallback ? [fallback] : [];
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const normalizeGoogleWebSessionRow = (row, { sessionToken = '' } = {}) => {
|
|
124
|
+
if (!row || typeof row !== 'object') return null;
|
|
125
|
+
const token = String(sessionToken || row.session_token || '').trim();
|
|
126
|
+
const sub = normalizeGoogleSubject(row.google_sub);
|
|
127
|
+
const ownerJid = normalizeJid(row.owner_jid) || '';
|
|
128
|
+
const ownerPhone = toWhatsAppPhoneDigits(row.owner_phone || ownerJid) || '';
|
|
129
|
+
const expiresAt = Number(new Date(row.expires_at || 0));
|
|
130
|
+
if (!token || !sub || !ownerJid || !Number.isFinite(expiresAt)) return null;
|
|
131
|
+
const createdAtRaw = Number(new Date(row.created_at || 0));
|
|
132
|
+
const lastSeenAtRaw = Number(new Date(row.last_seen_at || 0));
|
|
133
|
+
return {
|
|
134
|
+
token,
|
|
135
|
+
sessionRowKey:
|
|
136
|
+
String(row.session_token || '')
|
|
137
|
+
.trim()
|
|
138
|
+
.slice(0, 36) || null,
|
|
139
|
+
sub,
|
|
140
|
+
email:
|
|
141
|
+
String(row.email || '')
|
|
142
|
+
.trim()
|
|
143
|
+
.toLowerCase() || null,
|
|
144
|
+
name: sanitizeText(row.name || '', 120, { allowEmpty: true }) || null,
|
|
145
|
+
picture: String(row.picture_url || '').trim() || null,
|
|
146
|
+
ownerJid,
|
|
147
|
+
ownerPhone,
|
|
148
|
+
createdAt: Number.isFinite(createdAtRaw) ? createdAtRaw : Date.now(),
|
|
149
|
+
expiresAt,
|
|
150
|
+
lastSeenAt: Number.isFinite(lastSeenAtRaw) ? lastSeenAtRaw : 0,
|
|
151
|
+
lastDbTouchAt: Date.now(),
|
|
152
|
+
};
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const maybePruneExpiredGoogleSessionsFromDb = async () => {
|
|
156
|
+
const now = Date.now();
|
|
157
|
+
if (now - googleWebSessionDbPruneAt < sessionDbPruneIntervalMs) return;
|
|
158
|
+
googleWebSessionDbPruneAt = now;
|
|
159
|
+
try {
|
|
160
|
+
await executeQuery(
|
|
161
|
+
`DELETE FROM ${tables.STICKER_WEB_GOOGLE_SESSION}
|
|
162
|
+
WHERE revoked_at IS NOT NULL OR expires_at <= UTC_TIMESTAMP()`,
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
// Backfill hash for any old row that still doesn't have `session_token_hash`.
|
|
166
|
+
await executeQuery(
|
|
167
|
+
`UPDATE ${tables.STICKER_WEB_GOOGLE_SESSION}
|
|
168
|
+
SET session_token_hash = UNHEX(SHA2(session_token, 256))
|
|
169
|
+
WHERE session_token_hash IS NULL
|
|
170
|
+
AND session_token IS NOT NULL`,
|
|
171
|
+
).catch(() => {});
|
|
172
|
+
|
|
173
|
+
// `session_token` stores only a non-sensitive deterministic row key.
|
|
174
|
+
await executeQuery(
|
|
175
|
+
`UPDATE ${tables.STICKER_WEB_GOOGLE_SESSION}
|
|
176
|
+
SET session_token = LOWER(SUBSTRING(HEX(session_token_hash), 1, 36))
|
|
177
|
+
WHERE session_token_hash IS NOT NULL
|
|
178
|
+
AND session_token <> LOWER(SUBSTRING(HEX(session_token_hash), 1, 36))`,
|
|
179
|
+
).catch(() => {});
|
|
180
|
+
} catch (error) {
|
|
181
|
+
logger.warn('Falha ao limpar sessões Google web expiradas do banco.', {
|
|
182
|
+
action: 'sticker_pack_google_web_session_db_prune_failed',
|
|
183
|
+
error: error?.message,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const upsertGoogleWebUserRecord = async (user, connection = null) => {
|
|
189
|
+
const sub = normalizeGoogleSubject(user?.sub);
|
|
190
|
+
const ownerJid = normalizeJid(user?.ownerJid) || '';
|
|
191
|
+
if (!sub || !ownerJid) return;
|
|
192
|
+
const ownerPhone = toWhatsAppPhoneDigits(ownerJid) || null;
|
|
193
|
+
const email =
|
|
194
|
+
String(user?.email || '')
|
|
195
|
+
.trim()
|
|
196
|
+
.toLowerCase() || null;
|
|
197
|
+
const name = sanitizeText(user?.name || '', 120, { allowEmpty: true }) || null;
|
|
198
|
+
const pictureUrl =
|
|
199
|
+
String(user?.picture || '')
|
|
200
|
+
.trim()
|
|
201
|
+
.slice(0, 1024) || null;
|
|
202
|
+
|
|
203
|
+
await executeQuery(
|
|
204
|
+
`DELETE FROM ${tables.STICKER_WEB_GOOGLE_USER}
|
|
205
|
+
WHERE owner_jid = ?
|
|
206
|
+
AND google_sub <> ?`,
|
|
207
|
+
[ownerJid, sub],
|
|
208
|
+
connection,
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
await executeQuery(
|
|
212
|
+
`INSERT INTO ${tables.STICKER_WEB_GOOGLE_USER}
|
|
213
|
+
(google_sub, owner_jid, email, name, picture_url, last_login_at, last_seen_at)
|
|
214
|
+
VALUES (?, ?, ?, ?, ?, UTC_TIMESTAMP(), UTC_TIMESTAMP())
|
|
215
|
+
ON DUPLICATE KEY UPDATE
|
|
216
|
+
owner_jid = VALUES(owner_jid),
|
|
217
|
+
email = VALUES(email),
|
|
218
|
+
name = VALUES(name),
|
|
219
|
+
picture_url = VALUES(picture_url),
|
|
220
|
+
last_login_at = UTC_TIMESTAMP(),
|
|
221
|
+
last_seen_at = UTC_TIMESTAMP()`,
|
|
222
|
+
[sub, ownerJid, email, name, pictureUrl],
|
|
223
|
+
connection,
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
await executeQuery(
|
|
227
|
+
`UPDATE ${tables.STICKER_WEB_GOOGLE_USER}
|
|
228
|
+
SET owner_phone = COALESCE(?, owner_phone)
|
|
229
|
+
WHERE google_sub = ?`,
|
|
230
|
+
[ownerPhone, sub],
|
|
231
|
+
connection,
|
|
232
|
+
).catch(() => {});
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
const upsertGoogleWebSessionRecord = async (session, connection = null) => {
|
|
236
|
+
const token = String(session?.token || '').trim();
|
|
237
|
+
const tokenHash = hashSessionToken(token);
|
|
238
|
+
const sessionRowKey = buildSessionRowKeyFromHash(tokenHash);
|
|
239
|
+
const sub = normalizeGoogleSubject(session?.sub);
|
|
240
|
+
const ownerJid = normalizeJid(session?.ownerJid) || '';
|
|
241
|
+
const ownerPhone = toWhatsAppPhoneDigits(session?.ownerPhone || ownerJid) || null;
|
|
242
|
+
const expiresAt = Number(session?.expiresAt || 0);
|
|
243
|
+
if (!token || !tokenHash || !sessionRowKey || !sub || !ownerJid || !Number.isFinite(expiresAt) || expiresAt <= 0) return;
|
|
244
|
+
const email =
|
|
245
|
+
String(session?.email || '')
|
|
246
|
+
.trim()
|
|
247
|
+
.toLowerCase() || null;
|
|
248
|
+
const name = sanitizeText(session?.name || '', 120, { allowEmpty: true }) || null;
|
|
249
|
+
const pictureUrl =
|
|
250
|
+
String(session?.picture || '')
|
|
251
|
+
.trim()
|
|
252
|
+
.slice(0, 1024) || null;
|
|
253
|
+
|
|
254
|
+
await executeQuery(
|
|
255
|
+
`INSERT INTO ${tables.STICKER_WEB_GOOGLE_SESSION}
|
|
256
|
+
(session_token, session_token_hash, google_sub, owner_jid, email, name, picture_url, expires_at, revoked_at, last_seen_at)
|
|
257
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, NULL, UTC_TIMESTAMP())
|
|
258
|
+
ON DUPLICATE KEY UPDATE
|
|
259
|
+
session_token_hash = VALUES(session_token_hash),
|
|
260
|
+
google_sub = VALUES(google_sub),
|
|
261
|
+
owner_jid = VALUES(owner_jid),
|
|
262
|
+
email = VALUES(email),
|
|
263
|
+
name = VALUES(name),
|
|
264
|
+
picture_url = VALUES(picture_url),
|
|
265
|
+
expires_at = VALUES(expires_at),
|
|
266
|
+
revoked_at = NULL,
|
|
267
|
+
last_seen_at = UTC_TIMESTAMP()`,
|
|
268
|
+
[sessionRowKey, tokenHash, sub, ownerJid, email, name, pictureUrl, new Date(expiresAt)],
|
|
269
|
+
connection,
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
await executeQuery(
|
|
273
|
+
`UPDATE ${tables.STICKER_WEB_GOOGLE_SESSION}
|
|
274
|
+
SET owner_phone = COALESCE(?, owner_phone)
|
|
275
|
+
WHERE session_token_hash = ?`,
|
|
276
|
+
[ownerPhone, tokenHash],
|
|
277
|
+
connection,
|
|
278
|
+
).catch(() => {});
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
const persistGoogleWebSessionToDb = async (session) => {
|
|
282
|
+
if (!session?.token || !session?.sub || !session?.ownerJid) return;
|
|
283
|
+
await maybePruneExpiredGoogleSessionsFromDb();
|
|
284
|
+
await runSqlTransaction(async (connection) => {
|
|
285
|
+
await upsertGoogleWebUserRecord(
|
|
286
|
+
{
|
|
287
|
+
sub: session.sub,
|
|
288
|
+
ownerJid: session.ownerJid,
|
|
289
|
+
email: session.email,
|
|
290
|
+
name: session.name,
|
|
291
|
+
picture: session.picture,
|
|
292
|
+
},
|
|
293
|
+
connection,
|
|
294
|
+
);
|
|
295
|
+
await upsertGoogleWebSessionRecord(session, connection);
|
|
296
|
+
});
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
const findGoogleWebSessionInDbByToken = async (sessionToken) => {
|
|
300
|
+
const token = String(sessionToken || '').trim();
|
|
301
|
+
const tokenHash = hashSessionToken(token);
|
|
302
|
+
if (!token) return null;
|
|
303
|
+
await maybePruneExpiredGoogleSessionsFromDb();
|
|
304
|
+
const rows = await executeQuery(
|
|
305
|
+
`SELECT
|
|
306
|
+
s.session_token,
|
|
307
|
+
s.google_sub,
|
|
308
|
+
s.owner_jid,
|
|
309
|
+
s.owner_phone,
|
|
310
|
+
s.email,
|
|
311
|
+
s.name,
|
|
312
|
+
s.picture_url,
|
|
313
|
+
s.created_at,
|
|
314
|
+
s.expires_at,
|
|
315
|
+
s.last_seen_at
|
|
316
|
+
FROM ${tables.STICKER_WEB_GOOGLE_SESSION} s
|
|
317
|
+
LEFT JOIN ${USER_PASSWORD_TABLE} p
|
|
318
|
+
ON p.google_sub = s.google_sub
|
|
319
|
+
AND p.revoked_at IS NULL
|
|
320
|
+
WHERE s.session_token_hash = ?
|
|
321
|
+
AND s.revoked_at IS NULL
|
|
322
|
+
AND s.expires_at > UTC_TIMESTAMP()
|
|
323
|
+
AND (p.password_changed_at IS NULL OR s.created_at >= p.password_changed_at)
|
|
324
|
+
LIMIT 1`,
|
|
325
|
+
[tokenHash],
|
|
326
|
+
);
|
|
327
|
+
return normalizeGoogleWebSessionRow(Array.isArray(rows) ? rows[0] : null, {
|
|
328
|
+
sessionToken: token,
|
|
329
|
+
});
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
const touchGoogleWebSessionSeenInDb = async (sessionToken) => {
|
|
333
|
+
const token = String(sessionToken || '').trim();
|
|
334
|
+
const tokenHash = hashSessionToken(token);
|
|
335
|
+
if (!token) return;
|
|
336
|
+
await executeQuery(
|
|
337
|
+
`UPDATE ${tables.STICKER_WEB_GOOGLE_SESSION}
|
|
338
|
+
SET last_seen_at = UTC_TIMESTAMP()
|
|
339
|
+
WHERE session_token_hash = ?
|
|
340
|
+
AND revoked_at IS NULL`,
|
|
341
|
+
[tokenHash],
|
|
342
|
+
);
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
const touchGoogleWebUserSeenInDb = async (googleSub) => {
|
|
346
|
+
const sub = normalizeGoogleSubject(googleSub);
|
|
347
|
+
if (!sub) return;
|
|
348
|
+
await executeQuery(
|
|
349
|
+
`UPDATE ${tables.STICKER_WEB_GOOGLE_USER}
|
|
350
|
+
SET last_seen_at = UTC_TIMESTAMP()
|
|
351
|
+
WHERE google_sub = ?`,
|
|
352
|
+
[sub],
|
|
353
|
+
);
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
const deleteGoogleWebSessionFromDb = async (sessionToken) => {
|
|
357
|
+
const token = String(sessionToken || '').trim();
|
|
358
|
+
const tokenHash = hashSessionToken(token);
|
|
359
|
+
if (!token) return 0;
|
|
360
|
+
const result = await executeQuery(
|
|
361
|
+
`DELETE FROM ${tables.STICKER_WEB_GOOGLE_SESSION}
|
|
362
|
+
WHERE session_token_hash = ?`,
|
|
363
|
+
[tokenHash],
|
|
364
|
+
);
|
|
365
|
+
return Number(result?.affectedRows || 0);
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
const createGoogleWebSession = (claims, { ownerJid } = {}) => {
|
|
369
|
+
pruneExpiredGoogleSessions();
|
|
370
|
+
const token = randomUUID();
|
|
371
|
+
const now = Date.now();
|
|
372
|
+
const resolvedOwnerJid = normalizeJid(ownerJid) || buildGoogleOwnerJid(claims.sub);
|
|
373
|
+
const resolvedOwnerPhone = toWhatsAppPhoneDigits(resolvedOwnerJid) || '';
|
|
374
|
+
return {
|
|
375
|
+
token,
|
|
376
|
+
sub: claims.sub,
|
|
377
|
+
email: claims.email || null,
|
|
378
|
+
name: claims.name || null,
|
|
379
|
+
picture: claims.picture || null,
|
|
380
|
+
ownerJid: resolvedOwnerJid,
|
|
381
|
+
ownerPhone: resolvedOwnerPhone,
|
|
382
|
+
createdAt: now,
|
|
383
|
+
expiresAt: now + sessionTtlMs,
|
|
384
|
+
lastSeenAt: now,
|
|
385
|
+
lastDbTouchAt: 0,
|
|
386
|
+
};
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
const activateGoogleWebSession = (session) => {
|
|
390
|
+
if (!session?.token) return;
|
|
391
|
+
pruneExpiredGoogleSessions();
|
|
392
|
+
webGoogleSessionMap.set(session.token, session);
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
const issueAccessTokenForSession = (session) => {
|
|
396
|
+
if (!session?.sub) return '';
|
|
397
|
+
const expiresInSeconds = Math.max(60, Math.floor((Number(session.expiresAt || 0) - Date.now()) / 1000));
|
|
398
|
+
return (
|
|
399
|
+
signWebAuthJwt(
|
|
400
|
+
{
|
|
401
|
+
sub: session.sub,
|
|
402
|
+
sessionToken: session.token,
|
|
403
|
+
ownerJid: session.ownerJid,
|
|
404
|
+
ownerPhone: session.ownerPhone,
|
|
405
|
+
email: session.email,
|
|
406
|
+
name: session.name,
|
|
407
|
+
authMethod: 'google',
|
|
408
|
+
},
|
|
409
|
+
{ expiresInSeconds },
|
|
410
|
+
) || ''
|
|
411
|
+
);
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
const setGoogleWebSessionCookie = (req, res, sessionToken, maxAgeSeconds = Math.floor(sessionTtlMs / 1000)) => {
|
|
415
|
+
const token = String(sessionToken || '').trim();
|
|
416
|
+
if (!token) return;
|
|
417
|
+
appendSetCookie(
|
|
418
|
+
res,
|
|
419
|
+
buildCookieString(GOOGLE_WEB_SESSION_COOKIE_NAME, token, req, {
|
|
420
|
+
path: normalizedSessionCookiePath,
|
|
421
|
+
maxAgeSeconds,
|
|
422
|
+
}),
|
|
423
|
+
);
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
const createPersistedGoogleWebSessionFromIdentity = async ({ sub = '', email = '', name = '', picture = '', ownerJid = '', requestMeta = null } = {}) => {
|
|
427
|
+
const normalizedSub = normalizeGoogleSubject(sub);
|
|
428
|
+
const normalizedOwnerJid = normalizeJid(ownerJid) || '';
|
|
429
|
+
if (!normalizedSub || !normalizedOwnerJid) {
|
|
430
|
+
const error = new Error('Nao foi possivel criar sessao para a identidade informada.');
|
|
431
|
+
error.statusCode = 400;
|
|
432
|
+
throw error;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
await assertGoogleIdentityNotBanned({
|
|
436
|
+
sub: normalizedSub,
|
|
437
|
+
email: normalizeEmail(email),
|
|
438
|
+
ownerJid: normalizedOwnerJid,
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
const session = createGoogleWebSession(
|
|
442
|
+
{
|
|
443
|
+
sub: normalizedSub,
|
|
444
|
+
email: normalizeEmail(email) || null,
|
|
445
|
+
name: sanitizeText(name || '', 120, { allowEmpty: true }) || null,
|
|
446
|
+
picture: String(picture || '').trim() || null,
|
|
447
|
+
},
|
|
448
|
+
{ ownerJid: normalizedOwnerJid },
|
|
449
|
+
);
|
|
450
|
+
|
|
451
|
+
if (!session.ownerJid) {
|
|
452
|
+
const error = new Error('Nao foi possivel vincular a conta ao owner informado.');
|
|
453
|
+
error.statusCode = 400;
|
|
454
|
+
throw error;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
await persistGoogleWebSessionToDb(session);
|
|
458
|
+
activateGoogleWebSession(session);
|
|
459
|
+
|
|
460
|
+
if (typeof onGoogleWebSessionCreated === 'function') {
|
|
461
|
+
Promise.resolve(
|
|
462
|
+
onGoogleWebSessionCreated({
|
|
463
|
+
sub: session.sub,
|
|
464
|
+
email: session.email,
|
|
465
|
+
name: session.name,
|
|
466
|
+
ownerJid: session.ownerJid,
|
|
467
|
+
ownerPhone: session.ownerPhone,
|
|
468
|
+
expiresAt: session.expiresAt,
|
|
469
|
+
request: requestMeta || null,
|
|
470
|
+
}),
|
|
471
|
+
).catch((hookError) => {
|
|
472
|
+
logger.warn('Falha ao executar callback pós-criacao de sessao Google web.', {
|
|
473
|
+
action: 'sticker_pack_google_web_session_created_hook_failed',
|
|
474
|
+
error: hookError?.message,
|
|
475
|
+
});
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
return session;
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
const resolveGoogleWebSessionFromBearerToken = async (req) => {
|
|
483
|
+
const bearerToken = extractBearerTokenFromRequest(req);
|
|
484
|
+
if (!bearerToken) return null;
|
|
485
|
+
|
|
486
|
+
const claims = verifyWebAuthJwt(bearerToken);
|
|
487
|
+
if (!claims?.sub) return null;
|
|
488
|
+
|
|
489
|
+
const sessionToken = String(claims.sid || '').trim();
|
|
490
|
+
if (!sessionToken) return null;
|
|
491
|
+
return resolvePersistedGoogleWebSessionByToken(sessionToken, {
|
|
492
|
+
expectedSub: claims.sub,
|
|
493
|
+
});
|
|
494
|
+
};
|
|
495
|
+
|
|
496
|
+
const touchGoogleWebSessionActivity = (session) => {
|
|
497
|
+
if (!session?.token || !session?.sub) return;
|
|
498
|
+
const now = Date.now();
|
|
499
|
+
session.lastSeenAt = now;
|
|
500
|
+
if (now - Number(session.lastDbTouchAt || 0) < sessionDbTouchIntervalMs) {
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
session.lastDbTouchAt = now;
|
|
504
|
+
void touchGoogleWebSessionSeenInDb(session.token).catch((error) => {
|
|
505
|
+
logger.warn('Falha ao atualizar last_seen da sessão Google web.', {
|
|
506
|
+
action: 'sticker_pack_google_web_session_touch_failed',
|
|
507
|
+
error: error?.message,
|
|
508
|
+
});
|
|
509
|
+
});
|
|
510
|
+
void touchGoogleWebUserSeenInDb(session.sub).catch(() => {});
|
|
511
|
+
};
|
|
512
|
+
|
|
513
|
+
const resolvePersistedGoogleWebSessionByToken = async (sessionToken, { expectedSub = '' } = {}) => {
|
|
514
|
+
const token = String(sessionToken || '').trim();
|
|
515
|
+
if (!token) return null;
|
|
516
|
+
const normalizedExpectedSub = normalizeGoogleSubject(expectedSub);
|
|
517
|
+
|
|
518
|
+
let persistedSession = null;
|
|
519
|
+
try {
|
|
520
|
+
persistedSession = await findGoogleWebSessionInDbByToken(token);
|
|
521
|
+
} catch (error) {
|
|
522
|
+
logger.warn('Falha ao resolver sessão Google web no banco.', {
|
|
523
|
+
action: 'sticker_pack_google_web_session_db_resolve_failed',
|
|
524
|
+
error: error?.message,
|
|
525
|
+
});
|
|
526
|
+
return null;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
if (!persistedSession) {
|
|
530
|
+
webGoogleSessionMap.delete(token);
|
|
531
|
+
return null;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
if (normalizedExpectedSub && normalizeGoogleSubject(persistedSession.sub) !== normalizedExpectedSub) {
|
|
535
|
+
return null;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
try {
|
|
539
|
+
await assertGoogleIdentityNotBanned({
|
|
540
|
+
sub: persistedSession.sub,
|
|
541
|
+
email: persistedSession.email,
|
|
542
|
+
ownerJid: persistedSession.ownerJid,
|
|
543
|
+
});
|
|
544
|
+
} catch {
|
|
545
|
+
webGoogleSessionMap.delete(token);
|
|
546
|
+
await deleteGoogleWebSessionFromDb(token).catch(() => {});
|
|
547
|
+
return null;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
touchGoogleWebSessionActivity(persistedSession);
|
|
551
|
+
activateGoogleWebSession(persistedSession);
|
|
552
|
+
return persistedSession;
|
|
553
|
+
};
|
|
554
|
+
|
|
555
|
+
const resolveGoogleWebSessionFromRequest = async (req) => {
|
|
556
|
+
pruneExpiredGoogleSessions();
|
|
557
|
+
const sessionTokens = getGoogleWebSessionTokensFromRequest(req);
|
|
558
|
+
if (!sessionTokens.length) {
|
|
559
|
+
return resolveGoogleWebSessionFromBearerToken(req);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
for (const sessionToken of sessionTokens) {
|
|
563
|
+
const resolvedSession = await resolvePersistedGoogleWebSessionByToken(sessionToken);
|
|
564
|
+
if (resolvedSession) return resolvedSession;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
return resolveGoogleWebSessionFromBearerToken(req);
|
|
568
|
+
};
|
|
569
|
+
|
|
570
|
+
const clearGoogleWebSessionCookie = (req, res) => {
|
|
571
|
+
for (const pathValue of normalizedLegacyCookiePaths) {
|
|
572
|
+
appendSetCookie(
|
|
573
|
+
res,
|
|
574
|
+
buildCookieString(GOOGLE_WEB_SESSION_COOKIE_NAME, '', req, {
|
|
575
|
+
path: pathValue,
|
|
576
|
+
maxAgeSeconds: 0,
|
|
577
|
+
}),
|
|
578
|
+
);
|
|
579
|
+
appendSetCookie(
|
|
580
|
+
res,
|
|
581
|
+
buildCookieString(GOOGLE_WEB_SESSION_COOKIE_NAME, '', req, {
|
|
582
|
+
path: pathValue,
|
|
583
|
+
maxAgeSeconds: 0,
|
|
584
|
+
domain: false,
|
|
585
|
+
}),
|
|
586
|
+
);
|
|
587
|
+
}
|
|
588
|
+
};
|
|
589
|
+
|
|
590
|
+
const mapGoogleSessionResponseData = (session, { accessToken = '' } = {}) => {
|
|
591
|
+
if (!session) {
|
|
592
|
+
return {
|
|
593
|
+
authenticated: false,
|
|
594
|
+
provider: 'google',
|
|
595
|
+
user: null,
|
|
596
|
+
owner_jid: null,
|
|
597
|
+
owner_phone: null,
|
|
598
|
+
expires_at: null,
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
const payload = {
|
|
603
|
+
authenticated: true,
|
|
604
|
+
provider: 'google',
|
|
605
|
+
user: {
|
|
606
|
+
sub: session.sub,
|
|
607
|
+
email: session.email,
|
|
608
|
+
name: session.name,
|
|
609
|
+
picture: session.picture,
|
|
610
|
+
},
|
|
611
|
+
owner_jid: session.ownerJid,
|
|
612
|
+
owner_phone: toWhatsAppPhoneDigits(session.ownerPhone || session.ownerJid) || null,
|
|
613
|
+
expires_at: toIsoOrNull(session.expiresAt),
|
|
614
|
+
};
|
|
615
|
+
|
|
616
|
+
const normalizedAccessToken = String(accessToken || '').trim();
|
|
617
|
+
if (normalizedAccessToken) {
|
|
618
|
+
payload.token_type = 'Bearer';
|
|
619
|
+
payload.access_token = normalizedAccessToken;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
return payload;
|
|
623
|
+
};
|
|
624
|
+
|
|
625
|
+
const revokeGoogleWebSessionsByIdentity = async ({ googleSub = '', email = '', ownerJid = '' } = {}) => {
|
|
626
|
+
const normalizedSub = normalizeGoogleSubject(googleSub);
|
|
627
|
+
const normalizedEmail = normalizeEmail(email);
|
|
628
|
+
const normalizedOwnerJid = normalizeJid(ownerJid) || '';
|
|
629
|
+
|
|
630
|
+
const clauses = [];
|
|
631
|
+
const params = [];
|
|
632
|
+
if (normalizedSub) {
|
|
633
|
+
clauses.push('google_sub = ?');
|
|
634
|
+
params.push(normalizedSub);
|
|
635
|
+
}
|
|
636
|
+
if (normalizedEmail) {
|
|
637
|
+
clauses.push('email = ?');
|
|
638
|
+
params.push(normalizedEmail);
|
|
639
|
+
}
|
|
640
|
+
if (normalizedOwnerJid) {
|
|
641
|
+
clauses.push('owner_jid = ?');
|
|
642
|
+
params.push(normalizedOwnerJid);
|
|
643
|
+
}
|
|
644
|
+
if (!clauses.length) return 0;
|
|
645
|
+
|
|
646
|
+
let removed = 0;
|
|
647
|
+
for (const [token, session] of webGoogleSessionMap.entries()) {
|
|
648
|
+
if (!session) continue;
|
|
649
|
+
const sessionSub = normalizeGoogleSubject(session.sub);
|
|
650
|
+
const sessionEmail = normalizeEmail(session.email);
|
|
651
|
+
const sessionOwner = normalizeJid(session.ownerJid) || '';
|
|
652
|
+
if ((normalizedSub && sessionSub === normalizedSub) || (normalizedEmail && sessionEmail === normalizedEmail) || (normalizedOwnerJid && sessionOwner === normalizedOwnerJid)) {
|
|
653
|
+
webGoogleSessionMap.delete(token);
|
|
654
|
+
removed += 1;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
try {
|
|
659
|
+
await executeQuery(
|
|
660
|
+
`DELETE FROM ${tables.STICKER_WEB_GOOGLE_SESSION}
|
|
661
|
+
WHERE ${clauses.join(' OR ')}`,
|
|
662
|
+
params,
|
|
663
|
+
);
|
|
664
|
+
} catch (error) {
|
|
665
|
+
const wrapped = new Error('Nao foi possivel revogar sessoes web persistidas.');
|
|
666
|
+
wrapped.statusCode = 503;
|
|
667
|
+
wrapped.code = 'SESSION_REVOKE_FAILED';
|
|
668
|
+
wrapped.cause = error;
|
|
669
|
+
throw wrapped;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
return removed;
|
|
673
|
+
};
|
|
674
|
+
|
|
675
|
+
const handleGoogleAuthSessionRequest = async (req, res) => {
|
|
676
|
+
if (!googleClientId) {
|
|
677
|
+
sendJson(req, res, 404, { error: 'Login Google desabilitado.' });
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
if (req.method === 'GET' || req.method === 'HEAD') {
|
|
682
|
+
const session = await resolveGoogleWebSessionFromRequest(req);
|
|
683
|
+
sendJson(req, res, 200, {
|
|
684
|
+
data: mapGoogleSessionResponseData(session),
|
|
685
|
+
});
|
|
686
|
+
return;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
if (req.method === 'DELETE') {
|
|
690
|
+
const tokens = getGoogleWebSessionTokensFromRequest(req);
|
|
691
|
+
for (const token of tokens) {
|
|
692
|
+
webGoogleSessionMap.delete(token);
|
|
693
|
+
await deleteGoogleWebSessionFromDb(token).catch((error) => {
|
|
694
|
+
logger.warn('Falha ao remover sessão Google web do banco.', {
|
|
695
|
+
action: 'sticker_pack_google_web_session_db_delete_failed',
|
|
696
|
+
error: error?.message,
|
|
697
|
+
});
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
clearGoogleWebSessionCookie(req, res);
|
|
701
|
+
sendJson(req, res, 200, { data: { cleared: true } });
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
if (req.method !== 'POST') {
|
|
706
|
+
sendJson(req, res, 405, { error: 'Metodo nao permitido.' });
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
let payload = {};
|
|
711
|
+
try {
|
|
712
|
+
payload = await readJsonBody(req);
|
|
713
|
+
} catch (error) {
|
|
714
|
+
sendJson(req, res, Number(error?.statusCode || 400), {
|
|
715
|
+
error: error?.message || 'Body inválido.',
|
|
716
|
+
});
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
try {
|
|
721
|
+
payload = parseGoogleAuthSessionPayload(payload);
|
|
722
|
+
} catch (error) {
|
|
723
|
+
sendJson(req, res, Number(error?.statusCode || 400), {
|
|
724
|
+
error: error?.message || 'Payload de login invalido.',
|
|
725
|
+
code: error?.code || 'INVALID_PAYLOAD',
|
|
726
|
+
details: Array.isArray(error?.details) ? error.details : undefined,
|
|
727
|
+
});
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
try {
|
|
732
|
+
const claims = await verifyGoogleIdToken(payload?.google_id_token || payload?.id_token);
|
|
733
|
+
const linkedOwner = resolveWhatsAppOwnerJidFromLoginPayload(payload);
|
|
734
|
+
if (!linkedOwner.ownerJid) {
|
|
735
|
+
if (!linkedOwner.hasPayload) {
|
|
736
|
+
sendJson(req, res, 400, {
|
|
737
|
+
error: 'Abra esta pagina pelo link enviado no WhatsApp. Envie "iniciar" no bot para gerar o link de login.',
|
|
738
|
+
code: 'WHATSAPP_LOGIN_LINK_REQUIRED',
|
|
739
|
+
reason: 'missing_link',
|
|
740
|
+
});
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
const reason = String(linkedOwner.reason || '')
|
|
745
|
+
.trim()
|
|
746
|
+
.toLowerCase();
|
|
747
|
+
const isUnauthorizedAttempt = ['invalid_signature', 'missing_signature'].includes(reason);
|
|
748
|
+
const statusCode = isUnauthorizedAttempt ? 403 : 400;
|
|
749
|
+
const errorMessage = reason === 'expired' ? 'Link de login expirado. Envie "iniciar" novamente no WhatsApp.' : isUnauthorizedAttempt ? 'Tentativa de login sem permissao detectada. Gere um novo link enviando "iniciar" no privado do bot.' : 'Link de login invalido. Envie "iniciar" novamente no WhatsApp.';
|
|
750
|
+
|
|
751
|
+
logger.warn('Tentativa de login web bloqueada por validacao do link WhatsApp.', {
|
|
752
|
+
action: 'sticker_pack_google_web_login_link_blocked',
|
|
753
|
+
reason: reason || 'unknown',
|
|
754
|
+
remote_ip: resolveClientIp(req, { fallback: null }),
|
|
755
|
+
user_agent: req.headers?.['user-agent'] || null,
|
|
756
|
+
});
|
|
757
|
+
|
|
758
|
+
sendJson(req, res, statusCode, {
|
|
759
|
+
error: errorMessage,
|
|
760
|
+
code: 'WHATSAPP_LOGIN_LINK_INVALID',
|
|
761
|
+
reason: reason || 'invalid_link',
|
|
762
|
+
});
|
|
763
|
+
return;
|
|
764
|
+
}
|
|
765
|
+
const ownerJid = linkedOwner.ownerJid;
|
|
766
|
+
|
|
767
|
+
const session = await createPersistedGoogleWebSessionFromIdentity({
|
|
768
|
+
sub: claims.sub,
|
|
769
|
+
email: claims.email || '',
|
|
770
|
+
name: claims.name || '',
|
|
771
|
+
picture: claims.picture || '',
|
|
772
|
+
ownerJid,
|
|
773
|
+
requestMeta: {
|
|
774
|
+
remoteIp: resolveClientIp(req, { fallback: null }),
|
|
775
|
+
userAgent: req.headers?.['user-agent'] || null,
|
|
776
|
+
},
|
|
777
|
+
});
|
|
778
|
+
|
|
779
|
+
setGoogleWebSessionCookie(req, res, session.token, Math.floor(sessionTtlMs / 1000));
|
|
780
|
+
const accessToken = issueAccessTokenForSession(session);
|
|
781
|
+
sendJson(req, res, 200, {
|
|
782
|
+
data: mapGoogleSessionResponseData(session, { accessToken }),
|
|
783
|
+
});
|
|
784
|
+
} catch (error) {
|
|
785
|
+
const statusCode = Number(error?.statusCode || 500);
|
|
786
|
+
sendJson(req, res, statusCode, {
|
|
787
|
+
error: error?.message || 'Login Google inválido.',
|
|
788
|
+
code: statusCode >= 500 ? 'INTERNAL_ERROR' : notAllowedErrorCode,
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
};
|
|
792
|
+
|
|
793
|
+
return {
|
|
794
|
+
cookieName: GOOGLE_WEB_SESSION_COOKIE_NAME,
|
|
795
|
+
getGoogleWebSessionTokensFromRequest,
|
|
796
|
+
upsertGoogleWebUserRecord,
|
|
797
|
+
resolveGoogleWebSessionFromRequest,
|
|
798
|
+
clearGoogleWebSessionCookie,
|
|
799
|
+
setGoogleWebSessionCookie,
|
|
800
|
+
deleteGoogleWebSessionFromDb,
|
|
801
|
+
mapGoogleSessionResponseData,
|
|
802
|
+
issueAccessTokenForSession,
|
|
803
|
+
createPersistedGoogleWebSessionFromIdentity,
|
|
804
|
+
handleGoogleAuthSessionRequest,
|
|
805
|
+
revokeGoogleWebSessionsByIdentity,
|
|
806
|
+
};
|
|
807
|
+
};
|