@omnizap-system/omnizap 2.6.2 → 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 +24 -0
- package/app/config/index.js +4 -0
- package/app/configParts/adminIdentity.js +29 -0
- package/app/configParts/baileysConfig.js +116 -0
- package/app/configParts/groupUtils.js +221 -0
- package/app/configParts/loggerConfig.js +185 -0
- package/app/configParts/messagePersistenceService.js +169 -7
- package/app/configParts/sessionConfig.js +85 -0
- package/app/connection/baileysCompatibility.test.js +9 -0
- package/app/connection/baileysDbAuthState.js +205 -9
- package/app/connection/baileysLibsignalPatch.js +210 -0
- package/app/connection/groupOwnerWriteStateResolver.js +53 -21
- package/app/connection/socketController.js +95 -25
- package/app/connection/socketController.multiSession.test.js +20 -0
- package/app/controllers/messagePipeline/preProcessingMiddlewares.js +17 -3
- package/app/controllers/messageProcessingPipeline.js +2 -0
- package/app/controllers/messageProcessingPipeline.test.js +15 -13
- package/app/services/multiSession/assignmentBalancerService.js +1 -6
- package/app/services/multiSession/groupOwnershipRepository.js +9 -44
- package/app/services/multiSession/groupOwnershipService.js +9 -90
- package/app/services/multiSession/groupOwnershipService.test.js +12 -4
- package/app/services/multiSession/sessionRegistryService.js +6 -60
- package/app/utils/antiLink/antiLinkModule.js +54 -24
- package/docs/security/omnizap-static-security-headers.conf +3 -3
- package/package.json +3 -2
- package/public/comandos/commands-catalog.json +1 -1
- package/public/css/payments-react.css +478 -0
- 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/pages/pagamentos-cancelado.html +21 -0
- package/public/pages/pagamentos-sucesso.html +21 -0
- package/public/pages/pagamentos.html +30 -0
- package/scripts/deploy.sh +3 -0
- package/scripts/new-whatsapp-session.sh +247 -0
- package/server/controllers/admin/systemAdminController.js +4 -17
- package/server/controllers/payments/paymentsController.js +731 -0
- package/server/controllers/system/systemController.js +4 -30
- package/server/email/emailAutomationRuntime.js +36 -1
- package/server/email/emailAutomationService.js +42 -1
- package/server/email/emailTemplateService.js +137 -31
- package/server/http/httpRequestUtils.js +18 -14
- package/server/middleware/securityHeaders.js +15 -2
- package/server/routes/indexRouter.js +27 -7
- package/server/routes/payments/paymentsRouter.js +47 -0
- package/server/routes/static/staticPageRouter.js +3 -0
- package/vite.config.mjs +3 -0
|
@@ -15,6 +15,14 @@ CONNECT_NOW=1
|
|
|
15
15
|
RESET_AUTH=0
|
|
16
16
|
CLEAR_AUTH_FILES=0
|
|
17
17
|
ALLOW_REUSE=0
|
|
18
|
+
PM2_SYNC=1
|
|
19
|
+
PM2_TARGET_APP=""
|
|
20
|
+
PM2_DRY_RUN=0
|
|
21
|
+
RUN_HEALTHCHECK=1
|
|
22
|
+
PM2_WAIT_TIMEOUT_SECONDS=45
|
|
23
|
+
PM2_WAIT_INTERVAL_SECONDS=3
|
|
24
|
+
HEALTHCHECK_WAIT_SECONDS=45
|
|
25
|
+
HEALTHCHECK_INTERVAL_SECONDS=3
|
|
18
26
|
|
|
19
27
|
log() {
|
|
20
28
|
printf '[new-whatsapp-session] %s\n' "$*"
|
|
@@ -45,12 +53,17 @@ Opcoes:
|
|
|
45
53
|
--reset-auth Limpa credenciais atuais da sessao no MySQL antes do QR.
|
|
46
54
|
--clear-auth-files Junto com --reset-auth, remove app/connection/auth/*.json.
|
|
47
55
|
--no-connect Apenas atualiza .env (nao abre conexao para QR agora).
|
|
56
|
+
--no-pm2-sync Nao sincroniza/reinicia app PM2 automaticamente.
|
|
57
|
+
--pm2-app <nome> Nome do app PM2 alvo (padrao: <PM2_APP_NAME>-production).
|
|
58
|
+
--pm2-dry-run Exibe a sincronizacao PM2 sem executar restart real.
|
|
59
|
+
--no-healthcheck Nao executa healthcheck HTTP apos sincronizar PM2.
|
|
48
60
|
--help Mostra esta ajuda.
|
|
49
61
|
|
|
50
62
|
Exemplos:
|
|
51
63
|
npm run new:work
|
|
52
64
|
npm run new:work -- --session suporte_2 --primary
|
|
53
65
|
bash scripts/new-whatsapp-session.sh --prefix operador --no-connect
|
|
66
|
+
bash scripts/new-whatsapp-session.sh --no-connect --pm2-app omnizap-system-production
|
|
54
67
|
EOF
|
|
55
68
|
}
|
|
56
69
|
|
|
@@ -124,6 +137,222 @@ is_valid_weight() {
|
|
|
124
137
|
return 0
|
|
125
138
|
}
|
|
126
139
|
|
|
140
|
+
normalize_bool() {
|
|
141
|
+
local value
|
|
142
|
+
value="$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]' | xargs)"
|
|
143
|
+
if [[ -z "$value" ]]; then
|
|
144
|
+
printf 'unset'
|
|
145
|
+
return 0
|
|
146
|
+
fi
|
|
147
|
+
case "$value" in
|
|
148
|
+
1|true|yes|y|on)
|
|
149
|
+
printf 'true'
|
|
150
|
+
;;
|
|
151
|
+
0|false|no|n|off)
|
|
152
|
+
printf 'false'
|
|
153
|
+
;;
|
|
154
|
+
*)
|
|
155
|
+
printf 'unknown'
|
|
156
|
+
;;
|
|
157
|
+
esac
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
pm2_get_status() {
|
|
161
|
+
local app_name="$1"
|
|
162
|
+
pm2 jlist 2>/dev/null | node -e "
|
|
163
|
+
const appName = String(process.argv[1] || '').trim();
|
|
164
|
+
let raw = '';
|
|
165
|
+
process.stdin.setEncoding('utf8');
|
|
166
|
+
process.stdin.on('data', (chunk) => {
|
|
167
|
+
raw += chunk;
|
|
168
|
+
});
|
|
169
|
+
process.stdin.on('end', () => {
|
|
170
|
+
try {
|
|
171
|
+
const list = JSON.parse(raw || '[]');
|
|
172
|
+
const target = Array.isArray(list) ? list.find((entry) => String(entry?.name || '') === appName) : null;
|
|
173
|
+
if (!target) {
|
|
174
|
+
process.stdout.write('missing');
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const status = String(target?.pm2_env?.status || '').trim().toLowerCase();
|
|
178
|
+
process.stdout.write(status || 'unknown');
|
|
179
|
+
} catch {
|
|
180
|
+
process.stdout.write('error');
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
" "$app_name"
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
pm2_detect_app_name() {
|
|
187
|
+
local preferred="$1"
|
|
188
|
+
pm2 jlist 2>/dev/null | node -e "
|
|
189
|
+
const preferred = String(process.argv[1] || '').trim();
|
|
190
|
+
let raw = '';
|
|
191
|
+
process.stdin.setEncoding('utf8');
|
|
192
|
+
process.stdin.on('data', (chunk) => {
|
|
193
|
+
raw += chunk;
|
|
194
|
+
});
|
|
195
|
+
process.stdin.on('end', () => {
|
|
196
|
+
try {
|
|
197
|
+
const list = JSON.parse(raw || '[]');
|
|
198
|
+
const names = (Array.isArray(list) ? list : [])
|
|
199
|
+
.map((entry) => String(entry?.name || '').trim())
|
|
200
|
+
.filter(Boolean);
|
|
201
|
+
|
|
202
|
+
if (preferred && names.includes(preferred)) {
|
|
203
|
+
process.stdout.write(preferred);
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (names.includes('omnizap-production')) {
|
|
208
|
+
process.stdout.write('omnizap-production');
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const omnizapProduction = names.find((name) => /omnizap.*-production$/i.test(name));
|
|
213
|
+
if (omnizapProduction) {
|
|
214
|
+
process.stdout.write(omnizapProduction);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const anyProduction = names.find((name) => /-production$/i.test(name));
|
|
219
|
+
if (anyProduction) {
|
|
220
|
+
process.stdout.write(anyProduction);
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
process.stdout.write('');
|
|
225
|
+
} catch {
|
|
226
|
+
process.stdout.write('');
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
" "$preferred"
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
resolve_pm2_target_app() {
|
|
233
|
+
if [[ -n "$PM2_TARGET_APP" ]]; then
|
|
234
|
+
printf '%s' "$PM2_TARGET_APP"
|
|
235
|
+
return 0
|
|
236
|
+
fi
|
|
237
|
+
|
|
238
|
+
local pm2_base_name
|
|
239
|
+
pm2_base_name="${PM2_APP_NAME:-}"
|
|
240
|
+
if [[ -z "$pm2_base_name" ]]; then
|
|
241
|
+
pm2_base_name="$(read_env_value PM2_APP_NAME)"
|
|
242
|
+
fi
|
|
243
|
+
if [[ -n "$pm2_base_name" ]]; then
|
|
244
|
+
printf '%s' "${pm2_base_name}-production"
|
|
245
|
+
return 0
|
|
246
|
+
fi
|
|
247
|
+
|
|
248
|
+
printf '%s' "omnizap-production"
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
wait_pm2_online() {
|
|
252
|
+
local app_name="$1"
|
|
253
|
+
local waited=0
|
|
254
|
+
|
|
255
|
+
while (( waited <= PM2_WAIT_TIMEOUT_SECONDS )); do
|
|
256
|
+
local status
|
|
257
|
+
status="$(pm2_get_status "$app_name")"
|
|
258
|
+
if [[ "$status" == "online" ]]; then
|
|
259
|
+
log "PM2 app '$app_name' online."
|
|
260
|
+
return 0
|
|
261
|
+
fi
|
|
262
|
+
if [[ "$status" == "missing" ]]; then
|
|
263
|
+
fail "PM2 app '$app_name' nao encontrada durante sincronizacao."
|
|
264
|
+
fi
|
|
265
|
+
if [[ "$status" == "error" ]]; then
|
|
266
|
+
fail "Nao foi possivel consultar status do PM2 app '$app_name'."
|
|
267
|
+
fi
|
|
268
|
+
|
|
269
|
+
sleep "$PM2_WAIT_INTERVAL_SECONDS"
|
|
270
|
+
waited=$((waited + PM2_WAIT_INTERVAL_SECONDS))
|
|
271
|
+
done
|
|
272
|
+
|
|
273
|
+
fail "Timeout aguardando PM2 app '$app_name' ficar online."
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
run_http_healthcheck() {
|
|
277
|
+
local metrics_enabled_raw
|
|
278
|
+
metrics_enabled_raw="$(read_env_value METRICS_ENABLED)"
|
|
279
|
+
local metrics_enabled_state
|
|
280
|
+
metrics_enabled_state="$(normalize_bool "$metrics_enabled_raw")"
|
|
281
|
+
if [[ "$metrics_enabled_state" == "false" ]]; then
|
|
282
|
+
log "Healthcheck ignorado: METRICS_ENABLED=false."
|
|
283
|
+
return 0
|
|
284
|
+
fi
|
|
285
|
+
|
|
286
|
+
if ! command -v curl >/dev/null 2>&1; then
|
|
287
|
+
log "Healthcheck ignorado: comando 'curl' nao encontrado."
|
|
288
|
+
return 0
|
|
289
|
+
fi
|
|
290
|
+
|
|
291
|
+
local host port url
|
|
292
|
+
host="$(read_env_value METRICS_HOST)"
|
|
293
|
+
port="$(read_env_value METRICS_PORT)"
|
|
294
|
+
[[ -n "$host" ]] || host="127.0.0.1"
|
|
295
|
+
if [[ "$host" == "0.0.0.0" || "$host" == "::" ]]; then
|
|
296
|
+
host="127.0.0.1"
|
|
297
|
+
fi
|
|
298
|
+
if [[ ! "$port" =~ ^[0-9]+$ ]]; then
|
|
299
|
+
port="9102"
|
|
300
|
+
fi
|
|
301
|
+
url="http://${host}:${port}/healthz"
|
|
302
|
+
|
|
303
|
+
local waited=0
|
|
304
|
+
local last_code="000"
|
|
305
|
+
while (( waited <= HEALTHCHECK_WAIT_SECONDS )); do
|
|
306
|
+
local code
|
|
307
|
+
code="$(curl --silent --show-error --max-time 5 --output /dev/null --write-out '%{http_code}' "$url" || true)"
|
|
308
|
+
if [[ "$code" == "200" ]]; then
|
|
309
|
+
log "Healthcheck HTTP ok em $url."
|
|
310
|
+
return 0
|
|
311
|
+
fi
|
|
312
|
+
last_code="$code"
|
|
313
|
+
sleep "$HEALTHCHECK_INTERVAL_SECONDS"
|
|
314
|
+
waited=$((waited + HEALTHCHECK_INTERVAL_SECONDS))
|
|
315
|
+
done
|
|
316
|
+
|
|
317
|
+
fail "Healthcheck falhou em $url (ultimo HTTP: $last_code)."
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
sync_pm2_if_needed() {
|
|
321
|
+
if [[ "$PM2_SYNC" != "1" ]]; then
|
|
322
|
+
log "Sincronizacao PM2 desativada (--no-pm2-sync)."
|
|
323
|
+
return 0
|
|
324
|
+
fi
|
|
325
|
+
|
|
326
|
+
if ! command -v pm2 >/dev/null 2>&1; then
|
|
327
|
+
log "PM2 nao encontrado; sincronizacao automatica ignorada."
|
|
328
|
+
return 0
|
|
329
|
+
fi
|
|
330
|
+
|
|
331
|
+
local preferred_target
|
|
332
|
+
preferred_target="$(resolve_pm2_target_app)"
|
|
333
|
+
local app_target
|
|
334
|
+
app_target="$(pm2_detect_app_name "$preferred_target")"
|
|
335
|
+
if [[ -z "$app_target" ]]; then
|
|
336
|
+
log "Nenhum app PM2 de producao detectado para sincronizar."
|
|
337
|
+
return 0
|
|
338
|
+
fi
|
|
339
|
+
|
|
340
|
+
if [[ "$PM2_DRY_RUN" == "1" ]]; then
|
|
341
|
+
log "PM2 dry-run: executaria 'pm2 restart $app_target --update-env'."
|
|
342
|
+
return 0
|
|
343
|
+
fi
|
|
344
|
+
|
|
345
|
+
log "Sincronizando PM2 app '$app_target'..."
|
|
346
|
+
pm2 restart "$app_target" --update-env >/dev/null
|
|
347
|
+
wait_pm2_online "$app_target"
|
|
348
|
+
|
|
349
|
+
if [[ "$RUN_HEALTHCHECK" == "1" ]]; then
|
|
350
|
+
run_http_healthcheck
|
|
351
|
+
else
|
|
352
|
+
log "Healthcheck desativado (--no-healthcheck)."
|
|
353
|
+
fi
|
|
354
|
+
}
|
|
355
|
+
|
|
127
356
|
while [[ $# -gt 0 ]]; do
|
|
128
357
|
case "$1" in
|
|
129
358
|
--session)
|
|
@@ -161,6 +390,23 @@ while [[ $# -gt 0 ]]; do
|
|
|
161
390
|
CONNECT_NOW=0
|
|
162
391
|
shift
|
|
163
392
|
;;
|
|
393
|
+
--no-pm2-sync)
|
|
394
|
+
PM2_SYNC=0
|
|
395
|
+
shift
|
|
396
|
+
;;
|
|
397
|
+
--pm2-app)
|
|
398
|
+
[[ $# -ge 2 ]] || fail "faltou valor para --pm2-app"
|
|
399
|
+
PM2_TARGET_APP="$2"
|
|
400
|
+
shift 2
|
|
401
|
+
;;
|
|
402
|
+
--pm2-dry-run)
|
|
403
|
+
PM2_DRY_RUN=1
|
|
404
|
+
shift
|
|
405
|
+
;;
|
|
406
|
+
--no-healthcheck)
|
|
407
|
+
RUN_HEALTHCHECK=0
|
|
408
|
+
shift
|
|
409
|
+
;;
|
|
164
410
|
-h|--help)
|
|
165
411
|
usage
|
|
166
412
|
exit 0
|
|
@@ -298,6 +544,7 @@ log "Sessao preparada com sucesso."
|
|
|
298
544
|
log "session_id: $SESSION_ID"
|
|
299
545
|
log "BAILEYS_SESSION_IDS: $session_ids_value"
|
|
300
546
|
log "BAILEYS_PRIMARY_SESSION_ID: $current_primary_session_id"
|
|
547
|
+
sync_pm2_if_needed
|
|
301
548
|
|
|
302
549
|
if [[ "$CONNECT_NOW" == "1" ]]; then
|
|
303
550
|
require_cmd node
|
|
@@ -4,14 +4,7 @@ import { timingSafeEqual } from 'node:crypto';
|
|
|
4
4
|
import { URL } from 'node:url';
|
|
5
5
|
|
|
6
6
|
import logger from '#logger';
|
|
7
|
-
import {
|
|
8
|
-
forceSystemAdminGroupFailover,
|
|
9
|
-
listSystemAdminAssignmentHistory,
|
|
10
|
-
listSystemAdminAssignments,
|
|
11
|
-
listSystemAdminSessions,
|
|
12
|
-
setSystemAdminGroupPin,
|
|
13
|
-
triggerSystemAdminManualRebalance,
|
|
14
|
-
} from '../system/systemController.js';
|
|
7
|
+
import { forceSystemAdminGroupFailover, listSystemAdminAssignmentHistory, listSystemAdminAssignments, listSystemAdminSessions, setSystemAdminGroupPin, triggerSystemAdminManualRebalance } from '../system/systemController.js';
|
|
15
8
|
|
|
16
9
|
const parseEnvBool = (value, fallback) => {
|
|
17
10
|
if (value === undefined || value === null || value === '') return fallback;
|
|
@@ -48,9 +41,7 @@ const SITE_ORIGIN = String(process.env.SITE_ORIGIN || 'https://omnizap.shop')
|
|
|
48
41
|
|
|
49
42
|
const USER_SYSTEMADM_TEMPLATE_PATH = path.join(process.cwd(), 'public', 'pages', 'user-systemadm.html');
|
|
50
43
|
const LEGACY_STICKER_ADMIN_TEMPLATE_PATH = path.join(process.cwd(), 'public', 'pages', 'stickers-admin.html');
|
|
51
|
-
const SYSTEM_ADMIN_OPS_TOKEN = String(
|
|
52
|
-
process.env.SYSTEM_ADMIN_OPS_TOKEN || process.env.USER_INTERNAL_API_TOKEN || process.env.ADMIN_TOKEN || process.env.ADMIN_API_TOKEN || '',
|
|
53
|
-
).trim();
|
|
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();
|
|
54
45
|
|
|
55
46
|
let stickerCatalogControllerPromise = null;
|
|
56
47
|
const loadStickerCatalogController = async () => {
|
|
@@ -151,9 +142,7 @@ const extractBearerToken = (req) => {
|
|
|
151
142
|
return authHeader.slice(7).trim();
|
|
152
143
|
};
|
|
153
144
|
|
|
154
|
-
const resolveOpsTokenFromRequest = (req) =>
|
|
155
|
-
String(req?.headers?.['x-system-admin-token'] || req?.headers?.['x-internal-api-token'] || req?.headers?.['x-admin-token'] || '').trim() ||
|
|
156
|
-
extractBearerToken(req);
|
|
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);
|
|
157
146
|
|
|
158
147
|
const hasValidOpsToken = (req) => {
|
|
159
148
|
if (!SYSTEM_ADMIN_OPS_TOKEN) return true;
|
|
@@ -247,9 +236,7 @@ const requireSystemAdminOpsAccess = (req, res) => {
|
|
|
247
236
|
const normalizeMultiSessionSubPath = (value) => {
|
|
248
237
|
const raw = String(value || '/').trim();
|
|
249
238
|
if (!raw || raw === '/') return '/';
|
|
250
|
-
return `/${raw
|
|
251
|
-
.replace(/^\/+/g, '')
|
|
252
|
-
.replace(/\/+$/g, '')}`;
|
|
239
|
+
return `/${raw.replace(/^\/+/g, '').replace(/\/+$/g, '')}`;
|
|
253
240
|
};
|
|
254
241
|
|
|
255
242
|
const handleMultiSessionOpsRequest = async (req, res, { pathname, url }) => {
|