From 5e2cf86f4beaf766d43bdaf43e8092623fcee333 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Fri, 26 Apr 2024 14:38:03 -0700 Subject: [PATCH] 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 }