@omnizap-system/omnizap 2.6.1 → 2.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/.env.example +78 -9
  2. package/.github/workflows/ci.yml +3 -3
  3. package/.github/workflows/security-runner-hardening.yml +1 -1
  4. package/.github/workflows/security-zap-full-scan.yml +1 -0
  5. package/app/config/index.js +6 -0
  6. package/app/configParts/adminIdentity.js +36 -7
  7. package/app/configParts/baileysConfig.js +343 -56
  8. package/app/configParts/groupUtils.js +226 -0
  9. package/app/configParts/loggerConfig.js +185 -0
  10. package/app/configParts/messagePersistenceService.js +307 -5
  11. package/app/configParts/sessionConfig.js +242 -0
  12. package/app/connection/baileysCompatibility.test.js +10 -1
  13. package/app/connection/baileysDbAuthState.js +205 -9
  14. package/app/connection/baileysLibsignalPatch.js +210 -0
  15. package/app/connection/groupOwnerWriteStateResolver.js +141 -0
  16. package/app/connection/socketController.js +694 -123
  17. package/app/connection/socketController.multiSession.test.js +128 -0
  18. package/app/controllers/messageController.js +1 -1
  19. package/app/controllers/messagePipeline/commandMiddleware.js +12 -10
  20. package/app/controllers/messagePipeline/conversationMiddleware.js +2 -1
  21. package/app/controllers/messagePipeline/messagePipelineMiddlewares.test.js +104 -0
  22. package/app/controllers/messagePipeline/preProcessingMiddlewares.js +96 -4
  23. package/app/controllers/messageProcessingPipeline.js +90 -9
  24. package/app/controllers/messageProcessingPipeline.test.js +202 -0
  25. package/app/modules/adminModule/AGENT.md +1 -1
  26. package/app/modules/adminModule/commandConfig.json +3318 -1347
  27. package/app/modules/adminModule/groupCommandHandlers.js +856 -14
  28. package/app/modules/adminModule/groupCommandHandlers.test.js +375 -9
  29. package/app/modules/adminModule/groupWarningRepository.js +152 -0
  30. package/app/modules/aiModule/AGENT.md +47 -30
  31. package/app/modules/aiModule/aiConfigRuntime.js +1 -0
  32. package/app/modules/aiModule/catCommand.js +132 -25
  33. package/app/modules/aiModule/commandConfig.json +114 -28
  34. package/app/modules/analyticsModule/messageAnalysisEventRepository.js +54 -6
  35. package/app/modules/gameModule/AGENT.md +1 -1
  36. package/app/modules/gameModule/commandConfig.json +29 -0
  37. package/app/modules/menuModule/AGENT.md +1 -1
  38. package/app/modules/menuModule/commandConfig.json +45 -10
  39. package/app/modules/menuModule/menuCatalogService.js +190 -0
  40. package/app/modules/menuModule/menuCommandUsageRepository.js +109 -0
  41. package/app/modules/menuModule/menuDynamicService.js +511 -0
  42. package/app/modules/menuModule/menuDynamicService.test.js +141 -0
  43. package/app/modules/menuModule/menus.js +36 -5
  44. package/app/modules/playModule/AGENT.md +10 -5
  45. package/app/modules/playModule/commandConfig.json +74 -16
  46. package/app/modules/playModule/playCommandConstants.js +13 -7
  47. package/app/modules/playModule/playCommandCore.js +4 -6
  48. package/app/modules/playModule/{playCommandYtDlpClient.js → playCommandMediaClient.js} +684 -332
  49. package/app/modules/playModule/playConfigRuntime.js +5 -6
  50. package/app/modules/playModule/playModuleCriticalFlows.test.js +44 -59
  51. package/app/modules/quoteModule/AGENT.md +1 -1
  52. package/app/modules/quoteModule/commandConfig.json +29 -0
  53. package/app/modules/rpgPokemonModule/AGENT.md +1 -1
  54. package/app/modules/rpgPokemonModule/commandConfig.json +29 -0
  55. package/app/modules/statsModule/AGENT.md +1 -1
  56. package/app/modules/statsModule/commandConfig.json +58 -0
  57. package/app/modules/stickerModule/AGENT.md +1 -1
  58. package/app/modules/stickerModule/commandConfig.json +145 -0
  59. package/app/modules/stickerPackModule/AGENT.md +1 -1
  60. package/app/modules/stickerPackModule/autoPackCollectorService.js +5 -1
  61. package/app/modules/stickerPackModule/commandConfig.json +29 -0
  62. package/app/modules/stickerPackModule/stickerAutoPackByTagsRuntime.js +1 -1
  63. package/app/modules/stickerPackModule/stickerPackCommandHandlers.js +78 -57
  64. package/app/modules/stickerPackModule/stickerPackService.js +13 -6
  65. package/app/modules/systemMetricsModule/AGENT.md +1 -1
  66. package/app/modules/systemMetricsModule/commandConfig.json +29 -0
  67. package/app/modules/tiktokModule/AGENT.md +1 -1
  68. package/app/modules/tiktokModule/commandConfig.json +29 -0
  69. package/app/modules/userModule/AGENT.md +1 -1
  70. package/app/modules/userModule/commandConfig.json +29 -0
  71. package/app/modules/waifuPicsModule/AGENT.md +57 -27
  72. package/app/modules/waifuPicsModule/commandConfig.json +87 -0
  73. package/app/observability/metrics.js +136 -0
  74. package/app/services/ai/commandConfigEnrichmentService.js +229 -47
  75. package/app/services/ai/geminiService.js +131 -7
  76. package/app/services/ai/geminiService.test.js +59 -2
  77. package/app/services/ai/moduleAiHelpCoreService.js +33 -4
  78. package/app/services/group/groupMetadataService.js +24 -1
  79. package/app/services/infra/dbWriteQueue.js +51 -21
  80. package/app/services/messaging/newsBroadcastService.js +843 -27
  81. package/app/services/multiSession/assignmentBalancerService.js +452 -0
  82. package/app/services/multiSession/groupOwnershipRepository.js +346 -0
  83. package/app/services/multiSession/groupOwnershipService.js +809 -0
  84. package/app/services/multiSession/groupOwnershipService.test.js +317 -0
  85. package/app/services/multiSession/sessionRegistryService.js +239 -0
  86. package/app/store/aiPromptStore.js +36 -19
  87. package/app/store/groupConfigStore.js +41 -5
  88. package/app/store/premiumUserStore.js +21 -7
  89. package/app/utils/antiLink/antiLinkModule.js +391 -25
  90. package/app/workers/aiHelperContinuousLearningWorker.js +512 -0
  91. package/database/index.js +6 -0
  92. package/database/migrations/20260307_d0_hardening_down.sql +1 -1
  93. package/database/migrations/20260314_d7_canonical_sender_down.sql +1 -1
  94. package/database/migrations/20260406_d30_security_analytics_down.sql +1 -1
  95. package/database/migrations/20260411_d35_group_community_metadata_down.sql +59 -0
  96. package/database/migrations/20260411_d35_group_community_metadata_up.sql +62 -0
  97. package/database/migrations/20260412_d36_system_config_tables_down.sql +32 -0
  98. package/database/migrations/20260412_d36_system_config_tables_up.sql +66 -0
  99. package/database/migrations/20260413_d37_group_user_warnings_down.sql +11 -0
  100. package/database/migrations/20260413_d37_group_user_warnings_up.sql +24 -0
  101. package/database/migrations/20260414_d38_multi_session_foundation_down.sql +72 -0
  102. package/database/migrations/20260414_d38_multi_session_foundation_up.sql +125 -0
  103. package/database/migrations/20260414_d39_multi_session_cutover_down.sql +103 -0
  104. package/database/migrations/20260414_d39_multi_session_cutover_up.sql +83 -0
  105. package/database/schema.sql +102 -1
  106. package/docker-compose.yml +4 -1
  107. package/docs/compliance/acceptable-use-policy-2026-03-07.md +1 -1
  108. package/docs/compliance/privacy-policy-2026-03-07.md +2 -2
  109. package/docs/security/dsar-lgpd-runbook-2026-03-07.md +1 -1
  110. package/docs/security/network-hardening-runbook-2026-03-07.md +53 -0
  111. package/docs/security/omnizap-static-security-headers.conf +25 -0
  112. package/ecosystem.prod.config.cjs +31 -11
  113. package/index.js +52 -18
  114. package/observability/alert-rules.yml +20 -0
  115. package/observability/grafana/dashboards/omnizap-system-admin.json +229 -0
  116. package/observability/mysql-setup.sql +4 -4
  117. package/observability/system-admin-observability.md +26 -0
  118. package/package.json +14 -6
  119. package/public/comandos/commands-catalog.json +2253 -78
  120. package/public/css/payments-react.css +478 -0
  121. package/public/js/apps/commandsReactApp.js +267 -87
  122. package/public/js/apps/createPackApp.js +3 -3
  123. package/public/js/apps/homeReactApp.js +2 -2
  124. package/public/js/apps/paymentsCancelReactApp.js +45 -0
  125. package/public/js/apps/paymentsReactApp.js +399 -0
  126. package/public/js/apps/paymentsSuccessReactApp.js +148 -0
  127. package/public/js/apps/stickersApp.js +255 -103
  128. package/public/js/apps/termsReactApp.js +57 -8
  129. package/public/js/apps/userPasswordResetReactApp.js +406 -0
  130. package/public/js/apps/userReactApp.js +96 -47
  131. package/public/js/apps/userSystemAdmReactApp.js +1506 -0
  132. package/public/pages/pagamentos-cancelado.html +21 -0
  133. package/public/pages/pagamentos-sucesso.html +21 -0
  134. package/public/pages/pagamentos.html +30 -0
  135. package/public/pages/politica-de-privacidade.html +1 -1
  136. package/public/pages/stickers.html +5 -5
  137. package/public/pages/termos-de-uso-texto-integral.html +1 -1
  138. package/public/pages/termos-de-uso.html +1 -1
  139. package/public/pages/user-password-reset.html +3 -4
  140. package/public/pages/user-systemadm.html +8 -462
  141. package/public/pages/user.html +1 -1
  142. package/scripts/clear-whatsapp-session.sh +123 -0
  143. package/scripts/core-ai-mode.mjs +163 -0
  144. package/scripts/deploy.sh +13 -0
  145. package/scripts/enrich-command-config-ux-openai.mjs +492 -0
  146. package/scripts/generate-commands-catalog.mjs +155 -0
  147. package/scripts/new-whatsapp-session.sh +564 -0
  148. package/scripts/security-web-surface-check.mjs +218 -0
  149. package/server/controllers/admin/adminPanelHandlers.js +253 -3
  150. package/server/controllers/admin/systemAdminController.js +254 -0
  151. package/server/controllers/payments/paymentsController.js +731 -0
  152. package/server/controllers/sticker/stickerCatalogController.js +9 -23
  153. package/server/controllers/system/contactController.js +9 -17
  154. package/server/controllers/system/stickerCatalogSystemContext.js +27 -6
  155. package/server/controllers/system/systemController.js +228 -1
  156. package/server/controllers/userController.js +6 -0
  157. package/server/email/emailAutomationRuntime.js +36 -1
  158. package/server/email/emailAutomationService.js +42 -1
  159. package/server/email/emailTemplateService.js +140 -33
  160. package/server/http/httpRequestUtils.js +18 -14
  161. package/server/http/httpServer.js +8 -4
  162. package/server/middleware/securityHeaders.js +35 -3
  163. package/server/routes/admin/systemAdminRouter.js +6 -0
  164. package/server/routes/indexRouter.js +50 -6
  165. package/server/routes/observability/grafanaProxyRouter.js +254 -0
  166. package/server/routes/payments/paymentsRouter.js +47 -0
  167. package/server/routes/static/staticPageRouter.js +30 -1
  168. package/server/utils/publicContact.js +31 -0
  169. package/utils/whatsapp/contactEnv.js +39 -0
  170. package/vite.config.mjs +5 -1
  171. package/app/modules/playModule/local/installYtDlp.js +0 -25
  172. package/app/modules/playModule/local/ytDlpInstaller.js +0 -28
@@ -8,7 +8,10 @@ const html = htm.bind(React.createElement);
8
8
 
9
9
  const DEFAULT_API_BASE_PATH = '/api';
10
10
  const DEFAULT_LOGIN_PATH = '/login';
11
+ const DEFAULT_PASSWORD_RESET_WEB_PATH = '/user/password-reset';
11
12
  const DEFAULT_FALLBACK_AVATAR = '/assets/images/brand-logo-128.webp';
13
+ const DEFAULT_SUPPORT_WHATSAPP_NUMBER = '';
14
+ const DEFAULT_SUPPORT_WHATSAPP_URL = '/termos-de-uso/';
12
15
 
13
16
  const TABS = [
14
17
  { key: 'summary', label: 'Estatísticas', icon: '📊' },
@@ -23,6 +26,31 @@ const shortNum = (value) =>
23
26
  maximumFractionDigits: 1,
24
27
  }).format(Math.max(0, Number(value) || 0));
25
28
 
29
+ const normalizeRoutePath = (value, fallback) => {
30
+ const raw = String(value || '').trim();
31
+ if (!raw) return fallback;
32
+ if (!raw.startsWith('/')) return fallback;
33
+ if (/^\/\//.test(raw)) return fallback;
34
+ return raw;
35
+ };
36
+
37
+ const normalizePhoneDigits = (value) =>
38
+ String(value || '')
39
+ .replace(/\D+/g, '')
40
+ .slice(0, 15);
41
+
42
+ const buildWhatsappUrl = (value) => {
43
+ const digits = normalizePhoneDigits(value);
44
+ if (!digits) return '';
45
+ return `https://wa.me/${digits}`;
46
+ };
47
+
48
+ const resolveSupportWhatsappUrl = (config) => {
49
+ const explicitUrl = String(config?.supportWhatsappUrl || '').trim();
50
+ if (/^https?:\/\/wa\.me\/\d{8,15}(?:\?.*)?$/i.test(explicitUrl)) return explicitUrl;
51
+ return buildWhatsappUrl(config?.supportWhatsappNumber) || DEFAULT_SUPPORT_WHATSAPP_URL;
52
+ };
53
+
26
54
  const UserApp = ({ config }) => {
27
55
  const [activeTab, setActiveTab] = useState('summary');
28
56
  const [isLoading, setLoading] = useState(true);
@@ -30,6 +58,9 @@ const UserApp = ({ config }) => {
30
58
  const [session, setSession] = useState(null);
31
59
  const [isSidebarCollapsed, setSidebarCollapsed] = useState(false);
32
60
  const [isMobileMenuOpen, setMobileMenuOpen] = useState(false);
61
+ const [passwordResetBusy, setPasswordResetBusy] = useState(false);
62
+ const [passwordResetError, setPasswordResetError] = useState('');
63
+ const [passwordResetInfo, setPasswordResetInfo] = useState('');
33
64
 
34
65
  // Lock scroll when mobile menu is open
35
66
  useEffect(() => {
@@ -102,6 +133,7 @@ const UserApp = ({ config }) => {
102
133
 
103
134
  const rpgInfo = useMemo(() => summary?.rpg || { level: 1, xp: 0, gold: 0, karma: { score: 0, positive: 0, negative: 0 }, pvp: { matches: 0, wins: 0, losses: 0 }, inventory_count: 0, total_pokemons: 0 }, [summary]);
104
135
  const usageInfo = useMemo(() => summary?.usage || { messages: 0, packs: 0, stickers: 0, activity_chart: [], insights: {}, first_message_at: null, last_message_at: null }, [summary]);
136
+ const supportWhatsappUrl = useMemo(() => resolveSupportWhatsappUrl(config), [config.supportWhatsappNumber, config.supportWhatsappUrl]);
105
137
 
106
138
  const daysMember = useMemo(() => {
107
139
  if (!rpgInfo.member_since) return 0;
@@ -122,6 +154,54 @@ const UserApp = ({ config }) => {
122
154
  }
123
155
  };
124
156
 
157
+ const startPasswordResetFlow = async () => {
158
+ if (passwordResetBusy) return;
159
+ setPasswordResetError('');
160
+ setPasswordResetInfo('');
161
+ setPasswordResetBusy(true);
162
+
163
+ try {
164
+ const response = await fetch(`${config.apiBasePath}/auth/password/recovery/session`, {
165
+ method: 'POST',
166
+ credentials: 'include',
167
+ headers: {
168
+ 'Content-Type': 'application/json; charset=utf-8',
169
+ },
170
+ body: JSON.stringify({}),
171
+ });
172
+
173
+ let payload = null;
174
+ try {
175
+ payload = await response.json();
176
+ } catch {
177
+ payload = null;
178
+ }
179
+
180
+ if (!response.ok) {
181
+ throw new Error(payload?.error || `Falha HTTP ${response.status}`);
182
+ }
183
+
184
+ const data = payload?.data || {};
185
+ const sessionToken = String(data?.session_token || '')
186
+ .trim()
187
+ .slice(0, 4096);
188
+ const sessionPath = normalizeRoutePath(data?.session_path, config.passwordResetWebPath || DEFAULT_PASSWORD_RESET_WEB_PATH);
189
+ const targetUrl = new URL(sessionPath, window.location.origin);
190
+ if (sessionToken) {
191
+ targetUrl.searchParams.set('session_token', sessionToken);
192
+ }
193
+
194
+ setPasswordResetInfo('Sessão de redefinição criada. Redirecionando...');
195
+ window.setTimeout(() => {
196
+ window.location.assign(`${targetUrl.pathname}${targetUrl.search}`);
197
+ }, 120);
198
+ } catch (error) {
199
+ setPasswordResetError(error?.message || 'Não foi possível iniciar a redefinição de senha.');
200
+ } finally {
201
+ setPasswordResetBusy(false);
202
+ }
203
+ };
204
+
125
205
  return html`
126
206
  <style>
127
207
  .sidebar-transition {
@@ -130,7 +210,7 @@ const UserApp = ({ config }) => {
130
210
  transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
131
211
  }
132
212
  .content-transition {
133
- transition: padding-left 0.4s cubic-bezier(0.4, 0, 0.2, 1);
213
+ transition: margin-left 0.4s cubic-bezier(0.4, 0, 0.2, 1);
134
214
  }
135
215
  </style>
136
216
 
@@ -145,7 +225,7 @@ const UserApp = ({ config }) => {
145
225
  <div onClick=${() => setMobileMenuOpen(false)} className=${`fixed inset-0 z-[50] bg-[#020617]/80 lg:hidden transition-opacity duration-300 ${isMobileMenuOpen ? 'opacity-100' : 'opacity-0 pointer-events-none'}`}></div>
146
226
 
147
227
  <!-- Navbar -->
148
- <header className="sticky top-0 z-[40] border-b border-white/5 bg-[#020617]/80 backdrop-blur-xl">
228
+ <header className="fixed inset-x-0 top-0 z-[40] border-b border-white/5 bg-[#020617]/80 backdrop-blur-xl">
149
229
  <div className="px-4 lg:px-8">
150
230
  <div className="flex h-16 items-center justify-between gap-4">
151
231
  <div className="flex items-center gap-4">
@@ -171,9 +251,9 @@ const UserApp = ({ config }) => {
171
251
  </div>
172
252
  </header>
173
253
 
174
- <div className="flex relative">
254
+ <div className="flex relative pt-16">
175
255
  <!-- Sidebar -->
176
- <aside className=${`fixed lg:sticky top-0 lg:top-[65px] h-full lg:h-[calc(100vh-65px)] z-[60] lg:z-30 bg-[#020617] border-r border-white/5 sidebar-transition overflow-y-auto no-scrollbar ${isMobileMenuOpen ? 'translate-x-0 w-[280px]' : '-translate-x-full lg:translate-x-0'} ${isSidebarCollapsed ? 'lg:w-[85px]' : 'lg:w-[280px]'}`}>
256
+ <aside className=${`fixed lg:fixed top-0 lg:top-[65px] lg:left-0 h-full lg:h-[calc(100vh-65px)] z-[60] lg:z-30 bg-[#020617] border-r border-white/5 sidebar-transition overflow-y-auto no-scrollbar ${isMobileMenuOpen ? 'translate-x-0 w-[280px]' : '-translate-x-full lg:translate-x-0'} ${isSidebarCollapsed ? 'lg:w-[85px]' : 'lg:w-[280px]'}`}>
177
257
  <div className="p-4 flex flex-col h-full">
178
258
  <!-- Mobile Header inside Sidebar -->
179
259
  <div className="lg:hidden flex items-center justify-between mb-6 px-2">
@@ -240,7 +320,7 @@ const UserApp = ({ config }) => {
240
320
  </aside>
241
321
 
242
322
  <!-- Main Content -->
243
- <main className=${`flex-1 min-w-0 content-transition px-4 lg:px-10 py-8 lg:py-12`}>
323
+ <main className=${`flex-1 min-w-0 content-transition px-4 lg:px-10 py-8 lg:py-12 ${isSidebarCollapsed ? 'lg:ml-[85px]' : 'lg:ml-[280px]'}`}>
244
324
  <div className="max-w-5xl mx-auto space-y-10">
245
325
  <div data-reveal="fade-up" className="flex flex-col md:flex-row md:items-end justify-between gap-6">
246
326
  <div className="space-y-2">
@@ -361,37 +441,6 @@ const UserApp = ({ config }) => {
361
441
  </div>
362
442
  </div>
363
443
 
364
- <!-- Activity Chart Section -->
365
- ${usageInfo.activity_chart && usageInfo.activity_chart.length > 0
366
- ? html`
367
- <div className="p-8 lg:p-10 rounded-[3.5rem] bg-white/[0.02] border border-white/5 space-y-8 relative overflow-hidden">
368
- <div className="flex items-center justify-between">
369
- <h3 className="font-black text-xl flex items-center gap-3">
370
- <span className="w-2 h-2 rounded-full bg-primary shadow-[0_0_10px_rgba(34,197,94,0.5)]"></span>
371
- Fluxo de Atividade (7 dias)
372
- </h3>
373
- <span className="text-[9px] font-black uppercase text-white/20 tracking-widest">Update Realtime</span>
374
- </div>
375
- <div className="flex items-end justify-between gap-2 h-48 pt-4 px-2">
376
- ${usageInfo.activity_chart.map((data) => {
377
- const maxCount = Math.max(...usageInfo.activity_chart.map((d) => d.count), 1);
378
- const heightPercent = Math.max((data.count / maxCount) * 100, 5);
379
- return html`
380
- <div key=${data.day} className="flex flex-col items-center gap-3 flex-1 group min-w-0">
381
- <div className="w-full relative flex justify-center h-full items-end">
382
- <div className="w-full max-w-[2.5rem] bg-primary/10 hover:bg-primary transition-all rounded-t-xl group-hover:shadow-[0_0_30px_rgba(34,197,94,0.3)] relative" style=${{ height: heightPercent + '%' }}>
383
- <div className="absolute -top-10 left-1/2 -translate-x-1/2 opacity-0 group-hover:opacity-100 transition-all bg-white text-[#020617] text-[10px] font-black px-2.5 py-1.5 rounded-xl pointer-events-none whitespace-nowrap z-20 shadow-xl translate-y-2 group-hover:translate-y-0">${data.count} msgs</div>
384
- </div>
385
- </div>
386
- <span className="text-[8px] font-black text-white/20 uppercase tracking-tighter truncate w-full text-center">${data.day.split('-').reverse().slice(0, 2).join('/')}</span>
387
- </div>
388
- `;
389
- })}
390
- </div>
391
- </div>
392
- `
393
- : null}
394
-
395
444
  <!-- Details Card -->
396
445
  <div className="p-8 lg:p-12 rounded-[3.5rem] bg-white/[0.03] border border-white/5 relative overflow-hidden">
397
446
  <div className="absolute top-0 right-0 w-64 h-64 bg-primary/5 blur-[80px] rounded-full -translate-y-1/2 translate-x-1/2"></div>
@@ -407,7 +456,7 @@ const UserApp = ({ config }) => {
407
456
  </div>
408
457
  <div className="space-y-1.5">
409
458
  <p className="text-[10px] font-black uppercase text-white/20 tracking-widest">WhatsApp Vinculado</p>
410
- <p className="text-lg font-bold text-primary">${summary?.owner_phone ? `+${formatPhone(summary.owner_phone)}` : 'Vincular Conta'}</p>
459
+ <p className="text-lg font-bold text-primary">${summary?.owner_phone ? formatPhone(summary.owner_phone) : 'Vincular Conta'}</p>
411
460
  </div>
412
461
  <div className="space-y-1.5">
413
462
  <p className="text-[10px] font-black uppercase text-white/20 tracking-widest">Última Conexão</p>
@@ -518,16 +567,13 @@ const UserApp = ({ config }) => {
518
567
  </div>
519
568
  <div className="p-8 rounded-[3rem] bg-white/[0.03] border border-white/5 space-y-8">
520
569
  <div className="space-y-4">
521
- <div className="space-y-2">
522
- <label className="text-[10px] font-black uppercase tracking-widest text-white/30 ml-4">Alterar Senha</label>
523
- <input type="password" placeholder="Sua nova senha forte" className="w-full h-14 bg-[#020617] border border-white/10 rounded-2xl px-6 focus:border-primary outline-none transition-all font-mono text-sm" />
524
- </div>
525
- <div className="space-y-2">
526
- <label className="text-[10px] font-black uppercase tracking-widest text-white/30 ml-4">Confirmar Senha</label>
527
- <input type="password" placeholder="Repita a nova senha" className="w-full h-14 bg-[#020617] border border-white/10 rounded-2xl px-6 focus:border-primary outline-none transition-all font-mono text-sm" />
528
- </div>
570
+ <p className="text-sm text-white/60 leading-relaxed">Para alterar sua senha com segurança, o OmniZap envia um código de verificação para o e-mail vinculado à sua conta.</p>
571
+ <div className="rounded-2xl border border-white/10 bg-white/[0.02] px-4 py-3 text-xs text-white/50">Fluxo: criar sessão de redefinição → receber código por e-mail → confirmar nova senha.</div>
529
572
  </div>
530
- <button className="btn btn-primary btn-block h-14 rounded-2xl font-black uppercase text-[10px] tracking-[0.2em] shadow-xl shadow-primary/20">Atualizar Credenciais</button>
573
+
574
+ <button type="button" className="btn btn-primary btn-block h-14 rounded-2xl font-black uppercase text-[10px] tracking-[0.2em] shadow-xl shadow-primary/20" disabled=${passwordResetBusy} onClick=${startPasswordResetFlow}>${passwordResetBusy ? 'Preparando redefinição...' : 'Alterar Senha com Verificação'}</button>
575
+
576
+ ${passwordResetInfo ? html` <div className="rounded-2xl border border-emerald-400/30 bg-emerald-500/10 px-4 py-3 text-xs font-semibold text-emerald-300">${passwordResetInfo}</div> ` : null} ${passwordResetError ? html` <div className="rounded-2xl border border-red-400/30 bg-red-500/10 px-4 py-3 text-xs font-semibold text-red-300">${passwordResetError}</div> ` : null}
531
577
  </div>
532
578
  </div>
533
579
  </div>
@@ -541,7 +587,7 @@ const UserApp = ({ config }) => {
541
587
  </div>
542
588
  <h3 className="text-3xl font-black tracking-tighter mb-4">Central de Atendimento</h3>
543
589
  <p className="text-white/40 font-medium leading-relaxed mb-10 text-lg">Dúvidas sobre o sistema, planos ou bugs? Fale diretamente com nossa equipe técnica.</p>
544
- <a href="https://wa.me/559591122954" target="_blank" className="btn btn-primary btn-lg btn-block rounded-[2rem] font-black uppercase text-xs tracking-widest h-16 shadow-2xl shadow-primary/20 hover:scale-[1.02] active:scale-95 transition-all">Iniciar Chat Suporte</a>
590
+ <a href=${supportWhatsappUrl} target="_blank" className="btn btn-primary btn-lg btn-block rounded-[2rem] font-black uppercase text-xs tracking-widest h-16 shadow-2xl shadow-primary/20 hover:scale-[1.02] active:scale-95 transition-all">Iniciar Chat Suporte</a>
545
591
  </div>
546
592
  `}
547
593
  </div>
@@ -567,7 +613,10 @@ if (rootElement) {
567
613
  const config = {
568
614
  apiBasePath: rootElement.dataset.apiBasePath || DEFAULT_API_BASE_PATH,
569
615
  loginPath: rootElement.dataset.loginPath || DEFAULT_LOGIN_PATH,
616
+ passwordResetWebPath: normalizeRoutePath(rootElement.dataset.passwordResetWebPath, DEFAULT_PASSWORD_RESET_WEB_PATH),
570
617
  fallbackAvatar: DEFAULT_FALLBACK_AVATAR,
618
+ supportWhatsappNumber: rootElement.dataset.supportWhatsappNumber || DEFAULT_SUPPORT_WHATSAPP_NUMBER,
619
+ supportWhatsappUrl: rootElement.dataset.supportWhatsappUrl || DEFAULT_SUPPORT_WHATSAPP_URL,
571
620
  };
572
621
  createRoot(rootElement).render(html`<${UserApp} config=${config} />`);
573
622
  }