@kaikybrofc/omnizap-system 2.3.0 → 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.
- package/README.md +13 -13
- package/app/controllers/messageController.js +473 -255
- package/app/modules/analyticsModule/messageAnalysisEventRepository.js +83 -0
- package/app/modules/stickerPackModule/stickerDomainEventConsumerRuntime.js +25 -5
- package/app/observability/metrics.js +6 -3
- package/app/services/googleWebLinkService.js +77 -0
- package/database/index.js +3 -0
- package/database/migrations/20260228_0027_web_visit_event.sql +15 -0
- package/database/migrations/20260301_0028_message_analysis_event.sql +32 -0
- package/database/migrations/20260301_0029_admin_action_audit.sql +16 -0
- package/package.json +1 -1
- package/public/index.html +53 -59
- package/public/js/apps/homeApp.js +259 -61
- package/public/js/apps/loginApp.js +184 -29
- package/public/js/apps/stickersAdminApp.js +3 -9
- package/public/js/apps/stickersApp.js +0 -28
- package/public/js/apps/userApp.js +1160 -14
- package/public/js/apps/userProfileApp.js +244 -0
- package/public/licenca/index.html +98 -2
- package/public/login/index.html +430 -100
- package/public/termos-de-uso/index.html +245 -25
- package/public/user/index.html +3 -1
- package/public/user/systemadm/index.html +774 -0
- package/server/auth/googleWebAuth/googleWebAuthService.js +614 -0
- package/server/controllers/stickerCatalog/nonCatalogHandlers.js +208 -0
- package/server/controllers/stickerCatalogController.js +1350 -924
- package/server/controllers/systemAdminController.js +141 -0
- package/server/controllers/userController.js +87 -0
- package/server/http/httpServer.js +72 -32
- package/server/middleware/cachePolicy.js +24 -0
- package/server/middleware/cachePolicyHelpers.js +2 -0
- package/server/middleware/rateLimit.js +82 -0
- package/server/middleware/requestLogger.js +16 -0
- package/server/middleware/requireAdminAuth.js +42 -0
- package/server/middleware/securityHeaders.js +6 -0
- package/server/routes/admin/systemAdminRouter.js +56 -0
- package/server/routes/health/healthRouter.js +41 -0
- package/server/routes/indexRouter.js +203 -0
- package/server/routes/metrics/metricsRouter.js +13 -0
- package/server/routes/stickerCatalog/catalogHandlers/catalogAdminHttp.js +44 -0
- package/server/routes/stickerCatalog/stickerApiRouter.js +84 -0
- package/server/routes/stickerCatalog/stickerDataRouter.js +140 -0
- package/server/routes/stickerCatalog/stickerSiteRouter.js +43 -0
- package/server/routes/user/userRouter.js +56 -0
- package/server/utils/safePath.js +26 -0
- package/server/routes/metricsRoute.js +0 -7
- package/server/routes/stickerCatalogRoute.js +0 -20
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
const GOOGLE_GSI_SCRIPT_SRC = 'https://accounts.google.com/gsi/client';
|
|
4
4
|
const DEFAULT_API_BASE_PATH = '/api/sticker-packs';
|
|
5
|
+
const LOGIN_CONSENT_STORAGE_KEY = 'omnizap_login_terms_consent_v1';
|
|
6
|
+
const LOGIN_CONSENT_HINT = 'Aceite os Termos de Uso e a Politica de Privacidade para continuar.';
|
|
7
|
+
const DEFAULT_SUCCESS_CHAT_LABEL = 'Abrir WhatsApp do bot';
|
|
8
|
+
const DEFAULT_SUCCESS_HOME_LABEL = 'Ir para o painel';
|
|
9
|
+
const ALREADY_LOGGED_STATUS_TEXT = 'Voce ja esta logado neste navegador.';
|
|
10
|
+
const ALREADY_LOGGED_HINT_TEXT = 'Nao e necessario fazer login novamente. Escolha uma opcao abaixo.';
|
|
5
11
|
|
|
6
12
|
const root = document.getElementById('login-app-root');
|
|
7
13
|
|
|
@@ -13,7 +19,14 @@ if (root) {
|
|
|
13
19
|
googleArea: document.getElementById('google-login-area'),
|
|
14
20
|
googleButton: document.querySelector('[data-google-login-button]'),
|
|
15
21
|
googleState: document.getElementById('google-login-state'),
|
|
22
|
+
consentBox: document.getElementById('login-consent-box'),
|
|
23
|
+
consentCheckbox: document.getElementById('login-consent-checkbox'),
|
|
24
|
+
consentError: document.getElementById('login-consent-error'),
|
|
25
|
+
alreadyLoggedBanner: document.getElementById('already-logged-banner'),
|
|
26
|
+
alreadyLoggedTitle: document.getElementById('already-logged-title'),
|
|
27
|
+
alreadyLoggedDetail: document.getElementById('already-logged-detail'),
|
|
16
28
|
summary: document.getElementById('login-summary'),
|
|
29
|
+
summaryTitle: document.getElementById('login-summary-title'),
|
|
17
30
|
summaryOwner: document.getElementById('login-summary-owner'),
|
|
18
31
|
whatsappCta: document.getElementById('whatsapp-cta'),
|
|
19
32
|
whatsappCtaLink: document.getElementById('whatsapp-cta-link'),
|
|
@@ -21,6 +34,7 @@ if (root) {
|
|
|
21
34
|
successActions: document.getElementById('login-success-actions'),
|
|
22
35
|
successChat: document.getElementById('login-success-chat'),
|
|
23
36
|
successHome: document.getElementById('login-success-home'),
|
|
37
|
+
successCelebration: document.getElementById('login-success-celebration'),
|
|
24
38
|
};
|
|
25
39
|
|
|
26
40
|
const state = {
|
|
@@ -30,6 +44,8 @@ if (root) {
|
|
|
30
44
|
googleReady: false,
|
|
31
45
|
busy: false,
|
|
32
46
|
authenticated: false,
|
|
47
|
+
consentAccepted: false,
|
|
48
|
+
successAnimationTimer: 0,
|
|
33
49
|
botPhone: '',
|
|
34
50
|
sessionOwnerPhone: '',
|
|
35
51
|
hint: readWhatsAppHintFromUrl(window.location.search),
|
|
@@ -52,13 +68,67 @@ if (root) {
|
|
|
52
68
|
if (safe) ui.error.textContent = safe;
|
|
53
69
|
};
|
|
54
70
|
|
|
71
|
+
const showConsentError = (message) => {
|
|
72
|
+
if (!ui.consentError) return;
|
|
73
|
+
const safe = String(message || '').trim();
|
|
74
|
+
ui.consentError.hidden = !safe;
|
|
75
|
+
if (safe) ui.consentError.textContent = safe;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const persistConsentState = (accepted) => {
|
|
79
|
+
try {
|
|
80
|
+
if (accepted) {
|
|
81
|
+
window.localStorage.setItem(LOGIN_CONSENT_STORAGE_KEY, '1');
|
|
82
|
+
} else {
|
|
83
|
+
window.localStorage.removeItem(LOGIN_CONSENT_STORAGE_KEY);
|
|
84
|
+
}
|
|
85
|
+
} catch {}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const readConsentState = () => {
|
|
89
|
+
try {
|
|
90
|
+
return window.localStorage.getItem(LOGIN_CONSENT_STORAGE_KEY) === '1';
|
|
91
|
+
} catch {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const hideSuccessCelebration = () => {
|
|
97
|
+
if (!ui.successCelebration) return;
|
|
98
|
+
ui.successCelebration.classList.remove('is-visible');
|
|
99
|
+
window.setTimeout(() => {
|
|
100
|
+
if (!ui.successCelebration?.classList.contains('is-visible')) {
|
|
101
|
+
ui.successCelebration.hidden = true;
|
|
102
|
+
}
|
|
103
|
+
}, 320);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const playSuccessCelebration = () => {
|
|
107
|
+
if (!ui.successCelebration) return;
|
|
108
|
+
if (state.successAnimationTimer) {
|
|
109
|
+
window.clearTimeout(state.successAnimationTimer);
|
|
110
|
+
state.successAnimationTimer = 0;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
ui.successCelebration.hidden = false;
|
|
114
|
+
ui.successCelebration.classList.remove('is-visible');
|
|
115
|
+
void ui.successCelebration.offsetWidth;
|
|
116
|
+
ui.successCelebration.classList.add('is-visible');
|
|
117
|
+
|
|
118
|
+
state.successAnimationTimer = window.setTimeout(() => {
|
|
119
|
+
hideSuccessCelebration();
|
|
120
|
+
state.successAnimationTimer = 0;
|
|
121
|
+
}, 2300);
|
|
122
|
+
};
|
|
123
|
+
|
|
55
124
|
const setBusy = (value) => {
|
|
56
125
|
state.busy = Boolean(value);
|
|
57
126
|
if (ui.googleButton) {
|
|
58
|
-
|
|
59
|
-
ui.googleButton.style.
|
|
127
|
+
const canInteract = !state.busy && state.consentAccepted;
|
|
128
|
+
ui.googleButton.style.opacity = canInteract ? '1' : '0.55';
|
|
129
|
+
ui.googleButton.style.pointerEvents = canInteract ? 'auto' : 'none';
|
|
60
130
|
}
|
|
61
|
-
setText(ui.googleState, state.busy ? 'Finalizando login...' : state.googleReady ? '' : 'Carregando login Google...');
|
|
131
|
+
setText(ui.googleState, state.busy ? 'Finalizando login...' : state.consentAccepted ? (state.googleReady ? '' : 'Carregando login Google...') : LOGIN_CONSENT_HINT);
|
|
62
132
|
};
|
|
63
133
|
|
|
64
134
|
const formatPhone = (digits) => {
|
|
@@ -102,20 +172,67 @@ if (root) {
|
|
|
102
172
|
|
|
103
173
|
const canUseGoogleLogin = () => Boolean(state.hint.phone);
|
|
104
174
|
|
|
105
|
-
const renderSuccessActions = (sessionData) => {
|
|
175
|
+
const renderSuccessActions = (sessionData, options = {}) => {
|
|
106
176
|
if (!ui.successActions) return;
|
|
107
177
|
const authenticated = Boolean(sessionData?.authenticated);
|
|
108
178
|
ui.successActions.hidden = !authenticated;
|
|
109
179
|
if (!authenticated) return;
|
|
110
180
|
|
|
181
|
+
const chatLabel = String(options.chatLabel || DEFAULT_SUCCESS_CHAT_LABEL);
|
|
182
|
+
const homeLabel = String(options.homeLabel || DEFAULT_SUCCESS_HOME_LABEL);
|
|
183
|
+
const homeHref = String(options.homeHref || '/user/');
|
|
184
|
+
|
|
111
185
|
if (ui.successChat) {
|
|
112
186
|
ui.successChat.href = buildWhatsappMenuUrl(state.botPhone);
|
|
187
|
+
setText(ui.successChat, chatLabel);
|
|
113
188
|
}
|
|
114
189
|
if (ui.successHome) {
|
|
115
|
-
ui.successHome.href =
|
|
190
|
+
ui.successHome.href = homeHref;
|
|
191
|
+
setText(ui.successHome, homeLabel);
|
|
116
192
|
}
|
|
117
193
|
};
|
|
118
194
|
|
|
195
|
+
const renderAlreadyLoggedBanner = ({ visible = false, ownerPhone = '' } = {}) => {
|
|
196
|
+
if (!ui.alreadyLoggedBanner) return;
|
|
197
|
+
ui.alreadyLoggedBanner.hidden = !visible;
|
|
198
|
+
if (!visible) return;
|
|
199
|
+
setText(ui.alreadyLoggedTitle, 'Voce ja esta logado neste navegador.');
|
|
200
|
+
if (ownerPhone) {
|
|
201
|
+
setText(ui.alreadyLoggedDetail, `Sessao ativa para +${formatPhone(ownerPhone)}. Nao e necessario fazer login novamente.`);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
setText(ui.alreadyLoggedDetail, ALREADY_LOGGED_HINT_TEXT);
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
const renderAlreadyLoggedInState = (sessionData) => {
|
|
208
|
+
const ownerPhone = String(sessionData?.owner_phone || '').trim();
|
|
209
|
+
state.authenticated = true;
|
|
210
|
+
state.sessionOwnerPhone = ownerPhone;
|
|
211
|
+
|
|
212
|
+
setText(ui.status, ALREADY_LOGGED_STATUS_TEXT);
|
|
213
|
+
if (ownerPhone) {
|
|
214
|
+
setText(ui.hint, `Sessao ativa para +${formatPhone(ownerPhone)}.`);
|
|
215
|
+
} else {
|
|
216
|
+
setText(ui.hint, ALREADY_LOGGED_HINT_TEXT);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
showError('');
|
|
220
|
+
showConsentError('');
|
|
221
|
+
renderAlreadyLoggedBanner({ visible: true, ownerPhone });
|
|
222
|
+
hideSuccessCelebration();
|
|
223
|
+
if (ui.googleArea) ui.googleArea.hidden = true;
|
|
224
|
+
if (ui.summary) ui.summary.hidden = true;
|
|
225
|
+
if (ui.whatsappCta) ui.whatsappCta.hidden = true;
|
|
226
|
+
renderSuccessActions(
|
|
227
|
+
{ authenticated: true },
|
|
228
|
+
{
|
|
229
|
+
chatLabel: 'Abrir WPP no bot',
|
|
230
|
+
homeLabel: 'Voltar para tela inicial',
|
|
231
|
+
homeHref: '/',
|
|
232
|
+
},
|
|
233
|
+
);
|
|
234
|
+
};
|
|
235
|
+
|
|
119
236
|
const renderWhatsAppCta = () => {
|
|
120
237
|
if (!ui.whatsappCta || !ui.whatsappCtaLink) return;
|
|
121
238
|
|
|
@@ -146,19 +263,20 @@ if (root) {
|
|
|
146
263
|
|
|
147
264
|
const renderHint = () => {
|
|
148
265
|
if (!state.hint.hasPayload) {
|
|
149
|
-
setText(ui.hint, '
|
|
266
|
+
setText(ui.hint, 'Abra o link que o bot envia no WhatsApp para liberar o login neste navegador.');
|
|
150
267
|
return;
|
|
151
268
|
}
|
|
152
269
|
|
|
153
270
|
if (!state.hint.phone) {
|
|
154
|
-
setText(ui.hint, 'Este link nao
|
|
271
|
+
setText(ui.hint, 'Este link nao tem um numero valido. Gere um novo enviando "iniciar" no bot.');
|
|
155
272
|
return;
|
|
156
273
|
}
|
|
157
274
|
|
|
158
|
-
setText(ui.hint, `Numero detectado
|
|
275
|
+
setText(ui.hint, `Numero detectado: +${formatPhone(state.hint.phone)}.`);
|
|
159
276
|
};
|
|
160
277
|
|
|
161
278
|
const renderSessionSummary = (sessionData) => {
|
|
279
|
+
renderAlreadyLoggedBanner({ visible: false });
|
|
162
280
|
if (!canUseGoogleLogin()) {
|
|
163
281
|
state.authenticated = false;
|
|
164
282
|
state.sessionOwnerPhone = '';
|
|
@@ -181,18 +299,24 @@ if (root) {
|
|
|
181
299
|
const ownerPhone = String(sessionData?.owner_phone || '').trim();
|
|
182
300
|
state.sessionOwnerPhone = ownerPhone;
|
|
183
301
|
if (ownerPhone) {
|
|
184
|
-
|
|
302
|
+
if (ui.summary) ui.summary.dataset.state = 'ok';
|
|
303
|
+
setText(ui.summaryTitle, 'WhatsApp conectado');
|
|
304
|
+
setText(ui.summaryOwner, `+${formatPhone(ownerPhone)}`);
|
|
185
305
|
renderWhatsAppCta();
|
|
186
306
|
return;
|
|
187
307
|
}
|
|
188
308
|
|
|
189
309
|
if (state.hint.phone) {
|
|
190
|
-
|
|
310
|
+
if (ui.summary) ui.summary.dataset.state = 'pending';
|
|
311
|
+
setText(ui.summaryTitle, 'Vinculo em andamento');
|
|
312
|
+
setText(ui.summaryOwner, `Numero detectado: +${formatPhone(state.hint.phone)}`);
|
|
191
313
|
renderWhatsAppCta();
|
|
192
314
|
return;
|
|
193
315
|
}
|
|
194
316
|
|
|
195
|
-
|
|
317
|
+
if (ui.summary) ui.summary.dataset.state = 'ok';
|
|
318
|
+
setText(ui.summaryTitle, 'Conta ativa');
|
|
319
|
+
setText(ui.summaryOwner, 'Abra o link do WhatsApp para concluir o vinculo.');
|
|
196
320
|
renderWhatsAppCta();
|
|
197
321
|
};
|
|
198
322
|
|
|
@@ -231,7 +355,25 @@ if (root) {
|
|
|
231
355
|
return payload;
|
|
232
356
|
};
|
|
233
357
|
|
|
358
|
+
const syncConsentState = () => {
|
|
359
|
+
state.consentAccepted = Boolean(ui.consentCheckbox?.checked);
|
|
360
|
+
persistConsentState(state.consentAccepted);
|
|
361
|
+
if (ui.consentBox) {
|
|
362
|
+
ui.consentBox.classList.toggle('is-checked', state.consentAccepted);
|
|
363
|
+
}
|
|
364
|
+
if (state.consentAccepted) {
|
|
365
|
+
showConsentError('');
|
|
366
|
+
}
|
|
367
|
+
setBusy(state.busy);
|
|
368
|
+
};
|
|
369
|
+
|
|
234
370
|
const handleGoogleCredential = async (credential) => {
|
|
371
|
+
if (!state.consentAccepted) {
|
|
372
|
+
showConsentError(LOGIN_CONSENT_HINT);
|
|
373
|
+
setBusy(false);
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
|
|
235
377
|
const token = String(credential || '').trim();
|
|
236
378
|
if (!token) {
|
|
237
379
|
showError('Falha ao receber token do Google. Tente novamente.');
|
|
@@ -252,13 +394,15 @@ if (root) {
|
|
|
252
394
|
if (!sessionData?.authenticated) {
|
|
253
395
|
throw new Error('Nao foi possivel criar a sessao Google.');
|
|
254
396
|
}
|
|
255
|
-
setText(ui.status, '
|
|
397
|
+
setText(ui.status, 'Conta Google detectada');
|
|
256
398
|
renderSessionSummary(sessionData);
|
|
257
399
|
setText(ui.googleState, 'Login Google ativo.');
|
|
400
|
+
playSuccessCelebration();
|
|
258
401
|
} catch (error) {
|
|
259
402
|
showError(error?.message || 'Falha ao concluir login Google.');
|
|
260
|
-
setText(ui.status, '
|
|
403
|
+
setText(ui.status, 'Falha ao validar conta Google');
|
|
261
404
|
renderSessionSummary(null);
|
|
405
|
+
hideSuccessCelebration();
|
|
262
406
|
} finally {
|
|
263
407
|
setBusy(false);
|
|
264
408
|
}
|
|
@@ -314,18 +458,18 @@ if (root) {
|
|
|
314
458
|
const buttonWidth = Math.max(180, Math.min(320, measuredWidth || 320));
|
|
315
459
|
accounts.renderButton(ui.googleButton, {
|
|
316
460
|
type: 'standard',
|
|
317
|
-
theme: '
|
|
461
|
+
theme: 'outline',
|
|
318
462
|
size: 'large',
|
|
319
|
-
text: '
|
|
463
|
+
text: 'continue_with',
|
|
320
464
|
shape: 'pill',
|
|
321
465
|
width: buttonWidth,
|
|
322
466
|
});
|
|
323
467
|
|
|
324
468
|
state.googleReady = true;
|
|
325
|
-
|
|
469
|
+
setBusy(state.busy);
|
|
326
470
|
} catch (error) {
|
|
327
471
|
state.googleReady = false;
|
|
328
|
-
|
|
472
|
+
setBusy(state.busy);
|
|
329
473
|
showError(error?.message || 'Falha ao carregar login Google.');
|
|
330
474
|
}
|
|
331
475
|
};
|
|
@@ -382,24 +526,27 @@ if (root) {
|
|
|
382
526
|
};
|
|
383
527
|
|
|
384
528
|
const loadCurrentSession = async () => {
|
|
385
|
-
if (!canUseGoogleLogin()) {
|
|
386
|
-
renderSessionSummary(null);
|
|
387
|
-
return;
|
|
388
|
-
}
|
|
389
529
|
try {
|
|
390
530
|
const payload = await fetchJson(sessionApiPath, { method: 'GET' });
|
|
391
531
|
const sessionData = payload?.data || {};
|
|
392
532
|
if (sessionData?.authenticated) {
|
|
393
|
-
|
|
394
|
-
|
|
533
|
+
renderAlreadyLoggedInState(sessionData);
|
|
534
|
+
return true;
|
|
535
|
+
}
|
|
536
|
+
if (canUseGoogleLogin()) {
|
|
537
|
+
setText(ui.status, 'Entre com Google para continuar');
|
|
395
538
|
} else {
|
|
396
|
-
|
|
397
|
-
|
|
539
|
+
state.authenticated = false;
|
|
540
|
+
state.sessionOwnerPhone = '';
|
|
398
541
|
}
|
|
542
|
+
renderSessionSummary(null);
|
|
399
543
|
} catch {
|
|
400
|
-
|
|
544
|
+
if (canUseGoogleLogin()) {
|
|
545
|
+
setText(ui.status, 'Nao foi possivel validar sua sessao');
|
|
546
|
+
}
|
|
401
547
|
renderSessionSummary(null);
|
|
402
548
|
}
|
|
549
|
+
return false;
|
|
403
550
|
};
|
|
404
551
|
|
|
405
552
|
const renderGoogleLoginGate = () => {
|
|
@@ -408,7 +555,10 @@ if (root) {
|
|
|
408
555
|
ui.googleArea.hidden = !allowed;
|
|
409
556
|
}
|
|
410
557
|
if (!allowed) {
|
|
411
|
-
|
|
558
|
+
renderAlreadyLoggedBanner({ visible: false });
|
|
559
|
+
}
|
|
560
|
+
if (!allowed) {
|
|
561
|
+
setText(ui.status, 'Abra o link enviado no WhatsApp para continuar');
|
|
412
562
|
setText(ui.googleState, '');
|
|
413
563
|
if (ui.summary) ui.summary.hidden = true;
|
|
414
564
|
renderSuccessActions(null);
|
|
@@ -418,6 +568,11 @@ if (root) {
|
|
|
418
568
|
};
|
|
419
569
|
|
|
420
570
|
const init = async () => {
|
|
571
|
+
if (ui.consentCheckbox) {
|
|
572
|
+
ui.consentCheckbox.checked = readConsentState();
|
|
573
|
+
ui.consentCheckbox.addEventListener('change', syncConsentState);
|
|
574
|
+
}
|
|
575
|
+
syncConsentState();
|
|
421
576
|
renderHint();
|
|
422
577
|
if (ui.whatsappCta && state.hint.phone) {
|
|
423
578
|
ui.whatsappCta.hidden = true;
|
|
@@ -426,9 +581,9 @@ if (root) {
|
|
|
426
581
|
renderSuccessActions(null);
|
|
427
582
|
const allowGoogleLogin = renderGoogleLoginGate();
|
|
428
583
|
await loadBotPhone();
|
|
429
|
-
|
|
584
|
+
const alreadyLogged = await loadCurrentSession();
|
|
585
|
+
if (alreadyLogged || !allowGoogleLogin) return;
|
|
430
586
|
await loadConfig();
|
|
431
|
-
await loadCurrentSession();
|
|
432
587
|
await mountGoogleButton();
|
|
433
588
|
};
|
|
434
589
|
|
|
@@ -890,7 +890,7 @@ function renderSystemTab() {
|
|
|
890
890
|
<h3 class="panel-title">Autenticacao admin</h3>
|
|
891
891
|
</div>
|
|
892
892
|
<div class="kv-grid">
|
|
893
|
-
<div class="kv-item"><span class="kv-key">
|
|
893
|
+
<div class="kv-item"><span class="kv-key">Login provider</span><span class="kv-value">${escapeHtml(state.adminStatus?.session?.authenticated ? 'Google + senha' : 'Google')}</span></div>
|
|
894
894
|
<div class="kv-item"><span class="kv-key">Google session</span><span class="kv-value">${authGoogle?.authenticated ? 'Ativa' : 'Inativa'}</span></div>
|
|
895
895
|
<div class="kv-item"><span class="kv-key">Elegivel</span><span class="kv-value">${canUnlockAdmin() ? 'Sim' : 'Nao'}</span></div>
|
|
896
896
|
<div class="kv-item"><span class="kv-key">Seu papel</span><span class="kv-value">${escapeHtml(getAdminRole() || 'owner')}</span></div>
|
|
@@ -1060,7 +1060,6 @@ function renderUnlockView() {
|
|
|
1060
1060
|
const googleSession = google?.user ? google : { authenticated: false };
|
|
1061
1061
|
const localGoogleCache = readLocalGoogleAuthCache();
|
|
1062
1062
|
const hasLocalCache = Boolean(localGoogleCache?.user?.sub);
|
|
1063
|
-
const adminEmail = String(adminStatus?.admin_email || 'ADM_EMAIL');
|
|
1064
1063
|
const googleConfigEnabled = Boolean(state.googleAuthConfig?.enabled && state.googleAuthConfig?.clientId);
|
|
1065
1064
|
|
|
1066
1065
|
return `
|
|
@@ -1087,7 +1086,7 @@ function renderUnlockView() {
|
|
|
1087
1086
|
<div class="account-box">
|
|
1088
1087
|
<p class="row-title">${escapeHtml(googleSession.user?.name || 'Conta Google')}</p>
|
|
1089
1088
|
<p class="row-sub break-all">${escapeHtml(googleSession.user?.email || '')}</p>
|
|
1090
|
-
<p class="row-meta">${canUnlockAdmin() ? 'Conta elegivel para admin.' : 'Conta Google logada nao
|
|
1089
|
+
<p class="row-meta">${canUnlockAdmin() ? 'Conta elegivel para admin.' : 'Conta Google logada nao esta elegivel para admin.'}</p>
|
|
1091
1090
|
</div>
|
|
1092
1091
|
`
|
|
1093
1092
|
: `
|
|
@@ -1117,7 +1116,7 @@ function renderUnlockView() {
|
|
|
1117
1116
|
<form data-form="admin-unlock" class="form-grid">
|
|
1118
1117
|
<input class="search-input" type="password" name="password" placeholder="Digite a senha do painel" autocomplete="current-password" />
|
|
1119
1118
|
<button class="primary-btn" type="submit" ${state.busy ? 'disabled' : ''}>${state.busy ? 'Validando...' : 'Desbloquear Painel'}</button>
|
|
1120
|
-
${!canUnlockAdmin() ?
|
|
1119
|
+
${!canUnlockAdmin() ? '<p class="hint warning">A senha so desbloqueia apos sessao Google elegivel (owner ou moderador autorizado).</p>' : '<p class="hint">Sessao Google elegivel detectada. Informe a senha correspondente.</p>'}
|
|
1121
1120
|
</form>
|
|
1122
1121
|
</article>
|
|
1123
1122
|
</div>
|
|
@@ -1318,8 +1317,6 @@ async function unlockAdmin(password) {
|
|
|
1318
1317
|
async () => {
|
|
1319
1318
|
if (!canUnlockAdmin()) {
|
|
1320
1319
|
const google = state.adminStatus?.google || {};
|
|
1321
|
-
const adminEmail = String(state.adminStatus?.admin_email || '').trim();
|
|
1322
|
-
const loggedEmail = String(google?.user?.email || '').trim();
|
|
1323
1320
|
if (!google?.authenticated) {
|
|
1324
1321
|
const local = readLocalGoogleAuthCache();
|
|
1325
1322
|
if (local?.user?.email) {
|
|
@@ -1327,9 +1324,6 @@ async function unlockAdmin(password) {
|
|
|
1327
1324
|
}
|
|
1328
1325
|
throw new Error('Faca login Google no site com o email admin antes de digitar a senha.');
|
|
1329
1326
|
}
|
|
1330
|
-
if (loggedEmail && adminEmail && loggedEmail.toLowerCase() !== adminEmail.toLowerCase()) {
|
|
1331
|
-
throw new Error(`Email logado (${loggedEmail}) e diferente do ADM_EMAIL (${adminEmail}).`);
|
|
1332
|
-
}
|
|
1333
1327
|
throw new Error('Conta Google atual nao esta elegivel para desbloquear o painel.');
|
|
1334
1328
|
}
|
|
1335
1329
|
|
|
@@ -1729,7 +1729,6 @@ function PackPage({ pack, relatedPacks, onBack, onOpenRelated, onLike, onDislike
|
|
|
1729
1729
|
const packLockedByNsfw = isPackMarkedNsfw(pack) && !hasNsfwAccess;
|
|
1730
1730
|
const cover = packLockedByNsfw ? NSFW_STICKER_PLACEHOLDER_URL : pack?.cover_url || items?.[0]?.asset_url || DEFAULT_STICKER_PLACEHOLDER_URL;
|
|
1731
1731
|
const whatsappUrl = String(pack?.whatsapp?.url || '').trim();
|
|
1732
|
-
const packApiPath = `/api/sticker-packs/${encodeURIComponent(String(pack?.pack_key || '').trim())}`;
|
|
1733
1732
|
const engagement = getPackEngagement(pack);
|
|
1734
1733
|
const hasReactionRequest = Boolean(reactionLoading);
|
|
1735
1734
|
const [previewIndex, setPreviewIndex] = useState(-1);
|
|
@@ -1779,23 +1778,6 @@ function PackPage({ pack, relatedPacks, onBack, onOpenRelated, onLike, onDislike
|
|
|
1779
1778
|
</div>
|
|
1780
1779
|
|
|
1781
1780
|
${pack?.description ? html`<p className="text-sm leading-6 text-slate-300">${pack.description}</p>` : null}
|
|
1782
|
-
|
|
1783
|
-
<section className="rounded-xl border border-cyan-500/25 bg-cyan-500/5 p-3">
|
|
1784
|
-
<p className="text-[11px] uppercase tracking-wide text-cyan-200">Use este pack no seu bot</p>
|
|
1785
|
-
<p className="mt-1 text-sm text-slate-200">Este pack faz parte do módulo de stickers da plataforma OmniZap. Você pode consumir por API e conectar ao seu fluxo de automação WhatsApp.</p>
|
|
1786
|
-
<pre className="mt-2 overflow-auto rounded-lg border border-slate-800 bg-slate-950/60 p-2 text-[11px] text-slate-300">
|
|
1787
|
-
GET ${packApiPath}
|
|
1788
|
-
POST ${packApiPath}/open
|
|
1789
|
-
POST ${packApiPath}/like
|
|
1790
|
-
</pre
|
|
1791
|
-
>
|
|
1792
|
-
<div className="mt-2 flex flex-wrap gap-2">
|
|
1793
|
-
<a href="/api-docs/" className="inline-flex h-8 items-center rounded-lg border border-cyan-500/35 bg-cyan-500/10 px-3 text-[11px] font-semibold text-cyan-100 hover:bg-cyan-500/20"> Ver API e exemplos </a>
|
|
1794
|
-
<a href="/" className="inline-flex h-8 items-center rounded-lg border border-slate-700 bg-slate-900/70 px-3 text-[11px] text-slate-200 hover:bg-slate-800"> Plataforma OmniZap </a>
|
|
1795
|
-
<a href="/stickers/" className="inline-flex h-8 items-center rounded-lg border border-slate-700 bg-slate-900/70 px-3 text-[11px] text-slate-200 hover:bg-slate-800"> Voltar ao catálogo </a>
|
|
1796
|
-
</div>
|
|
1797
|
-
</section>
|
|
1798
|
-
|
|
1799
1781
|
${tags.length
|
|
1800
1782
|
? html`
|
|
1801
1783
|
<div className="space-y-1">
|
|
@@ -3765,16 +3747,6 @@ function StickersApp() {
|
|
|
3765
3747
|
: currentPackKey
|
|
3766
3748
|
? html` ${packLoading ? html`<${PackPageSkeleton} />` : html`<${PackPage} pack=${currentPack} relatedPacks=${relatedPacks} onBack=${goCatalog} onOpenRelated=${openPack} onLike=${handleLike} onDislike=${handleDislike} onTagClick=${openCatalogTagFilter} reactionLoading=${reactionLoading} reactionNotice=${reactionNotice} hasNsfwAccess=${hasNsfwAccess} onRequireLogin=${requestNsfwUnlock} />`} `
|
|
3767
3749
|
: html`
|
|
3768
|
-
<section className="rounded-2xl border border-slate-800 bg-slate-900/80 p-3 sm:p-4">
|
|
3769
|
-
<p className="text-[11px] uppercase tracking-wide text-slate-400">Módulo de stickers da plataforma OmniZap</p>
|
|
3770
|
-
<h1 className="mt-1 text-lg sm:text-xl font-extrabold tracking-tight text-slate-100">Catálogo de stickers integrável via API</h1>
|
|
3771
|
-
<p className="mt-1 text-sm text-slate-300">Os stickers são uma feature do OmniZap para bots e automação WhatsApp. Explore packs públicos e integre no seu sistema com endpoints documentados.</p>
|
|
3772
|
-
<div className="mt-2 flex flex-wrap gap-2">
|
|
3773
|
-
<a href="/api-docs/" className="inline-flex h-8 items-center rounded-lg border border-cyan-500/35 bg-cyan-500/10 px-3 text-[11px] font-semibold text-cyan-100 hover:bg-cyan-500/20"> Área de Desenvolvedor </a>
|
|
3774
|
-
<a href="/" className="inline-flex h-8 items-center rounded-lg border border-slate-700 bg-slate-900/70 px-3 text-[11px] text-slate-200 hover:bg-slate-800"> Página principal OmniZap </a>
|
|
3775
|
-
</div>
|
|
3776
|
-
</section>
|
|
3777
|
-
|
|
3778
3750
|
<div className="lg:grid lg:grid-cols-[220px_minmax(0,1fr)] lg:gap-4">
|
|
3779
3751
|
<aside className="hidden lg:block">
|
|
3780
3752
|
<div className="sticky top-[72px] space-y-2.5 rounded-2xl border border-slate-800 bg-slate-900/80 p-2.5">
|