mbkauthe 4.7.0 → 4.7.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/docs/db.sql +4 -1
- package/index.js +4 -0
- package/lib/config/cookies.js +2 -0
- package/lib/pool.js +5 -184
- package/lib/routes/auth.js +14 -8
- package/lib/routes/dbLogs.js +42 -8
- package/lib/routes/misc.js +17 -13
- package/lib/utils/dbQueryLogger.js +247 -0
- package/package.json +33 -4
- package/public/main.css +947 -0
- package/public/main.js +6 -2
- package/views/head.handlebars +12 -0
- package/views/pages/accountSwitch.handlebars +37 -16
- package/views/pages/dbLogs.handlebars +379 -152
- package/views/{errorCodes.handlebars → pages/errorCodes.handlebars} +30 -26
- package/views/pages/info_mbkauthe.handlebars +15 -15
- package/views/pages/loginmbkauthe.handlebars +13 -9
- package/views/pages/test.handlebars +27 -15
- package/views/profilemenu.handlebars +53 -57
- package/views/sharedStyles.handlebars +1 -895
- package/views/showmessage.handlebars +52 -30
- package/views/versionInfo.handlebars +2 -2
|
@@ -10,18 +10,18 @@
|
|
|
10
10
|
padding: 120px 1.7rem 40px;
|
|
11
11
|
position: relative;
|
|
12
12
|
overflow: hidden;
|
|
13
|
-
background: radial-gradient(circle at top right,
|
|
14
|
-
radial-gradient(circle at 20% 20%,
|
|
15
|
-
linear-gradient(135deg, var(--
|
|
13
|
+
background: radial-gradient(circle at top right, color-mix(in srgb, var(--primary) 25%, transparent 75%), transparent 55%),
|
|
14
|
+
radial-gradient(circle at 20% 20%, color-mix(in srgb, var(--accent) 18%, transparent 82%), transparent 50%),
|
|
15
|
+
linear-gradient(135deg, var(--hero-from), var(--hero-to));
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
.db-panel {
|
|
19
|
-
background:
|
|
19
|
+
background: color-mix(in srgb, var(--glass-bg) 96%, transparent 4%);
|
|
20
20
|
border-radius: 18px;
|
|
21
21
|
padding: 2.25rem;
|
|
22
22
|
width: min(1200px, 100%);
|
|
23
23
|
box-shadow: 0 18px 48px rgba(0, 0, 0, 0.45);
|
|
24
|
-
border: 1px solid
|
|
24
|
+
border: 1px solid color-mix(in srgb, var(--accent) 20%, transparent 80%);
|
|
25
25
|
position: relative;
|
|
26
26
|
z-index: 2;
|
|
27
27
|
overflow: hidden;
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
content: '';
|
|
32
32
|
position: absolute;
|
|
33
33
|
inset: 0;
|
|
34
|
-
background: linear-gradient(120deg,
|
|
34
|
+
background: linear-gradient(120deg, color-mix(in srgb, var(--primary) 8%, transparent 92%), transparent 55%);
|
|
35
35
|
pointer-events: none;
|
|
36
36
|
}
|
|
37
37
|
|
|
@@ -65,15 +65,15 @@
|
|
|
65
65
|
.db-link {
|
|
66
66
|
padding: 0.45rem 0.85rem;
|
|
67
67
|
border-radius: 0.6rem;
|
|
68
|
-
border: 1px solid
|
|
69
|
-
background:
|
|
68
|
+
border: 1px solid color-mix(in srgb, var(--primary) 45%, transparent 55%);
|
|
69
|
+
background: color-mix(in srgb, var(--primary) 14%, transparent 86%);
|
|
70
70
|
color: var(--light);
|
|
71
71
|
text-decoration: none;
|
|
72
72
|
transition: var(--transition);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
.db-link:hover {
|
|
76
|
-
background:
|
|
76
|
+
background: color-mix(in srgb, var(--primary) 24%, transparent 76%);
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
.db-card {
|
|
@@ -86,8 +86,8 @@
|
|
|
86
86
|
.stat-box {
|
|
87
87
|
padding: 1rem 1.2rem;
|
|
88
88
|
border-radius: 14px;
|
|
89
|
-
border: 1px solid
|
|
90
|
-
background:
|
|
89
|
+
border: 1px solid color-mix(in srgb, var(--accent) 25%, transparent 75%);
|
|
90
|
+
background: var(--surface-muted);
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
.stat-label {
|
|
@@ -119,69 +119,184 @@
|
|
|
119
119
|
width: 5rem;
|
|
120
120
|
padding: 0.35rem 0.5rem;
|
|
121
121
|
border-radius: 0.5rem;
|
|
122
|
-
border: 1px solid
|
|
123
|
-
background:
|
|
122
|
+
border: 1px solid var(--muted-border);
|
|
123
|
+
background: var(--input-bg);
|
|
124
124
|
color: var(--text);
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
.db-toolbar button {
|
|
128
128
|
padding: 0.45rem 0.85rem;
|
|
129
129
|
border-radius: 0.6rem;
|
|
130
|
-
border: 1px solid
|
|
131
|
-
background:
|
|
130
|
+
border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent 55%);
|
|
131
|
+
background: color-mix(in srgb, var(--accent) 14%, transparent 86%);
|
|
132
132
|
color: var(--light);
|
|
133
133
|
cursor: pointer;
|
|
134
134
|
transition: var(--transition);
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
.db-toolbar button:hover {
|
|
138
|
-
background:
|
|
138
|
+
background: color-mix(in srgb, var(--accent) 24%, transparent 76%);
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
.db-table-wrapper {
|
|
142
142
|
border-radius: 16px;
|
|
143
|
-
border: 1px solid
|
|
144
|
-
background:
|
|
145
|
-
padding: 0.
|
|
146
|
-
overflow
|
|
143
|
+
border: 1px solid var(--muted-border);
|
|
144
|
+
background: color-mix(in srgb, var(--surface-1) 82%, transparent 18%);
|
|
145
|
+
padding: 0.75rem;
|
|
146
|
+
overflow: hidden;
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
.
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
149
|
+
.log-list {
|
|
150
|
+
display: flex;
|
|
151
|
+
flex-direction: column;
|
|
152
|
+
gap: 0.85rem;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.log-card {
|
|
156
|
+
border: 1px solid var(--muted-border);
|
|
157
|
+
border-radius: 12px;
|
|
158
|
+
background: var(--surface-muted);
|
|
159
|
+
padding: 0.85rem;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.log-head {
|
|
163
|
+
display: flex;
|
|
164
|
+
align-items: center;
|
|
165
|
+
justify-content: space-between;
|
|
166
|
+
gap: 0.8rem;
|
|
167
|
+
margin-bottom: 0.75rem;
|
|
168
|
+
flex-wrap: wrap;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.log-mainline {
|
|
172
|
+
display: inline-flex;
|
|
173
|
+
align-items: center;
|
|
174
|
+
gap: 0.45rem;
|
|
175
|
+
flex-wrap: wrap;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.log-index {
|
|
156
179
|
font-size: 0.75rem;
|
|
157
|
-
|
|
180
|
+
color: var(--text-light);
|
|
181
|
+
border: 1px solid var(--muted-border);
|
|
182
|
+
border-radius: 999px;
|
|
183
|
+
padding: 0.1rem 0.45rem;
|
|
158
184
|
}
|
|
159
185
|
|
|
160
|
-
.
|
|
161
|
-
|
|
186
|
+
.log-time {
|
|
187
|
+
color: var(--text-light);
|
|
188
|
+
font-size: 0.8rem;
|
|
162
189
|
}
|
|
163
190
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
border-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
191
|
+
.log-query-block {
|
|
192
|
+
border: 1px solid var(--muted-border);
|
|
193
|
+
border-radius: 10px;
|
|
194
|
+
padding: 0.55rem;
|
|
195
|
+
margin-bottom: 0.65rem;
|
|
196
|
+
background: color-mix(in srgb, var(--surface-1) 50%, transparent 50%);
|
|
170
197
|
}
|
|
171
198
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
border-
|
|
175
|
-
|
|
199
|
+
.log-return-block {
|
|
200
|
+
border: 1px solid color-mix(in srgb, var(--primary) 35%, transparent 65%);
|
|
201
|
+
border-radius: 10px;
|
|
202
|
+
padding: 0.55rem;
|
|
203
|
+
margin-bottom: 0.65rem;
|
|
204
|
+
background: color-mix(in srgb, var(--primary) 10%, transparent 90%);
|
|
176
205
|
}
|
|
177
206
|
|
|
178
|
-
|
|
179
|
-
|
|
207
|
+
.log-return-block summary {
|
|
208
|
+
cursor: pointer;
|
|
209
|
+
color: var(--primary);
|
|
210
|
+
font-size: 0.82rem;
|
|
211
|
+
user-select: none;
|
|
180
212
|
font-weight: 600;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.log-return-block[open] summary {
|
|
216
|
+
margin-bottom: 0.45rem;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.log-label {
|
|
181
220
|
color: var(--text-light);
|
|
182
221
|
text-transform: uppercase;
|
|
183
|
-
font-size: 0.75rem;
|
|
184
222
|
letter-spacing: 0.08em;
|
|
223
|
+
font-size: 0.65rem;
|
|
224
|
+
margin-bottom: 0.35rem;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.log-query-block pre,
|
|
228
|
+
.log-return-block pre,
|
|
229
|
+
.log-detail-item pre {
|
|
230
|
+
margin: 0;
|
|
231
|
+
white-space: pre-wrap;
|
|
232
|
+
word-break: break-word;
|
|
233
|
+
overflow-wrap: anywhere;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.log-meta-grid {
|
|
237
|
+
display: grid;
|
|
238
|
+
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
|
239
|
+
gap: 0.45rem;
|
|
240
|
+
margin-bottom: 0.65rem;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.log-meta-item {
|
|
244
|
+
border: 1px solid var(--muted-border);
|
|
245
|
+
border-radius: 8px;
|
|
246
|
+
padding: 0.4rem 0.55rem;
|
|
247
|
+
background: color-mix(in srgb, var(--surface-1) 35%, transparent 65%);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.log-meta-item span {
|
|
251
|
+
color: var(--text-light);
|
|
252
|
+
font-size: 0.72rem;
|
|
253
|
+
display: block;
|
|
254
|
+
margin-bottom: 0.2rem;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.log-meta-item code {
|
|
258
|
+
font-size: 0.78rem;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.log-details {
|
|
262
|
+
border-top: 1px dashed var(--muted-border);
|
|
263
|
+
padding-top: 0.55rem;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.log-details summary {
|
|
267
|
+
cursor: pointer;
|
|
268
|
+
color: var(--accent);
|
|
269
|
+
font-size: 0.82rem;
|
|
270
|
+
user-select: none;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
.log-detail-grid {
|
|
274
|
+
display: grid;
|
|
275
|
+
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
|
276
|
+
gap: 0.45rem;
|
|
277
|
+
margin-top: 0.55rem;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.log-detail-item {
|
|
281
|
+
border: 1px solid var(--muted-border);
|
|
282
|
+
border-radius: 8px;
|
|
283
|
+
padding: 0.45rem 0.55rem;
|
|
284
|
+
background: color-mix(in srgb, var(--surface-1) 35%, transparent 65%);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.copy-row-btn {
|
|
288
|
+
padding: 0.25rem 0.6rem;
|
|
289
|
+
border-radius: 0.5rem;
|
|
290
|
+
border: 1px solid color-mix(in srgb, var(--primary) 55%, transparent 45%);
|
|
291
|
+
background: color-mix(in srgb, var(--primary) 14%, transparent 86%);
|
|
292
|
+
color: var(--light);
|
|
293
|
+
cursor: pointer;
|
|
294
|
+
font-size: 0.75rem;
|
|
295
|
+
transition: var(--transition);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
.copy-row-btn:hover {
|
|
299
|
+
background: color-mix(in srgb, var(--primary) 24%, transparent 76%);
|
|
185
300
|
}
|
|
186
301
|
|
|
187
302
|
code {
|
|
@@ -201,15 +316,15 @@
|
|
|
201
316
|
}
|
|
202
317
|
|
|
203
318
|
.badge.ok {
|
|
204
|
-
background:
|
|
319
|
+
background: color-mix(in srgb, var(--success) 15%, transparent 85%);
|
|
205
320
|
color: var(--success);
|
|
206
|
-
border: 1px solid
|
|
321
|
+
border: 1px solid color-mix(in srgb, var(--success) 45%, transparent 55%);
|
|
207
322
|
}
|
|
208
323
|
|
|
209
324
|
.badge.err {
|
|
210
|
-
background:
|
|
325
|
+
background: color-mix(in srgb, var(--danger) 15%, transparent 85%);
|
|
211
326
|
color: var(--danger);
|
|
212
|
-
border: 1px solid
|
|
327
|
+
border: 1px solid color-mix(in srgb, var(--danger) 40%, transparent 60%);
|
|
213
328
|
}
|
|
214
329
|
|
|
215
330
|
.empty {
|
|
@@ -223,9 +338,15 @@
|
|
|
223
338
|
margin-bottom: 1rem;
|
|
224
339
|
padding: 0.75rem 1rem;
|
|
225
340
|
border-radius: 0.7rem;
|
|
226
|
-
background:
|
|
341
|
+
background: color-mix(in srgb, var(--success) 14%, transparent 86%);
|
|
227
342
|
color: var(--success);
|
|
228
|
-
border: 1px solid
|
|
343
|
+
border: 1px solid color-mix(in srgb, var(--success) 45%, transparent 55%);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
.notification.warn {
|
|
347
|
+
background: color-mix(in srgb, var(--warning) 14%, transparent 86%);
|
|
348
|
+
color: var(--warning);
|
|
349
|
+
border: 1px solid color-mix(in srgb, var(--warning) 45%, transparent 55%);
|
|
229
350
|
}
|
|
230
351
|
|
|
231
352
|
@media (max-width: 900px) {
|
|
@@ -237,43 +358,12 @@
|
|
|
237
358
|
width: 100%;
|
|
238
359
|
}
|
|
239
360
|
|
|
240
|
-
table {
|
|
241
|
-
|
|
242
|
-
display: block;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
thead {
|
|
246
|
-
display: none;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
tbody,
|
|
250
|
-
tr,
|
|
251
|
-
td {
|
|
252
|
-
display: block;
|
|
253
|
-
width: 100%;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
tr {
|
|
257
|
-
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
258
|
-
border-radius: 12px;
|
|
259
|
-
margin-bottom: 0.9rem;
|
|
260
|
-
padding: 0.6rem 0.8rem;
|
|
261
|
-
background: rgba(0, 0, 0, 0.28);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
td {
|
|
265
|
-
border: none;
|
|
266
|
-
padding: 0.5rem 0;
|
|
361
|
+
.db-table-wrapper {
|
|
362
|
+
padding: 0.6rem;
|
|
267
363
|
}
|
|
268
364
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
display: block;
|
|
272
|
-
font-size: 0.7rem;
|
|
273
|
-
text-transform: uppercase;
|
|
274
|
-
letter-spacing: 0.08em;
|
|
275
|
-
color: var(--text-light);
|
|
276
|
-
margin-bottom: 0.25rem;
|
|
365
|
+
.log-detail-grid {
|
|
366
|
+
grid-template-columns: 1fr;
|
|
277
367
|
}
|
|
278
368
|
}
|
|
279
369
|
</style>"}}
|
|
@@ -291,7 +381,7 @@
|
|
|
291
381
|
<div class="db-subtitle">Live view of recent database queries and diagnostics</div>
|
|
292
382
|
</div>
|
|
293
383
|
<div class="db-actions">
|
|
294
|
-
<a class="db-link" href="/mbkauthe/
|
|
384
|
+
<a class="db-link" href="/mbkauthe/">Back</a>
|
|
295
385
|
</div>
|
|
296
386
|
</div>
|
|
297
387
|
|
|
@@ -309,19 +399,21 @@
|
|
|
309
399
|
<div class="db-toolbar">
|
|
310
400
|
<form id="limit-form" action="/mbkauthe/db" method="get">
|
|
311
401
|
<label for="limit">Limit</label>
|
|
312
|
-
<input id="limit" name="limit" type="number" min="1" max="500" value="{{queryLimit}}" />
|
|
313
|
-
<button type="submit">Update</button>
|
|
402
|
+
<input id="limit" name="limit" type="number" min="1" max="500" value="{{queryLimit}}" {{#unless isDev}}disabled{{/unless}} />
|
|
403
|
+
<button type="submit" {{#unless isDev}}disabled{{/unless}}>Update</button>
|
|
314
404
|
</form>
|
|
315
|
-
<button id="reset-btn" type="button">Reset</button>
|
|
316
|
-
<button id="copy-btn" type="button">Copy
|
|
405
|
+
<button id="reset-btn" type="button" {{#unless isDev}}disabled{{/unless}}>Reset</button>
|
|
406
|
+
<button id="copy-btn" type="button" {{#unless isDev}}disabled{{/unless}}>Copy Logs</button>
|
|
317
407
|
</div>
|
|
318
408
|
|
|
319
|
-
{{#
|
|
320
|
-
|
|
321
|
-
{{/
|
|
409
|
+
{{#unless isDev}}
|
|
410
|
+
<div class="notification warn" id="disabled-notice">{{disabledMessage}}</div>
|
|
411
|
+
{{/unless}}
|
|
412
|
+
|
|
413
|
+
<div class="notification" id="reset-notice" {{#unless resetDone}}hidden{{/unless}}>Query log and count have been reset.</div>
|
|
322
414
|
|
|
323
415
|
<div id="query-log" class="db-table-wrapper">
|
|
324
|
-
<div class="empty">Loading query log
|
|
416
|
+
<div class="empty">{{#if isDev}}Loading query log...{{else}}DB logs are disabled.{{/if}}</div>
|
|
325
417
|
</div>
|
|
326
418
|
</div>
|
|
327
419
|
</section>
|
|
@@ -333,31 +425,118 @@
|
|
|
333
425
|
const queryLogEl = document.getElementById('query-log');
|
|
334
426
|
const resetBtn = document.getElementById('reset-btn');
|
|
335
427
|
const copyBtn = document.getElementById('copy-btn');
|
|
428
|
+
const resetNoticeEl = document.getElementById('reset-notice');
|
|
429
|
+
const isDev = {{#if isDev}}true{{else}}false{{/if}};
|
|
430
|
+
let latestQueryLog = [];
|
|
431
|
+
|
|
432
|
+
const showDisabledState = (message = 'DB logs are disabled.') => {
|
|
433
|
+
queryCountEl.textContent = '-';
|
|
434
|
+
latestQueryLog = [];
|
|
435
|
+
queryLogEl.innerHTML = `<div class="empty">${escapeHtml(message)}</div>`;
|
|
436
|
+
};
|
|
336
437
|
|
|
337
438
|
const escapeHtml = (str) => String(str || '')
|
|
338
439
|
.replace(/[&<>"'`]/g, (s) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''', '`': '`' })[s]);
|
|
339
440
|
|
|
441
|
+
const escapeMarkdownCell = (value) => String(value || '')
|
|
442
|
+
.replace(/\\/g, '\\\\')
|
|
443
|
+
.replace(/\|/g, '\\|')
|
|
444
|
+
.replace(/\r?\n/g, '<br>');
|
|
445
|
+
|
|
446
|
+
const formatParamValues = (values, pretty = false) => {
|
|
447
|
+
if (!Array.isArray(values) || values.length === 0) return '';
|
|
448
|
+
|
|
449
|
+
if (pretty) {
|
|
450
|
+
return values
|
|
451
|
+
.map((v, i) => {
|
|
452
|
+
const printable = typeof v === 'string' ? v : JSON.stringify(v);
|
|
453
|
+
return `$${i + 1}: ${printable}`;
|
|
454
|
+
})
|
|
455
|
+
.join('\n');
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
return values
|
|
459
|
+
.map((v, i) => {
|
|
460
|
+
const printable = typeof v === 'string' ? v : JSON.stringify(v);
|
|
461
|
+
return `$${i + 1}: ${printable}`;
|
|
462
|
+
})
|
|
463
|
+
.join(' | ');
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
const toMarkdownReport = (queryLog, limit) => {
|
|
467
|
+
if (!Array.isArray(queryLog) || queryLog.length === 0) {
|
|
468
|
+
return '# DB Query Monitor\n\n_No queries recorded yet._';
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
const now = new Date().toISOString();
|
|
472
|
+
const header = [
|
|
473
|
+
'# DB Query Monitor',
|
|
474
|
+
'',
|
|
475
|
+
`- Exported: ${now}`,
|
|
476
|
+
`- Query Count: ${queryLog.length}`,
|
|
477
|
+
`- Limit: ${limit}`,
|
|
478
|
+
'',
|
|
479
|
+
'| # | Time | Name | Duration | Status | Query | Values | Return Value | Error | Request | Pool | Callsite |',
|
|
480
|
+
'|---:|---|---|---|---|---|---|---|---|---|---|---|'
|
|
481
|
+
];
|
|
482
|
+
|
|
483
|
+
const rows = queryLog.map((entry, idx) => {
|
|
484
|
+
const durationText = typeof entry.durationMs === 'number'
|
|
485
|
+
? `${entry.durationMs.toFixed(2)} ms`
|
|
486
|
+
: '';
|
|
487
|
+
const statusText = entry.success === true ? 'OK' : entry.success === false ? 'ERR' : '';
|
|
488
|
+
const values = formatParamValues(entry.values, false);
|
|
489
|
+
const returnValueText = entry.returnValue ? JSON.stringify(entry.returnValue) : '';
|
|
490
|
+
const errorText = entry.error
|
|
491
|
+
? `${entry.error.code ? `${entry.error.code}: ` : ''}${entry.error.message || ''}`
|
|
492
|
+
: '';
|
|
493
|
+
const requestText = entry.request
|
|
494
|
+
? `${entry.request.method || ''} ${entry.request.url || ''}`.trim() +
|
|
495
|
+
`\nuser: ${entry.request.username || entry.request.userId || 'anonymous'}` +
|
|
496
|
+
`\nip: ${entry.request.ip || ''}`
|
|
497
|
+
: '';
|
|
498
|
+
const poolText = entry.pool
|
|
499
|
+
? `total:${entry.pool.total} idle:${entry.pool.idle} waiting:${entry.pool.waiting}`
|
|
500
|
+
: '';
|
|
501
|
+
const callsiteText = entry.callsite
|
|
502
|
+
? `${entry.callsite.function || '(anonymous)'}\n${entry.callsite.file}:${entry.callsite.line}:${entry.callsite.column}`
|
|
503
|
+
: '';
|
|
504
|
+
|
|
505
|
+
return `| ${idx + 1} | ${escapeMarkdownCell(entry.time)} | ${escapeMarkdownCell(entry.name || '')} | ${escapeMarkdownCell(durationText)} | ${escapeMarkdownCell(statusText)} | ${escapeMarkdownCell(entry.query || '')} | ${escapeMarkdownCell(values)} | ${escapeMarkdownCell(returnValueText)} | ${escapeMarkdownCell(errorText)} | ${escapeMarkdownCell(requestText)} | ${escapeMarkdownCell(poolText)} | ${escapeMarkdownCell(callsiteText)} |`;
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
return [...header, ...rows].join('\n');
|
|
509
|
+
};
|
|
510
|
+
|
|
340
511
|
const buildTable = (queryLog) => {
|
|
341
512
|
if (!Array.isArray(queryLog) || queryLog.length === 0) {
|
|
342
513
|
queryLogEl.innerHTML = '<div class="empty">No queries recorded yet.</div>';
|
|
343
514
|
return;
|
|
344
515
|
}
|
|
345
516
|
|
|
346
|
-
const
|
|
347
|
-
const values =
|
|
517
|
+
const cards = queryLog.map((entry, idx) => {
|
|
518
|
+
const values = formatParamValues(entry.values, true);
|
|
519
|
+
const returnValueText = entry.returnValue ? JSON.stringify(entry.returnValue, null, 2) : '';
|
|
520
|
+
const returnValueSummary = entry.returnValue
|
|
521
|
+
? [
|
|
522
|
+
entry.returnValue.command ? `command: ${entry.returnValue.command}` : null,
|
|
523
|
+
typeof entry.returnValue.rowCount === 'number' ? `rowCount: ${entry.returnValue.rowCount}` : null,
|
|
524
|
+
typeof entry.returnValue.returnedRows === 'number' ? `rows: ${entry.returnValue.returnedRows}` : null
|
|
525
|
+
].filter(Boolean).join(' | ')
|
|
526
|
+
: '';
|
|
348
527
|
const callsite = entry.callsite || null;
|
|
349
528
|
const callsiteText = callsite
|
|
350
529
|
? `${callsite.function || '(anonymous)'}\n${callsite.file}:${callsite.line}:${callsite.column}`
|
|
351
530
|
: '';
|
|
352
531
|
const durationText = typeof entry.durationMs === 'number'
|
|
353
532
|
? `${entry.durationMs.toFixed(2)} ms`
|
|
354
|
-
: '';
|
|
355
|
-
const statusText = entry.success === true ? 'OK' : entry.success === false ? 'ERR' : '';
|
|
533
|
+
: 'n/a';
|
|
534
|
+
const statusText = entry.success === true ? 'OK' : entry.success === false ? 'ERR' : 'n/a';
|
|
356
535
|
const statusBadge = statusText === 'OK'
|
|
357
536
|
? '<span class="badge ok">OK</span>'
|
|
358
537
|
: statusText === 'ERR'
|
|
359
538
|
? '<span class="badge err">ERR</span>'
|
|
360
|
-
: '';
|
|
539
|
+
: '<span class="badge">N/A</span>';
|
|
361
540
|
const errorText = entry.error
|
|
362
541
|
? `${entry.error.code ? `${entry.error.code}: ` : ''}${entry.error.message || ''}`
|
|
363
542
|
: '';
|
|
@@ -370,83 +549,127 @@
|
|
|
370
549
|
? `total:${entry.pool.total} idle:${entry.pool.idle} waiting:${entry.pool.waiting}`
|
|
371
550
|
: '';
|
|
372
551
|
|
|
552
|
+
const detailBlocks = [
|
|
553
|
+
values ? `<div class="log-detail-item"><div class="log-label">Values</div><pre><code>${escapeHtml(values)}</code></pre></div>` : '',
|
|
554
|
+
errorText ? `<div class="log-detail-item"><div class="log-label">Error</div><pre><code>${escapeHtml(errorText)}</code></pre></div>` : '',
|
|
555
|
+
requestText ? `<div class="log-detail-item"><div class="log-label">Request</div><pre><code>${escapeHtml(requestText)}</code></pre></div>` : '',
|
|
556
|
+
poolText ? `<div class="log-detail-item"><div class="log-label">Pool</div><pre><code>${escapeHtml(poolText)}</code></pre></div>` : '',
|
|
557
|
+
callsiteText ? `<div class="log-detail-item"><div class="log-label">Callsite</div><pre><code>${escapeHtml(callsiteText)}</code></pre></div>` : ''
|
|
558
|
+
].filter(Boolean).join('');
|
|
559
|
+
|
|
373
560
|
return `
|
|
374
|
-
<
|
|
375
|
-
<
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
<
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
561
|
+
<article class="log-card">
|
|
562
|
+
<div class="log-head">
|
|
563
|
+
<div class="log-mainline">
|
|
564
|
+
<span class="log-index">#${idx + 1}</span>
|
|
565
|
+
${statusBadge}
|
|
566
|
+
<span class="log-time">${escapeHtml(entry.time)}</span>
|
|
567
|
+
</div>
|
|
568
|
+
<button class="copy-row-btn" data-copy='${escapeHtml(JSON.stringify(entry))}'>Copy</button>
|
|
569
|
+
</div>
|
|
570
|
+
|
|
571
|
+
<div class="log-query-block">
|
|
572
|
+
<div class="log-label">Query</div>
|
|
573
|
+
<pre><code>${escapeHtml(entry.query || '')}</code></pre>
|
|
574
|
+
</div>
|
|
575
|
+
|
|
576
|
+
<div class="log-meta-grid">
|
|
577
|
+
<div class="log-meta-item"><span>Name</span><code>${escapeHtml(entry.name || 'n/a')}</code></div>
|
|
578
|
+
<div class="log-meta-item"><span>Duration</span><code>${escapeHtml(durationText)}</code></div>
|
|
579
|
+
</div>
|
|
580
|
+
|
|
581
|
+
${returnValueText ? `<details class="log-return-block"><summary>Return Value${returnValueSummary ? ` (${escapeHtml(returnValueSummary)})` : ''}</summary><pre><code>${escapeHtml(returnValueText)}</code></pre></details>` : ''}
|
|
582
|
+
|
|
583
|
+
${detailBlocks ? `<details class="log-details"><summary>More details</summary><div class="log-detail-grid">${detailBlocks}</div></details>` : ''}
|
|
584
|
+
</article>
|
|
388
585
|
`;
|
|
389
586
|
}).join('');
|
|
390
587
|
|
|
391
|
-
queryLogEl.innerHTML =
|
|
392
|
-
<table>
|
|
393
|
-
<thead>
|
|
394
|
-
<tr>
|
|
395
|
-
<th>#</th>
|
|
396
|
-
<th>Time</th>
|
|
397
|
-
<th>Name</th>
|
|
398
|
-
<th>Duration</th>
|
|
399
|
-
<th>Status</th>
|
|
400
|
-
<th>Query</th>
|
|
401
|
-
<th>Values</th>
|
|
402
|
-
<th>Error</th>
|
|
403
|
-
<th>Request</th>
|
|
404
|
-
<th>Pool</th>
|
|
405
|
-
<th>Callsite</th>
|
|
406
|
-
<th>Copy</th>
|
|
407
|
-
</tr>
|
|
408
|
-
</thead>
|
|
409
|
-
<tbody>
|
|
410
|
-
${rows}
|
|
411
|
-
</tbody>
|
|
412
|
-
</table>
|
|
413
|
-
`;
|
|
588
|
+
queryLogEl.innerHTML = `<div class="log-list">${cards}</div>`;
|
|
414
589
|
};
|
|
415
590
|
|
|
416
|
-
const loadLog = async (
|
|
591
|
+
const loadLog = async () => {
|
|
592
|
+
if (!isDev) {
|
|
593
|
+
showDisabledState();
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
|
|
417
597
|
const limit = Number(limitInput.value) || 50;
|
|
418
598
|
queryLimitEl.textContent = String(limit);
|
|
419
599
|
|
|
420
600
|
const params = new URLSearchParams({ limit: String(limit) });
|
|
421
|
-
if (options.reset) params.set('reset', '1');
|
|
422
601
|
|
|
423
602
|
const response = await fetch(`/mbkauthe/db.json?${params.toString()}`);
|
|
424
603
|
if (!response.ok) {
|
|
425
|
-
|
|
604
|
+
let message = 'Failed to load query log.';
|
|
605
|
+
try {
|
|
606
|
+
const errorData = await response.json();
|
|
607
|
+
if (response.status === 403 || errorData?.isDev === false) {
|
|
608
|
+
showDisabledState(errorData?.message || 'DB logs are disabled.');
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
if (errorData?.message) message = errorData.message;
|
|
612
|
+
} catch {
|
|
613
|
+
// Ignore parse errors and keep fallback message.
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
queryLogEl.innerHTML = `<div class="empty">${escapeHtml(message)}</div>`;
|
|
426
617
|
return;
|
|
427
618
|
}
|
|
428
619
|
|
|
429
620
|
const data = await response.json();
|
|
621
|
+
if (data?.isDev === false) {
|
|
622
|
+
showDisabledState(data.message || 'DB logs are disabled.');
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
|
|
430
626
|
queryCountEl.textContent = data.queryCount ?? '-';
|
|
627
|
+
latestQueryLog = Array.isArray(data.queryLog) ? data.queryLog : [];
|
|
431
628
|
buildTable(data.queryLog);
|
|
432
|
-
|
|
433
|
-
if (options.reset) {
|
|
434
|
-
const next = new URLSearchParams({ limit: String(limit), resetDone: '1' });
|
|
435
|
-
window.location.assign(`/mbkauthe/db?${next.toString()}`);
|
|
436
|
-
}
|
|
437
629
|
};
|
|
438
630
|
|
|
439
631
|
resetBtn.addEventListener('click', () => {
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
632
|
+
if (!isDev) {
|
|
633
|
+
showDisabledState();
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
fetch('/mbkauthe/db/reset', { method: 'POST' })
|
|
638
|
+
.then(async (response) => {
|
|
639
|
+
if (!response.ok) {
|
|
640
|
+
const errorData = await response.json().catch(() => ({}));
|
|
641
|
+
if (response.status === 403 || errorData?.isDev === false) {
|
|
642
|
+
showDisabledState(errorData?.message || 'DB logs are disabled.');
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
throw new Error('reset failed');
|
|
646
|
+
}
|
|
647
|
+
const result = await response.json().catch(() => ({}));
|
|
648
|
+
|
|
649
|
+
if (resetNoticeEl) {
|
|
650
|
+
resetNoticeEl.textContent = result.message || 'Query log and count have been reset.';
|
|
651
|
+
resetNoticeEl.hidden = false;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
return loadLog();
|
|
655
|
+
})
|
|
656
|
+
.catch(() => {
|
|
657
|
+
queryLogEl.innerHTML = '<div class="empty">Failed to reset query log.</div>';
|
|
658
|
+
});
|
|
443
659
|
});
|
|
444
660
|
|
|
445
661
|
copyBtn.addEventListener('click', () => {
|
|
446
|
-
|
|
447
|
-
|
|
662
|
+
if (!isDev) {
|
|
663
|
+
showDisabledState();
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
const limit = Number(limitInput.value) || 50;
|
|
668
|
+
const markdown = toMarkdownReport(latestQueryLog, limit);
|
|
669
|
+
|
|
670
|
+
navigator.clipboard.writeText(markdown).then(() => {
|
|
448
671
|
copyBtn.textContent = 'Copied!';
|
|
449
|
-
setTimeout(() => (copyBtn.textContent = 'Copy
|
|
672
|
+
setTimeout(() => (copyBtn.textContent = 'Copy Logs'), 1200);
|
|
450
673
|
});
|
|
451
674
|
});
|
|
452
675
|
|
|
@@ -467,9 +690,13 @@
|
|
|
467
690
|
});
|
|
468
691
|
});
|
|
469
692
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
}
|
|
693
|
+
if (!isDev) {
|
|
694
|
+
showDisabledState();
|
|
695
|
+
} else {
|
|
696
|
+
loadLog().catch(() => {
|
|
697
|
+
queryLogEl.innerHTML = '<div class="empty">Failed to load query log.</div>';
|
|
698
|
+
});
|
|
699
|
+
}
|
|
473
700
|
</script>
|
|
474
701
|
{{> versionInfo}}
|
|
475
702
|
</body>
|