@kaikybrofc/omnizap-system 2.3.1 → 2.3.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/README.md +82 -483
- package/app/controllers/messageController.js +473 -255
- package/app/modules/analyticsModule/messageAnalysisEventRepository.js +83 -0
- package/app/modules/stickerModule/stickerCommand.js +7 -2
- package/app/modules/stickerModule/stickerTextCommand.js +7 -2
- package/app/modules/stickerPackModule/stickerDomainEventConsumerRuntime.js +1 -3
- package/app/modules/stickerPackModule/stickerPackCommandHandlers.js +224 -53
- package/app/observability/metrics.js +6 -3
- package/app/services/googleWebLinkService.js +77 -0
- package/app/services/lidMapService.js +83 -4
- package/database/index.js +2 -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 +12 -8
- package/public/js/apps/createPackApp.js +4 -4
- package/public/js/apps/homeApp.js +78 -34
- package/public/js/apps/loginApp.js +245 -35
- package/public/js/apps/stickersAdminApp.js +4 -10
- package/public/js/apps/stickersApp.js +1 -1
- package/public/js/apps/userApp.js +956 -55
- package/public/js/apps/userProfileApp.js +244 -0
- package/public/login/index.html +437 -101
- package/public/termos-de-uso/index.html +1 -1
- package/public/user/index.html +2 -181
- package/public/user/systemadm/index.html +774 -0
- package/server/controllers/stickerCatalog/nonCatalogHandlers.js +183 -0
- package/server/controllers/stickerCatalogController.js +1289 -368
- 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 +1 -0
- package/server/middleware/rateLimit.js +89 -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 +197 -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,18 +2,32 @@
|
|
|
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_HINT_TEXT = 'Nao e necessario fazer login novamente. Escolha uma opcao abaixo.';
|
|
5
10
|
|
|
6
11
|
const root = document.getElementById('login-app-root');
|
|
7
12
|
|
|
8
13
|
if (root) {
|
|
9
14
|
const ui = {
|
|
15
|
+
loginCard: document.getElementById('login-main-card'),
|
|
10
16
|
status: document.getElementById('login-status'),
|
|
11
17
|
hint: document.getElementById('login-hint'),
|
|
12
18
|
error: document.getElementById('login-error'),
|
|
13
19
|
googleArea: document.getElementById('google-login-area'),
|
|
20
|
+
googleButtonShell: document.getElementById('google-login-button-shell'),
|
|
14
21
|
googleButton: document.querySelector('[data-google-login-button]'),
|
|
15
22
|
googleState: document.getElementById('google-login-state'),
|
|
23
|
+
consentBox: document.getElementById('login-consent-box'),
|
|
24
|
+
consentCheckbox: document.getElementById('login-consent-checkbox'),
|
|
25
|
+
consentError: document.getElementById('login-consent-error'),
|
|
26
|
+
alreadyLoggedBanner: document.getElementById('already-logged-banner'),
|
|
27
|
+
alreadyLoggedTitle: document.getElementById('already-logged-title'),
|
|
28
|
+
alreadyLoggedDetail: document.getElementById('already-logged-detail'),
|
|
16
29
|
summary: document.getElementById('login-summary'),
|
|
30
|
+
summaryTitle: document.getElementById('login-summary-title'),
|
|
17
31
|
summaryOwner: document.getElementById('login-summary-owner'),
|
|
18
32
|
whatsappCta: document.getElementById('whatsapp-cta'),
|
|
19
33
|
whatsappCtaLink: document.getElementById('whatsapp-cta-link'),
|
|
@@ -21,6 +35,7 @@ if (root) {
|
|
|
21
35
|
successActions: document.getElementById('login-success-actions'),
|
|
22
36
|
successChat: document.getElementById('login-success-chat'),
|
|
23
37
|
successHome: document.getElementById('login-success-home'),
|
|
38
|
+
successCelebration: document.getElementById('login-success-celebration'),
|
|
24
39
|
};
|
|
25
40
|
|
|
26
41
|
const state = {
|
|
@@ -30,6 +45,8 @@ if (root) {
|
|
|
30
45
|
googleReady: false,
|
|
31
46
|
busy: false,
|
|
32
47
|
authenticated: false,
|
|
48
|
+
consentAccepted: false,
|
|
49
|
+
successAnimationTimer: 0,
|
|
33
50
|
botPhone: '',
|
|
34
51
|
sessionOwnerPhone: '',
|
|
35
52
|
hint: readWhatsAppHintFromUrl(window.location.search),
|
|
@@ -43,6 +60,25 @@ if (root) {
|
|
|
43
60
|
element.textContent = String(value || '');
|
|
44
61
|
};
|
|
45
62
|
|
|
63
|
+
const isAuthenticatedFlagEnabled = (value) => {
|
|
64
|
+
if (value === true || value === 1) return true;
|
|
65
|
+
if (typeof value === 'string') {
|
|
66
|
+
const normalized = value.trim().toLowerCase();
|
|
67
|
+
return normalized === 'true' || normalized === '1';
|
|
68
|
+
}
|
|
69
|
+
return false;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const isAuthenticatedGoogleSession = (sessionData) => {
|
|
73
|
+
if (!sessionData || typeof sessionData !== 'object') return false;
|
|
74
|
+
if (!isAuthenticatedFlagEnabled(sessionData.authenticated)) return false;
|
|
75
|
+
const provider = String(sessionData.provider || '').trim().toLowerCase();
|
|
76
|
+
if (provider && provider !== 'google') return false;
|
|
77
|
+
const ownerJid = String(sessionData.owner_jid || '').trim();
|
|
78
|
+
const userSub = String(sessionData?.user?.sub || '').trim();
|
|
79
|
+
return Boolean(ownerJid || userSub);
|
|
80
|
+
};
|
|
81
|
+
|
|
46
82
|
const normalizeDigits = (value) => String(value || '').replace(/\D+/g, '');
|
|
47
83
|
|
|
48
84
|
const showError = (message) => {
|
|
@@ -52,13 +88,110 @@ if (root) {
|
|
|
52
88
|
if (safe) ui.error.textContent = safe;
|
|
53
89
|
};
|
|
54
90
|
|
|
91
|
+
const showConsentError = (message) => {
|
|
92
|
+
if (!ui.consentError) return;
|
|
93
|
+
const safe = String(message || '').trim();
|
|
94
|
+
ui.consentError.hidden = !safe;
|
|
95
|
+
if (safe) ui.consentError.textContent = safe;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const persistConsentState = (accepted) => {
|
|
99
|
+
try {
|
|
100
|
+
if (accepted) {
|
|
101
|
+
window.localStorage.setItem(LOGIN_CONSENT_STORAGE_KEY, '1');
|
|
102
|
+
} else {
|
|
103
|
+
window.localStorage.removeItem(LOGIN_CONSENT_STORAGE_KEY);
|
|
104
|
+
}
|
|
105
|
+
} catch {
|
|
106
|
+
// Ignore storage errors (private mode or blocked storage).
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const readConsentState = () => {
|
|
111
|
+
try {
|
|
112
|
+
return window.localStorage.getItem(LOGIN_CONSENT_STORAGE_KEY) === '1';
|
|
113
|
+
} catch {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const hideSuccessCelebration = () => {
|
|
119
|
+
if (!ui.successCelebration) return;
|
|
120
|
+
ui.successCelebration.classList.remove('is-visible');
|
|
121
|
+
window.setTimeout(() => {
|
|
122
|
+
if (!ui.successCelebration?.classList.contains('is-visible')) {
|
|
123
|
+
ui.successCelebration.hidden = true;
|
|
124
|
+
}
|
|
125
|
+
}, 320);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const playSuccessCelebration = () => {
|
|
129
|
+
if (!ui.successCelebration) return;
|
|
130
|
+
if (state.successAnimationTimer) {
|
|
131
|
+
window.clearTimeout(state.successAnimationTimer);
|
|
132
|
+
state.successAnimationTimer = 0;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
ui.successCelebration.hidden = false;
|
|
136
|
+
ui.successCelebration.classList.remove('is-visible');
|
|
137
|
+
void ui.successCelebration.offsetWidth;
|
|
138
|
+
ui.successCelebration.classList.add('is-visible');
|
|
139
|
+
|
|
140
|
+
state.successAnimationTimer = window.setTimeout(() => {
|
|
141
|
+
hideSuccessCelebration();
|
|
142
|
+
state.successAnimationTimer = 0;
|
|
143
|
+
}, 2300);
|
|
144
|
+
};
|
|
145
|
+
|
|
55
146
|
const setBusy = (value) => {
|
|
56
147
|
state.busy = Boolean(value);
|
|
57
148
|
if (ui.googleButton) {
|
|
58
|
-
|
|
59
|
-
ui.googleButton.style.
|
|
149
|
+
const canInteract = !state.busy && state.consentAccepted;
|
|
150
|
+
ui.googleButton.style.opacity = canInteract ? '1' : '0.55';
|
|
151
|
+
ui.googleButton.style.pointerEvents = canInteract ? 'auto' : 'none';
|
|
60
152
|
}
|
|
61
|
-
setText(ui.googleState, state.busy ? 'Finalizando login...' : state.googleReady ? '' : 'Carregando login Google...');
|
|
153
|
+
setText(ui.googleState, state.busy ? 'Finalizando login...' : state.consentAccepted ? (state.googleReady ? '' : 'Carregando login Google...') : LOGIN_CONSENT_HINT);
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const renderGoogleLoginControls = () => {
|
|
157
|
+
const hideLoginControls = state.authenticated;
|
|
158
|
+
if (ui.consentBox) {
|
|
159
|
+
ui.consentBox.hidden = hideLoginControls;
|
|
160
|
+
}
|
|
161
|
+
if (ui.googleButtonShell) {
|
|
162
|
+
ui.googleButtonShell.hidden = hideLoginControls || !state.consentAccepted;
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const renderLoginCardVisibility = () => {
|
|
167
|
+
if (!ui.loginCard) return;
|
|
168
|
+
ui.loginCard.hidden = Boolean(state.authenticated);
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const resolveGoogleButtonWidth = () => {
|
|
172
|
+
const directWidth = Math.floor(Number(ui.googleButton?.clientWidth || 0));
|
|
173
|
+
if (directWidth > 0) {
|
|
174
|
+
return Math.max(180, Math.min(320, directWidth));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const areaWidth = Math.floor(Number(ui.googleArea?.clientWidth || 0));
|
|
178
|
+
if (areaWidth > 0) {
|
|
179
|
+
const areaStyles = ui.googleArea ? window.getComputedStyle(ui.googleArea) : null;
|
|
180
|
+
const shellStyles = ui.googleButtonShell ? window.getComputedStyle(ui.googleButtonShell) : null;
|
|
181
|
+
const areaPaddingX = areaStyles ? Number.parseFloat(areaStyles.paddingLeft || '0') + Number.parseFloat(areaStyles.paddingRight || '0') : 0;
|
|
182
|
+
const shellPaddingX = shellStyles ? Number.parseFloat(shellStyles.paddingLeft || '0') + Number.parseFloat(shellStyles.paddingRight || '0') : 16;
|
|
183
|
+
const shellBorderX = shellStyles ? Number.parseFloat(shellStyles.borderLeftWidth || '0') + Number.parseFloat(shellStyles.borderRightWidth || '0') : 2;
|
|
184
|
+
const available = Math.floor(areaWidth - areaPaddingX - shellPaddingX - shellBorderX);
|
|
185
|
+
if (available > 0) {
|
|
186
|
+
return Math.max(180, Math.min(320, available));
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const viewportAvailable = Math.floor(Number(window.innerWidth || 0)) - 96;
|
|
191
|
+
if (viewportAvailable > 0) {
|
|
192
|
+
return Math.max(180, Math.min(320, viewportAvailable));
|
|
193
|
+
}
|
|
194
|
+
return 280;
|
|
62
195
|
};
|
|
63
196
|
|
|
64
197
|
const formatPhone = (digits) => {
|
|
@@ -102,18 +235,47 @@ if (root) {
|
|
|
102
235
|
|
|
103
236
|
const canUseGoogleLogin = () => Boolean(state.hint.phone);
|
|
104
237
|
|
|
105
|
-
const renderSuccessActions = (sessionData) => {
|
|
238
|
+
const renderSuccessActions = (sessionData, options = {}) => {
|
|
106
239
|
if (!ui.successActions) return;
|
|
107
|
-
const authenticated =
|
|
240
|
+
const authenticated = isAuthenticatedFlagEnabled(sessionData?.authenticated);
|
|
108
241
|
ui.successActions.hidden = !authenticated;
|
|
109
242
|
if (!authenticated) return;
|
|
110
243
|
|
|
244
|
+
const chatLabel = String(options.chatLabel || DEFAULT_SUCCESS_CHAT_LABEL);
|
|
245
|
+
const homeLabel = String(options.homeLabel || DEFAULT_SUCCESS_HOME_LABEL);
|
|
246
|
+
const homeHref = String(options.homeHref || '/user/');
|
|
247
|
+
|
|
111
248
|
if (ui.successChat) {
|
|
112
249
|
ui.successChat.href = buildWhatsappMenuUrl(state.botPhone);
|
|
250
|
+
setText(ui.successChat, chatLabel);
|
|
113
251
|
}
|
|
114
252
|
if (ui.successHome) {
|
|
115
|
-
ui.successHome.href =
|
|
253
|
+
ui.successHome.href = homeHref;
|
|
254
|
+
setText(ui.successHome, homeLabel);
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
const renderAlreadyLoggedBanner = ({ visible = false, ownerPhone = '' } = {}) => {
|
|
259
|
+
if (!ui.alreadyLoggedBanner) return;
|
|
260
|
+
ui.alreadyLoggedBanner.hidden = !visible;
|
|
261
|
+
if (!visible) return;
|
|
262
|
+
setText(ui.alreadyLoggedTitle, 'Voce ja esta logado neste navegador.');
|
|
263
|
+
if (ownerPhone) {
|
|
264
|
+
setText(ui.alreadyLoggedDetail, `Sessao ativa para +${formatPhone(ownerPhone)}. Nao e necessario fazer login novamente.`);
|
|
265
|
+
return;
|
|
116
266
|
}
|
|
267
|
+
setText(ui.alreadyLoggedDetail, ALREADY_LOGGED_HINT_TEXT);
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
const renderAlreadyLoggedInState = (sessionData) => {
|
|
271
|
+
const ownerPhone = String(sessionData?.owner_phone || '').trim();
|
|
272
|
+
state.authenticated = true;
|
|
273
|
+
state.sessionOwnerPhone = ownerPhone;
|
|
274
|
+
|
|
275
|
+
showError('');
|
|
276
|
+
showConsentError('');
|
|
277
|
+
hideSuccessCelebration();
|
|
278
|
+
renderSessionSummary(sessionData);
|
|
117
279
|
};
|
|
118
280
|
|
|
119
281
|
const renderWhatsAppCta = () => {
|
|
@@ -146,30 +308,35 @@ if (root) {
|
|
|
146
308
|
|
|
147
309
|
const renderHint = () => {
|
|
148
310
|
if (!state.hint.hasPayload) {
|
|
149
|
-
setText(ui.hint, '
|
|
311
|
+
setText(ui.hint, 'Abra o link que o bot envia no WhatsApp para liberar o login neste navegador.');
|
|
150
312
|
return;
|
|
151
313
|
}
|
|
152
314
|
|
|
153
315
|
if (!state.hint.phone) {
|
|
154
|
-
setText(ui.hint, 'Este link nao
|
|
316
|
+
setText(ui.hint, 'Este link nao tem um numero valido. Gere um novo enviando "iniciar" no bot.');
|
|
155
317
|
return;
|
|
156
318
|
}
|
|
157
319
|
|
|
158
|
-
setText(ui.hint, `Numero detectado
|
|
320
|
+
setText(ui.hint, `Numero detectado: +${formatPhone(state.hint.phone)}.`);
|
|
159
321
|
};
|
|
160
322
|
|
|
161
323
|
const renderSessionSummary = (sessionData) => {
|
|
324
|
+
renderAlreadyLoggedBanner({ visible: false });
|
|
162
325
|
if (!canUseGoogleLogin()) {
|
|
163
326
|
state.authenticated = false;
|
|
164
327
|
state.sessionOwnerPhone = '';
|
|
328
|
+
renderGoogleLoginControls();
|
|
329
|
+
renderLoginCardVisibility();
|
|
165
330
|
if (ui.summary) ui.summary.hidden = true;
|
|
166
331
|
renderSuccessActions(null);
|
|
167
332
|
renderWhatsAppCta();
|
|
168
333
|
return;
|
|
169
334
|
}
|
|
170
335
|
|
|
171
|
-
const authenticated =
|
|
336
|
+
const authenticated = isAuthenticatedGoogleSession(sessionData);
|
|
172
337
|
state.authenticated = authenticated;
|
|
338
|
+
renderGoogleLoginControls();
|
|
339
|
+
renderLoginCardVisibility();
|
|
173
340
|
renderSuccessActions(sessionData);
|
|
174
341
|
if (ui.summary) ui.summary.hidden = !authenticated;
|
|
175
342
|
if (!authenticated) {
|
|
@@ -181,18 +348,24 @@ if (root) {
|
|
|
181
348
|
const ownerPhone = String(sessionData?.owner_phone || '').trim();
|
|
182
349
|
state.sessionOwnerPhone = ownerPhone;
|
|
183
350
|
if (ownerPhone) {
|
|
184
|
-
|
|
351
|
+
if (ui.summary) ui.summary.dataset.state = 'ok';
|
|
352
|
+
setText(ui.summaryTitle, 'WhatsApp conectado');
|
|
353
|
+
setText(ui.summaryOwner, `+${formatPhone(ownerPhone)}`);
|
|
185
354
|
renderWhatsAppCta();
|
|
186
355
|
return;
|
|
187
356
|
}
|
|
188
357
|
|
|
189
358
|
if (state.hint.phone) {
|
|
190
|
-
|
|
359
|
+
if (ui.summary) ui.summary.dataset.state = 'pending';
|
|
360
|
+
setText(ui.summaryTitle, 'Vinculo em andamento');
|
|
361
|
+
setText(ui.summaryOwner, `Numero detectado: +${formatPhone(state.hint.phone)}`);
|
|
191
362
|
renderWhatsAppCta();
|
|
192
363
|
return;
|
|
193
364
|
}
|
|
194
365
|
|
|
195
|
-
|
|
366
|
+
if (ui.summary) ui.summary.dataset.state = 'ok';
|
|
367
|
+
setText(ui.summaryTitle, 'Conta ativa');
|
|
368
|
+
setText(ui.summaryOwner, 'Abra o link do WhatsApp para concluir o vinculo.');
|
|
196
369
|
renderWhatsAppCta();
|
|
197
370
|
};
|
|
198
371
|
|
|
@@ -231,7 +404,26 @@ if (root) {
|
|
|
231
404
|
return payload;
|
|
232
405
|
};
|
|
233
406
|
|
|
407
|
+
const syncConsentState = () => {
|
|
408
|
+
state.consentAccepted = Boolean(ui.consentCheckbox?.checked);
|
|
409
|
+
persistConsentState(state.consentAccepted);
|
|
410
|
+
if (ui.consentBox) {
|
|
411
|
+
ui.consentBox.classList.toggle('is-checked', state.consentAccepted);
|
|
412
|
+
}
|
|
413
|
+
if (state.consentAccepted) {
|
|
414
|
+
showConsentError('');
|
|
415
|
+
}
|
|
416
|
+
renderGoogleLoginControls();
|
|
417
|
+
setBusy(state.busy);
|
|
418
|
+
};
|
|
419
|
+
|
|
234
420
|
const handleGoogleCredential = async (credential) => {
|
|
421
|
+
if (!state.consentAccepted) {
|
|
422
|
+
showConsentError(LOGIN_CONSENT_HINT);
|
|
423
|
+
setBusy(false);
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
|
|
235
427
|
const token = String(credential || '').trim();
|
|
236
428
|
if (!token) {
|
|
237
429
|
showError('Falha ao receber token do Google. Tente novamente.');
|
|
@@ -249,16 +441,18 @@ if (root) {
|
|
|
249
441
|
body: JSON.stringify(buildSessionPayload(token)),
|
|
250
442
|
});
|
|
251
443
|
const sessionData = sessionPayload?.data || {};
|
|
252
|
-
if (!sessionData
|
|
444
|
+
if (!isAuthenticatedGoogleSession(sessionData)) {
|
|
253
445
|
throw new Error('Nao foi possivel criar a sessao Google.');
|
|
254
446
|
}
|
|
255
|
-
setText(ui.status, '
|
|
447
|
+
setText(ui.status, 'Conta Google detectada');
|
|
256
448
|
renderSessionSummary(sessionData);
|
|
257
449
|
setText(ui.googleState, 'Login Google ativo.');
|
|
450
|
+
playSuccessCelebration();
|
|
258
451
|
} catch (error) {
|
|
259
452
|
showError(error?.message || 'Falha ao concluir login Google.');
|
|
260
|
-
setText(ui.status, '
|
|
453
|
+
setText(ui.status, 'Falha ao validar conta Google');
|
|
261
454
|
renderSessionSummary(null);
|
|
455
|
+
hideSuccessCelebration();
|
|
262
456
|
} finally {
|
|
263
457
|
setBusy(false);
|
|
264
458
|
}
|
|
@@ -310,22 +504,21 @@ if (root) {
|
|
|
310
504
|
});
|
|
311
505
|
|
|
312
506
|
ui.googleButton.innerHTML = '';
|
|
313
|
-
const
|
|
314
|
-
const buttonWidth = Math.max(180, Math.min(320, measuredWidth || 320));
|
|
507
|
+
const buttonWidth = resolveGoogleButtonWidth();
|
|
315
508
|
accounts.renderButton(ui.googleButton, {
|
|
316
509
|
type: 'standard',
|
|
317
|
-
theme: '
|
|
510
|
+
theme: 'outline',
|
|
318
511
|
size: 'large',
|
|
319
|
-
text: '
|
|
512
|
+
text: 'continue_with',
|
|
320
513
|
shape: 'pill',
|
|
321
514
|
width: buttonWidth,
|
|
322
515
|
});
|
|
323
516
|
|
|
324
517
|
state.googleReady = true;
|
|
325
|
-
|
|
518
|
+
setBusy(state.busy);
|
|
326
519
|
} catch (error) {
|
|
327
520
|
state.googleReady = false;
|
|
328
|
-
|
|
521
|
+
setBusy(state.busy);
|
|
329
522
|
showError(error?.message || 'Falha ao carregar login Google.');
|
|
330
523
|
}
|
|
331
524
|
};
|
|
@@ -382,24 +575,27 @@ if (root) {
|
|
|
382
575
|
};
|
|
383
576
|
|
|
384
577
|
const loadCurrentSession = async () => {
|
|
385
|
-
if (!canUseGoogleLogin()) {
|
|
386
|
-
renderSessionSummary(null);
|
|
387
|
-
return;
|
|
388
|
-
}
|
|
389
578
|
try {
|
|
390
579
|
const payload = await fetchJson(sessionApiPath, { method: 'GET' });
|
|
391
580
|
const sessionData = payload?.data || {};
|
|
392
|
-
if (sessionData
|
|
393
|
-
|
|
394
|
-
|
|
581
|
+
if (isAuthenticatedGoogleSession(sessionData)) {
|
|
582
|
+
renderAlreadyLoggedInState(sessionData);
|
|
583
|
+
return true;
|
|
584
|
+
}
|
|
585
|
+
if (canUseGoogleLogin()) {
|
|
586
|
+
setText(ui.status, 'Entre com Google para continuar');
|
|
395
587
|
} else {
|
|
396
|
-
|
|
397
|
-
|
|
588
|
+
state.authenticated = false;
|
|
589
|
+
state.sessionOwnerPhone = '';
|
|
398
590
|
}
|
|
591
|
+
renderSessionSummary(null);
|
|
399
592
|
} catch {
|
|
400
|
-
|
|
593
|
+
if (canUseGoogleLogin()) {
|
|
594
|
+
setText(ui.status, 'Nao foi possivel validar sua sessao');
|
|
595
|
+
}
|
|
401
596
|
renderSessionSummary(null);
|
|
402
597
|
}
|
|
598
|
+
return false;
|
|
403
599
|
};
|
|
404
600
|
|
|
405
601
|
const renderGoogleLoginGate = () => {
|
|
@@ -407,8 +603,12 @@ if (root) {
|
|
|
407
603
|
if (ui.googleArea) {
|
|
408
604
|
ui.googleArea.hidden = !allowed;
|
|
409
605
|
}
|
|
606
|
+
renderGoogleLoginControls();
|
|
607
|
+
if (!allowed) {
|
|
608
|
+
renderAlreadyLoggedBanner({ visible: false });
|
|
609
|
+
}
|
|
410
610
|
if (!allowed) {
|
|
411
|
-
setText(ui.status, '
|
|
611
|
+
setText(ui.status, 'Abra o link enviado no WhatsApp para continuar');
|
|
412
612
|
setText(ui.googleState, '');
|
|
413
613
|
if (ui.summary) ui.summary.hidden = true;
|
|
414
614
|
renderSuccessActions(null);
|
|
@@ -418,6 +618,12 @@ if (root) {
|
|
|
418
618
|
};
|
|
419
619
|
|
|
420
620
|
const init = async () => {
|
|
621
|
+
renderAlreadyLoggedBanner({ visible: false });
|
|
622
|
+
if (ui.consentCheckbox) {
|
|
623
|
+
ui.consentCheckbox.checked = readConsentState();
|
|
624
|
+
ui.consentCheckbox.addEventListener('change', syncConsentState);
|
|
625
|
+
}
|
|
626
|
+
syncConsentState();
|
|
421
627
|
renderHint();
|
|
422
628
|
if (ui.whatsappCta && state.hint.phone) {
|
|
423
629
|
ui.whatsappCta.hidden = true;
|
|
@@ -426,9 +632,13 @@ if (root) {
|
|
|
426
632
|
renderSuccessActions(null);
|
|
427
633
|
const allowGoogleLogin = renderGoogleLoginGate();
|
|
428
634
|
await loadBotPhone();
|
|
429
|
-
if (!allowGoogleLogin)
|
|
635
|
+
if (!allowGoogleLogin) {
|
|
636
|
+
renderSessionSummary(null);
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
const alreadyLogged = await loadCurrentSession();
|
|
640
|
+
if (alreadyLogged) return;
|
|
430
641
|
await loadConfig();
|
|
431
|
-
await loadCurrentSession();
|
|
432
642
|
await mountGoogleButton();
|
|
433
643
|
};
|
|
434
644
|
|
|
@@ -188,7 +188,7 @@ function loadScript(src) {
|
|
|
188
188
|
|
|
189
189
|
async function fetchJson(url, options = {}) {
|
|
190
190
|
const response = await fetch(url, {
|
|
191
|
-
credentials: '
|
|
191
|
+
credentials: 'include',
|
|
192
192
|
...options,
|
|
193
193
|
});
|
|
194
194
|
|
|
@@ -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
|
|
|
@@ -2293,7 +2293,7 @@ function StickersApp() {
|
|
|
2293
2293
|
let attempt = 0;
|
|
2294
2294
|
while (true) {
|
|
2295
2295
|
try {
|
|
2296
|
-
const response = await fetch(url, { credentials: '
|
|
2296
|
+
const response = await fetch(url, { credentials: 'include', ...opts });
|
|
2297
2297
|
const rawText = await response.text().catch(() => '');
|
|
2298
2298
|
let payload = {};
|
|
2299
2299
|
if (rawText) {
|