blackcoffee2 2.1.0
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/CHANGELOG.md +664 -0
- package/LICENSE +201 -0
- package/NOTICE +25 -0
- package/README.md +246 -0
- package/apps.zip +0 -0
- package/bin/adminclient +105 -0
- package/bin/blackcoffee +133 -0
- package/cli/admin-users.js +282 -0
- package/cli/commands/app.js +561 -0
- package/cli/commands/config.js +182 -0
- package/cli/commands/db.js +257 -0
- package/cli/commands/server.js +200 -0
- package/config/applications.json +5 -0
- package/config/database.json +28 -0
- package/config/database.json.example +23 -0
- package/config/server.json +32 -0
- package/controllers/admin/AdminController.js +529 -0
- package/controllers/admin/AdminViewController.js +90 -0
- package/controllers/admin/AuthController.js +293 -0
- package/controllers/admin/DatabaseAdminController.js +218 -0
- package/core/SQLiteAdapter.js +333 -0
- package/core/appLoader.js +385 -0
- package/core/databasePoolManager.js +431 -0
- package/core/hotReload.js +363 -0
- package/data/ADMIN-README.md +145 -0
- package/data/CHANGELOG.md +48 -0
- package/data/GTK3-NODE-PROPOSALS.md +410 -0
- package/data/admin-db.js +150 -0
- package/data/admin-gui.js +452 -0
- package/data/blackcoffee_admin.db-shm +0 -0
- package/data/blackcoffee_admin.db-wal +0 -0
- package/data/migrations/001_create_admin_users.sql +33 -0
- package/docs/APP_HOOKS_HANDLER.md +432 -0
- package/docs/APP_HOOKS_REQUIREMENTS.md +588 -0
- package/docs/ARCHITECTURE.md +435 -0
- package/docs/CREAR_APP_Y_USAR_POOLS.md +1595 -0
- package/docs/EVENTS_APP_MANUAL.md +289 -0
- package/docs/INSITU_BINARY_UPLOAD_PROPOSAL.md +186 -0
- package/docs/INSITU_FIREWALL_EXCEPTION.md +187 -0
- package/docs/ROADMAP.md +242 -0
- package/docs/ROADMAP.md.backup +243 -0
- package/includes/404-hooks.js +423 -0
- package/includes/adminAuth.js +214 -0
- package/includes/adminExtension.js +53 -0
- package/includes/appHooks.js +302 -0
- package/includes/initAdminDb.js +115 -0
- package/includes/routeLoader.js +67 -0
- package/includes/sessions.js +223 -0
- package/issues/001-duplicate-module-loading.md +92 -0
- package/manuales/ADMIN_EXTENSION_COMMANDS_MANUAL.md +261 -0
- package/manuales/ADMIN_EXTENSION_HOOK_EXAMPLE.md +28 -0
- package/manuales/ADMIN_EXTENSION_INTEGRATION_MANUAL.md +232 -0
- package/manuales/CACHE_REGEX_COMMANDS.md +136 -0
- package/manuales/CACHE_SYSTEM_MAP.md +206 -0
- package/manuales/CREACION_DE_CONTROLADORES_INSITU.md +383 -0
- package/manuales/QUEUE_CLI_MODULE_MANUAL.md +289 -0
- package/manuales/QUEUE_SYSTEM_MANUAL.md +320 -0
- package/manuales/ROUTE_CACHE_MODULE_MANUAL.md +205 -0
- package/manuales/SESSION_MANAGER_GUIDE.md +529 -0
- package/manuales/SESSION_SECURITY_FLAGS.md +174 -0
- package/manuales/WAF_MODULE_MANUAL.md +229 -0
- package/manuales/after_route_handler_filter_example.md +116 -0
- package/manuales/after_route_handler_usage.md +130 -0
- package/manuales/an/303/241lisis-completo-insitu-framework.md +213 -0
- package/manuales/async_hooks_promises_guide.md +325 -0
- package/manuales/before_route_handler_filter_example.md +97 -0
- package/manuales/before_route_handler_usage.md +122 -0
- package/manuales/hooks_chaining_conditions_guide.md +261 -0
- package/manuales/hooks_filters_documentation.md +493 -0
- package/manuales/hooks_filters_documentation_en.md +493 -0
- package/manuales/hooks_vs_middlewares_comparison.md +87 -0
- package/manuales/manual-mvc-completo.md +934 -0
- package/manuales/modulos_administracion.md +89 -0
- package/manuales/router_execution_points.md +74 -0
- package/manuales/static_file_hooks_usage.md +222 -0
- package/models/AdminUserModel.js +132 -0
- package/package.json +45 -0
- package/programatically/PRoutes.js +89 -0
- package/programatically/initFlow.js +211 -0
- package/public/admin/css/db-pools.css +336 -0
- package/public/admin/css/styles.css +310 -0
- package/public/admin/database.html +312 -0
- package/public/admin/index.html +116 -0
- package/public/admin/js/app.js +470 -0
- package/public/admin/js/db-pools.js +253 -0
- package/public/admin/login.html +278 -0
- package/public/assets/css/styles.css +477 -0
- package/public/assets/js/main.js +89 -0
- package/public/index.html +136 -0
- package/public/templates/404.html +158 -0
- package/routes/admin-views.json +20 -0
- package/routes/admin.json +38 -0
- package/routes/auth.json +32 -0
- package/routes/static.json +18 -0
- package/server.js +299 -0
- package/test-aplicacion.con-logisession/BlackCoffee.js +226 -0
- package/test-aplicacion.con-logisession/SSL_SETUP.md +53 -0
- package/test-aplicacion.con-logisession/certs/ca-certificate.pem +32 -0
- package/test-aplicacion.con-logisession/certs/ca-private-key.pem +52 -0
- package/test-aplicacion.con-logisession/certs/certificate-2048.pem +22 -0
- package/test-aplicacion.con-logisession/certs/certificate.pem +32 -0
- package/test-aplicacion.con-logisession/certs/private-key-2048.pem +28 -0
- package/test-aplicacion.con-logisession/certs/private-key.pem +52 -0
- package/test-aplicacion.con-logisession/config/iaQueueSetup.js +84 -0
- package/test-aplicacion.con-logisession/config/qwen-rules.json +39 -0
- package/test-aplicacion.con-logisession/controllers/analyticsController.js +117 -0
- package/test-aplicacion.con-logisession/controllers/auth/AdminAuthController.js +142 -0
- package/test-aplicacion.con-logisession/controllers/auth/AuthController.js +439 -0
- package/test-aplicacion.con-logisession/controllers/auth/AuthViewController.js +223 -0
- package/test-aplicacion.con-logisession/controllers/endpointController.js +66 -0
- package/test-aplicacion.con-logisession/controllers/example.js +183 -0
- package/test-aplicacion.con-logisession/controllers/iaQueueController.js +367 -0
- package/test-aplicacion.con-logisession/controllers/queueController.js +206 -0
- package/test-aplicacion.con-logisession/controllers/qwenQueueController.js +197 -0
- package/test-aplicacion.con-logisession/controllers/test.js +0 -0
- package/test-aplicacion.con-logisession/controllers/tracking/EventsNoFinishController.js +78 -0
- package/test-aplicacion.con-logisession/controllers/tracking/TrackingController.js +412 -0
- package/test-aplicacion.con-logisession/controllers/tracking/TrackingControllerWithLoadModel.js +437 -0
- package/test-aplicacion.con-logisession/hooks/admin-hooks.js +20 -0
- package/test-aplicacion.con-logisession/hooks/general-hooks.js +97 -0
- package/test-aplicacion.con-logisession/hooks/queue-hooks.js +64 -0
- package/test-aplicacion.con-logisession/hooks/route-directory-hooks.js +38 -0
- package/test-aplicacion.con-logisession/hooks/security-hooks.js +24 -0
- package/test-aplicacion.con-logisession/insitu-admin-client/README.md +69 -0
- package/test-aplicacion.con-logisession/insitu-admin-client/package.json +23 -0
- package/test-aplicacion.con-logisession/insitu-admin-client.js +257 -0
- package/test-aplicacion.con-logisession/models/ExampleModel.js +88 -0
- package/test-aplicacion.con-logisession/models/QueueJobModel.js +263 -0
- package/test-aplicacion.con-logisession/models/TokenModel.js +207 -0
- package/test-aplicacion.con-logisession/models/auth/AuthModel.js +66 -0
- package/test-aplicacion.con-logisession/models/auth/UserModel.js +189 -0
- package/test-aplicacion.con-logisession/models/tracking/CompletedCartModel.js +213 -0
- package/test-aplicacion.con-logisession/models/tracking/EventModel.js +366 -0
- package/test-aplicacion.con-logisession/models/tracking/EventsNoFinishModel.js +131 -0
- package/test-aplicacion.con-logisession/models/tracking/SessionModel.js +360 -0
- package/test-aplicacion.con-logisession/models/tracking/SiteFlowModel.js +286 -0
- package/test-aplicacion.con-logisession/models/tracking/TokenModel.js +207 -0
- package/test-aplicacion.con-logisession/package-lock.json +3313 -0
- package/test-aplicacion.con-logisession/package.json +32 -0
- package/test-aplicacion.con-logisession/public/blackcoffee-welcome/index.html +1339 -0
- package/test-aplicacion.con-logisession/public/css/style.css +64 -0
- package/test-aplicacion.con-logisession/public/ejemplo-estatica/index.html +18 -0
- package/test-aplicacion.con-logisession/public/ejemplo-estatica/script.js +16 -0
- package/test-aplicacion.con-logisession/public/ejemplo-estatica/styles.css +43 -0
- package/test-aplicacion.con-logisession/public/images/logo.svg +7 -0
- package/test-aplicacion.con-logisession/public/js/main.js +67 -0
- package/test-aplicacion.con-logisession/routes/analytics-routes.json +8 -0
- package/test-aplicacion.con-logisession/routes/auth-routes.json +98 -0
- package/test-aplicacion.con-logisession/routes/blackcoffee-welcome-routes.json +20 -0
- package/test-aplicacion.con-logisession/routes/duplicate-test-routes.json.disabled +16 -0
- package/test-aplicacion.con-logisession/routes/ejemplo-estatica-routes.json +11 -0
- package/test-aplicacion.con-logisession/routes/endpoints-routes.json +8 -0
- package/test-aplicacion.con-logisession/routes/ia-queue-routes.json +26 -0
- package/test-aplicacion.con-logisession/routes/product-routes.json.disabled +20 -0
- package/test-aplicacion.con-logisession/routes/queue-routes.json +32 -0
- package/test-aplicacion.con-logisession/routes/qwen-routes.json +14 -0
- package/test-aplicacion.con-logisession/routes/static-routes.json +29 -0
- package/test-aplicacion.con-logisession/routes/tracking-routes.json +58 -0
- package/test-aplicacion.con-logisession/routes/tracking-with-loadmodel-routes.json +51 -0
- package/test-aplicacion.con-logisession/utils/dbAdapter.js +88 -0
- package/test-aplicacion.con-logisession/utils/qbWrapper.js +4 -0
- package/test-aplicacion.con-logisession/utils/queueProcessor.js +305 -0
- package/test-aplicacion.con-logisession/utils/qwenRulesService.js +131 -0
- package/test-aplicacion.con-logisession/utils/tokenHelper.js +22 -0
- package/test-aplicacion.con-logisession/views/auth/dashboard.html +443 -0
- package/test-aplicacion.con-logisession/views/auth/forgot-password.html +200 -0
- package/test-aplicacion.con-logisession/views/auth/login.html +213 -0
- package/test-aplicacion.con-logisession/views/auth/register.html +294 -0
- package/test-aplicacion.con-logisession/views/contact/form.html +47 -0
- package/test-aplicacion.con-logisession/views/products/index.html +39 -0
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="es">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>{{title}} - BlackCoffee</title>
|
|
7
|
+
<style>
|
|
8
|
+
* {
|
|
9
|
+
margin: 0;
|
|
10
|
+
padding: 0;
|
|
11
|
+
box-sizing: border-box;
|
|
12
|
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
body {
|
|
16
|
+
background-color: #f5f5f5;
|
|
17
|
+
color: #333;
|
|
18
|
+
line-height: 1.6;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.navbar {
|
|
22
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
23
|
+
padding: 1rem 2rem;
|
|
24
|
+
display: flex;
|
|
25
|
+
justify-content: space-between;
|
|
26
|
+
align-items: center;
|
|
27
|
+
color: white;
|
|
28
|
+
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.navbar-brand {
|
|
32
|
+
font-size: 1.5rem;
|
|
33
|
+
font-weight: bold;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.navbar-user {
|
|
37
|
+
display: flex;
|
|
38
|
+
align-items: center;
|
|
39
|
+
gap: 1rem;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.user-info {
|
|
43
|
+
text-align: right;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.user-name {
|
|
47
|
+
font-weight: bold;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.user-email {
|
|
51
|
+
font-size: 0.9rem;
|
|
52
|
+
opacity: 0.8;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.container {
|
|
56
|
+
max-width: 1200px;
|
|
57
|
+
margin: 2rem auto;
|
|
58
|
+
padding: 0 2rem;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.welcome-section {
|
|
62
|
+
background: white;
|
|
63
|
+
border-radius: 10px;
|
|
64
|
+
padding: 2rem;
|
|
65
|
+
box-shadow: 0 5px 15px rgba(0,0,0,0.05);
|
|
66
|
+
margin-bottom: 2rem;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.welcome-section h1 {
|
|
70
|
+
color: #333;
|
|
71
|
+
margin-bottom: 1rem;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.user-details {
|
|
75
|
+
display: grid;
|
|
76
|
+
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
77
|
+
gap: 1.5rem;
|
|
78
|
+
margin-top: 1.5rem;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.detail-card {
|
|
82
|
+
background: #f8f9fa;
|
|
83
|
+
border-radius: 8px;
|
|
84
|
+
padding: 1.5rem;
|
|
85
|
+
border-left: 4px solid #667eea;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.detail-card h3 {
|
|
89
|
+
color: #555;
|
|
90
|
+
margin-bottom: 0.5rem;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.detail-card ul {
|
|
94
|
+
list-style-type: none;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.detail-card li {
|
|
98
|
+
padding: 0.25rem 0;
|
|
99
|
+
color: #666;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.capabilities-section {
|
|
103
|
+
background: white;
|
|
104
|
+
border-radius: 10px;
|
|
105
|
+
padding: 2rem;
|
|
106
|
+
box-shadow: 0 5px 15px rgba(0,0,0,0.05);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.capabilities-section h2 {
|
|
110
|
+
color: #333;
|
|
111
|
+
margin-bottom: 1rem;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.capabilities-grid {
|
|
115
|
+
display: grid;
|
|
116
|
+
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
|
117
|
+
gap: 1rem;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.capability-card {
|
|
121
|
+
background: #e8f4ff;
|
|
122
|
+
border-radius: 8px;
|
|
123
|
+
padding: 1rem;
|
|
124
|
+
border-left: 4px solid #2b7bd6;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.capability-name {
|
|
128
|
+
font-weight: bold;
|
|
129
|
+
color: #2b7bd6;
|
|
130
|
+
margin-bottom: 0.25rem;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.capability-desc {
|
|
134
|
+
font-size: 0.9rem;
|
|
135
|
+
color: #555;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.logout-btn {
|
|
139
|
+
background: #dc3545;
|
|
140
|
+
color: white;
|
|
141
|
+
border: none;
|
|
142
|
+
padding: 0.5rem 1rem;
|
|
143
|
+
border-radius: 5px;
|
|
144
|
+
cursor: pointer;
|
|
145
|
+
font-weight: 500;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.logout-btn:hover {
|
|
149
|
+
background: #c82333;
|
|
150
|
+
}
|
|
151
|
+
</style>
|
|
152
|
+
</head>
|
|
153
|
+
<body>
|
|
154
|
+
<nav class="navbar">
|
|
155
|
+
<div class="navbar-brand">BlackCoffee Dashboard</div>
|
|
156
|
+
<div class="navbar-user">
|
|
157
|
+
<div class="user-info">
|
|
158
|
+
<div class="user-name" id="user-username">{{user.username}}</div>
|
|
159
|
+
<div class="user-email" id="user-email">{{user.email}}</div>
|
|
160
|
+
</div>
|
|
161
|
+
<button class="logout-btn" onclick="logout()">Cerrar Sesión</button>
|
|
162
|
+
</div>
|
|
163
|
+
</nav>
|
|
164
|
+
|
|
165
|
+
<div class="container">
|
|
166
|
+
<section class="welcome-section">
|
|
167
|
+
<h1 id="welcome-message">¡Bienvenido, {{user.username}}!</h1>
|
|
168
|
+
<p>Has iniciado sesión correctamente en el sistema. Aquí tienes un resumen de tu información y permisos.</p>
|
|
169
|
+
|
|
170
|
+
<div class="user-details">
|
|
171
|
+
<div class="detail-card">
|
|
172
|
+
<h3>Información del Usuario</h3>
|
|
173
|
+
<ul>
|
|
174
|
+
<li><strong>ID:</strong> <span id="user-id">{{user.id}}</span></li>
|
|
175
|
+
<li><strong>Nombre de usuario:</strong> <span id="user-username-detail">{{user.username}}</span></li>
|
|
176
|
+
<li><strong>Email:</strong> <span id="user-email-detail">{{user.email}}</span></li>
|
|
177
|
+
</ul>
|
|
178
|
+
</div>
|
|
179
|
+
|
|
180
|
+
<div class="detail-card user-roles">
|
|
181
|
+
<h3>Roles Asignados</h3>
|
|
182
|
+
{{if user.roles}}
|
|
183
|
+
<ul>
|
|
184
|
+
{{foreach:user.roles}}
|
|
185
|
+
<li>{{name}} - {{description}}</li>
|
|
186
|
+
{{endfor}}
|
|
187
|
+
</ul>
|
|
188
|
+
{{else}}
|
|
189
|
+
<p>No tienes roles asignados.</p>
|
|
190
|
+
{{endif}}
|
|
191
|
+
</div>
|
|
192
|
+
|
|
193
|
+
<div class="detail-card">
|
|
194
|
+
<h3>Capacities Directas</h3>
|
|
195
|
+
{{if user.directCapabilities}}
|
|
196
|
+
<ul>
|
|
197
|
+
{{foreach:user.directCapabilities}}
|
|
198
|
+
<li>{{name}} - {{description}}</li>
|
|
199
|
+
{{endfor}}
|
|
200
|
+
</ul>
|
|
201
|
+
{{else}}
|
|
202
|
+
<p>No tienes capabilities directas asignadas.</p>
|
|
203
|
+
{{endif}}
|
|
204
|
+
</div>
|
|
205
|
+
</div>
|
|
206
|
+
</section>
|
|
207
|
+
|
|
208
|
+
<section class="capabilities-section">
|
|
209
|
+
<h2>Tus Capacities</h2>
|
|
210
|
+
<p>Aquí están todas las capacidades que tienes disponibles, ya sea directamente o a través de tus roles:</p>
|
|
211
|
+
|
|
212
|
+
<div class="capabilities-grid">
|
|
213
|
+
{{if user.allCapabilities}}
|
|
214
|
+
{{foreach:user.allCapabilities}}
|
|
215
|
+
<div class="capability-card">
|
|
216
|
+
<div class="capability-name">{{name}}</div>
|
|
217
|
+
<div class="capability-desc">{{description}}</div>
|
|
218
|
+
{{if resource}}<div><small>Recurso: {{resource}}</small></div>{{endif}}
|
|
219
|
+
{{if action}}<div><small>Acción: {{action}}</small></div>{{endif}}
|
|
220
|
+
</div>
|
|
221
|
+
{{endfor}}
|
|
222
|
+
{{else}}
|
|
223
|
+
<p>No tienes capacidades asignadas.</p>
|
|
224
|
+
{{endif}}
|
|
225
|
+
</div>
|
|
226
|
+
</section>
|
|
227
|
+
|
|
228
|
+
<!-- Sección para datos de carros abandonados -->
|
|
229
|
+
<section class="abandoned-carts-section">
|
|
230
|
+
<h2>Carros Abandonados</h2>
|
|
231
|
+
<p>Datos obtenidos del endpoint protegido de carros completados mediante AJAX:</p>
|
|
232
|
+
|
|
233
|
+
<div id="loadingIndicator" style="display: none; text-align: center; padding: 20px;">
|
|
234
|
+
<p>Cargando datos de carros abandonados...</p>
|
|
235
|
+
</div>
|
|
236
|
+
|
|
237
|
+
<div id="abandonedCartsData">
|
|
238
|
+
<p id="noDataMessage">No se han cargado datos aún.</p>
|
|
239
|
+
<div id="cartList" style="display: none;"></div>
|
|
240
|
+
</div>
|
|
241
|
+
</section>
|
|
242
|
+
</div>
|
|
243
|
+
|
|
244
|
+
<script>
|
|
245
|
+
// Verificar si hay un token válido en la sesión o en localStorage al cargar la página
|
|
246
|
+
document.addEventListener('DOMContentLoaded', async function() {
|
|
247
|
+
// Intentar obtener el token de la sesión o de localStorage
|
|
248
|
+
let token = localStorage.getItem('authToken');
|
|
249
|
+
|
|
250
|
+
// Si no hay token en localStorage, intentar obtenerlo de otro lugar
|
|
251
|
+
// En una implementación real, el token podría haber sido pasado en la vista
|
|
252
|
+
// o estar disponible en una variable JavaScript
|
|
253
|
+
|
|
254
|
+
// Si no hay token, redirigir al login
|
|
255
|
+
if (!token) {
|
|
256
|
+
// Intentar obtener el token de una variable global o de la sesión
|
|
257
|
+
// En este caso, asumiremos que el token se pasa en la vista como variable
|
|
258
|
+
// Si no está disponible, redirigir al login
|
|
259
|
+
window.location.href = '/login';
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
try {
|
|
264
|
+
const response = await fetch('/api/auth/profile', {
|
|
265
|
+
headers: {
|
|
266
|
+
'Authorization': `Bearer ${token}`
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
if (response.status === 401) {
|
|
271
|
+
// Token no válido, redirigir al login
|
|
272
|
+
window.location.href = '/login';
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const data = await response.json();
|
|
277
|
+
if (data.success && data.user) {
|
|
278
|
+
// Actualizar la interfaz con la información del usuario
|
|
279
|
+
document.querySelector('.user-name').textContent = data.user.username;
|
|
280
|
+
document.querySelector('.user-email').textContent = data.user.email;
|
|
281
|
+
|
|
282
|
+
// Actualizar otros elementos con la información del usuario
|
|
283
|
+
document.querySelector('h1').textContent = `¡Bienvenido, ${data.user.username}!`;
|
|
284
|
+
document.getElementById('user-id').textContent = data.user.id;
|
|
285
|
+
document.getElementById('user-username').textContent = data.user.username;
|
|
286
|
+
document.getElementById('user-email').textContent = data.user.email;
|
|
287
|
+
|
|
288
|
+
// Actualizar roles
|
|
289
|
+
const rolesContainer = document.querySelector('.user-roles ul');
|
|
290
|
+
if (rolesContainer && data.user.roles && data.user.roles.length > 0) {
|
|
291
|
+
rolesContainer.innerHTML = '';
|
|
292
|
+
data.user.roles.forEach(role => {
|
|
293
|
+
const li = document.createElement('li');
|
|
294
|
+
li.textContent = `${role.name} - ${role.description}`;
|
|
295
|
+
rolesContainer.appendChild(li);
|
|
296
|
+
});
|
|
297
|
+
} else if (rolesContainer) {
|
|
298
|
+
rolesContainer.innerHTML = '<li>No tienes roles asignados.</li>';
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Actualizar capabilities
|
|
302
|
+
const capsContainer = document.querySelector('.capabilities-grid');
|
|
303
|
+
if (capsContainer && data.user.allCapabilities && data.user.allCapabilities.length > 0) {
|
|
304
|
+
capsContainer.innerHTML = '';
|
|
305
|
+
data.user.allCapabilities.forEach(cap => {
|
|
306
|
+
const capCard = document.createElement('div');
|
|
307
|
+
capCard.className = 'capability-card';
|
|
308
|
+
capCard.innerHTML = `
|
|
309
|
+
<div class="capability-name">${cap.name}</div>
|
|
310
|
+
<div class="capability-desc">${cap.description}</div>
|
|
311
|
+
${cap.resource ? `<div><small>Recurso: ${cap.resource}</small></div>` : ''}
|
|
312
|
+
${cap.action ? `<div><small>Acción: ${cap.action}</small></div>` : ''}
|
|
313
|
+
`;
|
|
314
|
+
capsContainer.appendChild(capCard);
|
|
315
|
+
});
|
|
316
|
+
} else if (capsContainer) {
|
|
317
|
+
capsContainer.innerHTML = '<p>No tienes capacidades asignadas.</p>';
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Cargar datos de carros abandonados
|
|
321
|
+
loadAbandonedCarts(token);
|
|
322
|
+
} else {
|
|
323
|
+
// Token no válido, redirigir al login
|
|
324
|
+
window.location.href = '/login';
|
|
325
|
+
}
|
|
326
|
+
} catch (error) {
|
|
327
|
+
console.error('Error verificando la sesión:', error);
|
|
328
|
+
// En caso de error de red, también redirigir al login
|
|
329
|
+
window.location.href = '/login';
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
// Función para cargar datos de carros abandonados
|
|
334
|
+
async function loadAbandonedCarts(token) {
|
|
335
|
+
const loadingIndicator = document.getElementById('loadingIndicator');
|
|
336
|
+
const cartList = document.getElementById('cartList');
|
|
337
|
+
const noDataMessage = document.getElementById('noDataMessage');
|
|
338
|
+
|
|
339
|
+
try {
|
|
340
|
+
// Mostrar indicador de carga
|
|
341
|
+
loadingIndicator.style.display = 'block';
|
|
342
|
+
noDataMessage.style.display = 'none';
|
|
343
|
+
|
|
344
|
+
// Hacer la solicitud al endpoint protegido
|
|
345
|
+
const response = await fetch('/api/tracking/completed-carts', {
|
|
346
|
+
headers: {
|
|
347
|
+
'Authorization': `Bearer ${token}`
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
if (response.status === 401) {
|
|
352
|
+
// Token no válido, redirigir al login
|
|
353
|
+
alert('Tu sesión ha expirado. Por favor, inicia sesión nuevamente.');
|
|
354
|
+
window.location.href = '/login';
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const data = await response.json();
|
|
359
|
+
|
|
360
|
+
if (data.rows && data.rows.length > 0) {
|
|
361
|
+
// Crear contenido para mostrar los carros abandonados
|
|
362
|
+
let cartItemsHtml = '<ul class="cart-list">';
|
|
363
|
+
|
|
364
|
+
data.rows.slice(0, 10).forEach(cart => { // Mostrar solo los primeros 10
|
|
365
|
+
cartItemsHtml += `
|
|
366
|
+
<li class="cart-item">
|
|
367
|
+
<strong>ID:</strong> ${cart.id} |
|
|
368
|
+
<strong>Session:</strong> ${cart.session_id} |
|
|
369
|
+
<strong>Fecha:</strong> ${new Date(cart.created_at).toLocaleString()} |
|
|
370
|
+
<strong>URL:</strong> ${cart.site_url}
|
|
371
|
+
</li>
|
|
372
|
+
`;
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
cartItemsHtml += '</ul>';
|
|
376
|
+
|
|
377
|
+
cartList.innerHTML = cartItemsHtml;
|
|
378
|
+
cartList.style.display = 'block';
|
|
379
|
+
} else {
|
|
380
|
+
noDataMessage.textContent = 'No se encontraron carros abandonados.';
|
|
381
|
+
noDataMessage.style.display = 'block';
|
|
382
|
+
}
|
|
383
|
+
} catch (error) {
|
|
384
|
+
console.error('Error cargando carros abandonados:', error);
|
|
385
|
+
noDataMessage.textContent = 'Error al cargar los datos de carros abandonados.';
|
|
386
|
+
noDataMessage.style.display = 'block';
|
|
387
|
+
} finally {
|
|
388
|
+
// Ocultar indicador de carga
|
|
389
|
+
loadingIndicator.style.display = 'none';
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
async function logout() {
|
|
394
|
+
// Obtener el token de localStorage
|
|
395
|
+
const token = localStorage.getItem('authToken');
|
|
396
|
+
|
|
397
|
+
if (token) {
|
|
398
|
+
try {
|
|
399
|
+
// Llamar al endpoint de logout para revocar el token
|
|
400
|
+
await fetch('/api/auth/logout', {
|
|
401
|
+
method: 'POST',
|
|
402
|
+
headers: {
|
|
403
|
+
'Authorization': `Bearer ${token}`,
|
|
404
|
+
'Content-Type': 'application/json'
|
|
405
|
+
}
|
|
406
|
+
});
|
|
407
|
+
} catch (error) {
|
|
408
|
+
console.error('Error en logout API:', error);
|
|
409
|
+
// Continuar con el logout incluso si falla la llamada API
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Eliminar el token de localStorage
|
|
414
|
+
localStorage.removeItem('authToken');
|
|
415
|
+
|
|
416
|
+
// Redirigir al login
|
|
417
|
+
window.location.href = '/login';
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Verificar periodicamente si el token sigue siendo válido
|
|
421
|
+
setInterval(async () => {
|
|
422
|
+
const token = localStorage.getItem('authToken');
|
|
423
|
+
if (token) {
|
|
424
|
+
try {
|
|
425
|
+
const response = await fetch('/api/auth/profile', {
|
|
426
|
+
headers: {
|
|
427
|
+
'Authorization': `Bearer ${token}`
|
|
428
|
+
}
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
if (response.status === 401) {
|
|
432
|
+
// Token no válido, redirigir al login
|
|
433
|
+
alert('Tu sesión ha expirado. Por favor, inicia sesión nuevamente.');
|
|
434
|
+
logout();
|
|
435
|
+
}
|
|
436
|
+
} catch (error) {
|
|
437
|
+
console.error('Error verificando la sesión:', error);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}, 300000); // Verificar cada 5 minutos
|
|
441
|
+
</script>
|
|
442
|
+
</body>
|
|
443
|
+
</html>
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="es">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Recuperar Contraseña - BlackCoffee</title>
|
|
7
|
+
<style>
|
|
8
|
+
* {
|
|
9
|
+
margin: 0;
|
|
10
|
+
padding: 0;
|
|
11
|
+
box-sizing: border-box;
|
|
12
|
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
body {
|
|
16
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
17
|
+
min-height: 100vh;
|
|
18
|
+
display: flex;
|
|
19
|
+
align-items: center;
|
|
20
|
+
justify-content: center;
|
|
21
|
+
padding: 20px;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.forgot-container {
|
|
25
|
+
background: white;
|
|
26
|
+
padding: 40px;
|
|
27
|
+
border-radius: 10px;
|
|
28
|
+
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.1);
|
|
29
|
+
width: 100%;
|
|
30
|
+
max-width: 450px;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.forgot-header {
|
|
34
|
+
text-align: center;
|
|
35
|
+
margin-bottom: 30px;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.forgot-header h1 {
|
|
39
|
+
color: #333;
|
|
40
|
+
font-size: 28px;
|
|
41
|
+
margin-bottom: 10px;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.forgot-header p {
|
|
45
|
+
color: #666;
|
|
46
|
+
font-size: 14px;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.form-group {
|
|
50
|
+
margin-bottom: 20px;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.form-group label {
|
|
54
|
+
display: block;
|
|
55
|
+
margin-bottom: 8px;
|
|
56
|
+
color: #333;
|
|
57
|
+
font-weight: 500;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.form-group input {
|
|
61
|
+
width: 100%;
|
|
62
|
+
padding: 12px 15px;
|
|
63
|
+
border: 2px solid #e1e1e1;
|
|
64
|
+
border-radius: 5px;
|
|
65
|
+
font-size: 16px;
|
|
66
|
+
transition: border-color 0.3s ease;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.form-group input:focus {
|
|
70
|
+
outline: none;
|
|
71
|
+
border-color: #667eea;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.btn {
|
|
75
|
+
width: 100%;
|
|
76
|
+
padding: 12px;
|
|
77
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
78
|
+
color: white;
|
|
79
|
+
border: none;
|
|
80
|
+
border-radius: 5px;
|
|
81
|
+
font-size: 16px;
|
|
82
|
+
font-weight: 600;
|
|
83
|
+
cursor: pointer;
|
|
84
|
+
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.btn:hover {
|
|
88
|
+
transform: translateY(-2px);
|
|
89
|
+
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.btn:active {
|
|
93
|
+
transform: translateY(0);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.forgot-footer {
|
|
97
|
+
text-align: center;
|
|
98
|
+
margin-top: 20px;
|
|
99
|
+
color: #666;
|
|
100
|
+
font-size: 14px;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.forgot-footer a {
|
|
104
|
+
color: #667eea;
|
|
105
|
+
text-decoration: none;
|
|
106
|
+
font-weight: 500;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.forgot-footer a:hover {
|
|
110
|
+
text-decoration: underline;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.error-message {
|
|
114
|
+
display: none;
|
|
115
|
+
background: #fee;
|
|
116
|
+
color: #c33;
|
|
117
|
+
padding: 10px;
|
|
118
|
+
border-radius: 5px;
|
|
119
|
+
margin-bottom: 15px;
|
|
120
|
+
text-align: center;
|
|
121
|
+
font-size: 14px;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.success-message {
|
|
125
|
+
display: none;
|
|
126
|
+
background: #efe;
|
|
127
|
+
color: #363;
|
|
128
|
+
padding: 10px;
|
|
129
|
+
border-radius: 5px;
|
|
130
|
+
margin-bottom: 15px;
|
|
131
|
+
text-align: center;
|
|
132
|
+
font-size: 14px;
|
|
133
|
+
}
|
|
134
|
+
</style>
|
|
135
|
+
</head>
|
|
136
|
+
<body>
|
|
137
|
+
<div class="forgot-container">
|
|
138
|
+
<div class="forgot-header">
|
|
139
|
+
<h1>Recuperar Contraseña</h1>
|
|
140
|
+
<p>Ingresa tu email para recibir instrucciones</p>
|
|
141
|
+
</div>
|
|
142
|
+
|
|
143
|
+
<div id="errorMessage" class="error-message"></div>
|
|
144
|
+
<div id="successMessage" class="success-message"></div>
|
|
145
|
+
|
|
146
|
+
<form id="forgotForm">
|
|
147
|
+
<div class="form-group">
|
|
148
|
+
<label for="email">Email</label>
|
|
149
|
+
<input type="email" id="email" name="email" required placeholder="Ingresa tu email registrado">
|
|
150
|
+
</div>
|
|
151
|
+
|
|
152
|
+
<button type="submit" class="btn">Enviar Instrucciones</button>
|
|
153
|
+
</form>
|
|
154
|
+
|
|
155
|
+
<div class="forgot-footer">
|
|
156
|
+
<p><a href="/login">Volver al inicio de sesión</a></p>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
|
|
160
|
+
<script>
|
|
161
|
+
document.getElementById('forgotForm').addEventListener('submit', async (e) => {
|
|
162
|
+
e.preventDefault();
|
|
163
|
+
|
|
164
|
+
const email = document.getElementById('email').value;
|
|
165
|
+
const errorMessage = document.getElementById('errorMessage');
|
|
166
|
+
const successMessage = document.getElementById('successMessage');
|
|
167
|
+
|
|
168
|
+
// Validar email
|
|
169
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
170
|
+
if (!emailRegex.test(email)) {
|
|
171
|
+
errorMessage.textContent = 'Por favor, ingresa un email válido';
|
|
172
|
+
errorMessage.style.display = 'block';
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Ocultar mensajes anteriores
|
|
177
|
+
errorMessage.style.display = 'none';
|
|
178
|
+
successMessage.style.display = 'none';
|
|
179
|
+
|
|
180
|
+
try {
|
|
181
|
+
// Simular envío de solicitud de recuperación de contraseña
|
|
182
|
+
// En una implementación real, esto haría una llamada a una API
|
|
183
|
+
|
|
184
|
+
// Mostrar mensaje de éxito simulado
|
|
185
|
+
successMessage.textContent = 'Instrucciones de recuperación enviadas a tu email. Revisa tu bandeja de entrada.';
|
|
186
|
+
successMessage.style.display = 'block';
|
|
187
|
+
|
|
188
|
+
// Limpiar el formulario
|
|
189
|
+
document.getElementById('email').value = '';
|
|
190
|
+
|
|
191
|
+
console.log('Solicitud de recuperación de contraseña para:', email);
|
|
192
|
+
} catch (error) {
|
|
193
|
+
console.error('Error en la recuperación de contraseña:', error);
|
|
194
|
+
errorMessage.textContent = 'Error de conexión. Por favor, inténtalo de nuevo.';
|
|
195
|
+
errorMessage.style.display = 'block';
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
</script>
|
|
199
|
+
</body>
|
|
200
|
+
</html>
|