@omnizap-system/omnizap 2.6.1 → 2.6.3
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 +78 -9
- package/.github/workflows/ci.yml +3 -3
- package/.github/workflows/security-runner-hardening.yml +1 -1
- package/.github/workflows/security-zap-full-scan.yml +1 -0
- package/app/config/index.js +6 -0
- package/app/configParts/adminIdentity.js +36 -7
- package/app/configParts/baileysConfig.js +343 -56
- package/app/configParts/groupUtils.js +226 -0
- package/app/configParts/loggerConfig.js +185 -0
- package/app/configParts/messagePersistenceService.js +307 -5
- package/app/configParts/sessionConfig.js +242 -0
- package/app/connection/baileysCompatibility.test.js +10 -1
- package/app/connection/baileysDbAuthState.js +205 -9
- package/app/connection/baileysLibsignalPatch.js +210 -0
- package/app/connection/groupOwnerWriteStateResolver.js +141 -0
- package/app/connection/socketController.js +694 -123
- package/app/connection/socketController.multiSession.test.js +128 -0
- package/app/controllers/messageController.js +1 -1
- package/app/controllers/messagePipeline/commandMiddleware.js +12 -10
- package/app/controllers/messagePipeline/conversationMiddleware.js +2 -1
- package/app/controllers/messagePipeline/messagePipelineMiddlewares.test.js +104 -0
- package/app/controllers/messagePipeline/preProcessingMiddlewares.js +96 -4
- package/app/controllers/messageProcessingPipeline.js +90 -9
- package/app/controllers/messageProcessingPipeline.test.js +202 -0
- package/app/modules/adminModule/AGENT.md +1 -1
- package/app/modules/adminModule/commandConfig.json +3318 -1347
- package/app/modules/adminModule/groupCommandHandlers.js +856 -14
- package/app/modules/adminModule/groupCommandHandlers.test.js +375 -9
- package/app/modules/adminModule/groupWarningRepository.js +152 -0
- package/app/modules/aiModule/AGENT.md +47 -30
- package/app/modules/aiModule/aiConfigRuntime.js +1 -0
- package/app/modules/aiModule/catCommand.js +132 -25
- package/app/modules/aiModule/commandConfig.json +114 -28
- package/app/modules/analyticsModule/messageAnalysisEventRepository.js +54 -6
- package/app/modules/gameModule/AGENT.md +1 -1
- package/app/modules/gameModule/commandConfig.json +29 -0
- package/app/modules/menuModule/AGENT.md +1 -1
- package/app/modules/menuModule/commandConfig.json +45 -10
- package/app/modules/menuModule/menuCatalogService.js +190 -0
- package/app/modules/menuModule/menuCommandUsageRepository.js +109 -0
- package/app/modules/menuModule/menuDynamicService.js +511 -0
- package/app/modules/menuModule/menuDynamicService.test.js +141 -0
- package/app/modules/menuModule/menus.js +36 -5
- package/app/modules/playModule/AGENT.md +10 -5
- package/app/modules/playModule/commandConfig.json +74 -16
- package/app/modules/playModule/playCommandConstants.js +13 -7
- package/app/modules/playModule/playCommandCore.js +4 -6
- package/app/modules/playModule/{playCommandYtDlpClient.js → playCommandMediaClient.js} +684 -332
- package/app/modules/playModule/playConfigRuntime.js +5 -6
- package/app/modules/playModule/playModuleCriticalFlows.test.js +44 -59
- package/app/modules/quoteModule/AGENT.md +1 -1
- package/app/modules/quoteModule/commandConfig.json +29 -0
- package/app/modules/rpgPokemonModule/AGENT.md +1 -1
- package/app/modules/rpgPokemonModule/commandConfig.json +29 -0
- package/app/modules/statsModule/AGENT.md +1 -1
- package/app/modules/statsModule/commandConfig.json +58 -0
- package/app/modules/stickerModule/AGENT.md +1 -1
- package/app/modules/stickerModule/commandConfig.json +145 -0
- package/app/modules/stickerPackModule/AGENT.md +1 -1
- package/app/modules/stickerPackModule/autoPackCollectorService.js +5 -1
- package/app/modules/stickerPackModule/commandConfig.json +29 -0
- package/app/modules/stickerPackModule/stickerAutoPackByTagsRuntime.js +1 -1
- package/app/modules/stickerPackModule/stickerPackCommandHandlers.js +78 -57
- package/app/modules/stickerPackModule/stickerPackService.js +13 -6
- package/app/modules/systemMetricsModule/AGENT.md +1 -1
- package/app/modules/systemMetricsModule/commandConfig.json +29 -0
- package/app/modules/tiktokModule/AGENT.md +1 -1
- package/app/modules/tiktokModule/commandConfig.json +29 -0
- package/app/modules/userModule/AGENT.md +1 -1
- package/app/modules/userModule/commandConfig.json +29 -0
- package/app/modules/waifuPicsModule/AGENT.md +57 -27
- package/app/modules/waifuPicsModule/commandConfig.json +87 -0
- package/app/observability/metrics.js +136 -0
- package/app/services/ai/commandConfigEnrichmentService.js +229 -47
- package/app/services/ai/geminiService.js +131 -7
- package/app/services/ai/geminiService.test.js +59 -2
- package/app/services/ai/moduleAiHelpCoreService.js +33 -4
- package/app/services/group/groupMetadataService.js +24 -1
- package/app/services/infra/dbWriteQueue.js +51 -21
- package/app/services/messaging/newsBroadcastService.js +843 -27
- package/app/services/multiSession/assignmentBalancerService.js +452 -0
- package/app/services/multiSession/groupOwnershipRepository.js +346 -0
- package/app/services/multiSession/groupOwnershipService.js +809 -0
- package/app/services/multiSession/groupOwnershipService.test.js +317 -0
- package/app/services/multiSession/sessionRegistryService.js +239 -0
- package/app/store/aiPromptStore.js +36 -19
- package/app/store/groupConfigStore.js +41 -5
- package/app/store/premiumUserStore.js +21 -7
- package/app/utils/antiLink/antiLinkModule.js +391 -25
- package/app/workers/aiHelperContinuousLearningWorker.js +512 -0
- package/database/index.js +6 -0
- package/database/migrations/20260307_d0_hardening_down.sql +1 -1
- package/database/migrations/20260314_d7_canonical_sender_down.sql +1 -1
- package/database/migrations/20260406_d30_security_analytics_down.sql +1 -1
- package/database/migrations/20260411_d35_group_community_metadata_down.sql +59 -0
- package/database/migrations/20260411_d35_group_community_metadata_up.sql +62 -0
- package/database/migrations/20260412_d36_system_config_tables_down.sql +32 -0
- package/database/migrations/20260412_d36_system_config_tables_up.sql +66 -0
- package/database/migrations/20260413_d37_group_user_warnings_down.sql +11 -0
- package/database/migrations/20260413_d37_group_user_warnings_up.sql +24 -0
- package/database/migrations/20260414_d38_multi_session_foundation_down.sql +72 -0
- package/database/migrations/20260414_d38_multi_session_foundation_up.sql +125 -0
- package/database/migrations/20260414_d39_multi_session_cutover_down.sql +103 -0
- package/database/migrations/20260414_d39_multi_session_cutover_up.sql +83 -0
- package/database/schema.sql +102 -1
- package/docker-compose.yml +4 -1
- package/docs/compliance/acceptable-use-policy-2026-03-07.md +1 -1
- package/docs/compliance/privacy-policy-2026-03-07.md +2 -2
- package/docs/security/dsar-lgpd-runbook-2026-03-07.md +1 -1
- package/docs/security/network-hardening-runbook-2026-03-07.md +53 -0
- package/docs/security/omnizap-static-security-headers.conf +25 -0
- package/ecosystem.prod.config.cjs +31 -11
- package/index.js +52 -18
- package/observability/alert-rules.yml +20 -0
- package/observability/grafana/dashboards/omnizap-system-admin.json +229 -0
- package/observability/mysql-setup.sql +4 -4
- package/observability/system-admin-observability.md +26 -0
- package/package.json +14 -6
- package/public/comandos/commands-catalog.json +2253 -78
- package/public/css/payments-react.css +478 -0
- package/public/js/apps/commandsReactApp.js +267 -87
- package/public/js/apps/createPackApp.js +3 -3
- package/public/js/apps/homeReactApp.js +2 -2
- package/public/js/apps/paymentsCancelReactApp.js +45 -0
- package/public/js/apps/paymentsReactApp.js +399 -0
- package/public/js/apps/paymentsSuccessReactApp.js +148 -0
- package/public/js/apps/stickersApp.js +255 -103
- package/public/js/apps/termsReactApp.js +57 -8
- package/public/js/apps/userPasswordResetReactApp.js +406 -0
- package/public/js/apps/userReactApp.js +96 -47
- package/public/js/apps/userSystemAdmReactApp.js +1506 -0
- package/public/pages/pagamentos-cancelado.html +21 -0
- package/public/pages/pagamentos-sucesso.html +21 -0
- package/public/pages/pagamentos.html +30 -0
- package/public/pages/politica-de-privacidade.html +1 -1
- package/public/pages/stickers.html +5 -5
- package/public/pages/termos-de-uso-texto-integral.html +1 -1
- package/public/pages/termos-de-uso.html +1 -1
- package/public/pages/user-password-reset.html +3 -4
- package/public/pages/user-systemadm.html +8 -462
- package/public/pages/user.html +1 -1
- package/scripts/clear-whatsapp-session.sh +123 -0
- package/scripts/core-ai-mode.mjs +163 -0
- package/scripts/deploy.sh +13 -0
- package/scripts/enrich-command-config-ux-openai.mjs +492 -0
- package/scripts/generate-commands-catalog.mjs +155 -0
- package/scripts/new-whatsapp-session.sh +564 -0
- package/scripts/security-web-surface-check.mjs +218 -0
- package/server/controllers/admin/adminPanelHandlers.js +253 -3
- package/server/controllers/admin/systemAdminController.js +254 -0
- package/server/controllers/payments/paymentsController.js +731 -0
- package/server/controllers/sticker/stickerCatalogController.js +9 -23
- package/server/controllers/system/contactController.js +9 -17
- package/server/controllers/system/stickerCatalogSystemContext.js +27 -6
- package/server/controllers/system/systemController.js +228 -1
- package/server/controllers/userController.js +6 -0
- package/server/email/emailAutomationRuntime.js +36 -1
- package/server/email/emailAutomationService.js +42 -1
- package/server/email/emailTemplateService.js +140 -33
- package/server/http/httpRequestUtils.js +18 -14
- package/server/http/httpServer.js +8 -4
- package/server/middleware/securityHeaders.js +35 -3
- package/server/routes/admin/systemAdminRouter.js +6 -0
- package/server/routes/indexRouter.js +50 -6
- package/server/routes/observability/grafanaProxyRouter.js +254 -0
- package/server/routes/payments/paymentsRouter.js +47 -0
- package/server/routes/static/staticPageRouter.js +30 -1
- package/server/utils/publicContact.js +31 -0
- package/utils/whatsapp/contactEnv.js +39 -0
- package/vite.config.mjs +5 -1
- package/app/modules/playModule/local/installYtDlp.js +0 -25
- package/app/modules/playModule/local/ytDlpInstaller.js +0 -28
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import { timingSafeEqual } from 'node:crypto';
|
|
3
4
|
import { URL } from 'node:url';
|
|
4
5
|
|
|
5
6
|
import logger from '#logger';
|
|
7
|
+
import { forceSystemAdminGroupFailover, listSystemAdminAssignmentHistory, listSystemAdminAssignments, listSystemAdminSessions, setSystemAdminGroupPin, triggerSystemAdminManualRebalance } from '../system/systemController.js';
|
|
6
8
|
|
|
7
9
|
const parseEnvBool = (value, fallback) => {
|
|
8
10
|
if (value === undefined || value === null || value === '') return fallback;
|
|
@@ -25,6 +27,8 @@ const SYSTEM_ADMIN_API_BASE_PATH = normalizeBasePath(process.env.SYSTEM_ADMIN_AP
|
|
|
25
27
|
const SYSTEM_ADMIN_API_SESSION_PATH = `${SYSTEM_ADMIN_API_BASE_PATH}/session`;
|
|
26
28
|
const LEGACY_SYSTEM_ADMIN_API_BASE_PATH = `${LEGACY_STICKER_API_BASE_PATH}/admin`;
|
|
27
29
|
const LEGACY_SYSTEM_ADMIN_API_SESSION_PATH = `${LEGACY_SYSTEM_ADMIN_API_BASE_PATH}/session`;
|
|
30
|
+
const SYSTEM_ADMIN_MULTI_SESSION_API_PATH = `${SYSTEM_ADMIN_API_BASE_PATH}/multi-session`;
|
|
31
|
+
const LEGACY_SYSTEM_ADMIN_MULTI_SESSION_API_PATH = `${LEGACY_SYSTEM_ADMIN_API_BASE_PATH}/multi-session`;
|
|
28
32
|
const STICKER_LOGIN_WEB_PATH = normalizeBasePath(process.env.STICKER_LOGIN_WEB_PATH, '/login');
|
|
29
33
|
const STICKER_WEB_PATH = normalizeBasePath(process.env.STICKER_WEB_PATH, '/stickers');
|
|
30
34
|
const STICKER_ADMIN_WEB_PATH = `${STICKER_WEB_PATH}/admin`;
|
|
@@ -37,6 +41,7 @@ const SITE_ORIGIN = String(process.env.SITE_ORIGIN || 'https://omnizap.shop')
|
|
|
37
41
|
|
|
38
42
|
const USER_SYSTEMADM_TEMPLATE_PATH = path.join(process.cwd(), 'public', 'pages', 'user-systemadm.html');
|
|
39
43
|
const LEGACY_STICKER_ADMIN_TEMPLATE_PATH = path.join(process.cwd(), 'public', 'pages', 'stickers-admin.html');
|
|
44
|
+
const SYSTEM_ADMIN_OPS_TOKEN = String(process.env.SYSTEM_ADMIN_OPS_TOKEN || process.env.USER_INTERNAL_API_TOKEN || process.env.ADMIN_TOKEN || process.env.ADMIN_API_TOKEN || '').trim();
|
|
40
45
|
|
|
41
46
|
let stickerCatalogControllerPromise = null;
|
|
42
47
|
const loadStickerCatalogController = async () => {
|
|
@@ -108,6 +113,104 @@ const mapAdminApiPathToLegacy = (pathname) => {
|
|
|
108
113
|
return null;
|
|
109
114
|
};
|
|
110
115
|
|
|
116
|
+
const mapMultiSessionApiPath = (pathname) => {
|
|
117
|
+
if (hasPathPrefix(pathname, SYSTEM_ADMIN_MULTI_SESSION_API_PATH)) {
|
|
118
|
+
const suffix = pathname.slice(SYSTEM_ADMIN_MULTI_SESSION_API_PATH.length);
|
|
119
|
+
return suffix || '/';
|
|
120
|
+
}
|
|
121
|
+
if (hasPathPrefix(pathname, LEGACY_SYSTEM_ADMIN_MULTI_SESSION_API_PATH)) {
|
|
122
|
+
const suffix = pathname.slice(LEGACY_SYSTEM_ADMIN_MULTI_SESSION_API_PATH.length);
|
|
123
|
+
return suffix || '/';
|
|
124
|
+
}
|
|
125
|
+
return null;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const constantTimeStringEqual = (left, right) => {
|
|
129
|
+
const leftBuffer = Buffer.from(String(left || ''), 'utf8');
|
|
130
|
+
const rightBuffer = Buffer.from(String(right || ''), 'utf8');
|
|
131
|
+
if (!leftBuffer.length || leftBuffer.length !== rightBuffer.length) return false;
|
|
132
|
+
try {
|
|
133
|
+
return timingSafeEqual(leftBuffer, rightBuffer);
|
|
134
|
+
} catch {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const extractBearerToken = (req) => {
|
|
140
|
+
const authHeader = String(req?.headers?.authorization || '').trim();
|
|
141
|
+
if (!authHeader.toLowerCase().startsWith('bearer ')) return '';
|
|
142
|
+
return authHeader.slice(7).trim();
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const resolveOpsTokenFromRequest = (req) => String(req?.headers?.['x-system-admin-token'] || req?.headers?.['x-internal-api-token'] || req?.headers?.['x-admin-token'] || '').trim() || extractBearerToken(req);
|
|
146
|
+
|
|
147
|
+
const hasValidOpsToken = (req) => {
|
|
148
|
+
if (!SYSTEM_ADMIN_OPS_TOKEN) return true;
|
|
149
|
+
const requestToken = resolveOpsTokenFromRequest(req);
|
|
150
|
+
if (!requestToken) return false;
|
|
151
|
+
return constantTimeStringEqual(requestToken, SYSTEM_ADMIN_OPS_TOKEN);
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const readJsonBody = async (req, { maxBytes = 64 * 1024 } = {}) =>
|
|
155
|
+
new Promise((resolve, reject) => {
|
|
156
|
+
const chunks = [];
|
|
157
|
+
let total = 0;
|
|
158
|
+
|
|
159
|
+
req.on('data', (chunk) => {
|
|
160
|
+
total += chunk.length;
|
|
161
|
+
if (total > maxBytes) {
|
|
162
|
+
const error = new Error('Payload excedeu limite permitido.');
|
|
163
|
+
error.statusCode = 413;
|
|
164
|
+
reject(error);
|
|
165
|
+
req.destroy();
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
chunks.push(chunk);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
req.on('end', () => {
|
|
172
|
+
const raw = Buffer.concat(chunks).toString('utf8').trim();
|
|
173
|
+
if (!raw) {
|
|
174
|
+
resolve({});
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
resolve(JSON.parse(raw));
|
|
180
|
+
} catch {
|
|
181
|
+
const error = new Error('JSON invalido.');
|
|
182
|
+
error.statusCode = 400;
|
|
183
|
+
reject(error);
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
req.on('error', (error) => reject(error));
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
const parseBool = (value, fallback = false) => {
|
|
191
|
+
if (value === undefined || value === null || value === '') return fallback;
|
|
192
|
+
const normalized = String(value).trim().toLowerCase();
|
|
193
|
+
if (['1', 'true', 'yes', 'y', 'on'].includes(normalized)) return true;
|
|
194
|
+
if (['0', 'false', 'no', 'n', 'off'].includes(normalized)) return false;
|
|
195
|
+
return fallback;
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const parsePositiveInt = (value, fallback = 200, min = 1, max = 5000) => {
|
|
199
|
+
const parsed = Number.parseInt(String(value ?? ''), 10);
|
|
200
|
+
if (!Number.isFinite(parsed)) return fallback;
|
|
201
|
+
return Math.max(min, Math.min(max, parsed));
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
const decodePathSegment = (value) => {
|
|
205
|
+
const raw = String(value || '').trim();
|
|
206
|
+
if (!raw) return '';
|
|
207
|
+
try {
|
|
208
|
+
return decodeURIComponent(raw);
|
|
209
|
+
} catch {
|
|
210
|
+
return raw;
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
|
|
111
214
|
const renderUserSystemAdminHtml = async () => {
|
|
112
215
|
const template = await fs.readFile(USER_SYSTEMADM_TEMPLATE_PATH, 'utf8');
|
|
113
216
|
const dataAttributes = {
|
|
@@ -124,13 +227,145 @@ const renderUserSystemAdminHtml = async () => {
|
|
|
124
227
|
return html;
|
|
125
228
|
};
|
|
126
229
|
|
|
230
|
+
const requireSystemAdminOpsAccess = (req, res) => {
|
|
231
|
+
if (hasValidOpsToken(req)) return true;
|
|
232
|
+
sendJson(req, res, 401, { error: 'Nao autorizado para operacoes de system admin.' });
|
|
233
|
+
return false;
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const normalizeMultiSessionSubPath = (value) => {
|
|
237
|
+
const raw = String(value || '/').trim();
|
|
238
|
+
if (!raw || raw === '/') return '/';
|
|
239
|
+
return `/${raw.replace(/^\/+/g, '').replace(/\/+$/g, '')}`;
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
const handleMultiSessionOpsRequest = async (req, res, { pathname, url }) => {
|
|
243
|
+
if (!requireSystemAdminOpsAccess(req, res)) return true;
|
|
244
|
+
|
|
245
|
+
const subPath = normalizeMultiSessionSubPath(pathname);
|
|
246
|
+
const requestUrl = (() => {
|
|
247
|
+
try {
|
|
248
|
+
return new URL(String(url?.href || req.url || '/'), SITE_ORIGIN);
|
|
249
|
+
} catch {
|
|
250
|
+
return new URL(SITE_ORIGIN);
|
|
251
|
+
}
|
|
252
|
+
})();
|
|
253
|
+
|
|
254
|
+
if (subPath === '/sessions') {
|
|
255
|
+
if (!['GET', 'HEAD'].includes(req.method || '')) {
|
|
256
|
+
sendJson(req, res, 405, { error: 'Metodo nao permitido.' });
|
|
257
|
+
return true;
|
|
258
|
+
}
|
|
259
|
+
const payload = await listSystemAdminSessions({
|
|
260
|
+
status: requestUrl.searchParams.get('status'),
|
|
261
|
+
limit: parsePositiveInt(requestUrl.searchParams.get('limit'), 200, 1, 5000),
|
|
262
|
+
});
|
|
263
|
+
sendJson(req, res, 200, payload);
|
|
264
|
+
return true;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (subPath === '/assignments') {
|
|
268
|
+
if (!['GET', 'HEAD'].includes(req.method || '')) {
|
|
269
|
+
sendJson(req, res, 405, { error: 'Metodo nao permitido.' });
|
|
270
|
+
return true;
|
|
271
|
+
}
|
|
272
|
+
const payload = await listSystemAdminAssignments({
|
|
273
|
+
groupJid: requestUrl.searchParams.get('group_jid'),
|
|
274
|
+
ownerSessionId: requestUrl.searchParams.get('owner_session_id'),
|
|
275
|
+
includeExpired: parseBool(requestUrl.searchParams.get('include_expired'), false),
|
|
276
|
+
limit: parsePositiveInt(requestUrl.searchParams.get('limit'), 200, 1, 5000),
|
|
277
|
+
});
|
|
278
|
+
sendJson(req, res, 200, payload);
|
|
279
|
+
return true;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (subPath === '/history') {
|
|
283
|
+
if (!['GET', 'HEAD'].includes(req.method || '')) {
|
|
284
|
+
sendJson(req, res, 405, { error: 'Metodo nao permitido.' });
|
|
285
|
+
return true;
|
|
286
|
+
}
|
|
287
|
+
const payload = await listSystemAdminAssignmentHistory({
|
|
288
|
+
groupJid: requestUrl.searchParams.get('group_jid'),
|
|
289
|
+
limit: parsePositiveInt(requestUrl.searchParams.get('limit'), 100, 1, 5000),
|
|
290
|
+
});
|
|
291
|
+
sendJson(req, res, 200, payload);
|
|
292
|
+
return true;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (subPath === '/rebalance') {
|
|
296
|
+
if (!['POST'].includes(req.method || '')) {
|
|
297
|
+
sendJson(req, res, 405, { error: 'Metodo nao permitido.' });
|
|
298
|
+
return true;
|
|
299
|
+
}
|
|
300
|
+
const payload = await triggerSystemAdminManualRebalance();
|
|
301
|
+
sendJson(req, res, 200, payload);
|
|
302
|
+
return true;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const groupActionMatch = subPath.match(/^\/groups\/([^/]+)\/(pin|unpin|failover)$/i);
|
|
306
|
+
if (groupActionMatch) {
|
|
307
|
+
if (!['POST'].includes(req.method || '')) {
|
|
308
|
+
sendJson(req, res, 405, { error: 'Metodo nao permitido.' });
|
|
309
|
+
return true;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const groupJid = decodePathSegment(groupActionMatch[1]);
|
|
313
|
+
const action = String(groupActionMatch[2] || '').toLowerCase();
|
|
314
|
+
const body = await readJsonBody(req).catch((error) => {
|
|
315
|
+
const statusCode = Number(error?.statusCode || 400);
|
|
316
|
+
sendJson(req, res, statusCode, { error: error?.message || 'Falha ao interpretar payload JSON.' });
|
|
317
|
+
return null;
|
|
318
|
+
});
|
|
319
|
+
if (body === null) return true;
|
|
320
|
+
|
|
321
|
+
if (action === 'pin' || action === 'unpin') {
|
|
322
|
+
const pinned = action === 'pin';
|
|
323
|
+
const payload = await setSystemAdminGroupPin({
|
|
324
|
+
groupJid,
|
|
325
|
+
pinned,
|
|
326
|
+
sessionId: body?.session_id || body?.sessionId || null,
|
|
327
|
+
reason: body?.reason || null,
|
|
328
|
+
changedBy: 'system_admin_api',
|
|
329
|
+
metadata: body?.metadata || null,
|
|
330
|
+
});
|
|
331
|
+
sendJson(req, res, 200, payload);
|
|
332
|
+
return true;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (action === 'failover') {
|
|
336
|
+
const targetSessionId = String(body?.target_session_id || body?.targetSessionId || requestUrl.searchParams.get('target_session_id') || '')
|
|
337
|
+
.trim()
|
|
338
|
+
.slice(0, 64);
|
|
339
|
+
if (!targetSessionId) {
|
|
340
|
+
sendJson(req, res, 400, { error: 'target_session_id e obrigatorio.' });
|
|
341
|
+
return true;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const payload = await forceSystemAdminGroupFailover({
|
|
345
|
+
groupJid,
|
|
346
|
+
targetSessionId,
|
|
347
|
+
reason: body?.reason || 'admin_force_failover',
|
|
348
|
+
changedBy: 'system_admin_api',
|
|
349
|
+
metadata: body?.metadata || null,
|
|
350
|
+
});
|
|
351
|
+
sendJson(req, res, 200, payload);
|
|
352
|
+
return true;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
sendJson(req, res, 404, { error: 'Endpoint de operacao multi-session nao encontrado.' });
|
|
357
|
+
return true;
|
|
358
|
+
};
|
|
359
|
+
|
|
127
360
|
export const getSystemAdminRouteConfig = () => ({
|
|
128
361
|
webPath: USER_SYSTEMADM_WEB_PATH,
|
|
129
362
|
legacyWebPath: STICKER_ADMIN_WEB_PATH,
|
|
130
363
|
apiAdminBasePath: SYSTEM_ADMIN_API_BASE_PATH,
|
|
131
364
|
apiAdminSessionPath: SYSTEM_ADMIN_API_SESSION_PATH,
|
|
365
|
+
apiAdminMultiSessionPath: SYSTEM_ADMIN_MULTI_SESSION_API_PATH,
|
|
132
366
|
legacyApiAdminBasePath: LEGACY_SYSTEM_ADMIN_API_BASE_PATH,
|
|
133
367
|
legacyApiAdminSessionPath: LEGACY_SYSTEM_ADMIN_API_SESSION_PATH,
|
|
368
|
+
legacyApiAdminMultiSessionPath: LEGACY_SYSTEM_ADMIN_MULTI_SESSION_API_PATH,
|
|
134
369
|
});
|
|
135
370
|
|
|
136
371
|
export const maybeHandleSystemAdminRequest = async (req, res, { pathname, url }) => {
|
|
@@ -186,6 +421,25 @@ export const maybeHandleSystemAdminRequest = async (req, res, { pathname, url })
|
|
|
186
421
|
}
|
|
187
422
|
|
|
188
423
|
if (hasPathPrefix(pathname, SYSTEM_ADMIN_API_BASE_PATH) || hasPathPrefix(pathname, LEGACY_SYSTEM_ADMIN_API_BASE_PATH)) {
|
|
424
|
+
const multiSessionPath = mapMultiSessionApiPath(pathname);
|
|
425
|
+
if (multiSessionPath !== null) {
|
|
426
|
+
try {
|
|
427
|
+
return await handleMultiSessionOpsRequest(req, res, {
|
|
428
|
+
pathname: multiSessionPath,
|
|
429
|
+
url,
|
|
430
|
+
});
|
|
431
|
+
} catch (error) {
|
|
432
|
+
logger.error('Falha ao processar endpoint operacional multi-sessao.', {
|
|
433
|
+
action: 'system_admin_multi_session_endpoint_failed',
|
|
434
|
+
method: req.method,
|
|
435
|
+
path: pathname,
|
|
436
|
+
error: error?.message,
|
|
437
|
+
});
|
|
438
|
+
sendJson(req, res, 500, { error: 'Falha interna ao processar operacao multi-sessao.' });
|
|
439
|
+
return true;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
189
443
|
const legacyPathname = mapAdminApiPathToLegacy(pathname);
|
|
190
444
|
if (!legacyPathname) return false;
|
|
191
445
|
|