index.jsView |
---|
358 | 358 | this.wsPort = config.ws && Number(config.ws.port) || '8989' |
359 | 359 | this.blobsPrefix = 'http://' + (config.host || 'localhost') + ':' |
360 | 360 | + this.wsPort + '/blobs/get/' |
361 | 361 | this.getBootstrapInfo = onceify(this.getBootstrapInfo, this) |
| 362 | + this.getMsg = memo({cache: lru(100)}, this.getMsg) |
362 | 363 | } |
363 | 364 | |
364 | 365 | SsbNpmRegistryServer.prototype = Object.create(http.Server.prototype) |
365 | 366 | SsbNpmRegistryServer.prototype.constructor = SsbNpmRegistryServer |
504 | 505 | } |
505 | 506 | }) |
506 | 507 | } |
507 | 508 | |
| 509 | +SsbNpmRegistryServer.prototype.getMsg = function (id, cb) { |
| 510 | + if (this.sbot.ooo) return this.sbot.ooo.get(id, cb) |
| 511 | + else this.sbot.get(id, function (err, value) { |
| 512 | + if (err) return cb(err) |
| 513 | + cb(null, {key: id, value: value}) |
| 514 | + }) |
| 515 | +} |
| 516 | + |
| 517 | +SsbNpmRegistryServer.prototype.streamTree = function (heads, prop) { |
| 518 | + var self = this |
| 519 | + var stack = heads.slice() |
| 520 | + var seen = {} |
| 521 | + return function (abort, cb) { |
| 522 | + if (abort) return cb(abort) |
| 523 | + for (var id; stack.length && seen[id = stack.pop()];); |
| 524 | + if (!id) return cb(true) |
| 525 | + seen[id] = true |
| 526 | + |
| 527 | + self.getMsg(id, function (err, msg) { |
| 528 | + if (err) return cb(new Error(err.stack || err)) |
| 529 | + var c = msg && msg.value && msg.value.content |
| 530 | + var links = c && c[prop] |
| 531 | + if (Array.isArray(links)) { |
| 532 | + stack.push.apply(stack, links) |
| 533 | + } else if (links) { |
| 534 | + stack.push(links) |
| 535 | + } |
| 536 | + cb(null, msg) |
| 537 | + }) |
| 538 | + } |
| 539 | +} |
| 540 | + |
508 | 541 | function Req(server, req, res) { |
509 | 542 | this.server = server |
510 | 543 | this.req = req |
511 | 544 | this.res = res |
516 | 549 | Req.prototype.serve = function () { |
517 | 550 | console.log(this.req.method, this.req.url, this.req.socket.remoteAddress.replace(/^::ffff:/, '')) |
518 | 551 | var pathname = this.req.url.replace(/\?.*/, '') |
519 | 552 | var m |
| 553 | + if ((m = /^\/(%25.*sha256)(\/.*)$/.exec(pathname))) { |
| 554 | + try { |
| 555 | + this.headMsgId = decodeURIComponent(m[1]) |
| 556 | + } catch(e) { |
| 557 | + return this.respondError(400, e.stack || e) |
| 558 | + } |
| 559 | + pathname = m[2] |
| 560 | + } |
520 | 561 | if (pathname === '/') return this.serveHome() |
521 | 562 | if (pathname === '/bootstrap') return this.serveBootstrap() |
522 | 563 | if (pathname === '/-/whoami') return this.serveWhoami() |
523 | 564 | if (pathname === '/-/ping') return this.respond(200, true) |
594 | 635 | distTag: parts[3], |
595 | 636 | } |
596 | 637 | } |
597 | 638 | |
| 639 | +Req.prototype.getMentions = function (name) { |
| 640 | + return this.headMsgId ? pull( |
| 641 | + this.server.streamTree([this.headMsgId], 'dependencyBranch'), |
| 642 | + |
| 643 | + pull.map(function (msg) { |
| 644 | + var c = msg.value && msg.value.content |
| 645 | + if (!c.mentions || !Array.isArray(c.mentions)) return [] |
| 646 | + return c.mentions.map(function (mention) { |
| 647 | + return { |
| 648 | + name: mention.name, |
| 649 | + size: mention.size, |
| 650 | + link: mention.link, |
| 651 | + author: msg.value.author, |
| 652 | + ts: msg.value.timestamp, |
| 653 | + } |
| 654 | + }) |
| 655 | + }), |
| 656 | + pull.flatten(), |
| 657 | + pull.filter(typeof name === 'string' ? function (link) { |
| 658 | + return link.name === name |
| 659 | + } : name && name.$prefix ? function (link) { |
| 660 | + return link.name.substr(0, name.$prefix.length) === name.$prefix |
| 661 | + } : function () { |
| 662 | + throw new TypeError('unsupported name filter') |
| 663 | + }), |
| 664 | + ) : this.server.getMentions(name) |
| 665 | +} |
| 666 | + |
| 667 | +Req.prototype.getMentionLinks = function (blobId) { |
| 668 | + return pull( |
| 669 | + this.headMsgId |
| 670 | + ? this.server.streamTree([this.headMsgId], 'dependencyBranch') |
| 671 | + : this.server.sbot.links({ |
| 672 | + dest: blobId, |
| 673 | + rel: 'mentions', |
| 674 | + values: true, |
| 675 | + }), |
| 676 | + |
| 677 | + pull.map(function (msg) { |
| 678 | + var c = msg.value && msg.value.content |
| 679 | + return c && Array.isArray(c.mentions) || [] |
| 680 | + }), |
| 681 | + pull.flatten(), |
| 682 | + pull.filter(function (link) { |
| 683 | + return link && link.link === blobId |
| 684 | + }) |
| 685 | + ) |
| 686 | +} |
| 687 | + |
598 | 688 | Req.prototype.servePkg = function (pathname) { |
599 | 689 | var self = this |
600 | 690 | var parts = pathname.split('/') |
601 | 691 | var pkgName = parts.shift().replace(/%2f/i, '/') |
611 | 701 | versions: {} |
612 | 702 | } |
613 | 703 | var distTags = {} |
614 | 704 | pull( |
615 | | - self.server.getMentions({$prefix: 'npm:' + pkgName + ':'}), |
| 705 | + self.getMentions({$prefix: 'npm:' + pkgName + ':'}), |
616 | 706 | pull.drain(function (mention) { |
617 | 707 | var data = decodeName(mention.name) |
618 | 708 | if (!data.version) return |
619 | 709 | if (data.distTag) { |
646 | 736 | var version = obj['dist-tags'][spec] |
647 | 737 | || semver.maxSatisfying(Object.keys(obj.versions), spec) |
648 | 738 | obj = obj.versions[version] |
649 | 739 | if (!obj) return self.respondError(404, 'version not found: ' + spec) |
650 | | - populatePackageJson(self.server.sbot, obj, function (err, pkg) { |
| 740 | + self.populatePackageJson(obj, function (err, pkg) { |
651 | 741 | if (err) return self.respondError(500, err.stack || err) |
652 | 742 | obj = pkg || obj |
653 | 743 | done() |
654 | 744 | }) |
656 | 746 | function resolveAll() { |
657 | 747 | var waiting = 0 |
658 | 748 | for (var version in obj.versions) (function (version) { |
659 | 749 | waiting++ |
660 | | - populatePackageJson(self.server.sbot, obj.versions[version], function (err, pkg) { |
| 750 | + self.populatePackageJson(obj.versions[version], function (err, pkg) { |
661 | 751 | if (err && waiting <= 0) return console.trace(err) |
662 | 752 | if (err) return waiting = 0, self.respondError(500, err.stack || err) |
663 | 753 | if (pkg) obj.versions[version] = pkg |
664 | 754 | if (!--waiting) done() |
672 | 762 | } |
673 | 763 | |
674 | 764 | Req.prototype.servePrebuild = function (name) { |
675 | 765 | var self = this |
676 | | - var getMention = self.server.getMentions('prebuild:' + name) |
| 766 | + var getMention = self.getMentions('prebuild:' + name) |
677 | 767 | var blobsByAuthor = {} |
678 | 768 | getMention(null, function next(err, link) { |
679 | 769 | if (err === true) return done() |
680 | 770 | if (err) return self.respondError(500, err.stack || err) |
822 | 912 | }) |
823 | 913 | }) |
824 | 914 | } |
825 | 915 | |
826 | | -function populatePackageJson(sbot, obj, cb) { |
| 916 | +Req.prototype.populatePackageJson = function (obj, cb) { |
827 | 917 | var blobId = obj.dist.tarball.replace(/.*\/blobs\/get\//, '') |
| 918 | + var self = this |
828 | 919 | var deps, bundledDeps |
829 | 920 | |
830 | 921 | |
831 | 922 | |
832 | 923 | |
833 | 924 | pull( |
834 | | - sbot.links({ |
835 | | - dest: blobId, |
836 | | - rel: 'mentions', |
837 | | - values: true, |
838 | | - }), |
839 | | - |
840 | | - pull.map(function (msg) { |
841 | | - var c = msg.value && msg.value.content |
842 | | - var mentions = c && c.mentions |
843 | | - return Array.isArray(mentions) ? mentions : [] |
844 | | - }), |
845 | | - pull.flatten(), |
846 | | - pull.filter(function (link) { |
847 | | - return link && link.link === blobId |
848 | | - }), |
| 925 | + self.getMentionLinks(blobId), |
849 | 926 | pull.drain(function (link) { |
850 | 927 | if (link.dependencies) deps = link.dependencies |
851 | 928 | bundledDeps = link.bundledDependencies || link.bundleDependencies || bundledDeps |
852 | 929 | |
859 | 936 | obj.bundledDependencies = bundledDeps |
860 | 937 | cb(null, obj) |
861 | 938 | } else { |
862 | 939 | |
863 | | - getPackageJsonFromTarballBlob(sbot, blobId, function (err, pkg) { |
| 940 | + getPackageJsonFromTarballBlob(self.server.sbot, blobId, function (err, pkg) { |
864 | 941 | if (err) return cb(err) |
865 | 942 | pkg.dist = obj.dist |
866 | 943 | pkg.dist.shasum = pkg._shasum |
867 | 944 | pkg.author = pkg.author || obj.author |