@kaikybrofc/omnizap-system 2.2.10 → 2.3.0

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.
Files changed (119) hide show
  1. package/README.md +20 -18
  2. package/app/config/adminIdentity.js +1 -3
  3. package/app/connection/socketController.js +10 -20
  4. package/app/controllers/messageController.js +7 -28
  5. package/app/modules/aiModule/catCommand.js +29 -192
  6. package/app/modules/broadcastModule/noticeCommand.js +28 -97
  7. package/app/modules/gameModule/diceCommand.js +6 -32
  8. package/app/modules/playModule/playCommand.js +57 -258
  9. package/app/modules/quoteModule/quoteCommand.js +2 -4
  10. package/app/modules/rpgPokemonModule/rpgPokemonRepository.js +1 -13
  11. package/app/modules/statsModule/noMessageCommand.js +16 -84
  12. package/app/modules/statsModule/rankingCommand.js +5 -25
  13. package/app/modules/statsModule/rankingCommon.js +1 -9
  14. package/app/modules/stickerModule/convertToWebp.js +4 -27
  15. package/app/modules/stickerModule/stickerCommand.js +13 -24
  16. package/app/modules/stickerModule/stickerTextCommand.js +13 -25
  17. package/app/modules/stickerPackModule/autoPackCollectorService.js +16 -7
  18. package/app/modules/stickerPackModule/domainEventOutboxRepository.js +20 -36
  19. package/app/modules/stickerPackModule/domainEvents.js +2 -11
  20. package/app/modules/stickerPackModule/semanticReclassificationEngine.js +13 -50
  21. package/app/modules/stickerPackModule/semanticReclassificationEngine.test.js +2 -15
  22. package/app/modules/stickerPackModule/semanticThemeClusterService.js +14 -41
  23. package/app/modules/stickerPackModule/stickerAssetClassificationRepository.js +25 -95
  24. package/app/modules/stickerPackModule/stickerAssetRepository.js +12 -31
  25. package/app/modules/stickerPackModule/stickerAssetReprocessQueueRepository.js +13 -18
  26. package/app/modules/stickerPackModule/stickerAutoPackByTagsRuntime.js +284 -709
  27. package/app/modules/stickerPackModule/stickerClassificationBackgroundRuntime.js +27 -106
  28. package/app/modules/stickerPackModule/stickerClassificationService.js +46 -77
  29. package/app/modules/stickerPackModule/stickerDedicatedTaskWorkerRuntime.js +13 -53
  30. package/app/modules/stickerPackModule/stickerDomainEventBus.js +10 -16
  31. package/app/modules/stickerPackModule/stickerDomainEventConsumerRuntime.js +13 -34
  32. package/app/modules/stickerPackModule/stickerMarketplaceDriftService.js +1 -4
  33. package/app/modules/stickerPackModule/stickerObjectStorageService.js +26 -26
  34. package/app/modules/stickerPackModule/stickerPackCommandHandlers.js +32 -187
  35. package/app/modules/stickerPackModule/stickerPackInteractionEventRepository.js +6 -15
  36. package/app/modules/stickerPackModule/stickerPackItemRepository.js +6 -32
  37. package/app/modules/stickerPackModule/stickerPackMarketplaceService.js +12 -36
  38. package/app/modules/stickerPackModule/stickerPackMessageService.js +12 -40
  39. package/app/modules/stickerPackModule/stickerPackRepository.js +23 -66
  40. package/app/modules/stickerPackModule/stickerPackScoreSnapshotRepository.js +9 -21
  41. package/app/modules/stickerPackModule/stickerPackScoreSnapshotRuntime.js +10 -40
  42. package/app/modules/stickerPackModule/stickerPackService.js +50 -115
  43. package/app/modules/stickerPackModule/stickerPackServiceRuntime.js +2 -21
  44. package/app/modules/stickerPackModule/stickerPackUtils.js +13 -3
  45. package/app/modules/stickerPackModule/stickerStorageService.js +16 -65
  46. package/app/modules/stickerPackModule/stickerWorkerPipelineRuntime.js +4 -22
  47. package/app/modules/stickerPackModule/stickerWorkerTaskQueueRepository.js +14 -29
  48. package/app/modules/systemMetricsModule/pingCommand.js +9 -39
  49. package/app/modules/tiktokModule/tiktokCommand.js +17 -109
  50. package/app/modules/userModule/userCommand.js +2 -88
  51. package/app/observability/metrics.js +5 -16
  52. package/app/services/captchaService.js +1 -6
  53. package/app/services/dbWriteQueue.js +3 -18
  54. package/app/services/featureFlagService.js +2 -8
  55. package/app/services/newsBroadcastService.js +0 -1
  56. package/app/services/queueUtils.js +2 -4
  57. package/app/services/whatsappLoginLinkService.js +7 -9
  58. package/app/store/premiumUserStore.js +1 -2
  59. package/app/utils/antiLink/antiLinkModule.js +3 -233
  60. package/app/utils/logger/loggerModule.js +9 -34
  61. package/app/utils/systemMetrics/systemMetricsModule.js +1 -4
  62. package/database/init.js +1 -8
  63. package/docker-compose.yml +27 -27
  64. package/docs/seo/omnizap-seo-playbook-br-2026-02-28.md +26 -0
  65. package/docs/seo/satellite-page-template.md +2 -0
  66. package/docs/seo/satellite-pages-phase1.json +40 -177
  67. package/eslint.config.js +2 -15
  68. package/index.js +8 -36
  69. package/ml/clip_classifier/README.md +4 -6
  70. package/observability/alert-rules.yml +12 -12
  71. package/observability/grafana/provisioning/dashboards/dashboards.yml +1 -1
  72. package/package.json +6 -3
  73. package/public/api-docs/index.html +220 -193
  74. package/public/bot-whatsapp-para-grupo/index.html +291 -261
  75. package/public/bot-whatsapp-sem-programar/index.html +291 -261
  76. package/public/comandos/index.html +421 -406
  77. package/public/como-automatizar-avisos-no-whatsapp/index.html +291 -261
  78. package/public/como-criar-comandos-whatsapp/index.html +291 -261
  79. package/public/como-evitar-spam-no-whatsapp/index.html +291 -261
  80. package/public/como-moderar-grupo-whatsapp/index.html +291 -261
  81. package/public/como-organizar-comunidade-whatsapp/index.html +291 -261
  82. package/public/css/github-project-panel.css +13 -8
  83. package/public/css/stickers-admin.css +25 -9
  84. package/public/css/styles.css +23 -16
  85. package/public/index.html +1117 -994
  86. package/public/js/apps/apiDocsApp.js +17 -167
  87. package/public/js/apps/createPackApp.js +69 -332
  88. package/public/js/apps/homeApp.js +94 -74
  89. package/public/js/apps/loginApp.js +3 -12
  90. package/public/js/apps/stickersAdminApp.js +190 -181
  91. package/public/js/apps/stickersApp.js +496 -1397
  92. package/public/js/catalog.js +11 -74
  93. package/public/js/github-panel/components/ErrorState.js +1 -8
  94. package/public/js/github-panel/components/GithubProjectPanel.js +2 -9
  95. package/public/js/github-panel/components/SkeletonPanel.js +1 -11
  96. package/public/js/github-panel/components/StatCard.js +1 -7
  97. package/public/js/github-panel/vendor/react.js +1 -9
  98. package/public/js/runtime/react-runtime.js +1 -9
  99. package/public/licenca/index.html +104 -86
  100. package/public/login/index.html +315 -325
  101. package/public/melhor-bot-whatsapp-para-grupos/index.html +291 -261
  102. package/public/stickers/admin/index.html +14 -19
  103. package/public/stickers/create/index.html +39 -44
  104. package/public/stickers/index.html +96 -107
  105. package/public/termos-de-uso/index.html +142 -115
  106. package/public/user/index.html +347 -350
  107. package/scripts/cache-bust.mjs +5 -24
  108. package/scripts/generate-seo-satellite-pages.mjs +10 -13
  109. package/scripts/run-prettier-all.mjs +25 -0
  110. package/scripts/sticker-catalog-loadtest.mjs +13 -11
  111. package/scripts/sticker-worker-task.mjs +1 -4
  112. package/scripts/sync-readme-snapshot.mjs +3 -2
  113. package/server/controllers/stickerCatalogController.js +67 -5
  114. package/server/http/httpServer.js +2 -10
  115. package/server/routes/stickerCatalog/catalogHandlers/catalogAdminHttp.js +1 -8
  116. package/server/routes/stickerCatalog/catalogHandlers/catalogAuthHttp.js +1 -9
  117. package/server/routes/stickerCatalog/catalogHandlers/catalogPublicHttp.js +10 -11
  118. package/server/routes/stickerCatalog/catalogHandlers/catalogUploadHttp.js +1 -10
  119. package/server/routes/stickerCatalog/catalogRouter.js +11 -13
@@ -5,20 +5,10 @@ import path from 'node:path';
5
5
  import { URLSearchParams } from 'node:url';
6
6
 
7
7
  const TARGET_SOURCE_EXTENSIONS = new Set(['.html', '.js', '.mjs', '.css']);
8
- const ASSET_SUFFIX_PATTERN =
9
- String.raw`\.(?:js|mjs|cjs|css|png|jpe?g|gif|svg|webp|ico|json|map|woff2?|ttf|eot)(?:\?[^"'#\s)]*)?(?:#[^"' \s)]*)?`;
10
- const HTML_ATTRIBUTE_ASSET_PATTERN = new RegExp(
11
- String.raw`((?:src|href|poster)=["'])([^"']+?${ASSET_SUFFIX_PATTERN})(["'])`,
12
- 'gi',
13
- );
14
- const QUOTED_LOCAL_ASSET_PATTERN = new RegExp(
15
- String.raw`(["'])((?:\/|\.{1,2}\/)[^"'\s]+?${ASSET_SUFFIX_PATTERN})\1`,
16
- 'gi',
17
- );
18
- const CSS_URL_ASSET_PATTERN = new RegExp(
19
- String.raw`(url\(\s*["']?)([^"')\s]+?${ASSET_SUFFIX_PATTERN})(["']?\s*\))`,
20
- 'gi',
21
- );
8
+ const ASSET_SUFFIX_PATTERN = String.raw`\.(?:js|mjs|cjs|css|png|jpe?g|gif|svg|webp|ico|json|map|woff2?|ttf|eot)(?:\?[^"'#\s)]*)?(?:#[^"' \s)]*)?`;
9
+ const HTML_ATTRIBUTE_ASSET_PATTERN = new RegExp(String.raw`((?:src|href|poster)=["'])([^"']+?${ASSET_SUFFIX_PATTERN})(["'])`, 'gi');
10
+ const QUOTED_LOCAL_ASSET_PATTERN = new RegExp(String.raw`(["'])((?:\/|\.{1,2}\/)[^"'\s]+?${ASSET_SUFFIX_PATTERN})\1`, 'gi');
11
+ const CSS_URL_ASSET_PATTERN = new RegExp(String.raw`(url\(\s*["']?)([^"')\s]+?${ASSET_SUFFIX_PATTERN})(["']?\s*\))`, 'gi');
22
12
 
23
13
  const usage = () => {
24
14
  console.error('Uso: node scripts/cache-bust.mjs --dir <diretorio> --version <build_id>');
@@ -77,16 +67,7 @@ const isLocalAssetPath = (assetPath) => {
77
67
  if (!value) return false;
78
68
 
79
69
  const lower = value.toLowerCase();
80
- if (
81
- lower.startsWith('http://') ||
82
- lower.startsWith('https://') ||
83
- lower.startsWith('//') ||
84
- lower.startsWith('data:') ||
85
- lower.startsWith('mailto:') ||
86
- lower.startsWith('tel:') ||
87
- lower.startsWith('javascript:') ||
88
- lower.startsWith('#')
89
- ) {
70
+ if (lower.startsWith('http://') || lower.startsWith('https://') || lower.startsWith('//') || lower.startsWith('data:') || lower.startsWith('mailto:') || lower.startsWith('tel:') || lower.startsWith('javascript:') || lower.startsWith('#')) {
90
71
  return false;
91
72
  }
92
73
 
@@ -54,7 +54,12 @@ const ensurePageConfig = (page) => {
54
54
  h1: String(page.h1).trim(),
55
55
  intro: String(page.intro).trim(),
56
56
  intent_label: String(page.intent_label || 'Guia pratico').trim(),
57
- keywords: Array.isArray(page.keywords) ? page.keywords.filter(Boolean).map((item) => String(item).trim()).filter(Boolean) : [],
57
+ keywords: Array.isArray(page.keywords)
58
+ ? page.keywords
59
+ .filter(Boolean)
60
+ .map((item) => String(item).trim())
61
+ .filter(Boolean)
62
+ : [],
58
63
  sections: Array.isArray(page.sections) ? page.sections : [],
59
64
  faq: Array.isArray(page.faq) ? page.faq : [],
60
65
  related_links: Array.isArray(page.related_links) ? page.related_links : [],
@@ -63,19 +68,13 @@ const ensurePageConfig = (page) => {
63
68
 
64
69
  const renderSection = (section) => {
65
70
  const title = String(section?.title || '').trim();
66
- const paragraphs = Array.isArray(section?.paragraphs)
67
- ? section.paragraphs.map((item) => String(item || '').trim()).filter(Boolean)
68
- : [];
69
- const bullets = Array.isArray(section?.bullets)
70
- ? section.bullets.map((item) => String(item || '').trim()).filter(Boolean)
71
- : [];
71
+ const paragraphs = Array.isArray(section?.paragraphs) ? section.paragraphs.map((item) => String(item || '').trim()).filter(Boolean) : [];
72
+ const bullets = Array.isArray(section?.bullets) ? section.bullets.map((item) => String(item || '').trim()).filter(Boolean) : [];
72
73
 
73
74
  if (!title && paragraphs.length === 0 && bullets.length === 0) return '';
74
75
 
75
76
  const paragraphsHtml = paragraphs.map((paragraph) => ` <p>${escapeHtml(paragraph)}</p>`).join('\n');
76
- const bulletsHtml = bullets.length
77
- ? `\n <ul>\n${bullets.map((bullet) => ` <li>${escapeHtml(bullet)}</li>`).join('\n')}\n </ul>`
78
- : '';
77
+ const bulletsHtml = bullets.length ? `\n <ul>\n${bullets.map((bullet) => ` <li>${escapeHtml(bullet)}</li>`).join('\n')}\n </ul>` : '';
79
78
 
80
79
  return `<section class="card">\n ${title ? `<h2>${escapeHtml(title)}</h2>` : ''}\n${paragraphsHtml}${bulletsHtml}\n </section>`;
81
80
  };
@@ -122,9 +121,7 @@ const withRequiredLinks = (relatedLinks) => {
122
121
  const renderLinks = (links) => {
123
122
  if (!links.length) return '';
124
123
 
125
- return `<section class="card">\n <h2>Links úteis</h2>\n <div class="links-grid">\n${links
126
- .map((link) => ` <a href="${escapeHtml(link.href)}">${escapeHtml(link.label)}</a>`)
127
- .join('\n')}\n </div>\n </section>`;
124
+ return `<section class="card">\n <h2>Links úteis</h2>\n <div class="links-grid">\n${links.map((link) => ` <a href="${escapeHtml(link.href)}">${escapeHtml(link.label)}</a>`).join('\n')}\n </div>\n </section>`;
128
125
  };
129
126
 
130
127
  const renderPageHtml = (page, generatedAt) => {
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from 'node:child_process';
3
+ import { fileURLToPath } from 'node:url';
4
+ import path from 'node:path';
5
+
6
+ const scriptDir = path.dirname(fileURLToPath(import.meta.url));
7
+ const projectRoot = path.resolve(scriptDir, '..');
8
+ const prettierBin = path.join(projectRoot, 'node_modules', '.bin', process.platform === 'win32' ? 'prettier.cmd' : 'prettier');
9
+
10
+ const mode = process.argv.includes('--check') ? '--check' : '--write';
11
+ const args = ['.', mode, '--config', '.prettierrc', '--ignore-path', '.gitignore', '--ignore-unknown'];
12
+
13
+ const child = spawn(prettierBin, args, {
14
+ stdio: 'inherit',
15
+ cwd: projectRoot,
16
+ });
17
+
18
+ child.on('exit', (code) => {
19
+ process.exit(code ?? 1);
20
+ });
21
+
22
+ child.on('error', (error) => {
23
+ console.error(`[prettier] Falha ao executar: ${error?.message || 'erro desconhecido'}`);
24
+ process.exit(1);
25
+ });
@@ -6,11 +6,7 @@ import fs from 'node:fs/promises';
6
6
  import { URL } from 'node:url';
7
7
 
8
8
  const DEFAULT_BASE_URL = process.env.STICKER_LOADTEST_BASE_URL || 'http://127.0.0.1:9102';
9
- const DEFAULT_PATHS = [
10
- '/api/sticker-packs?limit=24&offset=0&sort=popular',
11
- '/api/sticker-packs/stats',
12
- '/api/sticker-packs/creators?limit=25',
13
- ];
9
+ const DEFAULT_PATHS = ['/api/sticker-packs?limit=24&offset=0&sort=popular', '/api/sticker-packs/stats', '/api/sticker-packs/creators?limit=25'];
14
10
  const DEFAULT_DURATION_SECONDS = 30;
15
11
  const DEFAULT_CONCURRENCY = 20;
16
12
  const DEFAULT_TIMEOUT_MS = 12_000;
@@ -33,7 +29,9 @@ const parseCliArgs = (argv) => {
33
29
  };
34
30
 
35
31
  const args = parseCliArgs(process.argv.slice(2));
36
- const baseUrl = String(args.get('--base-url') || DEFAULT_BASE_URL).trim().replace(/\/+$/, '');
32
+ const baseUrl = String(args.get('--base-url') || DEFAULT_BASE_URL)
33
+ .trim()
34
+ .replace(/\/+$/, '');
37
35
  const durationSeconds = Math.max(5, Number(args.get('--duration-seconds') || DEFAULT_DURATION_SECONDS));
38
36
  const concurrency = Math.max(1, Number(args.get('--concurrency') || DEFAULT_CONCURRENCY));
39
37
  const timeoutMs = Math.max(1000, Number(args.get('--timeout-ms') || DEFAULT_TIMEOUT_MS));
@@ -146,11 +144,15 @@ const main = async () => {
146
144
  console.log(`[loadtest] duration_seconds=${durationSeconds} concurrency=${concurrency} paths=${requestPaths.join(',')}`);
147
145
  await sleep(250);
148
146
 
149
- await Promise.all(Array.from({ length: concurrency }).map((_, index) => runWorker({
150
- deadlineMs,
151
- workerIndex: index,
152
- stats,
153
- })));
147
+ await Promise.all(
148
+ Array.from({ length: concurrency }).map((_, index) =>
149
+ runWorker({
150
+ deadlineMs,
151
+ workerIndex: index,
152
+ stats,
153
+ }),
154
+ ),
155
+ );
154
156
 
155
157
  const elapsedSeconds = Math.max(0.001, (Date.now() - startedAt) / 1000);
156
158
  const sortedLatencies = [...stats.latencies].sort((a, b) => a - b);
@@ -4,10 +4,7 @@ import 'dotenv/config';
4
4
  import logger from '../app/utils/logger/loggerModule.js';
5
5
  import initializeDatabase from '../database/init.js';
6
6
  import { closePool } from '../database/index.js';
7
- import {
8
- isSupportedStickerWorkerTaskType,
9
- startDedicatedStickerWorker,
10
- } from '../app/modules/stickerPackModule/stickerDedicatedTaskWorkerRuntime.js';
7
+ import { isSupportedStickerWorkerTaskType, startDedicatedStickerWorker } from '../app/modules/stickerPackModule/stickerDedicatedTaskWorkerRuntime.js';
11
8
 
12
9
  const parseCliArgs = (argv = []) => {
13
10
  const args = new Map();
@@ -15,7 +15,9 @@ const START_MARKER = '<!-- README_SNAPSHOT:START -->';
15
15
  const END_MARKER = '<!-- README_SNAPSHOT:END -->';
16
16
 
17
17
  const readmePath = path.resolve(projectRoot, process.env.README_SNAPSHOT_TARGET_PATH || 'README.md');
18
- const siteOrigin = String(process.env.SITE_ORIGIN || 'https://omnizap.shop').trim().replace(/\/+$/, '');
18
+ const siteOrigin = String(process.env.SITE_ORIGIN || 'https://omnizap.shop')
19
+ .trim()
20
+ .replace(/\/+$/, '');
19
21
  const sourceUrl = String(process.env.README_SNAPSHOT_SOURCE_URL || `${siteOrigin}/api/sticker-packs/readme-markdown`).trim();
20
22
  const timeoutMs = Math.max(1000, Number(process.env.README_SNAPSHOT_TIMEOUT_MS) || 15000);
21
23
 
@@ -68,4 +70,3 @@ syncReadmeSnapshot().catch((error) => {
68
70
  console.error(`[readme-sync] Falha: ${error?.message || error}`);
69
71
  process.exit(1);
70
72
  });
71
-
@@ -3033,11 +3033,7 @@ const renderCatalogHtml = async ({ initialPackKey }) => {
3033
3033
 
3034
3034
  const renderPackSeoHtml = ({ packSummary }) => {
3035
3035
  const packName = truncateText(packSummary?.name || packSummary?.pack_key || 'Pack', 95);
3036
- const packDescription = truncateText(
3037
- packSummary?.description
3038
- || `Pack de stickers "${packName}" disponível no catálogo OmniZap para uso em bots e automações WhatsApp via API.`,
3039
- 180,
3040
- );
3036
+ const packDescription = truncateText(packSummary?.description || `Pack de stickers "${packName}" disponível no catálogo OmniZap para uso em bots e automações WhatsApp via API.`, 180);
3041
3037
  const canonicalUrl = toSiteAbsoluteUrl(buildPackWebUrl(packSummary?.pack_key || ''));
3042
3038
  const catalogUrl = toSiteAbsoluteUrl(`${STICKER_WEB_PATH}/`);
3043
3039
  const homeUrl = toSiteAbsoluteUrl('/');
@@ -3648,6 +3644,71 @@ const handleMarketplaceStatsRequest = async (req, res, url) => {
3648
3644
  }
3649
3645
  };
3650
3646
 
3647
+ const handleHomeBootstrapRequest = async (req, res, url) => {
3648
+ const visibility = normalizeCatalogVisibility(url?.searchParams?.get('visibility'));
3649
+ const fetchTimeoutMs = {
3650
+ support: 450,
3651
+ session: 450,
3652
+ stats: 700,
3653
+ system_summary: 700,
3654
+ };
3655
+ const errors = [];
3656
+
3657
+ const [supportResult, sessionResult, statsResult, systemSummaryResult] = await Promise.allSettled([withTimeout(buildSupportInfo(), fetchTimeoutMs.support), STICKER_WEB_GOOGLE_CLIENT_ID ? withTimeout(resolveGoogleWebSessionFromRequest(req), fetchTimeoutMs.session) : Promise.resolve(null), withTimeout(getMarketplaceStatsCached(visibility), fetchTimeoutMs.stats), withTimeout(getSystemSummaryCached(), fetchTimeoutMs.system_summary)]);
3658
+
3659
+ const support = supportResult.status === 'fulfilled' ? supportResult.value || null : null;
3660
+ if (supportResult.status !== 'fulfilled') {
3661
+ errors.push({
3662
+ source: 'support',
3663
+ message: supportResult.reason?.message || 'support_unavailable',
3664
+ });
3665
+ }
3666
+
3667
+ const session = sessionResult.status === 'fulfilled' ? sessionResult.value || null : null;
3668
+ if (sessionResult.status !== 'fulfilled') {
3669
+ errors.push({
3670
+ source: 'session',
3671
+ message: sessionResult.reason?.message || 'session_unavailable',
3672
+ });
3673
+ }
3674
+
3675
+ const statsPayload = statsResult.status === 'fulfilled' ? statsResult.value || null : null;
3676
+ if (statsResult.status !== 'fulfilled') {
3677
+ errors.push({
3678
+ source: 'stats',
3679
+ message: statsResult.reason?.message || 'stats_unavailable',
3680
+ });
3681
+ }
3682
+
3683
+ const systemSummaryPayload = systemSummaryResult.status === 'fulfilled' ? systemSummaryResult.value || null : null;
3684
+ if (systemSummaryResult.status !== 'fulfilled') {
3685
+ errors.push({
3686
+ source: 'system_summary',
3687
+ message: systemSummaryResult.reason?.message || 'system_summary_unavailable',
3688
+ });
3689
+ }
3690
+
3691
+ sendJson(req, res, 200, {
3692
+ data: {
3693
+ support,
3694
+ session: mapGoogleSessionResponseData(session),
3695
+ stats: statsPayload?.data || null,
3696
+ stats_filters: statsPayload?.filters || null,
3697
+ system_summary: systemSummaryPayload?.data || null,
3698
+ },
3699
+ meta: {
3700
+ visibility,
3701
+ cache_seconds: {
3702
+ stats: HOME_MARKETPLACE_STATS_CACHE_SECONDS,
3703
+ system_summary: SYSTEM_SUMMARY_CACHE_SECONDS,
3704
+ },
3705
+ timeouts_ms: fetchTimeoutMs,
3706
+ partial: errors.length > 0,
3707
+ errors,
3708
+ },
3709
+ });
3710
+ };
3711
+
3651
3712
  const handleCreatePackConfigRequest = async (req, res) => {
3652
3713
  triggerStaleDraftCleanup();
3653
3714
  sendJson(req, res, 200, {
@@ -7624,6 +7685,7 @@ const catalogApiRouter = createCatalogApiRouter({
7624
7685
  handleCreatorRankingRequest,
7625
7686
  handleRecommendationsRequest,
7626
7687
  handleMarketplaceStatsRequest,
7688
+ handleHomeBootstrapRequest,
7627
7689
  handleCreatePackConfigRequest,
7628
7690
  handleOrphanStickerListRequest,
7629
7691
  handleDataFileListRequest,
@@ -1,17 +1,9 @@
1
1
  import http from 'node:http';
2
2
  import logger from '../../app/utils/logger/loggerModule.js';
3
- import {
4
- getMetricsServerConfig,
5
- isMetricsEnabled,
6
- recordHttpRequest,
7
- resolveRouteGroup,
8
- } from '../../app/observability/metrics.js';
3
+ import { getMetricsServerConfig, isMetricsEnabled, recordHttpRequest, resolveRouteGroup } from '../../app/observability/metrics.js';
9
4
  import { parseRequestUrl, normalizeRequestId } from './requestContext.js';
10
5
  import { maybeHandleMetricsRoute } from '../routes/metricsRoute.js';
11
- import {
12
- getStickerCatalogRouteConfig,
13
- maybeHandleStickerCatalogRoute,
14
- } from '../routes/stickerCatalogRoute.js';
6
+ import { getStickerCatalogRouteConfig, maybeHandleStickerCatalogRoute } from '../routes/stickerCatalogRoute.js';
15
7
 
16
8
  let server = null;
17
9
  let serverStarted = false;
@@ -1,11 +1,4 @@
1
- export const handleCatalogAdminRoutes = async ({
2
- req,
3
- res,
4
- url,
5
- segments,
6
- handlers,
7
- sendJson,
8
- }) => {
1
+ export const handleCatalogAdminRoutes = async ({ req, res, url, segments, handlers, sendJson }) => {
9
2
  if (segments[0] !== 'admin') return false;
10
3
 
11
4
  if (segments.length === 2 && segments[1] === 'overview') {
@@ -2,15 +2,7 @@ const METHOD_NOT_ALLOWED_BODY = { error: 'Metodo nao permitido.' };
2
2
 
3
3
  const isReadMethod = (method) => method === 'GET' || method === 'HEAD';
4
4
 
5
- export const handleCatalogAuthRoutes = async ({
6
- req,
7
- res,
8
- pathname,
9
- url,
10
- apiBasePath,
11
- handlers,
12
- sendJson,
13
- }) => {
5
+ export const handleCatalogAuthRoutes = async ({ req, res, pathname, url, apiBasePath, handlers, sendJson }) => {
14
6
  if (pathname === `${apiBasePath}/auth/google/session`) {
15
7
  await handlers.handleGoogleAuthSessionRequest(req, res);
16
8
  return true;
@@ -2,17 +2,7 @@ const METHOD_NOT_ALLOWED_BODY = { error: 'Metodo nao permitido.' };
2
2
 
3
3
  const isReadMethod = (method) => method === 'GET' || method === 'HEAD';
4
4
 
5
- export const handleCatalogPublicRoutes = async ({
6
- req,
7
- res,
8
- pathname,
9
- url,
10
- segments,
11
- apiBasePath,
12
- orphanApiPath,
13
- handlers,
14
- sendJson,
15
- }) => {
5
+ export const handleCatalogPublicRoutes = async ({ req, res, pathname, url, segments, apiBasePath, orphanApiPath, handlers, sendJson }) => {
16
6
  if (pathname === apiBasePath) {
17
7
  if (!isReadMethod(req.method || '')) {
18
8
  sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
@@ -58,6 +48,15 @@ export const handleCatalogPublicRoutes = async ({
58
48
  return true;
59
49
  }
60
50
 
51
+ if (pathname === `${apiBasePath}/home-bootstrap`) {
52
+ if (!isReadMethod(req.method || '')) {
53
+ sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
54
+ return true;
55
+ }
56
+ await handlers.handleHomeBootstrapRequest(req, res, url);
57
+ return true;
58
+ }
59
+
61
60
  if (pathname === `${apiBasePath}/create-config`) {
62
61
  if (!isReadMethod(req.method || '')) {
63
62
  sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
@@ -2,16 +2,7 @@ const METHOD_NOT_ALLOWED_BODY = { error: 'Metodo nao permitido.' };
2
2
 
3
3
  const isPublishStateMethod = (method) => method === 'GET' || method === 'HEAD' || method === 'POST';
4
4
 
5
- export const handleCatalogUploadRoutes = async ({
6
- req,
7
- res,
8
- pathname,
9
- url,
10
- segments,
11
- apiBasePath,
12
- handlers,
13
- sendJson,
14
- }) => {
5
+ export const handleCatalogUploadRoutes = async ({ req, res, pathname, url, segments, apiBasePath, handlers, sendJson }) => {
15
6
  if (pathname === `${apiBasePath}/create`) {
16
7
  if (req.method !== 'POST') {
17
8
  sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
@@ -4,20 +4,18 @@ import { handleCatalogUploadRoutes } from './catalogHandlers/catalogUploadHttp.j
4
4
  import { handleCatalogPublicRoutes } from './catalogHandlers/catalogPublicHttp.js';
5
5
 
6
6
  const decodePathSegments = (suffix) =>
7
- suffix.split('/').filter(Boolean).map((segment) => {
8
- try {
9
- return decodeURIComponent(segment);
10
- } catch {
11
- return segment;
12
- }
13
- });
7
+ suffix
8
+ .split('/')
9
+ .filter(Boolean)
10
+ .map((segment) => {
11
+ try {
12
+ return decodeURIComponent(segment);
13
+ } catch {
14
+ return segment;
15
+ }
16
+ });
14
17
 
15
- export const createCatalogApiRouter = ({
16
- apiBasePath,
17
- orphanApiPath,
18
- handlers,
19
- sendJson,
20
- }) => {
18
+ export const createCatalogApiRouter = ({ apiBasePath, orphanApiPath, handlers, sendJson }) => {
21
19
  if (!apiBasePath || typeof handlers !== 'object' || typeof sendJson !== 'function') {
22
20
  throw new Error('catalog_api_router_config_invalid');
23
21
  }