aboutsummaryrefslogtreecommitdiff
path: root/lib/serve.js
diff options
context:
space:
mode:
Diffstat (limited to 'lib/serve.js')
-rw-r--r--lib/serve.js97
1 files changed, 97 insertions, 0 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