From 425d2ada65f0f7cac82068580bb44c9342bf77ba Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sat, 20 Apr 2024 07:14:17 -0700 Subject: [PATCH 1/9] Binding 'this' object to some subfunctions --- src/controllers/index.js | 7 +++++++ src/controllers/rest-api/index.js | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/controllers/index.js b/src/controllers/index.js index 117de46..12f91ac 100644 --- a/src/controllers/index.js +++ b/src/controllers/index.js @@ -21,6 +21,13 @@ class Controllers { this.useCases = new UseCases({ adapters: this.adapters }) this.timerControllers = new TimerControllers({ adapters: this.adapters, useCases: this.useCases }) this.config = config + + // Bind 'this' object to all subfunction + this.initAdapters = this.initAdapters.bind(this) + this.initUseCases = this.initUseCases.bind(this) + this.attachRESTControllers = this.attachRESTControllers.bind(this) + this.attachControllers = this.attachControllers.bind(this) + this.attachRPCControllers = this.attachRPCControllers.bind(this) } // Spin up any adapter libraries that have async startup needs. diff --git a/src/controllers/rest-api/index.js b/src/controllers/rest-api/index.js index 63e18a6..c6dbf22 100644 --- a/src/controllers/rest-api/index.js +++ b/src/controllers/rest-api/index.js @@ -29,7 +29,8 @@ class RESTControllers { ) } - // console.log('Controllers localConfig: ', localConfig) + // Bind 'this' object to all subfunctions. + this.attachRESTControllers = this.attachRESTControllers.bind(this) } attachRESTControllers (app) { From 5e2cf86f4beaf766d43bdaf43e8092623fcee333 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Fri, 26 Apr 2024 14:38:03 -0700 Subject: [PATCH 2/9] Checking in code before switching branches --- bin/server.js | 5 + src/controllers/rest-api/usage/controller.js | 145 +++++++++++++++++++ src/controllers/rest-api/usage/index.js | 67 +++++++++ src/use-cases/usage-use-cases.js | 69 +++++++++ 4 files changed, 286 insertions(+) create mode 100644 src/controllers/rest-api/usage/controller.js create mode 100644 src/controllers/rest-api/usage/index.js create mode 100644 src/use-cases/usage-use-cases.js diff --git a/bin/server.js b/bin/server.js index 535c238..ccf49b2 100644 --- a/bin/server.js +++ b/bin/server.js @@ -25,10 +25,14 @@ import config from '../config/index.js' // this first. import AdminLib from '../src/adapters/admin.js' import errorMiddleware from '../src/controllers/rest-api/middleware/error.js' +import { usageMiddleware } from '../src/use-cases/usage-use-cases.js' import wlogger from '../src/adapters/wlogger.js' import Controllers from '../src/controllers/index.js' import { applyPassportMods } from '../config/passport.js' +// import {usageMiddleware} from '../src/use-cases/usage-use-cases.js' +// console.log('usageMiddleware: ', usageMiddleware) + class Server { constructor () { // Encapsulate dependencies @@ -65,6 +69,7 @@ class Server { app.use(bodyParser()) app.use(session()) app.use(errorMiddleware()) + app.use(usageMiddleware()) // Used to generate the docs. app.use(mount('/', serve(`${process.cwd()}/docs`))) diff --git a/src/controllers/rest-api/usage/controller.js b/src/controllers/rest-api/usage/controller.js new file mode 100644 index 0000000..d883288 --- /dev/null +++ b/src/controllers/rest-api/usage/controller.js @@ -0,0 +1,145 @@ +/* + REST API Controller library for the /ipfs route +*/ + +// Global npm libraries + +// Local libraries +import wlogger from '../../../adapters/wlogger.js' + +class IpfsRESTControllerLib { + constructor (localConfig = {}) { + // Dependency Injection. + this.adapters = localConfig.adapters + if (!this.adapters) { + throw new Error( + 'Instance of Adapters library required when instantiating /ipfs REST Controller.' + ) + } + this.useCases = localConfig.useCases + if (!this.useCases) { + throw new Error( + 'Instance of Use Cases library required when instantiating /ipfs REST Controller.' + ) + } + + // Encapsulate dependencies + // this.UserModel = this.adapters.localdb.Users + // this.userUseCases = this.useCases.user + + // Bind 'this' object to all subfunctions + this.getStatus = this.getStatus.bind(this) + this.getPeers = this.getPeers.bind(this) + this.getRelays = this.getRelays.bind(this) + this.handleError = this.handleError.bind(this) + this.connect = this.connect.bind(this) + this.getThisNode = this.getThisNode.bind(this) + } + + /** + * @api {get} /ipfs Get status on IPFS infrastructure + * @apiPermission public + * @apiName GetIpfsStatus + * @apiGroup REST BCH + * + * @apiExample Example usage: + * curl -H "Content-Type: application/json" -X GET localhost:5001/ipfs + * + */ + async getStatus (ctx) { + try { + const status = await this.adapters.ipfs.getStatus() + + ctx.body = { status } + } catch (err) { + wlogger.error('Error in ipfs/controller.js/getStatus(): ') + // ctx.throw(422, err.message) + this.handleError(ctx, err) + } + } + + // Return information on IPFS peers this node is connected to. + async getPeers (ctx) { + try { + const showAll = ctx.request.body.showAll + + const peers = await this.adapters.ipfs.getPeers(showAll) + + ctx.body = { peers } + } catch (err) { + wlogger.error('Error in ipfs/controller.js/getPeers(): ') + // ctx.throw(422, err.message) + this.handleError(ctx, err) + } + } + + // Get data about the known Circuit Relays. Hydrate with data from peers list. + async getRelays (ctx) { + try { + const relays = await this.adapters.ipfs.getRelays() + + ctx.body = { relays } + } catch (err) { + wlogger.error('Error in ipfs/controller.js/getRelays(): ') + // ctx.throw(422, err.message) + this.handleError(ctx, err) + } + } + + async connect (ctx) { + try { + const multiaddr = ctx.request.body.multiaddr + const getDetails = ctx.request.body.getDetails + + // console.log('this.adapters.ipfs.ipfsCoordAdapter.ipfsCoord.adapters.ipfs: ', this.adapters.ipfs.ipfsCoordAdapter.ipfsCoord.adapters.ipfs) + const result = await this.adapters.ipfs.ipfsCoordAdapter.ipfsCoord.adapters.ipfs.connectToPeer({ multiaddr, getDetails }) + // console.log('result: ', result) + + ctx.body = result + } catch (err) { + wlogger.error('Error in ipfs/controller.js/connect():', err) + // ctx.throw(422, err.message) + this.handleError(ctx, err) + } + } + + /** + * @api {get} /ipfs/node Get a copy of the thisNode object from helia-coord + * @apiPermission public + * @apiName GetThisNode + * @apiGroup REST BCH + * + * @apiExample Example usage: + * curl -H "Content-Type: application/json" -X GET localhost:5001/ipfs/node + * + */ + async getThisNode (ctx) { + try { + const thisNode = this.adapters.ipfs.ipfsCoordAdapter.ipfsCoord.thisNode + + ctx.body = { thisNode } + } catch (err) { + wlogger.error('Error in ipfs/controller.js/getThisNode(): ') + // ctx.throw(422, err.message) + this.handleError(ctx, err) + } + } + + // DRY error handler + handleError (ctx, err) { + // If an HTTP status is specified by the buisiness logic, use that. + if (err.status) { + if (err.message) { + ctx.throw(err.status, err.message) + } else { + ctx.throw(err.status) + } + } else { + // By default use a 422 error if the HTTP status is not specified. + ctx.throw(422, err.message) + } + } +} + +// module.exports = IpfsRESTControllerLib +export default IpfsRESTControllerLib diff --git a/src/controllers/rest-api/usage/index.js b/src/controllers/rest-api/usage/index.js new file mode 100644 index 0000000..1bf0d11 --- /dev/null +++ b/src/controllers/rest-api/usage/index.js @@ -0,0 +1,67 @@ +/* + REST API library for the /usage route. +*/ + +// Public npm libraries. +import Router from 'koa-router' + +// Local libraries. +import IPFSRESTControllerLib from './controller.js' +import Validators from '../middleware/validators.js' + +// let _this + +class IpfsRouter { + constructor (localConfig = {}) { + // Dependency Injection. + this.adapters = localConfig.adapters + if (!this.adapters) { + throw new Error( + 'Instance of Adapters library required when instantiating IPFS REST Controller.' + ) + } + this.useCases = localConfig.useCases + if (!this.useCases) { + throw new Error( + 'Instance of Use Cases library required when instantiating IPFS REST Controller.' + ) + } + + const dependencies = { + adapters: this.adapters, + useCases: this.useCases + } + + // Encapsulate dependencies. + this.ipfsRESTController = new IPFSRESTControllerLib(dependencies) + this.validators = new Validators() + + // Instantiate the router and set the base route. + const baseUrl = '/ipfs' + this.router = new Router({ prefix: baseUrl }) + + // _this = this + } + + attach (app) { + if (!app) { + throw new Error( + 'Must pass app object when attaching REST API controllers.' + ) + } + + // Define the routes and attach the controller. + this.router.get('/', this.ipfsRESTController.getStatus) + this.router.post('/peers', this.ipfsRESTController.getPeers) + this.router.post('/relays', this.ipfsRESTController.getRelays) + this.router.post('/connect', this.ipfsRESTController.connect) + this.router.get('/node', this.ipfsRESTController.getThisNode) + + // Attach the Controller routes to the Koa app. + app.use(this.router.routes()) + app.use(this.router.allowedMethods()) + } +} + +// module.exports = BchRouter +export default IpfsRouter diff --git a/src/use-cases/usage-use-cases.js b/src/use-cases/usage-use-cases.js new file mode 100644 index 0000000..d982ae1 --- /dev/null +++ b/src/use-cases/usage-use-cases.js @@ -0,0 +1,69 @@ +/* + Use Case library for tracking usage. This library contains business logic + for tracking the usage of REST API and JSON RPC calls. This library is used + by admins to keep an eye on how many API calls were made in a 24-hour and + 1-hour time period. +*/ + +// This global variable is used to share data between the REST middleware and +// the Usage Use Case class instance. +const restCalls = [] + +class UsageUseCases { + constructor (localConfig = {}) { + // console.log('User localConfig: ', localConfig) + this.adapters = localConfig.adapters + if (!this.adapters) { + throw new Error( + 'Instance of adapters must be passed in when instantiating Usage Use Cases library.' + ) + } + + // Bind 'this' object to all subfunctions + + + // State + + } + + // Track the calls to a REST API + getRestSummary (inObj = {}) { + try { + console.log(`getRestSummary(): There have been ${restCalls.length} REST calls`) + + return restCalls.length + } catch (err) { + console.error('Error in usage-use-cases.js/getRestSummary()') + throw err + } + } +} + +// This Koa middleware is called any time there is a REST API. It logs the +// details from the request object. +function usageMiddleware () { + return async (ctx, next) => { + try { + await next() + + console.log('ctx.request: ', ctx.request) + const now = new Date() + + const reqObj = { + ip: ctx.request.ip, + url: ctx.request.url, + method: ctx.request.method, + timestamp: now.getTime() + } + console.log('reqObj: ', reqObj) + + restCalls.push(reqObj) + } catch (err) { + ctx.status = err.status || 500 + ctx.body = err.message + ctx.app.emit('error', err, ctx) + } + } +}; + +export { UsageUseCases, usageMiddleware } From 064ac36dc721183be7d01006f8cc01c4c618a816 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Fri, 26 Apr 2024 14:38:17 -0700 Subject: [PATCH 3/9] linting --- src/use-cases/usage-use-cases.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/use-cases/usage-use-cases.js b/src/use-cases/usage-use-cases.js index d982ae1..5bd0475 100644 --- a/src/use-cases/usage-use-cases.js +++ b/src/use-cases/usage-use-cases.js @@ -21,9 +21,7 @@ class UsageUseCases { // Bind 'this' object to all subfunctions - // State - } // Track the calls to a REST API From db3f1bf2cfd53c5d33e2385f279020de4f4531cd Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Tue, 17 Sep 2024 07:49:43 -0700 Subject: [PATCH 4/9] fix(webRTC): Renabling webRTC & circuit relays --- package-lock.json | 220 ++++++++++++++++++++------------------ package.json | 2 +- src/adapters/ipfs/ipfs.js | 18 ++-- 3 files changed, 126 insertions(+), 114 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2f66591..7667bbc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,7 +28,7 @@ "datastore-fs": "9.1.7", "glob": "7.1.6", "helia": "4.2.5", - "helia-coord": "1.5.14", + "helia-coord": "1.5.15", "jsonrpc-lite": "2.2.0", "jsonwebtoken": "8.5.1", "jwt-bch-lib": "1.3.0", @@ -3056,9 +3056,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.11.0", - "resolved": "http://94.130.170.209:4873/@eslint-community%2fregexpp/-/regexpp-4.11.0.tgz", - "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", + "version": "4.11.1", + "resolved": "http://94.130.170.209:4873/@eslint-community%2fregexpp/-/regexpp-4.11.1.tgz", + "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", "dev": true, "license": "MIT", "engines": { @@ -3157,9 +3157,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.57.0", - "resolved": "http://94.130.170.209:4873/@eslint%2fjs/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "version": "8.57.1", + "resolved": "http://94.130.170.209:4873/@eslint%2fjs/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, "license": "MIT", "engines": { @@ -3190,14 +3190,14 @@ } }, "node_modules/@helia/bitswap": { - "version": "1.1.3", - "resolved": "http://94.130.170.209:4873/@helia%2fbitswap/-/bitswap-1.1.3.tgz", - "integrity": "sha512-nCszNky/kOxaj1CZrZbzzFz6Fn7uNWigudn4KaLV+ZuFx6xyB8+t6aCmjKQ59me0WA3mwkmHkPtUOi9Y75zLSw==", + "version": "1.1.4", + "resolved": "http://94.130.170.209:4873/@helia%2fbitswap/-/bitswap-1.1.4.tgz", + "integrity": "sha512-BcmIc2FZ0NEjSYrwJxzFO5OapcOCBfcHmaBKaDoPNVh1egLLUkws2a7C3hPH5ZR7PXGQPXonH1YFz1CKYypzgA==", "license": "Apache-2.0 OR MIT", "dependencies": { "@helia/interface": "^4.3.1", "@helia/utils": "^0.3.3", - "@libp2p/interface": "^1.1.2", + "@libp2p/interface": "^1.1.4", "@libp2p/logger": "^4.0.5", "@libp2p/peer-collections": "^5.1.6", "@libp2p/utils": "^5.2.3", @@ -3222,12 +3222,12 @@ } }, "node_modules/@helia/block-brokers": { - "version": "3.0.3", - "resolved": "http://94.130.170.209:4873/@helia%2fblock-brokers/-/block-brokers-3.0.3.tgz", - "integrity": "sha512-xSnmWv1jgeBJURcdF/KFOHEa1CQygk3jm1qxYPYqnMWxRXhsFG5Qi13EA+QfslCXyywfxbr4yJWleUUV2Mw4oQ==", + "version": "3.0.4", + "resolved": "http://94.130.170.209:4873/@helia%2fblock-brokers/-/block-brokers-3.0.4.tgz", + "integrity": "sha512-ymtV2cNrVeRoXsAOTpx558nDzfyOu87bLAewF98OVZ/NB28EUTy0c592GHLX3QcMaW/XfTzSGCi+te3fvVIuQg==", "license": "Apache-2.0 OR MIT", "dependencies": { - "@helia/bitswap": "^1.1.3", + "@helia/bitswap": "^1.1.4", "@helia/interface": "^4.3.1", "@helia/utils": "^0.3.3", "@libp2p/interface": "^1.1.4", @@ -3358,13 +3358,13 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "http://94.130.170.209:4873/@humanwhocodes%2fconfig-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "version": "0.13.0", + "resolved": "http://94.130.170.209:4873/@humanwhocodes%2fconfig-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", + "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", "minimatch": "^3.0.5" }, @@ -6649,9 +6649,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.5.4", - "resolved": "http://94.130.170.209:4873/@types%2fnode/-/node-22.5.4.tgz", - "integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==", + "version": "22.5.5", + "resolved": "http://94.130.170.209:4873/@types%2fnode/-/node-22.5.5.tgz", + "integrity": "sha512-Xjs4y5UPO/CLdzpgR6GirZJx36yScjh73+2NlLlkFRSoQN8B0DpfXPdZGnvVmLRLOsqDpOfTNv7D9trgGhmOIA==", "license": "MIT", "dependencies": { "undici-types": "~6.19.2" @@ -10251,9 +10251,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.21", - "resolved": "http://94.130.170.209:4873/electron-to-chromium/-/electron-to-chromium-1.5.21.tgz", - "integrity": "sha512-+rBAerCpQvFSPyAO677i5gJuWGO2WFsoujENdcMzsrpP7Ebcc3pmpERgU8CV4fFF10a5haP4ivnFQ/AmLICBVg==", + "version": "1.5.24", + "resolved": "http://94.130.170.209:4873/electron-to-chromium/-/electron-to-chromium-1.5.24.tgz", + "integrity": "sha512-0x0wLCmpdKFCi9ulhvYZebgcPmHTkFVUfU2wzDykadkslKwT4oAmDTHEKLnlrDsMGZe4B+ksn8quZfZjYsBetA==", "license": "ISC" }, "node_modules/elliptic": { @@ -10471,9 +10471,9 @@ } }, "node_modules/envinfo": { - "version": "7.13.0", - "resolved": "http://94.130.170.209:4873/envinfo/-/envinfo-7.13.0.tgz", - "integrity": "sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q==", + "version": "7.14.0", + "resolved": "http://94.130.170.209:4873/envinfo/-/envinfo-7.14.0.tgz", + "integrity": "sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg==", "license": "MIT", "bin": { "envinfo": "dist/cli.js" @@ -10792,17 +10792,17 @@ } }, "node_modules/eslint": { - "version": "8.57.0", - "resolved": "http://94.130.170.209:4873/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "version": "8.57.1", + "resolved": "http://94.130.170.209:4873/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", @@ -11938,9 +11938,9 @@ "peer": true }, "node_modules/flow-parser": { - "version": "0.245.2", - "resolved": "http://94.130.170.209:4873/flow-parser/-/flow-parser-0.245.2.tgz", - "integrity": "sha512-FU4yuqC1j2IeWWicpzG0YJrXHJgKjK/AU8QKK/7MvQaNhcoGisDoE7FJLGCtbvnifzsgDWdm9/jtTF7Mp+PJXQ==", + "version": "0.246.0", + "resolved": "http://94.130.170.209:4873/flow-parser/-/flow-parser-0.246.0.tgz", + "integrity": "sha512-WHRizzSrWFTcKo7cVcbP3wzZVhzsoYxoWqbnH4z+JXGqrjVmnsld6kBZWVlB200PwD5ur8r+HV3KUDxv3cHhOQ==", "license": "MIT", "peer": true, "engines": { @@ -12742,9 +12742,9 @@ } }, "node_modules/helia-coord": { - "version": "1.5.14", - "resolved": "http://94.130.170.209:4873/helia-coord/-/helia-coord-1.5.14.tgz", - "integrity": "sha512-HUd6jYla/bM0tAV2bc5NWQFPBUScBTljoxl2SulvYWcH19k8LAwL5cZjbBJG88/nzC6RwtQFRV1zMQxKjBy/WA==", + "version": "1.5.15", + "resolved": "http://94.130.170.209:4873/helia-coord/-/helia-coord-1.5.15.tgz", + "integrity": "sha512-j3Oe0HKRf60iGfM0zigkaTEOE+KArWyI4LArQuVyew+EJ10anIfz6lgl+NBsOd8IlgtbaORmJlf2/Qed76HKkQ==", "license": "MIT", "dependencies": { "@chris.troutner/retry-queue": "1.0.10", @@ -13262,35 +13262,35 @@ "license": "ISC" }, "node_modules/interface-blockstore": { - "version": "5.3.0", - "resolved": "http://94.130.170.209:4873/interface-blockstore/-/interface-blockstore-5.3.0.tgz", - "integrity": "sha512-MsOE2H9duZdpxXk7KVOQLaQnX4LfqHegshKxSr4b/YD44t13koT5Lg4lphZ0FwB5uvtvqQPm7LNcS0T/5Z2YUQ==", + "version": "5.3.1", + "resolved": "http://94.130.170.209:4873/interface-blockstore/-/interface-blockstore-5.3.1.tgz", + "integrity": "sha512-nhgrQnz6yUQEqxTFLhlOBurQOy5lWlwCpgFmZ3GTObTVTQS9RZjK/JTozY6ty9uz2lZs7VFJSqwjWAltorJ4Vw==", "license": "Apache-2.0 OR MIT", "dependencies": { - "interface-store": "6.0.0", - "multiformats": "^13.0.1" + "interface-store": "^6.0.0", + "multiformats": "^13.2.3" } }, "node_modules/interface-blockstore/node_modules/interface-store": { - "version": "6.0.0", - "resolved": "http://94.130.170.209:4873/interface-store/-/interface-store-6.0.0.tgz", - "integrity": "sha512-HkjsDPsjA7SKkCr+TH1elUQApAAM3X3JPwrz3vFzaf614wI+ZD6GVvwKGZCHYcbSRqeZP/uzVPqezzeISeo5kA==", + "version": "6.0.2", + "resolved": "http://94.130.170.209:4873/interface-store/-/interface-store-6.0.2.tgz", + "integrity": "sha512-KSFCXtBlNoG0hzwNa0RmhHtrdhzexp+S+UY2s0rWTBJyfdEIgn6i6Zl9otVqrcFYbYrneBT7hbmHQ8gE0C3umA==", "license": "Apache-2.0 OR MIT" }, "node_modules/interface-datastore": { - "version": "8.3.0", - "resolved": "http://94.130.170.209:4873/interface-datastore/-/interface-datastore-8.3.0.tgz", - "integrity": "sha512-RM/rTSmRcnoCwGZIHrPm+nlGYVoT4R0lcFvNnDyhdFT4R6BuHHhfFP47UldVEjs98SfxLuMhaNMsyjI918saHw==", + "version": "8.3.1", + "resolved": "http://94.130.170.209:4873/interface-datastore/-/interface-datastore-8.3.1.tgz", + "integrity": "sha512-3r0ETmHIi6HmvM5sc09QQiCD3gUfwtEM/AAChOyAd/UAKT69uk8LXfTSUBufbUIO/dU65Vj8nb9O6QjwW8vDSQ==", "license": "Apache-2.0 OR MIT", "dependencies": { - "interface-store": "6.0.0", - "uint8arrays": "^5.0.2" + "interface-store": "^6.0.0", + "uint8arrays": "^5.1.0" } }, "node_modules/interface-datastore/node_modules/interface-store": { - "version": "6.0.0", - "resolved": "http://94.130.170.209:4873/interface-store/-/interface-store-6.0.0.tgz", - "integrity": "sha512-HkjsDPsjA7SKkCr+TH1elUQApAAM3X3JPwrz3vFzaf614wI+ZD6GVvwKGZCHYcbSRqeZP/uzVPqezzeISeo5kA==", + "version": "6.0.2", + "resolved": "http://94.130.170.209:4873/interface-store/-/interface-store-6.0.2.tgz", + "integrity": "sha512-KSFCXtBlNoG0hzwNa0RmhHtrdhzexp+S+UY2s0rWTBJyfdEIgn6i6Zl9otVqrcFYbYrneBT7hbmHQ8gE0C3umA==", "license": "Apache-2.0 OR MIT" }, "node_modules/interface-store": { @@ -13362,63 +13362,77 @@ } }, "node_modules/ipfs-unixfs": { - "version": "11.1.4", - "resolved": "http://94.130.170.209:4873/ipfs-unixfs/-/ipfs-unixfs-11.1.4.tgz", - "integrity": "sha512-RE4nyx5qgG2w7JOLj0Y0D7SfAR1ZkEdramNaBx0OSD4DlQ2Y2NORgc4FHfej3Pgy31v+QISDVP1pQJhdv3bUUg==", + "version": "11.2.0", + "resolved": "http://94.130.170.209:4873/ipfs-unixfs/-/ipfs-unixfs-11.2.0.tgz", + "integrity": "sha512-J8FN1qM5nfrDo8sQKQwfj0+brTg1uBfZK2vY9hxci33lcl3BFrsELS9+1+4q/8tO1ASKfxZO8W3Pi2O4sVX2Lg==", "license": "Apache-2.0 OR MIT", "dependencies": { - "err-code": "^3.0.1", - "protons-runtime": "^5.4.0", + "protons-runtime": "^5.5.0", "uint8arraylist": "^2.4.8" } }, "node_modules/ipfs-unixfs-exporter": { - "version": "13.5.0", - "resolved": "http://94.130.170.209:4873/ipfs-unixfs-exporter/-/ipfs-unixfs-exporter-13.5.0.tgz", - "integrity": "sha512-s1eWXzoyhQFNEAB1p+QE3adjhW+lBdgpORmmjiCLiruHs5z7T5zsAgRVcWpM8LWYhq2flRtJHObb7Hg73J+oLQ==", + "version": "13.6.1", + "resolved": "http://94.130.170.209:4873/ipfs-unixfs-exporter/-/ipfs-unixfs-exporter-13.6.1.tgz", + "integrity": "sha512-pYPI4oBTWao2//sFzAL0pURyojn79q/u5BuK6L5/nVbVUQVw6DcVP5uB1ySdWlTM2H+0Zlhp9+OL9aJBRIICpg==", "license": "Apache-2.0 OR MIT", "dependencies": { - "@ipld/dag-cbor": "^9.0.0", - "@ipld/dag-json": "^10.1.7", - "@ipld/dag-pb": "^4.0.0", - "@multiformats/murmur3": "^2.0.0", - "err-code": "^3.0.1", - "hamt-sharding": "^3.0.0", - "interface-blockstore": "^5.0.0", + "@ipld/dag-cbor": "^9.2.1", + "@ipld/dag-json": "^10.2.2", + "@ipld/dag-pb": "^4.1.2", + "@multiformats/murmur3": "^2.1.8", + "hamt-sharding": "^3.0.6", + "interface-blockstore": "^5.3.0", "ipfs-unixfs": "^11.0.0", - "it-filter": "^3.0.2", - "it-last": "^3.0.2", - "it-map": "^3.0.3", - "it-parallel": "^3.0.0", + "it-filter": "^3.1.1", + "it-last": "^3.0.6", + "it-map": "^3.1.1", + "it-parallel": "^3.0.8", "it-pipe": "^3.0.1", - "it-pushable": "^3.1.0", - "multiformats": "^13.0.0", + "it-pushable": "^3.2.3", + "multiformats": "^13.2.3", "p-queue": "^8.0.1", - "progress-events": "^1.0.0" + "progress-events": "^1.0.1" } }, "node_modules/ipfs-unixfs-importer": { - "version": "15.2.5", - "resolved": "http://94.130.170.209:4873/ipfs-unixfs-importer/-/ipfs-unixfs-importer-15.2.5.tgz", - "integrity": "sha512-K8tybgmhOthQ/ThPcjohYnTaVhv1hi3YKo+gPREBXcMUUG7Yd2HYfz1jExuOfhaMzICl0hDKwUMJsaU6FXMJew==", + "version": "15.3.1", + "resolved": "http://94.130.170.209:4873/ipfs-unixfs-importer/-/ipfs-unixfs-importer-15.3.1.tgz", + "integrity": "sha512-wHCTBqNsZXLJZ9/GSr7Msb3FDXD5yXF20Y9sKyUbbqNjbvaXs3n3h1+NM/5+WrgESHfwRcJIlJtaOKafL8Ymdg==", "license": "Apache-2.0 OR MIT", "dependencies": { - "@ipld/dag-pb": "^4.1.0", + "@ipld/dag-pb": "^4.1.2", "@multiformats/murmur3": "^2.1.8", - "err-code": "^3.0.1", "hamt-sharding": "^3.0.6", - "interface-blockstore": "^5.2.10", - "interface-store": "^5.1.8", + "interface-blockstore": "^5.3.0", + "interface-store": "^6.0.0", "ipfs-unixfs": "^11.0.0", - "it-all": "^3.0.4", - "it-batch": "^3.0.4", - "it-first": "^3.0.4", - "it-parallel-batch": "^3.0.4", - "multiformats": "^13.1.0", - "progress-events": "^1.0.0", + "it-all": "^3.0.6", + "it-batch": "^3.0.6", + "it-first": "^3.0.6", + "it-parallel-batch": "^3.0.6", + "multiformats": "^13.2.3", + "progress-events": "^1.0.1", "rabin-wasm": "^0.1.5", "uint8arraylist": "^2.4.8", - "uint8arrays": "^5.0.3" + "uint8arrays": "^5.1.0" + } + }, + "node_modules/ipfs-unixfs-importer/node_modules/interface-store": { + "version": "6.0.2", + "resolved": "http://94.130.170.209:4873/interface-store/-/interface-store-6.0.2.tgz", + "integrity": "sha512-KSFCXtBlNoG0hzwNa0RmhHtrdhzexp+S+UY2s0rWTBJyfdEIgn6i6Zl9otVqrcFYbYrneBT7hbmHQ8gE0C3umA==", + "license": "Apache-2.0 OR MIT" + }, + "node_modules/ipfs-unixfs/node_modules/protons-runtime": { + "version": "5.5.0", + "resolved": "http://94.130.170.209:4873/protons-runtime/-/protons-runtime-5.5.0.tgz", + "integrity": "sha512-EsALjF9QsrEk6gbCx3lmfHxVN0ah7nG3cY7GySD4xf4g8cr7g543zB88Foh897Sr1RQJ9yDCUsoT1i1H/cVUFA==", + "license": "Apache-2.0 OR MIT", + "dependencies": { + "uint8-varint": "^2.0.2", + "uint8arraylist": "^2.4.3", + "uint8arrays": "^5.0.1" } }, "node_modules/ipns": { @@ -16865,7 +16879,6 @@ "resolved": "http://94.130.170.209:4873/mkdirp/-/mkdirp-0.5.6.tgz", "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "license": "MIT", - "peer": true, "dependencies": { "minimist": "^1.2.6" }, @@ -17267,9 +17280,9 @@ } }, "node_modules/multiformats": { - "version": "13.2.3", - "resolved": "http://94.130.170.209:4873/multiformats/-/multiformats-13.2.3.tgz", - "integrity": "sha512-TXwZIhK1GYOM6zO4H8tRYgXi9wRrRl+MSa3PY5DZaD7YdUMLYLXRbRTJwtCrqeDW8dLhzO9ehlC7A8vi6dMJyA==", + "version": "13.3.0", + "resolved": "http://94.130.170.209:4873/multiformats/-/multiformats-13.3.0.tgz", + "integrity": "sha512-CBiqvsufgmpo01VT5ze94O+uc+Pbf6f/sThlvWss0sBZmAOu6GQn5usrYV2sf2mr17FWYc0rO8c/CNe2T90QAA==", "license": "Apache-2.0 OR MIT" }, "node_modules/murmurhash3js-revisited": { @@ -24478,9 +24491,9 @@ } }, "node_modules/tape": { - "version": "5.8.1", - "resolved": "http://94.130.170.209:4873/tape/-/tape-5.8.1.tgz", - "integrity": "sha512-pUzADXBVYm5Jkneh9hfXnirADrzQrDA3vddKbPOc/ZLORj4dFQ6GR1KdGWX0/NvOLDcYkVgeMdw78Uf6BzO3KA==", + "version": "5.9.0", + "resolved": "http://94.130.170.209:4873/tape/-/tape-5.9.0.tgz", + "integrity": "sha512-czbGgxSVwRlbB3Ly/aqQrNwrDAzKHDW/kVXegp4hSFmR2c8qqm3hCgZbUy1+3QAQFGhPDG7J56UsV1uNilBFCA==", "license": "MIT", "dependencies": { "@ljharb/resumer": "^0.1.3", @@ -24498,8 +24511,8 @@ "inherits": "^2.0.4", "is-regex": "^1.1.4", "minimist": "^1.2.8", - "mock-property": "^1.0.3", - "object-inspect": "^1.13.1", + "mock-property": "^1.1.0", + "object-inspect": "^1.13.2", "object-is": "^1.1.6", "object-keys": "^1.1.1", "object.assign": "^4.1.5", @@ -24728,9 +24741,9 @@ } }, "node_modules/terser": { - "version": "5.32.0", - "resolved": "http://94.130.170.209:4873/terser/-/terser-5.32.0.tgz", - "integrity": "sha512-v3Gtw3IzpBJ0ugkxEX8U0W6+TnPKRRCWGh1jC/iM/e3Ki5+qvO1L1EAZ56bZasc64aXHwRHNIQEzm6//i5cemQ==", + "version": "5.33.0", + "resolved": "http://94.130.170.209:4873/terser/-/terser-5.33.0.tgz", + "integrity": "sha512-JuPVaB7s1gdFKPKTelwUyRq5Sid2A3Gko2S0PncwdBq7kN9Ti9HPWDQ06MPsEDGsZeVESjKEnyGy68quBk1w6g==", "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", @@ -26096,7 +26109,6 @@ "resolved": "http://94.130.170.209:4873/write-file-atomic/-/write-file-atomic-2.4.3.tgz", "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", "license": "ISC", - "peer": true, "dependencies": { "graceful-fs": "^4.1.11", "imurmurhash": "^0.1.4", diff --git a/package.json b/package.json index b19958a..baedcf9 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "datastore-fs": "9.1.7", "glob": "7.1.6", "helia": "4.2.5", - "helia-coord": "1.5.14", + "helia-coord": "1.5.15", "jsonrpc-lite": "2.2.0", "jsonwebtoken": "8.5.1", "jwt-bch-lib": "1.3.0", diff --git a/src/adapters/ipfs/ipfs.js b/src/adapters/ipfs/ipfs.js index 73a9af9..d6667cd 100644 --- a/src/adapters/ipfs/ipfs.js +++ b/src/adapters/ipfs/ipfs.js @@ -20,13 +20,13 @@ import { yamux } from '@chainsafe/libp2p-yamux' // import { bootstrap } from '@libp2p/bootstrap' // import { identifyService } from 'libp2p/identify' import { identify } from '@libp2p/identify' -// import { circuitRelayServer, circuitRelayTransport } from 'libp2p/circuit-relay' -import { circuitRelayServer } from '@libp2p/circuit-relay-v2' +import { circuitRelayServer, circuitRelayTransport } from '@libp2p/circuit-relay-v2' +// import { circuitRelayServer } from '@libp2p/circuit-relay-v2' import { gossipsub } from '@chainsafe/libp2p-gossipsub' import { webSockets } from '@libp2p/websockets' import { publicIpv4 } from 'public-ip' import { multiaddr } from '@multiformats/multiaddr' -// import { webRTC } from '@libp2p/webrtc' +import { webRTC } from '@libp2p/webrtc' import { keychain } from '@libp2p/keychain' import { defaultLogger } from '@libp2p/logger' import { unixfs } from '@helia/unixfs' @@ -177,12 +177,12 @@ class IpfsAdapter { const transports = [ tcp(), - webSockets() - // circuitRelayTransport({ - // discoverRelays: 3, - // reservationConcurrency: 3 - // }), - // webRTC() + webSockets(), + circuitRelayTransport({ + discoverRelays: 3, + reservationConcurrency: 3 + }), + webRTC() ] // libp2p is the networking layer that underpins Helia From f2f3208fda928c323391fa5bc51e65770d341cc4 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Wed, 18 Sep 2024 12:00:56 -0700 Subject: [PATCH 5/9] fix(ipfs): Disabling webRTC again --- src/adapters/ipfs/ipfs.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/adapters/ipfs/ipfs.js b/src/adapters/ipfs/ipfs.js index d6667cd..e7a9fc6 100644 --- a/src/adapters/ipfs/ipfs.js +++ b/src/adapters/ipfs/ipfs.js @@ -20,13 +20,13 @@ import { yamux } from '@chainsafe/libp2p-yamux' // import { bootstrap } from '@libp2p/bootstrap' // import { identifyService } from 'libp2p/identify' import { identify } from '@libp2p/identify' -import { circuitRelayServer, circuitRelayTransport } from '@libp2p/circuit-relay-v2' -// import { circuitRelayServer } from '@libp2p/circuit-relay-v2' +// import { circuitRelayServer, circuitRelayTransport } from '@libp2p/circuit-relay-v2' +import { circuitRelayServer } from '@libp2p/circuit-relay-v2' import { gossipsub } from '@chainsafe/libp2p-gossipsub' import { webSockets } from '@libp2p/websockets' import { publicIpv4 } from 'public-ip' import { multiaddr } from '@multiformats/multiaddr' -import { webRTC } from '@libp2p/webrtc' +// import { webRTC } from '@libp2p/webrtc' import { keychain } from '@libp2p/keychain' import { defaultLogger } from '@libp2p/logger' import { unixfs } from '@helia/unixfs' @@ -177,12 +177,12 @@ class IpfsAdapter { const transports = [ tcp(), - webSockets(), - circuitRelayTransport({ - discoverRelays: 3, - reservationConcurrency: 3 - }), - webRTC() + webSockets() + // circuitRelayTransport({ + // discoverRelays: 3, + // reservationConcurrency: 3 + // }), + // webRTC() ] // libp2p is the networking layer that underpins Helia @@ -194,7 +194,7 @@ class IpfsAdapter { '/ip4/127.0.0.1/tcp/0', `/ip4/0.0.0.0/tcp/${this.config.ipfsTcpPort}`, `/ip4/0.0.0.0/tcp/${this.config.ipfsWsPort}/ws`, - '/webrtc' + // '/webrtc' ] }, transports, From 5a819c1d9c8b9d00d024c943c15be632e268cf52 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Wed, 18 Sep 2024 12:01:10 -0700 Subject: [PATCH 6/9] linting --- src/adapters/ipfs/ipfs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/adapters/ipfs/ipfs.js b/src/adapters/ipfs/ipfs.js index e7a9fc6..2aa16f5 100644 --- a/src/adapters/ipfs/ipfs.js +++ b/src/adapters/ipfs/ipfs.js @@ -193,7 +193,7 @@ class IpfsAdapter { listen: [ '/ip4/127.0.0.1/tcp/0', `/ip4/0.0.0.0/tcp/${this.config.ipfsTcpPort}`, - `/ip4/0.0.0.0/tcp/${this.config.ipfsWsPort}/ws`, + `/ip4/0.0.0.0/tcp/${this.config.ipfsWsPort}/ws` // '/webrtc' ] }, From 556c6ec522a53eb436095348ad91d0c783180b26 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sat, 7 Dec 2024 20:48:08 -0800 Subject: [PATCH 7/9] fix(usage): Adding Usage REST API --- src/controllers/rest-api/index.js | 5 ++ src/controllers/rest-api/usage/controller.js | 88 ++------------------ src/controllers/rest-api/usage/index.js | 16 ++-- src/use-cases/index.js | 3 + src/use-cases/usage-use-cases.js | 1 + 5 files changed, 24 insertions(+), 89 deletions(-) diff --git a/src/controllers/rest-api/index.js b/src/controllers/rest-api/index.js index 3537539..74d5175 100644 --- a/src/controllers/rest-api/index.js +++ b/src/controllers/rest-api/index.js @@ -13,6 +13,7 @@ import ContactRESTController from './contact/index.js' import LogsRESTController from './logs/index.js' import IpfsRESTController from './ipfs/index.js' import config from '../../../config/index.js' +import UsageRESTController from './usage/index.js' class RESTControllers { constructor (localConfig = {}) { @@ -64,6 +65,10 @@ class RESTControllers { // Attach the REST API Controllers associated with the /ipfs route const ipfsRESTController = new IpfsRESTController(dependencies) ipfsRESTController.attach(app) + + // Attach the REST API Controllers associated with the /usage route + const usageRESTController = new UsageRESTController(dependencies) + usageRESTController.attach(app) } } diff --git a/src/controllers/rest-api/usage/controller.js b/src/controllers/rest-api/usage/controller.js index d883288..4c05da7 100644 --- a/src/controllers/rest-api/usage/controller.js +++ b/src/controllers/rest-api/usage/controller.js @@ -7,7 +7,7 @@ // Local libraries import wlogger from '../../../adapters/wlogger.js' -class IpfsRESTControllerLib { +class UsageRESTControllerLib { constructor (localConfig = {}) { // Dependency Injection. this.adapters = localConfig.adapters @@ -29,97 +29,27 @@ class IpfsRESTControllerLib { // Bind 'this' object to all subfunctions this.getStatus = this.getStatus.bind(this) - this.getPeers = this.getPeers.bind(this) - this.getRelays = this.getRelays.bind(this) this.handleError = this.handleError.bind(this) - this.connect = this.connect.bind(this) - this.getThisNode = this.getThisNode.bind(this) } /** - * @api {get} /ipfs Get status on IPFS infrastructure + * @api {get} /usage Get status on IPFS infrastructure * @apiPermission public - * @apiName GetIpfsStatus - * @apiGroup REST BCH + * @apiName GetUsageStatus + * @apiGroup REST Usage * * @apiExample Example usage: - * curl -H "Content-Type: application/json" -X GET localhost:5001/ipfs + * curl -H "Content-Type: application/json" -X GET localhost:5020/usage * */ async getStatus (ctx) { try { - const status = await this.adapters.ipfs.getStatus() + // const status = await this.adapters.ipfs.getStatus() + const status = await this.useCases.usage.getRestSummary() ctx.body = { status } } catch (err) { - wlogger.error('Error in ipfs/controller.js/getStatus(): ') - // ctx.throw(422, err.message) - this.handleError(ctx, err) - } - } - - // Return information on IPFS peers this node is connected to. - async getPeers (ctx) { - try { - const showAll = ctx.request.body.showAll - - const peers = await this.adapters.ipfs.getPeers(showAll) - - ctx.body = { peers } - } catch (err) { - wlogger.error('Error in ipfs/controller.js/getPeers(): ') - // ctx.throw(422, err.message) - this.handleError(ctx, err) - } - } - - // Get data about the known Circuit Relays. Hydrate with data from peers list. - async getRelays (ctx) { - try { - const relays = await this.adapters.ipfs.getRelays() - - ctx.body = { relays } - } catch (err) { - wlogger.error('Error in ipfs/controller.js/getRelays(): ') - // ctx.throw(422, err.message) - this.handleError(ctx, err) - } - } - - async connect (ctx) { - try { - const multiaddr = ctx.request.body.multiaddr - const getDetails = ctx.request.body.getDetails - - // console.log('this.adapters.ipfs.ipfsCoordAdapter.ipfsCoord.adapters.ipfs: ', this.adapters.ipfs.ipfsCoordAdapter.ipfsCoord.adapters.ipfs) - const result = await this.adapters.ipfs.ipfsCoordAdapter.ipfsCoord.adapters.ipfs.connectToPeer({ multiaddr, getDetails }) - // console.log('result: ', result) - - ctx.body = result - } catch (err) { - wlogger.error('Error in ipfs/controller.js/connect():', err) - // ctx.throw(422, err.message) - this.handleError(ctx, err) - } - } - - /** - * @api {get} /ipfs/node Get a copy of the thisNode object from helia-coord - * @apiPermission public - * @apiName GetThisNode - * @apiGroup REST BCH - * - * @apiExample Example usage: - * curl -H "Content-Type: application/json" -X GET localhost:5001/ipfs/node - * - */ - async getThisNode (ctx) { - try { - const thisNode = this.adapters.ipfs.ipfsCoordAdapter.ipfsCoord.thisNode - - ctx.body = { thisNode } - } catch (err) { - wlogger.error('Error in ipfs/controller.js/getThisNode(): ') + wlogger.error('Error in usage/controller.js/getStatus(): ') // ctx.throw(422, err.message) this.handleError(ctx, err) } @@ -142,4 +72,4 @@ class IpfsRESTControllerLib { } // module.exports = IpfsRESTControllerLib -export default IpfsRESTControllerLib +export default UsageRESTControllerLib diff --git a/src/controllers/rest-api/usage/index.js b/src/controllers/rest-api/usage/index.js index 1bf0d11..64733b2 100644 --- a/src/controllers/rest-api/usage/index.js +++ b/src/controllers/rest-api/usage/index.js @@ -6,12 +6,12 @@ import Router from 'koa-router' // Local libraries. -import IPFSRESTControllerLib from './controller.js' +import UsageRESTControllerLib from './controller.js' import Validators from '../middleware/validators.js' // let _this -class IpfsRouter { +class UsageRouter { constructor (localConfig = {}) { // Dependency Injection. this.adapters = localConfig.adapters @@ -33,11 +33,11 @@ class IpfsRouter { } // Encapsulate dependencies. - this.ipfsRESTController = new IPFSRESTControllerLib(dependencies) + this.usageRESTController = new UsageRESTControllerLib(dependencies) this.validators = new Validators() // Instantiate the router and set the base route. - const baseUrl = '/ipfs' + const baseUrl = '/usage' this.router = new Router({ prefix: baseUrl }) // _this = this @@ -51,11 +51,7 @@ class IpfsRouter { } // Define the routes and attach the controller. - this.router.get('/', this.ipfsRESTController.getStatus) - this.router.post('/peers', this.ipfsRESTController.getPeers) - this.router.post('/relays', this.ipfsRESTController.getRelays) - this.router.post('/connect', this.ipfsRESTController.connect) - this.router.get('/node', this.ipfsRESTController.getThisNode) + this.router.get('/', this.usageRESTController.getStatus) // Attach the Controller routes to the Koa app. app.use(this.router.routes()) @@ -64,4 +60,4 @@ class IpfsRouter { } // module.exports = BchRouter -export default IpfsRouter +export default UsageRouter diff --git a/src/use-cases/index.js b/src/use-cases/index.js index a2ffac6..8ce82b3 100644 --- a/src/use-cases/index.js +++ b/src/use-cases/index.js @@ -4,7 +4,9 @@ https://troutsblog.com/blog/clean-architecture */ +// Local libraries import UserUseCases from './user.js' +import { UsageUseCases } from './usage-use-cases.js' class UseCases { constructor (localConfig = {}) { @@ -17,6 +19,7 @@ class UseCases { // console.log('use-cases/index.js localConfig: ', localConfig) this.user = new UserUseCases(localConfig) + this.usage = new UsageUseCases(localConfig) } // Run any startup Use Cases at the start of the app. diff --git a/src/use-cases/usage-use-cases.js b/src/use-cases/usage-use-cases.js index 5bd0475..0920031 100644 --- a/src/use-cases/usage-use-cases.js +++ b/src/use-cases/usage-use-cases.js @@ -20,6 +20,7 @@ class UsageUseCases { } // Bind 'this' object to all subfunctions + this.getRestSummary = this.getRestSummary.bind(this) // State } From 764905b04187a5bdb31ac896996d304f31f929c7 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sat, 7 Dec 2024 21:25:10 -0800 Subject: [PATCH 8/9] fix(usage): Adding endpoints to analyize 24 hour usage of the REST API --- src/controllers/rest-api/usage/controller.js | 50 +++++++++++++- src/controllers/rest-api/usage/index.js | 2 + src/controllers/timer-controllers.js | 17 ++++- src/use-cases/usage-use-cases.js | 69 +++++++++++++++++++- 4 files changed, 134 insertions(+), 4 deletions(-) diff --git a/src/controllers/rest-api/usage/controller.js b/src/controllers/rest-api/usage/controller.js index 4c05da7..d78db1e 100644 --- a/src/controllers/rest-api/usage/controller.js +++ b/src/controllers/rest-api/usage/controller.js @@ -29,6 +29,8 @@ class UsageRESTControllerLib { // Bind 'this' object to all subfunctions this.getStatus = this.getStatus.bind(this) + this.getTopIps = this.getTopIps.bind(this) + this.getTopEndpoints = this.getTopEndpoints.bind(this) this.handleError = this.handleError.bind(this) } @@ -42,10 +44,10 @@ class UsageRESTControllerLib { * curl -H "Content-Type: application/json" -X GET localhost:5020/usage * */ - async getStatus (ctx) { + getStatus (ctx) { try { // const status = await this.adapters.ipfs.getStatus() - const status = await this.useCases.usage.getRestSummary() + const status = this.useCases.usage.getRestSummary() ctx.body = { status } } catch (err) { @@ -55,6 +57,50 @@ class UsageRESTControllerLib { } } + /** + * @api {get} /usage/ips Get top IP addresses consuming the REST API + * @apiPermission public + * @apiName GetUsageIPs + * @apiGroup REST Usage + * + * @apiExample Example usage: + * curl -H "Content-Type: application/json" -X GET localhost:5020/usage/ips + * + */ + getTopIps (ctx) { + try { + const ips = this.useCases.usage.getTopIps() + + ctx.body = { ips } + } catch (err) { + wlogger.error('Error in usage/controller.js/getTopIps(): ') + // ctx.throw(422, err.message) + this.handleError(ctx, err) + } + } + + /** + * @api {get} /usage/endpoints Get top endpoints consumed from the REST API + * @apiPermission public + * @apiName GetUsageEndpoints + * @apiGroup REST Usage + * + * @apiExample Example usage: + * curl -H "Content-Type: application/json" -X GET localhost:5020/usage/endpoints + * + */ + getTopEndpoints (ctx) { + try { + const endpoints = this.useCases.usage.getTopEndpoints() + + ctx.body = { endpoints } + } catch (err) { + wlogger.error('Error in usage/controller.js/getTopEndpoints(): ') + // ctx.throw(422, err.message) + this.handleError(ctx, err) + } + } + // DRY error handler handleError (ctx, err) { // If an HTTP status is specified by the buisiness logic, use that. diff --git a/src/controllers/rest-api/usage/index.js b/src/controllers/rest-api/usage/index.js index 64733b2..f0b1004 100644 --- a/src/controllers/rest-api/usage/index.js +++ b/src/controllers/rest-api/usage/index.js @@ -52,6 +52,8 @@ class UsageRouter { // Define the routes and attach the controller. this.router.get('/', this.usageRESTController.getStatus) + this.router.get('/ips', this.usageRESTController.getTopIps) + this.router.get('/endpoints', this.usageRESTController.getTopEndpoints) // Attach the Controller routes to the Koa app. app.use(this.router.routes()) diff --git a/src/controllers/timer-controllers.js b/src/controllers/timer-controllers.js index daa0c64..b96b224 100644 --- a/src/controllers/timer-controllers.js +++ b/src/controllers/timer-controllers.js @@ -28,6 +28,7 @@ class TimerControllers { // Bind 'this' object to all subfunctions. this.exampleTimerFunc = this.exampleTimerFunc.bind(this) + this.cleanUsage = this.cleanUsage.bind(this) // this.startTimers() } @@ -36,13 +37,15 @@ class TimerControllers { startTimers () { // Any new timer control functions can be added here. They will be started // when the server starts. - this.optimizeWalletHandle = setInterval(this.exampleTimerFunc, 60000 * 10) + this.optimizeWalletHandle = setInterval(this.exampleTimerFunc, 60000 * 60) + this.cleanUsageHandle = setInterval(this.cleanUsage, 60000 * 1) // 1 hour return true } stopTimers () { clearInterval(this.optimizeWalletHandle) + clearInterval(this.cleanusageHandle) } // Replace this example function with your own timer handler. @@ -60,6 +63,18 @@ class TimerControllers { return false } } + + // Clean the usage state so that stats reflect the last 24 hours. + cleanUsage () { + try { + this.useCases.usage.cleanUsage() + } catch (err) { + console.error('Error in time-controller.js/cleanUsage(): ', err) + + // Note: Do not throw an error. This is a top-level function. + return false + } + } } export default TimerControllers diff --git a/src/use-cases/usage-use-cases.js b/src/use-cases/usage-use-cases.js index 0920031..356485a 100644 --- a/src/use-cases/usage-use-cases.js +++ b/src/use-cases/usage-use-cases.js @@ -7,7 +7,7 @@ // This global variable is used to share data between the REST middleware and // the Usage Use Case class instance. -const restCalls = [] +let restCalls = [] class UsageUseCases { constructor (localConfig = {}) { @@ -21,10 +21,27 @@ class UsageUseCases { // Bind 'this' object to all subfunctions this.getRestSummary = this.getRestSummary.bind(this) + this.getTopIps = this.getTopIps.bind(this) + this.getTopEndpoints = this.getTopEndpoints.bind(this) // State } + // Clean up the state by removing entries that are older than 24 hours. This + // ensures stats reflect only the last 24 hours. + // This function is called by a Timer Controller. + cleanUsage () { + try { + const now = new Date() + const twentyFourHoursAgo = now.getTime() - (60000 * 60 * 24) + + restCalls = restCalls.filter(x => x.timestamp > twentyFourHoursAgo) + } catch (err) { + console.error('Error in usage-use-cases.js/cleanUsage()') + throw err + } + } + // Track the calls to a REST API getRestSummary (inObj = {}) { try { @@ -36,6 +53,56 @@ class UsageUseCases { throw err } } + + // Get the top 20 IP addresses from the stats. + getTopIps () { + try { + const ips = restCalls.map(x => x.ip) + + // Create a Map to count occurrences of each IP address string + const countMap = new Map() + ips.forEach(ip => { + countMap.set(ip, (countMap.get(ip) || 0) + 1) + }) + + // Convert the Map into an array of objects with `str` and `cnt` properties + const result = Array.from(countMap, ([ip, cnt]) => ({ ip, cnt })) + + // Sort the results by the `cnt` property in descending order + result.sort((a, b) => b.cnt - a.cnt) + + // Ensure the result has at most 20 elements + return result.slice(0, 20) + } catch (err) { + console.error('Error in usage-use-cases.js/getTopIps()') + throw err + } + } + + // Get the top 20 most consumed endpoints. + getTopEndpoints () { + try { + const endpoints = restCalls.map(x => `${x.method} ${x.url}`) + + // Create a Map to count occurrences of each IP address string + const countMap = new Map() + endpoints.forEach(endpoint => { + countMap.set(endpoint, (countMap.get(endpoint) || 0) + 1) + }) + + // Convert the Map into an array of objects with `str` and `cnt` properties + const result = Array.from(countMap, ([endpoint, cnt]) => ({ endpoint, cnt })) + + // Sort the results by the `cnt` property in descending order + result.sort((a, b) => b.cnt - a.cnt) + + // Ensure the result has at most 20 elements + return result.slice(0, 20) + } catch (err) { + console.error('Error in usage-use-cases.js/getTopEndpoints()') + throw err + } + } } // This Koa middleware is called any time there is a REST API. It logs the From 3606a2d31b890ab85c4850178f4fdbcd7f72edd7 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sat, 7 Dec 2024 21:27:41 -0800 Subject: [PATCH 9/9] code cleanup --- bin/server.js | 3 --- src/controllers/timer-controllers.js | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/bin/server.js b/bin/server.js index 55fd7a1..8fb2d65 100644 --- a/bin/server.js +++ b/bin/server.js @@ -30,9 +30,6 @@ import wlogger from '../src/adapters/wlogger.js' import Controllers from '../src/controllers/index.js' import { applyPassportMods } from '../config/passport.js' -// import {usageMiddleware} from '../src/use-cases/usage-use-cases.js' -// console.log('usageMiddleware: ', usageMiddleware) - class Server { constructor () { // Encapsulate dependencies diff --git a/src/controllers/timer-controllers.js b/src/controllers/timer-controllers.js index b96b224..10224b9 100644 --- a/src/controllers/timer-controllers.js +++ b/src/controllers/timer-controllers.js @@ -38,7 +38,7 @@ class TimerControllers { // Any new timer control functions can be added here. They will be started // when the server starts. this.optimizeWalletHandle = setInterval(this.exampleTimerFunc, 60000 * 60) - this.cleanUsageHandle = setInterval(this.cleanUsage, 60000 * 1) // 1 hour + this.cleanUsageHandle = setInterval(this.cleanUsage, 60000 * 60) // 1 hour return true }