@kaikybrofc/omnizap-system 2.2.6 → 2.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -53,26 +53,26 @@ Atualização em cache: **30 minutos** por padrão (`README_SUMMARY_CACHE_SECOND
53
53
  <!-- README_SNAPSHOT:START -->
54
54
  ### Snapshot do Sistema
55
55
 
56
- > Atualizado em `2026-02-28T06:26:02.459Z` | cache `1800s`
56
+ > Atualizado em `2026-02-28T09:29:12.922Z` | cache `1800s`
57
57
 
58
58
  | Métrica | Valor |
59
59
  | --- | ---: |
60
60
  | Usuários (lid_map) | 5.504 |
61
61
  | Grupos | 116 |
62
- | Packs | 293 |
63
- | Stickers | 6.775 |
64
- | Mensagens registradas | 440.765 |
62
+ | Packs | 295 |
63
+ | Stickers | 6.811 |
64
+ | Mensagens registradas | 441.124 |
65
65
 
66
66
  #### Tipos de mensagem mais usados (amostra: 25.000)
67
67
  | Tipo | Total |
68
68
  | --- | ---: |
69
- | `texto` | 16.279 |
70
- | `figurinha` | 4.721 |
71
- | `reacao` | 1.504 |
72
- | `imagem` | 1.290 |
73
- | `outros` | 757 |
74
- | `video` | 228 |
75
- | `audio` | 216 |
69
+ | `texto` | 16.180 |
70
+ | `figurinha` | 4.806 |
71
+ | `reacao` | 1.502 |
72
+ | `imagem` | 1.298 |
73
+ | `outros` | 769 |
74
+ | `video` | 227 |
75
+ | `audio` | 213 |
76
76
  | `documento` | 5 |
77
77
 
78
78
  <details><summary>Comandos disponíveis (62)</summary>
@@ -1,10 +1,6 @@
1
1
  import 'dotenv/config';
2
2
 
3
- import http from 'node:http';
4
- import { randomUUID } from 'node:crypto';
5
- import { URL } from 'node:url';
6
3
  import client from 'prom-client';
7
- import logger from '../utils/logger/loggerModule.js';
8
4
 
9
5
  const parseEnvBool = (value, fallback) => {
10
6
  if (value === undefined || value === null || value === '') return fallback;
@@ -39,23 +35,12 @@ const QUERY_THRESHOLDS_MS = parseThresholds(process.env.DB_QUERY_ALERT_THRESHOLD
39
35
 
40
36
  const registry = new client.Registry();
41
37
  let metrics = null;
42
- let server = null;
43
- let serverStarted = false;
44
- let stickerCatalogModulePromise = null;
45
38
 
46
39
  const normalizeLabel = (value, fallback = 'unknown') => {
47
40
  if (value === undefined || value === null || value === '') return fallback;
48
41
  return String(value);
49
42
  };
50
43
 
51
- const normalizeRequestId = (value) => {
52
- const token = String(value || '')
53
- .trim()
54
- .replace(/[^a-zA-Z0-9._:-]/g, '')
55
- .slice(0, 120);
56
- return token || randomUUID();
57
- };
58
-
59
44
  const normalizeHttpMethod = (method) => {
60
45
  const normalized = String(method || '').trim().toUpperCase();
61
46
  if (!normalized) return 'UNKNOWN';
@@ -70,7 +55,7 @@ const toStatusClass = (statusCode) => {
70
55
  return `${head}xx`;
71
56
  };
72
57
 
73
- const resolveRouteGroup = ({ pathname, metricsPath, catalogConfig = null } = {}) => {
58
+ export const resolveRouteGroup = ({ pathname, metricsPath, catalogConfig = null } = {}) => {
74
59
  if (pathname?.startsWith(metricsPath)) return 'metrics';
75
60
  if (pathname === '/sitemap.xml') return 'sitemap';
76
61
  if (pathname === '/api/marketplace/stats') return 'marketplace_stats';
@@ -390,137 +375,23 @@ const ensureMetrics = () => {
390
375
  return metrics;
391
376
  };
392
377
 
393
- const loadStickerCatalogModule = async () => {
394
- if (!stickerCatalogModulePromise) {
395
- stickerCatalogModulePromise = import('../modules/stickerPackModule/stickerPackCatalogHttp.js');
396
- }
397
- return stickerCatalogModulePromise;
398
- };
399
-
400
378
  export const isMetricsEnabled = () => METRICS_ENABLED;
401
379
 
402
- export const startMetricsServer = () => {
403
- if (!METRICS_ENABLED || serverStarted) return;
404
- ensureMetrics();
405
- server = http.createServer(async (req, res) => {
406
- const requestStartedAt = Date.now();
407
- const requestId = normalizeRequestId(req.headers['x-request-id']);
408
- res.setHeader('X-Request-Id', requestId);
409
-
410
- const host = req.headers.host || `${METRICS_HOST}:${METRICS_PORT}`;
411
- let parsedUrl;
412
- try {
413
- parsedUrl = new URL(req.url || '/', `http://${host}`);
414
- } catch {
415
- parsedUrl = new URL(req.url || '/', 'http://localhost');
416
- }
417
- const pathname = parsedUrl.pathname;
418
- let routeGroup = resolveRouteGroup({
419
- pathname,
420
- metricsPath: METRICS_PATH,
421
- catalogConfig: null,
422
- });
423
-
424
- res.once('finish', () => {
425
- recordHttpRequest({
426
- durationMs: Date.now() - requestStartedAt,
427
- method: req.method,
428
- statusCode: res.statusCode,
429
- routeGroup,
430
- });
431
- });
432
-
433
- try {
434
- const stickerCatalogModule = await loadStickerCatalogModule();
435
- const catalogConfig =
436
- typeof stickerCatalogModule.getStickerCatalogConfig === 'function'
437
- ? stickerCatalogModule.getStickerCatalogConfig()
438
- : null;
439
- routeGroup = resolveRouteGroup({
440
- pathname,
441
- metricsPath: METRICS_PATH,
442
- catalogConfig,
443
- });
444
- const handledByCatalog = await stickerCatalogModule.maybeHandleStickerCatalogRequest(req, res, {
445
- pathname,
446
- url: parsedUrl,
447
- });
380
+ export const getMetricsServerConfig = () => ({
381
+ enabled: METRICS_ENABLED,
382
+ host: METRICS_HOST,
383
+ port: METRICS_PORT,
384
+ path: METRICS_PATH,
385
+ });
448
386
 
449
- if (handledByCatalog) {
450
- return;
451
- }
452
- } catch (error) {
453
- logger.error('Erro ao inicializar rotas web de sticker packs.', {
454
- error: error.message,
455
- });
456
- }
457
-
458
- if (!pathname.startsWith(METRICS_PATH)) {
459
- res.statusCode = 404;
460
- res.end('Not Found');
461
- return;
462
- }
463
-
464
- try {
465
- const body = await registry.metrics();
466
- res.statusCode = 200;
467
- res.setHeader('Content-Type', registry.contentType);
468
- res.end(body);
469
- } catch (error) {
470
- res.statusCode = 500;
471
- res.end('Metrics error');
472
- logger.error('Erro ao gerar /metrics', { error: error.message });
473
- }
474
- });
475
-
476
- server.listen(METRICS_PORT, METRICS_HOST, () => {
477
- serverStarted = true;
478
- logger.info('Servidor /metrics iniciado', {
479
- host: METRICS_HOST,
480
- port: METRICS_PORT,
481
- path: METRICS_PATH,
482
- });
483
-
484
- loadStickerCatalogModule()
485
- .then((module) => {
486
- const config = typeof module.getStickerCatalogConfig === 'function' ? module.getStickerCatalogConfig() : null;
487
- if (!config?.enabled) return;
488
- logger.info('Catalogo web de sticker packs habilitado', {
489
- web_path: config.webPath,
490
- api_base_path: config.apiBasePath,
491
- orphan_api_path: config.orphanApiPath,
492
- data_public_path: config.dataPublicPath,
493
- data_public_dir: config.dataPublicDir,
494
- host: METRICS_HOST,
495
- port: METRICS_PORT,
496
- });
497
- })
498
- .catch((error) => {
499
- logger.warn('Nao foi possivel carregar configuracao do catalogo de sticker packs.', {
500
- error: error.message,
501
- });
502
- });
503
- });
504
-
505
- server.on('error', (error) => {
506
- logger.error('Falha ao iniciar servidor /metrics', { error: error.message });
507
- });
508
- };
509
-
510
- export const stopMetricsServer = async () => {
511
- if (!serverStarted || !server) return;
512
- const current = server;
513
- server = null;
514
- serverStarted = false;
515
- await new Promise((resolve, reject) => {
516
- current.close((error) => {
517
- if (error) {
518
- reject(error);
519
- return;
520
- }
521
- resolve();
522
- });
523
- });
387
+ export const getMetricsPayload = async () => {
388
+ if (!METRICS_ENABLED) return null;
389
+ ensureMetrics();
390
+ const body = await registry.metrics();
391
+ return {
392
+ body,
393
+ contentType: registry.contentType,
394
+ };
524
395
  };
525
396
 
526
397
  export const recordError = (scope = 'app') => {
package/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Responsabilidades principais:
5
5
  * - Inicializar o banco (garantindo DB e tabelas).
6
- * - Subir servidor de métricas.
6
+ * - Subir servidor HTTP (catálogo web + endpoint de métricas).
7
7
  * - Rodar backfill do lid_map (opcional, em background).
8
8
  * - Conectar ao WhatsApp (Baileys).
9
9
  * - Iniciar serviços auxiliares (ex: broadcast de notícias).
@@ -25,7 +25,7 @@ import {
25
25
  stopNewsBroadcastService,
26
26
  } from './app/services/newsBroadcastService.js';
27
27
  import initializeDatabase from './database/init.js';
28
- import { startMetricsServer, stopMetricsServer } from './app/observability/metrics.js';
28
+ import { startHttpServer, stopHttpServer } from './server/index.js';
29
29
  import {
30
30
  startStickerClassificationBackground,
31
31
  stopStickerClassificationBackground,
@@ -198,7 +198,7 @@ async function closeDatabasePool() {
198
198
  *
199
199
  * Fluxo de startup:
200
200
  * 1) Inicializa DB (cria/verifica DB/tabelas)
201
- * 2) Sobe servidor de métricas
201
+ * 2) Sobe servidor HTTP (catálogo + métricas)
202
202
  * 3) Inicia backfill (opcional) em background
203
203
  * 4) Conecta no WhatsApp
204
204
  * 5) Inicia serviços auxiliares (news broadcast)
@@ -216,8 +216,8 @@ async function startApp() {
216
216
  await withTimeout(initializeDatabase(), DB_INIT_TIMEOUT_MS, 'Inicializacao do banco');
217
217
  logger.info('Banco de dados pronto.');
218
218
 
219
- logger.info('Inicializando servidor de metricas...');
220
- startMetricsServer();
219
+ logger.info('Inicializando servidor HTTP...');
220
+ startHttpServer();
221
221
  if (isStickerWorkerPipelineEnabled()) {
222
222
  startStickerWorkerPipeline();
223
223
  } else {
@@ -274,7 +274,7 @@ startApp();
274
274
  * - serviço de notícias (se stop existir)
275
275
  * - aguarda backfill (com timeout curto)
276
276
  * - encerra socket do WhatsApp
277
- * - encerra servidor de métricas
277
+ * - encerra servidor HTTP
278
278
  * - encerra pool do MySQL
279
279
  *
280
280
  * Regras:
@@ -339,14 +339,14 @@ async function shutdown(signal, error) {
339
339
  }
340
340
  }
341
341
 
342
- // 4) Encerrar servidor de métricas
343
- if (typeof stopMetricsServer === 'function') {
342
+ // 4) Encerrar servidor HTTP
343
+ if (typeof stopHttpServer === 'function') {
344
344
  try {
345
- logger.info('Encerrando servidor de metricas...');
346
- await withTimeout(stopMetricsServer(), 8000, 'Encerramento metricas');
347
- logger.info('Servidor de metricas encerrado.');
348
- } catch (metricsError) {
349
- logger.warn('Falha ao encerrar servidor de metricas.', { error: metricsError.message });
345
+ logger.info('Encerrando servidor HTTP...');
346
+ await withTimeout(stopHttpServer(), 8000, 'Encerramento HTTP');
347
+ logger.info('Servidor HTTP encerrado.');
348
+ } catch (httpError) {
349
+ logger.warn('Falha ao encerrar servidor HTTP.', { error: httpError.message });
350
350
  }
351
351
  }
352
352
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaikybrofc/omnizap-system",
3
- "version": "2.2.6",
3
+ "version": "2.2.7",
4
4
  "description": "Sistema profissional de automação WhatsApp com tecnologia Baileys",
5
5
  "main": "index.js",
6
6
  "publishConfig": {
@@ -0,0 +1,21 @@
1
+ import logger from '../../app/utils/logger/loggerModule.js';
2
+ import { getMetricsPayload } from '../../app/observability/metrics.js';
3
+
4
+ export const sendMetricsResponse = async (res) => {
5
+ try {
6
+ const payload = await getMetricsPayload();
7
+ if (!payload) {
8
+ res.statusCode = 404;
9
+ res.end('Not Found');
10
+ return;
11
+ }
12
+
13
+ res.statusCode = 200;
14
+ res.setHeader('Content-Type', payload.contentType);
15
+ res.end(payload.body);
16
+ } catch (error) {
17
+ res.statusCode = 500;
18
+ res.end('Metrics error');
19
+ logger.error('Erro ao gerar /metrics', { error: error?.message });
20
+ }
21
+ };
@@ -4,14 +4,18 @@ import { createHash, randomBytes, randomUUID, scryptSync, timingSafeEqual } from
4
4
  import { URL, URLSearchParams } from 'node:url';
5
5
  import axios from 'axios';
6
6
 
7
- import { executeQuery, pool, TABLES } from '../../../database/index.js';
8
- import { getJidUser, normalizeJid, resolveBotJid } from '../../config/baileysConfig.js';
9
- import { getAdminPhone, getAdminRawValue, resolveAdminJid } from '../../config/adminIdentity.js';
10
- import { getActiveSocket } from '../../services/socketState.js';
11
- import { extractUserIdInfo, resolveUserId } from '../../services/lidMapService.js';
12
- import { resolveWhatsAppOwnerJidFromLoginPayload, toWhatsAppOwnerJid, toWhatsAppPhoneDigits } from '../../services/whatsappLoginLinkService.js';
13
- import logger from '../../utils/logger/loggerModule.js';
14
- import { getSystemMetrics } from '../../utils/systemMetrics/systemMetricsModule.js';
7
+ import { executeQuery, pool, TABLES } from '../../database/index.js';
8
+ import { getJidUser, normalizeJid, resolveBotJid } from '../../app/config/baileysConfig.js';
9
+ import { getAdminPhone, getAdminRawValue, resolveAdminJid } from '../../app/config/adminIdentity.js';
10
+ import { getActiveSocket } from '../../app/services/socketState.js';
11
+ import { extractUserIdInfo, resolveUserId } from '../../app/services/lidMapService.js';
12
+ import {
13
+ resolveWhatsAppOwnerJidFromLoginPayload,
14
+ toWhatsAppOwnerJid,
15
+ toWhatsAppPhoneDigits,
16
+ } from '../../app/services/whatsappLoginLinkService.js';
17
+ import logger from '../../app/utils/logger/loggerModule.js';
18
+ import { getSystemMetrics } from '../../app/utils/systemMetrics/systemMetricsModule.js';
15
19
  import {
16
20
  listStickerPacksForCatalog,
17
21
  findStickerPackByPackKey,
@@ -20,7 +24,7 @@ import {
20
24
  findStickerPackByOwnerAndIdentifier,
21
25
  softDeleteStickerPack,
22
26
  updateStickerPackFields,
23
- } from './stickerPackRepository.js';
27
+ } from '../../app/modules/stickerPackModule/stickerPackRepository.js';
24
28
  import {
25
29
  listStickerPackItems,
26
30
  countStickerPackItemRefsByStickerId,
@@ -28,23 +32,23 @@ import {
28
32
  getStickerPackItemByStickerId,
29
33
  removeStickerPackItemByStickerId,
30
34
  removeStickerPackItemsByPackId,
31
- } from './stickerPackItemRepository.js';
35
+ } from '../../app/modules/stickerPackModule/stickerPackItemRepository.js';
32
36
  import {
33
37
  listClassifiedStickerAssetsWithoutPack,
34
38
  listStickerAssetsWithoutPack,
35
39
  deleteStickerAssetById,
36
40
  findStickerAssetsByIds,
37
- } from './stickerAssetRepository.js';
41
+ } from '../../app/modules/stickerPackModule/stickerAssetRepository.js';
38
42
  import {
39
43
  deleteStickerAssetClassificationByAssetId,
40
44
  findStickerClassificationByAssetId,
41
45
  listStickerClassificationsByAssetIds,
42
- } from './stickerAssetClassificationRepository.js';
46
+ } from '../../app/modules/stickerPackModule/stickerAssetClassificationRepository.js';
43
47
  import {
44
48
  decoratePackClassificationSummary,
45
49
  decorateStickerClassification,
46
50
  getPackClassificationSummaryByAssetIds,
47
- } from './stickerClassificationService.js';
51
+ } from '../../app/modules/stickerPackModule/stickerClassificationService.js';
48
52
  import {
49
53
  getEmptyStickerPackEngagement,
50
54
  getStickerPackEngagementByPackId,
@@ -52,21 +56,21 @@ import {
52
56
  incrementStickerPackLike,
53
57
  incrementStickerPackOpen,
54
58
  listStickerPackEngagementByPackIds,
55
- } from './stickerPackEngagementRepository.js';
59
+ } from '../../app/modules/stickerPackModule/stickerPackEngagementRepository.js';
56
60
  import {
57
61
  createStickerPackInteractionEvent,
58
62
  listStickerPackInteractionStatsByPackIds,
59
63
  listViewerRecentPackIds,
60
- } from './stickerPackInteractionEventRepository.js';
64
+ } from '../../app/modules/stickerPackModule/stickerPackInteractionEventRepository.js';
61
65
  import {
62
66
  buildCreatorRanking,
63
67
  buildIntentCollections,
64
68
  buildPersonalizedRecommendations,
65
69
  buildViewerTagAffinity,
66
70
  computePackSignals,
67
- } from './stickerPackMarketplaceService.js';
68
- import { listStickerPackScoreSnapshotsByPackIds } from './stickerPackScoreSnapshotRepository.js';
69
- import { createCatalogApiRouter } from './catalogRouter.js';
71
+ } from '../../app/modules/stickerPackModule/stickerPackMarketplaceService.js';
72
+ import { listStickerPackScoreSnapshotsByPackIds } from '../../app/modules/stickerPackModule/stickerPackScoreSnapshotRepository.js';
73
+ import { createCatalogApiRouter } from '../routes/stickerCatalog/catalogRouter.js';
70
74
  import {
71
75
  buildAdminMenu,
72
76
  buildAiMenu,
@@ -76,19 +80,19 @@ import {
76
80
  buildQuoteMenu,
77
81
  buildStatsMenu,
78
82
  buildStickerMenu,
79
- } from '../menuModule/common.js';
80
- import { getMarketplaceDriftSnapshot } from './stickerMarketplaceDriftService.js';
83
+ } from '../../app/modules/menuModule/common.js';
84
+ import { getMarketplaceDriftSnapshot } from '../../app/modules/stickerPackModule/stickerMarketplaceDriftService.js';
81
85
  import {
82
86
  getStickerAssetExternalUrl,
83
87
  getStickerStorageConfig,
84
88
  readStickerAssetBuffer,
85
89
  saveStickerAssetFromBuffer,
86
- } from './stickerStorageService.js';
87
- import { convertToWebp } from '../stickerModule/convertToWebp.js';
88
- import { sanitizeText } from './stickerPackUtils.js';
89
- import stickerPackService from './stickerPackServiceRuntime.js';
90
- import { STICKER_PACK_ERROR_CODES, StickerPackError } from './stickerPackErrors.js';
91
- import { isFeatureEnabled } from '../../services/featureFlagService.js';
90
+ } from '../../app/modules/stickerPackModule/stickerStorageService.js';
91
+ import { convertToWebp } from '../../app/modules/stickerModule/convertToWebp.js';
92
+ import { sanitizeText } from '../../app/modules/stickerPackModule/stickerPackUtils.js';
93
+ import stickerPackService from '../../app/modules/stickerPackModule/stickerPackServiceRuntime.js';
94
+ import { STICKER_PACK_ERROR_CODES, StickerPackError } from '../../app/modules/stickerPackModule/stickerPackErrors.js';
95
+ import { isFeatureEnabled } from '../../app/services/featureFlagService.js';
92
96
 
93
97
  const parseEnvBool = (value, fallback) => {
94
98
  if (value === undefined || value === null || value === '') return fallback;
@@ -539,7 +543,9 @@ const getCookieValuesFromRequest = (req, cookieName) => {
539
543
  let decodedValue = encodedValue;
540
544
  try {
541
545
  decodedValue = decodeURIComponent(encodedValue);
542
- } catch {}
546
+ } catch (_error) {
547
+ decodedValue = encodedValue;
548
+ }
543
549
  const normalizedValue = String(decodedValue || '').trim();
544
550
  if (!normalizedValue) continue;
545
551
  if (!values.includes(normalizedValue)) values.push(normalizedValue);
@@ -0,0 +1,121 @@
1
+ import http from 'node:http';
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';
9
+ import { parseRequestUrl, normalizeRequestId } from './requestContext.js';
10
+ import { maybeHandleMetricsRoute } from '../routes/metricsRoute.js';
11
+ import {
12
+ getStickerCatalogRouteConfig,
13
+ maybeHandleStickerCatalogRoute,
14
+ } from '../routes/stickerCatalogRoute.js';
15
+
16
+ let server = null;
17
+ let serverStarted = false;
18
+
19
+ export const startHttpServer = () => {
20
+ if (!isMetricsEnabled() || serverStarted) return;
21
+
22
+ const { host, port, path: metricsPath } = getMetricsServerConfig();
23
+ server = http.createServer(async (req, res) => {
24
+ const requestStartedAt = Date.now();
25
+ const requestId = normalizeRequestId(req.headers['x-request-id']);
26
+ res.setHeader('X-Request-Id', requestId);
27
+
28
+ const parsedUrl = parseRequestUrl(req, host, port);
29
+ const pathname = parsedUrl.pathname;
30
+ let routeGroup = resolveRouteGroup({
31
+ pathname,
32
+ metricsPath,
33
+ catalogConfig: null,
34
+ });
35
+
36
+ res.once('finish', () => {
37
+ recordHttpRequest({
38
+ durationMs: Date.now() - requestStartedAt,
39
+ method: req.method,
40
+ statusCode: res.statusCode,
41
+ routeGroup,
42
+ });
43
+ });
44
+
45
+ try {
46
+ const catalogConfig = await getStickerCatalogRouteConfig();
47
+ routeGroup = resolveRouteGroup({
48
+ pathname,
49
+ metricsPath,
50
+ catalogConfig,
51
+ });
52
+ const handledByCatalog = await maybeHandleStickerCatalogRoute(req, res, {
53
+ pathname,
54
+ url: parsedUrl,
55
+ });
56
+ if (handledByCatalog) return;
57
+ } catch (error) {
58
+ logger.error('Erro ao inicializar rotas web de sticker packs.', {
59
+ error: error?.message,
60
+ });
61
+ }
62
+
63
+ const handledMetrics = await maybeHandleMetricsRoute(req, res, {
64
+ pathname,
65
+ metricsPath,
66
+ });
67
+ if (handledMetrics) return;
68
+
69
+ res.statusCode = 404;
70
+ res.end('Not Found');
71
+ });
72
+
73
+ server.listen(port, host, () => {
74
+ serverStarted = true;
75
+ logger.info('Servidor HTTP iniciado', {
76
+ host,
77
+ port,
78
+ metrics_path: metricsPath,
79
+ });
80
+
81
+ getStickerCatalogRouteConfig()
82
+ .then((config) => {
83
+ if (!config?.enabled) return;
84
+ logger.info('Catalogo web de sticker packs habilitado', {
85
+ web_path: config.webPath,
86
+ api_base_path: config.apiBasePath,
87
+ orphan_api_path: config.orphanApiPath,
88
+ data_public_path: config.dataPublicPath,
89
+ data_public_dir: config.dataPublicDir,
90
+ host,
91
+ port,
92
+ });
93
+ })
94
+ .catch((error) => {
95
+ logger.warn('Nao foi possivel carregar configuracao do catalogo de sticker packs.', {
96
+ error: error?.message,
97
+ });
98
+ });
99
+ });
100
+
101
+ server.on('error', (error) => {
102
+ logger.error('Falha ao iniciar servidor HTTP', { error: error?.message });
103
+ });
104
+ };
105
+
106
+ export const stopHttpServer = async () => {
107
+ if (!serverStarted || !server) return;
108
+
109
+ const current = server;
110
+ server = null;
111
+ serverStarted = false;
112
+ await new Promise((resolve, reject) => {
113
+ current.close((error) => {
114
+ if (error) {
115
+ reject(error);
116
+ return;
117
+ }
118
+ resolve();
119
+ });
120
+ });
121
+ };
@@ -0,0 +1,20 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { URL } from 'node:url';
3
+
4
+ export const normalizeRequestId = (value) => {
5
+ const token = String(value || '')
6
+ .trim()
7
+ .replace(/[^a-zA-Z0-9._:-]/g, '')
8
+ .slice(0, 120);
9
+ return token || randomUUID();
10
+ };
11
+
12
+ export const parseRequestUrl = (req, host, port) => {
13
+ const fallbackHost = `${host}:${port}`;
14
+ const requestHost = req.headers.host || fallbackHost;
15
+ try {
16
+ return new URL(req.url || '/', `http://${requestHost}`);
17
+ } catch {
18
+ return new URL(req.url || '/', 'http://localhost');
19
+ }
20
+ };
@@ -0,0 +1 @@
1
+ export { startHttpServer, stopHttpServer } from './http/httpServer.js';
@@ -0,0 +1,7 @@
1
+ import { sendMetricsResponse } from '../controllers/metricsController.js';
2
+
3
+ export const maybeHandleMetricsRoute = async (req, res, { pathname, metricsPath }) => {
4
+ if (!pathname.startsWith(metricsPath)) return false;
5
+ await sendMetricsResponse(res);
6
+ return true;
7
+ };
@@ -0,0 +1,20 @@
1
+ let stickerCatalogControllerPromise = null;
2
+
3
+ const loadStickerCatalogController = async () => {
4
+ if (!stickerCatalogControllerPromise) {
5
+ stickerCatalogControllerPromise = import('../controllers/stickerCatalogController.js');
6
+ }
7
+ return stickerCatalogControllerPromise;
8
+ };
9
+
10
+ export const getStickerCatalogRouteConfig = async () => {
11
+ const controller = await loadStickerCatalogController();
12
+ if (typeof controller.getStickerCatalogConfig !== 'function') return null;
13
+ return controller.getStickerCatalogConfig();
14
+ };
15
+
16
+ export const maybeHandleStickerCatalogRoute = async (req, res, { pathname, url }) => {
17
+ const controller = await loadStickerCatalogController();
18
+ if (typeof controller.maybeHandleStickerCatalogRequest !== 'function') return false;
19
+ return controller.maybeHandleStickerCatalogRequest(req, res, { pathname, url });
20
+ };
Binary file