Compare commits

...

10 Commits

Author SHA1 Message Date
Douglas Christopher Wilson
63456cccf2 1.2.0 2014-08-25 19:13:55 -04:00
Douglas Christopher Wilson
6ce2fd216b Add debug messages 2014-08-25 18:56:27 -04:00
Douglas Christopher Wilson
83de752968 Resolve relative paths at middleware setup
fixes #15
2014-08-25 18:51:14 -04:00
Douglas Christopher Wilson
86db218bdb docs: readme tweaks 2014-08-25 18:36:10 -04:00
Douglas Christopher Wilson
0375dd3638 tests: check for global leaks 2014-08-25 18:33:22 -04:00
Douglas Christopher Wilson
005dbad307 build: change readme/history capitalization 2014-08-25 18:32:20 -04:00
Douglas Christopher Wilson
a189d6b5e5 docs: link to license from readme 2014-08-25 18:30:59 -04:00
Douglas Christopher Wilson
a01c0286df docs: update badges 2014-08-25 18:29:49 -04:00
Douglas Christopher Wilson
6e4da1e8d6 build: remove npmignore file 2014-08-25 18:27:54 -04:00
Douglas Christopher Wilson
d88117fad9 docs: expand examples
closes #14
2014-08-17 13:38:00 -04:00
7 changed files with 173 additions and 88 deletions

View File

@@ -1,3 +0,0 @@
coverage
test
.travis.yml

View File

@@ -1,3 +1,9 @@
1.2.0 / 2014-08-25
==================
* Add `debug` messages
* Resolve relative paths at middleware setup
1.1.6 / 2014-08-10
==================

123
README.md Normal file
View File

@@ -0,0 +1,123 @@
# serve-index
[![NPM Version][npm-image]][npm-url]
[![NPM Downloads][downloads-image]][downloads-url]
[![Build Status][travis-image]][travis-url]
[![Test Coverage][coveralls-image]][coveralls-url]
[![Gittip][gittip-image]][gittip-url]
Serves pages that contain directory listings for a given path.
## Install
```sh
$ npm install serve-index
```
## API
```js
var serveIndex = require('serve-index')
```
### serveIndex(path, options)
Returns middlware that serves an index of the directory in the given `path`.
The `path` is based off the `req.url` value, so a `req.url` of `'/some/dir`
with a `path` of `'public'` will look at `'public/some/dir'`. If you are using
something like `express`, you can change the URL "base" with `app.use` (see
the express example).
#### Options
Serve index accepts these properties in the options object.
##### filter
Apply this filter function to files. Defaults to `false`.
##### hidden
Display hidden (dot) files. Defaults to `false`.
##### icons
Display icons. Defaults to `false`.
##### stylesheet
Optional path to a CSS stylesheet. Defaults to a built-in stylesheet.
##### template
Optional path to an HTML template. Defaults to a built-in template.
The following tokens are replaced in templates:
* `{directory}` with the name of the directory.
* `{files}` with the HTML of an unordered list of file links.
* `{linked-path}` with the HTML of a link to the directory.
* `{style}` with the specified stylesheet and embedded images.
##### view
Display mode. `tiles` and `details` are available. Defaults to `tiles`.
## Examples
### Serve directory indexes with vanilla node.js http server
```js
var finalhandler = require('finalhandler')
var http = require('http')
var serveIndex = require('serve-index')
var serveStatic = require('serve-static')
// Serve directory indexes for public/ftp folder (with icons)
var index = serveIndex('public/ftp', {'icons': true})
// Serve up public/ftp folder files
var serve = serveStatic('public/ftp')
// Create server
var server = http.createServer(function onRequest(req, res){
var done = finalhandler(req, res)
serve(req, res, function onNext(err) {
if (err) return done(err)
index(req, res, done)
})
})
// Listen
server.listen(3000)
```
### Serve directory indexes with express
```js
var express = require('express')
var serveIndex = require('serve-index')
var app = express()
// Serve URLs like /ftp/thing as public/ftp/thing
app.use('/ftp', serveIndex('public/ftp', {'icons': true}))
app.listen()
```
## License
[MIT](LICENSE). The [Silk](http://www.famfamfam.com/lab/icons/silk/) icons
are created by/copyright of [FAMFAMFAM](http://www.famfamfam.com/).
[npm-image]: https://img.shields.io/npm/v/serve-index.svg?style=flat
[npm-url]: https://npmjs.org/package/serve-index
[travis-image]: https://img.shields.io/travis/expressjs/serve-index.svg?style=flat
[travis-url]: https://travis-ci.org/expressjs/serve-index
[coveralls-image]: https://img.shields.io/coveralls/expressjs/serve-index.svg?style=flat
[coveralls-url]: https://coveralls.io/r/expressjs/serve-index?branch=master
[downloads-image]: http://img.shields.io/npm/dm/serve-index.svg?style=flat
[downloads-url]: https://npmjs.org/package/serve-index
[gittip-image]: https://img.shields.io/gittip/dougwilson.svg?style=flat
[gittip-url]: https://www.gittip.com/dougwilson/

View File

@@ -1,65 +0,0 @@
# serve-index
[![NPM version](https://badge.fury.io/js/serve-index.svg)](http://badge.fury.io/js/serve-index)
[![Build Status](https://travis-ci.org/expressjs/serve-index.svg?branch=master)](https://travis-ci.org/expressjs/serve-index)
[![Coverage Status](https://img.shields.io/coveralls/expressjs/serve-index.svg?branch=master)](https://coveralls.io/r/expressjs/serve-index)
[![Gittip](http://img.shields.io/gittip/dougwilson.svg)](https://www.gittip.com/dougwilson/)
Serves pages that contain directory listings for a given path.
## API
```js
var express = require('express')
var serveIndex = require('serve-index')
var app = express()
app.use(serveIndex('public/ftp', {'icons': true}))
app.listen()
```
### serveIndex(path, options)
Returns middlware that serves an index of the directory in the given `path`.
#### Options
- `hidden` - display hidden (dot) files. Defaults to `false`.
- `view` - display mode. `tiles` and `details` are available. Defaults to `tiles`.
- `icons` - display icons. Defaults to `false`.
- `filter` - Apply this filter function to files. Defaults to `false`.
- `stylesheet` - Optional path to a CSS stylesheet. Defaults to a built-in stylesheet.
- `template` - Optional path to an HTML template. Defaults to a built-in template.
- The following tokens are replaced in templates:
- `{directory}` with the name of the directory.
- `{files}` with the HTML of an unordered list of file links.
- `{linked-path}` with the HTML of a link to the directory.
- `{style}` with the specified stylesheet and embedded images.
## License
The MIT License (MIT)
Copyright (c) 2014 Douglas Christopher Wilson
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
The [Silk](http://www.famfamfam.com/lab/icons/silk/) icons are created
by/copyright of [FAMFAMFAM](http://www.famfamfam.com/).

View File

@@ -15,6 +15,7 @@
*/
var accepts = require('accepts');
var debug = require('debug')('serve-index');
var http = require('http')
, fs = require('fs')
, path = require('path')
@@ -24,6 +25,7 @@ var http = require('http')
, join = path.join;
var Batch = require('batch');
var parseUrl = require('parseurl');
var resolve = require('path').resolve;
/*!
* Icon cache.
@@ -76,11 +78,13 @@ exports = module.exports = function serveIndex(root, options){
// root required
if (!root) throw new TypeError('serveIndex() root path required');
// resolve root to absolute
root = resolve(root);
var hidden = options.hidden
, icons = options.icons
, view = options.view || 'tiles'
, filter = options.filter
, root = normalize(root + sep)
, template = options.template || defaultTemplate
, stylesheet = options.stylesheet || defaultStylesheet;
@@ -101,15 +105,19 @@ exports = module.exports = function serveIndex(root, options){
var dir = decodeURIComponent(url.pathname)
, path = normalize(join(root, dir))
, originalDir = decodeURIComponent(originalUrl.pathname)
, showUp = path != root;
var showUp = resolve(path) !== root;
// null byte(s), bad request
if (~path.indexOf('\0')) return next(createError(400));
// malicious path, forbidden
if (0 != path.indexOf(root)) return next(createError(403));
// malicious path
if (path.substr(0, root.length) !== root) {
debug('malicious path "%s"', path);
return next(createError(403));
}
// check if we have a directory
debug('stat "%s"', path);
fs.stat(path, function(err, stat){
if (err && err.code === 'ENOENT') {
return next();
@@ -125,6 +133,7 @@ exports = module.exports = function serveIndex(root, options){
if (!stat.isDirectory()) return next();
// fetch files
debug('readdir "%s"', path);
fs.readdir(path, function(err, files){
if (err) return next(err);
if (!hidden) files = removeHidden(files);

View File

@@ -1,13 +1,14 @@
{
"name": "serve-index",
"description": "Serve directory listings",
"version": "1.1.6",
"version": "1.2.0",
"author": "Douglas Christopher Wilson <doug@somethingdoug.com>",
"license": "MIT",
"repository": "expressjs/serve-index",
"dependencies": {
"accepts": "~1.0.7",
"batch": "0.5.1",
"debug": "1.0.4",
"parseurl": "~1.3.0"
},
"devDependencies": {
@@ -16,12 +17,18 @@
"should": "~4.0.0",
"supertest": "~0.13.0"
},
"files": [
"public/",
"LICENSE",
"HISTORY.md",
"index.js"
],
"engines": {
"node": ">= 0.8.0"
},
"scripts": {
"test": "mocha --reporter dot",
"test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot",
"test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec"
"test": "mocha --reporter spec --bail --check-leaks test/",
"test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/",
"test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/"
}
}

View File

@@ -1,10 +1,16 @@
var http = require('http');
var fs = require('fs');
var path = require('path');
var request = require('supertest');
var should = require('should');
var serveIndex = require('..');
var fixtures = path.join(__dirname, '/fixtures');
var relative = path.relative(process.cwd(), fixtures);
var skipRelative = ~relative.indexOf('..') || path.resolve(relative) === relative;
describe('serveIndex(root)', function () {
it('should require root', function () {
serveIndex.should.throw(/root path required/)
@@ -222,7 +228,7 @@ describe('serveIndex(root)', function () {
describe('with "filter" option', function () {
it('should custom filter files', function (done) {
var seen = false
var server = createServer('test/fixtures', {'filter': filter})
var server = createServer(fixtures, {'filter': filter})
function filter(name) {
if (name.indexOf('foo') === -1) return true
@@ -242,7 +248,7 @@ describe('serveIndex(root)', function () {
it('should filter after hidden filter', function (done) {
var seen = false
var server = createServer('test/fixtures', {'filter': filter, 'hidden': false})
var server = createServer(fixtures, {'filter': filter, 'hidden': false})
function filter(name) {
seen = seen || name.indexOf('.') === 0
@@ -261,7 +267,7 @@ describe('serveIndex(root)', function () {
describe('with "icons" option', function () {
it('should include icons for html', function (done) {
var server = createServer('test/fixtures', {'icons': true})
var server = createServer(fixtures, {'icons': true})
request(server)
.get('/')
@@ -468,7 +474,7 @@ describe('serveIndex(root)', function () {
describe('when setting a custom template', function () {
var server;
before(function () {
server = createServer('test/fixtures', {'template': __dirname + '/shared/template.html'});
server = createServer(fixtures, {'template': __dirname + '/shared/template.html'});
});
it('should respond with file list', function (done) {
@@ -500,7 +506,7 @@ describe('serveIndex(root)', function () {
describe('when setting a custom stylesheet', function () {
var server;
before(function () {
server = createServer('test/fixtures', {'stylesheet': __dirname + '/shared/styles.css'});
server = createServer(fixtures, {'stylesheet': __dirname + '/shared/styles.css'});
});
it('should respond with appropriate embedded styles', function (done) {
@@ -517,7 +523,7 @@ describe('serveIndex(root)', function () {
describe('when set with trailing slash', function () {
var server;
before(function () {
server = createServer('test/fixtures/');
server = createServer(fixtures + '/');
});
it('should respond with file list', function (done) {
@@ -533,20 +539,22 @@ describe('serveIndex(root)', function () {
});
});
describe('when set to \'.\'', function () {
(skipRelative ? describe.skip : describe)('when set to \'.\'', function () {
var server;
before(function () {
server = createServer('.');
});
it('should respond with file list', function (done) {
var dest = relative.split(path.sep).join('/');
request(server)
.get('/')
.get('/' + dest + '/')
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(/LICENSE/)
.expect(/public/)
.expect(/test/)
.expect(/users/)
.expect(/file #1\.txt/)
.expect(/nums/)
.expect(/todo\.txt/)
.expect(200, done)
});
@@ -560,7 +568,7 @@ describe('serveIndex(root)', function () {
});
function createServer(dir, opts) {
dir = dir || 'test/fixtures'
dir = dir || fixtures
var _serveIndex = serveIndex(dir, opts)