From bcb25299e3ad4af71de348fa62dc6027b80cdc12 Mon Sep 17 00:00:00 2001 From: cel Date: Sat, 30 Dec 2017 13:38:57 -1000 Subject: Serve files in zip blobs --- lib/serve.js | 97 ++++++++++++++++++++++++++++ package-lock.json | 184 +++++++++++++++++++++++++++++++++++++++++++++++++----- package.json | 3 +- 3 files changed, 267 insertions(+), 17 deletions(-) diff --git a/lib/serve.js b/lib/serve.js index 0c1ede8..abf15c3 100644 --- a/lib/serve.js +++ b/lib/serve.js @@ -21,6 +21,7 @@ var htime = require('human-time') var ph = require('pull-hyperscript') var emojis = require('emoji-named-characters') var jpeg = require('jpeg-autorotate') +var unzip = require('unzip') module.exports = Serve @@ -335,6 +336,7 @@ Serve.prototype.path = function (url) { case '/npm-prebuilds': return this.npmPrebuilds(m[2]) case '/npm-readme': return this.npmReadme(m[2]) case '/markdown': return this.markdown(m[2]) + case '/zip': return this.zip(m[2]) } return this.respond(404, 'Not found') } @@ -2202,6 +2204,101 @@ Serve.prototype.markdown = function (url) { ) } +Serve.prototype.zip = function (url) { + var self = this + var parts = url.split('/').slice(1) + var id = decodeURIComponent(parts.shift()) + var filename = parts.join('/') + var blobs = self.app.sbot.blobs + var etag = id + filename + var index = filename === '' || /\/$/.test(filename) + var indexFilename = index && (filename + 'index.html') + if (filename === '/' || /\/\/$/.test(filename)) { + // force directory listing if path ends in // + filename = filename.replace(/\/$/, '') + indexFilename = false + } + var files = index && [] + if (self.req.headers['if-none-match'] === etag) return self.respond(304) + blobs.size(id, function (err, size) { + if (size == null) return askWantBlobsForm([id]) + if (err) { + if (/^invalid/.test(err.message)) return self.respond(400, err.message) + else return self.respond(500, err.message || err) + } + var parseUnzip = unzip.Parse() + var gotEntry = false + parseUnzip.on('entry', function (entry) { + if (index) { + if (!gotEntry) { + if (entry.path === indexFilename) { + gotEntry = true + return serveFile(entry) + } else if (entry.path.substr(0, filename.length) === filename) { + files.push({path: entry.path, type: entry.type, props: entry.props}) + } + } + } else { + if (!gotEntry && entry.path === filename) { + gotEntry = true + // if (false && entry.type === 'Directory') return serveDirectory(entry) + return serveFile(entry) + } + } + entry.autodrain() + }) + parseUnzip.on('close', function () { + if (gotEntry) return + if (!index) return self.respond(404, 'Entry not found') + pull( + ph('section', {}, [ + ph('h3', [ + ph('a', {href: self.app.render.toUrl('/links/' + id)}, id.substr(0, 8) + '…'), + ' ', + ph('a', {href: self.app.render.toUrl('/zip/' + encodeURIComponent(id) + '/' + filename)}, filename || '/'), + ]), + pull( + pull.values(files), + pull.map(function (file) { + var path = '/zip/' + encodeURIComponent(id) + '/' + file.path + return ph('li', [ + ph('a', {href: self.app.render.toUrl(path)}, file.path) + ]) + }) + ) + ]), + self.wrapPage(id + filename), + self.respondSink(200) + ) + gotEntry = true // so that the handler on error event does not run + }) + parseUnzip.on('error', function (err) { + if (!gotEntry) return self.respond(400, err.message) + }) + var size + function serveFile(entry) { + size = entry.size + pull( + toPull.source(entry), + ident(gotType), + self.respondSink() + ) + } + pull( + self.app.getBlob(id), + toPull(parseUnzip) + ) + function gotType(type) { + type = type && mime.lookup(type) + if (type) self.res.setHeader('Content-Type', type) + if (size) self.res.setHeader('Content-Length', size) + self.res.setHeader('Cache-Control', 'public, max-age=315360000') + self.res.setHeader('etag', etag) + self.res.writeHead(200) + } + }) +} + // wrap a binary source and render it or turn into an embed Serve.prototype.wrapBinary = function (opts) { var self = this diff --git a/package-lock.json b/package-lock.json index ae79d80..0f5d62f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,6 +30,15 @@ "resolved": "http://localhost:8989/blobs/get/&tix85Coqflfg1RbOQ/x6cq0E1Rry5rDhGiIHyRdM+8s=.sha256", "integrity": "sha256-tix85Coqflfg1RbOQ/x6cq0E1Rry5rDhGiIHyRdM+8s=" }, + "binary": { + "version": "0.3.0", + "resolved": "http://localhost:8989/blobs/get/&lAvJtA+yqHWHbjyhvj0Fp6QZiB5CAcdsg/+7BVaLaQk=.sha256", + "integrity": "sha256-lAvJtA+yqHWHbjyhvj0Fp6QZiB5CAcdsg/+7BVaLaQk=", + "requires": { + "buffers": "0.1.1", + "chainsaw": "0.1.0" + } + }, "brace-expansion": { "version": "http://localhost:8989/blobs/get/&fdjQQT88BvLg9ynhFsffO5ZCC04SoSTrnmmGGWWxcPo=.sha256", "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", @@ -42,6 +51,11 @@ "version": "http://localhost:8989/blobs/get/&u3cvbnn1mLUZCBFO3qRNABW3op1sgtwIK2UCQ/RIYGY=.sha256", "integrity": "sha1-QUGcrvdpdVkp3VGJZ9PuwKYmJ3E=" }, + "buffers": { + "version": "0.1.1", + "resolved": "http://localhost:8989/blobs/get/&+N5Jxg5GcAUYLZaHtdh3dbvOxjhaxjIgt3QTJ0Qd620=.sha256", + "integrity": "sha256-+N5Jxg5GcAUYLZaHtdh3dbvOxjhaxjIgt3QTJ0Qd620=" + }, "builtin-modules": { "version": "http://localhost:8989/blobs/get/&54lxeCToaIJpwkHCA9n2Fc8VKG1iF9dN78fzlbDaXxE=.sha256", "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" @@ -60,6 +74,14 @@ "resolved": "http://localhost:8989/blobs/get/&mjv2gAClOLf1jZaoHspyZ5ZbBYFn6imQeCwXDtp1kvU=.sha256", "integrity": "sha256-mjv2gAClOLf1jZaoHspyZ5ZbBYFn6imQeCwXDtp1kvU=" }, + "chainsaw": { + "version": "0.1.0", + "resolved": "http://localhost:8989/blobs/get/&AVhqaqHRF0rgev9pgi0B4HBP4w8jrCIiHJuAS6uXbPs=.sha256", + "integrity": "sha256-AVhqaqHRF0rgev9pgi0B4HBP4w8jrCIiHJuAS6uXbPs=", + "requires": { + "traverse": "0.3.9" + } + }, "chloride": { "version": "2.2.7", "resolved": "http://localhost:8989/blobs/get/&/ahFOC6wlOMnO1HdAIAcuY6NQ9AkKjmR1Os/xnts6N8=.sha256", @@ -216,6 +238,27 @@ "version": "http://localhost:8989/blobs/get/&noDLhxMSWqU9+BopYm97gfJqm+HNQYQLPM3K5NUuj5w=.sha256", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, + "fstream": { + "version": "0.1.31", + "resolved": "http://localhost:8989/blobs/get/&MgatCTGF+duNaFmT4+Nyh0UEi9rjNltsqSTLdeLCKwE=.sha256", + "integrity": "sha256-MgatCTGF+duNaFmT4+Nyh0UEi9rjNltsqSTLdeLCKwE=", + "requires": { + "graceful-fs": "3.0.11", + "inherits": "2.0.3", + "mkdirp": "0.5.1", + "rimraf": "2.6.2" + }, + "dependencies": { + "graceful-fs": { + "version": "3.0.11", + "resolved": "http://localhost:8989/blobs/get/&WtP5g2DDTtr9aihenL4LdIICzr9RtKdKV9qRCFH1JQM=.sha256", + "integrity": "sha256-WtP5g2DDTtr9aihenL4LdIICzr9RtKdKV9qRCFH1JQM=", + "requires": { + "natives": "1.1.0" + } + } + } + }, "get-caller-file": { "version": "1.0.2", "resolved": "http://localhost:8989/blobs/get/&FMgvATYUUtPunnh52Q5mgZVX2OhKaewPbHxlxlYwSac=.sha256", @@ -437,6 +480,27 @@ "yallist": "http://localhost:8989/blobs/get/&x7MQhNNSXhcxS0bCjljOts2PEKn/km2vSDA5dvissLI=.sha256" } }, + "match-stream": { + "version": "0.0.2", + "resolved": "http://localhost:8989/blobs/get/&8ifmMRW33QbZzSrMY/n26MvE9tZtdgdmHG2P8iAx9/w=.sha256", + "integrity": "sha256-8ifmMRW33QbZzSrMY/n26MvE9tZtdgdmHG2P8iAx9/w=", + "requires": { + "buffers": "0.1.1", + "readable-stream": "1.0.34" + }, + "dependencies": { + "readable-stream": { + "version": "1.0.34", + "resolved": "http://localhost:8989/blobs/get/&0QEMlyAUePLZCW/tXOEhdYfVI4R19FBv4JyHzdW8VqY=.sha256", + "requires": { + "core-util-is": "http://localhost:8989/blobs/get/&pKRNq2V57ePgat5Y0m+P1kLq4JFT/VnGCPy3lRpJk5g=.sha256", + "inherits": "2.0.3", + "isarray": "http://localhost:8989/blobs/get/&PoREAgaWgArtkqCdTFJgKtx2FAo5tsjpT+fInPcKnwo=.sha256", + "string_decoder": "http://localhost:8989/blobs/get/&Pm5v/q/mFX6yJ4qQmvwLhFI0sTRG3KipUYwrebnCIIY=.sha256" + } + } + } + }, "mem": { "version": "1.1.0", "resolved": "http://localhost:8989/blobs/get/&JPEeBd7nMfhv4111AT1r8bKQHDHp5jbjOlgYO63qN5I=.sha256", @@ -527,6 +591,11 @@ "integrity": "sha256-dF6wPKUaGRuuJwIvKGrSOSZpzkskLzu2eXPH9iZqSKs=", "optional": true }, + "natives": { + "version": "1.1.0", + "resolved": "http://localhost:8989/blobs/get/&Xc7wzjsjGc4j8E08ehRgLsKR8ETg+IRu5fMxRHJryZA=.sha256", + "integrity": "sha256-Xc7wzjsjGc4j8E08ehRgLsKR8ETg+IRu5fMxRHJryZA=" + }, "node-gyp-build": { "version": "3.2.2", "resolved": "http://localhost:8989/blobs/get/&4FlrrtCACsITHl9W+Fo8zZxpVDu6KPKVlqzJERNerJo=.sha256", @@ -597,6 +666,11 @@ "mem": "1.1.0" } }, + "over": { + "version": "0.0.5", + "resolved": "http://localhost:8989/blobs/get/&2SFVsXLBCq9u2cfVbIv27fccQ+MOAk5V8HAOWGbFwK4=.sha256", + "integrity": "sha256-2SFVsXLBCq9u2cfVbIv27fccQ+MOAk5V8HAOWGbFwK4=" + }, "p-finally": { "version": "1.0.0", "resolved": "http://localhost:8989/blobs/get/&7/2E4J4TMFQqhKJD8fTaIacA1Fm4N2HqyhYHDrH7iEE=.sha256", @@ -699,8 +773,8 @@ "integrity": "sha256-+uVE8RHNwJIJa68sQGICGbxnCTCOdLLhSt/Pb4NmgL0=" }, "pull-defer": { - "version": "0.2.2", - "resolved": "http://localhost:8989/blobs/get/&mDI2o/JC9yvFOVbeAhDN+hwTm7cK+dBwISYO49EMY24=.sha256" + "version": "http://localhost:8989/blobs/get/&mDI2o/JC9yvFOVbeAhDN+hwTm7cK+dBwISYO49EMY24=.sha256", + "integrity": "sha1-CIew/7MK8ypW2+z6csFnInHwexM=" }, "pull-git-packidx-parser": { "version": "http://localhost:8989/blobs/get/&ou0MPQZabBgzrHDu54jzLU3Sc6Rf5a/lost0whPQUJ0=.sha256", @@ -777,8 +851,8 @@ } }, "pull-many": { - "version": "1.0.8", - "resolved": "http://localhost:8989/blobs/get/&wBHfPheBEjwjjkNoeM5mWtU/tYDs8+xnt5w6C1+EK3g=.sha256", + "version": "http://localhost:8989/blobs/get/&wBHfPheBEjwjjkNoeM5mWtU/tYDs8+xnt5w6C1+EK3g=.sha256", + "integrity": "sha1-Pa3ZttFWxUVyG9qNAAPdjqoGKT4=", "requires": { "pull-stream": "3.6.1" } @@ -840,6 +914,29 @@ "ws": "1.1.4" } }, + "pullstream": { + "version": "0.4.1", + "resolved": "http://localhost:8989/blobs/get/&dU4CcxT0bjZmQhlL7P9dpSxkrrm+K5JOy8/1SDVpwSs=.sha256", + "integrity": "sha256-dU4CcxT0bjZmQhlL7P9dpSxkrrm+K5JOy8/1SDVpwSs=", + "requires": { + "over": "0.0.5", + "readable-stream": "1.0.34", + "setimmediate": "1.0.5", + "slice-stream": "1.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "1.0.34", + "resolved": "http://localhost:8989/blobs/get/&0QEMlyAUePLZCW/tXOEhdYfVI4R19FBv4JyHzdW8VqY=.sha256", + "requires": { + "core-util-is": "http://localhost:8989/blobs/get/&pKRNq2V57ePgat5Y0m+P1kLq4JFT/VnGCPy3lRpJk5g=.sha256", + "inherits": "2.0.3", + "isarray": "http://localhost:8989/blobs/get/&PoREAgaWgArtkqCdTFJgKtx2FAo5tsjpT+fInPcKnwo=.sha256", + "string_decoder": "http://localhost:8989/blobs/get/&Pm5v/q/mFX6yJ4qQmvwLhFI0sTRG3KipUYwrebnCIIY=.sha256" + } + } + } + }, "rc": { "version": "1.2.1", "resolved": "http://localhost:8989/blobs/get/&XPry2asXmpIfJLFtlg5hZmO2uM0QlZd5nCr/AJBOLek=.sha256", @@ -895,6 +992,14 @@ "resolved": "http://localhost:8989/blobs/get/&Y6ct0k73eVjQG88nl6Ll/lfhxdEVefvRvci3hMiZmcM=.sha256", "integrity": "sha256-Y6ct0k73eVjQG88nl6Ll/lfhxdEVefvRvci3hMiZmcM=" }, + "rimraf": { + "version": "2.6.2", + "resolved": "http://localhost:8989/blobs/get/&5u4iUQN5Nebmc0+RrO4XpcxAXxAREHuP7ND3+WhZKd4=.sha256", + "integrity": "sha256-5u4iUQN5Nebmc0+RrO4XpcxAXxAREHuP7ND3+WhZKd4=", + "requires": { + "glob": "http://localhost:8989/blobs/get/&zz0+R6Ewi1Ev1wfx31k3N8tWkHnQS/l14fxI3mpintE=.sha256" + } + }, "safe-buffer": { "version": "5.1.1", "resolved": "http://localhost:8989/blobs/get/&DBRHICIwyQWmEc+9TG2DT/zR9Zja3LyNW+dIJuXjsXE=.sha256", @@ -925,6 +1030,11 @@ "version": "http://localhost:8989/blobs/get/&2TSu59ueCdoJ6HckdDMV/+iIEwqm4E+73srJhfauaT0=.sha256", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, + "setimmediate": { + "version": "1.0.5", + "resolved": "http://localhost:8989/blobs/get/&XLn8ImmDZO1CwC1qo9xQ/+r6aEUq6EaZZy49/XSSLJ4=.sha256", + "integrity": "sha256-XLn8ImmDZO1CwC1qo9xQ/+r6aEUq6EaZZy49/XSSLJ4=" + }, "sha.js": { "version": "2.4.5", "resolved": "http://localhost:8989/blobs/get/&JxjiN22oEOlVmJQn3aII8ykRlfimM2Ph+URrf/2KZhs=.sha256", @@ -948,6 +1058,26 @@ "version": "http://localhost:8989/blobs/get/&2JAO0FDbn45cJ1KdQ8Al0Lu7FnN8oZnYa9UP3Ixb0tg=.sha256", "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" }, + "slice-stream": { + "version": "1.0.0", + "resolved": "http://localhost:8989/blobs/get/&vZJmJejF0Cfij1r3a/BjIvBStHkzidwIh8n5j5k63Us=.sha256", + "integrity": "sha256-vZJmJejF0Cfij1r3a/BjIvBStHkzidwIh8n5j5k63Us=", + "requires": { + "readable-stream": "1.0.34" + }, + "dependencies": { + "readable-stream": { + "version": "1.0.34", + "resolved": "http://localhost:8989/blobs/get/&0QEMlyAUePLZCW/tXOEhdYfVI4R19FBv4JyHzdW8VqY=.sha256", + "requires": { + "core-util-is": "http://localhost:8989/blobs/get/&pKRNq2V57ePgat5Y0m+P1kLq4JFT/VnGCPy3lRpJk5g=.sha256", + "inherits": "2.0.3", + "isarray": "http://localhost:8989/blobs/get/&PoREAgaWgArtkqCdTFJgKtx2FAo5tsjpT+fInPcKnwo=.sha256", + "string_decoder": "http://localhost:8989/blobs/get/&Pm5v/q/mFX6yJ4qQmvwLhFI0sTRG3KipUYwrebnCIIY=.sha256" + } + } + } + }, "smart-buffer": { "version": "1.1.15", "resolved": "http://localhost:8989/blobs/get/&WkLju39M7S7Fo0ag0wBPhdAHagB2f32gvxvGhsfZgfc=.sha256", @@ -1054,11 +1184,11 @@ } }, "ssb-contact": { - "version": "1.2.0", - "resolved": "http://localhost:8989/blobs/get/&V4bNrv533a7pKfPxw7Ok92hNWBN+eCbyybmi8cGMzrM=.sha256", + "version": "http://localhost:8989/blobs/get/&V4bNrv533a7pKfPxw7Ok92hNWBN+eCbyybmi8cGMzrM=.sha256", + "integrity": "sha1-lGp4PIQOWRbVEsRTiJYQlwWQzWw=", "requires": { - "pull-defer": "0.2.2", - "pull-many": "1.0.8", + "pull-defer": "http://localhost:8989/blobs/get/&mDI2o/JC9yvFOVbeAhDN+hwTm7cK+dBwISYO49EMY24=.sha256", + "pull-many": "http://localhost:8989/blobs/get/&wBHfPheBEjwjjkNoeM5mWtU/tYDs8+xnt5w6C1+EK3g=.sha256", "pull-stream": "3.6.1" } }, @@ -1076,14 +1206,6 @@ "version": "http://localhost:8989/blobs/get/&5yyYBZpB4w+X6kW42KMh43Xz9KP3XW79mYOj40/CHA4=.sha256", "integrity": "sha1-Fg4kETeCqcpegGByqnpl58hl2/I=" }, - "ssb-mentions": { - "version": "http://localhost:8989/blobs/get/&GKwsJ3ykvOXc24wyNuWBdmnCFpQ+Ltd9ggjoOuOF/Pg=.sha256", - "integrity": "sha1-en5LsSk2uHwNcjeC+b1ZMfuFef4=", - "requires": { - "ssb-marked": "http://localhost:8989/blobs/get/&5yyYBZpB4w+X6kW42KMh43Xz9KP3XW79mYOj40/CHA4=.sha256", - "ssb-ref": "http://localhost:8989/blobs/get/&wLimOD3785KVj7kBIAbjN6GH8EMhKRkcZSPrEMhdhks=.sha256" - } - }, "ssb-ref": { "version": "http://localhost:8989/blobs/get/&wLimOD3785KVj7kBIAbjN6GH8EMhKRkcZSPrEMhdhks=.sha256", "integrity": "sha1-XU7/xUXsD/1/wVuieCmmQLiir7o=", @@ -1176,6 +1298,11 @@ "resolved": "http://localhost:8989/blobs/get/&2+Rf668b9yZcJXMyQrwOesOLYy22qOGfA0GvR3BCWJk=.sha256", "integrity": "sha256-2+Rf668b9yZcJXMyQrwOesOLYy22qOGfA0GvR3BCWJk=" }, + "traverse": { + "version": "0.3.9", + "resolved": "http://localhost:8989/blobs/get/&eBP2sa38eIi/Jilni5wRnDaHQPA0jLlcxH4Fex4WCUI=.sha256", + "integrity": "sha256-eBP2sa38eIi/Jilni5wRnDaHQPA0jLlcxH4Fex4WCUI=" + }, "tweetnacl": { "version": "0.14.5", "resolved": "http://localhost:8989/blobs/get/&bOoz1nqb2D+L0lBlXHiiyJ6pErzGvpHI5lgHzmnP39Y=.sha256", @@ -1194,6 +1321,31 @@ "resolved": "http://localhost:8989/blobs/get/&x9CnHOGgcWXdpCT6uvXvsVHIbcTZ12GYkjEOgXE31BQ=.sha256", "integrity": "sha256-x9CnHOGgcWXdpCT6uvXvsVHIbcTZ12GYkjEOgXE31BQ=" }, + "unzip": { + "version": "0.1.11", + "resolved": "http://localhost:8989/blobs/get/&lUPDF4rcMYrDcMNc3I0oVd9vWMwOqoEHf7ycdfWAjOo=.sha256", + "integrity": "sha256-lUPDF4rcMYrDcMNc3I0oVd9vWMwOqoEHf7ycdfWAjOo=", + "requires": { + "binary": "0.3.0", + "fstream": "0.1.31", + "match-stream": "0.0.2", + "pullstream": "0.4.1", + "readable-stream": "1.0.34", + "setimmediate": "1.0.5" + }, + "dependencies": { + "readable-stream": { + "version": "1.0.34", + "resolved": "http://localhost:8989/blobs/get/&0QEMlyAUePLZCW/tXOEhdYfVI4R19FBv4JyHzdW8VqY=.sha256", + "requires": { + "core-util-is": "http://localhost:8989/blobs/get/&pKRNq2V57ePgat5Y0m+P1kLq4JFT/VnGCPy3lRpJk5g=.sha256", + "inherits": "2.0.3", + "isarray": "http://localhost:8989/blobs/get/&PoREAgaWgArtkqCdTFJgKtx2FAo5tsjpT+fInPcKnwo=.sha256", + "string_decoder": "http://localhost:8989/blobs/get/&Pm5v/q/mFX6yJ4qQmvwLhFI0sTRG3KipUYwrebnCIIY=.sha256" + } + } + } + }, "validate-npm-package-license": { "version": "http://localhost:8989/blobs/get/&FZFTRu/blzraNxOJt63Ut68JKa3cpNPwb1RBvaWZBW4=.sha256", "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=", diff --git a/package.json b/package.json index 76417ae..d4f48d7 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,8 @@ "ssb-marked": "^0.7.1", "ssb-mentions": "http://localhost:8989/blobs/get/&GjuxknqKwJqHznKueFNCyIh52v1woz5PB41vqmoHfyM=.sha256", "ssb-sort": "^1.0.0", - "stream-to-pull-stream": "^1.7.2" + "stream-to-pull-stream": "^1.7.2", + "unzip": "^0.1.11" }, "scripts": { "start": "ssb-client ." -- cgit v1.2.3