From 8c4ef23da3e16e97cf98c9b7b3b7505082c35dab Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Sun, 12 Aug 2018 18:57:51 -0600 Subject: [PATCH 1/7] #73 stat files before serveIndex, also output date, size, and type --- index.js | 139 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 95 insertions(+), 44 deletions(-) diff --git a/index.js b/index.js index e707bbc5..d72fe1ce 100644 --- a/index.js +++ b/index.js @@ -107,6 +107,13 @@ function serveIndex(root, options) { return; } + // content-negotiation + var accept = accepts(req); + var type = accept.type(mediaTypes); + + // not acceptable + if (!type) return next(createError(406)); + // parse URLs var url = parseUrl(req); var originalUrl = parseUrl.original(req); @@ -154,13 +161,26 @@ function serveIndex(root, options) { }); files.sort(); - // content-negotiation - var accept = accepts(req); - var type = accept.type(mediaTypes); + // add parent directory as first + if (showUp) { + files.unshift('..'); + } + + // stat all files + fstat(path, files, function (err, stats) { + if (err) return next(err); - // not acceptable - if (!type) return next(createError(406)); - serveIndex[mediaType[type]](req, res, files, next, originalDir, showUp, icons, path, view, template, stylesheet); + // 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); + + var dir = { name: originalDir, stat: stat }; + serveIndex[mediaType[type]](req, res, dir, fileList, next, showUp, icons, path, view, template, stylesheet); + }); }); }); }; @@ -170,46 +190,29 @@ function serveIndex(root, options) { * Respond with text/html. */ -serveIndex.html = function _html(req, res, files, next, dir, showUp, icons, path, view, template, stylesheet) { +serveIndex.html = function _html(req, res, dir, files, next, 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) { + // read stylesheet + fs.readFile(stylesheet, 'utf8', function (err, style) { 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); + // create locals for rendering + var locals = { + directory: dir.name, + displayIcons: Boolean(icons), + fileList: files, + path: path, + style: style, + viewName: view + }; - // read stylesheet - fs.readFile(stylesheet, 'utf8', function (err, style) { + // render html + render(locals, function (err, body) { 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) - }); + send(res, 'text/html', body) }); }); }; @@ -218,16 +221,49 @@ serveIndex.html = function _html(req, res, files, next, dir, showUp, icons, path * Respond with application/json. */ -serveIndex.json = function _json(req, res, files) { - send(res, 'application/json', JSON.stringify(files)) +serveIndex.json = function _json(req, res, dir, files) { + var directory = { + name: dir.name, + type: 'inode/directory', + size: dir.stat.size, + lastModified: dir.stat.mtime.toISOString() + } + + // similar to W3 FileAPI Object + var nodes = files.map(function (file) { + var ext = extname(file.name) + var mimetype = mime.lookup(ext) + return { + name: file.name, + type: file.stat.isDirectory() ? 'inode/directory' : mimetype, + size: file.stat.size, + lastModified: file.stat.mtime.toISOString() + } + }) + send(res, 'application/json', JSON.stringify({ directory: directory, nodes: nodes })) }; /** * Respond with text/plain. */ -serveIndex.plain = function _plain(req, res, files) { - send(res, 'text/plain', (files.join('\n') + '\n')) +serveIndex.plain = function _plain(req, res, dir, files) { + var directory = { + name: dir.name.replace(/\/$/, '\/') + }; + + // include size and date + var nodes = files.map(function (file) { + // human readable + var size = hsize(file.stat.size) + + return [ + file.stat.mtime.toISOString(), + size, + file.name + (file.stat.isDirectory() ? '/' : '') + ].join('\t') + }) + send(res, 'text/plain', (directory.name + '\n' + nodes.join('\n') + '\n')) }; /** @@ -247,7 +283,7 @@ function createHtmlFileList(files, dir, useIcons, view) { html += files.map(function (file) { var classes = []; var isDir = file.stat && file.stat.isDirectory(); - var path = dir.split('/').map(function (c) { return encodeURIComponent(c); }); + var path = dir.name.split('/').map(function (c) { return encodeURIComponent(c); }); if (useIcons) { classes.push('icon'); @@ -506,12 +542,27 @@ function send (res, type, body) { res.end(body, 'utf8') } +/** + * Convert size to human-readable size + */ + +var SIZES = [ 'B', 'K', 'M', 'G', 'T', 'P', 'E' ] +function hsize(size) { + var i = 0 + while (size > 1024 && i < SIZES.length) { + size = Math.floor(size/1024) + i += 1 + } + return size + SIZES[i] +} + + /** * Stat all files and return array of stat * in same order. */ -function stat(dir, files, cb) { +function fstat(dir, files, cb) { var batch = new Batch(); batch.concurrency(10); From bbfd384d9fe0e4d96f70d293d92fd5c42d93c27d Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Sun, 12 Aug 2018 20:32:11 -0600 Subject: [PATCH 2/7] use dir as string, not object --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index d72fe1ce..6ef75b33 100644 --- a/index.js +++ b/index.js @@ -283,7 +283,7 @@ function createHtmlFileList(files, dir, useIcons, view) { html += files.map(function (file) { var classes = []; var isDir = file.stat && file.stat.isDirectory(); - var path = dir.name.split('/').map(function (c) { return encodeURIComponent(c); }); + var path = dir.split('/').map(function (c) { return encodeURIComponent(c); }); if (useIcons) { classes.push('icon'); From af9d2be5354a00620ff0c6075a020daefdea7a18 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Sun, 12 Aug 2018 20:39:32 -0600 Subject: [PATCH 3/7] #73 update tests for first PR --- test/test.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/test.js b/test/test.js index b0c9531d..05d12f0f 100644 --- a/test/test.js +++ b/test/test.js @@ -504,9 +504,9 @@ describe('serveIndex(root)', function () { it('should get file list', function (done) { var server = createServer() - serveIndex.html = function (req, res, files) { + serveIndex.html = function (req, res, dir, files) { var text = files - .filter(function (f) { return /\.txt$/.test(f) }) + .filter(function (f) { return /\.txt$/.test(f.name) }) .sort() res.setHeader('Content-Type', 'text/html') res.end('' + text.length + ' text files') @@ -521,9 +521,9 @@ describe('serveIndex(root)', function () { it('should get dir name', function (done) { var server = createServer() - serveIndex.html = function (req, res, files, next, dir) { + serveIndex.html = function (req, res, dir, files, next) { res.setHeader('Content-Type', 'text/html') - res.end('' + dir + '') + res.end('' + dir.name + '') } request(server) @@ -535,7 +535,7 @@ describe('serveIndex(root)', function () { it('should get template path', function (done) { var server = createServer() - serveIndex.html = function (req, res, files, next, dir, showUp, icons, path, view, template) { + serveIndex.html = function (req, res, dir, files, next, showUp, icons, path, view, template) { res.setHeader('Content-Type', 'text/html') res.end(String(fs.existsSync(template))) } @@ -549,7 +549,7 @@ describe('serveIndex(root)', function () { it('should get template with tokens', function (done) { var server = createServer() - serveIndex.html = function (req, res, files, next, dir, showUp, icons, path, view, template) { + serveIndex.html = function (req, res, dir, files, next, showUp, icons, path, view, template) { res.setHeader('Content-Type', 'text/html') res.end(fs.readFileSync(template, 'utf8')) } @@ -567,7 +567,7 @@ describe('serveIndex(root)', function () { it('should get stylesheet path', function (done) { var server = createServer() - serveIndex.html = function (req, res, files, next, dir, showUp, icons, path, view, template, stylesheet) { + serveIndex.html = function (req, res, dir, files, next, showUp, icons, path, view, template, stylesheet) { res.setHeader('Content-Type', 'text/html') res.end(String(fs.existsSync(stylesheet))) } From 79d55721fd7befaa683c01e938a12564d9666da6 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Sun, 12 Aug 2018 22:44:41 -0600 Subject: [PATCH 4/7] convert from fs.stat to vanilla json before response templates --- index.js | 62 ++++++++++++++++++++++++++-------------------------- test/test.js | 6 +++-- 2 files changed, 35 insertions(+), 33 deletions(-) diff --git a/index.js b/index.js index 6ef75b33..817df1ec 100644 --- a/index.js +++ b/index.js @@ -178,8 +178,26 @@ function serveIndex(root, options) { // sort file list fileList.sort(fileSort); - var dir = { name: originalDir, stat: stat }; - serveIndex[mediaType[type]](req, res, dir, fileList, next, showUp, icons, path, view, template, stylesheet); + // make similar to W3 FileAPI Object + var directory = { + name: originalDir, + type: 'inode/directory', + size: stat.size, + lastModified: stat.mtime + } + + var nodes = fileList.map(function (file) { + var ext = extname(file.name) + var mimetype = mime.lookup(ext) + return { + name: file.name, + type: file.stat.isDirectory() ? 'inode/directory' : mimetype, + size: file.stat.size, + lastModified: file.stat.mtime + } + }) + + serveIndex[mediaType[type]](req, res, directory, nodes, next, showUp, icons, path, view, template, stylesheet); }); }); }); @@ -221,25 +239,7 @@ serveIndex.html = function _html(req, res, dir, files, next, showUp, icons, path * Respond with application/json. */ -serveIndex.json = function _json(req, res, dir, files) { - var directory = { - name: dir.name, - type: 'inode/directory', - size: dir.stat.size, - lastModified: dir.stat.mtime.toISOString() - } - - // similar to W3 FileAPI Object - var nodes = files.map(function (file) { - var ext = extname(file.name) - var mimetype = mime.lookup(ext) - return { - name: file.name, - type: file.stat.isDirectory() ? 'inode/directory' : mimetype, - size: file.stat.size, - lastModified: file.stat.mtime.toISOString() - } - }) +serveIndex.json = function _json(req, res, directory, nodes) { send(res, 'application/json', JSON.stringify({ directory: directory, nodes: nodes })) }; @@ -255,12 +255,12 @@ serveIndex.plain = function _plain(req, res, dir, files) { // include size and date var nodes = files.map(function (file) { // human readable - var size = hsize(file.stat.size) + var size = hsize(file.size) return [ - file.stat.mtime.toISOString(), + file.lastModified.toISOString(), size, - file.name + (file.stat.isDirectory() ? '/' : '') + file.name + ('inode/directory' === file.type ? '/' : '') ].join('\t') }) send(res, 'text/plain', (directory.name + '\n' + nodes.join('\n') + '\n')) @@ -271,7 +271,7 @@ serveIndex.plain = function _plain(req, res, dir, files) { * @private */ -function createHtmlFileList(files, dir, useIcons, view) { +function createHtmlFileList(files, dirname, useIcons, view) { var html = '