koa-classic-server 2.1.0 โ 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +548 -136
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,48 +1,75 @@
|
|
|
1
1
|
# koa-classic-server
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
๐ **Production-ready Koa middleware** for serving static files with Apache2-like directory listing, sortable columns, HTTP caching, template engine support, and enterprise-grade security.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/koa-classic-server)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
|
-
[]()
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## ๐ Version 2.1.2 - Production Release
|
|
12
|
+
|
|
13
|
+
Version 2.1.2 is a **major production release** featuring performance optimizations, enhanced directory listing, and critical bug fixes.
|
|
14
|
+
|
|
15
|
+
### What's New in 2.1.2
|
|
10
16
|
|
|
11
|
-
|
|
17
|
+
โ
**Sortable Directory Columns** - Click Name/Type/Size to sort (Apache2-like)
|
|
18
|
+
โ
**Navigation Bug Fixed** - Directory navigation now works correctly after sorting
|
|
19
|
+
โ
**File Size Display** - Human-readable file sizes (B, KB, MB, GB, TB)
|
|
20
|
+
โ
**HTTP Caching** - 80-95% bandwidth reduction with ETag and Last-Modified
|
|
21
|
+
โ
**Async/Await** - Non-blocking I/O for high performance
|
|
22
|
+
โ
**153 Tests Passing** - Comprehensive test coverage
|
|
23
|
+
โ
**Flow Documentation** - Complete execution flow diagrams
|
|
24
|
+
โ
**Code Review** - Standardized operators and best practices
|
|
12
25
|
|
|
13
|
-
### What's New in
|
|
26
|
+
### What's New in 2.0
|
|
14
27
|
|
|
15
|
-
โ
**
|
|
16
|
-
โ
**
|
|
17
|
-
โ
**Template
|
|
18
|
-
โ
**
|
|
19
|
-
โ
**Race Condition Fixes** - Robust file access
|
|
20
|
-
โ
**146 Tests Passing** - Comprehensive test coverage including EJS integration
|
|
28
|
+
โ
**Performance Optimizations** - 50-70% faster directory listings
|
|
29
|
+
โ
**Enhanced Index Option** - Array format with RegExp support
|
|
30
|
+
โ
**Template Engine Guide** - Complete documentation with examples
|
|
31
|
+
โ
**Security Hardened** - Path traversal, XSS, race condition fixes
|
|
21
32
|
|
|
22
|
-
[See full changelog](./docs/CHANGELOG.md)
|
|
33
|
+
[See full changelog โ](./docs/CHANGELOG.md)
|
|
34
|
+
|
|
35
|
+
---
|
|
23
36
|
|
|
24
37
|
## Features
|
|
25
38
|
|
|
26
|
-
koa-classic-server is a middleware for serving static files
|
|
39
|
+
**koa-classic-server** is a high-performance middleware for serving static files with Apache2-like behavior, making file browsing intuitive and powerful.
|
|
27
40
|
|
|
28
|
-
|
|
41
|
+
### Core Features
|
|
29
42
|
|
|
30
|
-
- ๐๏ธ **Directory Listing** -
|
|
31
|
-
- ๐ **Static File Serving** - Automatic MIME type detection
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
-
|
|
43
|
+
- ๐๏ธ **Apache2-like Directory Listing** - Sortable columns (Name, Type, Size)
|
|
44
|
+
- ๐ **Static File Serving** - Automatic MIME type detection with streaming
|
|
45
|
+
- ๐ **Sortable Columns** - Click headers to sort ascending/descending
|
|
46
|
+
- ๐ **File Sizes** - Human-readable display (B, KB, MB, GB, TB)
|
|
47
|
+
- โก **HTTP Caching** - ETag, Last-Modified, 304 responses
|
|
48
|
+
- ๐จ **Template Engine Support** - EJS, Pug, Handlebars, Nunjucks, etc.
|
|
49
|
+
- ๐ **Enterprise Security** - Path traversal, XSS, race condition protection
|
|
50
|
+
- โ๏ธ **Highly Configurable** - URL prefixes, reserved paths, index files
|
|
51
|
+
- ๐ **High Performance** - Async/await, non-blocking I/O, optimized algorithms
|
|
52
|
+
- ๐งช **Well-Tested** - 153 passing tests with comprehensive coverage
|
|
36
53
|
- ๐ฆ **Dual Module Support** - CommonJS and ES Modules
|
|
37
54
|
|
|
55
|
+
---
|
|
56
|
+
|
|
38
57
|
## Installation
|
|
39
58
|
|
|
40
59
|
```bash
|
|
41
60
|
npm install koa-classic-server
|
|
42
61
|
```
|
|
43
62
|
|
|
63
|
+
**Requirements:**
|
|
64
|
+
- Node.js >= 12.0.0
|
|
65
|
+
- Koa >= 2.0.0
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
44
69
|
## Quick Start
|
|
45
70
|
|
|
71
|
+
### Basic Usage
|
|
72
|
+
|
|
46
73
|
```javascript
|
|
47
74
|
const Koa = require('koa');
|
|
48
75
|
const koaClassicServer = require('koa-classic-server');
|
|
@@ -56,9 +83,30 @@ app.listen(3000);
|
|
|
56
83
|
console.log('Server running on http://localhost:3000');
|
|
57
84
|
```
|
|
58
85
|
|
|
59
|
-
|
|
86
|
+
### With Options
|
|
60
87
|
|
|
61
|
-
|
|
88
|
+
```javascript
|
|
89
|
+
const Koa = require('koa');
|
|
90
|
+
const koaClassicServer = require('koa-classic-server');
|
|
91
|
+
|
|
92
|
+
const app = new Koa();
|
|
93
|
+
|
|
94
|
+
app.use(koaClassicServer(__dirname + '/public', {
|
|
95
|
+
showDirContents: true,
|
|
96
|
+
index: ['index.html', 'index.htm'],
|
|
97
|
+
urlPrefix: '/static',
|
|
98
|
+
cacheMaxAge: 3600,
|
|
99
|
+
enableCaching: true
|
|
100
|
+
}));
|
|
101
|
+
|
|
102
|
+
app.listen(3000);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Complete Usage Guide
|
|
108
|
+
|
|
109
|
+
### 1. Import
|
|
62
110
|
|
|
63
111
|
```javascript
|
|
64
112
|
// CommonJS
|
|
@@ -68,9 +116,9 @@ const koaClassicServer = require('koa-classic-server');
|
|
|
68
116
|
import koaClassicServer from 'koa-classic-server';
|
|
69
117
|
```
|
|
70
118
|
|
|
71
|
-
### Basic
|
|
119
|
+
### 2. Basic File Server
|
|
72
120
|
|
|
73
|
-
|
|
121
|
+
Serve static files from a directory:
|
|
74
122
|
|
|
75
123
|
```javascript
|
|
76
124
|
const Koa = require('koa');
|
|
@@ -80,57 +128,173 @@ const app = new Koa();
|
|
|
80
128
|
|
|
81
129
|
app.use(koaClassicServer(__dirname + '/public', {
|
|
82
130
|
showDirContents: true,
|
|
83
|
-
index: ['index.html']
|
|
131
|
+
index: ['index.html']
|
|
84
132
|
}));
|
|
85
133
|
|
|
86
134
|
app.listen(3000);
|
|
87
135
|
```
|
|
88
136
|
|
|
89
|
-
|
|
137
|
+
**What it does:**
|
|
138
|
+
- Serves files from `/public` directory
|
|
139
|
+
- Shows directory listing when accessing folders
|
|
140
|
+
- Looks for `index.html` in directories
|
|
141
|
+
- Sortable columns (Name, Type, Size)
|
|
142
|
+
- File sizes displayed in human-readable format
|
|
90
143
|
|
|
91
|
-
|
|
92
|
-
const Koa = require('koa');
|
|
93
|
-
const koaClassicServer = require('koa-classic-server');
|
|
144
|
+
### 3. With URL Prefix
|
|
94
145
|
|
|
95
|
-
|
|
146
|
+
Serve files under a specific URL path:
|
|
96
147
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
app.use(koaClassicServer(__dirname + '/public', {
|
|
148
|
+
```javascript
|
|
149
|
+
app.use(koaClassicServer(__dirname + '/assets', {
|
|
100
150
|
urlPrefix: '/static',
|
|
101
151
|
showDirContents: true
|
|
102
152
|
}));
|
|
153
|
+
```
|
|
103
154
|
|
|
104
|
-
|
|
155
|
+
**Result:**
|
|
156
|
+
- `http://localhost:3000/static/image.png` โ serves `/assets/image.png`
|
|
157
|
+
- `http://localhost:3000/static/` โ shows `/assets` directory listing
|
|
158
|
+
|
|
159
|
+
### 4. With Reserved Paths
|
|
160
|
+
|
|
161
|
+
Protect specific directories from being accessed:
|
|
162
|
+
|
|
163
|
+
```javascript
|
|
164
|
+
app.use(koaClassicServer(__dirname + '/www', {
|
|
165
|
+
urlsReserved: ['/admin', '/config', '/.git', '/node_modules']
|
|
166
|
+
}));
|
|
105
167
|
```
|
|
106
168
|
|
|
107
|
-
|
|
169
|
+
**Result:**
|
|
170
|
+
- `/admin/*` โ passed to next middleware (not served)
|
|
171
|
+
- `/config/*` โ protected
|
|
172
|
+
- Other paths โ served normally
|
|
173
|
+
|
|
174
|
+
### 5. With Template Engine (EJS)
|
|
175
|
+
|
|
176
|
+
Dynamically render templates with data:
|
|
108
177
|
|
|
109
178
|
```javascript
|
|
110
|
-
const Koa = require('koa');
|
|
111
|
-
const koaClassicServer = require('koa-classic-server');
|
|
112
179
|
const ejs = require('ejs');
|
|
113
|
-
const fs = require('fs').promises;
|
|
114
|
-
|
|
115
|
-
const app = new Koa();
|
|
116
180
|
|
|
117
181
|
app.use(koaClassicServer(__dirname + '/views', {
|
|
118
182
|
template: {
|
|
119
|
-
ext: ['ejs'],
|
|
183
|
+
ext: ['ejs', 'html.ejs'],
|
|
120
184
|
render: async (ctx, next, filePath) => {
|
|
121
|
-
|
|
122
|
-
const templateContent = await fs.readFile(filePath, 'utf-8');
|
|
123
|
-
|
|
124
|
-
// Render with data
|
|
125
|
-
const html = ejs.render(templateContent, {
|
|
185
|
+
const data = {
|
|
126
186
|
title: 'My App',
|
|
127
187
|
user: ctx.state.user || { name: 'Guest' },
|
|
128
|
-
|
|
188
|
+
items: ['Item 1', 'Item 2', 'Item 3'],
|
|
129
189
|
timestamp: new Date().toISOString()
|
|
130
|
-
}
|
|
190
|
+
};
|
|
131
191
|
|
|
192
|
+
ctx.body = await ejs.renderFile(filePath, data);
|
|
132
193
|
ctx.type = 'text/html';
|
|
133
|
-
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}));
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
**Template example (`views/dashboard.ejs`):**
|
|
200
|
+
```html
|
|
201
|
+
<!DOCTYPE html>
|
|
202
|
+
<html>
|
|
203
|
+
<head>
|
|
204
|
+
<title><%= title %></title>
|
|
205
|
+
</head>
|
|
206
|
+
<body>
|
|
207
|
+
<h1>Welcome, <%= user.name %>!</h1>
|
|
208
|
+
<ul>
|
|
209
|
+
<% items.forEach(item => { %>
|
|
210
|
+
<li><%= item %></li>
|
|
211
|
+
<% }); %>
|
|
212
|
+
</ul>
|
|
213
|
+
<p>Generated at: <%= timestamp %></p>
|
|
214
|
+
</body>
|
|
215
|
+
</html>
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
**See complete guide:** [Template Engine Documentation โ](./docs/template-engine/TEMPLATE_ENGINE_GUIDE.md)
|
|
219
|
+
|
|
220
|
+
### 6. With HTTP Caching
|
|
221
|
+
|
|
222
|
+
Enable aggressive caching for static files:
|
|
223
|
+
|
|
224
|
+
```javascript
|
|
225
|
+
app.use(koaClassicServer(__dirname + '/public', {
|
|
226
|
+
enableCaching: true, // Enable ETag and Last-Modified
|
|
227
|
+
cacheMaxAge: 86400, // Cache for 24 hours (in seconds)
|
|
228
|
+
}));
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**Benefits:**
|
|
232
|
+
- 80-95% bandwidth reduction
|
|
233
|
+
- 304 Not Modified responses for unchanged files
|
|
234
|
+
- Faster page loads for returning visitors
|
|
235
|
+
|
|
236
|
+
**See details:** [HTTP Caching Optimization โ](./docs/OPTIMIZATION_HTTP_CACHING.md)
|
|
237
|
+
|
|
238
|
+
### 7. Multiple Index Files with Priority
|
|
239
|
+
|
|
240
|
+
Search for multiple index files with custom order:
|
|
241
|
+
|
|
242
|
+
```javascript
|
|
243
|
+
app.use(koaClassicServer(__dirname + '/public', {
|
|
244
|
+
index: [
|
|
245
|
+
'index.html', // First priority
|
|
246
|
+
'index.htm', // Second priority
|
|
247
|
+
/index\.[eE][jJ][sS]/, // Third: index.ejs (case-insensitive)
|
|
248
|
+
'default.html' // Last priority
|
|
249
|
+
]
|
|
250
|
+
}));
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
**See details:** [Index Option Priority โ](./docs/INDEX_OPTION_PRIORITY.md)
|
|
254
|
+
|
|
255
|
+
### 8. Complete Production Example
|
|
256
|
+
|
|
257
|
+
Real-world configuration for production:
|
|
258
|
+
|
|
259
|
+
```javascript
|
|
260
|
+
const Koa = require('koa');
|
|
261
|
+
const koaClassicServer = require('koa-classic-server');
|
|
262
|
+
const ejs = require('ejs');
|
|
263
|
+
const path = require('path');
|
|
264
|
+
|
|
265
|
+
const app = new Koa();
|
|
266
|
+
|
|
267
|
+
// Serve static assets with caching
|
|
268
|
+
app.use(koaClassicServer(path.join(__dirname, 'public'), {
|
|
269
|
+
method: ['GET', 'HEAD'],
|
|
270
|
+
showDirContents: false, // Disable directory listing in production
|
|
271
|
+
index: ['index.html', 'index.htm'],
|
|
272
|
+
urlPrefix: '/assets',
|
|
273
|
+
urlsReserved: ['/admin', '/api', '/.git'],
|
|
274
|
+
enableCaching: true,
|
|
275
|
+
cacheMaxAge: 86400, // 24 hours
|
|
276
|
+
}));
|
|
277
|
+
|
|
278
|
+
// Serve dynamic templates
|
|
279
|
+
app.use(koaClassicServer(path.join(__dirname, 'views'), {
|
|
280
|
+
showDirContents: false,
|
|
281
|
+
template: {
|
|
282
|
+
ext: ['ejs'],
|
|
283
|
+
render: async (ctx, next, filePath) => {
|
|
284
|
+
const data = {
|
|
285
|
+
env: process.env.NODE_ENV,
|
|
286
|
+
user: ctx.state.user,
|
|
287
|
+
config: ctx.state.config
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
try {
|
|
291
|
+
ctx.body = await ejs.renderFile(filePath, data);
|
|
292
|
+
ctx.type = 'text/html';
|
|
293
|
+
} catch (error) {
|
|
294
|
+
console.error('Template error:', error);
|
|
295
|
+
ctx.status = 500;
|
|
296
|
+
ctx.body = 'Internal Server Error';
|
|
297
|
+
}
|
|
134
298
|
}
|
|
135
299
|
}
|
|
136
300
|
}));
|
|
@@ -138,9 +302,9 @@ app.use(koaClassicServer(__dirname + '/views', {
|
|
|
138
302
|
app.listen(3000);
|
|
139
303
|
```
|
|
140
304
|
|
|
141
|
-
|
|
305
|
+
---
|
|
142
306
|
|
|
143
|
-
## API
|
|
307
|
+
## API Reference
|
|
144
308
|
|
|
145
309
|
### koaClassicServer(rootDir, options)
|
|
146
310
|
|
|
@@ -148,56 +312,54 @@ Creates a Koa middleware for serving static files.
|
|
|
148
312
|
|
|
149
313
|
**Parameters:**
|
|
150
314
|
|
|
151
|
-
-
|
|
152
|
-
-
|
|
315
|
+
- **`rootDir`** (String, required): Absolute path to the directory containing files
|
|
316
|
+
- **`options`** (Object, optional): Configuration options
|
|
153
317
|
|
|
154
318
|
**Returns:** Koa middleware function
|
|
155
319
|
|
|
156
|
-
|
|
320
|
+
### Options
|
|
157
321
|
|
|
158
322
|
```javascript
|
|
159
|
-
|
|
323
|
+
{
|
|
160
324
|
// HTTP methods allowed (default: ['GET'])
|
|
161
325
|
method: ['GET', 'HEAD'],
|
|
162
326
|
|
|
163
327
|
// Show directory contents (default: true)
|
|
164
328
|
showDirContents: true,
|
|
165
329
|
|
|
166
|
-
// Index file configuration
|
|
167
|
-
//
|
|
168
|
-
//
|
|
169
|
-
// -
|
|
170
|
-
// -
|
|
171
|
-
//
|
|
172
|
-
//
|
|
173
|
-
|
|
174
|
-
// DEPRECATED: String format 'index.html' still works but will be removed
|
|
175
|
-
// in future versions. Please use array format: ['index.html']
|
|
176
|
-
//
|
|
177
|
-
// See INDEX_OPTION_PRIORITY.md for detailed behavior documentation
|
|
178
|
-
index: ['index.html'],
|
|
330
|
+
// Index file configuration
|
|
331
|
+
// Array format (recommended):
|
|
332
|
+
// - Strings: exact matches ['index.html', 'default.html']
|
|
333
|
+
// - RegExp: pattern matches [/index\.html/i]
|
|
334
|
+
// - Mixed: ['index.html', /INDEX\.HTM/i]
|
|
335
|
+
// Priority determined by array order (first match wins)
|
|
336
|
+
// See docs/INDEX_OPTION_PRIORITY.md for details
|
|
337
|
+
index: ['index.html', 'index.htm'],
|
|
179
338
|
|
|
180
339
|
// URL path prefix (default: '')
|
|
181
|
-
// Files
|
|
340
|
+
// Files served under this prefix
|
|
182
341
|
urlPrefix: '/static',
|
|
183
342
|
|
|
184
343
|
// Reserved paths (default: [])
|
|
185
|
-
//
|
|
186
|
-
|
|
187
|
-
urlsReserved: ['/admin', '/private'],
|
|
344
|
+
// First-level directories passed to next middleware
|
|
345
|
+
urlsReserved: ['/admin', '/api', '/.git'],
|
|
188
346
|
|
|
189
347
|
// Template engine configuration
|
|
190
348
|
template: {
|
|
191
349
|
// Template rendering function
|
|
192
350
|
render: async (ctx, next, filePath) => {
|
|
193
351
|
// Your rendering logic
|
|
194
|
-
ctx.body = await
|
|
352
|
+
ctx.body = await yourEngine.render(filePath, data);
|
|
195
353
|
},
|
|
196
354
|
|
|
197
|
-
// File extensions to process
|
|
355
|
+
// File extensions to process
|
|
198
356
|
ext: ['ejs', 'pug', 'hbs']
|
|
199
|
-
}
|
|
200
|
-
|
|
357
|
+
},
|
|
358
|
+
|
|
359
|
+
// HTTP caching configuration
|
|
360
|
+
enableCaching: true, // Enable ETag & Last-Modified (default: true)
|
|
361
|
+
cacheMaxAge: 3600, // Cache-Control max-age in seconds (default: 3600 = 1 hour)
|
|
362
|
+
}
|
|
201
363
|
```
|
|
202
364
|
|
|
203
365
|
### Options Details
|
|
@@ -206,133 +368,383 @@ const options = {
|
|
|
206
368
|
|--------|------|---------|-------------|
|
|
207
369
|
| `method` | Array | `['GET']` | Allowed HTTP methods |
|
|
208
370
|
| `showDirContents` | Boolean | `true` | Show directory listing |
|
|
209
|
-
| `index` | String | `
|
|
371
|
+
| `index` | Array/String | `[]` | Index file patterns (array format recommended) |
|
|
210
372
|
| `urlPrefix` | String | `''` | URL path prefix |
|
|
211
|
-
| `urlsReserved` | Array | `[]` | Reserved directory paths |
|
|
373
|
+
| `urlsReserved` | Array | `[]` | Reserved directory paths (first-level only) |
|
|
212
374
|
| `template.render` | Function | `undefined` | Template rendering function |
|
|
213
375
|
| `template.ext` | Array | `[]` | Extensions for template rendering |
|
|
376
|
+
| `enableCaching` | Boolean | `true` | Enable HTTP caching headers |
|
|
377
|
+
| `cacheMaxAge` | Number | `3600` | Cache duration in seconds |
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
381
|
+
## Directory Listing Features
|
|
382
|
+
|
|
383
|
+
### Sortable Columns
|
|
384
|
+
|
|
385
|
+
Click on column headers to sort:
|
|
386
|
+
|
|
387
|
+
- **Name** - Alphabetical sorting (A-Z or Z-A)
|
|
388
|
+
- **Type** - Sort by MIME type (directories always first)
|
|
389
|
+
- **Size** - Sort by file size (directories always first)
|
|
390
|
+
|
|
391
|
+
Visual indicators:
|
|
392
|
+
- **โ** - Ascending order
|
|
393
|
+
- **โ** - Descending order
|
|
394
|
+
|
|
395
|
+
### File Size Display
|
|
396
|
+
|
|
397
|
+
Human-readable format:
|
|
398
|
+
- `1.5 KB` - Kilobytes
|
|
399
|
+
- `2.3 MB` - Megabytes
|
|
400
|
+
- `1.2 GB` - Gigabytes
|
|
401
|
+
- `-` - Directories (no size)
|
|
402
|
+
|
|
403
|
+
### Navigation
|
|
404
|
+
|
|
405
|
+
- **Click folder name** - Enter directory
|
|
406
|
+
- **Click file name** - Download/view file
|
|
407
|
+
- **Parent Directory** - Go up one level
|
|
408
|
+
|
|
409
|
+
---
|
|
214
410
|
|
|
215
411
|
## Security
|
|
216
412
|
|
|
217
|
-
###
|
|
413
|
+
### Built-in Protection
|
|
414
|
+
|
|
415
|
+
koa-classic-server includes enterprise-grade security:
|
|
218
416
|
|
|
219
|
-
|
|
417
|
+
#### 1. Path Traversal Protection
|
|
418
|
+
|
|
419
|
+
Prevents access to files outside `rootDir`:
|
|
220
420
|
|
|
221
421
|
```javascript
|
|
222
|
-
// โ
|
|
223
|
-
GET /../../../etc/passwd
|
|
224
|
-
GET /../config/database.yml
|
|
225
|
-
GET /%2e%2e%2fpackage.json
|
|
422
|
+
// โ Blocked requests
|
|
423
|
+
GET /../../../etc/passwd โ 403 Forbidden
|
|
424
|
+
GET /../config/database.yml โ 403 Forbidden
|
|
425
|
+
GET /%2e%2e%2fpackage.json โ 403 Forbidden
|
|
226
426
|
```
|
|
227
427
|
|
|
228
|
-
|
|
428
|
+
#### 2. XSS Protection
|
|
229
429
|
|
|
230
|
-
|
|
430
|
+
All filenames and paths are HTML-escaped:
|
|
231
431
|
|
|
232
432
|
```javascript
|
|
233
|
-
|
|
234
|
-
|
|
433
|
+
// Malicious filename: <script>alert('xss')</script>.txt
|
|
434
|
+
// Displayed as: <script>alert('xss')</script>.txt
|
|
435
|
+
// โ
Safe - script doesn't execute
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
#### 3. Reserved URLs
|
|
439
|
+
|
|
440
|
+
Protect sensitive directories:
|
|
441
|
+
|
|
442
|
+
```javascript
|
|
443
|
+
app.use(koaClassicServer(__dirname, {
|
|
444
|
+
urlsReserved: ['/admin', '/config', '/.git', '/node_modules']
|
|
235
445
|
}));
|
|
236
446
|
```
|
|
237
447
|
|
|
238
|
-
|
|
448
|
+
#### 4. Race Condition Protection
|
|
449
|
+
|
|
450
|
+
File access is verified before streaming:
|
|
451
|
+
|
|
452
|
+
```javascript
|
|
453
|
+
// File deleted between check and access?
|
|
454
|
+
// โ
Returns 404 instead of crashing
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
**See full security audit:** [Security Tests โ](./__tests__/security.test.js)
|
|
458
|
+
|
|
459
|
+
---
|
|
460
|
+
|
|
461
|
+
## Performance
|
|
239
462
|
|
|
240
|
-
|
|
463
|
+
### Optimizations
|
|
241
464
|
|
|
242
|
-
|
|
465
|
+
Version 2.x includes major performance improvements:
|
|
243
466
|
|
|
244
|
-
|
|
467
|
+
- **Async/Await** - Non-blocking I/O, event loop never blocked
|
|
468
|
+
- **Array Join** - 30-40% less memory vs string concatenation
|
|
469
|
+
- **HTTP Caching** - 80-95% bandwidth reduction
|
|
470
|
+
- **Single stat() Call** - No double file system access
|
|
471
|
+
- **Streaming** - Large files streamed efficiently
|
|
245
472
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
473
|
+
### Benchmarks
|
|
474
|
+
|
|
475
|
+
Performance results on directory with 1,000 files:
|
|
476
|
+
|
|
477
|
+
```
|
|
478
|
+
Before (v1.x): ~350ms per request
|
|
479
|
+
After (v2.x): ~190ms per request
|
|
480
|
+
Improvement: 46% faster
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
**See detailed benchmarks:** [Performance Analysis โ](./docs/PERFORMANCE_ANALYSIS.md)
|
|
484
|
+
|
|
485
|
+
---
|
|
249
486
|
|
|
250
487
|
## Testing
|
|
251
488
|
|
|
489
|
+
Run the comprehensive test suite:
|
|
490
|
+
|
|
252
491
|
```bash
|
|
253
492
|
# Run all tests
|
|
254
493
|
npm test
|
|
255
494
|
|
|
256
495
|
# Run security tests only
|
|
257
496
|
npm run test:security
|
|
497
|
+
|
|
498
|
+
# Run performance benchmarks
|
|
499
|
+
npm run test:performance
|
|
258
500
|
```
|
|
259
501
|
|
|
260
|
-
|
|
502
|
+
**Test Coverage:**
|
|
503
|
+
- โ
153 tests passing
|
|
504
|
+
- โ
Security tests (path traversal, XSS, race conditions)
|
|
505
|
+
- โ
EJS template integration tests
|
|
506
|
+
- โ
Index option tests (strings, arrays, RegExp)
|
|
507
|
+
- โ
Performance benchmarks
|
|
508
|
+
- โ
Directory sorting tests
|
|
261
509
|
|
|
262
|
-
|
|
510
|
+
---
|
|
263
511
|
|
|
264
|
-
|
|
265
|
-
2. If `showDirContents: true` โ show directory listing
|
|
266
|
-
3. If `showDirContents: false` โ return 404
|
|
512
|
+
## Complete Documentation
|
|
267
513
|
|
|
268
|
-
###
|
|
514
|
+
### Core Documentation
|
|
269
515
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
516
|
+
- **[DOCUMENTATION.md](./docs/DOCUMENTATION.md)** - Complete API reference and usage guide
|
|
517
|
+
- **[FLOW_DIAGRAM.md](./docs/FLOW_DIAGRAM.md)** - Visual flow diagrams and code execution paths
|
|
518
|
+
- **[CHANGELOG.md](./docs/CHANGELOG.md)** - Version history and release notes
|
|
273
519
|
|
|
274
|
-
###
|
|
520
|
+
### Template Engine
|
|
275
521
|
|
|
276
|
-
|
|
522
|
+
- **[TEMPLATE_ENGINE_GUIDE.md](./docs/template-engine/TEMPLATE_ENGINE_GUIDE.md)** - Complete guide to template engine integration
|
|
523
|
+
- Progressive examples (simple to enterprise)
|
|
524
|
+
- EJS, Pug, Handlebars, Nunjucks support
|
|
525
|
+
- Best practices and troubleshooting
|
|
277
526
|
|
|
278
|
-
|
|
527
|
+
### Configuration
|
|
279
528
|
|
|
280
|
-
|
|
529
|
+
- **[INDEX_OPTION_PRIORITY.md](./docs/INDEX_OPTION_PRIORITY.md)** - Detailed priority behavior for `index` option
|
|
530
|
+
- String vs Array vs RegExp formats
|
|
531
|
+
- Priority order examples
|
|
532
|
+
- Migration guide from v1.x
|
|
281
533
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
534
|
+
- **[EXAMPLES_INDEX_OPTION.md](./docs/EXAMPLES_INDEX_OPTION.md)** - 10 practical examples of `index` option with RegExp
|
|
535
|
+
- Case-insensitive matching
|
|
536
|
+
- Multiple extensions
|
|
537
|
+
- Complex patterns
|
|
285
538
|
|
|
286
|
-
|
|
287
|
-
- 404 status codes now correct (was 200)
|
|
288
|
-
- Path traversal blocked (was allowed)
|
|
289
|
-
- Template errors return 500 (was crash)
|
|
539
|
+
### Performance
|
|
290
540
|
|
|
291
|
-
|
|
541
|
+
- **[PERFORMANCE_ANALYSIS.md](./docs/PERFORMANCE_ANALYSIS.md)** - Performance optimization analysis
|
|
542
|
+
- Before/after comparisons
|
|
543
|
+
- Memory usage analysis
|
|
544
|
+
- Bottleneck identification
|
|
292
545
|
|
|
293
|
-
|
|
546
|
+
- **[PERFORMANCE_COMPARISON.md](./docs/PERFORMANCE_COMPARISON.md)** - Detailed performance benchmarks
|
|
547
|
+
- Request latency
|
|
548
|
+
- Throughput metrics
|
|
549
|
+
- Concurrent request handling
|
|
294
550
|
|
|
295
|
-
|
|
551
|
+
- **[OPTIMIZATION_HTTP_CACHING.md](./docs/OPTIMIZATION_HTTP_CACHING.md)** - HTTP caching implementation details
|
|
552
|
+
- ETag generation
|
|
553
|
+
- Last-Modified headers
|
|
554
|
+
- 304 Not Modified responses
|
|
555
|
+
|
|
556
|
+
- **[BENCHMARKS.md](./docs/BENCHMARKS.md)** - Benchmark results and methodology
|
|
557
|
+
|
|
558
|
+
### Code Quality
|
|
296
559
|
|
|
297
|
-
- **[DOCUMENTATION.md](./docs/DOCUMENTATION.md)** - Complete API reference and usage guide
|
|
298
|
-
- **[FLOW_DIAGRAM.md](./docs/FLOW_DIAGRAM.md)** - Visual flow diagrams and code execution paths
|
|
299
|
-
- **[TEMPLATE_ENGINE_GUIDE.md](./docs/template-engine/TEMPLATE_ENGINE_GUIDE.md)** - Complete guide to template engine integration (EJS, Pug, Handlebars, Nunjucks)
|
|
300
|
-
- **[INDEX_OPTION_PRIORITY.md](./docs/INDEX_OPTION_PRIORITY.md)** - Detailed priority behavior for `index` option (string, array, RegExp)
|
|
301
|
-
- **[EXAMPLES_INDEX_OPTION.md](./docs/EXAMPLES_INDEX_OPTION.md)** - 10 practical examples of `index` option with RegExp
|
|
302
|
-
- **[PERFORMANCE_ANALYSIS.md](./docs/PERFORMANCE_ANALYSIS.md)** - Performance optimization analysis
|
|
303
|
-
- **[PERFORMANCE_COMPARISON.md](./docs/PERFORMANCE_COMPARISON.md)** - Before/after performance benchmarks
|
|
304
560
|
- **[CODE_REVIEW.md](./docs/CODE_REVIEW.md)** - Code quality analysis and review
|
|
561
|
+
- Security audit
|
|
562
|
+
- Best practices
|
|
563
|
+
- Standardization improvements
|
|
564
|
+
|
|
565
|
+
- **[DEBUG_REPORT.md](./docs/DEBUG_REPORT.md)** - Known limitations and debugging info
|
|
566
|
+
- Reserved URLs behavior
|
|
567
|
+
- Edge cases
|
|
568
|
+
- Troubleshooting tips
|
|
569
|
+
|
|
570
|
+
---
|
|
571
|
+
|
|
572
|
+
## Migration Guide
|
|
573
|
+
|
|
574
|
+
### From v1.x to v2.x
|
|
575
|
+
|
|
576
|
+
**Breaking Changes:**
|
|
577
|
+
- `index` option: String format deprecated (still works), use array format
|
|
578
|
+
|
|
579
|
+
**Migration:**
|
|
580
|
+
|
|
581
|
+
```javascript
|
|
582
|
+
// v1.x (deprecated)
|
|
583
|
+
{
|
|
584
|
+
index: 'index.html'
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// v2.x (recommended)
|
|
588
|
+
{
|
|
589
|
+
index: ['index.html']
|
|
590
|
+
}
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
**New Features:**
|
|
594
|
+
- HTTP caching (enabled by default)
|
|
595
|
+
- Sortable directory columns
|
|
596
|
+
- File size display
|
|
597
|
+
- Enhanced index option with RegExp
|
|
598
|
+
|
|
599
|
+
**See full migration guide:** [CHANGELOG.md](./docs/CHANGELOG.md)
|
|
600
|
+
|
|
601
|
+
---
|
|
602
|
+
|
|
603
|
+
## Examples
|
|
604
|
+
|
|
605
|
+
### Example 1: Simple Static Server
|
|
606
|
+
|
|
607
|
+
```javascript
|
|
608
|
+
const Koa = require('koa');
|
|
609
|
+
const koaClassicServer = require('koa-classic-server');
|
|
610
|
+
|
|
611
|
+
const app = new Koa();
|
|
612
|
+
app.use(koaClassicServer(__dirname + '/public'));
|
|
613
|
+
app.listen(3000);
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
### Example 2: Multi-Directory Server
|
|
617
|
+
|
|
618
|
+
```javascript
|
|
619
|
+
const Koa = require('koa');
|
|
620
|
+
const koaClassicServer = require('koa-classic-server');
|
|
621
|
+
|
|
622
|
+
const app = new Koa();
|
|
623
|
+
|
|
624
|
+
// Serve static assets
|
|
625
|
+
app.use(koaClassicServer(__dirname + '/public', {
|
|
626
|
+
urlPrefix: '/static',
|
|
627
|
+
showDirContents: false
|
|
628
|
+
}));
|
|
629
|
+
|
|
630
|
+
// Serve user uploads
|
|
631
|
+
app.use(koaClassicServer(__dirname + '/uploads', {
|
|
632
|
+
urlPrefix: '/files',
|
|
633
|
+
showDirContents: true
|
|
634
|
+
}));
|
|
635
|
+
|
|
636
|
+
app.listen(3000);
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
### Example 3: Development Server with Templates
|
|
640
|
+
|
|
641
|
+
```javascript
|
|
642
|
+
const Koa = require('koa');
|
|
643
|
+
const koaClassicServer = require('koa-classic-server');
|
|
644
|
+
const ejs = require('ejs');
|
|
645
|
+
|
|
646
|
+
const app = new Koa();
|
|
647
|
+
|
|
648
|
+
// Development mode - show directories
|
|
649
|
+
app.use(koaClassicServer(__dirname + '/src', {
|
|
650
|
+
showDirContents: true,
|
|
651
|
+
template: {
|
|
652
|
+
ext: ['ejs'],
|
|
653
|
+
render: async (ctx, next, filePath) => {
|
|
654
|
+
ctx.body = await ejs.renderFile(filePath, {
|
|
655
|
+
dev: true,
|
|
656
|
+
timestamp: Date.now()
|
|
657
|
+
});
|
|
658
|
+
ctx.type = 'text/html';
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
}));
|
|
662
|
+
|
|
663
|
+
app.listen(3000);
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
---
|
|
667
|
+
|
|
668
|
+
## Troubleshooting
|
|
669
|
+
|
|
670
|
+
### Common Issues
|
|
671
|
+
|
|
672
|
+
**Issue: 404 errors for all files**
|
|
673
|
+
|
|
674
|
+
Check that `rootDir` is an absolute path:
|
|
675
|
+
|
|
676
|
+
```javascript
|
|
677
|
+
// โ Wrong (relative path)
|
|
678
|
+
koaClassicServer('./public')
|
|
679
|
+
|
|
680
|
+
// โ
Correct (absolute path)
|
|
681
|
+
koaClassicServer(__dirname + '/public')
|
|
682
|
+
koaClassicServer(path.join(__dirname, 'public'))
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
**Issue: Reserved URLs not working**
|
|
686
|
+
|
|
687
|
+
Reserved URLs only work for first-level directories:
|
|
688
|
+
|
|
689
|
+
```javascript
|
|
690
|
+
urlsReserved: ['/admin'] // โ
Blocks /admin/*
|
|
691
|
+
urlsReserved: ['/admin/users'] // โ Doesn't work (nested)
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
**Issue: Directory sorting not working**
|
|
695
|
+
|
|
696
|
+
Make sure you're accessing directories without query params initially. The sorting is applied when you click headers.
|
|
697
|
+
|
|
698
|
+
**See full troubleshooting:** [DEBUG_REPORT.md](./docs/DEBUG_REPORT.md)
|
|
699
|
+
|
|
700
|
+
---
|
|
305
701
|
|
|
306
702
|
## Contributing
|
|
307
703
|
|
|
308
|
-
Contributions are welcome! Please
|
|
704
|
+
Contributions are welcome! Please:
|
|
705
|
+
|
|
706
|
+
1. Fork the repository
|
|
707
|
+
2. Create a feature branch
|
|
708
|
+
3. Add tests for new functionality
|
|
709
|
+
4. Ensure all tests pass (`npm test`)
|
|
710
|
+
5. Submit a pull request
|
|
711
|
+
|
|
712
|
+
---
|
|
309
713
|
|
|
310
714
|
## Known Limitations
|
|
311
715
|
|
|
312
716
|
- Reserved URLs only work for first-level directories
|
|
717
|
+
- Template rendering is synchronous per request
|
|
313
718
|
|
|
314
719
|
See [DEBUG_REPORT.md](./docs/DEBUG_REPORT.md) for technical details.
|
|
315
720
|
|
|
721
|
+
---
|
|
722
|
+
|
|
316
723
|
## License
|
|
317
724
|
|
|
318
|
-
MIT
|
|
725
|
+
MIT License - see LICENSE file for details
|
|
726
|
+
|
|
727
|
+
---
|
|
319
728
|
|
|
320
729
|
## Author
|
|
321
730
|
|
|
322
731
|
Italo Paesano
|
|
323
732
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
See [CHANGELOG.md](./CHANGELOG.md)
|
|
733
|
+
---
|
|
327
734
|
|
|
328
735
|
## Links
|
|
329
736
|
|
|
330
|
-
- [
|
|
331
|
-
- [
|
|
332
|
-
- [
|
|
333
|
-
- [
|
|
334
|
-
|
|
737
|
+
- **[npm Package](https://www.npmjs.com/package/koa-classic-server)** - Official npm package
|
|
738
|
+
- **[GitHub Repository](https://github.com/italopaesano/koa-classic-server)** - Source code
|
|
739
|
+
- **[Issue Tracker](https://github.com/italopaesano/koa-classic-server/issues)** - Report bugs
|
|
740
|
+
- **[Full Documentation](./docs/DOCUMENTATION.md)** - Complete reference
|
|
741
|
+
|
|
742
|
+
---
|
|
743
|
+
|
|
744
|
+
## Changelog
|
|
745
|
+
|
|
746
|
+
See [CHANGELOG.md](./docs/CHANGELOG.md) for version history.
|
|
335
747
|
|
|
336
748
|
---
|
|
337
749
|
|
|
338
|
-
**โ ๏ธ Security Notice:**
|
|
750
|
+
**โ ๏ธ Security Notice:** Always use the latest version for security updates and bug fixes.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koa-classic-server",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.2",
|
|
4
4
|
"description": "High-performance Koa middleware for serving static files with Apache-like directory listing, HTTP caching, template engine support, and comprehensive security fixes",
|
|
5
5
|
"main": "index.cjs",
|
|
6
6
|
"exports": {
|