@kaikybrofc/omnizap-system 2.3.1 → 2.3.2

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.
Files changed (43) hide show
  1. package/README.md +20 -18
  2. package/app/controllers/messageController.js +473 -255
  3. package/app/modules/analyticsModule/messageAnalysisEventRepository.js +83 -0
  4. package/app/modules/stickerPackModule/stickerDomainEventConsumerRuntime.js +1 -3
  5. package/app/observability/metrics.js +6 -3
  6. package/app/services/googleWebLinkService.js +77 -0
  7. package/database/index.js +2 -0
  8. package/database/migrations/20260301_0028_message_analysis_event.sql +32 -0
  9. package/database/migrations/20260301_0029_admin_action_audit.sql +16 -0
  10. package/package.json +1 -1
  11. package/public/index.html +12 -8
  12. package/public/js/apps/homeApp.js +75 -30
  13. package/public/js/apps/loginApp.js +184 -29
  14. package/public/js/apps/stickersAdminApp.js +3 -9
  15. package/public/js/apps/userApp.js +985 -55
  16. package/public/js/apps/userProfileApp.js +244 -0
  17. package/public/login/index.html +430 -100
  18. package/public/termos-de-uso/index.html +1 -1
  19. package/public/user/index.html +2 -180
  20. package/public/user/systemadm/index.html +774 -0
  21. package/server/controllers/stickerCatalog/nonCatalogHandlers.js +208 -0
  22. package/server/controllers/stickerCatalogController.js +1186 -363
  23. package/server/controllers/systemAdminController.js +141 -0
  24. package/server/controllers/userController.js +87 -0
  25. package/server/http/httpServer.js +72 -32
  26. package/server/middleware/cachePolicy.js +24 -0
  27. package/server/middleware/cachePolicyHelpers.js +2 -0
  28. package/server/middleware/rateLimit.js +82 -0
  29. package/server/middleware/requestLogger.js +16 -0
  30. package/server/middleware/requireAdminAuth.js +42 -0
  31. package/server/middleware/securityHeaders.js +6 -0
  32. package/server/routes/admin/systemAdminRouter.js +56 -0
  33. package/server/routes/health/healthRouter.js +41 -0
  34. package/server/routes/indexRouter.js +203 -0
  35. package/server/routes/metrics/metricsRouter.js +13 -0
  36. package/server/routes/stickerCatalog/catalogHandlers/catalogAdminHttp.js +44 -0
  37. package/server/routes/stickerCatalog/stickerApiRouter.js +84 -0
  38. package/server/routes/stickerCatalog/stickerDataRouter.js +140 -0
  39. package/server/routes/stickerCatalog/stickerSiteRouter.js +43 -0
  40. package/server/routes/user/userRouter.js +56 -0
  41. package/server/utils/safePath.js +26 -0
  42. package/server/routes/metricsRoute.js +0 -7
  43. package/server/routes/stickerCatalogRoute.js +0 -20
@@ -0,0 +1,774 @@
1
+ <!doctype html>
2
+ <html lang="pt-BR">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>OmniZap System | System Admin</title>
7
+ <meta name="description" content="Painel System Admin do OmniZap com moderação, saúde, auditoria e operações." />
8
+ <meta name="robots" content="noindex, nofollow" />
9
+ <meta name="theme-color" content="#0f172a" />
10
+ <link rel="canonical" href="https://omnizap.shop/user/systemadm/" />
11
+ <link rel="icon" type="image/jpeg" href="https://iili.io/FC3FABe.jpg" />
12
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
13
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
14
+ <link href="https://fonts.googleapis.com/css2?family=Manrope:wght@400;500;600;700;800&family=Sora:wght@500;600;700&display=swap" rel="stylesheet" />
15
+ <style>
16
+ :root {
17
+ --bg: #0f172a;
18
+ --bg-2: #111827;
19
+ --card: #1e293bcf;
20
+ --line: rgba(255, 255, 255, 0.05);
21
+ --text: #f8fafc;
22
+ --muted: #94a3b8;
23
+ --primary: #22c55e;
24
+ --radius: 18px;
25
+ }
26
+
27
+ * {
28
+ box-sizing: border-box;
29
+ }
30
+
31
+ html,
32
+ body {
33
+ margin: 0;
34
+ padding: 0;
35
+ min-height: 100%;
36
+ }
37
+
38
+ body {
39
+ color: var(--text);
40
+ font-family: 'Manrope', system-ui, sans-serif;
41
+ background: radial-gradient(55rem 22rem at -10% -10%, #2563eb30, transparent 60%), radial-gradient(55rem 22rem at 110% -8%, #7c3aed22, transparent 56%), linear-gradient(160deg, var(--bg), var(--bg-2));
42
+ min-height: 100vh;
43
+ display: flex;
44
+ align-items: center;
45
+ justify-content: center;
46
+ padding: 24px 14px;
47
+ }
48
+
49
+ .page {
50
+ width: min(840px, 100%);
51
+ border: 1px solid rgba(255, 255, 255, 0.05);
52
+ border-radius: 24px;
53
+ background: linear-gradient(150deg, #111827e8, #1e293bee);
54
+ box-shadow:
55
+ 0 18px 44px #0209166e,
56
+ inset 0 1px 0 #95c1ff1a;
57
+ overflow: hidden;
58
+ }
59
+
60
+ .head {
61
+ border-bottom: 1px solid rgba(255, 255, 255, 0.05);
62
+ padding: 20px 22px;
63
+ display: flex;
64
+ align-items: center;
65
+ justify-content: space-between;
66
+ gap: 14px;
67
+ flex-wrap: wrap;
68
+ }
69
+
70
+ .brand {
71
+ display: inline-flex;
72
+ align-items: center;
73
+ gap: 10px;
74
+ font-family: 'Sora', sans-serif;
75
+ font-weight: 700;
76
+ letter-spacing: 0.2px;
77
+ text-decoration: none;
78
+ color: var(--text);
79
+ }
80
+
81
+ .brand img {
82
+ width: 30px;
83
+ height: 30px;
84
+ border-radius: 50%;
85
+ border: 1px solid rgba(255, 255, 255, 0.05);
86
+ object-fit: cover;
87
+ }
88
+
89
+ .head-actions {
90
+ display: flex;
91
+ flex-wrap: wrap;
92
+ gap: 8px;
93
+ }
94
+
95
+ .btn {
96
+ border: 1px solid var(--line);
97
+ border-radius: 12px;
98
+ background: #101a2f;
99
+ color: var(--text);
100
+ text-decoration: none;
101
+ padding: 9px 12px;
102
+ font-size: 14px;
103
+ font-weight: 600;
104
+ transition:
105
+ transform 0.2s ease,
106
+ border-color 0.2s ease,
107
+ box-shadow 0.2s ease;
108
+ cursor: pointer;
109
+ }
110
+
111
+ .btn:hover {
112
+ transform: translateY(-1px);
113
+ border-color: #2563eb;
114
+ box-shadow: 0 10px 20px #02091650;
115
+ }
116
+
117
+ .btn.primary {
118
+ border-color: transparent;
119
+ background: #22c55e;
120
+ color: #0f172a;
121
+ box-shadow: 0 10px 22px #22c55e30;
122
+ }
123
+
124
+ .btn.primary:hover {
125
+ background: #16a34a;
126
+ }
127
+
128
+ .content {
129
+ padding: 24px 22px;
130
+ display: grid;
131
+ gap: 14px;
132
+ }
133
+
134
+ h1 {
135
+ margin: 0;
136
+ font-family: 'Sora', sans-serif;
137
+ font-size: clamp(26px, 3.6vw, 36px);
138
+ line-height: 1.08;
139
+ letter-spacing: -0.02em;
140
+ background: linear-gradient(90deg, #eef5ff 0%, #60a5fa 46%, #a78bfa 100%);
141
+ -webkit-background-clip: text;
142
+ background-clip: text;
143
+ color: transparent;
144
+ }
145
+
146
+ .lead {
147
+ margin: 0;
148
+ color: #bfd1ea;
149
+ line-height: 1.6;
150
+ font-size: 16px;
151
+ }
152
+
153
+ .card {
154
+ border: 1px solid #334f7bc7;
155
+ border-radius: var(--radius);
156
+ background: var(--card);
157
+ padding: 16px;
158
+ display: grid;
159
+ gap: 12px;
160
+ backdrop-filter: blur(8px);
161
+ }
162
+
163
+ .status {
164
+ margin: 0;
165
+ color: #dce8fa;
166
+ font-size: 15px;
167
+ line-height: 1.5;
168
+ }
169
+
170
+ .error {
171
+ margin: 0;
172
+ border: 1px solid #a74949;
173
+ border-radius: 12px;
174
+ background: #3b181899;
175
+ color: #ffd7d7;
176
+ padding: 9px 10px;
177
+ font-size: 14px;
178
+ }
179
+
180
+ .profile {
181
+ display: grid;
182
+ grid-template-columns: auto 1fr;
183
+ gap: 12px;
184
+ align-items: center;
185
+ border: 1px solid #31517fb5;
186
+ border-radius: 14px;
187
+ background: #1e293bbf;
188
+ padding: 12px;
189
+ }
190
+
191
+ .avatar {
192
+ width: 66px;
193
+ height: 66px;
194
+ border-radius: 16px;
195
+ border: 1px solid #3c5a89;
196
+ object-fit: cover;
197
+ background: #0b1323;
198
+ }
199
+
200
+ .profile-meta h2 {
201
+ margin: 0 0 4px;
202
+ font-size: 20px;
203
+ line-height: 1.2;
204
+ }
205
+
206
+ .profile-meta p {
207
+ margin: 0;
208
+ color: var(--muted);
209
+ font-size: 14px;
210
+ line-height: 1.45;
211
+ word-break: break-word;
212
+ }
213
+
214
+ .grid {
215
+ display: grid;
216
+ grid-template-columns: repeat(4, minmax(0, 1fr));
217
+ gap: 8px;
218
+ }
219
+
220
+ .metric {
221
+ border: 1px solid #31517fb5;
222
+ border-radius: 12px;
223
+ background: #1e293bbf;
224
+ padding: 10px;
225
+ }
226
+
227
+ .metric-label {
228
+ margin: 0 0 4px;
229
+ color: #94a3b8;
230
+ font-size: 12px;
231
+ }
232
+
233
+ .metric-value {
234
+ margin: 0;
235
+ color: #e8f1ff;
236
+ font-size: 19px;
237
+ font-weight: 800;
238
+ letter-spacing: -0.01em;
239
+ }
240
+
241
+ .summary {
242
+ border: 1px solid #2b8b5d9c;
243
+ border-radius: 12px;
244
+ background: #11302294;
245
+ padding: 10px;
246
+ display: grid;
247
+ gap: 6px;
248
+ color: #dfffe8;
249
+ font-size: 14px;
250
+ }
251
+
252
+ .summary strong {
253
+ color: #f4fff8;
254
+ }
255
+
256
+ .admin-panel {
257
+ border: 1px solid #5b4a8f8f;
258
+ border-radius: 12px;
259
+ background: #1a1d3aa8;
260
+ padding: 12px;
261
+ display: grid;
262
+ gap: 10px;
263
+ }
264
+
265
+ .admin-headline {
266
+ display: flex;
267
+ align-items: center;
268
+ justify-content: space-between;
269
+ gap: 8px;
270
+ flex-wrap: wrap;
271
+ }
272
+
273
+ .admin-title {
274
+ margin: 0;
275
+ font-size: 17px;
276
+ color: #eef3ff;
277
+ }
278
+
279
+ .admin-role {
280
+ border: 1px solid #6f5cc2;
281
+ border-radius: 999px;
282
+ padding: 4px 10px;
283
+ font-size: 12px;
284
+ font-weight: 700;
285
+ color: #dad1ff;
286
+ background: #2a2155;
287
+ }
288
+
289
+ .admin-note {
290
+ margin: 0;
291
+ color: #d8d2ff;
292
+ font-size: 14px;
293
+ line-height: 1.45;
294
+ }
295
+
296
+ .admin-error {
297
+ margin: 0;
298
+ border: 1px solid #a74949;
299
+ border-radius: 10px;
300
+ background: #3b181899;
301
+ color: #ffd7d7;
302
+ padding: 8px 10px;
303
+ font-size: 13px;
304
+ }
305
+
306
+ .admin-form {
307
+ display: grid;
308
+ gap: 8px;
309
+ }
310
+
311
+ .admin-label {
312
+ color: #d8d2ff;
313
+ font-size: 13px;
314
+ font-weight: 600;
315
+ }
316
+
317
+ .admin-form-row {
318
+ display: grid;
319
+ grid-template-columns: 1fr auto;
320
+ gap: 8px;
321
+ }
322
+
323
+ .admin-input {
324
+ min-height: 40px;
325
+ border: 1px solid #4f4a77;
326
+ border-radius: 10px;
327
+ background: #11162f;
328
+ color: #f4f7ff;
329
+ font-size: 14px;
330
+ padding: 0 10px;
331
+ }
332
+
333
+ .admin-input:focus {
334
+ outline: none;
335
+ border-color: #7c6be0;
336
+ }
337
+
338
+ .admin-grid {
339
+ display: grid;
340
+ grid-template-columns: repeat(4, minmax(0, 1fr));
341
+ gap: 8px;
342
+ }
343
+
344
+ .admin-metric {
345
+ border: 1px solid #45409d7d;
346
+ border-radius: 10px;
347
+ background: #1a2044bf;
348
+ padding: 9px;
349
+ }
350
+
351
+ .admin-metric-label {
352
+ margin: 0 0 4px;
353
+ color: #b9b3e8;
354
+ font-size: 12px;
355
+ }
356
+
357
+ .admin-metric-value {
358
+ margin: 0;
359
+ color: #f4f6ff;
360
+ font-size: 17px;
361
+ font-weight: 800;
362
+ }
363
+
364
+ .admin-actions {
365
+ display: flex;
366
+ flex-wrap: wrap;
367
+ gap: 8px;
368
+ }
369
+
370
+ .admin-section {
371
+ margin-top: 10px;
372
+ border: 1px solid #433b74;
373
+ border-radius: 10px;
374
+ background: #141936b3;
375
+ padding: 10px;
376
+ display: grid;
377
+ gap: 8px;
378
+ }
379
+
380
+ .admin-section-title {
381
+ margin: 0;
382
+ font-size: 14px;
383
+ color: #ede9ff;
384
+ letter-spacing: 0.01em;
385
+ }
386
+
387
+ .admin-list {
388
+ display: grid;
389
+ gap: 8px;
390
+ }
391
+
392
+ .admin-item {
393
+ border: 1px solid #4a4684;
394
+ border-radius: 9px;
395
+ background: #0f1431d4;
396
+ padding: 8px;
397
+ display: grid;
398
+ gap: 6px;
399
+ }
400
+
401
+ .admin-item-title {
402
+ margin: 0;
403
+ color: #f4f7ff;
404
+ font-size: 13px;
405
+ font-weight: 700;
406
+ }
407
+
408
+ .admin-item-meta {
409
+ margin: 0;
410
+ color: #c9c3ec;
411
+ font-size: 12px;
412
+ line-height: 1.45;
413
+ }
414
+
415
+ .admin-item-actions {
416
+ display: flex;
417
+ flex-wrap: wrap;
418
+ gap: 6px;
419
+ }
420
+
421
+ .admin-mini-btn {
422
+ border: 1px solid #4a5ea4;
423
+ border-radius: 8px;
424
+ background: #18244a;
425
+ color: #e4edff;
426
+ font-size: 12px;
427
+ font-weight: 700;
428
+ padding: 6px 8px;
429
+ cursor: pointer;
430
+ }
431
+
432
+ .admin-mini-btn:hover {
433
+ border-color: #7089d0;
434
+ }
435
+
436
+ .admin-inline-form {
437
+ display: grid;
438
+ grid-template-columns: 1fr auto;
439
+ gap: 8px;
440
+ }
441
+
442
+ .admin-inline-form .admin-input {
443
+ min-height: 36px;
444
+ }
445
+
446
+ .admin-badge {
447
+ display: inline-flex;
448
+ align-items: center;
449
+ border: 1px solid #5f6aa5;
450
+ border-radius: 999px;
451
+ padding: 3px 8px;
452
+ font-size: 11px;
453
+ font-weight: 800;
454
+ letter-spacing: 0.02em;
455
+ width: fit-content;
456
+ }
457
+
458
+ .admin-badge.critical {
459
+ border-color: #aa4d4d;
460
+ background: #4a1f1f;
461
+ color: #ffd7d7;
462
+ }
463
+
464
+ .admin-badge.high {
465
+ border-color: #ad6739;
466
+ background: #4b2b14;
467
+ color: #ffe0c8;
468
+ }
469
+
470
+ .admin-badge.medium {
471
+ border-color: #a9882f;
472
+ background: #44380d;
473
+ color: #fff2c6;
474
+ }
475
+
476
+ .admin-badge.low {
477
+ border-color: #516aa8;
478
+ background: #1d2b4c;
479
+ color: #d9e6ff;
480
+ }
481
+
482
+ .actions {
483
+ display: flex;
484
+ flex-wrap: wrap;
485
+ gap: 8px;
486
+ }
487
+
488
+ .footer {
489
+ margin: 0;
490
+ color: #7f94b8;
491
+ font-size: 12px;
492
+ }
493
+
494
+ @media (max-width: 760px) {
495
+ .grid {
496
+ grid-template-columns: repeat(2, minmax(0, 1fr));
497
+ }
498
+
499
+ .admin-grid {
500
+ grid-template-columns: repeat(2, minmax(0, 1fr));
501
+ }
502
+ }
503
+
504
+ @media (max-width: 620px) {
505
+ .head {
506
+ padding: 16px;
507
+ }
508
+
509
+ .content {
510
+ padding: 18px 16px;
511
+ }
512
+
513
+ .profile {
514
+ grid-template-columns: 1fr;
515
+ text-align: center;
516
+ }
517
+
518
+ .avatar {
519
+ margin-inline: auto;
520
+ }
521
+
522
+ .actions .btn {
523
+ width: 100%;
524
+ text-align: center;
525
+ }
526
+
527
+ .admin-form-row {
528
+ grid-template-columns: 1fr;
529
+ }
530
+
531
+ .admin-inline-form {
532
+ grid-template-columns: 1fr;
533
+ }
534
+ }
535
+ </style>
536
+ </head>
537
+ <body>
538
+ <main id="user-app-root" class="page" data-api-base-path="/api/sticker-packs" data-login-path="/login" data-stickers-path="/stickers">
539
+ <header class="head">
540
+ <a href="/" class="brand">
541
+ <img src="https://iili.io/FC3FABe.jpg" alt="OmniZap" loading="lazy" decoding="async" />
542
+ <span>OmniZap System</span>
543
+ </a>
544
+ <div class="head-actions">
545
+ <a class="btn" href="/">Home</a>
546
+ <a class="btn" href="/user/">Minha Conta</a>
547
+ <a id="user-manage-head-link" class="btn" href="/stickers/perfil">Gerenciar Stickers</a>
548
+ </div>
549
+ </header>
550
+
551
+ <section class="content">
552
+ <h1>System Admin</h1>
553
+ <p class="lead">Painel administrativo do sistema com moderação, saúde operacional, auditoria e exportação.</p>
554
+
555
+ <article class="card">
556
+ <p id="user-status" class="status">Carregando dados da conta...</p>
557
+ <p id="user-error" class="error" hidden></p>
558
+
559
+ <div id="user-profile" class="profile" hidden>
560
+ <img id="user-avatar" class="avatar" src="https://iili.io/FC3FABe.jpg" alt="Avatar do usuário" />
561
+ <div class="profile-meta">
562
+ <h2 id="user-name">Conta</h2>
563
+ <p id="user-email"></p>
564
+ <p id="user-whatsapp"></p>
565
+ </div>
566
+ </div>
567
+
568
+ <div id="user-grid" class="grid" hidden>
569
+ <article class="metric">
570
+ <p class="metric-label">Packs</p>
571
+ <p id="metric-packs" class="metric-value">0</p>
572
+ </article>
573
+ <article class="metric">
574
+ <p class="metric-label">Stickers</p>
575
+ <p id="metric-stickers" class="metric-value">0</p>
576
+ </article>
577
+ <article class="metric">
578
+ <p class="metric-label">Downloads</p>
579
+ <p id="metric-downloads" class="metric-value">0</p>
580
+ </article>
581
+ <article class="metric">
582
+ <p class="metric-label">Likes</p>
583
+ <p id="metric-likes" class="metric-value">0</p>
584
+ </article>
585
+ </div>
586
+
587
+ <div id="user-summary" class="summary" hidden>
588
+ <div><strong>Owner JID:</strong> <span id="user-owner-jid"></span></div>
589
+ <div><strong>Google ID:</strong> <span id="user-google-sub"></span></div>
590
+ <div><strong>Sessão expira:</strong> <span id="user-expires-at"></span></div>
591
+ </div>
592
+
593
+ <section id="user-admin-panel" class="admin-panel" hidden>
594
+ <div class="admin-headline">
595
+ <h3 class="admin-title">Área Admin</h3>
596
+ <span id="user-admin-role" class="admin-role">admin</span>
597
+ </div>
598
+ <p id="user-admin-status" class="admin-note"></p>
599
+ <p id="user-admin-error" class="admin-error" hidden></p>
600
+
601
+ <form id="user-admin-unlock-form" class="admin-form" hidden>
602
+ <label for="user-admin-password" class="admin-label">Senha do painel admin</label>
603
+ <div class="admin-form-row">
604
+ <input id="user-admin-password" class="admin-input" type="password" autocomplete="current-password" placeholder="Digite sua senha" />
605
+ <button id="user-admin-unlock-btn" type="submit" class="btn">Desbloquear</button>
606
+ </div>
607
+ </form>
608
+
609
+ <div id="user-admin-overview" hidden>
610
+ <section class="admin-section">
611
+ <h4 class="admin-section-title">Dashboard rápido</h4>
612
+ <div class="admin-grid">
613
+ <article class="admin-metric">
614
+ <p class="admin-metric-label">Bots online</p>
615
+ <p id="user-admin-bots-online" class="admin-metric-value">0</p>
616
+ </article>
617
+ <article class="admin-metric">
618
+ <p class="admin-metric-label">Mensagens hoje</p>
619
+ <p id="user-admin-messages-today" class="admin-metric-value">0</p>
620
+ </article>
621
+ <article class="admin-metric">
622
+ <p class="admin-metric-label">Spam bloqueado</p>
623
+ <p id="user-admin-spam-blocked" class="admin-metric-value">0</p>
624
+ </article>
625
+ <article class="admin-metric">
626
+ <p class="admin-metric-label">Uptime</p>
627
+ <p id="user-admin-uptime" class="admin-metric-value">n/d</p>
628
+ </article>
629
+ <article class="admin-metric">
630
+ <p class="admin-metric-label">Erros 5xx</p>
631
+ <p id="user-admin-errors-5xx" class="admin-metric-value">0</p>
632
+ </article>
633
+ <article class="admin-metric">
634
+ <p class="admin-metric-label">Packs (total)</p>
635
+ <p id="user-admin-total-packs" class="admin-metric-value">0</p>
636
+ </article>
637
+ <article class="admin-metric">
638
+ <p class="admin-metric-label">Stickers (total)</p>
639
+ <p id="user-admin-total-stickers" class="admin-metric-value">0</p>
640
+ </article>
641
+ <article class="admin-metric">
642
+ <p class="admin-metric-label">Bans ativos</p>
643
+ <p id="user-admin-active-bans" class="admin-metric-value">0</p>
644
+ </article>
645
+ </div>
646
+ </section>
647
+
648
+ <section class="admin-section">
649
+ <h4 class="admin-section-title">Moderação</h4>
650
+ <div id="user-admin-moderation-list" class="admin-list"></div>
651
+ </section>
652
+
653
+ <section class="admin-section">
654
+ <h4 class="admin-section-title">Usuários e sessões</h4>
655
+ <div class="admin-grid">
656
+ <article class="admin-metric">
657
+ <p class="admin-metric-label">Usuários Google</p>
658
+ <p id="user-admin-known-users" class="admin-metric-value">0</p>
659
+ </article>
660
+ <article class="admin-metric">
661
+ <p class="admin-metric-label">Sessões Google</p>
662
+ <p id="user-admin-active-sessions" class="admin-metric-value">0</p>
663
+ </article>
664
+ <article class="admin-metric">
665
+ <p class="admin-metric-label">Visitas 24h</p>
666
+ <p id="user-admin-visits-24h" class="admin-metric-value">0</p>
667
+ </article>
668
+ <article class="admin-metric">
669
+ <p class="admin-metric-label">Visitas 7d</p>
670
+ <p id="user-admin-visits-7d" class="admin-metric-value">0</p>
671
+ </article>
672
+ <article class="admin-metric">
673
+ <p class="admin-metric-label">Visitantes 7d</p>
674
+ <p id="user-admin-unique-visitors-7d" class="admin-metric-value">0</p>
675
+ </article>
676
+ </div>
677
+ <div id="user-admin-sessions-list" class="admin-list"></div>
678
+ <div id="user-admin-users-list" class="admin-list"></div>
679
+ <div id="user-admin-bans-list" class="admin-list"></div>
680
+ </section>
681
+
682
+ <section class="admin-section">
683
+ <h4 class="admin-section-title">Saúde do sistema</h4>
684
+ <div class="admin-grid">
685
+ <article class="admin-metric">
686
+ <p class="admin-metric-label">CPU</p>
687
+ <p id="user-admin-health-cpu" class="admin-metric-value">n/d</p>
688
+ </article>
689
+ <article class="admin-metric">
690
+ <p class="admin-metric-label">RAM</p>
691
+ <p id="user-admin-health-ram" class="admin-metric-value">n/d</p>
692
+ </article>
693
+ <article class="admin-metric">
694
+ <p class="admin-metric-label">Latência p95</p>
695
+ <p id="user-admin-health-latency" class="admin-metric-value">n/d</p>
696
+ </article>
697
+ <article class="admin-metric">
698
+ <p class="admin-metric-label">Fila pendente</p>
699
+ <p id="user-admin-health-queue" class="admin-metric-value">n/d</p>
700
+ </article>
701
+ <article class="admin-metric">
702
+ <p class="admin-metric-label">Status DB</p>
703
+ <p id="user-admin-health-db" class="admin-metric-value">n/d</p>
704
+ </article>
705
+ </div>
706
+ </section>
707
+
708
+ <section class="admin-section">
709
+ <h4 class="admin-section-title">Auditoria</h4>
710
+ <div id="user-admin-audit-list" class="admin-list"></div>
711
+ </section>
712
+
713
+ <section class="admin-section">
714
+ <h4 class="admin-section-title">Controle de recursos</h4>
715
+ <div id="user-admin-flags-list" class="admin-list"></div>
716
+ </section>
717
+
718
+ <section class="admin-section">
719
+ <h4 class="admin-section-title">Alertas</h4>
720
+ <div id="user-admin-alerts-list" class="admin-list"></div>
721
+ </section>
722
+
723
+ <section class="admin-section">
724
+ <h4 class="admin-section-title">Atalhos operacionais</h4>
725
+ <div class="admin-item-actions">
726
+ <button type="button" class="admin-mini-btn" data-admin-op-action="restart_worker">Reiniciar worker</button>
727
+ <button type="button" class="admin-mini-btn" data-admin-op-action="clear_cache">Limpar cache</button>
728
+ <button type="button" class="admin-mini-btn" data-admin-op-action="reprocess_jobs">Reprocessar jobs</button>
729
+ </div>
730
+ <p id="user-admin-ops-status" class="admin-item-meta"></p>
731
+ </section>
732
+
733
+ <section class="admin-section">
734
+ <h4 class="admin-section-title">Busca global</h4>
735
+ <form id="user-admin-search-form" class="admin-inline-form">
736
+ <input id="user-admin-search-input" class="admin-input" type="text" placeholder="Buscar por usuário, grupo, pack ou sessão" />
737
+ <button id="user-admin-search-btn" type="submit" class="btn">Buscar</button>
738
+ </form>
739
+ <div id="user-admin-search-results" class="admin-list"></div>
740
+ </section>
741
+
742
+ <section class="admin-section">
743
+ <h4 class="admin-section-title">Exportação</h4>
744
+ <div class="admin-item-actions">
745
+ <button id="user-admin-export-metrics-json" type="button" class="admin-mini-btn">Métricas JSON</button>
746
+ <button id="user-admin-export-metrics-csv" type="button" class="admin-mini-btn">Métricas CSV</button>
747
+ <button id="user-admin-export-events-json" type="button" class="admin-mini-btn">Eventos JSON</button>
748
+ <button id="user-admin-export-events-csv" type="button" class="admin-mini-btn">Eventos CSV</button>
749
+ </div>
750
+ </section>
751
+
752
+ <div class="admin-actions">
753
+ <button id="user-admin-refresh-btn" type="button" class="btn">Atualizar dados admin</button>
754
+ <button id="user-admin-logout-btn" type="button" class="btn">Sair do admin</button>
755
+ </div>
756
+ </div>
757
+ </section>
758
+
759
+ <div id="user-actions" class="actions" hidden>
760
+ <a id="user-chat-link" class="btn primary" href="https://api.whatsapp.com/send/?text=%2Fmenu&type=custom_url&app_absent=0" target="_blank" rel="noreferrer noopener"> Voltar ao chat do bot (/menu) </a>
761
+ <a class="btn" href="/user/">Voltar para Minha Conta</a>
762
+ <a id="user-manage-main-link" class="btn" href="/stickers/perfil">Gerenciar meus stickers</a>
763
+ <a class="btn" href="/">Voltar para Home</a>
764
+ <button id="user-logout-btn" type="button" class="btn">Encerrar sessão</button>
765
+ </div>
766
+ </article>
767
+
768
+ <p class="footer">OmniZap System · <span id="user-current-year"></span></p>
769
+ </section>
770
+ </main>
771
+
772
+ <script type="module" src="/js/apps/userApp.js?v=20260301-user-systemadm-v1"></script>
773
+ </body>
774
+ </html>