@wxn0brp/falcon-frame 0.8.3 → 0.8.4
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/dist/cors.js +2 -2
- package/dist/debug.d.ts +7 -0
- package/dist/debug.html +484 -0
- package/dist/debug.js +52 -0
- package/dist/req.js +10 -5
- package/dist/router.d.ts +1 -0
- package/dist/router.js +4 -0
- package/dist/types.d.ts +2 -1
- package/dist/types.js +1 -0
- package/package.json +1 -1
package/dist/cors.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
function setHeader(res, opts) {
|
|
2
2
|
if (opts.accessControlAllowMethods)
|
|
3
|
-
res.setHeader("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS");
|
|
3
|
+
res.setHeader("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,PATCH,OPTIONS");
|
|
4
4
|
if (opts.accessControlAllowHeaders)
|
|
5
|
-
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
|
5
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, x-request-id");
|
|
6
6
|
}
|
|
7
7
|
function parseOrigin(origin) {
|
|
8
8
|
try {
|
package/dist/debug.d.ts
ADDED
package/dist/debug.html
ADDED
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="UTF-8">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
+
<meta name="color-scheme" content="dark">
|
|
8
|
+
<title>FalconFrame Debug</title>
|
|
9
|
+
<style>
|
|
10
|
+
* {
|
|
11
|
+
box-sizing: border-box;
|
|
12
|
+
margin: 0;
|
|
13
|
+
padding: 0
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
:root {
|
|
17
|
+
--bg: #0d1117;
|
|
18
|
+
--surface: #161b22;
|
|
19
|
+
--border: #30363d;
|
|
20
|
+
--text: #c9d1d9;
|
|
21
|
+
--text-muted: #8b949e;
|
|
22
|
+
--accent: #58a6ff;
|
|
23
|
+
--success: #3fb950;
|
|
24
|
+
--warning: #d29922;
|
|
25
|
+
--danger: #f85149;
|
|
26
|
+
--get: #58a6ff;
|
|
27
|
+
--post: #3fb950;
|
|
28
|
+
--put: #d29922;
|
|
29
|
+
--delete: #f85149;
|
|
30
|
+
--patch: #bc8cff;
|
|
31
|
+
--radius: 6px;
|
|
32
|
+
--font: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
body {
|
|
36
|
+
background: var(--bg);
|
|
37
|
+
color: var(--text);
|
|
38
|
+
font-family: var(--font);
|
|
39
|
+
font-size: 13px;
|
|
40
|
+
line-height: 1.5;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.btn {
|
|
44
|
+
font-family: var(--font);
|
|
45
|
+
font-size: 12px;
|
|
46
|
+
padding: 4px 12px;
|
|
47
|
+
border-radius: var(--radius);
|
|
48
|
+
border: 1px solid var(--border);
|
|
49
|
+
background: var(--surface);
|
|
50
|
+
color: var(--text);
|
|
51
|
+
cursor: pointer;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.btn:hover {
|
|
55
|
+
filter: brightness(1.2);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.btn.danger {
|
|
59
|
+
border-color: var(--danger);
|
|
60
|
+
color: var(--danger);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.filters {
|
|
64
|
+
padding: 8px 20px;
|
|
65
|
+
display: flex;
|
|
66
|
+
gap: 16px;
|
|
67
|
+
flex-wrap: wrap;
|
|
68
|
+
border-bottom: 1px solid var(--border);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.filter-group {
|
|
72
|
+
display: flex;
|
|
73
|
+
gap: 6px;
|
|
74
|
+
align-items: center;
|
|
75
|
+
font-size: 11px;
|
|
76
|
+
text-transform: uppercase;
|
|
77
|
+
color: var(--text-muted);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.filter-group input {
|
|
81
|
+
font-family: var(--font);
|
|
82
|
+
font-size: 12px;
|
|
83
|
+
padding: 4px 8px;
|
|
84
|
+
border-radius: var(--radius);
|
|
85
|
+
border: 1px solid var(--border);
|
|
86
|
+
background: var(--surface);
|
|
87
|
+
color: var(--text);
|
|
88
|
+
width: 180px;
|
|
89
|
+
outline: none;
|
|
90
|
+
text-transform: none;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.method-filters {
|
|
94
|
+
display: flex;
|
|
95
|
+
gap: 4px;
|
|
96
|
+
text-transform: none;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.method-chip {
|
|
100
|
+
font-family: var(--font);
|
|
101
|
+
font-size: 11px;
|
|
102
|
+
padding: 2px 8px;
|
|
103
|
+
border-radius: var(--radius);
|
|
104
|
+
border: 1px solid var(--border);
|
|
105
|
+
background: var(--surface);
|
|
106
|
+
color: var(--text-muted);
|
|
107
|
+
cursor: pointer;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.method-chip.active {
|
|
111
|
+
border-color: currentColor;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.method-chip.get.active {
|
|
115
|
+
color: var(--get)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.method-chip.post.active {
|
|
119
|
+
color: var(--post)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.method-chip.put.active {
|
|
123
|
+
color: var(--put)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.method-chip.delete.active {
|
|
127
|
+
color: var(--delete)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.method-chip.patch.active {
|
|
131
|
+
color: var(--patch)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.table-wrap {
|
|
135
|
+
overflow-x: auto;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
table {
|
|
139
|
+
width: 100%;
|
|
140
|
+
border-collapse: collapse;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
thead {
|
|
144
|
+
position: sticky;
|
|
145
|
+
top: 0;
|
|
146
|
+
background: var(--surface);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
th {
|
|
150
|
+
text-align: left;
|
|
151
|
+
padding: 8px 12px;
|
|
152
|
+
font-size: 11px;
|
|
153
|
+
font-weight: 600;
|
|
154
|
+
text-transform: uppercase;
|
|
155
|
+
color: var(--text-muted);
|
|
156
|
+
border-bottom: 2px solid var(--border);
|
|
157
|
+
white-space: nowrap;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
td {
|
|
161
|
+
padding: 6px 12px;
|
|
162
|
+
border-bottom: 1px solid var(--border);
|
|
163
|
+
vertical-align: top;
|
|
164
|
+
white-space: nowrap;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
tr[data-row] {
|
|
168
|
+
cursor: pointer;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
tr[data-row]:hover td {
|
|
172
|
+
background: rgba(88, 166, 255, 0.04);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.method {
|
|
176
|
+
font-weight: 700;
|
|
177
|
+
font-size: 11px;
|
|
178
|
+
text-transform: uppercase;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.method.get {
|
|
182
|
+
color: var(--get)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.method.post {
|
|
186
|
+
color: var(--post)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.method.put {
|
|
190
|
+
color: var(--put)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.method.delete {
|
|
194
|
+
color: var(--delete)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.method.patch {
|
|
198
|
+
color: var(--patch)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.status {
|
|
202
|
+
font-weight: 600
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.status.s2xx {
|
|
206
|
+
color: var(--success)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.status.s3xx {
|
|
210
|
+
color: var(--accent)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.status.s4xx {
|
|
214
|
+
color: var(--warning)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.status.s5xx {
|
|
218
|
+
color: var(--danger)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.duration {
|
|
222
|
+
color: var(--text-muted)
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.duration.slow {
|
|
226
|
+
color: var(--warning)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.duration.very-slow {
|
|
230
|
+
color: var(--danger)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.url-cell {
|
|
234
|
+
max-width: 400px;
|
|
235
|
+
overflow: hidden;
|
|
236
|
+
text-overflow: ellipsis;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.detail-row td {
|
|
240
|
+
padding: 0;
|
|
241
|
+
background: rgba(22, 27, 34, 0.6);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.detail-inner {
|
|
245
|
+
padding: 12px 16px;
|
|
246
|
+
display: grid;
|
|
247
|
+
grid-template-columns: 1fr 1fr;
|
|
248
|
+
gap: 12px;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.detail-inner>* {
|
|
252
|
+
flex: 1 1 300px;
|
|
253
|
+
min-width: 240px;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.detail-section h4 {
|
|
257
|
+
font-size: 11px;
|
|
258
|
+
text-transform: uppercase;
|
|
259
|
+
color: var(--text-muted);
|
|
260
|
+
margin-bottom: 4px;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.detail-section pre {
|
|
264
|
+
font-family: var(--font);
|
|
265
|
+
font-size: 12px;
|
|
266
|
+
background: var(--bg);
|
|
267
|
+
border: 1px solid var(--border);
|
|
268
|
+
border-radius: var(--radius);
|
|
269
|
+
padding: 8px 12px;
|
|
270
|
+
max-height: 200px;
|
|
271
|
+
overflow: auto;
|
|
272
|
+
color: var(--text);
|
|
273
|
+
white-space: pre-wrap;
|
|
274
|
+
word-break: break-all;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.empty {
|
|
278
|
+
text-align: center;
|
|
279
|
+
padding: 60px 20px;
|
|
280
|
+
color: var(--text-muted);
|
|
281
|
+
font-size: 14px;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.time {
|
|
285
|
+
color: var(--text-muted);
|
|
286
|
+
font-size: 11px;
|
|
287
|
+
}
|
|
288
|
+
</style>
|
|
289
|
+
</head>
|
|
290
|
+
|
|
291
|
+
<body>
|
|
292
|
+
<div class="filters">
|
|
293
|
+
<div class="filter-group">
|
|
294
|
+
<label for="urlFilter">URL</label>
|
|
295
|
+
<input type="text" id="urlFilter" placeholder="Filter by URL...">
|
|
296
|
+
</div>
|
|
297
|
+
<div class="filter-group">
|
|
298
|
+
<label for="statusFilter">Status</label>
|
|
299
|
+
<input type="text" id="statusFilter" placeholder="e.g. 200, 4xx">
|
|
300
|
+
</div>
|
|
301
|
+
<div class="filter-group">
|
|
302
|
+
<span>Methods</span>
|
|
303
|
+
<div class="method-filters" id="methodFilters"></div>
|
|
304
|
+
</div>
|
|
305
|
+
</div>
|
|
306
|
+
<div class="table-wrap">
|
|
307
|
+
<table id="logTable">
|
|
308
|
+
<thead>
|
|
309
|
+
<tr>
|
|
310
|
+
<th>#</th>
|
|
311
|
+
<th>Method</th>
|
|
312
|
+
<th>URL</th>
|
|
313
|
+
<th>Status</th>
|
|
314
|
+
<th>Duration</th>
|
|
315
|
+
<th>Time</th>
|
|
316
|
+
</tr>
|
|
317
|
+
</thead>
|
|
318
|
+
<tbody id="logBody">
|
|
319
|
+
<tr class="empty">
|
|
320
|
+
<td colspan="6">
|
|
321
|
+
<p>Waiting for requests...</p>
|
|
322
|
+
</td>
|
|
323
|
+
</tr>
|
|
324
|
+
</tbody>
|
|
325
|
+
</table>
|
|
326
|
+
</div>
|
|
327
|
+
|
|
328
|
+
<script type="module">
|
|
329
|
+
const METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH"];
|
|
330
|
+
const logBody = document.querySelector("#logBody");
|
|
331
|
+
const urlFilter = document.querySelector("#urlFilter");
|
|
332
|
+
const statusFilter = document.querySelector("#statusFilter");
|
|
333
|
+
const methodFilters = document.querySelector("#methodFilters");
|
|
334
|
+
const emptyRow = logBody.querySelector(".empty");
|
|
335
|
+
let entries = [];
|
|
336
|
+
let seq = 0;
|
|
337
|
+
let expanded = new Set();
|
|
338
|
+
let activeMethods = new Set(METHODS);
|
|
339
|
+
|
|
340
|
+
function statusClass(code) {
|
|
341
|
+
if (code < 300) return "s2xx";
|
|
342
|
+
if (code < 400) return "s3xx";
|
|
343
|
+
if (code < 500) return "s4xx";
|
|
344
|
+
return "s5xx";
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function durationClass(ms) {
|
|
348
|
+
if (ms > 1000) return "very-slow";
|
|
349
|
+
if (ms > 300) return "slow";
|
|
350
|
+
return "";
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function formatJSON(obj) {
|
|
354
|
+
try {
|
|
355
|
+
return JSON.stringify(obj, null, 2);
|
|
356
|
+
} catch {
|
|
357
|
+
return String(obj);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function esc(str) {
|
|
362
|
+
const el = document.createElement("span");
|
|
363
|
+
el.textContent = str;
|
|
364
|
+
return el.innerHTML;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function buildMethodChips() {
|
|
368
|
+
methodFilters.innerHTML = METHODS.map(m =>
|
|
369
|
+
`<button class="method-chip ${m.toLowerCase()} active" data-method="${m}">${m}</button>`
|
|
370
|
+
).join("");
|
|
371
|
+
methodFilters.querySelectorAll(".method-chip").forEach(chip => {
|
|
372
|
+
chip.addEventListener("click", () => {
|
|
373
|
+
const m = chip.dataset.method;
|
|
374
|
+
if (activeMethods.has(m)) {
|
|
375
|
+
activeMethods.delete(m);
|
|
376
|
+
chip.classList.remove("active");
|
|
377
|
+
} else {
|
|
378
|
+
activeMethods.add(m);
|
|
379
|
+
chip.classList.add("active");
|
|
380
|
+
}
|
|
381
|
+
render();
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function matchEntry(e) {
|
|
387
|
+
if (!activeMethods.has(e.method)) return false;
|
|
388
|
+
if (urlFilter.value) {
|
|
389
|
+
const q = urlFilter.value.toLowerCase();
|
|
390
|
+
if (!e.url.toLowerCase().includes(q)) return false;
|
|
391
|
+
}
|
|
392
|
+
if (statusFilter.value) {
|
|
393
|
+
const regex = new RegExp(statusFilter.value.trim().replaceAll("x", "\\d"), "i");
|
|
394
|
+
if (!regex.test(String(e.status))) return false;
|
|
395
|
+
}
|
|
396
|
+
return true;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function render() {
|
|
400
|
+
const filtered = entries.filter(matchEntry).reverse();
|
|
401
|
+
|
|
402
|
+
if (filtered.length === 0) {
|
|
403
|
+
logBody.innerHTML = `<tr class="empty"><td colspan="6"><p>No matching requests</p></td></tr>`;
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
logBody.innerHTML = filtered.map((e, i) => {
|
|
408
|
+
const idx = filtered.length - i;
|
|
409
|
+
const dur = e.duration != null ? `${e.duration}ms` : "—";
|
|
410
|
+
const rowId = `${idx}@${e.time}`;
|
|
411
|
+
const isExpanded = expanded.has(rowId);
|
|
412
|
+
const escUrl = esc(e.url);
|
|
413
|
+
|
|
414
|
+
let html = `<tr data-row="${rowId}">`;
|
|
415
|
+
html += `<td>${idx}</td>`;
|
|
416
|
+
html += `<td><span class="method ${e.method.toLowerCase()}">${esc(e.method)}</span></td>`;
|
|
417
|
+
html += `<td><span class="url-cell" title="${escUrl}">${escUrl}</span></td>`;
|
|
418
|
+
html += `<td><span class="status ${statusClass(e.status)}">${e.status}</span></td>`;
|
|
419
|
+
html += `<td><span class="duration ${durationClass(e.duration)}">${dur}</span></td>`;
|
|
420
|
+
html += `<td class="time">${new Date(e.time).toLocaleTimeString()}</td>`;
|
|
421
|
+
html += `</tr>`;
|
|
422
|
+
|
|
423
|
+
if (isExpanded) {
|
|
424
|
+
html += `<tr class="detail-row"><td colspan="6">`;
|
|
425
|
+
html += `<div class="detail-inner">`;
|
|
426
|
+
html += `<div class="detail-section"><h4>Headers</h4><pre>${esc(formatJSON(e.headers))}</pre></div>`;
|
|
427
|
+
html += `<div class="detail-section"><h4>Query</h4><pre>${esc(formatJSON(e.query))}</pre></div>`;
|
|
428
|
+
html += `<div class="detail-section"><h4>Cookies</h4><pre>${esc(formatJSON(e.cookies))}</pre></div>`;
|
|
429
|
+
html += `<div class="detail-section"><h4>Body</h4><pre>${esc(formatJSON(e.body))}</pre></div>`;
|
|
430
|
+
html += `</div></td></tr>`;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
return html;
|
|
434
|
+
}).join("");
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
logBody.addEventListener("click", (e) => {
|
|
438
|
+
const row = e.target.closest("tr[data-row]");
|
|
439
|
+
if (!row) return;
|
|
440
|
+
const rid = row.dataset.row;
|
|
441
|
+
expanded.has(rid) ? expanded.delete(rid) : expanded.add(rid);
|
|
442
|
+
render();
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
function addEntry(entry) {
|
|
446
|
+
entries.push(entry);
|
|
447
|
+
if (entries.length > 500) entries = entries.slice(-500);
|
|
448
|
+
render();
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
urlFilter.addEventListener("input", render);
|
|
452
|
+
statusFilter.addEventListener("input", render);
|
|
453
|
+
|
|
454
|
+
buildMethodChips();
|
|
455
|
+
|
|
456
|
+
const pass = new URLSearchParams(window.location.search).get("pass") || "";
|
|
457
|
+
const sseUrl = window.location.pathname.replace(/\/?$/, "") + "/sse" + (pass ? "?pass=" + encodeURIComponent(pass) : "");
|
|
458
|
+
|
|
459
|
+
function connectSSE() {
|
|
460
|
+
const es = new EventSource(sseUrl);
|
|
461
|
+
|
|
462
|
+
es.onopen = () => {
|
|
463
|
+
document.title = "FalconFrame Debug | Connected";
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
es.addEventListener("message", (e) => {
|
|
467
|
+
try {
|
|
468
|
+
const data = JSON.parse(e.data);
|
|
469
|
+
addEntry(data);
|
|
470
|
+
} catch { }
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
es.onerror = () => {
|
|
474
|
+
document.title = "FalconFrame Debug | Disconnected";
|
|
475
|
+
es.close();
|
|
476
|
+
setTimeout(connectSSE, 2000);
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
connectSSE();
|
|
481
|
+
</script>
|
|
482
|
+
</body>
|
|
483
|
+
|
|
484
|
+
</html>
|
package/dist/debug.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { join } from "path";
|
|
2
|
+
import { Router } from "./router.js";
|
|
3
|
+
export function createDebug(opts = {}) {
|
|
4
|
+
const { url = "/debug", hosts = ["localhost", "127.0.0.1", "::1"], } = opts;
|
|
5
|
+
if (process.env.NODE_ENV === "production" &&
|
|
6
|
+
!process.env.FF_DEBUG_PRODUCTION_ENABLED)
|
|
7
|
+
return (req, res, next) => { next(); };
|
|
8
|
+
const pass = process.env.FF_DEBUG_PASS;
|
|
9
|
+
function isHostAllowed(req) {
|
|
10
|
+
return hosts.some((h) => h === req.socket.remoteAddress);
|
|
11
|
+
}
|
|
12
|
+
function checkAuth(req) {
|
|
13
|
+
if (!pass)
|
|
14
|
+
return true;
|
|
15
|
+
return req.query?.pass === pass;
|
|
16
|
+
}
|
|
17
|
+
function isAllowed(req) {
|
|
18
|
+
return isHostAllowed(req) && checkAuth(req);
|
|
19
|
+
}
|
|
20
|
+
const router = new Router();
|
|
21
|
+
router.get(url, (req, res, next) => {
|
|
22
|
+
if (!isAllowed(req))
|
|
23
|
+
return next();
|
|
24
|
+
res.sendFile(join(import.meta.dirname, "debug.html"));
|
|
25
|
+
});
|
|
26
|
+
const sse = router.sse(url + "/sse", (req, res, next) => {
|
|
27
|
+
if (!isAllowed(req))
|
|
28
|
+
return req.FF._413(req, res);
|
|
29
|
+
next();
|
|
30
|
+
}, () => { });
|
|
31
|
+
router.use("/", (req, res, next) => {
|
|
32
|
+
const start = Date.now();
|
|
33
|
+
const hook = res.end.bind(res);
|
|
34
|
+
res.end = function (...args) {
|
|
35
|
+
const entry = {
|
|
36
|
+
method: req.method,
|
|
37
|
+
url: req.url,
|
|
38
|
+
headers: req.headers,
|
|
39
|
+
query: req.query || {},
|
|
40
|
+
cookies: req.cookies || {},
|
|
41
|
+
body: req.body || {},
|
|
42
|
+
status: res.statusCode,
|
|
43
|
+
duration: Date.now() - start,
|
|
44
|
+
time: new Date().toISOString(),
|
|
45
|
+
};
|
|
46
|
+
sse.sendAll(entry);
|
|
47
|
+
return hook(...args);
|
|
48
|
+
};
|
|
49
|
+
next();
|
|
50
|
+
});
|
|
51
|
+
return router;
|
|
52
|
+
}
|
package/dist/req.js
CHANGED
|
@@ -36,12 +36,17 @@ export function handleRequest(req, res, FF, reRoute = false) {
|
|
|
36
36
|
handleRequest(req, res, FF, true);
|
|
37
37
|
};
|
|
38
38
|
if (FF.opts.xRequestId !== "disable") {
|
|
39
|
-
if (req.headers["x-request-id"])
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
if (req.headers["x-request-id"]) {
|
|
40
|
+
req.id = req.headers["x-request-id"];
|
|
41
|
+
res.setHeader("x-request-id", req.id);
|
|
42
|
+
}
|
|
43
|
+
else if (FF.opts.xRequestId === "auto") {
|
|
44
|
+
req.id = randomUUID();
|
|
45
|
+
res.setHeader("x-request-id", req.id);
|
|
46
|
+
}
|
|
43
47
|
}
|
|
44
|
-
logger.info(`Incoming request: ${req.method} ${req.url}`
|
|
48
|
+
logger.info(`Incoming request: ${req.method} ${req.url}` +
|
|
49
|
+
(req.id ? ` [${req.id}]` : ""));
|
|
45
50
|
const middlewaresPath = req.path + "/";
|
|
46
51
|
const middlewares = getMiddlewares(FF.middlewares, middlewaresPath.replace(/\/+/g, "/"));
|
|
47
52
|
const matchedTypeMiddlewares = middlewares.filter((middleware) => middleware.method === req.method.toLowerCase() ||
|
package/dist/router.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export declare class Router {
|
|
|
11
11
|
post(path: string, ...handlers: RouteHandler[]): this;
|
|
12
12
|
put(path: string, ...handlers: RouteHandler[]): this;
|
|
13
13
|
delete(path: string, ...handlers: RouteHandler[]): this;
|
|
14
|
+
patch(path: string, ...handlers: RouteHandler[]): this;
|
|
14
15
|
all(path: string, ...handlers: RouteHandler[]): this;
|
|
15
16
|
static(apiPath: string, dirPath?: string, opts?: StaticServeOptions): this;
|
|
16
17
|
sse(path: string, ...handlers: RouteHandler[]): SSEManager;
|
package/dist/router.js
CHANGED
|
@@ -46,6 +46,10 @@ export class Router {
|
|
|
46
46
|
this.addRoute("delete", path, ...handlers);
|
|
47
47
|
return this;
|
|
48
48
|
}
|
|
49
|
+
patch(path, ...handlers) {
|
|
50
|
+
this.addRoute("patch", path, ...handlers);
|
|
51
|
+
return this;
|
|
52
|
+
}
|
|
49
53
|
all(path, ...handlers) {
|
|
50
54
|
this.addRoute("all", path, ...handlers);
|
|
51
55
|
return this;
|
package/dist/types.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { FFResponse } from "./res.js";
|
|
|
4
4
|
import http from "http";
|
|
5
5
|
export type RouteHandler = (req: FFRequest, res: FFResponse, next?: () => void) => void | any;
|
|
6
6
|
export type ErrorHandler = (err: Error, req: FFRequest, res: FFResponse) => void | any;
|
|
7
|
-
export type Method = "get" | "post" | "put" | "delete" | "all";
|
|
7
|
+
export type Method = "get" | "post" | "put" | "delete" | "patch" | "all";
|
|
8
8
|
export interface Params {
|
|
9
9
|
[key: string]: string;
|
|
10
10
|
}
|
|
@@ -30,6 +30,7 @@ export declare class FFRequest extends http.IncomingMessage {
|
|
|
30
30
|
body: Body;
|
|
31
31
|
valid: (schema: ValidationSchema) => ValidationResult;
|
|
32
32
|
middleware: Middleware;
|
|
33
|
+
id?: string;
|
|
33
34
|
sseId?: string;
|
|
34
35
|
reRoute: (fn: (req: FFRequest) => void) => void;
|
|
35
36
|
_compression?: true;
|
package/dist/types.js
CHANGED