'use strict'; const styles = require('./styles'); const lastModifiedToString = require('./last-modified-to-string'); const permsToString = require('./perms-to-string'); const sizeToString = require('./size-to-string'); const sortFiles = require('./sort-files'); const fs = require('fs'); const path = require('path'); const he = require('he'); const etag = require('../etag'); const url = require('url'); const status = require('../status-handlers'); const supportedIcons = styles.icons; const css = styles.css; module.exports = (opts) => { // opts are parsed by opts.js, defaults already applied const cache = opts.cache; const root = path.resolve(opts.root); const baseDir = opts.baseDir; const humanReadable = opts.humanReadable; const hidePermissions = opts.hidePermissions; const handleError = opts.handleError; const showDotfiles = opts.showDotfiles; const si = opts.si; const weakEtags = opts.weakEtags; return function middleware(req, res, next) { // Figure out the path for the file from the given url const parsed = url.parse(req.url); const pathname = decodeURIComponent(parsed.pathname); const dir = path.normalize( path.join( root, path.relative( path.join('/', baseDir), pathname ) ) ); fs.stat(dir, (statErr, stat) => { if (statErr) { if (handleError) { status[500](res, next, { error: statErr }); } else { next(); } return; } // files are the listing of dir fs.readdir(dir, (readErr, _files) => { let files = _files; if (readErr) { if (handleError) { status[500](res, next, { error: readErr }); } else { next(); } return; } // Optionally exclude dotfiles from directory listing. if (!showDotfiles) { files = files.filter(filename => filename.slice(0, 1) !== '.'); } res.setHeader('content-type', 'text/html'); res.setHeader('etag', etag(stat, weakEtags)); res.setHeader('last-modified', (new Date(stat.mtime)).toUTCString()); res.setHeader('cache-control', cache); function render(dirs, renderFiles, lolwuts) { // each entry in the array is a [name, stat] tuple let html = `${[ '', '', ' ', ' ', ' ', ` Index of ${he.encode(pathname)}`, ` `, ' ', ' ', `

Index of ${he.encode(pathname)}

`, ].join('\n')}\n`; html += ''; const failed = false; const writeRow = (file) => { // render a row given a [name, stat] tuple const isDir = file[1].isDirectory && file[1].isDirectory(); let href = `./${encodeURIComponent(file[0])}`; // append trailing slash and query for dir entry if (isDir) { href += `/${he.encode((parsed.search) ? parsed.search : '')}`; } const displayName = he.encode(file[0]) + ((isDir) ? '/' : ''); const ext = file[0].split('.').pop(); const classForNonDir = supportedIcons[ext] ? ext : '_page'; const iconClass = `icon-${isDir ? '_blank' : classForNonDir}`; // TODO: use stylessheets? html += `${'' + '`; if (!hidePermissions) { html += ``; } html += `` + `` + `` + '\n'; }; dirs.sort((a, b) => a[0].toString().localeCompare(b[0].toString())).forEach(writeRow); renderFiles.sort((a, b) => a.toString().localeCompare(b.toString())).forEach(writeRow); lolwuts.sort((a, b) => a[0].toString().localeCompare(b[0].toString())).forEach(writeRow); html += '
(${permsToString(file[1])})${lastModifiedToString(file[1])}${sizeToString(file[1], humanReadable, si)}${displayName}
\n'; html += `
Node.js ${ process.version }/ http-server ` + `server running @ ${ he.encode(req.headers.host || '')}
\n` + '' ; if (!failed) { res.writeHead(200, { 'Content-Type': 'text/html' }); res.end(html); } } sortFiles(dir, files, (lolwuts, dirs, sortedFiles) => { // It's possible to get stat errors for all sorts of reasons here. // Unfortunately, our two choices are to either bail completely, // or just truck along as though everything's cool. In this case, // I decided to just tack them on as "??!?" items along with dirs // and files. // // Whatever. // if it makes sense to, add a .. link if (path.resolve(dir, '..').slice(0, root.length) === root) { fs.stat(path.join(dir, '..'), (err, s) => { if (err) { if (handleError) { status[500](res, next, { error: err }); } else { next(); } return; } dirs.unshift(['..', s]); render(dirs, sortedFiles, lolwuts); }); } else { render(dirs, sortedFiles, lolwuts); } }); }); }); }; };