initial commit
This commit is contained in:
commit
0e5d0e7596
6
.dockerignore
Normal file
6
.dockerignore
Normal file
|
@ -0,0 +1,6 @@
|
|||
.dockerignore
|
||||
.gitignore
|
||||
Dockerfile
|
||||
node_modules
|
||||
*.log
|
||||
README.md
|
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
node_modules
|
||||
*.log
|
9
Dockerfile
Normal file
9
Dockerfile
Normal file
|
@ -0,0 +1,9 @@
|
|||
FROM node:12-alpine as base
|
||||
WORKDIR /app
|
||||
FROM base as packages
|
||||
ADD package.json yarn.lock /app/
|
||||
RUN yarn --production --frozen-lockfile
|
||||
FROM base as final
|
||||
COPY --from="packages" /app/node_modules /app/node_modules
|
||||
ADD . /app
|
||||
CMD node index.js
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2020 Kegan Myers
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
22
README.md
Normal file
22
README.md
Normal file
|
@ -0,0 +1,22 @@
|
|||
# fetch-dht
|
||||
|
||||
fetch-dht is a server that fetches peers from the [DHT](https://www.bittorrent.org/beps/bep_0005.html) swarm and serves them, likely to a bittorrent client performing an "announce".
|
||||
|
||||
# fetch-dht server list
|
||||
|
||||
- [dht.terrible.network](https://dht.terrible.network) - hosted by author
|
||||
|
||||
# License
|
||||
|
||||
[MIT](./LICENSE)
|
||||
|
||||
# Contributing
|
||||
|
||||
This gitea instance is currently not open for self registration. If you [email me](mailto:kegan@keganmyers.com) I will create you an account, after which you can use your account to make a pull request.
|
||||
|
||||
Please note that by contributing to this repository you are:
|
||||
|
||||
- Agreeing to publish your code under the MIT license
|
||||
- Confirming that you have the right (copyright, patent, etc.) to release your contribution under an MIT license
|
||||
|
||||
No PRs with modified licenses will be accepted or reviewed.
|
40
index.js
Normal file
40
index.js
Normal file
|
@ -0,0 +1,40 @@
|
|||
const express = require('express');
|
||||
const path = require('path');
|
||||
const serveStatic = require('serve-static');
|
||||
|
||||
require('dotenv').config();
|
||||
|
||||
const DHT = require('./lib/dht');
|
||||
const log = require('./lib/log');
|
||||
|
||||
const config = process.env;
|
||||
|
||||
const dht = new DHT(config.DHT_PORT);
|
||||
const app = express();
|
||||
|
||||
app.use(log.middleware);
|
||||
app.use(require('./routes')({ dht }));
|
||||
app.use(
|
||||
serveStatic(path.resolve(__dirname, 'static'), {
|
||||
acceptRanges: false,
|
||||
dotfiles: 'ignore',
|
||||
etag: true,
|
||||
extensions: false,
|
||||
fallthrough: true,
|
||||
immutable: false,
|
||||
index: 'index.html',
|
||||
lastModified: false,
|
||||
maxAge: '1d',
|
||||
redirect: false,
|
||||
}),
|
||||
);
|
||||
app.use(({ log }, res) => {
|
||||
log({ status: 'noRoute' });
|
||||
res.status(404).end();
|
||||
});
|
||||
app.use((e, { log }, res, next) => {
|
||||
log({ status: 'routerError', error: e.message, stack: e.stack });
|
||||
res.status(500).end();
|
||||
});
|
||||
|
||||
app.listen(config.PORT || 3000);
|
59
lib/binaryQuerystring/index.js
Normal file
59
lib/binaryQuerystring/index.js
Normal file
|
@ -0,0 +1,59 @@
|
|||
const qs = require('querystring');
|
||||
|
||||
const spliceString = (string, index, removals = 0, insert = '') => {
|
||||
return (
|
||||
string.slice(0, index) + insert + string.slice(index + Math.abs(removals))
|
||||
);
|
||||
};
|
||||
|
||||
const DBUC_REGEX = /^[0-9a-fA-F]{2}$/;
|
||||
const decodeComponent = (string) => {
|
||||
let percentIndex;
|
||||
while ((percentIndex = string.indexOf('%')) !== -1) {
|
||||
const encodedByte = string.substr(percentIndex + 1, 2);
|
||||
if (!DBUC_REGEX.test(encodedByte)) {
|
||||
throw new Error('Invalid input (not URI encoded?)');
|
||||
}
|
||||
string = spliceString(
|
||||
string,
|
||||
percentIndex,
|
||||
3,
|
||||
String.fromCharCode(parseInt(encodedByte, 16)),
|
||||
);
|
||||
}
|
||||
return string;
|
||||
};
|
||||
|
||||
const parse = (query, maxKeys = 12) => {
|
||||
return qs.parse(query, '&', '=', {
|
||||
decodeURIComponent: decodeComponent,
|
||||
maxKeys,
|
||||
});
|
||||
};
|
||||
|
||||
const fromUrl = (url, max) => {
|
||||
const splitUrl = url.split('?');
|
||||
if (splitUrl.length > 2) {
|
||||
throw new Error('Invalid URL');
|
||||
}
|
||||
if (splitUrl.length === 1) {
|
||||
return {};
|
||||
}
|
||||
return parse(splitUrl[1]);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
decodeURIComponent: decodeComponent,
|
||||
decodeUriComponent: decodeComponent,
|
||||
parse,
|
||||
fromUrl,
|
||||
__test: (cb) => {
|
||||
cb({
|
||||
spliceString,
|
||||
DBUC_REGEX,
|
||||
decodeComponent,
|
||||
parse,
|
||||
fromUrl,
|
||||
});
|
||||
},
|
||||
};
|
46
lib/binaryQuerystring/index.test.js
Normal file
46
lib/binaryQuerystring/index.test.js
Normal file
|
@ -0,0 +1,46 @@
|
|||
const assert = require('assert');
|
||||
|
||||
require('./index.js').__test(
|
||||
({ spliceString, DBUC_REGEX, decodeComponent, parse, fromUrl }) => {
|
||||
assert.strictEqual(
|
||||
spliceString('abcdefg', 2, 3, 'HELLO'),
|
||||
'abHELLOfg',
|
||||
'splice string not working as intended (0)',
|
||||
);
|
||||
assert.strictEqual(
|
||||
spliceString('abcd', 2, 0, 'HELLO'),
|
||||
'abHELLOcd',
|
||||
'splice string not working as intended (1)',
|
||||
);
|
||||
assert.strictEqual(
|
||||
spliceString('abcd', 2, -2, 'HELLO'),
|
||||
'abHELLO',
|
||||
'splice string not working as intended (2)',
|
||||
);
|
||||
|
||||
for (const [input, expected] of [
|
||||
[
|
||||
'%28m.%5BO%83i%85S%283j%C1%26%3A%E0%2Az%60%D5',
|
||||
'(m.[O\u0083i\u0085S(3j\u00C1&\u003A\u00E0\u002Az`\u00D5',
|
||||
],
|
||||
[
|
||||
'(m.%5bO%83i%85S(3j%c1%26%3a%e0*z%60%d5',
|
||||
'(m.[O\u0083i\u0085S(3j\u00C1&\u003A\u00E0\u002Az`\u00D5',
|
||||
],
|
||||
]) {
|
||||
const actual = decodeComponent(input);
|
||||
assert.strictEqual(
|
||||
actual,
|
||||
expected,
|
||||
`decodeComponent failed on input '${input}'`,
|
||||
);
|
||||
}
|
||||
|
||||
// expect other tests to validate deeper functionality
|
||||
assert.deepEqual(
|
||||
fromUrl('http://example.com/foo?bar=hi'),
|
||||
{ bar: 'hi' },
|
||||
`parseBinaryQuerystring failed`,
|
||||
);
|
||||
},
|
||||
);
|
36
lib/dht/index.js
Normal file
36
lib/dht/index.js
Normal file
|
@ -0,0 +1,36 @@
|
|||
const DHT = require('bittorrent-dht');
|
||||
|
||||
class DhtWrapper {
|
||||
constructor(DHT_PORT = 20000) {
|
||||
this._peerMap = new Map();
|
||||
this._port = DHT_PORT;
|
||||
|
||||
this._dht = new DHT();
|
||||
this._dht.listen(this._port);
|
||||
this._dht.on('peer', ({ host, port }, infoHash) => {
|
||||
const hexHash = Buffer.from(infoHash).toString('hex');
|
||||
if (!this._peerMap.has(hexHash)) {
|
||||
return;
|
||||
}
|
||||
this._peerMap.get(hexHash).add(`${host}:${port}`);
|
||||
});
|
||||
}
|
||||
async lookup(infoHash) {
|
||||
const peers = new Set();
|
||||
this._peerMap.set(infoHash, peers);
|
||||
return await new Promise((resolve, reject) => {
|
||||
this._dht.lookup(infoHash, (e) => {
|
||||
if (!!e) {
|
||||
reject(e);
|
||||
return;
|
||||
}
|
||||
if (this._peerMap.get(infoHash) === peers) {
|
||||
this._peerMap.delete(infoHash);
|
||||
}
|
||||
resolve(Array.from(peers));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DhtWrapper;
|
20
lib/fakePeerId/index.js
Normal file
20
lib/fakePeerId/index.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
const crypto = require('crypto');
|
||||
|
||||
const memo = new Map();
|
||||
|
||||
const fakePeerId = (addr) => {
|
||||
const hash = crypto.createHash('sha1');
|
||||
hash.update(addr);
|
||||
return hash.digest('hex').slice(0, 20);
|
||||
};
|
||||
|
||||
// not super expensive, but probably worth memoizing
|
||||
module.exports = (addr) => {
|
||||
if (memo.size >= 300000) {
|
||||
memo.clear();
|
||||
}
|
||||
if (!memo.has(addr)) {
|
||||
memo.set(addr, fakePeerId(addr));
|
||||
}
|
||||
return memo.get(addr);
|
||||
};
|
23
lib/log/index.js
Normal file
23
lib/log/index.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
const uuid = require('uuid');
|
||||
|
||||
module.exports = {};
|
||||
|
||||
const log = ({ time, ...args }) => {
|
||||
console.log(
|
||||
JSON.stringify({ time: new Date().toISOString(), requestId, ...args }),
|
||||
);
|
||||
};
|
||||
|
||||
const traced = (requestId) => ({ requestId: _, ...args }) =>
|
||||
log({ requestId, ...args });
|
||||
|
||||
const middleware = (req, res, next) => {
|
||||
// generate (or use existing) requestId
|
||||
req.requestId = req.requestId || uuid.v4();
|
||||
// set a tracing logger use elsewhere
|
||||
req.log = traced(req.requestId);
|
||||
req.log({ status: 'new' });
|
||||
next();
|
||||
};
|
||||
|
||||
module.exports = { log, traced, middleware };
|
23
lib/peerListFormatters/bencoded.js
Normal file
23
lib/peerListFormatters/bencoded.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
const addrToIPPort = require('addr-to-ip-port');
|
||||
const bencode = require('bencode');
|
||||
|
||||
const fakePeerId = require('../fakePeerId');
|
||||
|
||||
module.exports = (peers, res) => {
|
||||
const response = bencode.encode({
|
||||
interval: 30 * 60, // 30 minutes between requests
|
||||
peers: peers.map((peer) => {
|
||||
const [ip, port] = addrToIPPort(peer);
|
||||
return {
|
||||
ip,
|
||||
'peer id': fakePeerId(peer),
|
||||
port,
|
||||
};
|
||||
}),
|
||||
});
|
||||
res
|
||||
.set({
|
||||
'Content-Length': response.length,
|
||||
})
|
||||
.end(response);
|
||||
};
|
14
lib/peerListFormatters/compact.js
Normal file
14
lib/peerListFormatters/compact.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
const bencode = require('bencode');
|
||||
const string2compact = require('string2compact');
|
||||
|
||||
module.exports = (peers, res) => {
|
||||
const response = bencode.encode({
|
||||
interval: 30 * 60, // 30 minutes between requests
|
||||
peers: string2compact(peers),
|
||||
});
|
||||
res
|
||||
.set({
|
||||
'Content-Length': response.length,
|
||||
})
|
||||
.end(response);
|
||||
};
|
14
lib/peerListFormatters/index.js
Normal file
14
lib/peerListFormatters/index.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
const formatters = new Map();
|
||||
const DEFAULT = 'bencoded';
|
||||
|
||||
formatters.set('bencoded', require('./bencoded'));
|
||||
formatters.set('json', require('./json'));
|
||||
formatters.set('compact', require('./compact'));
|
||||
|
||||
module.exports = (peers, res, format = DEFAULT) => {
|
||||
if (!formatters.has(format)) {
|
||||
format = DEFAULT;
|
||||
}
|
||||
formatters.get(format)(peers, res);
|
||||
return format;
|
||||
};
|
3
lib/peerListFormatters/json.js
Normal file
3
lib/peerListFormatters/json.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
module.exports = (peers, res) => {
|
||||
const response = res.json(peers);
|
||||
};
|
23
package.json
Normal file
23
package.json
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"name": "@terribleplan/dht-peers",
|
||||
"version": "0.0.1",
|
||||
"description": "A gateway for querying the DHT",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "node ."
|
||||
},
|
||||
"author": "Kegan Myers <kegan@keganmyers.com>",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"addr-to-ip-port": "^1.5.1",
|
||||
"bencode": "^2.0.1",
|
||||
"bittorrent-dht": "^9.0.3",
|
||||
"dotenv": "^8.2.0",
|
||||
"express": "^4.17.1",
|
||||
"express-promise-router": "^3.0.3",
|
||||
"serve-static": "^1.14.1",
|
||||
"string2compact": "^1.3.0",
|
||||
"uuid": "^7.0.3"
|
||||
}
|
||||
}
|
87
routes/fetch.js
Normal file
87
routes/fetch.js
Normal file
|
@ -0,0 +1,87 @@
|
|||
const Router = require('express-promise-router');
|
||||
const bencode = require('bencode');
|
||||
const qs = require('querystring');
|
||||
|
||||
const peerListFormatters = require('../lib/peerListFormatters');
|
||||
const bqs = require('../lib/binaryQuerystring');
|
||||
|
||||
const INFO_HASH_REGEX = new RegExp('^[0-9a-f]{40}$', 'i');
|
||||
|
||||
module.exports = ({ dht }) => {
|
||||
const app = Router();
|
||||
app.get(
|
||||
'/',
|
||||
async (
|
||||
{
|
||||
log,
|
||||
originalUrl,
|
||||
query: { info_hash: infoHash, compact, json, event = '' },
|
||||
},
|
||||
res,
|
||||
) => {
|
||||
// worst case if every character is percent encoded, or best case if every _is_ decoded
|
||||
if (!infoHash || infoHash.length > 60 || infoHash < 20) {
|
||||
res
|
||||
.status(400)
|
||||
.end(bencode.encode({ 'failure reason': 'invalid arguments' }));
|
||||
return;
|
||||
}
|
||||
|
||||
if (infoHash.length !== 20) {
|
||||
infoHash = bqs.fromUrl(originalUrl).info_hash;
|
||||
|
||||
if (!infoHash || infoHash.length !== 20) {
|
||||
res
|
||||
.status(400)
|
||||
.end(bencode.encode({ 'failure reason': 'invalid arguments' }));
|
||||
log({ status: 'refused', type: 'dhtGateway' });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
infoHash = Buffer.from(infoHash, 'latin1').toString('hex');
|
||||
if (!infoHash.match(INFO_HASH_REGEX)) {
|
||||
res
|
||||
.status(400)
|
||||
.end(bencode.encode({ 'failure reason': 'invalid arguments' }));
|
||||
log({ status: 'refused', type: 'dhtGateway' });
|
||||
return;
|
||||
}
|
||||
|
||||
let format = 'bencoded';
|
||||
if (compact === '1') {
|
||||
format = 'compact';
|
||||
} else if (json === '1') {
|
||||
format = 'json';
|
||||
}
|
||||
|
||||
try {
|
||||
let peers = [];
|
||||
// a completed or stopped torrent does not need peers
|
||||
if (event !== 'completed' && event !== 'stopped') {
|
||||
peers = await dht.lookup(infoHash);
|
||||
}
|
||||
// this sends the response as well as formatting it
|
||||
peerListFormatters(peers, res, format);
|
||||
log({
|
||||
status: 'served',
|
||||
type: 'dhtGateway',
|
||||
format,
|
||||
count: peers.length,
|
||||
});
|
||||
} catch (e) {
|
||||
res
|
||||
.status(500)
|
||||
.end(bencode.encode({ 'failure reason': 'internal error' }));
|
||||
log({
|
||||
status: 'failed',
|
||||
type: 'dhtGateway',
|
||||
format,
|
||||
error: e.message,
|
||||
stack: e.stack,
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
return app;
|
||||
};
|
9
routes/index.js
Normal file
9
routes/index.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
const Router = require('express-promise-router');
|
||||
|
||||
module.exports = (params) => {
|
||||
const app = Router();
|
||||
|
||||
app.use('/fetch', require('./fetch')(params));
|
||||
|
||||
return app;
|
||||
};
|
BIN
static/favicon.ico
Normal file
BIN
static/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 318 B |
400
static/index.css
Normal file
400
static/index.css
Normal file
|
@ -0,0 +1,400 @@
|
|||
/* github-markdown-css | Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com) | MIT | https://raw.githubusercontent.com/sindresorhus/github-markdown-css/1485dd78f5e744ef36e946e5ae44838e3906f9d8/license */
|
||||
body {
|
||||
-ms-text-size-adjust: 100%;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
line-height: 1.5;
|
||||
color: #24292e;
|
||||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial,
|
||||
sans-serif, Apple Color Emoji, Segoe UI Emoji;
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
body details {
|
||||
display: block;
|
||||
}
|
||||
body summary {
|
||||
display: list-item;
|
||||
}
|
||||
body a {
|
||||
background-color: initial;
|
||||
}
|
||||
body a:active,
|
||||
body a:hover {
|
||||
outline-width: 0;
|
||||
}
|
||||
body strong {
|
||||
font-weight: inherit;
|
||||
font-weight: bolder;
|
||||
}
|
||||
body h1 {
|
||||
font-size: 2em;
|
||||
margin: 0.67em 0;
|
||||
}
|
||||
body img {
|
||||
border-style: none;
|
||||
}
|
||||
body code,
|
||||
body kbd,
|
||||
body pre {
|
||||
font-family: monospace, monospace;
|
||||
font-size: 1em;
|
||||
}
|
||||
body hr {
|
||||
box-sizing: initial;
|
||||
height: 0;
|
||||
overflow: visible;
|
||||
}
|
||||
body input {
|
||||
font: inherit;
|
||||
margin: 0;
|
||||
}
|
||||
body input {
|
||||
overflow: visible;
|
||||
}
|
||||
body [type='checkbox'] {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
}
|
||||
body * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body input {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
body a {
|
||||
color: #0366d6;
|
||||
text-decoration: none;
|
||||
}
|
||||
body a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
body strong {
|
||||
font-weight: 600;
|
||||
}
|
||||
body hr {
|
||||
height: 0;
|
||||
margin: 15px 0;
|
||||
overflow: hidden;
|
||||
background: 0 0;
|
||||
border: 0;
|
||||
border-bottom: 1px solid #dfe2e5;
|
||||
}
|
||||
body hr:after,
|
||||
body hr:before {
|
||||
display: table;
|
||||
content: '';
|
||||
}
|
||||
body hr:after {
|
||||
clear: both;
|
||||
}
|
||||
body table {
|
||||
border-spacing: 0;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
body td,
|
||||
body th {
|
||||
padding: 0;
|
||||
}
|
||||
body details summary {
|
||||
cursor: pointer;
|
||||
}
|
||||
body kbd {
|
||||
display: inline-block;
|
||||
padding: 3px 5px;
|
||||
font: 11px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
line-height: 10px;
|
||||
color: #444d56;
|
||||
vertical-align: middle;
|
||||
background-color: #fafbfc;
|
||||
border: 1px solid #d1d5da;
|
||||
border-radius: 3px;
|
||||
box-shadow: inset 0 -1px 0 #d1d5da;
|
||||
}
|
||||
body h1,
|
||||
body h2,
|
||||
body h3,
|
||||
body h4,
|
||||
body h5,
|
||||
body h6 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
body h1 {
|
||||
font-size: 32px;
|
||||
}
|
||||
body h1,
|
||||
body h2 {
|
||||
font-weight: 600;
|
||||
}
|
||||
body h2 {
|
||||
font-size: 24px;
|
||||
}
|
||||
body h3 {
|
||||
font-size: 20px;
|
||||
}
|
||||
body h3,
|
||||
body h4 {
|
||||
font-weight: 600;
|
||||
}
|
||||
body h4 {
|
||||
font-size: 16px;
|
||||
}
|
||||
body h5 {
|
||||
font-size: 14px;
|
||||
}
|
||||
body h5,
|
||||
body h6 {
|
||||
font-weight: 600;
|
||||
}
|
||||
body h6 {
|
||||
font-size: 12px;
|
||||
}
|
||||
body p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
body blockquote {
|
||||
margin: 0;
|
||||
}
|
||||
body ol,
|
||||
body ul {
|
||||
padding-left: 0;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
body ol ol,
|
||||
body ul ol {
|
||||
list-style-type: lower-roman;
|
||||
}
|
||||
body ol ol ol,
|
||||
body ol ul ol,
|
||||
body ul ol ol,
|
||||
body ul ul ol {
|
||||
list-style-type: lower-alpha;
|
||||
}
|
||||
body dd {
|
||||
margin-left: 0;
|
||||
}
|
||||
body code,
|
||||
body pre {
|
||||
font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
body pre {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
body input::-webkit-inner-spin-button,
|
||||
body input::-webkit-outer-spin-button {
|
||||
margin: 0;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
}
|
||||
body :checked + .radio-label {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
border-color: #0366d6;
|
||||
}
|
||||
body hr {
|
||||
border-bottom-color: #eee;
|
||||
}
|
||||
body kbd {
|
||||
display: inline-block;
|
||||
padding: 3px 5px;
|
||||
font: 11px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
line-height: 10px;
|
||||
color: #444d56;
|
||||
vertical-align: middle;
|
||||
background-color: #fafbfc;
|
||||
border: 1px solid #d1d5da;
|
||||
border-radius: 3px;
|
||||
box-shadow: inset 0 -1px 0 #d1d5da;
|
||||
}
|
||||
body:after,
|
||||
body:before {
|
||||
display: table;
|
||||
content: '';
|
||||
}
|
||||
body:after {
|
||||
clear: both;
|
||||
}
|
||||
body > :first-child {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
body > :last-child {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
body a:not([href]) {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
body blockquote,
|
||||
body details,
|
||||
body dl,
|
||||
body ol,
|
||||
body p,
|
||||
body pre,
|
||||
body table,
|
||||
body ul {
|
||||
margin-top: 0;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
body hr {
|
||||
height: 0.25em;
|
||||
padding: 0;
|
||||
margin: 24px 0;
|
||||
background-color: #e1e4e8;
|
||||
border: 0;
|
||||
}
|
||||
body blockquote {
|
||||
padding: 0 1em;
|
||||
color: #6a737d;
|
||||
border-left: 0.25em solid #dfe2e5;
|
||||
}
|
||||
body blockquote > :first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
body blockquote > :last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
body h1,
|
||||
body h2,
|
||||
body h3,
|
||||
body h4,
|
||||
body h5,
|
||||
body h6 {
|
||||
margin-top: 24px;
|
||||
margin-bottom: 16px;
|
||||
font-weight: 600;
|
||||
line-height: 1.25;
|
||||
}
|
||||
body h1 {
|
||||
font-size: 2em;
|
||||
}
|
||||
body h1,
|
||||
body h2 {
|
||||
padding-bottom: 0.3em;
|
||||
border-bottom: 1px solid #eaecef;
|
||||
}
|
||||
body h2 {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
body h3 {
|
||||
font-size: 1.25em;
|
||||
}
|
||||
body h4 {
|
||||
font-size: 1em;
|
||||
}
|
||||
body h5 {
|
||||
font-size: 0.875em;
|
||||
}
|
||||
body h6 {
|
||||
font-size: 0.85em;
|
||||
color: #6a737d;
|
||||
}
|
||||
body ol,
|
||||
body ul {
|
||||
padding-left: 2em;
|
||||
}
|
||||
body ol ol,
|
||||
body ol ul,
|
||||
body ul ol,
|
||||
body ul ul {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
body li {
|
||||
word-wrap: break-all;
|
||||
}
|
||||
body li > p {
|
||||
margin-top: 16px;
|
||||
}
|
||||
body li + li {
|
||||
margin-top: 0.25em;
|
||||
}
|
||||
body dl {
|
||||
padding: 0;
|
||||
}
|
||||
body dl dt {
|
||||
padding: 0;
|
||||
margin-top: 16px;
|
||||
font-size: 1em;
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
}
|
||||
body dl dd {
|
||||
padding: 0 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
body table {
|
||||
display: block;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
body table th {
|
||||
font-weight: 600;
|
||||
}
|
||||
body table td,
|
||||
body table th {
|
||||
padding: 6px 13px;
|
||||
border: 1px solid #dfe2e5;
|
||||
}
|
||||
body table tr {
|
||||
background-color: #fff;
|
||||
border-top: 1px solid #c6cbd1;
|
||||
}
|
||||
body table tr:nth-child(2n) {
|
||||
background-color: #f6f8fa;
|
||||
}
|
||||
body img {
|
||||
max-width: 100%;
|
||||
box-sizing: initial;
|
||||
background-color: #fff;
|
||||
}
|
||||
body img[align='right'] {
|
||||
padding-left: 20px;
|
||||
}
|
||||
body img[align='left'] {
|
||||
padding-right: 20px;
|
||||
}
|
||||
body code {
|
||||
padding: 0.2em 0.4em;
|
||||
margin: 0;
|
||||
font-size: 85%;
|
||||
background-color: rgba(27, 31, 35, 0.05);
|
||||
border-radius: 3px;
|
||||
}
|
||||
body pre {
|
||||
word-wrap: normal;
|
||||
}
|
||||
body pre > code {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-size: 100%;
|
||||
word-break: normal;
|
||||
white-space: pre;
|
||||
background: 0 0;
|
||||
border: 0;
|
||||
}
|
||||
body pre {
|
||||
padding: 16px;
|
||||
overflow: auto;
|
||||
font-size: 85%;
|
||||
line-height: 1.45;
|
||||
background-color: #f6f8fa;
|
||||
border-radius: 3px;
|
||||
}
|
||||
body pre code {
|
||||
display: inline;
|
||||
max-width: auto;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
overflow: visible;
|
||||
line-height: inherit;
|
||||
word-wrap: normal;
|
||||
background-color: initial;
|
||||
border: 0;
|
||||
}
|
93
static/index.html
Normal file
93
static/index.html
Normal file
|
@ -0,0 +1,93 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Fetch Magnet</title>
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
|
||||
<link rel="stylesheet" type="text/css" href="/index.css" />
|
||||
<style type="text/css">
|
||||
body {
|
||||
max-width: 76rem;
|
||||
margin: 0 auto;
|
||||
padding: 1rem;
|
||||
}
|
||||
body input {
|
||||
width: 100%;
|
||||
margin: 0.5rem 0;
|
||||
padding-left: 0.3rem;
|
||||
padding-right: 0.3rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>About</h1>
|
||||
<p>
|
||||
<a href="https://www.bittorrent.org/beps/bep_0005.html">DHT</a> is a
|
||||
powerful technology that enables "trackerless" torrents; however, there
|
||||
are a number of reasons why a client may be unable to join the DHT swarm
|
||||
such as local or ISP blocking. This server acts as a gateway to discover
|
||||
clients that have announced to the DHT network, and provides a
|
||||
tracker-compatible interface for ease of use.
|
||||
</p>
|
||||
<h1>Usage</h1>
|
||||
<p>
|
||||
You will most likely want to add
|
||||
<code>https://dht.terrible.network/fetch</code> to the list of trackers in
|
||||
your torrent client.
|
||||
</p>
|
||||
<p>
|
||||
In addition to supporting "compact" representation
|
||||
(<code>?compact=1</code>) this service also supports "json" representation
|
||||
(<code>?json=1</code>) for ease of use in code.
|
||||
</p>
|
||||
<h1>FAQ</h1>
|
||||
<h2>So is this just a BitTorrent tracker?</h2>
|
||||
<p>
|
||||
No. This server does not collect/store the information necessary to act as
|
||||
a tracker (IP address, "port" parameter), it does not serve anything other
|
||||
than information queried from the DHT.
|
||||
</p>
|
||||
<h2>Will this announce me to the DHT?</h2>
|
||||
<p>
|
||||
No. This server does not collect the information necessary to do so (IP
|
||||
address), and it is technologically impossible to announce an IP other
|
||||
than one owned by the machine speaking to the DHT swarm.
|
||||
</p>
|
||||
<h1>Support</h1>
|
||||
<p>
|
||||
This service is provided for free by the autor of this software,
|
||||
<a href="https://terribleplan.com/">Kegan Myers</a>, you can support him
|
||||
via <a href="https://www.patreon.com/terribleplan">Patreon</a>.
|
||||
</p>
|
||||
<h1>Open Source</h1>
|
||||
<p>
|
||||
The source that runs this service is available on
|
||||
<a href="https://git.keganmyers.com/terribleplan/fetch-dht">
|
||||
my gitea server
|
||||
</a>
|
||||
and is open source under an MIT license.
|
||||
</p>
|
||||
<h1>Legal</h1>
|
||||
<ul>
|
||||
<li>
|
||||
This service is provided as-is on a best-effort basis. No warranty is or
|
||||
shall be issued for the use of or reliance upon this service.
|
||||
</li>
|
||||
<li>
|
||||
This service respects your privacy, no identifying information is
|
||||
collected, shared, or stored.
|
||||
</li>
|
||||
<li>
|
||||
Access to this service may be revoked at any time for any reason without
|
||||
warning.
|
||||
</li>
|
||||
<li>
|
||||
Piracy is illegal. The end user of this service bears all responsibility
|
||||
for what is done with the data returned by this service.
|
||||
</li>
|
||||
<li>
|
||||
This service does not host any content, it is a gateway that finds peers
|
||||
in the distributed hash table and serves a list of them.
|
||||
</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
467
yarn.lock
Normal file
467
yarn.lock
Normal file
|
@ -0,0 +1,467 @@
|
|||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
accepts@~1.3.7:
|
||||
version "1.3.7"
|
||||
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd"
|
||||
dependencies:
|
||||
mime-types "~2.1.24"
|
||||
negotiator "0.6.2"
|
||||
|
||||
addr-to-ip-port@^1.0.1, addr-to-ip-port@^1.5.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/addr-to-ip-port/-/addr-to-ip-port-1.5.1.tgz#bfada13fd6aeeeac19f1e9f7d84b4bbab45e5208"
|
||||
|
||||
array-flatten@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
|
||||
|
||||
bencode@^2.0.0, bencode@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/bencode/-/bencode-2.0.1.tgz#667a6a31c5e038d558608333da6b7c94e836c85b"
|
||||
dependencies:
|
||||
safe-buffer "^5.1.1"
|
||||
|
||||
bittorrent-dht@^9.0.3:
|
||||
version "9.0.3"
|
||||
resolved "https://registry.yarnpkg.com/bittorrent-dht/-/bittorrent-dht-9.0.3.tgz#bdcac9383bdc5e2a459eef6418332f3ae182d63f"
|
||||
dependencies:
|
||||
bencode "^2.0.0"
|
||||
debug "^4.1.1"
|
||||
inherits "^2.0.1"
|
||||
k-bucket "^5.0.0"
|
||||
k-rpc "^5.0.0"
|
||||
last-one-wins "^1.0.4"
|
||||
lru "^3.1.0"
|
||||
randombytes "^2.0.5"
|
||||
record-cache "^1.0.2"
|
||||
simple-sha1 "^3.0.0"
|
||||
|
||||
body-parser@1.19.0:
|
||||
version "1.19.0"
|
||||
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a"
|
||||
dependencies:
|
||||
bytes "3.1.0"
|
||||
content-type "~1.0.4"
|
||||
debug "2.6.9"
|
||||
depd "~1.1.2"
|
||||
http-errors "1.7.2"
|
||||
iconv-lite "0.4.24"
|
||||
on-finished "~2.3.0"
|
||||
qs "6.7.0"
|
||||
raw-body "2.4.0"
|
||||
type-is "~1.6.17"
|
||||
|
||||
bytes@3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
|
||||
|
||||
chrome-dgram@^3.0.2:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/chrome-dgram/-/chrome-dgram-3.0.4.tgz#aa785f23d1fc71c8619e8af166db7b9dc21a4f3e"
|
||||
dependencies:
|
||||
inherits "^2.0.1"
|
||||
run-series "^1.1.2"
|
||||
|
||||
chrome-dns@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/chrome-dns/-/chrome-dns-1.0.1.tgz#6870af680a40d2c4b2efc2154a378793f5a4ce4b"
|
||||
dependencies:
|
||||
chrome-net "^3.3.2"
|
||||
|
||||
chrome-net@^3.3.2:
|
||||
version "3.3.3"
|
||||
resolved "https://registry.yarnpkg.com/chrome-net/-/chrome-net-3.3.3.tgz#09b40337d97fa857ac44ee9a2d82a66e43863401"
|
||||
dependencies:
|
||||
inherits "^2.0.1"
|
||||
|
||||
content-disposition@0.5.3:
|
||||
version "0.5.3"
|
||||
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd"
|
||||
dependencies:
|
||||
safe-buffer "5.1.2"
|
||||
|
||||
content-type@~1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
|
||||
|
||||
cookie-signature@1.0.6:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
|
||||
|
||||
cookie@0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba"
|
||||
|
||||
debug@2.6.9:
|
||||
version "2.6.9"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
debug@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
|
||||
dependencies:
|
||||
ms "^2.1.1"
|
||||
|
||||
depd@~1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
|
||||
|
||||
destroy@~1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
|
||||
|
||||
dotenv@^8.2.0:
|
||||
version "8.2.0"
|
||||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a"
|
||||
|
||||
ee-first@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
|
||||
|
||||
encodeurl@~1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
|
||||
|
||||
escape-html@~1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
|
||||
|
||||
etag@~1.8.1:
|
||||
version "1.8.1"
|
||||
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
|
||||
|
||||
express-promise-router@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/express-promise-router/-/express-promise-router-3.0.3.tgz#5e6d22a5a3f013d71833172fe8d7ab780c3f6b70"
|
||||
dependencies:
|
||||
is-promise "^2.1.0"
|
||||
lodash.flattendeep "^4.0.0"
|
||||
methods "^1.0.0"
|
||||
|
||||
express@^4.17.1:
|
||||
version "4.17.1"
|
||||
resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134"
|
||||
dependencies:
|
||||
accepts "~1.3.7"
|
||||
array-flatten "1.1.1"
|
||||
body-parser "1.19.0"
|
||||
content-disposition "0.5.3"
|
||||
content-type "~1.0.4"
|
||||
cookie "0.4.0"
|
||||
cookie-signature "1.0.6"
|
||||
debug "2.6.9"
|
||||
depd "~1.1.2"
|
||||
encodeurl "~1.0.2"
|
||||
escape-html "~1.0.3"
|
||||
etag "~1.8.1"
|
||||
finalhandler "~1.1.2"
|
||||
fresh "0.5.2"
|
||||
merge-descriptors "1.0.1"
|
||||
methods "~1.1.2"
|
||||
on-finished "~2.3.0"
|
||||
parseurl "~1.3.3"
|
||||
path-to-regexp "0.1.7"
|
||||
proxy-addr "~2.0.5"
|
||||
qs "6.7.0"
|
||||
range-parser "~1.2.1"
|
||||
safe-buffer "5.1.2"
|
||||
send "0.17.1"
|
||||
serve-static "1.14.1"
|
||||
setprototypeof "1.1.1"
|
||||
statuses "~1.5.0"
|
||||
type-is "~1.6.18"
|
||||
utils-merge "1.0.1"
|
||||
vary "~1.1.2"
|
||||
|
||||
finalhandler@~1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d"
|
||||
dependencies:
|
||||
debug "2.6.9"
|
||||
encodeurl "~1.0.2"
|
||||
escape-html "~1.0.3"
|
||||
on-finished "~2.3.0"
|
||||
parseurl "~1.3.3"
|
||||
statuses "~1.5.0"
|
||||
unpipe "~1.0.0"
|
||||
|
||||
forwarded@~0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
|
||||
|
||||
fresh@0.5.2:
|
||||
version "0.5.2"
|
||||
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
|
||||
|
||||
http-errors@1.7.2:
|
||||
version "1.7.2"
|
||||
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f"
|
||||
dependencies:
|
||||
depd "~1.1.2"
|
||||
inherits "2.0.3"
|
||||
setprototypeof "1.1.1"
|
||||
statuses ">= 1.5.0 < 2"
|
||||
toidentifier "1.0.0"
|
||||
|
||||
http-errors@~1.7.2:
|
||||
version "1.7.3"
|
||||
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06"
|
||||
dependencies:
|
||||
depd "~1.1.2"
|
||||
inherits "2.0.4"
|
||||
setprototypeof "1.1.1"
|
||||
statuses ">= 1.5.0 < 2"
|
||||
toidentifier "1.0.0"
|
||||
|
||||
iconv-lite@0.4.24:
|
||||
version "0.4.24"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
|
||||
dependencies:
|
||||
safer-buffer ">= 2.1.2 < 3"
|
||||
|
||||
inherits@2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
|
||||
|
||||
inherits@2.0.4, inherits@^2.0.1:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||
|
||||
ipaddr.js@1.9.1, ipaddr.js@^1.0.1:
|
||||
version "1.9.1"
|
||||
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
|
||||
|
||||
is-promise@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa"
|
||||
|
||||
k-bucket@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/k-bucket/-/k-bucket-5.0.0.tgz#ef7a401fcd4c37cd31dceaa6ae4440ca91055e01"
|
||||
dependencies:
|
||||
randombytes "^2.0.3"
|
||||
|
||||
k-rpc-socket@^1.7.2:
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/k-rpc-socket/-/k-rpc-socket-1.11.1.tgz#f14b4b240a716c6cad7b6434b21716dbd7c7b0e8"
|
||||
dependencies:
|
||||
bencode "^2.0.0"
|
||||
chrome-dgram "^3.0.2"
|
||||
chrome-dns "^1.0.0"
|
||||
chrome-net "^3.3.2"
|
||||
|
||||
k-rpc@^5.0.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/k-rpc/-/k-rpc-5.1.0.tgz#af2052de2e84994d55da3032175da5dad8640174"
|
||||
dependencies:
|
||||
k-bucket "^5.0.0"
|
||||
k-rpc-socket "^1.7.2"
|
||||
randombytes "^2.0.5"
|
||||
|
||||
last-one-wins@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/last-one-wins/-/last-one-wins-1.0.4.tgz#c1bfd0cbcb46790ec9156b8d1aee8fcb86cda22a"
|
||||
|
||||
lodash.flattendeep@^4.0.0:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2"
|
||||
|
||||
lru@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/lru/-/lru-3.1.0.tgz#ea7fb8546d83733396a13091d76cfeb4c06837d5"
|
||||
dependencies:
|
||||
inherits "^2.0.1"
|
||||
|
||||
media-typer@0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
|
||||
|
||||
merge-descriptors@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
|
||||
|
||||
methods@^1.0.0, methods@~1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
|
||||
|
||||
mime-db@1.43.0:
|
||||
version "1.43.0"
|
||||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58"
|
||||
|
||||
mime-types@~2.1.24:
|
||||
version "2.1.26"
|
||||
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.26.tgz#9c921fc09b7e149a65dfdc0da4d20997200b0a06"
|
||||
dependencies:
|
||||
mime-db "1.43.0"
|
||||
|
||||
mime@1.6.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
|
||||
|
||||
ms@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||
|
||||
ms@2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
|
||||
|
||||
ms@^2.1.1:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||
|
||||
negotiator@0.6.2:
|
||||
version "0.6.2"
|
||||
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
|
||||
|
||||
on-finished@~2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
|
||||
dependencies:
|
||||
ee-first "1.1.1"
|
||||
|
||||
parseurl@~1.3.3:
|
||||
version "1.3.3"
|
||||
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
|
||||
|
||||
path-to-regexp@0.1.7:
|
||||
version "0.1.7"
|
||||
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
|
||||
|
||||
proxy-addr@~2.0.5:
|
||||
version "2.0.6"
|
||||
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf"
|
||||
dependencies:
|
||||
forwarded "~0.1.2"
|
||||
ipaddr.js "1.9.1"
|
||||
|
||||
qs@6.7.0:
|
||||
version "6.7.0"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
|
||||
|
||||
queue-microtask@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.1.2.tgz#139bf8186db0c545017ec66c2664ac646d5c571e"
|
||||
|
||||
randombytes@^2.0.3, randombytes@^2.0.5:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
|
||||
dependencies:
|
||||
safe-buffer "^5.1.0"
|
||||
|
||||
range-parser@~1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
|
||||
|
||||
raw-body@2.4.0:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332"
|
||||
dependencies:
|
||||
bytes "3.1.0"
|
||||
http-errors "1.7.2"
|
||||
iconv-lite "0.4.24"
|
||||
unpipe "1.0.0"
|
||||
|
||||
record-cache@^1.0.2:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/record-cache/-/record-cache-1.1.0.tgz#f8a467a691a469584b26e88d36b18afdb3932037"
|
||||
|
||||
run-series@^1.1.2:
|
||||
version "1.1.8"
|
||||
resolved "https://registry.yarnpkg.com/run-series/-/run-series-1.1.8.tgz#2c4558f49221e01cd6371ff4e0a1e203e460fc36"
|
||||
|
||||
rusha@^0.8.1:
|
||||
version "0.8.13"
|
||||
resolved "https://registry.yarnpkg.com/rusha/-/rusha-0.8.13.tgz#9a084e7b860b17bff3015b92c67a6a336191513a"
|
||||
|
||||
safe-buffer@5.1.2:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||
|
||||
safe-buffer@^5.1.0, safe-buffer@^5.1.1:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519"
|
||||
|
||||
"safer-buffer@>= 2.1.2 < 3":
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||
|
||||
send@0.17.1:
|
||||
version "0.17.1"
|
||||
resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8"
|
||||
dependencies:
|
||||
debug "2.6.9"
|
||||
depd "~1.1.2"
|
||||
destroy "~1.0.4"
|
||||
encodeurl "~1.0.2"
|
||||
escape-html "~1.0.3"
|
||||
etag "~1.8.1"
|
||||
fresh "0.5.2"
|
||||
http-errors "~1.7.2"
|
||||
mime "1.6.0"
|
||||
ms "2.1.1"
|
||||
on-finished "~2.3.0"
|
||||
range-parser "~1.2.1"
|
||||
statuses "~1.5.0"
|
||||
|
||||
serve-static@1.14.1, serve-static@^1.14.1:
|
||||
version "1.14.1"
|
||||
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9"
|
||||
dependencies:
|
||||
encodeurl "~1.0.2"
|
||||
escape-html "~1.0.3"
|
||||
parseurl "~1.3.3"
|
||||
send "0.17.1"
|
||||
|
||||
setprototypeof@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683"
|
||||
|
||||
simple-sha1@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/simple-sha1/-/simple-sha1-3.0.1.tgz#b34c3c978d74ac4baf99b6555c1e6736e0d6e700"
|
||||
dependencies:
|
||||
queue-microtask "^1.1.2"
|
||||
rusha "^0.8.1"
|
||||
|
||||
"statuses@>= 1.5.0 < 2", statuses@~1.5.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
|
||||
|
||||
string2compact@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/string2compact/-/string2compact-1.3.0.tgz#22d946127b082d1203c51316af60117a337423c3"
|
||||
dependencies:
|
||||
addr-to-ip-port "^1.0.1"
|
||||
ipaddr.js "^1.0.1"
|
||||
|
||||
toidentifier@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"
|
||||
|
||||
type-is@~1.6.17, type-is@~1.6.18:
|
||||
version "1.6.18"
|
||||
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
|
||||
dependencies:
|
||||
media-typer "0.3.0"
|
||||
mime-types "~2.1.24"
|
||||
|
||||
unpipe@1.0.0, unpipe@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
|
||||
|
||||
utils-merge@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
|
||||
|
||||
uuid@^7.0.3:
|
||||
version "7.0.3"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b"
|
||||
|
||||
vary@~1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
|
Loading…
Reference in a new issue