@vnphu/nestjs-api-explorer 0.1.0 → 0.2.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/README.md +409 -77
- package/dist/api-docs-parser.d.ts +24 -0
- package/dist/api-docs-parser.d.ts.map +1 -0
- package/dist/api-docs-parser.js +234 -0
- package/dist/api-docs-parser.js.map +1 -0
- package/dist/api-explorer.html.d.ts.map +1 -1
- package/dist/api-explorer.html.js +514 -21
- package/dist/api-explorer.html.js.map +1 -1
- package/dist/api-explorer.module.d.ts +27 -4
- package/dist/api-explorer.module.d.ts.map +1 -1
- package/dist/api-explorer.module.js +10 -1
- package/dist/api-explorer.module.js.map +1 -1
- package/dist/api-explorer.service.d.ts +12 -0
- package/dist/api-explorer.service.d.ts.map +1 -1
- package/dist/api-explorer.service.js +44 -26
- package/dist/api-explorer.service.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
|
@@ -106,9 +106,53 @@ function getExplorerHtml(options) {
|
|
|
106
106
|
width: 34px; height: 34px; display: flex; align-items: center; justify-content: center;
|
|
107
107
|
border-radius: var(--radius); border: 1.5px solid var(--border);
|
|
108
108
|
background: var(--bg-white); color: var(--text-muted);
|
|
109
|
-
cursor: pointer; transition: all .15s; flex-shrink: 0;
|
|
109
|
+
cursor: pointer; transition: all .15s; flex-shrink: 0; position: relative;
|
|
110
110
|
}
|
|
111
111
|
.icon-btn:hover { border-color: var(--accent); color: var(--accent); background: var(--accent-bg); }
|
|
112
|
+
.icon-btn.active { border-color: var(--accent); color: var(--accent); background: var(--accent-bg); }
|
|
113
|
+
.icon-btn .dot {
|
|
114
|
+
position: absolute; top: -3px; right: -3px;
|
|
115
|
+
width: 8px; height: 8px; border-radius: 50%;
|
|
116
|
+
background: var(--get-fg); border: 2px solid var(--bg-white);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/* ── Global Auth Dropdown ── */
|
|
120
|
+
#global-auth-dropdown {
|
|
121
|
+
position: absolute; top: 56px; right: 16px; z-index: 100;
|
|
122
|
+
width: 340px; background: var(--bg-white);
|
|
123
|
+
border: 1.5px solid var(--border); border-radius: var(--radius);
|
|
124
|
+
box-shadow: var(--shadow); padding: 16px;
|
|
125
|
+
}
|
|
126
|
+
#global-auth-dropdown.hidden { display: none; }
|
|
127
|
+
.gauth-title {
|
|
128
|
+
font-size: 12px; font-weight: 700; color: var(--text);
|
|
129
|
+
margin-bottom: 12px; display: flex; align-items: center; gap: 8px;
|
|
130
|
+
}
|
|
131
|
+
.gauth-title svg { color: var(--accent); }
|
|
132
|
+
.gauth-active-badge {
|
|
133
|
+
font-size: 10px; font-weight: 600; padding: 2px 7px; border-radius: 10px;
|
|
134
|
+
background: var(--get-bg); border: 1px solid var(--get-bdr); color: var(--get-fg);
|
|
135
|
+
}
|
|
136
|
+
.gauth-type-row { display: flex; gap: 5px; flex-wrap: wrap; margin-bottom: 14px; }
|
|
137
|
+
.gauth-type-btn {
|
|
138
|
+
padding: 4px 11px; border-radius: 20px; border: 1.5px solid var(--border);
|
|
139
|
+
background: var(--bg-input); color: var(--text-muted);
|
|
140
|
+
font-size: 11px; font-weight: 500; cursor: pointer; transition: all .12s;
|
|
141
|
+
}
|
|
142
|
+
.gauth-type-btn:hover { border-color: var(--accent); color: var(--accent); }
|
|
143
|
+
.gauth-type-btn.active { border-color: var(--accent); background: var(--accent-bg); color: var(--accent); font-weight: 600; }
|
|
144
|
+
.gauth-footer { margin-top: 14px; display: flex; gap: 8px; }
|
|
145
|
+
.gauth-save-btn {
|
|
146
|
+
flex: 1; height: 32px; background: var(--accent); color: #fff; border: none;
|
|
147
|
+
border-radius: var(--radius-sm); font-size: 12px; font-weight: 600; cursor: pointer; transition: background .15s;
|
|
148
|
+
}
|
|
149
|
+
.gauth-save-btn:hover { background: #2563eb; }
|
|
150
|
+
.gauth-clear-btn {
|
|
151
|
+
height: 32px; padding: 0 12px; background: var(--bg-input); color: var(--text-muted);
|
|
152
|
+
border: 1.5px solid var(--border); border-radius: var(--radius-sm);
|
|
153
|
+
font-size: 12px; cursor: pointer; transition: all .15s;
|
|
154
|
+
}
|
|
155
|
+
.gauth-clear-btn:hover { border-color: var(--del-fg); color: var(--del-fg); }
|
|
112
156
|
|
|
113
157
|
/* ── Main ── */
|
|
114
158
|
#main { display: flex; flex: 1; overflow: hidden; }
|
|
@@ -137,9 +181,33 @@ function getExplorerHtml(options) {
|
|
|
137
181
|
#search::placeholder { color: var(--text-dim); }
|
|
138
182
|
.search-icon { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: var(--text-dim); pointer-events: none; }
|
|
139
183
|
|
|
140
|
-
#route-list { flex: 1; overflow-y: auto; padding:
|
|
184
|
+
#route-list { flex: 1; overflow-y: auto; padding: 4px 8px 16px; min-height: 0; }
|
|
141
185
|
.route-count { font-size: 10px; color: var(--text-dim); padding: 8px 6px 4px; font-weight: 500; }
|
|
142
186
|
|
|
187
|
+
.route-group { margin-bottom: 4px; }
|
|
188
|
+
.route-group-header {
|
|
189
|
+
display: flex; align-items: center; gap: 6px;
|
|
190
|
+
padding: 6px 8px 4px; cursor: pointer; user-select: none;
|
|
191
|
+
border-radius: var(--radius-sm);
|
|
192
|
+
transition: background .12s;
|
|
193
|
+
}
|
|
194
|
+
.route-group-header:hover { background: var(--bg-hover); }
|
|
195
|
+
.route-group-name {
|
|
196
|
+
font-size: 11px; font-weight: 700; color: var(--text);
|
|
197
|
+
text-transform: uppercase; letter-spacing: 0.5px; flex: 1;
|
|
198
|
+
}
|
|
199
|
+
.route-group-count {
|
|
200
|
+
font-size: 10px; font-weight: 600; color: var(--text-dim);
|
|
201
|
+
background: var(--bg-hover); border: 1px solid var(--border);
|
|
202
|
+
padding: 1px 6px; border-radius: 10px;
|
|
203
|
+
}
|
|
204
|
+
.route-group-chevron {
|
|
205
|
+
color: var(--text-dim); transition: transform .2s; flex-shrink: 0;
|
|
206
|
+
}
|
|
207
|
+
.route-group-chevron.collapsed { transform: rotate(-90deg); }
|
|
208
|
+
.route-group-body { padding-left: 4px; }
|
|
209
|
+
.route-group-body.collapsed { display: none; }
|
|
210
|
+
|
|
143
211
|
.route-item {
|
|
144
212
|
display: flex; align-items: center; gap: 8px;
|
|
145
213
|
padding: 7px 9px; border-radius: var(--radius-sm);
|
|
@@ -166,6 +234,10 @@ function getExplorerHtml(options) {
|
|
|
166
234
|
.method-HEAD { background: var(--head-bg); border-color: var(--head-bdr); color: var(--head-fg); }
|
|
167
235
|
.method-OPTIONS{ background: var(--opt-bg); border-color: var(--opt-bdr); color: var(--opt-fg); }
|
|
168
236
|
|
|
237
|
+
.route-desc {
|
|
238
|
+
display: block; font-size: 10px; color: var(--text-dim); margin-top: 1px;
|
|
239
|
+
overflow: hidden; text-overflow: ellipsis; white-space: nowrap; width: 100%;
|
|
240
|
+
}
|
|
169
241
|
.route-path {
|
|
170
242
|
font-family: var(--font-mono); font-size: 11.5px; color: var(--text-muted);
|
|
171
243
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1;
|
|
@@ -177,7 +249,74 @@ function getExplorerHtml(options) {
|
|
|
177
249
|
#sidebar-resize:hover, #sidebar-resize.dragging { background: var(--accent); opacity: .4; }
|
|
178
250
|
|
|
179
251
|
/* ── Content ── */
|
|
180
|
-
#content { flex: 1; display: flex; flex-direction:
|
|
252
|
+
#content { flex: 1; display: flex; flex-direction: row; overflow: hidden; min-width: 0; }
|
|
253
|
+
#content-main { flex: 1; display: flex; flex-direction: column; overflow: hidden; min-width: 0; }
|
|
254
|
+
|
|
255
|
+
/* ── Summary Panel ── */
|
|
256
|
+
#summary-panel {
|
|
257
|
+
width: 240px; flex-shrink: 0; border-left: 1px solid var(--border);
|
|
258
|
+
background: var(--bg-white); display: flex; flex-direction: column; overflow: hidden;
|
|
259
|
+
}
|
|
260
|
+
#summary-panel.hidden { display: none; }
|
|
261
|
+
.summary-header {
|
|
262
|
+
padding: 10px 14px 8px; border-bottom: 1px solid var(--border); flex-shrink: 0;
|
|
263
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
264
|
+
}
|
|
265
|
+
.summary-header-title { font-size: 11px; font-weight: 700; color: var(--text); text-transform: uppercase; letter-spacing: 0.5px; }
|
|
266
|
+
#summary-body { flex: 1; overflow-y: auto; padding: 10px 14px 16px; display: flex; flex-direction: column; gap: 14px; }
|
|
267
|
+
.summary-section { display: flex; flex-direction: column; gap: 6px; }
|
|
268
|
+
.summary-section-label {
|
|
269
|
+
font-size: 10px; font-weight: 700; color: var(--text-dim);
|
|
270
|
+
text-transform: uppercase; letter-spacing: 0.7px;
|
|
271
|
+
}
|
|
272
|
+
.summary-url {
|
|
273
|
+
font-family: var(--font-mono); font-size: 11px; color: var(--text);
|
|
274
|
+
word-break: break-all; line-height: 1.6; background: var(--bg-input);
|
|
275
|
+
border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 6px 8px;
|
|
276
|
+
}
|
|
277
|
+
.summary-url .url-path-param { color: var(--put-fg); font-weight: 600; }
|
|
278
|
+
.summary-row {
|
|
279
|
+
display: grid; grid-template-columns: 1fr 1fr; gap: 4px;
|
|
280
|
+
font-size: 11px; padding: 3px 0; border-bottom: 1px dashed var(--border);
|
|
281
|
+
}
|
|
282
|
+
.summary-row:last-child { border-bottom: none; }
|
|
283
|
+
.summary-key { font-family: var(--font-mono); color: var(--accent); font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
284
|
+
.summary-val { font-family: var(--font-mono); color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
285
|
+
.summary-none { font-size: 11px; color: var(--text-dim); font-style: italic; }
|
|
286
|
+
.summary-method-pill {
|
|
287
|
+
display: inline-block; font-family: var(--font-mono); font-size: 10px; font-weight: 700;
|
|
288
|
+
padding: 2px 7px; border-radius: 4px; margin-right: 6px; vertical-align: middle;
|
|
289
|
+
border-width: 1px; border-style: solid;
|
|
290
|
+
}
|
|
291
|
+
.summary-auth-row {
|
|
292
|
+
display: flex; align-items: center; gap: 6px; font-size: 11px;
|
|
293
|
+
padding: 4px 8px; background: var(--bg-input); border-radius: var(--radius-sm);
|
|
294
|
+
border: 1px solid var(--border);
|
|
295
|
+
}
|
|
296
|
+
.summary-auth-icon { color: var(--accent); flex-shrink: 0; }
|
|
297
|
+
.summary-auth-label { color: var(--text-muted); }
|
|
298
|
+
.summary-auth-val { font-family: var(--font-mono); color: var(--text); font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
299
|
+
.summary-body-preview {
|
|
300
|
+
font-family: var(--font-mono); font-size: 11px; color: var(--text-muted);
|
|
301
|
+
background: var(--bg-input); border: 1px solid var(--border); border-radius: var(--radius-sm);
|
|
302
|
+
padding: 6px 8px; max-height: 100px; overflow-y: auto; white-space: pre-wrap; word-break: break-all; line-height: 1.5;
|
|
303
|
+
}
|
|
304
|
+
.summary-empty {
|
|
305
|
+
flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
|
|
306
|
+
gap: 8px; padding: 20px; color: var(--text-dim); text-align: center;
|
|
307
|
+
}
|
|
308
|
+
.summary-empty p { font-size: 11px; line-height: 1.6; }
|
|
309
|
+
/* ── DocField rows (body/query/headers/response from docs file) ── */
|
|
310
|
+
.df-row { padding: 5px 0; border-bottom: 1px dashed var(--border); }
|
|
311
|
+
.df-row:last-child { border-bottom: none; }
|
|
312
|
+
.df-top { display: flex; align-items: center; gap: 5px; flex-wrap: wrap; }
|
|
313
|
+
.df-bottom { margin-top: 2px; }
|
|
314
|
+
.df-name { font-family: var(--font-mono); font-size: 11px; font-weight: 600; color: var(--accent); }
|
|
315
|
+
.df-type { font-size: 9px; font-weight: 600; padding: 1px 5px; border-radius: 3px; background: var(--bg-input); border: 1px solid var(--border); color: var(--text-muted); }
|
|
316
|
+
.df-req { font-size: 9px; font-weight: 700; padding: 1px 5px; border-radius: 3px; background: #fef2f2; border: 1px solid #fecaca; color: var(--del-fg); }
|
|
317
|
+
.df-opt { font-size: 9px; font-weight: 500; padding: 1px 5px; border-radius: 3px; background: var(--bg-input); border: 1px solid var(--border); color: var(--text-dim); }
|
|
318
|
+
.df-rules { font-size: 10px; color: var(--text-dim); font-family: var(--font-mono); }
|
|
319
|
+
.df-desc { font-size: 10px; color: var(--text-muted); font-style: italic; }
|
|
181
320
|
|
|
182
321
|
/* ── URL bar ── */
|
|
183
322
|
#url-bar {
|
|
@@ -462,7 +601,13 @@ function getExplorerHtml(options) {
|
|
|
462
601
|
<span class="base-url-label">Base URL</span>
|
|
463
602
|
<input id="base-url" type="url" spellcheck="false" />
|
|
464
603
|
</div>
|
|
465
|
-
<button class="icon-btn" id="
|
|
604
|
+
<button class="icon-btn" id="global-auth-btn" title="Global Authentication">
|
|
605
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
606
|
+
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"/>
|
|
607
|
+
<path d="M7 11V7a5 5 0 0 1 10 0v4"/>
|
|
608
|
+
</svg>
|
|
609
|
+
</button>
|
|
610
|
+
<button class="icon-btn" id="reload-btn" title="Reload routes">
|
|
466
611
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
467
612
|
<polyline points="23 4 23 10 17 10"/>
|
|
468
613
|
<path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/>
|
|
@@ -470,6 +615,48 @@ function getExplorerHtml(options) {
|
|
|
470
615
|
</button>
|
|
471
616
|
</header>
|
|
472
617
|
|
|
618
|
+
<!-- Global Auth Dropdown -->
|
|
619
|
+
<div id="global-auth-dropdown" class="hidden">
|
|
620
|
+
<div class="gauth-title">
|
|
621
|
+
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
|
|
622
|
+
Global Authentication
|
|
623
|
+
<span class="gauth-active-badge hidden" id="gauth-active-badge">Active</span>
|
|
624
|
+
</div>
|
|
625
|
+
<div class="gauth-type-row">
|
|
626
|
+
<button class="gauth-type-btn active" data-gauth="none">None</button>
|
|
627
|
+
<button class="gauth-type-btn" data-gauth="bearer">Bearer Token</button>
|
|
628
|
+
<button class="gauth-type-btn" data-gauth="apikey">API Key</button>
|
|
629
|
+
</div>
|
|
630
|
+
<div id="gauth-none-panel" class="text-muted" style="font-size:12px">No global auth. Per-request auth (Auth tab) still applies.</div>
|
|
631
|
+
<div id="gauth-bearer-panel" class="hidden flex-col gap-6">
|
|
632
|
+
<div class="field-group">
|
|
633
|
+
<label class="field-label">Bearer Token</label>
|
|
634
|
+
<input class="kv-input" id="gauth-bearer-token" type="text" style="width:100%" placeholder="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" spellcheck="false" autocomplete="off" />
|
|
635
|
+
</div>
|
|
636
|
+
</div>
|
|
637
|
+
<div id="gauth-apikey-panel" class="hidden flex-col gap-6">
|
|
638
|
+
<div class="field-group">
|
|
639
|
+
<label class="field-label">Key Name</label>
|
|
640
|
+
<input class="kv-input" id="gauth-apikey-name" type="text" style="width:100%" placeholder="X-API-Key" value="X-API-Key" spellcheck="false" />
|
|
641
|
+
</div>
|
|
642
|
+
<div class="field-group">
|
|
643
|
+
<label class="field-label">Key Value</label>
|
|
644
|
+
<input class="kv-input" id="gauth-apikey-value" type="text" style="width:100%" placeholder="your-secret-key" autocomplete="off" spellcheck="false" />
|
|
645
|
+
</div>
|
|
646
|
+
<div class="field-group">
|
|
647
|
+
<label class="field-label">Add To</label>
|
|
648
|
+
<select class="styled-select" id="gauth-apikey-in" style="width:160px">
|
|
649
|
+
<option value="header">Header</option>
|
|
650
|
+
<option value="query">Query String</option>
|
|
651
|
+
</select>
|
|
652
|
+
</div>
|
|
653
|
+
</div>
|
|
654
|
+
<div class="gauth-footer">
|
|
655
|
+
<button class="gauth-save-btn" id="gauth-save-btn">Apply Globally</button>
|
|
656
|
+
<button class="gauth-clear-btn" id="gauth-clear-btn">Clear</button>
|
|
657
|
+
</div>
|
|
658
|
+
</div>
|
|
659
|
+
|
|
473
660
|
<!-- Main -->
|
|
474
661
|
<div id="main">
|
|
475
662
|
|
|
@@ -491,6 +678,7 @@ function getExplorerHtml(options) {
|
|
|
491
678
|
|
|
492
679
|
<!-- Content -->
|
|
493
680
|
<div id="content">
|
|
681
|
+
<div id="content-main">
|
|
494
682
|
|
|
495
683
|
<!-- URL bar -->
|
|
496
684
|
<div id="url-bar">
|
|
@@ -635,9 +823,25 @@ function getExplorerHtml(options) {
|
|
|
635
823
|
</div>
|
|
636
824
|
|
|
637
825
|
</div>
|
|
826
|
+
</div><!-- content-main -->
|
|
827
|
+
|
|
828
|
+
<!-- Summary Panel -->
|
|
829
|
+
<div id="summary-panel" class="hidden">
|
|
830
|
+
<div class="summary-header">
|
|
831
|
+
<span class="summary-header-title">Request Summary</span>
|
|
832
|
+
</div>
|
|
833
|
+
<div id="summary-body">
|
|
834
|
+
<div class="summary-empty" id="summary-empty">
|
|
835
|
+
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="var(--text-dim)" stroke-width="1.5"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg>
|
|
836
|
+
<p>Select a route to see the request summary</p>
|
|
837
|
+
</div>
|
|
838
|
+
<div id="summary-content" class="hidden" style="display:flex;flex-direction:column;gap:14px"></div>
|
|
839
|
+
</div>
|
|
638
840
|
</div>
|
|
639
|
-
|
|
640
|
-
</div
|
|
841
|
+
|
|
842
|
+
</div><!-- content -->
|
|
843
|
+
</div><!-- main -->
|
|
844
|
+
</div><!-- app -->
|
|
641
845
|
|
|
642
846
|
<script>
|
|
643
847
|
const CONFIG = ${config};
|
|
@@ -648,7 +852,8 @@ const S = {
|
|
|
648
852
|
pathParams: {}, queryParams: [], reqHeaders: [],
|
|
649
853
|
auth: { type: 'none', bearerToken: '', basicUsername: '', basicPassword: '', apiKeyName: 'X-API-Key', apiKeyValue: '', apiKeyIn: 'header' },
|
|
650
854
|
body: { type: 'none', content: '' },
|
|
651
|
-
response: null, reqTab: 'params', resTab: 'body', loading: false, _uid: 0,
|
|
855
|
+
response: null, reqTab: 'params', resTab: 'body', loading: false, _uid: 0, collapsedGroups: new Set(),
|
|
856
|
+
globalAuth: { type: 'none', bearerToken: '', apiKeyName: 'X-API-Key', apiKeyValue: '', apiKeyIn: 'header' },
|
|
652
857
|
};
|
|
653
858
|
function uid() { return ++S._uid; }
|
|
654
859
|
|
|
@@ -685,6 +890,7 @@ function init() {
|
|
|
685
890
|
if (isLocal) el.envTag.classList.add('dev');
|
|
686
891
|
bindEvents();
|
|
687
892
|
loadRoutes();
|
|
893
|
+
renderSummary();
|
|
688
894
|
}
|
|
689
895
|
|
|
690
896
|
// ── Routes ─────────────────────────────────────────────────────────
|
|
@@ -709,25 +915,85 @@ function applyFilter() {
|
|
|
709
915
|
S.filtered = q ? base.filter(r => r.path.toLowerCase().includes(q) || r.method.toLowerCase().includes(q)) : base;
|
|
710
916
|
}
|
|
711
917
|
|
|
918
|
+
function groupRoutes(routes) {
|
|
919
|
+
const groups = {};
|
|
920
|
+
routes.forEach(r => {
|
|
921
|
+
// Use group from docs file if available, otherwise fall back to first path segment
|
|
922
|
+
const key = r.group || (r.path.split('/').filter(Boolean)[0] || 'general');
|
|
923
|
+
if (!groups[key]) groups[key] = [];
|
|
924
|
+
groups[key].push(r);
|
|
925
|
+
});
|
|
926
|
+
return groups;
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
function renderRouteItem(r) {
|
|
930
|
+
const active = S.selected?.method === r.method && S.selected?.path === r.path ? ' active' : '';
|
|
931
|
+
const desc = r.description ? \`<span class="route-desc">\${esc(r.description.split('\\n')[0])}</span>\` : '';
|
|
932
|
+
return \`<div class="route-item\${active}" data-method="\${r.method}" data-path="\${esc(r.path)}">
|
|
933
|
+
<span class="method-badge method-\${r.method}">\${r.method}</span>
|
|
934
|
+
<span class="route-path" title="\${esc(r.path)}">\${esc(r.path)}</span>
|
|
935
|
+
\${desc}
|
|
936
|
+
</div>\`;
|
|
937
|
+
}
|
|
938
|
+
|
|
712
939
|
function renderRouteList() {
|
|
713
940
|
if (!S.filtered.length) {
|
|
714
941
|
el.routeList.innerHTML = \`<div class="empty-routes">\${S.routes.length ? 'No routes match your search.' : 'No routes found.'}</div>\`;
|
|
715
942
|
return;
|
|
716
943
|
}
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
944
|
+
|
|
945
|
+
const searching = el.search.value.trim().length > 0;
|
|
946
|
+
const total = S.filtered.length;
|
|
947
|
+
|
|
948
|
+
// When searching, show flat list; otherwise group by prefix
|
|
949
|
+
if (searching) {
|
|
950
|
+
el.routeList.innerHTML =
|
|
951
|
+
\`<div class="route-count">\${total} result\${total !== 1 ? 's' : ''}</div>\` +
|
|
952
|
+
S.filtered.map(renderRouteItem).join('');
|
|
953
|
+
} else {
|
|
954
|
+
const groups = groupRoutes(S.filtered);
|
|
955
|
+
el.routeList.innerHTML =
|
|
956
|
+
\`<div class="route-count">\${total} route\${total !== 1 ? 's' : ''}</div>\` +
|
|
957
|
+
Object.entries(groups).map(([name, routes]) => {
|
|
958
|
+
const isCollapsed = S.collapsedGroups && S.collapsedGroups.has(name) ? 'collapsed' : '';
|
|
959
|
+
return \`<div class="route-group" data-group="\${esc(name)}">
|
|
960
|
+
<div class="route-group-header" data-toggle="\${esc(name)}">
|
|
961
|
+
<svg class="route-group-chevron \${isCollapsed}" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg>
|
|
962
|
+
<span class="route-group-name">/\${esc(name)}</span>
|
|
963
|
+
<span class="route-group-count">\${routes.length}</span>
|
|
964
|
+
</div>
|
|
965
|
+
<div class="route-group-body \${isCollapsed}">\${routes.map(renderRouteItem).join('')}</div>
|
|
966
|
+
</div>\`;
|
|
967
|
+
}).join('');
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
// Click handlers for route items
|
|
725
971
|
el.routeList.querySelectorAll('.route-item').forEach(item => {
|
|
726
972
|
item.addEventListener('click', () => {
|
|
727
973
|
const route = S.routes.find(r => r.method === item.dataset.method && r.path === item.dataset.path);
|
|
728
974
|
if (route) selectRoute(route);
|
|
729
975
|
});
|
|
730
976
|
});
|
|
977
|
+
|
|
978
|
+
// Collapse/expand group headers
|
|
979
|
+
el.routeList.querySelectorAll('[data-toggle]').forEach(header => {
|
|
980
|
+
header.addEventListener('click', () => {
|
|
981
|
+
const name = header.dataset.toggle;
|
|
982
|
+
if (!S.collapsedGroups) S.collapsedGroups = new Set();
|
|
983
|
+
const group = header.closest('.route-group');
|
|
984
|
+
const body = group.querySelector('.route-group-body');
|
|
985
|
+
const chevron = group.querySelector('.route-group-chevron');
|
|
986
|
+
if (S.collapsedGroups.has(name)) {
|
|
987
|
+
S.collapsedGroups.delete(name);
|
|
988
|
+
body.classList.remove('collapsed');
|
|
989
|
+
chevron.classList.remove('collapsed');
|
|
990
|
+
} else {
|
|
991
|
+
S.collapsedGroups.add(name);
|
|
992
|
+
body.classList.add('collapsed');
|
|
993
|
+
chevron.classList.add('collapsed');
|
|
994
|
+
}
|
|
995
|
+
});
|
|
996
|
+
});
|
|
731
997
|
}
|
|
732
998
|
|
|
733
999
|
function selectRoute(route) {
|
|
@@ -735,7 +1001,7 @@ function selectRoute(route) {
|
|
|
735
1001
|
S.pathParams = {};
|
|
736
1002
|
(route.params || []).forEach(p => { S.pathParams[p] = ''; });
|
|
737
1003
|
S.response = null;
|
|
738
|
-
renderResponse(); renderUrlBar(); renderPathParams(); renderRouteList();
|
|
1004
|
+
renderResponse(); renderUrlBar(); renderPathParams(); renderRouteList(); renderSummary();
|
|
739
1005
|
el.sendBtn.disabled = false;
|
|
740
1006
|
el.methodPill.style.visibility = 'visible';
|
|
741
1007
|
|
|
@@ -764,8 +1030,13 @@ function buildUrl() {
|
|
|
764
1030
|
Object.entries(S.pathParams).forEach(([k, v]) => {
|
|
765
1031
|
path = path.replace(':' + k, encodeURIComponent(v || (':' + k)));
|
|
766
1032
|
});
|
|
767
|
-
|
|
768
|
-
|
|
1033
|
+
// Per-route apikey in query (overrides global if set)
|
|
1034
|
+
const perRouteQpAuth = S.auth.type === 'apikey' && S.auth.apiKeyIn === 'query' && S.auth.apiKeyValue
|
|
1035
|
+
? [[S.auth.apiKeyName, S.auth.apiKeyValue]] : [];
|
|
1036
|
+
// Global apikey in query (only if per-route auth is 'none')
|
|
1037
|
+
const globalQpAuth = S.auth.type === 'none' && S.globalAuth.type === 'apikey' && S.globalAuth.apiKeyIn === 'query' && S.globalAuth.apiKeyValue
|
|
1038
|
+
? [[S.globalAuth.apiKeyName, S.globalAuth.apiKeyValue]] : [];
|
|
1039
|
+
const qp = [...S.queryParams.filter(p => p.enabled && p.key).map(p => [p.key, p.value]), ...perRouteQpAuth, ...globalQpAuth];
|
|
769
1040
|
const qs = qp.map(([k, v]) => encodeURIComponent(k) + '=' + encodeURIComponent(v)).join('&');
|
|
770
1041
|
return base + path + (qs ? '?' + qs : '');
|
|
771
1042
|
}
|
|
@@ -788,7 +1059,7 @@ function renderPathParams() {
|
|
|
788
1059
|
<input class="kv-input" data-path-param="\${esc(p)}" placeholder="value" value="\${esc(S.pathParams[p] || '')}" spellcheck="false" />
|
|
789
1060
|
</div>\`).join('');
|
|
790
1061
|
el.pathParamsRows.querySelectorAll('[data-path-param]').forEach(input => {
|
|
791
|
-
input.addEventListener('input', () => { S.pathParams[input.dataset.pathParam] = input.value; renderUrlBar(); });
|
|
1062
|
+
input.addEventListener('input', () => { S.pathParams[input.dataset.pathParam] = input.value; renderUrlBar(); renderSummary(); });
|
|
792
1063
|
});
|
|
793
1064
|
}
|
|
794
1065
|
|
|
@@ -840,6 +1111,14 @@ async function sendRequest() {
|
|
|
840
1111
|
|
|
841
1112
|
const headers = {};
|
|
842
1113
|
S.reqHeaders.filter(h => h.enabled && h.key).forEach(h => { headers[h.key] = h.value; });
|
|
1114
|
+
|
|
1115
|
+
// Apply global auth first (lower priority)
|
|
1116
|
+
if (S.globalAuth.type === 'bearer' && S.globalAuth.bearerToken)
|
|
1117
|
+
headers['Authorization'] = 'Bearer ' + S.globalAuth.bearerToken;
|
|
1118
|
+
else if (S.globalAuth.type === 'apikey' && S.globalAuth.apiKeyIn === 'header' && S.globalAuth.apiKeyValue)
|
|
1119
|
+
headers[S.globalAuth.apiKeyName || 'X-API-Key'] = S.globalAuth.apiKeyValue;
|
|
1120
|
+
|
|
1121
|
+
// Per-route auth overrides global (higher priority)
|
|
843
1122
|
if (S.auth.type === 'bearer' && S.auth.bearerToken) headers['Authorization'] = 'Bearer ' + S.auth.bearerToken;
|
|
844
1123
|
else if (S.auth.type === 'basic') headers['Authorization'] = 'Basic ' + btoa(S.auth.basicUsername + ':' + S.auth.basicPassword);
|
|
845
1124
|
else if (S.auth.type === 'apikey' && S.auth.apiKeyIn === 'header' && S.auth.apiKeyValue) headers[S.auth.apiKeyName || 'X-API-Key'] = S.auth.apiKeyValue;
|
|
@@ -913,6 +1192,155 @@ function renderResponse() {
|
|
|
913
1192
|
).join('');
|
|
914
1193
|
}
|
|
915
1194
|
|
|
1195
|
+
// ── Summary Panel ──────────────────────────────────────────────────
|
|
1196
|
+
function renderSummary() {
|
|
1197
|
+
const panel = $('summary-panel');
|
|
1198
|
+
const empty = $('summary-empty');
|
|
1199
|
+
const content = $('summary-content');
|
|
1200
|
+
|
|
1201
|
+
if (!S.selected) {
|
|
1202
|
+
panel.classList.remove('hidden');
|
|
1203
|
+
empty.style.display = 'flex';
|
|
1204
|
+
content.style.display = 'none';
|
|
1205
|
+
return;
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
panel.classList.remove('hidden');
|
|
1209
|
+
empty.style.display = 'none';
|
|
1210
|
+
content.style.display = 'flex';
|
|
1211
|
+
|
|
1212
|
+
const method = S.selected.method;
|
|
1213
|
+
const fullUrl = buildUrl();
|
|
1214
|
+
|
|
1215
|
+
// Highlight path params in URL
|
|
1216
|
+
let urlHtml = esc(fullUrl);
|
|
1217
|
+
(S.selected.params || []).forEach(p => {
|
|
1218
|
+
const val = S.pathParams[p];
|
|
1219
|
+
if (val) urlHtml = urlHtml.replace(esc(encodeURIComponent(val)), \`<span class="url-path-param">\${esc(val)}</span>\`);
|
|
1220
|
+
});
|
|
1221
|
+
|
|
1222
|
+
// Active query params
|
|
1223
|
+
const activeQuery = S.queryParams.filter(p => p.enabled && p.key);
|
|
1224
|
+
|
|
1225
|
+
// Active headers (custom + auth)
|
|
1226
|
+
const activeHeaders = S.reqHeaders.filter(h => h.enabled && h.key);
|
|
1227
|
+
|
|
1228
|
+
// Auth being used
|
|
1229
|
+
const effectiveAuth = S.auth.type !== 'none' ? S.auth : S.globalAuth;
|
|
1230
|
+
let authHtml = '';
|
|
1231
|
+
if (effectiveAuth.type === 'bearer') {
|
|
1232
|
+
const tok = effectiveAuth.bearerToken;
|
|
1233
|
+
authHtml = \`<div class="summary-auth-row">
|
|
1234
|
+
<svg class="summary-auth-icon" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
|
|
1235
|
+
<span class="summary-auth-label">Bearer</span>
|
|
1236
|
+
<span class="summary-auth-val">\${tok ? tok.slice(0,24) + (tok.length > 24 ? '…' : '') : '<em>no token</em>'}</span>
|
|
1237
|
+
\${S.auth.type === 'none' ? '<span style="font-size:9px;color:var(--text-dim);margin-left:auto">global</span>' : ''}
|
|
1238
|
+
</div>\`;
|
|
1239
|
+
} else if (effectiveAuth.type === 'basic') {
|
|
1240
|
+
authHtml = \`<div class="summary-auth-row">
|
|
1241
|
+
<svg class="summary-auth-icon" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
|
|
1242
|
+
<span class="summary-auth-label">Basic</span>
|
|
1243
|
+
<span class="summary-auth-val">\${esc(effectiveAuth.basicUsername || '—')}</span>
|
|
1244
|
+
</div>\`;
|
|
1245
|
+
} else if (effectiveAuth.type === 'apikey') {
|
|
1246
|
+
authHtml = \`<div class="summary-auth-row">
|
|
1247
|
+
<svg class="summary-auth-icon" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4"/></svg>
|
|
1248
|
+
<span class="summary-auth-label">\${esc(effectiveAuth.apiKeyName)}</span>
|
|
1249
|
+
<span class="summary-auth-val">\${effectiveAuth.apiKeyIn === 'query' ? '(query)' : '(header)'}</span>
|
|
1250
|
+
\${S.auth.type === 'none' && S.globalAuth.type !== 'none' ? '<span style="font-size:9px;color:var(--text-dim);margin-left:auto">global</span>' : ''}
|
|
1251
|
+
</div>\`;
|
|
1252
|
+
} else {
|
|
1253
|
+
authHtml = '<span class="summary-none">No auth</span>';
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
// Body
|
|
1257
|
+
let bodyHtml = '';
|
|
1258
|
+
if (['POST','PUT','PATCH'].includes(method) && S.body.type !== 'none' && S.body.content) {
|
|
1259
|
+
const preview = S.body.content.slice(0, 200) + (S.body.content.length > 200 ? '…' : '');
|
|
1260
|
+
bodyHtml = \`<div class="summary-section">
|
|
1261
|
+
<div class="summary-section-label">Body <span style="font-weight:400;text-transform:none;color:var(--text-dim)">\${S.body.type}</span></div>
|
|
1262
|
+
<div class="summary-body-preview">\${esc(preview)}</div>
|
|
1263
|
+
</div>\`;
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
content.innerHTML = \`
|
|
1267
|
+
<!-- URL -->
|
|
1268
|
+
<div class="summary-section">
|
|
1269
|
+
<div class="summary-section-label">
|
|
1270
|
+
<span class="summary-method-pill method-\${method}" style="border-color:inherit">\${method}</span>
|
|
1271
|
+
Endpoint
|
|
1272
|
+
</div>
|
|
1273
|
+
<div class="summary-url">\${urlHtml}</div>
|
|
1274
|
+
\${S.selected.description ? \`<div style="font-size:11px;color:var(--text-muted);margin-top:5px;line-height:1.5">\${esc(S.selected.description)}</div>\` : ''}
|
|
1275
|
+
</div>
|
|
1276
|
+
|
|
1277
|
+
<!-- Path params -->
|
|
1278
|
+
\${(S.selected.params || []).length ? \`<div class="summary-section">
|
|
1279
|
+
<div class="summary-section-label">Path Params</div>
|
|
1280
|
+
\${S.selected.params.map(p => \`<div class="summary-row">
|
|
1281
|
+
<span class="summary-key">:\${esc(p)}</span>
|
|
1282
|
+
<span class="summary-val">\${S.pathParams[p] ? esc(S.pathParams[p]) : '<em style="color:var(--del-fg)">empty</em>'}</span>
|
|
1283
|
+
</div>\`).join('')}
|
|
1284
|
+
</div>\` : ''}
|
|
1285
|
+
|
|
1286
|
+
<!-- Query params -->
|
|
1287
|
+
<div class="summary-section">
|
|
1288
|
+
<div class="summary-section-label">Query Params</div>
|
|
1289
|
+
\${activeQuery.length
|
|
1290
|
+
? activeQuery.map(p => \`<div class="summary-row">
|
|
1291
|
+
<span class="summary-key">\${esc(p.key)}</span>
|
|
1292
|
+
<span class="summary-val">\${esc(p.value)}</span>
|
|
1293
|
+
</div>\`).join('')
|
|
1294
|
+
: '<span class="summary-none">None</span>'}
|
|
1295
|
+
</div>
|
|
1296
|
+
|
|
1297
|
+
<!-- Headers -->
|
|
1298
|
+
<div class="summary-section">
|
|
1299
|
+
<div class="summary-section-label">Headers</div>
|
|
1300
|
+
\${activeHeaders.length
|
|
1301
|
+
? activeHeaders.map(h => \`<div class="summary-row">
|
|
1302
|
+
<span class="summary-key">\${esc(h.key)}</span>
|
|
1303
|
+
<span class="summary-val">\${esc(h.value)}</span>
|
|
1304
|
+
</div>\`).join('')
|
|
1305
|
+
: '<span class="summary-none">None</span>'}
|
|
1306
|
+
</div>
|
|
1307
|
+
|
|
1308
|
+
<!-- Auth -->
|
|
1309
|
+
<div class="summary-section">
|
|
1310
|
+
<div class="summary-section-label">Auth</div>
|
|
1311
|
+
\${authHtml}
|
|
1312
|
+
</div>
|
|
1313
|
+
|
|
1314
|
+
\${bodyHtml}
|
|
1315
|
+
|
|
1316
|
+
\${renderDocFields('Body Schema', S.selected.body)}
|
|
1317
|
+
\${renderDocFields('Query Params', S.selected.query)}
|
|
1318
|
+
\${renderDocFields('Required Headers', S.selected.headers)}
|
|
1319
|
+
\${renderDocFields('Response', S.selected.response)}
|
|
1320
|
+
\`;
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
// Render a list of DocField items (body/query/headers/response) in the summary panel
|
|
1324
|
+
function renderDocFields(label, fields) {
|
|
1325
|
+
if (!fields || fields.length === 0) return '';
|
|
1326
|
+
return \`<div class="summary-section">
|
|
1327
|
+
<div class="summary-section-label">\${label}</div>
|
|
1328
|
+
\${fields.map(f => {
|
|
1329
|
+
const typeTag = f.type ? \`<span class="df-type">\${esc(f.type)}</span>\` : '';
|
|
1330
|
+
const reqTag = f.required ? '<span class="df-req">required</span>' : '<span class="df-opt">optional</span>';
|
|
1331
|
+
const rulesTxt = f.rules?.length ? \`<span class="df-rules">\${esc(f.rules.join(' · '))}</span>\` : '';
|
|
1332
|
+
const descTxt = f.description ? \`<span class="df-desc">\${esc(f.description)}</span>\` : '';
|
|
1333
|
+
return \`<div class="df-row">
|
|
1334
|
+
<div class="df-top">
|
|
1335
|
+
<span class="df-name">\${esc(f.name)}</span>
|
|
1336
|
+
\${typeTag}\${reqTag}\${rulesTxt}
|
|
1337
|
+
</div>
|
|
1338
|
+
\${descTxt ? \`<div class="df-bottom">\${descTxt}</div>\` : ''}
|
|
1339
|
+
</div>\`;
|
|
1340
|
+
}).join('')}
|
|
1341
|
+
</div>\`;
|
|
1342
|
+
}
|
|
1343
|
+
|
|
916
1344
|
function syntaxHighlight(json) {
|
|
917
1345
|
return json.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>')
|
|
918
1346
|
.replace(/("(?:\\\\u[\\da-fA-F]{4}|\\\\[^u]|[^\\\\\\"])*"(?:\\s*:)?|\\b(?:true|false|null)\\b|-?\\d+(?:\\.\\d+)?(?:[eE][+\\-]?\\d+)?)/g, m => {
|
|
@@ -991,7 +1419,72 @@ function initSidebarResize() {
|
|
|
991
1419
|
function bindEvents() {
|
|
992
1420
|
el.reloadBtn.addEventListener('click', loadRoutes);
|
|
993
1421
|
el.search.addEventListener('input', () => { applyFilter(); renderRouteList(); });
|
|
994
|
-
|
|
1422
|
+
|
|
1423
|
+
// ── Global Auth ──
|
|
1424
|
+
const gauthBtn = $('global-auth-btn');
|
|
1425
|
+
const gauthDrop = $('global-auth-dropdown');
|
|
1426
|
+
const gauthBadge = $('gauth-active-badge');
|
|
1427
|
+
const gauthBearer = $('gauth-bearer-token');
|
|
1428
|
+
const gauthKeyName= $('gauth-apikey-name');
|
|
1429
|
+
const gauthKeyVal = $('gauth-apikey-value');
|
|
1430
|
+
const gauthKeyIn = $('gauth-apikey-in');
|
|
1431
|
+
|
|
1432
|
+
gauthBtn.addEventListener('click', e => {
|
|
1433
|
+
e.stopPropagation();
|
|
1434
|
+
gauthDrop.classList.toggle('hidden');
|
|
1435
|
+
gauthBtn.classList.toggle('active', !gauthDrop.classList.contains('hidden'));
|
|
1436
|
+
});
|
|
1437
|
+
document.addEventListener('click', e => {
|
|
1438
|
+
if (!gauthDrop.contains(e.target) && e.target !== gauthBtn) {
|
|
1439
|
+
gauthDrop.classList.add('hidden');
|
|
1440
|
+
gauthBtn.classList.remove('active');
|
|
1441
|
+
}
|
|
1442
|
+
});
|
|
1443
|
+
|
|
1444
|
+
document.querySelectorAll('.gauth-type-btn').forEach(btn => {
|
|
1445
|
+
btn.addEventListener('click', () => {
|
|
1446
|
+
document.querySelectorAll('.gauth-type-btn').forEach(b => b.classList.remove('active'));
|
|
1447
|
+
btn.classList.add('active');
|
|
1448
|
+
const t = btn.dataset.gauth;
|
|
1449
|
+
['none','bearer','apikey'].forEach(x => $('gauth-'+x+'-panel').classList.toggle('hidden', x !== t));
|
|
1450
|
+
});
|
|
1451
|
+
});
|
|
1452
|
+
|
|
1453
|
+
$('gauth-save-btn').addEventListener('click', () => {
|
|
1454
|
+
const activeBtn = document.querySelector('.gauth-type-btn.active');
|
|
1455
|
+
const type = activeBtn ? activeBtn.dataset.gauth : 'none';
|
|
1456
|
+
S.globalAuth = {
|
|
1457
|
+
type,
|
|
1458
|
+
bearerToken: gauthBearer.value,
|
|
1459
|
+
apiKeyName: gauthKeyName.value || 'X-API-Key',
|
|
1460
|
+
apiKeyValue: gauthKeyVal.value,
|
|
1461
|
+
apiKeyIn: gauthKeyIn.value,
|
|
1462
|
+
};
|
|
1463
|
+
const isActive = type !== 'none' && (S.globalAuth.bearerToken || S.globalAuth.apiKeyValue);
|
|
1464
|
+
gauthBadge.classList.toggle('hidden', !isActive);
|
|
1465
|
+
gauthBtn.classList.toggle('active', isActive);
|
|
1466
|
+
// Show dot indicator
|
|
1467
|
+
const dot = gauthBtn.querySelector('.dot');
|
|
1468
|
+
if (isActive && !dot) {
|
|
1469
|
+
const d = document.createElement('span'); d.className = 'dot';
|
|
1470
|
+
gauthBtn.appendChild(d);
|
|
1471
|
+
} else if (!isActive && dot) dot.remove();
|
|
1472
|
+
gauthDrop.classList.add('hidden');
|
|
1473
|
+
renderUrlBar(); renderSummary();
|
|
1474
|
+
});
|
|
1475
|
+
|
|
1476
|
+
$('gauth-clear-btn').addEventListener('click', () => {
|
|
1477
|
+
S.globalAuth = { type: 'none', bearerToken: '', apiKeyName: 'X-API-Key', apiKeyValue: '', apiKeyIn: 'header' };
|
|
1478
|
+
document.querySelectorAll('.gauth-type-btn').forEach(b => b.classList.remove('active'));
|
|
1479
|
+
document.querySelector('[data-gauth="none"]').classList.add('active');
|
|
1480
|
+
['none','bearer','apikey'].forEach(x => $('gauth-'+x+'-panel').classList.toggle('hidden', x !== 'none'));
|
|
1481
|
+
gauthBearer.value = ''; gauthKeyVal.value = '';
|
|
1482
|
+
gauthBadge.classList.add('hidden');
|
|
1483
|
+
gauthBtn.classList.remove('active');
|
|
1484
|
+
gauthBtn.querySelector('.dot')?.remove();
|
|
1485
|
+
renderUrlBar();
|
|
1486
|
+
});
|
|
1487
|
+
el.baseUrl.addEventListener('input', () => { renderUrlBar(); renderSummary(); });
|
|
995
1488
|
el.sendBtn.addEventListener('click', sendRequest);
|
|
996
1489
|
|
|
997
1490
|
el.addQueryBtn.addEventListener('click', () => {
|
|
@@ -1029,7 +1522,7 @@ function bindEvents() {
|
|
|
1029
1522
|
el.formatBtn.style.display = S.body.type === 'json' ? '' : 'none';
|
|
1030
1523
|
});
|
|
1031
1524
|
});
|
|
1032
|
-
el.bodyEditor.addEventListener('input', () => { S.body.content = el.bodyEditor.value; });
|
|
1525
|
+
el.bodyEditor.addEventListener('input', () => { S.body.content = el.bodyEditor.value; renderSummary(); });
|
|
1033
1526
|
el.formatBtn.addEventListener('click', () => {
|
|
1034
1527
|
try {
|
|
1035
1528
|
el.bodyEditor.value = JSON.stringify(JSON.parse(el.bodyEditor.value), null, 2);
|