nest-scramble 1.4.9 → 1.5.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 +81 -97
- package/dist/controllers/DocsController.js +473 -473
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -149,7 +149,7 @@ You'll see a beautiful dashboard in your terminal:
|
|
|
149
149
|
|
|
150
150
|
Then open your browser:
|
|
151
151
|
|
|
152
|
-
- **📖
|
|
152
|
+
- **📖 Professional API Docs (Modal UI)**: http://localhost:3000/docs
|
|
153
153
|
- **📄 OpenAPI JSON Spec**: http://localhost:3000/docs-json
|
|
154
154
|
- **🎭 Mock Endpoints**: http://localhost:3000/scramble-mock/*
|
|
155
155
|
|
|
@@ -164,20 +164,25 @@ Then open your browser:
|
|
|
164
164
|
|
|
165
165
|
```typescript
|
|
166
166
|
NestScrambleModule.forRoot({
|
|
167
|
+
// Documentation path served by the module
|
|
168
|
+
path: '/docs', // default: '/docs'
|
|
169
|
+
|
|
167
170
|
// Source directory to scan for controllers
|
|
168
171
|
sourcePath: 'src', // default: 'src'
|
|
169
172
|
|
|
170
173
|
// API base URL for OpenAPI spec
|
|
171
174
|
baseUrl: 'http://localhost:3000', // default: 'http://localhost:3000'
|
|
172
175
|
|
|
173
|
-
// Enable live mocking
|
|
176
|
+
// Enable live mocking endpoints under /scramble-mock/*
|
|
174
177
|
enableMock: true, // default: true
|
|
175
178
|
|
|
176
|
-
// Auto-export Postman collection
|
|
177
|
-
autoExportPostman:
|
|
179
|
+
// Auto-export a Postman collection when generating
|
|
180
|
+
autoExportPostman: true,
|
|
181
|
+
postmanOutputPath: 'collection.json',
|
|
178
182
|
|
|
179
|
-
//
|
|
180
|
-
|
|
183
|
+
// API metadata
|
|
184
|
+
apiTitle: 'My API', // API documentation title
|
|
185
|
+
apiVersion: '1.0.0', // API version number
|
|
181
186
|
})
|
|
182
187
|
```
|
|
183
188
|
|
|
@@ -185,11 +190,14 @@ NestScrambleModule.forRoot({
|
|
|
185
190
|
|
|
186
191
|
| Option | Type | Default | Description |
|
|
187
192
|
|--------|------|---------|-------------|
|
|
193
|
+
| `path` | `string` | `'/docs'` | Documentation route path |
|
|
188
194
|
| `sourcePath` | `string` | `'src'` | Directory where your NestJS controllers are located |
|
|
189
195
|
| `baseUrl` | `string` | `'http://localhost:3000'` | Base URL for your API (used in OpenAPI spec) |
|
|
190
196
|
| `enableMock` | `boolean` | `true` | Enable `/scramble-mock/*` endpoints for testing |
|
|
191
197
|
| `autoExportPostman` | `boolean` | `false` | Automatically generate Postman collection file |
|
|
192
198
|
| `postmanOutputPath` | `string` | `'collection.json'` | Output path for Postman collection |
|
|
199
|
+
| `apiTitle` | `string` | Auto-detected | API documentation title |
|
|
200
|
+
| `apiVersion` | `string` | Auto-detected | API version number |
|
|
193
201
|
|
|
194
202
|
## 🎭 Live Mocking Guide
|
|
195
203
|
|
|
@@ -346,94 +354,71 @@ jobs:
|
|
|
346
354
|
|
|
347
355
|
## 🎨 Documentation UI - Professional API Dashboard
|
|
348
356
|
|
|
349
|
-
### ✨
|
|
357
|
+
### ✨ Professional Dashboard Design (NEW!)
|
|
350
358
|
|
|
351
|
-
Nest-Scramble features a **
|
|
359
|
+
Nest-Scramble features a **modern, professional API dashboard** where each request is displayed on a separate page with focused documentation and beautiful interactions!
|
|
352
360
|
|
|
353
361
|
**🚀 Key Features:**
|
|
354
|
-
- **
|
|
355
|
-
- **
|
|
356
|
-
- **
|
|
357
|
-
- **
|
|
358
|
-
- **
|
|
359
|
-
- **
|
|
360
|
-
- GET =
|
|
361
|
-
- POST =
|
|
362
|
-
- PUT =
|
|
363
|
-
- PATCH =
|
|
364
|
-
- DELETE =
|
|
365
|
-
- **
|
|
366
|
-
- **
|
|
367
|
-
- **
|
|
368
|
-
- **
|
|
369
|
-
- **
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
-
|
|
379
|
-
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
-
|
|
383
|
-
|
|
384
|
-
**
|
|
385
|
-
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
-
|
|
389
|
-
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
**
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
theme: 'futuristic', // Professional dark dashboard
|
|
404
|
-
primaryColor: '#00f2ff', // Cyber-Cyan (default)
|
|
405
|
-
customDomainIcon: '/logo.png', // Your brand favicon
|
|
406
|
-
apiTitle: 'My Awesome API',
|
|
407
|
-
})
|
|
408
|
-
```
|
|
409
|
-
|
|
410
|
-
**Classic Theme:**
|
|
411
|
-
```typescript
|
|
412
|
-
NestScrambleModule.forRoot({
|
|
413
|
-
theme: 'classic', // Clean, light, professional
|
|
414
|
-
primaryColor: '#0066cc', // Corporate blue
|
|
415
|
-
apiTitle: 'Enterprise API',
|
|
416
|
-
})
|
|
417
|
-
```
|
|
418
|
-
|
|
419
|
-
**Custom Color Branding:**
|
|
420
|
-
```typescript
|
|
421
|
-
// One line changes the entire UI color scheme!
|
|
422
|
-
NestScrambleModule.forRoot({
|
|
423
|
-
primaryColor: '#a855f7', // Electric Purple
|
|
424
|
-
// or '#10b981' for Emerald Green
|
|
425
|
-
// or '#f59e0b' for Amber Orange
|
|
426
|
-
// or any hex color you want!
|
|
427
|
-
})
|
|
428
|
-
```
|
|
362
|
+
- **Separate Pages Per Endpoint** - Each request opens in its own modal with dedicated view
|
|
363
|
+
- **Smart Search & Filtering** - Real-time search across all endpoints
|
|
364
|
+
- **Controller Grouping** - Endpoints organized by tags with counters
|
|
365
|
+
- **Gradient Backgrounds** - Dynamic colors based on your brand
|
|
366
|
+
- **Smooth Animations** - Professional transitions and hover effects
|
|
367
|
+
- **Color-Coded HTTP Methods** - Visual distinction for different request types:
|
|
368
|
+
- GET = Green (`#c6f6d5`)
|
|
369
|
+
- POST = Blue (`#bee3f8`)
|
|
370
|
+
- PUT = Orange (`#feebc8`)
|
|
371
|
+
- PATCH = Purple (`#e9d8fd`)
|
|
372
|
+
- DELETE = Red (`#fed7d7`)
|
|
373
|
+
- **Professional Typography** - Clean, readable fonts with proper hierarchy
|
|
374
|
+
- **Responsive Design** - Works perfectly on desktop, tablet, and mobile
|
|
375
|
+
- **Modal-Based Navigation** - Clean overlay system for endpoint details
|
|
376
|
+
- **Parameter Documentation** - Clear display of request parameters and types
|
|
377
|
+
- **Response Examples** - Formatted JSON responses with syntax highlighting
|
|
378
|
+
|
|
379
|
+
### 📐 Single-Request Interface
|
|
380
|
+
|
|
381
|
+
Each endpoint gets its own dedicated modal view with organized sections:
|
|
382
|
+
|
|
383
|
+
**Header Section:**
|
|
384
|
+
- Large HTTP method badge with color coding
|
|
385
|
+
- Full endpoint path in monospace font
|
|
386
|
+
- Summary and description of the endpoint
|
|
387
|
+
- Close button with rotation animation
|
|
388
|
+
|
|
389
|
+
**Information Sections:**
|
|
390
|
+
- **Description** - Clear documentation of what the endpoint does
|
|
391
|
+
- **Parameters** - Detailed breakdown of path, query, and body parameters
|
|
392
|
+
- **Request Body** - JSON schema examples with syntax highlighting
|
|
393
|
+
- **Responses** - All possible response codes with examples
|
|
394
|
+
|
|
395
|
+
**Interactive Features:**
|
|
396
|
+
- Click any endpoint card to open detailed view
|
|
397
|
+
- Search across all endpoints in real-time
|
|
398
|
+
- Smooth transitions and micro-interactions
|
|
399
|
+
- Click outside modal or press X to close
|
|
400
|
+
- Keyboard-friendly navigation
|
|
401
|
+
|
|
402
|
+
### 🎨 Professional Design Features
|
|
403
|
+
|
|
404
|
+
**Current Design System:**
|
|
405
|
+
- **Beautiful Gradient Background** - Purple to blue gradient (`#667eea` to `#764ba2`)
|
|
406
|
+
- **Modal-Based Navigation** - Each endpoint opens in its own modal overlay
|
|
407
|
+
- **Smart Search Functionality** - Real-time filtering of endpoints
|
|
408
|
+
- **Controller Grouping** - Endpoints organized by tags with counters
|
|
409
|
+
- **Responsive Design** - Works perfectly on all devices
|
|
410
|
+
- **Smooth Animations** - Professional transitions and hover effects
|
|
429
411
|
|
|
430
412
|
### 🎭 UI Configuration Options
|
|
431
413
|
|
|
432
414
|
| Option | Type | Default | Description |
|
|
433
415
|
|--------|------|---------|-------------|
|
|
434
|
-
| `
|
|
435
|
-
| `
|
|
436
|
-
| `
|
|
416
|
+
| `path` | `string` | `'/docs'` | Documentation route path |
|
|
417
|
+
| `sourcePath` | `string` | `'src'` | Directory where your NestJS controllers are located |
|
|
418
|
+
| `baseUrl` | `string` | `'http://localhost:3000'` | Base URL for your API (used in OpenAPI spec) |
|
|
419
|
+
| `enableMock` | `boolean` | `true` | Enable `/scramble-mock/*` endpoints for testing |
|
|
420
|
+
| `autoExportPostman` | `boolean` | `false` | Automatically generate Postman collection file |
|
|
421
|
+
| `postmanOutputPath` | `string` | `'collection.json'` | Output path for Postman collection |
|
|
437
422
|
| `apiTitle` | `string` | Auto-detected | API documentation title |
|
|
438
423
|
| `apiVersion` | `string` | Auto-detected | API version number |
|
|
439
424
|
|
|
@@ -441,16 +426,15 @@ NestScrambleModule.forRoot({
|
|
|
441
426
|
|
|
442
427
|
When you visit `http://localhost:3000/docs`, you'll experience:
|
|
443
428
|
|
|
444
|
-
- 🎯 **
|
|
445
|
-
- 📂 **
|
|
446
|
-
-
|
|
447
|
-
-
|
|
448
|
-
-
|
|
449
|
-
-
|
|
450
|
-
-
|
|
451
|
-
-
|
|
452
|
-
-
|
|
453
|
-
- 🎭 **Developer Easter Eggs** - Check your browser console for surprises!
|
|
429
|
+
- 🎯 **Modal-Based Navigation** - Each endpoint opens in its own modal
|
|
430
|
+
- 📂 **Smart Grouping** - Endpoints organized by tags with counters
|
|
431
|
+
- 🔍 **Real-Time Search** - Instant filtering as you type
|
|
432
|
+
- 🎨 **Beautiful Gradients** - Professional purple to blue background
|
|
433
|
+
- 📱 **Responsive Design** - Perfect on desktop, tablet, and mobile
|
|
434
|
+
- ✨ **Smooth Animations** - Professional transitions and hover effects
|
|
435
|
+
- 📋 **Parameter Details** - Clear documentation of request parameters
|
|
436
|
+
- 💻 **Code Examples** - Formatted JSON with syntax highlighting
|
|
437
|
+
- 🔄 **Interactive Elements** - Click to explore, search to filter
|
|
454
438
|
|
|
455
439
|
### 🖥️ Terminal Dashboard
|
|
456
440
|
|
|
@@ -487,7 +471,7 @@ For complete UI customization guide, see:
|
|
|
487
471
|
|
|
488
472
|
| Endpoint | Description |
|
|
489
473
|
|----------|-------------|
|
|
490
|
-
| `GET /docs` |
|
|
474
|
+
| `GET /docs` | Professional API documentation with modal-based endpoint pages |
|
|
491
475
|
| `GET /docs-json` | OpenAPI 3.0 JSON specification |
|
|
492
476
|
| `GET /docs/json` | Legacy endpoint (same as above) |
|
|
493
477
|
| `GET /docs/spec` | OpenAPI spec as JSON response |
|
|
@@ -60,398 +60,398 @@ let DocsController = class DocsController {
|
|
|
60
60
|
}
|
|
61
61
|
generateMainPage(endpoints) {
|
|
62
62
|
const groupedEndpoints = this.groupByTag(endpoints);
|
|
63
|
-
return `<!DOCTYPE html>
|
|
64
|
-
<html lang="en">
|
|
65
|
-
<head>
|
|
66
|
-
<meta charset="UTF-8">
|
|
67
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
68
|
-
<title>${this.openApiSpec?.info?.title || 'API Documentation'}</title>
|
|
69
|
-
<style>
|
|
70
|
-
* {
|
|
71
|
-
margin: 0;
|
|
72
|
-
padding: 0;
|
|
73
|
-
box-sizing: border-box;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
body {
|
|
77
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
78
|
-
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
79
|
-
min-height: 100vh;
|
|
80
|
-
color: #1a202c;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
.container {
|
|
84
|
-
max-width: 1400px;
|
|
85
|
-
margin: 0 auto;
|
|
86
|
-
padding: 2rem;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
.header {
|
|
90
|
-
text-align: center;
|
|
91
|
-
color: white;
|
|
92
|
-
margin-bottom: 3rem;
|
|
93
|
-
animation: fadeInDown 0.6s ease-out;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
.header h1 {
|
|
97
|
-
font-size: 2.5rem;
|
|
98
|
-
font-weight: 700;
|
|
99
|
-
margin-bottom: 0.5rem;
|
|
100
|
-
text-shadow: 0 2px 10px rgba(0,0,0,0.2);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
.header p {
|
|
104
|
-
font-size: 1.1rem;
|
|
105
|
-
opacity: 0.95;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
.search-box {
|
|
109
|
-
max-width: 600px;
|
|
110
|
-
margin: 0 auto 3rem;
|
|
111
|
-
animation: fadeIn 0.8s ease-out;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
.search-input {
|
|
115
|
-
width: 100%;
|
|
116
|
-
padding: 1rem 1.5rem;
|
|
117
|
-
font-size: 1rem;
|
|
118
|
-
border: none;
|
|
119
|
-
border-radius: 12px;
|
|
120
|
-
background: white;
|
|
121
|
-
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
|
|
122
|
-
transition: all 0.3s ease;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
.search-input:focus {
|
|
126
|
-
outline: none;
|
|
127
|
-
box-shadow: 0 15px 40px rgba(0,0,0,0.3);
|
|
128
|
-
transform: translateY(-2px);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
.endpoints-grid {
|
|
132
|
-
display: grid;
|
|
133
|
-
gap: 1.5rem;
|
|
134
|
-
animation: fadeInUp 0.8s ease-out;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
.tag-section {
|
|
138
|
-
background: white;
|
|
139
|
-
border-radius: 16px;
|
|
140
|
-
padding: 2rem;
|
|
141
|
-
box-shadow: 0 10px 30px rgba(0,0,0,0.15);
|
|
142
|
-
transition: all 0.3s ease;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
.tag-section:hover {
|
|
146
|
-
box-shadow: 0 15px 40px rgba(0,0,0,0.2);
|
|
147
|
-
transform: translateY(-4px);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
.tag-title {
|
|
151
|
-
font-size: 1.5rem;
|
|
152
|
-
font-weight: 600;
|
|
153
|
-
margin-bottom: 1.5rem;
|
|
154
|
-
color: #2d3748;
|
|
155
|
-
display: flex;
|
|
156
|
-
align-items: center;
|
|
157
|
-
gap: 0.5rem;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
.tag-badge {
|
|
161
|
-
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
162
|
-
color: white;
|
|
163
|
-
padding: 0.25rem 0.75rem;
|
|
164
|
-
border-radius: 20px;
|
|
165
|
-
font-size: 0.875rem;
|
|
166
|
-
font-weight: 500;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
.endpoint-list {
|
|
170
|
-
display: grid;
|
|
171
|
-
gap: 1rem;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
.endpoint-card {
|
|
175
|
-
background: #f7fafc;
|
|
176
|
-
border-radius: 12px;
|
|
177
|
-
padding: 1.25rem;
|
|
178
|
-
cursor: pointer;
|
|
179
|
-
transition: all 0.3s ease;
|
|
180
|
-
border: 2px solid transparent;
|
|
181
|
-
text-decoration: none;
|
|
182
|
-
display: block;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
.endpoint-card:hover {
|
|
186
|
-
background: white;
|
|
187
|
-
border-color: #667eea;
|
|
188
|
-
transform: translateX(8px);
|
|
189
|
-
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.15);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
.endpoint-header {
|
|
193
|
-
display: flex;
|
|
194
|
-
align-items: center;
|
|
195
|
-
gap: 1rem;
|
|
196
|
-
margin-bottom: 0.75rem;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
.method-badge {
|
|
200
|
-
padding: 0.375rem 0.875rem;
|
|
201
|
-
border-radius: 6px;
|
|
202
|
-
font-weight: 600;
|
|
203
|
-
font-size: 0.875rem;
|
|
204
|
-
text-transform: uppercase;
|
|
205
|
-
letter-spacing: 0.5px;
|
|
206
|
-
min-width: 70px;
|
|
207
|
-
text-align: center;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
.method-get { background: #c6f6d5; color: #22543d; }
|
|
211
|
-
.method-post { background: #bee3f8; color: #2c5282; }
|
|
212
|
-
.method-put { background: #feebc8; color: #7c2d12; }
|
|
213
|
-
.method-patch { background: #e9d8fd; color: #44337a; }
|
|
214
|
-
.method-delete { background: #fed7d7; color: #742a2a; }
|
|
215
|
-
|
|
216
|
-
.endpoint-path {
|
|
217
|
-
font-family: 'Courier New', monospace;
|
|
218
|
-
font-size: 1rem;
|
|
219
|
-
color: #2d3748;
|
|
220
|
-
font-weight: 500;
|
|
221
|
-
flex: 1;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
.endpoint-summary {
|
|
225
|
-
color: #718096;
|
|
226
|
-
font-size: 0.95rem;
|
|
227
|
-
line-height: 1.5;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
.endpoint-detail {
|
|
231
|
-
display: none;
|
|
232
|
-
position: fixed;
|
|
233
|
-
top: 0;
|
|
234
|
-
left: 0;
|
|
235
|
-
right: 0;
|
|
236
|
-
bottom: 0;
|
|
237
|
-
background: rgba(0,0,0,0.5);
|
|
238
|
-
z-index: 1000;
|
|
239
|
-
overflow-y: auto;
|
|
240
|
-
animation: fadeIn 0.3s ease-out;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
.endpoint-detail.active {
|
|
244
|
-
display: flex;
|
|
245
|
-
align-items: center;
|
|
246
|
-
justify-content: center;
|
|
247
|
-
padding: 2rem;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
.detail-content {
|
|
251
|
-
background: white;
|
|
252
|
-
border-radius: 16px;
|
|
253
|
-
max-width: 900px;
|
|
254
|
-
width: 100%;
|
|
255
|
-
max-height: 90vh;
|
|
256
|
-
overflow-y: auto;
|
|
257
|
-
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
|
|
258
|
-
animation: slideUp 0.4s ease-out;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
.detail-header {
|
|
262
|
-
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
263
|
-
color: white;
|
|
264
|
-
padding: 2rem;
|
|
265
|
-
border-radius: 16px 16px 0 0;
|
|
266
|
-
position: sticky;
|
|
267
|
-
top: 0;
|
|
268
|
-
z-index: 10;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
.detail-close {
|
|
272
|
-
float: right;
|
|
273
|
-
background: rgba(255,255,255,0.2);
|
|
274
|
-
border: none;
|
|
275
|
-
color: white;
|
|
276
|
-
font-size: 1.5rem;
|
|
277
|
-
width: 40px;
|
|
278
|
-
height: 40px;
|
|
279
|
-
border-radius: 50%;
|
|
280
|
-
cursor: pointer;
|
|
281
|
-
transition: all 0.3s ease;
|
|
282
|
-
display: flex;
|
|
283
|
-
align-items: center;
|
|
284
|
-
justify-content: center;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
.detail-close:hover {
|
|
288
|
-
background: rgba(255,255,255,0.3);
|
|
289
|
-
transform: rotate(90deg);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
.detail-body {
|
|
293
|
-
padding: 2rem;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
.section {
|
|
297
|
-
margin-bottom: 2rem;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
.section-title {
|
|
301
|
-
font-size: 1.25rem;
|
|
302
|
-
font-weight: 600;
|
|
303
|
-
color: #2d3748;
|
|
304
|
-
margin-bottom: 1rem;
|
|
305
|
-
padding-bottom: 0.5rem;
|
|
306
|
-
border-bottom: 2px solid #e2e8f0;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
.param-item, .response-item {
|
|
310
|
-
background: #f7fafc;
|
|
311
|
-
padding: 1rem;
|
|
312
|
-
border-radius: 8px;
|
|
313
|
-
margin-bottom: 0.75rem;
|
|
314
|
-
border-left: 3px solid #667eea;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
.param-name, .response-code {
|
|
318
|
-
font-weight: 600;
|
|
319
|
-
color: #2d3748;
|
|
320
|
-
font-family: 'Courier New', monospace;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
.param-type {
|
|
324
|
-
color: #667eea;
|
|
325
|
-
font-size: 0.875rem;
|
|
326
|
-
font-weight: 500;
|
|
327
|
-
margin-left: 0.5rem;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
.param-desc {
|
|
331
|
-
color: #718096;
|
|
332
|
-
margin-top: 0.5rem;
|
|
333
|
-
font-size: 0.95rem;
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
.code-block {
|
|
337
|
-
background: #1a202c;
|
|
338
|
-
color: #e2e8f0;
|
|
339
|
-
padding: 1.5rem;
|
|
340
|
-
border-radius: 8px;
|
|
341
|
-
overflow-x: auto;
|
|
342
|
-
font-family: 'Courier New', monospace;
|
|
343
|
-
font-size: 0.9rem;
|
|
344
|
-
line-height: 1.6;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
.no-results {
|
|
348
|
-
text-align: center;
|
|
349
|
-
padding: 3rem;
|
|
350
|
-
color: white;
|
|
351
|
-
font-size: 1.1rem;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
@keyframes fadeIn {
|
|
355
|
-
from { opacity: 0; }
|
|
356
|
-
to { opacity: 1; }
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
@keyframes fadeInDown {
|
|
360
|
-
from { opacity: 0; transform: translateY(-20px); }
|
|
361
|
-
to { opacity: 1; transform: translateY(0); }
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
@keyframes fadeInUp {
|
|
365
|
-
from { opacity: 0; transform: translateY(20px); }
|
|
366
|
-
to { opacity: 1; transform: translateY(0); }
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
@keyframes slideUp {
|
|
370
|
-
from { opacity: 0; transform: translateY(40px); }
|
|
371
|
-
to { opacity: 1; transform: translateY(0); }
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
@media (max-width: 768px) {
|
|
375
|
-
.container { padding: 1rem; }
|
|
376
|
-
.header h1 { font-size: 2rem; }
|
|
377
|
-
.detail-content { margin: 1rem; }
|
|
378
|
-
}
|
|
379
|
-
</style>
|
|
380
|
-
</head>
|
|
381
|
-
<body>
|
|
382
|
-
<div class="container">
|
|
383
|
-
<div class="header">
|
|
384
|
-
<h1>${this.openApiSpec?.info?.title || 'API Documentation'}</h1>
|
|
385
|
-
<p>${this.openApiSpec?.info?.description || ''} ${this.openApiSpec?.info?.version ? `v${this.openApiSpec.info.version}` : ''}</p>
|
|
386
|
-
</div>
|
|
387
|
-
|
|
388
|
-
<div class="search-box">
|
|
389
|
-
<input type="text" class="search-input" id="searchInput" placeholder="🔍 Search endpoints...">
|
|
390
|
-
</div>
|
|
391
|
-
|
|
392
|
-
<div class="endpoints-grid" id="endpointsGrid">
|
|
393
|
-
${this.renderGroupedEndpoints(groupedEndpoints)}
|
|
394
|
-
</div>
|
|
395
|
-
|
|
396
|
-
<div class="no-results" id="noResults" style="display: none;">
|
|
397
|
-
No endpoints found matching your search.
|
|
398
|
-
</div>
|
|
399
|
-
</div>
|
|
400
|
-
|
|
401
|
-
${this.renderEndpointModals(endpoints)}
|
|
402
|
-
|
|
403
|
-
<script>
|
|
404
|
-
const searchInput = document.getElementById('searchInput');
|
|
405
|
-
const endpointsGrid = document.getElementById('endpointsGrid');
|
|
406
|
-
const noResults = document.getElementById('noResults');
|
|
407
|
-
|
|
408
|
-
searchInput.addEventListener('input', (e) => {
|
|
409
|
-
const query = e.target.value.toLowerCase();
|
|
410
|
-
const sections = endpointsGrid.querySelectorAll('.tag-section');
|
|
411
|
-
let hasResults = false;
|
|
412
|
-
|
|
413
|
-
sections.forEach(section => {
|
|
414
|
-
const cards = section.querySelectorAll('.endpoint-card');
|
|
415
|
-
let sectionHasResults = false;
|
|
416
|
-
|
|
417
|
-
cards.forEach(card => {
|
|
418
|
-
const text = card.textContent.toLowerCase();
|
|
419
|
-
if (text.includes(query)) {
|
|
420
|
-
card.style.display = 'block';
|
|
421
|
-
sectionHasResults = true;
|
|
422
|
-
hasResults = true;
|
|
423
|
-
} else {
|
|
424
|
-
card.style.display = 'none';
|
|
425
|
-
}
|
|
426
|
-
});
|
|
427
|
-
|
|
428
|
-
section.style.display = sectionHasResults ? 'block' : 'none';
|
|
429
|
-
});
|
|
430
|
-
|
|
431
|
-
noResults.style.display = hasResults ? 'none' : 'block';
|
|
432
|
-
endpointsGrid.style.display = hasResults ? 'grid' : 'none';
|
|
433
|
-
});
|
|
434
|
-
|
|
435
|
-
function openEndpoint(id) {
|
|
436
|
-
document.getElementById('detail-' + id).classList.add('active');
|
|
437
|
-
document.body.style.overflow = 'hidden';
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
function closeEndpoint(id) {
|
|
441
|
-
document.getElementById('detail-' + id).classList.remove('active');
|
|
442
|
-
document.body.style.overflow = 'auto';
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
document.querySelectorAll('.endpoint-detail').forEach(modal => {
|
|
446
|
-
modal.addEventListener('click', (e) => {
|
|
447
|
-
if (e.target === modal) {
|
|
448
|
-
modal.classList.remove('active');
|
|
449
|
-
document.body.style.overflow = 'auto';
|
|
450
|
-
}
|
|
451
|
-
});
|
|
452
|
-
});
|
|
453
|
-
</script>
|
|
454
|
-
</body>
|
|
63
|
+
return `<!DOCTYPE html>
|
|
64
|
+
<html lang="en">
|
|
65
|
+
<head>
|
|
66
|
+
<meta charset="UTF-8">
|
|
67
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
68
|
+
<title>${this.openApiSpec?.info?.title || 'API Documentation'}</title>
|
|
69
|
+
<style>
|
|
70
|
+
* {
|
|
71
|
+
margin: 0;
|
|
72
|
+
padding: 0;
|
|
73
|
+
box-sizing: border-box;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
body {
|
|
77
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
78
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
79
|
+
min-height: 100vh;
|
|
80
|
+
color: #1a202c;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.container {
|
|
84
|
+
max-width: 1400px;
|
|
85
|
+
margin: 0 auto;
|
|
86
|
+
padding: 2rem;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.header {
|
|
90
|
+
text-align: center;
|
|
91
|
+
color: white;
|
|
92
|
+
margin-bottom: 3rem;
|
|
93
|
+
animation: fadeInDown 0.6s ease-out;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.header h1 {
|
|
97
|
+
font-size: 2.5rem;
|
|
98
|
+
font-weight: 700;
|
|
99
|
+
margin-bottom: 0.5rem;
|
|
100
|
+
text-shadow: 0 2px 10px rgba(0,0,0,0.2);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.header p {
|
|
104
|
+
font-size: 1.1rem;
|
|
105
|
+
opacity: 0.95;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.search-box {
|
|
109
|
+
max-width: 600px;
|
|
110
|
+
margin: 0 auto 3rem;
|
|
111
|
+
animation: fadeIn 0.8s ease-out;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.search-input {
|
|
115
|
+
width: 100%;
|
|
116
|
+
padding: 1rem 1.5rem;
|
|
117
|
+
font-size: 1rem;
|
|
118
|
+
border: none;
|
|
119
|
+
border-radius: 12px;
|
|
120
|
+
background: white;
|
|
121
|
+
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
|
|
122
|
+
transition: all 0.3s ease;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.search-input:focus {
|
|
126
|
+
outline: none;
|
|
127
|
+
box-shadow: 0 15px 40px rgba(0,0,0,0.3);
|
|
128
|
+
transform: translateY(-2px);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.endpoints-grid {
|
|
132
|
+
display: grid;
|
|
133
|
+
gap: 1.5rem;
|
|
134
|
+
animation: fadeInUp 0.8s ease-out;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.tag-section {
|
|
138
|
+
background: white;
|
|
139
|
+
border-radius: 16px;
|
|
140
|
+
padding: 2rem;
|
|
141
|
+
box-shadow: 0 10px 30px rgba(0,0,0,0.15);
|
|
142
|
+
transition: all 0.3s ease;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.tag-section:hover {
|
|
146
|
+
box-shadow: 0 15px 40px rgba(0,0,0,0.2);
|
|
147
|
+
transform: translateY(-4px);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.tag-title {
|
|
151
|
+
font-size: 1.5rem;
|
|
152
|
+
font-weight: 600;
|
|
153
|
+
margin-bottom: 1.5rem;
|
|
154
|
+
color: #2d3748;
|
|
155
|
+
display: flex;
|
|
156
|
+
align-items: center;
|
|
157
|
+
gap: 0.5rem;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.tag-badge {
|
|
161
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
162
|
+
color: white;
|
|
163
|
+
padding: 0.25rem 0.75rem;
|
|
164
|
+
border-radius: 20px;
|
|
165
|
+
font-size: 0.875rem;
|
|
166
|
+
font-weight: 500;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.endpoint-list {
|
|
170
|
+
display: grid;
|
|
171
|
+
gap: 1rem;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.endpoint-card {
|
|
175
|
+
background: #f7fafc;
|
|
176
|
+
border-radius: 12px;
|
|
177
|
+
padding: 1.25rem;
|
|
178
|
+
cursor: pointer;
|
|
179
|
+
transition: all 0.3s ease;
|
|
180
|
+
border: 2px solid transparent;
|
|
181
|
+
text-decoration: none;
|
|
182
|
+
display: block;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.endpoint-card:hover {
|
|
186
|
+
background: white;
|
|
187
|
+
border-color: #667eea;
|
|
188
|
+
transform: translateX(8px);
|
|
189
|
+
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.15);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.endpoint-header {
|
|
193
|
+
display: flex;
|
|
194
|
+
align-items: center;
|
|
195
|
+
gap: 1rem;
|
|
196
|
+
margin-bottom: 0.75rem;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.method-badge {
|
|
200
|
+
padding: 0.375rem 0.875rem;
|
|
201
|
+
border-radius: 6px;
|
|
202
|
+
font-weight: 600;
|
|
203
|
+
font-size: 0.875rem;
|
|
204
|
+
text-transform: uppercase;
|
|
205
|
+
letter-spacing: 0.5px;
|
|
206
|
+
min-width: 70px;
|
|
207
|
+
text-align: center;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.method-get { background: #c6f6d5; color: #22543d; }
|
|
211
|
+
.method-post { background: #bee3f8; color: #2c5282; }
|
|
212
|
+
.method-put { background: #feebc8; color: #7c2d12; }
|
|
213
|
+
.method-patch { background: #e9d8fd; color: #44337a; }
|
|
214
|
+
.method-delete { background: #fed7d7; color: #742a2a; }
|
|
215
|
+
|
|
216
|
+
.endpoint-path {
|
|
217
|
+
font-family: 'Courier New', monospace;
|
|
218
|
+
font-size: 1rem;
|
|
219
|
+
color: #2d3748;
|
|
220
|
+
font-weight: 500;
|
|
221
|
+
flex: 1;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.endpoint-summary {
|
|
225
|
+
color: #718096;
|
|
226
|
+
font-size: 0.95rem;
|
|
227
|
+
line-height: 1.5;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.endpoint-detail {
|
|
231
|
+
display: none;
|
|
232
|
+
position: fixed;
|
|
233
|
+
top: 0;
|
|
234
|
+
left: 0;
|
|
235
|
+
right: 0;
|
|
236
|
+
bottom: 0;
|
|
237
|
+
background: rgba(0,0,0,0.5);
|
|
238
|
+
z-index: 1000;
|
|
239
|
+
overflow-y: auto;
|
|
240
|
+
animation: fadeIn 0.3s ease-out;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.endpoint-detail.active {
|
|
244
|
+
display: flex;
|
|
245
|
+
align-items: center;
|
|
246
|
+
justify-content: center;
|
|
247
|
+
padding: 2rem;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.detail-content {
|
|
251
|
+
background: white;
|
|
252
|
+
border-radius: 16px;
|
|
253
|
+
max-width: 900px;
|
|
254
|
+
width: 100%;
|
|
255
|
+
max-height: 90vh;
|
|
256
|
+
overflow-y: auto;
|
|
257
|
+
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
|
|
258
|
+
animation: slideUp 0.4s ease-out;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.detail-header {
|
|
262
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
263
|
+
color: white;
|
|
264
|
+
padding: 2rem;
|
|
265
|
+
border-radius: 16px 16px 0 0;
|
|
266
|
+
position: sticky;
|
|
267
|
+
top: 0;
|
|
268
|
+
z-index: 10;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
.detail-close {
|
|
272
|
+
float: right;
|
|
273
|
+
background: rgba(255,255,255,0.2);
|
|
274
|
+
border: none;
|
|
275
|
+
color: white;
|
|
276
|
+
font-size: 1.5rem;
|
|
277
|
+
width: 40px;
|
|
278
|
+
height: 40px;
|
|
279
|
+
border-radius: 50%;
|
|
280
|
+
cursor: pointer;
|
|
281
|
+
transition: all 0.3s ease;
|
|
282
|
+
display: flex;
|
|
283
|
+
align-items: center;
|
|
284
|
+
justify-content: center;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.detail-close:hover {
|
|
288
|
+
background: rgba(255,255,255,0.3);
|
|
289
|
+
transform: rotate(90deg);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.detail-body {
|
|
293
|
+
padding: 2rem;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
.section {
|
|
297
|
+
margin-bottom: 2rem;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.section-title {
|
|
301
|
+
font-size: 1.25rem;
|
|
302
|
+
font-weight: 600;
|
|
303
|
+
color: #2d3748;
|
|
304
|
+
margin-bottom: 1rem;
|
|
305
|
+
padding-bottom: 0.5rem;
|
|
306
|
+
border-bottom: 2px solid #e2e8f0;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
.param-item, .response-item {
|
|
310
|
+
background: #f7fafc;
|
|
311
|
+
padding: 1rem;
|
|
312
|
+
border-radius: 8px;
|
|
313
|
+
margin-bottom: 0.75rem;
|
|
314
|
+
border-left: 3px solid #667eea;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.param-name, .response-code {
|
|
318
|
+
font-weight: 600;
|
|
319
|
+
color: #2d3748;
|
|
320
|
+
font-family: 'Courier New', monospace;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
.param-type {
|
|
324
|
+
color: #667eea;
|
|
325
|
+
font-size: 0.875rem;
|
|
326
|
+
font-weight: 500;
|
|
327
|
+
margin-left: 0.5rem;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
.param-desc {
|
|
331
|
+
color: #718096;
|
|
332
|
+
margin-top: 0.5rem;
|
|
333
|
+
font-size: 0.95rem;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
.code-block {
|
|
337
|
+
background: #1a202c;
|
|
338
|
+
color: #e2e8f0;
|
|
339
|
+
padding: 1.5rem;
|
|
340
|
+
border-radius: 8px;
|
|
341
|
+
overflow-x: auto;
|
|
342
|
+
font-family: 'Courier New', monospace;
|
|
343
|
+
font-size: 0.9rem;
|
|
344
|
+
line-height: 1.6;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
.no-results {
|
|
348
|
+
text-align: center;
|
|
349
|
+
padding: 3rem;
|
|
350
|
+
color: white;
|
|
351
|
+
font-size: 1.1rem;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
@keyframes fadeIn {
|
|
355
|
+
from { opacity: 0; }
|
|
356
|
+
to { opacity: 1; }
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
@keyframes fadeInDown {
|
|
360
|
+
from { opacity: 0; transform: translateY(-20px); }
|
|
361
|
+
to { opacity: 1; transform: translateY(0); }
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
@keyframes fadeInUp {
|
|
365
|
+
from { opacity: 0; transform: translateY(20px); }
|
|
366
|
+
to { opacity: 1; transform: translateY(0); }
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
@keyframes slideUp {
|
|
370
|
+
from { opacity: 0; transform: translateY(40px); }
|
|
371
|
+
to { opacity: 1; transform: translateY(0); }
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
@media (max-width: 768px) {
|
|
375
|
+
.container { padding: 1rem; }
|
|
376
|
+
.header h1 { font-size: 2rem; }
|
|
377
|
+
.detail-content { margin: 1rem; }
|
|
378
|
+
}
|
|
379
|
+
</style>
|
|
380
|
+
</head>
|
|
381
|
+
<body>
|
|
382
|
+
<div class="container">
|
|
383
|
+
<div class="header">
|
|
384
|
+
<h1>${this.openApiSpec?.info?.title || 'API Documentation'}</h1>
|
|
385
|
+
<p>${this.openApiSpec?.info?.description || ''} ${this.openApiSpec?.info?.version ? `v${this.openApiSpec.info.version}` : ''}</p>
|
|
386
|
+
</div>
|
|
387
|
+
|
|
388
|
+
<div class="search-box">
|
|
389
|
+
<input type="text" class="search-input" id="searchInput" placeholder="🔍 Search endpoints...">
|
|
390
|
+
</div>
|
|
391
|
+
|
|
392
|
+
<div class="endpoints-grid" id="endpointsGrid">
|
|
393
|
+
${this.renderGroupedEndpoints(groupedEndpoints)}
|
|
394
|
+
</div>
|
|
395
|
+
|
|
396
|
+
<div class="no-results" id="noResults" style="display: none;">
|
|
397
|
+
No endpoints found matching your search.
|
|
398
|
+
</div>
|
|
399
|
+
</div>
|
|
400
|
+
|
|
401
|
+
${this.renderEndpointModals(endpoints)}
|
|
402
|
+
|
|
403
|
+
<script>
|
|
404
|
+
const searchInput = document.getElementById('searchInput');
|
|
405
|
+
const endpointsGrid = document.getElementById('endpointsGrid');
|
|
406
|
+
const noResults = document.getElementById('noResults');
|
|
407
|
+
|
|
408
|
+
searchInput.addEventListener('input', (e) => {
|
|
409
|
+
const query = e.target.value.toLowerCase();
|
|
410
|
+
const sections = endpointsGrid.querySelectorAll('.tag-section');
|
|
411
|
+
let hasResults = false;
|
|
412
|
+
|
|
413
|
+
sections.forEach(section => {
|
|
414
|
+
const cards = section.querySelectorAll('.endpoint-card');
|
|
415
|
+
let sectionHasResults = false;
|
|
416
|
+
|
|
417
|
+
cards.forEach(card => {
|
|
418
|
+
const text = card.textContent.toLowerCase();
|
|
419
|
+
if (text.includes(query)) {
|
|
420
|
+
card.style.display = 'block';
|
|
421
|
+
sectionHasResults = true;
|
|
422
|
+
hasResults = true;
|
|
423
|
+
} else {
|
|
424
|
+
card.style.display = 'none';
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
section.style.display = sectionHasResults ? 'block' : 'none';
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
noResults.style.display = hasResults ? 'none' : 'block';
|
|
432
|
+
endpointsGrid.style.display = hasResults ? 'grid' : 'none';
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
function openEndpoint(id) {
|
|
436
|
+
document.getElementById('detail-' + id).classList.add('active');
|
|
437
|
+
document.body.style.overflow = 'hidden';
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function closeEndpoint(id) {
|
|
441
|
+
document.getElementById('detail-' + id).classList.remove('active');
|
|
442
|
+
document.body.style.overflow = 'auto';
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
document.querySelectorAll('.endpoint-detail').forEach(modal => {
|
|
446
|
+
modal.addEventListener('click', (e) => {
|
|
447
|
+
if (e.target === modal) {
|
|
448
|
+
modal.classList.remove('active');
|
|
449
|
+
document.body.style.overflow = 'auto';
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
});
|
|
453
|
+
</script>
|
|
454
|
+
</body>
|
|
455
455
|
</html>`;
|
|
456
456
|
}
|
|
457
457
|
groupByTag(endpoints) {
|
|
@@ -466,90 +466,90 @@ let DocsController = class DocsController {
|
|
|
466
466
|
return grouped;
|
|
467
467
|
}
|
|
468
468
|
renderGroupedEndpoints(grouped) {
|
|
469
|
-
return Object.entries(grouped).map(([tag, endpoints]) => `
|
|
470
|
-
<div class="tag-section">
|
|
471
|
-
<div class="tag-title">
|
|
472
|
-
${tag}
|
|
473
|
-
<span class="tag-badge">${endpoints.length}</span>
|
|
474
|
-
</div>
|
|
475
|
-
<div class="endpoint-list">
|
|
476
|
-
${endpoints.map(ep => `
|
|
477
|
-
<div class="endpoint-card" onclick="openEndpoint('${ep.id}')">
|
|
478
|
-
<div class="endpoint-header">
|
|
479
|
-
<span class="method-badge method-${ep.method.toLowerCase()}">${ep.method}</span>
|
|
480
|
-
<span class="endpoint-path">${ep.path}</span>
|
|
481
|
-
</div>
|
|
482
|
-
${ep.summary ? `<div class="endpoint-summary">${ep.summary}</div>` : ''}
|
|
483
|
-
</div>
|
|
484
|
-
`).join('')}
|
|
485
|
-
</div>
|
|
486
|
-
</div>
|
|
469
|
+
return Object.entries(grouped).map(([tag, endpoints]) => `
|
|
470
|
+
<div class="tag-section">
|
|
471
|
+
<div class="tag-title">
|
|
472
|
+
${tag}
|
|
473
|
+
<span class="tag-badge">${endpoints.length}</span>
|
|
474
|
+
</div>
|
|
475
|
+
<div class="endpoint-list">
|
|
476
|
+
${endpoints.map(ep => `
|
|
477
|
+
<div class="endpoint-card" onclick="openEndpoint('${ep.id}')">
|
|
478
|
+
<div class="endpoint-header">
|
|
479
|
+
<span class="method-badge method-${ep.method.toLowerCase()}">${ep.method}</span>
|
|
480
|
+
<span class="endpoint-path">${ep.path}</span>
|
|
481
|
+
</div>
|
|
482
|
+
${ep.summary ? `<div class="endpoint-summary">${ep.summary}</div>` : ''}
|
|
483
|
+
</div>
|
|
484
|
+
`).join('')}
|
|
485
|
+
</div>
|
|
486
|
+
</div>
|
|
487
487
|
`).join('');
|
|
488
488
|
}
|
|
489
489
|
renderEndpointModals(endpoints) {
|
|
490
|
-
return endpoints.map(ep => `
|
|
491
|
-
<div class="endpoint-detail" id="detail-${ep.id}">
|
|
492
|
-
<div class="detail-content">
|
|
493
|
-
<div class="detail-header">
|
|
494
|
-
<button class="detail-close" onclick="closeEndpoint('${ep.id}')">×</button>
|
|
495
|
-
<div style="clear: both;">
|
|
496
|
-
<div style="display: flex; align-items: center; gap: 1rem; margin-bottom: 1rem;">
|
|
497
|
-
<span class="method-badge method-${ep.method.toLowerCase()}">${ep.method}</span>
|
|
498
|
-
<span style="font-family: 'Courier New', monospace; font-size: 1.25rem;">${ep.path}</span>
|
|
499
|
-
</div>
|
|
500
|
-
${ep.summary ? `<p style="opacity: 0.95; font-size: 1.1rem;">${ep.summary}</p>` : ''}
|
|
501
|
-
</div>
|
|
502
|
-
</div>
|
|
503
|
-
|
|
504
|
-
<div class="detail-body">
|
|
505
|
-
${ep.description ? `
|
|
506
|
-
<div class="section">
|
|
507
|
-
<div class="section-title">Description</div>
|
|
508
|
-
<p style="color: #4a5568; line-height: 1.6;">${ep.description}</p>
|
|
509
|
-
</div>
|
|
510
|
-
` : ''}
|
|
511
|
-
|
|
512
|
-
${ep.parameters && ep.parameters.length > 0 ? `
|
|
513
|
-
<div class="section">
|
|
514
|
-
<div class="section-title">Parameters</div>
|
|
515
|
-
${ep.parameters.map((param) => `
|
|
516
|
-
<div class="param-item">
|
|
517
|
-
<div>
|
|
518
|
-
<span class="param-name">${param.name}</span>
|
|
519
|
-
<span class="param-type">${param.schema?.type || param.type || 'string'}</span>
|
|
520
|
-
${param.required ? '<span style="color: #e53e3e; font-size: 0.875rem; margin-left: 0.5rem;">*required</span>' : ''}
|
|
521
|
-
</div>
|
|
522
|
-
<div style="color: #a0aec0; font-size: 0.875rem; margin-top: 0.25rem;">in: ${param.in}</div>
|
|
523
|
-
${param.description ? `<div class="param-desc">${param.description}</div>` : ''}
|
|
524
|
-
</div>
|
|
525
|
-
`).join('')}
|
|
526
|
-
</div>
|
|
527
|
-
` : ''}
|
|
528
|
-
|
|
529
|
-
${ep.requestBody ? `
|
|
530
|
-
<div class="section">
|
|
531
|
-
<div class="section-title">Request Body</div>
|
|
532
|
-
<div class="code-block">${this.formatJson(ep.requestBody.content?.['application/json']?.schema || ep.requestBody)}</div>
|
|
533
|
-
</div>
|
|
534
|
-
` : ''}
|
|
535
|
-
|
|
536
|
-
${ep.responses && Object.keys(ep.responses).length > 0 ? `
|
|
537
|
-
<div class="section">
|
|
538
|
-
<div class="section-title">Responses</div>
|
|
539
|
-
${Object.entries(ep.responses).map(([code, response]) => `
|
|
540
|
-
<div class="response-item">
|
|
541
|
-
<div class="response-code">Status ${code}</div>
|
|
542
|
-
${response.description ? `<div class="param-desc">${response.description}</div>` : ''}
|
|
543
|
-
${response.content?.['application/json']?.schema ? `
|
|
544
|
-
<div class="code-block" style="margin-top: 0.75rem;">${this.formatJson(response.content['application/json'].schema)}</div>
|
|
545
|
-
` : ''}
|
|
546
|
-
</div>
|
|
547
|
-
`).join('')}
|
|
548
|
-
</div>
|
|
549
|
-
` : ''}
|
|
550
|
-
</div>
|
|
551
|
-
</div>
|
|
552
|
-
</div>
|
|
490
|
+
return endpoints.map(ep => `
|
|
491
|
+
<div class="endpoint-detail" id="detail-${ep.id}">
|
|
492
|
+
<div class="detail-content">
|
|
493
|
+
<div class="detail-header">
|
|
494
|
+
<button class="detail-close" onclick="closeEndpoint('${ep.id}')">×</button>
|
|
495
|
+
<div style="clear: both;">
|
|
496
|
+
<div style="display: flex; align-items: center; gap: 1rem; margin-bottom: 1rem;">
|
|
497
|
+
<span class="method-badge method-${ep.method.toLowerCase()}">${ep.method}</span>
|
|
498
|
+
<span style="font-family: 'Courier New', monospace; font-size: 1.25rem;">${ep.path}</span>
|
|
499
|
+
</div>
|
|
500
|
+
${ep.summary ? `<p style="opacity: 0.95; font-size: 1.1rem;">${ep.summary}</p>` : ''}
|
|
501
|
+
</div>
|
|
502
|
+
</div>
|
|
503
|
+
|
|
504
|
+
<div class="detail-body">
|
|
505
|
+
${ep.description ? `
|
|
506
|
+
<div class="section">
|
|
507
|
+
<div class="section-title">Description</div>
|
|
508
|
+
<p style="color: #4a5568; line-height: 1.6;">${ep.description}</p>
|
|
509
|
+
</div>
|
|
510
|
+
` : ''}
|
|
511
|
+
|
|
512
|
+
${ep.parameters && ep.parameters.length > 0 ? `
|
|
513
|
+
<div class="section">
|
|
514
|
+
<div class="section-title">Parameters</div>
|
|
515
|
+
${ep.parameters.map((param) => `
|
|
516
|
+
<div class="param-item">
|
|
517
|
+
<div>
|
|
518
|
+
<span class="param-name">${param.name}</span>
|
|
519
|
+
<span class="param-type">${param.schema?.type || param.type || 'string'}</span>
|
|
520
|
+
${param.required ? '<span style="color: #e53e3e; font-size: 0.875rem; margin-left: 0.5rem;">*required</span>' : ''}
|
|
521
|
+
</div>
|
|
522
|
+
<div style="color: #a0aec0; font-size: 0.875rem; margin-top: 0.25rem;">in: ${param.in}</div>
|
|
523
|
+
${param.description ? `<div class="param-desc">${param.description}</div>` : ''}
|
|
524
|
+
</div>
|
|
525
|
+
`).join('')}
|
|
526
|
+
</div>
|
|
527
|
+
` : ''}
|
|
528
|
+
|
|
529
|
+
${ep.requestBody ? `
|
|
530
|
+
<div class="section">
|
|
531
|
+
<div class="section-title">Request Body</div>
|
|
532
|
+
<div class="code-block">${this.formatJson(ep.requestBody.content?.['application/json']?.schema || ep.requestBody)}</div>
|
|
533
|
+
</div>
|
|
534
|
+
` : ''}
|
|
535
|
+
|
|
536
|
+
${ep.responses && Object.keys(ep.responses).length > 0 ? `
|
|
537
|
+
<div class="section">
|
|
538
|
+
<div class="section-title">Responses</div>
|
|
539
|
+
${Object.entries(ep.responses).map(([code, response]) => `
|
|
540
|
+
<div class="response-item">
|
|
541
|
+
<div class="response-code">Status ${code}</div>
|
|
542
|
+
${response.description ? `<div class="param-desc">${response.description}</div>` : ''}
|
|
543
|
+
${response.content?.['application/json']?.schema ? `
|
|
544
|
+
<div class="code-block" style="margin-top: 0.75rem;">${this.formatJson(response.content['application/json'].schema)}</div>
|
|
545
|
+
` : ''}
|
|
546
|
+
</div>
|
|
547
|
+
`).join('')}
|
|
548
|
+
</div>
|
|
549
|
+
` : ''}
|
|
550
|
+
</div>
|
|
551
|
+
</div>
|
|
552
|
+
</div>
|
|
553
553
|
`).join('');
|
|
554
554
|
}
|
|
555
555
|
formatJson(obj) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nest-scramble",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "A next-generation, decorator-free API documentation engine and intelligent mock server for NestJS, engineered by Mohamed Mustafa",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|