pax_global_header 0000666 0000000 0000000 00000000064 13163345337 0014522 g ustar 00root root 0000000 0000000 52 comment=a399faa1801f02ee1885e5664ed21a9c7990b63a serve-index-1.9.1/ 0000775 0000000 0000000 00000000000 13163345337 0013763 5 ustar 00root root 0000000 0000000 serve-index-1.9.1/.gitignore 0000664 0000000 0000000 00000000066 13163345337 0015755 0 ustar 00root root 0000000 0000000 coverage node_modules npm-debug.log package-lock.json serve-index-1.9.1/.travis.yml 0000664 0000000 0000000 00000001452 13163345337 0016076 0 ustar 00root root 0000000 0000000 language: node_js node_js: - "0.8" - "0.10" - "0.12" - "1.8" - "2.5" - "3.3" - "4.8" - "5.12" - "6.11" - "7.10" - "8.3" sudo: false cache: directories: - node_modules before_install: # Skip updating shrinkwrap / lock - "npm config set shrinkwrap false" # Setup Node.js version-specific dependencies - "test $TRAVIS_NODE_VERSION != '0.8' || npm rm --save-dev istanbul" # Update Node.js modules - "test ! -d node_modules || npm prune" - "test ! -d node_modules || npm rebuild" script: # Run test script, depending on istanbul install - "test ! -z $(npm -ps ls istanbul) || npm test" - "test -z $(npm -ps ls istanbul) || npm run-script test-ci" after_script: - "test -e ./coverage/lcov.info && npm install coveralls@2 && cat ./coverage/lcov.info | coveralls" serve-index-1.9.1/HISTORY.md 0000664 0000000 0000000 00000015473 13163345337 0015460 0 ustar 00root root 0000000 0000000 1.9.1 / 2017-09-28 ================== * deps: accepts@~1.3.4 - deps: mime-types@~2.1.16 * deps: debug@2.6.9 * deps: http-errors@~1.6.2 - deps: depd@1.1.1 * deps: mime-types@~2.1.17 - Add new mime types - deps: mime-db@~1.30.0 * deps: parseurl@~1.3.2 - perf: reduce overhead for full URLs - perf: unroll the "fast-path" `RegExp` 1.9.0 / 2017-05-25 ================== * Set `X-Content-Type-Options: nosniff` header * deps: batch@0.6.1 * deps: debug@2.6.8 - Allow colors in workers - Deprecated `DEBUG_FD` environment variable set to `3` or higher - Fix `DEBUG_MAX_ARRAY_LENGTH` - Fix error when running under React Native - Use same color for same namespace - deps: ms@2.0.0 * deps: http-errors@~1.6.1 - Make `message` property enumerable for `HttpError`s - deps: inherits@2.0.3 - deps: setprototypeof@1.0.3 - deps: statuses@'>= 1.3.1 < 2' * deps: mime-types@~2.1.15 - Add new mime types - Add `audio/mp3` 1.8.0 / 2016-06-17 ================== * Make inline file search case-insensitive * deps: accepts@~1.3.3 - deps: mime-types@~2.1.11 - deps: negotiator@0.6.1 - perf: improve header parsing speed * deps: http-errors@~1.5.0 - Use `setprototypeof` module to replace `__proto__` setting - deps: inherits@2.0.1 - deps: statuses@'>= 1.3.0 < 2' - perf: enable strict mode * deps: mime-types@~2.1.11 - Add new mime types - Update primary extension for `audio/mp4` - deps: mime-db@~1.23.0 1.7.3 / 2016-01-24 ================== * deps: accepts@~1.2.13 - deps: mime-types@~2.1.6 * deps: batch@0.5.3 - Fix invalid dependency for browserify * deps: escape-html@~1.0.3 - perf: enable strict mode - perf: optimize string replacement - perf: use faster string coercion * deps: mime-types@~2.1.9 - Add new mime types * deps: parseurl@~1.3.1 - perf: enable strict mode 1.7.2 / 2015-07-30 ================== * deps: accepts@~1.2.12 - deps: mime-types@~2.1.4 * deps: mime-types@~2.1.4 - Add new mime types 1.7.1 / 2015-07-05 ================== * deps: accepts@~1.2.10 - deps: mime-types@~2.1.2 * deps: mime-types@~2.1.2 - Add new mime types 1.7.0 / 2015-06-15 ================== * Accept `function` value for `template` option * Send non-chunked response for `OPTIONS` * Stat parent directory when necessary * Use `Date.prototype.toLocaleDateString` to format date * deps: accepts@~1.2.9 - deps: mime-types@~2.1.1 - deps: negotiator@0.5.3 - perf: avoid argument reassignment & argument slice - perf: avoid negotiator recursive construction - perf: enable strict mode - perf: remove unnecessary bitwise operator * deps: escape-html@1.0.2 * deps: mime-types@~2.1.1 - Add new mime types * perf: enable strict mode * perf: remove argument reassignment 1.6.4 / 2015-05-12 ================== * deps: accepts@~1.2.7 - deps: mime-types@~2.0.11 - deps: negotiator@0.5.3 * deps: debug@~2.2.0 - deps: ms@0.7.1 * deps: mime-types@~2.0.11 - Add new mime types 1.6.3 / 2015-03-13 ================== * Properly escape file names in HTML * deps: accepts@~1.2.5 - deps: mime-types@~2.0.10 * deps: debug@~2.1.3 - Fix high intensity foreground color for bold - deps: ms@0.7.0 * deps: escape-html@1.0.1 * deps: mime-types@~2.0.10 - Add new mime types 1.6.2 / 2015-02-16 ================== * deps: accepts@~1.2.4 - deps: mime-types@~2.0.9 - deps: negotiator@0.5.1 * deps: http-errors@~1.3.1 - Construct errors using defined constructors from `createError` - Fix error names that are not identifiers - Set a meaningful `name` property on constructed errors * deps: mime-types@~2.0.9 - Add new mime types - deps: mime-db@~1.7.0 1.6.1 / 2015-01-31 ================== * deps: accepts@~1.2.3 - deps: mime-types@~2.0.8 * deps: mime-types@~2.0.8 - Add new mime types - deps: mime-db@~1.6.0 1.6.0 / 2015-01-01 ================== * Add link to root directory * deps: accepts@~1.2.2 - deps: mime-types@~2.0.7 - deps: negotiator@0.5.0 * deps: batch@0.5.2 * deps: debug@~2.1.1 * deps: mime-types@~2.0.7 - Add new mime types - Fix missing extensions - Fix various invalid MIME type entries - Remove example template MIME types - deps: mime-db@~1.5.0 1.5.3 / 2014-12-10 ================== * deps: accepts@~1.1.4 - deps: mime-types@~2.0.4 * deps: http-errors@~1.2.8 - Fix stack trace from exported function * deps: mime-types@~2.0.4 - Add new mime types - deps: mime-db@~1.3.0 1.5.2 / 2014-12-03 ================== * Fix icon name background alignment on mobile view 1.5.1 / 2014-11-22 ================== * deps: accepts@~1.1.3 - deps: mime-types@~2.0.3 * deps: mime-types@~2.0.3 - Add new mime types - deps: mime-db@~1.2.0 1.5.0 / 2014-10-16 ================== * Create errors with `http-errors` * deps: debug@~2.1.0 - Implement `DEBUG_FD` env variable support * deps: mime-types@~2.0.2 - deps: mime-db@~1.1.0 1.4.1 / 2014-10-15 ================== * deps: accepts@~1.1.2 - Fix error when media type has invalid parameter - deps: negotiator@0.4.9 1.4.0 / 2014-10-03 ================== * Add `dir` argument to `filter` function * Support using tokens multiple times 1.3.1 / 2014-10-01 ================== * Fix incorrect 403 on Windows and Node.js 0.11 * deps: accepts@~1.1.1 - deps: mime-types@~2.0.2 - deps: negotiator@0.4.8 1.3.0 / 2014-09-20 ================== * Add icon for mkv files * Lookup icon by mime type for greater icon support 1.2.1 / 2014-09-05 ================== * deps: accepts@~1.1.0 * deps: debug@~2.0.0 1.2.0 / 2014-08-25 ================== * Add `debug` messages * Resolve relative paths at middleware setup 1.1.6 / 2014-08-10 ================== * Fix URL parsing * deps: parseurl@~1.3.0 1.1.5 / 2014-07-27 ================== * Fix Content-Length calculation for multi-byte file names * deps: accepts@~1.0.7 - deps: negotiator@0.4.7 1.1.4 / 2014-06-20 ================== * deps: accepts@~1.0.5 1.1.3 / 2014-06-20 ================== * deps: accepts@~1.0.4 - use `mime-types` 1.1.2 / 2014-06-19 ================== * deps: batch@0.5.1 1.1.1 / 2014-06-11 ================== * deps: accepts@1.0.3 1.1.0 / 2014-05-29 ================== * Fix content negotiation when no `Accept` header * Properly support all HTTP methods * Support vanilla node.js http servers * Treat `ENAMETOOLONG` as code 414 * Use accepts for negotiation 1.0.3 / 2014-05-20 ================== * Fix error from non-statable files in HTML view 1.0.2 / 2014-04-28 ================== * Add `stylesheet` option * deps: negotiator@0.4.3 1.0.1 / 2014-03-05 ================== * deps: negotiator@0.4.2 1.0.0 / 2014-03-05 ================== * Genesis from connect serve-index-1.9.1/LICENSE 0000664 0000000 0000000 00000002245 13163345337 0014773 0 ustar 00root root 0000000 0000000 (The MIT License) Copyright (c) 2010 Sencha Inc. Copyright (c) 2011 LearnBoost Copyright (c) 2011 TJ Holowaychuk Copyright (c) 2014-2015 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. serve-index-1.9.1/README.md 0000664 0000000 0000000 00000012006 13163345337 0015241 0 ustar 00root root 0000000 0000000 # serve-index [![NPM Version][npm-image]][npm-url] [![NPM Downloads][downloads-image]][downloads-url] [![Linux Build][travis-image]][travis-url] [![Windows Build][appveyor-image]][appveyor-url] [![Test Coverage][coveralls-image]][coveralls-url] [![Gratipay][gratipay-image]][gratipay-url] Serves pages that contain directory listings for a given path. ## Install This is a [Node.js](https://nodejs.org/en/) module available through the [npm registry](https://www.npmjs.com/). Installation is done using the [`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally): ```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`. The `filter` function is called for each file, with the signature `filter(filename, index, files, dir)` where `filename` is the name of the file, `index` is the array index, `files` is the array of files and `dir` is the absolute path the file is located (and thus, the directory the listing is for). ##### 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 or a function that will render a HTML string. Defaults to a built-in template. When given a string, the string is used as a file path to load and then 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. When given as a function, the function is called as `template(locals, callback)` and it needs to invoke `callback(error, htmlString)`. The following are the provided locals: * `directory` is the directory being displayed (where `/` is the root). * `displayIcons` is a Boolean for if icons should be rendered or not. * `fileList` is a sorted array of files in the directory. The array contains objects with the following properties: - `name` is the relative name for the file. - `stat` is a `fs.Stats` object for the file. * `path` is the full filesystem path to `directory`. * `style` is the default stylesheet or the contents of the `stylesheet` option. * `viewName` is the view name provided by the `view` option. ##### 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 // The express.static serves the file contents // The serveIndex is this module serving the directory app.use('/ftp', express.static('public/ftp'), serveIndex('public/ftp', {'icons': true})) // Listen app.listen(3000) ``` ## 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 [npm-url]: https://npmjs.org/package/serve-index [travis-image]: https://img.shields.io/travis/expressjs/serve-index/master.svg?label=linux [travis-url]: https://travis-ci.org/expressjs/serve-index [appveyor-image]: https://img.shields.io/appveyor/ci/dougwilson/serve-index/master.svg?label=windows [appveyor-url]: https://ci.appveyor.com/project/dougwilson/serve-index [coveralls-image]: https://img.shields.io/coveralls/expressjs/serve-index/master.svg [coveralls-url]: https://coveralls.io/r/expressjs/serve-index?branch=master [downloads-image]: https://img.shields.io/npm/dm/serve-index.svg [downloads-url]: https://npmjs.org/package/serve-index [gratipay-image]: https://img.shields.io/gratipay/dougwilson.svg [gratipay-url]: https://www.gratipay.com/dougwilson/ serve-index-1.9.1/appveyor.yml 0000664 0000000 0000000 00000001466 13163345337 0016362 0 ustar 00root root 0000000 0000000 environment: matrix: - nodejs_version: "0.8" - nodejs_version: "0.10" - nodejs_version: "0.12" - nodejs_version: "1.8" - nodejs_version: "2.5" - nodejs_version: "3.3" - nodejs_version: "4.8" - nodejs_version: "5.12" - nodejs_version: "6.11" - nodejs_version: "7.10" - nodejs_version: "8.3" cache: - node_modules install: - ps: Install-Product node $env:nodejs_version - npm config set shrinkwrap false - if "%nodejs_version%" equ "0.8" npm rm --save-dev istanbul - if exist node_modules npm prune - if exist node_modules npm rebuild - npm install build: off test_script: - node --version - npm --version - set npm_test_command=test - for /f %%l in ('npm -ps ls istanbul') do set npm_test_command=test-ci - npm run %npm_test_command% version: "{build}" serve-index-1.9.1/index.js 0000664 0000000 0000000 00000036527 13163345337 0015445 0 ustar 00root root 0000000 0000000 /*! * serve-index * Copyright(c) 2011 Sencha Inc. * Copyright(c) 2011 TJ Holowaychuk * Copyright(c) 2014-2015 Douglas Christopher Wilson * MIT Licensed */ 'use strict'; /** * Module dependencies. * @private */ var accepts = require('accepts'); var createError = require('http-errors'); var debug = require('debug')('serve-index'); var escapeHtml = require('escape-html'); var fs = require('fs') , path = require('path') , normalize = path.normalize , sep = path.sep , extname = path.extname , join = path.join; var Batch = require('batch'); var mime = require('mime-types'); var parseUrl = require('parseurl'); var resolve = require('path').resolve; /** * Module exports. * @public */ module.exports = serveIndex; /*! * Icon cache. */ var cache = {}; /*! * Default template. */ var defaultTemplate = join(__dirname, 'public', 'directory.html'); /*! * Stylesheet. */ var defaultStylesheet = join(__dirname, 'public', 'style.css'); /** * Media types and the map for content negotiation. */ var mediaTypes = [ 'text/html', 'text/plain', 'application/json' ]; var mediaType = { 'text/html': 'html', 'text/plain': 'plain', 'application/json': 'json' }; /** * Serve directory listings with the given `root` path. * * See Readme.md for documentation of options. * * @param {String} root * @param {Object} options * @return {Function} middleware * @public */ function serveIndex(root, options) { var opts = options || {}; // root required if (!root) { throw new TypeError('serveIndex() root path required'); } // resolve root to absolute and normalize var rootPath = normalize(resolve(root) + sep); var filter = opts.filter; var hidden = opts.hidden; var icons = opts.icons; var stylesheet = opts.stylesheet || defaultStylesheet; var template = opts.template || defaultTemplate; var view = opts.view || 'tiles'; return function (req, res, next) { if (req.method !== 'GET' && req.method !== 'HEAD') { res.statusCode = 'OPTIONS' === req.method ? 200 : 405; res.setHeader('Allow', 'GET, HEAD, OPTIONS'); res.setHeader('Content-Length', '0'); res.end(); return; } // parse URLs var url = parseUrl(req); var originalUrl = parseUrl.original(req); var dir = decodeURIComponent(url.pathname); var originalDir = decodeURIComponent(originalUrl.pathname); // join / normalize from root dir var path = normalize(join(rootPath, dir)); // null byte(s), bad request if (~path.indexOf('\0')) return next(createError(400)); // malicious path if ((path + sep).substr(0, rootPath.length) !== rootPath) { debug('malicious path "%s"', path); return next(createError(403)); } // determine ".." display var showUp = normalize(resolve(path) + sep) !== rootPath; // check if we have a directory debug('stat "%s"', path); fs.stat(path, function(err, stat){ if (err && err.code === 'ENOENT') { return next(); } if (err) { err.status = err.code === 'ENAMETOOLONG' ? 414 : 500; return next(err); } 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); if (filter) files = files.filter(function(filename, index, list) { return filter(filename, index, list, path); }); files.sort(); // content-negotiation var accept = accepts(req); var type = accept.type(mediaTypes); // not acceptable if (!type) return next(createError(406)); serveIndex[mediaType[type]](req, res, files, next, originalDir, showUp, icons, path, view, template, stylesheet); }); }); }; }; /** * Respond with text/html. */ serveIndex.html = function _html(req, res, files, next, dir, showUp, icons, path, view, template, stylesheet) { var render = typeof template !== 'function' ? createHtmlRender(template) : template if (showUp) { files.unshift('..'); } // stat all files stat(path, files, function (err, stats) { if (err) return next(err); // combine the stats into the file list var fileList = files.map(function (file, i) { return { name: file, stat: stats[i] }; }); // sort file list fileList.sort(fileSort); // read stylesheet fs.readFile(stylesheet, 'utf8', function (err, style) { if (err) return next(err); // create locals for rendering var locals = { directory: dir, displayIcons: Boolean(icons), fileList: fileList, path: path, style: style, viewName: view }; // render html render(locals, function (err, body) { if (err) return next(err); send(res, 'text/html', body) }); }); }); }; /** * Respond with application/json. */ serveIndex.json = function _json(req, res, files) { send(res, 'application/json', JSON.stringify(files)) }; /** * Respond with text/plain. */ serveIndex.plain = function _plain(req, res, files) { send(res, 'text/plain', (files.join('\n') + '\n')) }; /** * Map html `files`, returning an html unordered list. * @private */ function createHtmlFileList(files, dir, useIcons, view) { var html = '