Commit 62b4a27ac9eb2784595f2f9224d65c5db9ac5bd1
Add new bootstrap page
cel committed on 8/15/2018, 5:55:28 AMParent: 939ae205075004b45edf0eb46a9acb675baec4ac
Files changed
index.js | changed |
package.json | changed |
index.js | ||
---|---|---|
@@ -20,8 +20,13 @@ | ||
20 | 20 … | .replace(/</g, '<') |
21 | 21 … | .replace(/>/g, '>') |
22 | 22 … | } |
23 | 23 … | |
24 … | +function idToHex(id) { | |
25 … | + var b64 = String(id).replace(/^[%#&]|\.[a-z0-9]*$/g, '') | |
26 … | + return new Buffer(b64, 'base64').toString('hex') | |
27 … | +} | |
28 … | + | |
24 | 29 … | function onceify(fn, self) { |
25 | 30 … | var cbs = [], err, data |
26 | 31 … | return function (cb) { |
27 | 32 … | if (fn) { |
@@ -469,8 +474,9 @@ | ||
469 | 474 … | } |
470 | 475 … | pathname = m[3] |
471 | 476 … | } |
472 | 477 … | if (pathname === '/') return this.serveHome() |
478 … | + if (pathname === '/bootstrap') return this.serveBootstrap() | |
473 | 479 … | if (pathname === '/-/whoami') return this.serveWhoami() |
474 | 480 … | if (pathname === '/-/ping') return this.respond(200, true) |
475 | 481 … | if (pathname === '/-/user/org.couchdb.user:1') return this.serveUser1() |
476 | 482 … | if ((m = /^\/-\/prebuild\/(.*)$/.exec(pathname))) return this.servePrebuild(m[1]) |
@@ -486,22 +492,141 @@ | ||
486 | 492 … | Req.prototype.respondError = function (status, message) { |
487 | 493 … | this.respond(status, {error: message}) |
488 | 494 … | } |
489 | 495 … | |
496 … | +Req.prototype.respondErrorStr = function (status, err) { | |
497 … | + this.res.writeHead(status, {'content-type': 'text/plain'}) | |
498 … | + this.res.end(err.stack || err) | |
499 … | +} | |
500 … | + | |
490 | 501 … | Req.prototype.serveHome = function () { |
491 | 502 … | var self = this |
492 | 503 … | self.res.writeHead(200, {'content-type': 'text/html'}) |
493 | 504 … | var port = 8044 |
494 | 505 … | self.res.end('<!doctype html><html><head><meta charset=utf-8>' + |
495 | 506 … | '<title>' + escapeHTML(pkg.name) + '</title></head><body>' + |
496 | 507 … | '<h1>' + escapeHTML(pkg.name) + '</h1>\n' + |
508 … | + '<p><a href="/bootstrap">Bootstrap</a></p>\n' + | |
497 | 509 … | '</body></html>') |
498 | 510 … | } |
499 | 511 … | |
512 … | +Req.prototype.getMsgIdForBlobMention = function (blobId, feedId, cb) { | |
513 … | + var self = this | |
514 … | + pull( | |
515 … | + self.server.sbot.links({ | |
516 … | + source: feedId, | |
517 … | + dest: blobId, | |
518 … | + rel: 'mentions', | |
519 … | + values: true, | |
520 … | + }), | |
521 … | + pull.filter(function (msg) { | |
522 … | + var c = msg && msg.value && msg.value.content | |
523 … | + return c.type === 'npm-packages' | |
524 … | + }), | |
525 … | + pull.collect(function (err, msgs) { | |
526 … | + if (err) return cb(err) | |
527 … | + if (msgs.length === 0) return cb(new Error('Unable to find message id for mention ' + blobId + ' ' + feedId)) | |
528 … | + if (msgs.length > 1) console.warn('Warning: multiple messages mentioning blob id ' + blobId + ' ' + feedId) | |
529 … | + // TODO: make a smarter decision about which message id to use | |
530 … | + cb(null, msgs.pop().key) | |
531 … | + }) | |
532 … | + ) | |
533 … | +} | |
534 … | + | |
535 … | +Req.prototype.resolvePkg = function (pkgSpec, cb) { | |
536 … | + var m = /(.[^@]*)(?:@(.*))?/.exec(pkgSpec) | |
537 … | + if (!m) return cb(new Error('unable to parse spec: \'' + pkgSpec + '\'')) | |
538 … | + var self = this | |
539 … | + var pkgName = m[1] | |
540 … | + var spec = m[2] || '*' | |
541 … | + var versions = {} | |
542 … | + var distTags = {} | |
543 … | + pull( | |
544 … | + self.getMentions({$prefix: 'npm:' + pkgName + ':'}), | |
545 … | + pull.drain(function (mention) { | |
546 … | + var data = decodeName(mention.name) | |
547 … | + if (!data.version) return | |
548 … | + if (data.distTag) { | |
549 … | + distTags[data.distTag] = data.version | |
550 … | + } | |
551 … | + versions[data.version] = { | |
552 … | + author: mention.author, | |
553 … | + name: pkgName, | |
554 … | + version: data.version, | |
555 … | + blobId: mention.link | |
556 … | + } | |
557 … | + }, function (err) { | |
558 … | + if (err) return cb(err) | |
559 … | + var version = distTags[spec] | |
560 … | + || semver.maxSatisfying(Object.keys(versions), spec) | |
561 … | + var item = versions[version] | |
562 … | + if (!item) return cb(new Error('Version not found: ' + name + '@' + spec)) | |
563 … | + self.getMsgIdForBlobMention(item.blobId, item.author, function (err, id) { | |
564 … | + if (err) return cb(err) | |
565 … | + item.msgId = id | |
566 … | + cb(null, item) | |
567 … | + }) | |
568 … | + }) | |
569 … | + ) | |
570 … | +} | |
571 … | + | |
572 … | +Req.prototype.resolvePkgs = function (specs, cb) { | |
573 … | + var done = multicb({pluck: 1}) | |
574 … | + var self = this | |
575 … | + specs.forEach(function (spec) { | |
576 … | + self.resolvePkg(spec, done()) | |
577 … | + }) | |
578 … | + done(cb) | |
579 … | +} | |
580 … | + | |
581 … | +Req.prototype.serveBootstrap = function () { | |
582 … | + var self = this | |
583 … | + var pkgs = self.server.npmConfig.defaultPkgs || ['scuttlebot', 'ssb-npm', 'git-ssb'] | |
584 … | + var ssbNpmRegistryName = require('./package.json').name | |
585 … | + var ssbNpmRegistryVersion = require('./package.json').version | |
586 … | + var ssbNpmRegistrySpec = ssbNpmRegistryName + '@^' + ssbNpmRegistryVersion | |
587 … | + var done = multicb({pluck: 1, spread: true}) | |
588 … | + self.resolvePkg(ssbNpmRegistrySpec, done()) | |
589 … | + self.resolvePkgs(pkgs, done()) | |
590 … | + done(function (err, ssbNpmRegistryPkgInfo, pkgsInfo) { | |
591 … | + if (err) return self.respondErrorStr(500, err.stack || err) | |
592 … | + if (!ssbNpmRegistryPkgInfo) return self.respondErrorStr(500, 'Missing ssb-npm-registry package') | |
593 … | + var ssbNpmRegistryBlobId = ssbNpmRegistryPkgInfo.blobId | |
594 … | + var ssbNpmRegistryBlobHex = idToHex(ssbNpmRegistryBlobId) | |
595 … | + var pkgMsgs = pkgsInfo.map(function (info) { return info.msgId }) | |
596 … | + var globalPkgs = pkgsInfo.map(function (info) { | |
597 … | + return info.name + '@' + info.version | |
598 … | + }).join(' ') | |
599 … | + var npmCmd = 'npm install -g ' + globalPkgs | |
600 … | + | |
601 … | + var pkgTmpText = '/tmp/' + ssbNpmRegistryName + '.tar.gz' | |
602 … | + var host = String(self.req.headers.host).replace(/:[0-9]*$/, '') || self.req.socket.localAddress | |
603 … | + var httpHost = /^[^\[]:.*:.*:/.test(host) ? '[' + host + ']' : host | |
604 … | + var wsLink = 'http://' + httpHost + ':' + self.server.wsPort + '/' | |
605 … | + var tarballLink = wsLink + 'blobs/get/' + ssbNpmRegistryBlobId | |
606 … | + | |
607 … | + var script = | |
608 … | + 'wget \'' + tarballLink + '\' -O ' + pkgTmpText + ' &&\n' + | |
609 … | + 'echo ' + ssbNpmRegistryBlobHex + ' ' + pkgTmpText + ' | sha256sum -c &&\n' + | |
610 … | + 'mkdir -p ~/.ssb/node_modules && cd ~/.ssb/node_modules &&\n' + | |
611 … | + 'tar xzf ' + pkgTmpText + ' &&\n' + | |
612 … | + 'mv package ' + ssbNpmRegistryName + ' &&\n' + | |
613 … | + ssbNpmRegistryName + '/bootstrap/bin.js --ws-url ' + wsLink + ' \\\n' + | |
614 … | + pkgMsgs.map(function (id) { | |
615 … | + return ' --branch ' + id + ' \\\n' | |
616 … | + }).join('') + | |
617 … | + ' -- ' + npmCmd + ' &&\n' + | |
618 … | + 'sbot server' | |
619 … | + | |
620 … | + self.res.writeHead(200, {'Content-type': 'text/plain'}) | |
621 … | + self.res.end(script) | |
622 … | + }) | |
623 … | +} | |
624 … | + | |
500 | 625 … | Req.prototype.serveWhoami = function () { |
501 | 626 … | var self = this |
502 | 627 … | self.server.sbot.whoami(function (err, feed) { |
503 | - if (err) return self.respondError(err.stack || err) | |
628 … | + if (err) return self.respondError(500, err.stack || err) | |
504 | 629 … | self.respond(200, {username: feed.id}) |
505 | 630 … | }) |
506 | 631 … | } |
507 | 632 … |
Built with git-ssb-web