diff --git a/.eslintrc.js b/.eslintrc.js index 9d070d28..51d7ceea 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -26,6 +26,7 @@ module.exports = { ], 'import/no-extraneous-dependencies': 0, 'import/prefer-default-export': 0, + 'lines-between-class-members': ['error', 'always', {exceptAfterSingleLine: true}], 'max-len': [ 'error', { diff --git a/client/.eslintrc.js b/client/.eslintrc.js index db0947e1..948951f8 100644 --- a/client/.eslintrc.js +++ b/client/.eslintrc.js @@ -35,7 +35,6 @@ module.exports = { 'jsx-a11y/mouse-events-have-key-events': 0, 'jsx-a11y/no-noninteractive-element-interactions': 0, 'jsx-a11y/no-static-element-interactions': 0, - 'lines-between-class-members': ['error', 'always', {exceptAfterSingleLine: true}], 'no-console': [2, {allow: ['warn', 'error']}], 'react/button-has-type': 0, 'react/default-props-match-prop-types': 0, diff --git a/package-lock.json b/package-lock.json index a4e0f397..569373f5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1615,6 +1615,25 @@ "integrity": "sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA==", "dev": true }, + "@types/bencode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/bencode/-/bencode-2.0.0.tgz", + "integrity": "sha512-ntDggX576d+MULpy9ApOy3OI9GqO86H+T9zEwYk3fdVaLi85M/1l+GVR/UWfITg9czcOO2SxZJyzyTOrI8UsFA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==", + "dev": true, + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, "@types/classnames": { "version": "2.2.10", "resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.10.tgz", @@ -1626,6 +1645,33 @@ "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" }, + "@types/compression": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@types/compression/-/compression-1.7.0.tgz", + "integrity": "sha512-3LzWUM+3k3XdWOUk/RO+uSjv7YWOatYq2QADJntK1pjkk4DfVP0KrIEPDnXRJxAAGKe0VpIPRmlINLDuCedZWw==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, + "@types/connect": { + "version": "3.4.33", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz", + "integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/cookie-parser": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.2.tgz", + "integrity": "sha512-uwcY8m6SDQqciHsqcKDGbo10GdasYsPCYkH3hVegj9qAah6pX5HivOnOuI3WYmyQMnOATV39zv/Ybs0bC/6iVg==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, "@types/css-modules-loader-core": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@types/css-modules-loader-core/-/css-modules-loader-core-1.1.0.tgz", @@ -1959,6 +2005,29 @@ "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", "dev": true }, + "@types/express": { + "version": "4.17.8", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.8.tgz", + "integrity": "sha512-wLhcKh3PMlyA2cNAB9sjM1BntnhPMiM0JOBwPBqttjHev2428MLEB4AYVN+d8s2iyCVZac+o41Pflm/ZH5vLXQ==", + "dev": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "*", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.12", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.12.tgz", + "integrity": "sha512-EaEdY+Dty1jEU7U6J4CUWwxL+hyEGMkO5jan5gplfegUgCUsIUWqXxqw47uGjimeT4Qgkz/XUfwoau08+fgvKA==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, "@types/fbemitter": { "version": "2.0.32", "resolved": "https://registry.npmjs.org/@types/fbemitter/-/fbemitter-2.0.32.tgz", @@ -2024,6 +2093,12 @@ "integrity": "sha512-iYCgjm1dGPRuo12+BStjd1HiVQqhlRhWDOQigNxn023HcjnhsiFz9pc6CzJj4HwDCSQca9bxTL4PxJDbkdm3PA==", "dev": true }, + "@types/http-errors": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-1.8.0.tgz", + "integrity": "sha512-2aoSC4UUbHDj2uCsCxcG/vRMXey/m17bC7UwitVm5hn22nI8O8Y9iDpA76Orc+DWkQ4zZrOKEshCqR/jSuXAHA==", + "dev": true + }, "@types/json-schema": { "version": "7.0.5", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.5.tgz", @@ -2057,24 +2132,60 @@ "loud-rejection": "*" } }, + "@types/mime": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.3.tgz", + "integrity": "sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q==", + "dev": true + }, "@types/minimatch": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", "dev": true }, + "@types/morgan": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.1.tgz", + "integrity": "sha512-2j5IKrgJpEP6xw/uiVb2Xfga0W0sSVD9JP9t7EZLvpBENdB0OKgcnoKS8IsjNeNnZ/86robdZ61Orl0QCFGOXg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/node": { "version": "11.15.20", "resolved": "https://registry.npmjs.org/@types/node/-/node-11.15.20.tgz", "integrity": "sha512-DY2QwdrBqNlsxdMehwzUtSsWHgYYPLVCAuXvOcu3wkzYmchbRunQ7OEZFOrmFoBLfA1ysz2Ypr6vtNP9WQkUaQ==", "dev": true }, + "@types/passport": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.4.tgz", + "integrity": "sha512-h5OfAbfBBYSzjeU0GTuuqYEk9McTgWeGQql9g3gUw2/NNCfD7VgExVRYJVVeU13Twn202Mvk9BT0bUrl30sEgA==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, "@types/prop-types": { "version": "15.7.3", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==", "dev": true }, + "@types/qs": { + "version": "6.9.5", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.5.tgz", + "integrity": "sha512-/JHkVHtx/REVG0VVToGRGH2+23hsYLHdyG+GrvoUGlGAd0ErauXDyvHtRI/7H7mzLm+tBCKA7pfcpkQ1lf58iQ==", + "dev": true + }, + "@types/range-parser": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", + "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==", + "dev": true + }, "@types/react": { "version": "16.9.49", "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.49.tgz", @@ -2133,6 +2244,16 @@ "@types/react": "*" } }, + "@types/serve-static": { + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.5.tgz", + "integrity": "sha512-6M64P58N+OXjU432WoLLBQxbA0LRGBCRm7aAGQJ+SMC1IMl0dgRVi9EFfoDcS2a7Xogygk/eGN94CfwU9UF7UQ==", + "dev": true, + "requires": { + "@types/express-serve-static-core": "*", + "@types/mime": "*" + } + }, "@types/source-list-map": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", @@ -3941,6 +4062,18 @@ "ms": "2.0.0" } }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -9775,15 +9908,27 @@ "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=" }, "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.0.tgz", + "integrity": "sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A==", "requires": { "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", "statuses": ">= 1.5.0 < 2", "toidentifier": "1.0.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + } } }, "http-parser-js": { @@ -14008,6 +14153,20 @@ "http-errors": "1.7.2", "iconv-lite": "0.4.24", "unpipe": "1.0.0" + }, + "dependencies": { + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + } } }, "rc": { @@ -15815,6 +15974,23 @@ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" } } + }, + "http-errors": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", + "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" } } }, @@ -17758,6 +17934,15 @@ "integrity": "sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ==", "dev": true }, + "typescript-transpile-only": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/typescript-transpile-only/-/typescript-transpile-only-0.0.4.tgz", + "integrity": "sha512-iRNrUzeUZPra6lwEiHwobrKS77Lc3W6/dlngW2G4FA1SWuSyrc1LiZtsHcS4JQO7L/3oiiMbouXr1UPZtWroag==", + "dev": true, + "requires": { + "tslib": "^1.9.3" + } + }, "ua-parser-js": { "version": "0.7.19", "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.19.tgz", diff --git a/package.json b/package.json index 93ced6b9..7399799d 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "scripts": { "build": "npm run build-assets && npm run build-ts", "build-assets": "node client/scripts/build.js", - "build-ts": "cd server && tsc && cat ../config.js > ../dist/config.js && chmod 755 ../dist/server/bin/start.js", + "build-ts": "tsc-transpile-only -p server/tsconfig.json && cat config.js > dist/config.js && chmod 755 dist/server/bin/start.js", "build-docs": "jsdoc -c ./.jsdoc.json", "build-i18n": "formatjs compile-folder --ast --format simple client/src/javascript/i18n client/src/javascript/i18n/compiled && formatjs compile-folder --ast --format simple client/src/javascript/i18n/translations client/src/javascript/i18n/compiled", "deprecated-warning": "node client/scripts/deprecated-warning.js && sleep 10", @@ -29,7 +29,7 @@ "start": "node --use_strict dist/server/bin/start.js", "start:development": "UPDATED_SCRIPT=start:development:server npm run deprecated-warning && npm run start:development:server", "start:development:client": "node client/scripts/start.js", - "start:development:server": "NODE_ENV=development ts-node-dev server/bin/start.ts", + "start:development:server": "NODE_ENV=development ts-node-dev --transpile-only server/bin/start.ts", "start:production": "UPDATED_SCRIPT=start npm run deprecated-warning && npm start", "start:watch": "UPDATED_SCRIPT=start:development:client npm run deprecated-warning && npm run start:development:client" }, @@ -46,6 +46,7 @@ "feedsub": "^0.7.1", "fs-extra": "^9.0.1", "geoip-country": "^4.0.36", + "http-errors": "^1.8.0", "joi": "^17.2.1", "jsonwebtoken": "^8.4.0", "lodash": "^4.17.20", @@ -71,10 +72,18 @@ "@babel/preset-react": "^7.10.4", "@babel/preset-typescript": "^7.10.4", "@formatjs/cli": "^2.11.2", + "@types/bencode": "^2.0.0", + "@types/body-parser": "^1.19.0", "@types/classnames": "^2.2.10", + "@types/compression": "^1.7.0", + "@types/cookie-parser": "^1.4.2", "@types/d3": "^5.7.2", "@types/debug": "^4.1.5", + "@types/express": "^4.17.8", "@types/flux": "^3.1.9", + "@types/http-errors": "^1.8.0", + "@types/morgan": "^1.9.1", + "@types/passport": "^1.0.4", "@types/react": "^16.9.49", "@types/react-dom": "^16.9.8", "@types/react-measure": "^2.0.6", @@ -141,6 +150,7 @@ "typed-css-modules": "^0.6.4", "typed-emitter": "^1.3.0", "typescript": "^4.0.2", + "typescript-transpile-only": "0.0.4", "url-loader": "^4.1.0", "webpack": "^4.44.1", "webpack-dev-server": "^3.11.0", diff --git a/server/app.js b/server/app.ts similarity index 75% rename from server/app.js rename to server/app.ts index 745b722c..17f73dd7 100644 --- a/server/app.js +++ b/server/app.ts @@ -1,8 +1,9 @@ import bodyParser from 'body-parser'; import compression from 'compression'; import cookieParser from 'cookie-parser'; -import express from 'express'; +import express, {Request, Response, NextFunction, ErrorRequestHandler} from 'express'; import fs from 'fs'; +import createHttpError, {HttpError} from 'http-errors'; import morgan from 'morgan'; import passport from 'passport'; import path from 'path'; @@ -58,31 +59,34 @@ app.get(path.join(paths.servedPath, 'overview'), (_req, res) => { // Catch 404 and forward to error handler. app.use((_req, _res, next) => { - const err = new Error('Not Found'); + const err = createHttpError('Not Found'); err.status = 404; next(err); }); +// Production error handler, no stacktrace leaked to user. +let errorRequestHandler: ErrorRequestHandler = (err: HttpError, _req: Request, res: Response, _next: NextFunction) => { + res.status(err.status || 500); + res.render('error', { + message: err.message, + error: {}, + title: 'Flood Error', + }); +}; + // Development error handler, will print stacktrace. if (app.get('env') === 'development') { - app.use((err, req, res) => { + errorRequestHandler = (err: HttpError, _req: Request, res: Response, _next: NextFunction) => { res.status(err.status || 500); res.render('error', { message: err.message, error: err, title: 'Flood Error', }); - }); -} else { - // Production error handler, no stacktraces leaked to user. - app.use((err, req, res) => { - res.status(err.status || 500); - res.render('error', { - message: err.message, - error: {}, - title: 'Flood Error', - }); - }); + }; } +// Error handler. +app.use(errorRequestHandler); + export default app; diff --git a/server/models/TemporaryStorage.js b/server/models/TemporaryStorage.ts similarity index 56% rename from server/models/TemporaryStorage.js rename to server/models/TemporaryStorage.ts index 3021245b..d33080dd 100644 --- a/server/models/TemporaryStorage.js +++ b/server/models/TemporaryStorage.ts @@ -5,14 +5,14 @@ import {tempPath} from '../../config'; class TemporaryStorage { constructor() { - fs.mkdir(tempPath, {recursive: true}); + fs.mkdirSync(tempPath, {recursive: true}); } - deleteFile(filename) { - fs.unlink(this.getTempPath(filename)); + deleteFile(filename: string): void { + fs.unlinkSync(this.getTempPath(filename)); } - getTempPath(filename) { + getTempPath(filename: string): string { return path.join(tempPath, filename); } } diff --git a/server/util/ajaxUtil.js b/server/util/ajaxUtil.ts similarity index 63% rename from server/util/ajaxUtil.js rename to server/util/ajaxUtil.ts index e6bf9311..2482682f 100644 --- a/server/util/ajaxUtil.js +++ b/server/util/ajaxUtil.ts @@ -1,14 +1,14 @@ +import type {Response} from 'express'; + const ajaxUtil = { - getResponseFn: (res) => (data, error) => { + getResponseFn: (res: Response) => (data: D, error: Error | string) => { if (error) { if (process.env.NODE_ENV === 'development') { console.trace(error); } if (typeof error === 'string') { - error = { - message: error, - }; + error = Error(error); } res.status(500).json(error); diff --git a/server/util/numberUtils.js b/server/util/numberUtils.ts similarity index 67% rename from server/util/numberUtils.js rename to server/util/numberUtils.ts index dc2ea792..7ac2c92e 100644 --- a/server/util/numberUtils.js +++ b/server/util/numberUtils.ts @@ -1,4 +1,4 @@ -const truncateTo = (num, precision = 0) => { +const truncateTo = (num: number, precision = 0) => { const factor = 10 ** precision; return Math.floor(num * factor) / factor; }; diff --git a/server/util/rTorrentDeserializer.js b/server/util/rTorrentDeserializer.js index be407547..008f5981 100644 --- a/server/util/rTorrentDeserializer.js +++ b/server/util/rTorrentDeserializer.js @@ -6,9 +6,9 @@ let tmpData; let dataIsVal; let endOfResponse; let rejectCallback; -let parser = new Parser(); let parserInit = false; +const parser = new Parser(); const unescapeXMLString = (value) => value diff --git a/server/util/rTorrentPropMap.js b/server/util/rTorrentPropMap.ts similarity index 96% rename from server/util/rTorrentPropMap.js rename to server/util/rTorrentPropMap.ts index 88503136..e352f534 100644 --- a/server/util/rTorrentPropMap.js +++ b/server/util/rTorrentPropMap.ts @@ -7,6 +7,6 @@ const RTORRENT_PROPS_MAP = { downloadTotal: 'throttle.global_down.total', downloadThrottle: 'throttle.global_down.max_rate', }, -}; +} as const; export default RTORRENT_PROPS_MAP; diff --git a/server/util/torrentFileUtil.js b/server/util/torrentFileUtil.ts similarity index 87% rename from server/util/torrentFileUtil.js rename to server/util/torrentFileUtil.ts index 60f3a253..980fa81e 100644 --- a/server/util/torrentFileUtil.js +++ b/server/util/torrentFileUtil.ts @@ -1,7 +1,7 @@ import bencode from 'bencode'; import fs from 'fs'; -const setTracker = (torrent, tracker) => { +const setTracker = (torrent: string, tracker: string) => { fs.readFile(torrent, (err, data) => { if (err) { return;