@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,120 @@
|
|
|
1
|
+
const METHOD_NOT_ALLOWED_BODY = { error: 'Metodo nao permitido.' };
|
|
2
|
+
|
|
3
|
+
const isReadMethod = (method) => method === 'GET' || method === 'HEAD';
|
|
4
|
+
const RESERVED_NON_STICKER_SEGMENTS = new Set(['home-bootstrap', 'system-summary', 'project-summary', 'global-ranking-summary', 'readme-markdown', 'support', 'bot-contact']);
|
|
5
|
+
|
|
6
|
+
export const handleCatalogPublicRoutes = async ({ req, res, pathname, url, segments, apiBasePath, orphanApiPath, handlers, sendJson }) => {
|
|
7
|
+
if (pathname === apiBasePath) {
|
|
8
|
+
if (!isReadMethod(req.method || '')) {
|
|
9
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
await handlers.handleListRequest(req, res, url);
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (pathname === `${apiBasePath}/intents`) {
|
|
17
|
+
if (!isReadMethod(req.method || '')) {
|
|
18
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
await handlers.handleIntentCollectionsRequest(req, res, url);
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (pathname === `${apiBasePath}/creators`) {
|
|
26
|
+
if (!isReadMethod(req.method || '')) {
|
|
27
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
await handlers.handleCreatorRankingRequest(req, res, url);
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (pathname === `${apiBasePath}/recommendations`) {
|
|
35
|
+
if (!isReadMethod(req.method || '')) {
|
|
36
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
await handlers.handleRecommendationsRequest(req, res, url);
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (pathname === `${apiBasePath}/stats`) {
|
|
44
|
+
if (!isReadMethod(req.method || '')) {
|
|
45
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
await handlers.handleMarketplaceStatsRequest(req, res, url);
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (pathname === `${apiBasePath}/create-config`) {
|
|
53
|
+
if (!isReadMethod(req.method || '')) {
|
|
54
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
await handlers.handleCreatePackConfigRequest(req, res);
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (pathname === orphanApiPath) {
|
|
62
|
+
if (!isReadMethod(req.method || '')) {
|
|
63
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
await handlers.handleOrphanStickerListRequest(req, res, url);
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (segments.length === 1 && segments[0] === 'data-files') {
|
|
71
|
+
if (!isReadMethod(req.method || '')) {
|
|
72
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
await handlers.handleDataFileListRequest(req, res, url);
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (segments.length === 1 && segments[0] === 'readme-summary') {
|
|
80
|
+
if (!isReadMethod(req.method || '')) {
|
|
81
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
await handlers.handleReadmeSummaryRequest(req, res);
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (segments.length === 1 && RESERVED_NON_STICKER_SEGMENTS.has(segments[0])) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (segments.length === 1) {
|
|
93
|
+
if (!isReadMethod(req.method || '')) {
|
|
94
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
await handlers.handleDetailsRequest(req, res, segments[0], url);
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (segments.length === 2 && ['open', 'like', 'dislike'].includes(segments[1])) {
|
|
102
|
+
if (req.method !== 'POST') {
|
|
103
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
await handlers.handlePackInteractionRequest(req, res, segments[0], segments[1], url);
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (segments.length === 3 && segments[1] === 'stickers') {
|
|
111
|
+
if (!isReadMethod(req.method || '')) {
|
|
112
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
await handlers.handleAssetRequest(req, res, segments[0], segments[2], url);
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return false;
|
|
120
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
const METHOD_NOT_ALLOWED_BODY = { error: 'Metodo nao permitido.' };
|
|
2
|
+
|
|
3
|
+
const isPublishStateMethod = (method) => method === 'GET' || method === 'HEAD' || method === 'POST';
|
|
4
|
+
|
|
5
|
+
export const handleCatalogUploadRoutes = async ({ req, res, pathname, url, segments, apiBasePath, handlers, sendJson }) => {
|
|
6
|
+
if (pathname === `${apiBasePath}/create`) {
|
|
7
|
+
if (req.method !== 'POST') {
|
|
8
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
await handlers.handleCreatePackRequest(req, res);
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (segments.length === 2 && segments[1] === 'manage') {
|
|
16
|
+
await handlers.handleManagedPackRequest(req, res, segments[0]);
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (segments.length === 3 && segments[1] === 'manage' && segments[2] === 'clone') {
|
|
21
|
+
await handlers.handleManagedPackCloneRequest(req, res, segments[0]);
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (segments.length === 3 && segments[1] === 'manage' && segments[2] === 'cover') {
|
|
26
|
+
await handlers.handleManagedPackCoverRequest(req, res, segments[0]);
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (segments.length === 3 && segments[1] === 'manage' && segments[2] === 'reorder') {
|
|
31
|
+
await handlers.handleManagedPackReorderRequest(req, res, segments[0]);
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (segments.length === 3 && segments[1] === 'manage' && segments[2] === 'analytics') {
|
|
36
|
+
await handlers.handleManagedPackAnalyticsRequest(req, res, segments[0]);
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (segments.length === 3 && segments[1] === 'manage' && segments[2] === 'stickers') {
|
|
41
|
+
await handlers.handleManagedPackStickerCreateRequest(req, res, segments[0]);
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (segments.length === 4 && segments[1] === 'manage' && segments[2] === 'stickers') {
|
|
46
|
+
await handlers.handleManagedPackStickerDeleteRequest(req, res, segments[0], segments[3]);
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (segments.length === 5 && segments[1] === 'manage' && segments[2] === 'stickers' && segments[4] === 'replace') {
|
|
51
|
+
await handlers.handleManagedPackStickerReplaceRequest(req, res, segments[0], segments[3]);
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (segments.length === 2 && segments[1] === 'publish-state') {
|
|
56
|
+
if (!isPublishStateMethod(req.method || '')) {
|
|
57
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
await handlers.handlePackPublishStateRequest(req, res, segments[0], url);
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (segments.length === 2 && segments[1] === 'finalize') {
|
|
65
|
+
if (req.method !== 'POST') {
|
|
66
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
await handlers.handleFinalizePackRequest(req, res, segments[0]);
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (segments.length === 2 && segments[1] === 'stickers-upload') {
|
|
74
|
+
if (req.method !== 'POST') {
|
|
75
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
await handlers.handleUploadStickerToPackRequest(req, res, segments[0]);
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return false;
|
|
83
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { handleCatalogAuthRoutes } from './catalogHandlers/catalogAuthHttp.js';
|
|
2
|
+
import { handleCatalogAdminRoutes } from './catalogHandlers/catalogAdminHttp.js';
|
|
3
|
+
import { handleCatalogUploadRoutes } from './catalogHandlers/catalogUploadHttp.js';
|
|
4
|
+
import { handleCatalogPublicRoutes } from './catalogHandlers/catalogPublicHttp.js';
|
|
5
|
+
|
|
6
|
+
const decodePathSegments = (suffix) =>
|
|
7
|
+
suffix
|
|
8
|
+
.split('/')
|
|
9
|
+
.filter(Boolean)
|
|
10
|
+
.map((segment) => {
|
|
11
|
+
try {
|
|
12
|
+
return decodeURIComponent(segment);
|
|
13
|
+
} catch {
|
|
14
|
+
return segment;
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export const createCatalogApiRouter = ({ apiBasePath, orphanApiPath, handlers, sendJson }) => {
|
|
19
|
+
if (!apiBasePath || typeof handlers !== 'object' || typeof sendJson !== 'function') {
|
|
20
|
+
throw new Error('catalog_api_router_config_invalid');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return async ({ req, res, pathname, url }) => {
|
|
24
|
+
const handledAuth = await handleCatalogAuthRoutes({
|
|
25
|
+
req,
|
|
26
|
+
res,
|
|
27
|
+
pathname,
|
|
28
|
+
url,
|
|
29
|
+
apiBasePath,
|
|
30
|
+
handlers,
|
|
31
|
+
sendJson,
|
|
32
|
+
});
|
|
33
|
+
if (handledAuth) return true;
|
|
34
|
+
if (!pathname.startsWith(apiBasePath)) return false;
|
|
35
|
+
|
|
36
|
+
const suffix = pathname.slice(apiBasePath.length).replace(/^\/+/, '');
|
|
37
|
+
const segments = decodePathSegments(suffix);
|
|
38
|
+
|
|
39
|
+
const handledAdmin = await handleCatalogAdminRoutes({
|
|
40
|
+
req,
|
|
41
|
+
res,
|
|
42
|
+
url,
|
|
43
|
+
segments,
|
|
44
|
+
handlers,
|
|
45
|
+
sendJson,
|
|
46
|
+
});
|
|
47
|
+
if (handledAdmin) return true;
|
|
48
|
+
|
|
49
|
+
const handledUpload = await handleCatalogUploadRoutes({
|
|
50
|
+
req,
|
|
51
|
+
res,
|
|
52
|
+
pathname,
|
|
53
|
+
url,
|
|
54
|
+
segments,
|
|
55
|
+
apiBasePath,
|
|
56
|
+
handlers,
|
|
57
|
+
sendJson,
|
|
58
|
+
});
|
|
59
|
+
if (handledUpload) return true;
|
|
60
|
+
|
|
61
|
+
const handledPublic = await handleCatalogPublicRoutes({
|
|
62
|
+
req,
|
|
63
|
+
res,
|
|
64
|
+
pathname,
|
|
65
|
+
url,
|
|
66
|
+
segments,
|
|
67
|
+
apiBasePath,
|
|
68
|
+
orphanApiPath,
|
|
69
|
+
handlers,
|
|
70
|
+
sendJson,
|
|
71
|
+
});
|
|
72
|
+
if (handledPublic) return true;
|
|
73
|
+
|
|
74
|
+
sendJson(req, res, 404, { error: 'Rota de sticker pack nao encontrada.' });
|
|
75
|
+
return true;
|
|
76
|
+
};
|
|
77
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { requireAdminAuth } from '../../middleware/requireAdminAuth.js';
|
|
2
|
+
import { createAdminApiRateLimit } from '../../middleware/rateLimit.js';
|
|
3
|
+
|
|
4
|
+
let stickerCatalogControllerPromise = null;
|
|
5
|
+
|
|
6
|
+
const loadStickerCatalogController = async () => {
|
|
7
|
+
if (!stickerCatalogControllerPromise) {
|
|
8
|
+
stickerCatalogControllerPromise = import('../../controllers/sticker/stickerCatalogController.js');
|
|
9
|
+
}
|
|
10
|
+
return stickerCatalogControllerPromise;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const normalizeBasePath = (value, fallback) => {
|
|
14
|
+
const raw = String(value || '').trim() || fallback;
|
|
15
|
+
const withLeadingSlash = raw.startsWith('/') ? raw : `/${raw}`;
|
|
16
|
+
const withoutTrailingSlash = withLeadingSlash.length > 1 && withLeadingSlash.endsWith('/') ? withLeadingSlash.slice(0, -1) : withLeadingSlash;
|
|
17
|
+
return withoutTrailingSlash || fallback;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const startsWithPath = (pathname, prefix) => {
|
|
21
|
+
if (!pathname || !prefix) return false;
|
|
22
|
+
if (pathname === prefix) return true;
|
|
23
|
+
return pathname.startsWith(`${prefix}/`);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const DEFAULT_STICKER_API_BASE_PATH = '/api/sticker-packs';
|
|
27
|
+
const DEFAULT_MARKETPLACE_STATS_PATH = '/api/marketplace/stats';
|
|
28
|
+
const ALLOWED_METHODS = new Set(['GET', 'HEAD', 'POST', 'PATCH', 'DELETE']);
|
|
29
|
+
const adminApiRateLimit = createAdminApiRateLimit();
|
|
30
|
+
|
|
31
|
+
const sendMethodNotAllowed = (req, res) => {
|
|
32
|
+
if (res.writableEnded) return;
|
|
33
|
+
res.statusCode = 405;
|
|
34
|
+
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
35
|
+
if (req.method === 'HEAD') {
|
|
36
|
+
res.end();
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
res.end(JSON.stringify({ error: 'Method Not Allowed' }));
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const getStickerApiRouterConfig = async () => {
|
|
43
|
+
const controller = await loadStickerCatalogController();
|
|
44
|
+
const legacyConfig = (typeof controller?.getStickerCatalogConfig === 'function' ? controller.getStickerCatalogConfig() : null) || {};
|
|
45
|
+
return {
|
|
46
|
+
apiBasePath: normalizeBasePath(legacyConfig.apiBasePath, DEFAULT_STICKER_API_BASE_PATH),
|
|
47
|
+
marketplaceStatsPath: DEFAULT_MARKETPLACE_STATS_PATH,
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const shouldHandleStickerApiPath = (pathname, stickerConfig = null) => {
|
|
52
|
+
const apiBasePath = normalizeBasePath(stickerConfig?.apiBasePath, DEFAULT_STICKER_API_BASE_PATH);
|
|
53
|
+
const marketplaceStatsPath = normalizeBasePath(stickerConfig?.marketplaceStatsPath, DEFAULT_MARKETPLACE_STATS_PATH);
|
|
54
|
+
return startsWithPath(pathname, apiBasePath) || startsWithPath(pathname, marketplaceStatsPath);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const maybeHandleStickerApiRequest = async (req, res, { pathname, url, config = null }) => {
|
|
58
|
+
const resolvedConfig = config || (await getStickerApiRouterConfig());
|
|
59
|
+
if (!shouldHandleStickerApiPath(pathname, resolvedConfig)) return false;
|
|
60
|
+
|
|
61
|
+
const method = String(req.method || '').toUpperCase();
|
|
62
|
+
if (!ALLOWED_METHODS.has(method)) {
|
|
63
|
+
sendMethodNotAllowed(req, res);
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const apiBasePath = normalizeBasePath(resolvedConfig.apiBasePath, DEFAULT_STICKER_API_BASE_PATH);
|
|
68
|
+
const adminBasePath = `${apiBasePath}/admin`;
|
|
69
|
+
const adminSessionPath = `${adminBasePath}/session`;
|
|
70
|
+
|
|
71
|
+
if (startsWithPath(pathname, adminBasePath)) {
|
|
72
|
+
const allowedByRateLimit = adminApiRateLimit(req, res);
|
|
73
|
+
if (!allowedByRateLimit) return true;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (startsWithPath(pathname, adminBasePath) && pathname !== adminSessionPath) {
|
|
77
|
+
const allowed = requireAdminAuth(req, res);
|
|
78
|
+
if (!allowed) return true;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const controller = await loadStickerCatalogController();
|
|
82
|
+
if (typeof controller?.maybeHandleStickerCatalogRequest !== 'function') return false;
|
|
83
|
+
return controller.maybeHandleStickerCatalogRequest(req, res, { pathname, url });
|
|
84
|
+
};
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
import { safeJoin } from '../../utils/safePath.js';
|
|
5
|
+
|
|
6
|
+
let stickerCatalogControllerPromise = null;
|
|
7
|
+
|
|
8
|
+
const loadStickerCatalogController = async () => {
|
|
9
|
+
if (!stickerCatalogControllerPromise) {
|
|
10
|
+
stickerCatalogControllerPromise = import('../../controllers/sticker/stickerCatalogController.js');
|
|
11
|
+
}
|
|
12
|
+
return stickerCatalogControllerPromise;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const normalizeBasePath = (value, fallback) => {
|
|
16
|
+
const raw = String(value || '').trim() || fallback;
|
|
17
|
+
const withLeadingSlash = raw.startsWith('/') ? raw : `/${raw}`;
|
|
18
|
+
const withoutTrailingSlash = withLeadingSlash.length > 1 && withLeadingSlash.endsWith('/') ? withLeadingSlash.slice(0, -1) : withLeadingSlash;
|
|
19
|
+
return withoutTrailingSlash || fallback;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const startsWithPath = (pathname, prefix) => {
|
|
23
|
+
if (!pathname || !prefix) return false;
|
|
24
|
+
if (pathname === prefix) return true;
|
|
25
|
+
return pathname.startsWith(`${prefix}/`);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const DEFAULT_DATA_PUBLIC_PATH = '/data';
|
|
29
|
+
const DEFAULT_DATA_PUBLIC_DIR = path.resolve(process.cwd(), 'data');
|
|
30
|
+
const ALLOWED_EXTENSIONS = new Set(['.webp', '.png', '.jpg', '.jpeg', '.gif', '.avif', '.bmp']);
|
|
31
|
+
|
|
32
|
+
const sendJson = (req, res, statusCode, payload) => {
|
|
33
|
+
if (res.writableEnded) return;
|
|
34
|
+
res.statusCode = statusCode;
|
|
35
|
+
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
36
|
+
if (req.method === 'HEAD') {
|
|
37
|
+
res.end();
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
res.end(JSON.stringify(payload));
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const resolveContentType = (extension) => {
|
|
44
|
+
if (extension === '.png') return 'image/png';
|
|
45
|
+
if (extension === '.jpg' || extension === '.jpeg') return 'image/jpeg';
|
|
46
|
+
if (extension === '.gif') return 'image/gif';
|
|
47
|
+
if (extension === '.avif') return 'image/avif';
|
|
48
|
+
if (extension === '.bmp') return 'image/bmp';
|
|
49
|
+
return 'image/webp';
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const decodeRelativePath = (pathname, dataPublicPath) => {
|
|
53
|
+
const rawSuffix = pathname.slice(dataPublicPath.length).replace(/^\/+/, '');
|
|
54
|
+
if (!rawSuffix) return '';
|
|
55
|
+
|
|
56
|
+
const decodedSegments = rawSuffix
|
|
57
|
+
.split('/')
|
|
58
|
+
.filter(Boolean)
|
|
59
|
+
.map((segment) => decodeURIComponent(segment));
|
|
60
|
+
|
|
61
|
+
return decodedSegments.join('/');
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export const getStickerDataRouterConfig = async () => {
|
|
65
|
+
const controller = await loadStickerCatalogController();
|
|
66
|
+
const legacyConfig = (typeof controller?.getStickerCatalogConfig === 'function' ? controller.getStickerCatalogConfig() : null) || {};
|
|
67
|
+
return {
|
|
68
|
+
dataPublicPath: normalizeBasePath(legacyConfig.dataPublicPath, DEFAULT_DATA_PUBLIC_PATH),
|
|
69
|
+
dataPublicDir: path.resolve(legacyConfig.dataPublicDir || DEFAULT_DATA_PUBLIC_DIR),
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export const shouldHandleStickerDataPath = (pathname, stickerConfig = null) => {
|
|
74
|
+
const dataPublicPath = normalizeBasePath(stickerConfig?.dataPublicPath, DEFAULT_DATA_PUBLIC_PATH);
|
|
75
|
+
return startsWithPath(pathname, dataPublicPath);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export const maybeHandleStickerDataRequest = async (req, res, { pathname, config = null }) => {
|
|
79
|
+
const resolvedConfig = config || (await getStickerDataRouterConfig());
|
|
80
|
+
const dataPublicPath = normalizeBasePath(resolvedConfig.dataPublicPath, DEFAULT_DATA_PUBLIC_PATH);
|
|
81
|
+
|
|
82
|
+
if (!shouldHandleStickerDataPath(pathname, resolvedConfig)) return false;
|
|
83
|
+
|
|
84
|
+
if (!['GET', 'HEAD'].includes(req.method || '')) {
|
|
85
|
+
sendJson(req, res, 405, { error: 'Method Not Allowed' });
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
let relativePath = '';
|
|
90
|
+
try {
|
|
91
|
+
relativePath = decodeRelativePath(pathname, dataPublicPath);
|
|
92
|
+
} catch {
|
|
93
|
+
sendJson(req, res, 400, { error: 'Invalid path encoding' });
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (!relativePath) {
|
|
98
|
+
sendJson(req, res, 400, { error: 'Invalid path' });
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const absolutePath = safeJoin(resolvedConfig.dataPublicDir, relativePath);
|
|
103
|
+
if (!absolutePath) {
|
|
104
|
+
sendJson(req, res, 400, { error: 'Invalid path' });
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const extension = path.extname(absolutePath).toLowerCase();
|
|
109
|
+
if (!ALLOWED_EXTENSIONS.has(extension)) {
|
|
110
|
+
sendJson(req, res, 403, { error: 'Forbidden file type' });
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
const fileHandle = await fs.open(absolutePath, 'r');
|
|
116
|
+
try {
|
|
117
|
+
const fileStat = await fileHandle.stat();
|
|
118
|
+
if (!fileStat.isFile()) {
|
|
119
|
+
sendJson(req, res, 404, { error: 'Not Found' });
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const fileBuffer = req.method === 'HEAD' ? null : await fileHandle.readFile();
|
|
124
|
+
res.statusCode = 200;
|
|
125
|
+
res.setHeader('Content-Type', resolveContentType(extension));
|
|
126
|
+
res.setHeader('Cache-Control', 'public, max-age=300');
|
|
127
|
+
if (req.method === 'HEAD') {
|
|
128
|
+
res.end();
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
res.end(fileBuffer);
|
|
132
|
+
return true;
|
|
133
|
+
} finally {
|
|
134
|
+
await fileHandle.close();
|
|
135
|
+
}
|
|
136
|
+
} catch (error) {
|
|
137
|
+
if (error?.code === 'ENOENT') {
|
|
138
|
+
sendJson(req, res, 404, { error: 'Not Found' });
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
sendJson(req, res, 500, { error: 'Read error' });
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
let stickerCatalogControllerPromise = null;
|
|
2
|
+
|
|
3
|
+
const loadStickerCatalogController = async () => {
|
|
4
|
+
if (!stickerCatalogControllerPromise) {
|
|
5
|
+
stickerCatalogControllerPromise = import('../../controllers/sticker/stickerCatalogController.js');
|
|
6
|
+
}
|
|
7
|
+
return stickerCatalogControllerPromise;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const normalizeBasePath = (value, fallback) => {
|
|
11
|
+
const raw = String(value || '').trim() || fallback;
|
|
12
|
+
const withLeadingSlash = raw.startsWith('/') ? raw : `/${raw}`;
|
|
13
|
+
const withoutTrailingSlash = withLeadingSlash.length > 1 && withLeadingSlash.endsWith('/') ? withLeadingSlash.slice(0, -1) : withLeadingSlash;
|
|
14
|
+
return withoutTrailingSlash || fallback;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const startsWithPath = (pathname, prefix) => {
|
|
18
|
+
if (!pathname || !prefix) return false;
|
|
19
|
+
if (pathname === prefix) return true;
|
|
20
|
+
return pathname.startsWith(`${prefix}/`);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const DEFAULT_STICKER_WEB_PATH = '/stickers';
|
|
24
|
+
|
|
25
|
+
export const getStickerSiteRouterConfig = async () => {
|
|
26
|
+
const controller = await loadStickerCatalogController();
|
|
27
|
+
const legacyConfig = (typeof controller?.getStickerCatalogConfig === 'function' ? controller.getStickerCatalogConfig() : null) || {};
|
|
28
|
+
return {
|
|
29
|
+
...legacyConfig,
|
|
30
|
+
webPath: normalizeBasePath(legacyConfig.webPath, DEFAULT_STICKER_WEB_PATH),
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const shouldHandleStickerSitePath = (pathname, stickerConfig = null) => {
|
|
35
|
+
const resolvedWebPath = normalizeBasePath(stickerConfig?.webPath, DEFAULT_STICKER_WEB_PATH);
|
|
36
|
+
return pathname === '/sitemap.xml' || startsWithPath(pathname, resolvedWebPath);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const maybeHandleStickerSiteRequest = async (req, res, { pathname, url }) => {
|
|
40
|
+
const controller = await loadStickerCatalogController();
|
|
41
|
+
if (typeof controller?.maybeHandleStickerCatalogRequest !== 'function') return false;
|
|
42
|
+
return controller.maybeHandleStickerCatalogRequest(req, res, { pathname, url });
|
|
43
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export const normalizeBasePath = (value, fallback) => {
|
|
2
|
+
const raw = String(value || '').trim() || fallback;
|
|
3
|
+
const withLeadingSlash = raw.startsWith('/') ? raw : `/${raw}`;
|
|
4
|
+
const withoutTrailingSlash = withLeadingSlash.length > 1 && withLeadingSlash.endsWith('/') ? withLeadingSlash.slice(0, -1) : withLeadingSlash;
|
|
5
|
+
return withoutTrailingSlash || fallback;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const normalizePathname = (value) => {
|
|
9
|
+
const raw = String(value || '').trim();
|
|
10
|
+
if (!raw) return '/';
|
|
11
|
+
if (raw.length > 1 && raw.endsWith('/')) return raw.slice(0, -1);
|
|
12
|
+
return raw;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const hasPathPrefix = (pathname, prefix) => {
|
|
16
|
+
if (pathname === prefix) return true;
|
|
17
|
+
const normalizedPrefix = String(prefix || '').endsWith('/') ? String(prefix || '') : `${prefix}/`;
|
|
18
|
+
return pathname.startsWith(normalizedPrefix);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const DEFAULT_USER_API_BASE_PATH = '/api';
|
|
22
|
+
export const DEFAULT_LEGACY_STICKER_API_BASE_PATH = '/api/sticker-packs';
|
|
23
|
+
|
|
24
|
+
const USER_API_EXACT_ROUTE_SUFFIXES = Object.freeze(['', '/auth/google/session', '/auth/login', '/auth/terms/acceptance', '/auth/password', '/auth/password/recovery/request', '/auth/password/recovery/verify', '/auth/password/recovery/session', '/auth/password/recovery/session/request', '/auth/password/recovery/session/verify', '/me', '/bot-contact', '/support', '/create-config', '/system-summary', '/project-summary', '/global-ranking-summary', '/readme-summary', '/readme-markdown', '/intents', '/creators', '/recommendations', '/stats', '/home-bootstrap']);
|
|
25
|
+
|
|
26
|
+
const USER_API_PRIMARY_ONLY_ROUTE_SUFFIXES = Object.freeze(['/home-bootstrap', '/system-summary', '/project-summary', '/global-ranking-summary', '/readme-markdown', '/support', '/bot-contact']);
|
|
27
|
+
const USER_API_PRIMARY_ONLY_ROUTE_SUFFIX_SET = new Set(USER_API_PRIMARY_ONLY_ROUTE_SUFFIXES);
|
|
28
|
+
|
|
29
|
+
const USER_API_LEGACY_EXACT_ROUTE_SUFFIXES = Object.freeze(USER_API_EXACT_ROUTE_SUFFIXES.filter((suffix) => !USER_API_PRIMARY_ONLY_ROUTE_SUFFIX_SET.has(suffix)));
|
|
30
|
+
|
|
31
|
+
const USER_API_PREFIX_ROUTE_SUFFIXES = Object.freeze([]);
|
|
32
|
+
|
|
33
|
+
const buildUserApiMatcher = (apiBasePath, { legacyCompatible = false } = {}) => {
|
|
34
|
+
const exactRouteSuffixes = legacyCompatible ? USER_API_LEGACY_EXACT_ROUTE_SUFFIXES : USER_API_EXACT_ROUTE_SUFFIXES;
|
|
35
|
+
const basePath = normalizeBasePath(apiBasePath, DEFAULT_USER_API_BASE_PATH);
|
|
36
|
+
const exactPaths = new Set(exactRouteSuffixes.map((suffix) => `${basePath}${suffix}`));
|
|
37
|
+
const prefixPaths = USER_API_PREFIX_ROUTE_SUFFIXES.map((suffix) => `${basePath}${suffix}`);
|
|
38
|
+
return {
|
|
39
|
+
basePath,
|
|
40
|
+
exactPaths,
|
|
41
|
+
prefixPaths,
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const buildUserApiPaths = (apiBasePath, options = {}) => buildUserApiMatcher(apiBasePath, options).exactPaths;
|
|
46
|
+
|
|
47
|
+
export const isUserApiPath = (pathname, apiBasePath, options = {}) => {
|
|
48
|
+
const normalizedPathname = normalizePathname(pathname);
|
|
49
|
+
const matcher = buildUserApiMatcher(apiBasePath, options);
|
|
50
|
+
if (matcher.exactPaths.has(normalizedPathname)) return true;
|
|
51
|
+
return matcher.prefixPaths.some((prefixPath) => hasPathPrefix(normalizedPathname, prefixPath));
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const resolveLegacyUserApiPath = (pathname, { apiBasePath = DEFAULT_USER_API_BASE_PATH, legacyApiBasePath = DEFAULT_LEGACY_STICKER_API_BASE_PATH, legacyCompatible = false } = {}) => {
|
|
55
|
+
const normalizedPathname = normalizePathname(pathname);
|
|
56
|
+
const resolvedApiBasePath = normalizeBasePath(apiBasePath, DEFAULT_USER_API_BASE_PATH);
|
|
57
|
+
const resolvedLegacyBasePath = normalizeBasePath(legacyApiBasePath, DEFAULT_LEGACY_STICKER_API_BASE_PATH);
|
|
58
|
+
|
|
59
|
+
if (!isUserApiPath(normalizedPathname, resolvedApiBasePath, { legacyCompatible })) return null;
|
|
60
|
+
if (resolvedApiBasePath === resolvedLegacyBasePath) return normalizedPathname;
|
|
61
|
+
if (normalizedPathname === resolvedApiBasePath) return resolvedLegacyBasePath;
|
|
62
|
+
|
|
63
|
+
const suffix = normalizedPathname.slice(resolvedApiBasePath.length);
|
|
64
|
+
if (!suffix) return resolvedLegacyBasePath;
|
|
65
|
+
return `${resolvedLegacyBasePath}${suffix.startsWith('/') ? suffix : `/${suffix}`}`;
|
|
66
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { DEFAULT_LEGACY_STICKER_API_BASE_PATH, DEFAULT_USER_API_BASE_PATH, buildUserApiPaths, isUserApiPath, normalizeBasePath } from './userApiPaths.js';
|
|
2
|
+
export { buildUserApiPaths };
|
|
3
|
+
|
|
4
|
+
let userControllerPromise = null;
|
|
5
|
+
|
|
6
|
+
const loadUserController = async () => {
|
|
7
|
+
if (!userControllerPromise) {
|
|
8
|
+
userControllerPromise = import('../../controllers/userController.js');
|
|
9
|
+
}
|
|
10
|
+
return userControllerPromise;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const startsWithPath = (pathname, prefix) => {
|
|
14
|
+
if (!pathname || !prefix) return false;
|
|
15
|
+
if (pathname === prefix) return true;
|
|
16
|
+
return pathname.startsWith(`${prefix}/`);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const DEFAULT_USER_WEB_PATH = '/user';
|
|
20
|
+
const DEFAULT_USER_LEGACY_API_BASE_PATH = DEFAULT_LEGACY_STICKER_API_BASE_PATH;
|
|
21
|
+
const DEFAULT_USER_PASSWORD_RESET_WEB_PATH = `${DEFAULT_USER_WEB_PATH}/password-reset`;
|
|
22
|
+
const resolveDefaultPasswordResetWebPath = (webPath) => {
|
|
23
|
+
const normalizedWebPath = normalizeBasePath(webPath, DEFAULT_USER_WEB_PATH);
|
|
24
|
+
if (normalizedWebPath === '/') return DEFAULT_USER_PASSWORD_RESET_WEB_PATH;
|
|
25
|
+
return normalizeBasePath(`${normalizedWebPath}/password-reset`, DEFAULT_USER_PASSWORD_RESET_WEB_PATH);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const getUserRouterConfig = async () => {
|
|
29
|
+
const controller = await loadUserController();
|
|
30
|
+
const legacyConfig = (typeof controller?.getUserRouteConfig === 'function' ? controller.getUserRouteConfig() : null) || {};
|
|
31
|
+
const webPath = normalizeBasePath(legacyConfig.webPath, DEFAULT_USER_WEB_PATH);
|
|
32
|
+
const fallbackPasswordResetWebPath = resolveDefaultPasswordResetWebPath(webPath);
|
|
33
|
+
return {
|
|
34
|
+
webPath,
|
|
35
|
+
passwordResetWebPath: normalizeBasePath(legacyConfig.passwordResetWebPath, fallbackPasswordResetWebPath),
|
|
36
|
+
apiBasePath: normalizeBasePath(legacyConfig.apiBasePath, DEFAULT_USER_API_BASE_PATH),
|
|
37
|
+
legacyApiBasePath: normalizeBasePath(legacyConfig.legacyApiBasePath, DEFAULT_USER_LEGACY_API_BASE_PATH),
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const shouldHandleUserPath = (pathname, userConfig = null) => {
|
|
42
|
+
const resolvedConfig = userConfig || {
|
|
43
|
+
webPath: DEFAULT_USER_WEB_PATH,
|
|
44
|
+
passwordResetWebPath: DEFAULT_USER_PASSWORD_RESET_WEB_PATH,
|
|
45
|
+
apiBasePath: DEFAULT_USER_API_BASE_PATH,
|
|
46
|
+
legacyApiBasePath: DEFAULT_USER_LEGACY_API_BASE_PATH,
|
|
47
|
+
};
|
|
48
|
+
const webPath = normalizeBasePath(resolvedConfig.webPath, DEFAULT_USER_WEB_PATH);
|
|
49
|
+
const fallbackPasswordResetWebPath = resolveDefaultPasswordResetWebPath(webPath);
|
|
50
|
+
const passwordResetWebPath = normalizeBasePath(resolvedConfig.passwordResetWebPath, fallbackPasswordResetWebPath);
|
|
51
|
+
|
|
52
|
+
if (startsWithPath(pathname, webPath) || startsWithPath(pathname, passwordResetWebPath)) return true;
|
|
53
|
+
|
|
54
|
+
const apiBasePath = normalizeBasePath(resolvedConfig.apiBasePath, DEFAULT_USER_API_BASE_PATH);
|
|
55
|
+
if (isUserApiPath(pathname, apiBasePath)) return true;
|
|
56
|
+
|
|
57
|
+
const legacyApiBasePath = normalizeBasePath(resolvedConfig.legacyApiBasePath, DEFAULT_USER_LEGACY_API_BASE_PATH);
|
|
58
|
+
return isUserApiPath(pathname, legacyApiBasePath, { legacyCompatible: true });
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const maybeHandleUserRequest = async (req, res, { pathname, url }) => {
|
|
62
|
+
const controller = await loadUserController();
|
|
63
|
+
if (typeof controller?.maybeHandleUserRequest !== 'function') return false;
|
|
64
|
+
return controller.maybeHandleUserRequest(req, res, { pathname, url });
|
|
65
|
+
};
|