@kaikybrofc/omnizap-system 2.2.6 → 2.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -12
- package/app/observability/metrics.js +15 -144
- package/index.js +13 -13
- package/kaikybrofc-omnizap-system-2.2.7.tgz +0 -0
- package/kaikybrofc-omnizap-system-2.2.8.tgz +0 -0
- package/package.json +1 -1
- package/server/controllers/metricsController.js +21 -0
- package/{app/modules/stickerPackModule/stickerPackCatalogHttp.js → server/controllers/stickerCatalogController.js} +33 -27
- package/server/http/httpServer.js +121 -0
- package/server/http/requestContext.js +20 -0
- package/server/index.js +1 -0
- package/server/routes/metricsRoute.js +7 -0
- package/server/routes/stickerCatalogRoute.js +20 -0
- package/kaikybrofc-omnizap-system-2.2.6.tgz +0 -0
- /package/{app/modules/stickerPackModule → server/routes/stickerCatalog}/catalogHandlers/catalogAdminHttp.js +0 -0
- /package/{app/modules/stickerPackModule → server/routes/stickerCatalog}/catalogHandlers/catalogAuthHttp.js +0 -0
- /package/{app/modules/stickerPackModule → server/routes/stickerCatalog}/catalogHandlers/catalogPublicHttp.js +0 -0
- /package/{app/modules/stickerPackModule → server/routes/stickerCatalog}/catalogHandlers/catalogUploadHttp.js +0 -0
- /package/{app/modules/stickerPackModule → server/routes/stickerCatalog}/catalogRouter.js +0 -0
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-
|
|
56
|
+
> Atualizado em `2026-02-28T10:29:57.995Z` | cache `1800s`
|
|
57
57
|
|
|
58
58
|
| Métrica | Valor |
|
|
59
59
|
| --- | ---: |
|
|
60
|
-
| Usuários (lid_map) | 5.
|
|
60
|
+
| Usuários (lid_map) | 5.505 |
|
|
61
61
|
| Grupos | 116 |
|
|
62
|
-
| Packs |
|
|
63
|
-
| Stickers | 6.
|
|
64
|
-
| Mensagens registradas |
|
|
62
|
+
| Packs | 295 |
|
|
63
|
+
| Stickers | 6.811 |
|
|
64
|
+
| Mensagens registradas | 441.144 |
|
|
65
65
|
|
|
66
66
|
#### Tipos de mensagem mais usados (amostra: 25.000)
|
|
67
67
|
| Tipo | Total |
|
|
68
68
|
| --- | ---: |
|
|
69
|
-
| `texto` | 16.
|
|
70
|
-
| `figurinha` | 4.
|
|
71
|
-
| `reacao` | 1.
|
|
72
|
-
| `imagem` | 1.
|
|
73
|
-
| `outros` |
|
|
74
|
-
| `video` |
|
|
75
|
-
| `audio` |
|
|
69
|
+
| `texto` | 16.177 |
|
|
70
|
+
| `figurinha` | 4.807 |
|
|
71
|
+
| `reacao` | 1.502 |
|
|
72
|
+
| `imagem` | 1.300 |
|
|
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
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
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
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
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 {
|
|
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
|
|
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
|
|
220
|
-
|
|
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
|
|
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
|
|
343
|
-
if (typeof
|
|
342
|
+
// 4) Encerrar servidor HTTP
|
|
343
|
+
if (typeof stopHttpServer === 'function') {
|
|
344
344
|
try {
|
|
345
|
-
logger.info('Encerrando servidor
|
|
346
|
-
await withTimeout(
|
|
347
|
-
logger.info('Servidor
|
|
348
|
-
} catch (
|
|
349
|
-
logger.warn('Falha ao encerrar servidor
|
|
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
|
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -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 '
|
|
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 {
|
|
13
|
-
|
|
14
|
-
|
|
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 '
|
|
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 '
|
|
35
|
+
} from '../../app/modules/stickerPackModule/stickerPackItemRepository.js';
|
|
32
36
|
import {
|
|
33
37
|
listClassifiedStickerAssetsWithoutPack,
|
|
34
38
|
listStickerAssetsWithoutPack,
|
|
35
39
|
deleteStickerAssetById,
|
|
36
40
|
findStickerAssetsByIds,
|
|
37
|
-
} from '
|
|
41
|
+
} from '../../app/modules/stickerPackModule/stickerAssetRepository.js';
|
|
38
42
|
import {
|
|
39
43
|
deleteStickerAssetClassificationByAssetId,
|
|
40
44
|
findStickerClassificationByAssetId,
|
|
41
45
|
listStickerClassificationsByAssetIds,
|
|
42
|
-
} from '
|
|
46
|
+
} from '../../app/modules/stickerPackModule/stickerAssetClassificationRepository.js';
|
|
43
47
|
import {
|
|
44
48
|
decoratePackClassificationSummary,
|
|
45
49
|
decorateStickerClassification,
|
|
46
50
|
getPackClassificationSummaryByAssetIds,
|
|
47
|
-
} from '
|
|
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 '
|
|
59
|
+
} from '../../app/modules/stickerPackModule/stickerPackEngagementRepository.js';
|
|
56
60
|
import {
|
|
57
61
|
createStickerPackInteractionEvent,
|
|
58
62
|
listStickerPackInteractionStatsByPackIds,
|
|
59
63
|
listViewerRecentPackIds,
|
|
60
|
-
} from '
|
|
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 '
|
|
68
|
-
import { listStickerPackScoreSnapshotsByPackIds } from '
|
|
69
|
-
import { createCatalogApiRouter } from '
|
|
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 '
|
|
80
|
-
import { getMarketplaceDriftSnapshot } from '
|
|
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 '
|
|
87
|
-
import { convertToWebp } from '
|
|
88
|
-
import { sanitizeText } from '
|
|
89
|
-
import stickerPackService from '
|
|
90
|
-
import { STICKER_PACK_ERROR_CODES, StickerPackError } from '
|
|
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
|
+
};
|
package/server/index.js
ADDED
|
@@ -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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|