koa-classic-server 2.1.0 โ†’ 2.1.3

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