@kaikybrofc/omnizap-system 2.2.4 → 2.2.6
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/.env.example +5 -0
- package/.prettierrc +16 -0
- package/README.md +13 -13
- package/app/modules/stickerPackModule/autoPackCollectorService.js +63 -8
- package/app/modules/stickerPackModule/catalogHandlers/catalogAdminHttp.js +68 -0
- package/app/modules/stickerPackModule/catalogHandlers/catalogAuthHttp.js +34 -0
- package/app/modules/stickerPackModule/catalogHandlers/catalogPublicHttp.js +179 -0
- package/app/modules/stickerPackModule/catalogHandlers/catalogUploadHttp.js +92 -0
- package/app/modules/stickerPackModule/catalogRouter.js +79 -0
- package/app/modules/stickerPackModule/domainEventOutboxRepository.js +243 -0
- package/app/modules/stickerPackModule/domainEvents.js +61 -0
- package/app/modules/stickerPackModule/stickerAssetClassificationRepository.js +21 -0
- package/app/modules/stickerPackModule/stickerAssetRepository.js +19 -0
- package/app/modules/stickerPackModule/stickerClassificationBackgroundRuntime.js +55 -15
- package/app/modules/stickerPackModule/stickerDedicatedTaskWorkerRuntime.js +238 -0
- package/app/modules/stickerPackModule/stickerDomainEventBus.js +71 -0
- package/app/modules/stickerPackModule/stickerDomainEventConsumerRuntime.js +198 -0
- package/app/modules/stickerPackModule/stickerObjectStorageService.js +285 -0
- package/app/modules/stickerPackModule/stickerPackCatalogHttp.js +570 -536
- package/app/modules/stickerPackModule/stickerPackEngagementRepository.js +44 -0
- package/app/modules/stickerPackModule/stickerPackItemRepository.js +18 -0
- package/app/modules/stickerPackModule/stickerPackRepository.js +51 -0
- package/app/modules/stickerPackModule/stickerPackScoreSnapshotRepository.js +191 -0
- package/app/modules/stickerPackModule/stickerPackScoreSnapshotRuntime.js +301 -0
- package/app/modules/stickerPackModule/stickerStorageService.js +111 -10
- package/app/modules/stickerPackModule/stickerWorkerPipelineRuntime.js +21 -0
- package/app/modules/stickerPackModule/stickerWorkerTaskQueueRepository.js +59 -7
- package/app/observability/metrics.js +169 -0
- package/app/services/featureFlagService.js +137 -0
- package/database/index.js +5 -0
- package/database/migrations/20260228_0022_sticker_scale_indexes.sql +16 -0
- package/database/migrations/20260228_0023_sticker_pack_score_snapshot.sql +25 -0
- package/database/migrations/20260228_0024_domain_event_outbox.sql +42 -0
- package/database/migrations/20260228_0025_sticker_worker_task_idempotency_dlq.sql +23 -0
- package/database/migrations/20260228_0026_feature_flags.sql +21 -0
- package/ecosystem.prod.config.cjs +70 -9
- package/index.js +26 -0
- package/kaikybrofc-omnizap-system-2.2.6.tgz +0 -0
- package/observability/sticker-catalog-slo.md +83 -0
- package/observability/sticker-scale-hardening-rollout.md +128 -0
- package/package.json +7 -35
- 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.avif +0 -0
- package/public/assets/images/hero-banner-1280.jpg +0 -0
- package/public/assets/images/hero-banner-1280.webp +0 -0
- package/public/assets/images/hero-banner-720.avif +0 -0
- package/public/assets/images/hero-banner-720.webp +0 -0
- package/public/index.html +120 -18
- package/public/js/apps/homeApp.js +469 -353
- package/public/robots.txt +9 -0
- package/public/sitemap.xml +28 -0
- package/scripts/sticker-catalog-loadtest.mjs +208 -0
- package/scripts/sticker-worker-task.mjs +122 -0
- package/observability/mysql-exporter.cnf +0 -5
package/.env.example
CHANGED
|
@@ -434,6 +434,11 @@ WHATSAPP_LOGIN_PATH=/login/
|
|
|
434
434
|
WHATSAPP_LOGIN_LINK_SECRET=troque_por_um_segredo_forte
|
|
435
435
|
WHATSAPP_LOGIN_LINK_TTL_SECONDS=900
|
|
436
436
|
WHATSAPP_LOGIN_REQUIRE_SIGNATURE=true
|
|
437
|
+
# Canonical/cookies do catalogo web (necessario para compartilhar sessao entre www e dominio raiz)
|
|
438
|
+
SITE_CANONICAL_HOST=omnizap.shop
|
|
439
|
+
SITE_CANONICAL_SCHEME=https
|
|
440
|
+
SITE_CANONICAL_REDIRECT_ENABLED=true
|
|
441
|
+
SITE_COOKIE_DOMAIN=omnizap.shop
|
|
437
442
|
|
|
438
443
|
# Paths locais (legado/uso auxiliar)
|
|
439
444
|
STORE_PATH=./temp
|
package/.prettierrc
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"printWidth": 12000,
|
|
3
|
+
"tabWidth": 2,
|
|
4
|
+
"useTabs": false,
|
|
5
|
+
"semi": true,
|
|
6
|
+
"singleQuote": true,
|
|
7
|
+
"trailingComma": "all",
|
|
8
|
+
"bracketSpacing": true,
|
|
9
|
+
"arrowParens": "always",
|
|
10
|
+
"endOfLine": "lf",
|
|
11
|
+
"bracketSameLine": false,
|
|
12
|
+
"proseWrap": "preserve",
|
|
13
|
+
"htmlWhitespaceSensitivity": "css",
|
|
14
|
+
"quoteProps": "as-needed",
|
|
15
|
+
"embeddedLanguageFormatting": "auto"
|
|
16
|
+
}
|
package/README.md
CHANGED
|
@@ -53,27 +53,27 @@ 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-28T06:26:02.459Z` | cache `1800s`
|
|
57
57
|
|
|
58
58
|
| Métrica | Valor |
|
|
59
59
|
| --- | ---: |
|
|
60
|
-
| Usuários (lid_map) | 5.
|
|
60
|
+
| Usuários (lid_map) | 5.504 |
|
|
61
61
|
| Grupos | 116 |
|
|
62
|
-
| Packs |
|
|
63
|
-
| Stickers | 6.
|
|
64
|
-
| Mensagens registradas |
|
|
62
|
+
| Packs | 293 |
|
|
63
|
+
| Stickers | 6.775 |
|
|
64
|
+
| Mensagens registradas | 440.765 |
|
|
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` |
|
|
76
|
-
| `documento` |
|
|
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 |
|
|
76
|
+
| `documento` | 5 |
|
|
77
77
|
|
|
78
78
|
<details><summary>Comandos disponíveis (62)</summary>
|
|
79
79
|
|
|
@@ -8,6 +8,8 @@ const DEFAULT_AUTO_PACK_NAME = process.env.STICKER_PACK_AUTO_PACK_NAME || 'pack'
|
|
|
8
8
|
const AUTO_PACK_TARGET_VISIBILITY = 'unlisted';
|
|
9
9
|
const AUTO_COLLECT_ENABLED = process.env.STICKER_PACK_AUTO_COLLECT_ENABLED !== 'false';
|
|
10
10
|
const AUTO_PACK_NAME_MAX_LENGTH = 120;
|
|
11
|
+
const AUTO_PACK_DESCRIPTION_MARKER = '[auto-pack:collector]';
|
|
12
|
+
const AUTO_PACK_DESCRIPTION_TEXT = 'Coleção automática de figurinhas criadas pelo usuário.';
|
|
11
13
|
const normalizeVisibility = (value) => String(value || '').trim().toLowerCase();
|
|
12
14
|
|
|
13
15
|
/**
|
|
@@ -43,6 +45,42 @@ const normalizeAutoPackName = (value, { fallback = 'pack', maxLength = AUTO_PACK
|
|
|
43
45
|
return normalized || fallback;
|
|
44
46
|
};
|
|
45
47
|
|
|
48
|
+
const buildAutoPackDescription = () => `${AUTO_PACK_DESCRIPTION_TEXT} ${AUTO_PACK_DESCRIPTION_MARKER}`.trim();
|
|
49
|
+
|
|
50
|
+
const isThemeCurationAutoPack = (pack) => {
|
|
51
|
+
if (!pack || typeof pack !== 'object') return false;
|
|
52
|
+
const description = String(pack.description || '').toLowerCase();
|
|
53
|
+
if (description.includes('[auto-theme:') || description.includes('[auto-tag:')) return true;
|
|
54
|
+
if (String(pack.name || '').trim().toLowerCase().startsWith('[auto]')) return true;
|
|
55
|
+
return Boolean(String(pack.pack_theme_key || '').trim());
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const isAutoCollectorPack = (pack) => {
|
|
59
|
+
if (!pack || typeof pack !== 'object') return false;
|
|
60
|
+
if (isThemeCurationAutoPack(pack)) return false;
|
|
61
|
+
|
|
62
|
+
const description = String(pack.description || '').toLowerCase();
|
|
63
|
+
if (description.includes(AUTO_PACK_DESCRIPTION_MARKER)) return true;
|
|
64
|
+
if (description.includes('coleção automática de figurinhas criadas pelo usuário.')) return true;
|
|
65
|
+
|
|
66
|
+
const normalizedName = normalizeAutoPackName(pack.name, { fallback: '', maxLength: AUTO_PACK_NAME_MAX_LENGTH });
|
|
67
|
+
if (!normalizedName) return false;
|
|
68
|
+
|
|
69
|
+
const base = normalizeAutoPackName(DEFAULT_AUTO_PACK_NAME, { fallback: 'pack', maxLength: AUTO_PACK_NAME_MAX_LENGTH });
|
|
70
|
+
const matcher = new RegExp(`^${escapeRegex(base.toLowerCase())}\\d+$`, 'i');
|
|
71
|
+
const looksLikeLegacyCollector = matcher.test(normalizedName);
|
|
72
|
+
if (looksLikeLegacyCollector) return true;
|
|
73
|
+
|
|
74
|
+
return pack.is_auto_pack === true || Number(pack.is_auto_pack || 0) === 1;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const isUserManagedPackCandidate = (pack) => {
|
|
78
|
+
if (!pack || typeof pack !== 'object') return false;
|
|
79
|
+
if (isThemeCurationAutoPack(pack)) return false;
|
|
80
|
+
if (isAutoCollectorPack(pack)) return false;
|
|
81
|
+
return Number(pack.is_auto_pack || 0) !== 1;
|
|
82
|
+
};
|
|
83
|
+
|
|
46
84
|
/**
|
|
47
85
|
* Monta candidato incremental para nome de pack automático.
|
|
48
86
|
*
|
|
@@ -177,21 +215,37 @@ export function createAutoPackCollector(options = {}) {
|
|
|
177
215
|
const ensureTargetPack = async ({ ownerJid, senderName }) => {
|
|
178
216
|
const packs = await deps.stickerPackService.listPacks({ ownerJid, limit: 30 });
|
|
179
217
|
if (packs.length > 0) {
|
|
218
|
+
// Prioriza pack gerenciado pelo usuário (pack atual/manual).
|
|
219
|
+
const userManagedPacks = packs.filter((entry) => isUserManagedPackCandidate(entry));
|
|
220
|
+
if (userManagedPacks.length > 0) {
|
|
221
|
+
return {
|
|
222
|
+
pack: userManagedPacks[0],
|
|
223
|
+
packs,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const managedAutoPacks = packs.filter((entry) => isAutoCollectorPack(entry));
|
|
180
228
|
const preferredPack =
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
229
|
+
managedAutoPacks.find((entry) => normalizeVisibility(entry?.visibility) === AUTO_PACK_TARGET_VISIBILITY)
|
|
230
|
+
|| managedAutoPacks[0]
|
|
231
|
+
|| null;
|
|
232
|
+
|
|
233
|
+
if (preferredPack) {
|
|
234
|
+
const ensuredPack = await ensureAutoPackVisibility({ ownerJid, pack: preferredPack });
|
|
235
|
+
return {
|
|
236
|
+
pack: ensuredPack,
|
|
237
|
+
packs,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
187
240
|
}
|
|
188
241
|
|
|
189
242
|
const created = await deps.stickerPackService.createPack({
|
|
190
243
|
ownerJid,
|
|
191
244
|
name: makeAutoPackName([]),
|
|
192
245
|
publisher: sanitizeText(senderName, 120, { allowEmpty: true }) || 'OmniZap',
|
|
193
|
-
description:
|
|
246
|
+
description: buildAutoPackDescription(),
|
|
194
247
|
visibility: AUTO_PACK_TARGET_VISIBILITY,
|
|
248
|
+
isAutoPack: true,
|
|
195
249
|
});
|
|
196
250
|
|
|
197
251
|
return {
|
|
@@ -250,8 +304,9 @@ export function createAutoPackCollector(options = {}) {
|
|
|
250
304
|
ownerJid,
|
|
251
305
|
name: makeAutoPackName(packs),
|
|
252
306
|
publisher: sanitizeText(senderName, 120, { allowEmpty: true }) || targetPack.publisher || 'OmniZap',
|
|
253
|
-
description:
|
|
307
|
+
description: buildAutoPackDescription(),
|
|
254
308
|
visibility: AUTO_PACK_TARGET_VISIBILITY,
|
|
309
|
+
isAutoPack: true,
|
|
255
310
|
});
|
|
256
311
|
|
|
257
312
|
const updated = await deps.stickerPackService.addStickerToPack({
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export const handleCatalogAdminRoutes = async ({
|
|
2
|
+
req,
|
|
3
|
+
res,
|
|
4
|
+
url,
|
|
5
|
+
segments,
|
|
6
|
+
handlers,
|
|
7
|
+
sendJson,
|
|
8
|
+
}) => {
|
|
9
|
+
if (segments[0] !== 'admin') return false;
|
|
10
|
+
|
|
11
|
+
if (segments.length === 2 && segments[1] === 'overview') {
|
|
12
|
+
await handlers.handleAdminOverviewRequest(req, res);
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (segments.length === 2 && segments[1] === 'users') {
|
|
17
|
+
await handlers.handleAdminUsersRequest(req, res, url);
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (segments.length === 2 && segments[1] === 'moderators') {
|
|
22
|
+
await handlers.handleAdminModeratorsRequest(req, res);
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (segments.length === 3 && segments[1] === 'moderators') {
|
|
27
|
+
await handlers.handleAdminModeratorDeleteRequest(req, res, segments[2]);
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (segments.length === 2 && segments[1] === 'packs') {
|
|
32
|
+
await handlers.handleAdminPacksRequest(req, res, url);
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (segments.length === 3 && segments[1] === 'packs') {
|
|
37
|
+
await handlers.handleAdminPackDetailsRequest(req, res, segments[2]);
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (segments.length === 4 && segments[1] === 'packs' && segments[3] === 'delete') {
|
|
42
|
+
await handlers.handleAdminPackDeleteRequest(req, res, segments[2]);
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (segments.length === 6 && segments[1] === 'packs' && segments[3] === 'stickers' && segments[5] === 'delete') {
|
|
47
|
+
await handlers.handleAdminPackStickerDeleteRequest(req, res, segments[2], segments[4]);
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (segments.length === 4 && segments[1] === 'stickers' && segments[3] === 'delete') {
|
|
52
|
+
await handlers.handleAdminGlobalStickerDeleteRequest(req, res, segments[2]);
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (segments.length === 2 && segments[1] === 'bans') {
|
|
57
|
+
await handlers.handleAdminBansRequest(req, res);
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (segments.length === 4 && segments[1] === 'bans' && segments[3] === 'revoke') {
|
|
62
|
+
await handlers.handleAdminBanRevokeRequest(req, res, segments[2]);
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
sendJson(req, res, 404, { error: 'Rota admin nao encontrada.' });
|
|
67
|
+
return true;
|
|
68
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const METHOD_NOT_ALLOWED_BODY = { error: 'Metodo nao permitido.' };
|
|
2
|
+
|
|
3
|
+
const isReadMethod = (method) => method === 'GET' || method === 'HEAD';
|
|
4
|
+
|
|
5
|
+
export const handleCatalogAuthRoutes = async ({
|
|
6
|
+
req,
|
|
7
|
+
res,
|
|
8
|
+
pathname,
|
|
9
|
+
url,
|
|
10
|
+
apiBasePath,
|
|
11
|
+
handlers,
|
|
12
|
+
sendJson,
|
|
13
|
+
}) => {
|
|
14
|
+
if (pathname === `${apiBasePath}/auth/google/session`) {
|
|
15
|
+
await handlers.handleGoogleAuthSessionRequest(req, res);
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (pathname === `${apiBasePath}/me`) {
|
|
20
|
+
if (!isReadMethod(req.method || '')) {
|
|
21
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
await handlers.handleMyProfileRequest(req, res, url);
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (pathname === `${apiBasePath}/admin/session`) {
|
|
29
|
+
await handlers.handleAdminPanelSessionRequest(req, res);
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return false;
|
|
34
|
+
};
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
const METHOD_NOT_ALLOWED_BODY = { error: 'Metodo nao permitido.' };
|
|
2
|
+
|
|
3
|
+
const isReadMethod = (method) => method === 'GET' || method === 'HEAD';
|
|
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
|
+
}) => {
|
|
16
|
+
if (pathname === apiBasePath) {
|
|
17
|
+
if (!isReadMethod(req.method || '')) {
|
|
18
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
await handlers.handleListRequest(req, res, url);
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (pathname === `${apiBasePath}/intents`) {
|
|
26
|
+
if (!isReadMethod(req.method || '')) {
|
|
27
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
await handlers.handleIntentCollectionsRequest(req, res, url);
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (pathname === `${apiBasePath}/creators`) {
|
|
35
|
+
if (!isReadMethod(req.method || '')) {
|
|
36
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
await handlers.handleCreatorRankingRequest(req, res, url);
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (pathname === `${apiBasePath}/recommendations`) {
|
|
44
|
+
if (!isReadMethod(req.method || '')) {
|
|
45
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
await handlers.handleRecommendationsRequest(req, res, url);
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (pathname === `${apiBasePath}/stats`) {
|
|
53
|
+
if (!isReadMethod(req.method || '')) {
|
|
54
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
await handlers.handleMarketplaceStatsRequest(req, res, url);
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (pathname === `${apiBasePath}/create-config`) {
|
|
62
|
+
if (!isReadMethod(req.method || '')) {
|
|
63
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
await handlers.handleCreatePackConfigRequest(req, res);
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (pathname === orphanApiPath) {
|
|
71
|
+
if (!isReadMethod(req.method || '')) {
|
|
72
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
await handlers.handleOrphanStickerListRequest(req, res, url);
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (segments.length === 1 && segments[0] === 'data-files') {
|
|
80
|
+
if (!isReadMethod(req.method || '')) {
|
|
81
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
await handlers.handleDataFileListRequest(req, res, url);
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (segments.length === 1 && segments[0] === 'system-summary') {
|
|
89
|
+
if (!isReadMethod(req.method || '')) {
|
|
90
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
await handlers.handleSystemSummaryRequest(req, res);
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (segments.length === 1 && segments[0] === 'project-summary') {
|
|
98
|
+
if (!isReadMethod(req.method || '')) {
|
|
99
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
await handlers.handleGitHubProjectSummaryRequest(req, res);
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (segments.length === 1 && segments[0] === 'global-ranking-summary') {
|
|
107
|
+
if (!isReadMethod(req.method || '')) {
|
|
108
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
await handlers.handleGlobalRankingSummaryRequest(req, res);
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (segments.length === 1 && segments[0] === 'readme-summary') {
|
|
116
|
+
if (!isReadMethod(req.method || '')) {
|
|
117
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
await handlers.handleReadmeSummaryRequest(req, res);
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (segments.length === 1 && segments[0] === 'readme-markdown') {
|
|
125
|
+
if (!isReadMethod(req.method || '')) {
|
|
126
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
await handlers.handleReadmeMarkdownRequest(req, res);
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (segments.length === 1 && segments[0] === 'support') {
|
|
134
|
+
if (!isReadMethod(req.method || '')) {
|
|
135
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
await handlers.handleSupportInfoRequest(req, res);
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (segments.length === 1 && segments[0] === 'bot-contact') {
|
|
143
|
+
if (!isReadMethod(req.method || '')) {
|
|
144
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
await handlers.handleBotContactInfoRequest(req, res);
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (segments.length === 1) {
|
|
152
|
+
if (!isReadMethod(req.method || '')) {
|
|
153
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
await handlers.handleDetailsRequest(req, res, segments[0], url);
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (segments.length === 2 && ['open', 'like', 'dislike'].includes(segments[1])) {
|
|
161
|
+
if (req.method !== 'POST') {
|
|
162
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
await handlers.handlePackInteractionRequest(req, res, segments[0], segments[1], url);
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (segments.length === 3 && segments[1] === 'stickers') {
|
|
170
|
+
if (!isReadMethod(req.method || '')) {
|
|
171
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
await handlers.handleAssetRequest(req, res, segments[0], segments[2]);
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return false;
|
|
179
|
+
};
|
|
@@ -0,0 +1,92 @@
|
|
|
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 ({
|
|
6
|
+
req,
|
|
7
|
+
res,
|
|
8
|
+
pathname,
|
|
9
|
+
url,
|
|
10
|
+
segments,
|
|
11
|
+
apiBasePath,
|
|
12
|
+
handlers,
|
|
13
|
+
sendJson,
|
|
14
|
+
}) => {
|
|
15
|
+
if (pathname === `${apiBasePath}/create`) {
|
|
16
|
+
if (req.method !== 'POST') {
|
|
17
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
await handlers.handleCreatePackRequest(req, res);
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (segments.length === 2 && segments[1] === 'manage') {
|
|
25
|
+
await handlers.handleManagedPackRequest(req, res, segments[0]);
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (segments.length === 3 && segments[1] === 'manage' && segments[2] === 'clone') {
|
|
30
|
+
await handlers.handleManagedPackCloneRequest(req, res, segments[0]);
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (segments.length === 3 && segments[1] === 'manage' && segments[2] === 'cover') {
|
|
35
|
+
await handlers.handleManagedPackCoverRequest(req, res, segments[0]);
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (segments.length === 3 && segments[1] === 'manage' && segments[2] === 'reorder') {
|
|
40
|
+
await handlers.handleManagedPackReorderRequest(req, res, segments[0]);
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (segments.length === 3 && segments[1] === 'manage' && segments[2] === 'analytics') {
|
|
45
|
+
await handlers.handleManagedPackAnalyticsRequest(req, res, segments[0]);
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (segments.length === 3 && segments[1] === 'manage' && segments[2] === 'stickers') {
|
|
50
|
+
await handlers.handleManagedPackStickerCreateRequest(req, res, segments[0]);
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (segments.length === 4 && segments[1] === 'manage' && segments[2] === 'stickers') {
|
|
55
|
+
await handlers.handleManagedPackStickerDeleteRequest(req, res, segments[0], segments[3]);
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (segments.length === 5 && segments[1] === 'manage' && segments[2] === 'stickers' && segments[4] === 'replace') {
|
|
60
|
+
await handlers.handleManagedPackStickerReplaceRequest(req, res, segments[0], segments[3]);
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (segments.length === 2 && segments[1] === 'publish-state') {
|
|
65
|
+
if (!isPublishStateMethod(req.method || '')) {
|
|
66
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
await handlers.handlePackPublishStateRequest(req, res, segments[0], url);
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (segments.length === 2 && segments[1] === 'finalize') {
|
|
74
|
+
if (req.method !== 'POST') {
|
|
75
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
await handlers.handleFinalizePackRequest(req, res, segments[0]);
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (segments.length === 2 && segments[1] === 'stickers-upload') {
|
|
83
|
+
if (req.method !== 'POST') {
|
|
84
|
+
sendJson(req, res, 405, METHOD_NOT_ALLOWED_BODY);
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
await handlers.handleUploadStickerToPackRequest(req, res, segments[0]);
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return false;
|
|
92
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
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.split('/').filter(Boolean).map((segment) => {
|
|
8
|
+
try {
|
|
9
|
+
return decodeURIComponent(segment);
|
|
10
|
+
} catch {
|
|
11
|
+
return segment;
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export const createCatalogApiRouter = ({
|
|
16
|
+
apiBasePath,
|
|
17
|
+
orphanApiPath,
|
|
18
|
+
handlers,
|
|
19
|
+
sendJson,
|
|
20
|
+
}) => {
|
|
21
|
+
if (!apiBasePath || typeof handlers !== 'object' || typeof sendJson !== 'function') {
|
|
22
|
+
throw new Error('catalog_api_router_config_invalid');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return async ({ req, res, pathname, url }) => {
|
|
26
|
+
const handledAuth = await handleCatalogAuthRoutes({
|
|
27
|
+
req,
|
|
28
|
+
res,
|
|
29
|
+
pathname,
|
|
30
|
+
url,
|
|
31
|
+
apiBasePath,
|
|
32
|
+
handlers,
|
|
33
|
+
sendJson,
|
|
34
|
+
});
|
|
35
|
+
if (handledAuth) return true;
|
|
36
|
+
if (!pathname.startsWith(apiBasePath)) return false;
|
|
37
|
+
|
|
38
|
+
const suffix = pathname.slice(apiBasePath.length).replace(/^\/+/, '');
|
|
39
|
+
const segments = decodePathSegments(suffix);
|
|
40
|
+
|
|
41
|
+
const handledAdmin = await handleCatalogAdminRoutes({
|
|
42
|
+
req,
|
|
43
|
+
res,
|
|
44
|
+
url,
|
|
45
|
+
segments,
|
|
46
|
+
handlers,
|
|
47
|
+
sendJson,
|
|
48
|
+
});
|
|
49
|
+
if (handledAdmin) return true;
|
|
50
|
+
|
|
51
|
+
const handledUpload = await handleCatalogUploadRoutes({
|
|
52
|
+
req,
|
|
53
|
+
res,
|
|
54
|
+
pathname,
|
|
55
|
+
url,
|
|
56
|
+
segments,
|
|
57
|
+
apiBasePath,
|
|
58
|
+
handlers,
|
|
59
|
+
sendJson,
|
|
60
|
+
});
|
|
61
|
+
if (handledUpload) return true;
|
|
62
|
+
|
|
63
|
+
const handledPublic = await handleCatalogPublicRoutes({
|
|
64
|
+
req,
|
|
65
|
+
res,
|
|
66
|
+
pathname,
|
|
67
|
+
url,
|
|
68
|
+
segments,
|
|
69
|
+
apiBasePath,
|
|
70
|
+
orphanApiPath,
|
|
71
|
+
handlers,
|
|
72
|
+
sendJson,
|
|
73
|
+
});
|
|
74
|
+
if (handledPublic) return true;
|
|
75
|
+
|
|
76
|
+
sendJson(req, res, 404, { error: 'Rota de sticker pack nao encontrada.' });
|
|
77
|
+
return true;
|
|
78
|
+
};
|
|
79
|
+
};
|