Fixed merge conflicts

This commit is contained in:
Chris Troutner
2024-03-22 10:59:22 -07:00
113 changed files with 16476 additions and 21438 deletions
+93
View File
@@ -0,0 +1,93 @@
# Permissionless Software Foundation Community Contributing Guide 1.0
This document describes a very simple process suitable for most projects under
the PSF umbrella. It is based on [this Medium article](https://medium.com/the-node-js-collection/healthy-open-source-967fa8be7951) and the [Node.js Community Contribution Guide](https://github.com/nodejs/TSC/blob/master/BasePolicies/CONTRIBUTING.md).
Projects are encouraged to adopt this whether they
are hosted under the PSF or not.
The goal of this document is to create a contribution process that:
* Encourages new contributions.
* Encourages contributors to remain involved.
* Avoids unnecessary processes and bureaucracy whenever possible.
* Creates a transparent decision making process which makes it clear how
contributors can be involved in decision making.
This document is based on much prior art in the Node.js community, io.js,
and the Node.js project.
Additional guidance can be found at the [Permissionless Software Foundation Telegram Channel](https://t.me/permissionless_software).
## Vocabulary
* A **Contributor** is any individual creating or commenting on an issue or pull request.
* A **Committer** is a subset of contributors who have been given write access to the repository.
* A **TC (Technical Committee)** is a group of committers representing the required technical
expertise to resolve rare disputes.
# Logging Issues
Log an issue for any question or problem you might have. When in doubt, log an issue,
any additional policies about what to include will be provided in the responses. The only
exception is security disclosures which should be sent privately.
Committers may direct you to another repository, ask for additional clarifications, and
add appropriate metadata before the issue is addressed.
Please be courteous, respectful, and every participant is expected to follow the
project's Code of Conduct.
# Contributions
Any change to resources in this repository must be through pull requests. This applies to all changes
to documentation, code, binary files, etc. Even long term committers and TC members must use
pull requests.
No pull request can be merged without being reviewed.
For non-trivial contributions, pull requests should sit for at least 36 hours to ensure that
contributors in other timezones have time to review. Consideration should also be given to
weekends and other holiday periods to ensure active committers all have reasonable time to
become involved in the discussion and review process if they wish.
The default for each contribution is that it is accepted once no committer has an objection.
During review committers may also request that a specific contributor who is most versed in a
particular area gives a "LGTM" before the PR can be merged. There is no additional "sign off"
process for contributions to land. Once all issues brought by committers are addressed it can
be landed by any committer.
In the case of an objection being raised in a pull request by another committer, all involved
committers should seek to arrive at a consensus by way of addressing concerns being expressed
by discussion, compromise on the proposed change, or withdrawal of the proposed change.
If a contribution is controversial and committers cannot agree about how to get it to land
or if it should land then it should be escalated to the TC. TC members should regularly
discuss pending contributions in order to find a resolution. It is expected that only a
small minority of issues be brought to the TC for resolution and that discussion and
compromise among committers be the default resolution mechanism.
# Becoming a Committer
All contributors who land a non-trivial contribution should be on-boarded in a timely manner,
and added as a committer, and be given write access to the repository.
Committers are expected to follow this policy and continue to send pull requests, go through
proper review, and have other committers merge their pull requests.
# TC Process
The TC uses a "consensus seeking" process for issues that are escalated to the TC.
The group tries to find a resolution that has no open objections among TC members.
If a consensus cannot be reached that has no objections then a majority wins vote
is called. It is also expected that the majority of decisions made by the TC are via
a consensus seeking process and that voting is only used as a last-resort.
Resolution may involve returning the issue to committers with suggestions on how to
move forward towards a consensus. It is not expected that a meeting of the TC
will resolve all issues on its agenda during that meeting and may prefer to continue
the discussion happening among the committers.
Members can be added to the TC at any time. Any committer can nominate another committer
to the TC and the TC uses its standard consensus seeking process to evaluate whether or
not to add this new member. Members who do not participate consistently at the level of
a majority of the other members are expected to resign.
+1 -1
View File
@@ -1,5 +1,5 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2022 Permissionless Software Foundation Copyright (c) 2021-2024 Permissionless Software Foundation
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: 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:
+3
View File
@@ -0,0 +1,3 @@
# Pedigree
This repository was forked from [koa-api-boilerplate](https://github.com/christroutner/koa-api-boilerplate). Code changes from that upstream repository is frequently pulled in and merged to this repository.
+18 -16
View File
@@ -24,8 +24,8 @@ This repository is one step in a longer path. Here are the major milestones of t
## Requirements ## Requirements
- node **^14.18.2** - node **^16.20.2**
- npm **^8.3.0** - npm **^8.19.4**
- Docker **^20.10.8** - Docker **^20.10.8**
- Docker Compose **^1.27.4** - Docker Compose **^1.27.4**
@@ -58,35 +58,37 @@ docker-compose pull
docker-compose up -d docker-compose up -d
``` ```
You can bring the containers back up with `docker-compose up -d`. - You can bring the containers down with `docker-compose down`
- You can bring the containers back up with `docker-compose up -d`.
### Operation Notes ### Development Environment
- There is a memory leak in the version of js-ipfs. The app is currently configured to shut down every 8 hours to flush memory. It relies on a process manager like pm2, Docker, or systemd to restart the app after it shuts down, in order to ensure continuous operation. A development environment will allow you modify the code on-the-fly and contribute to the code base of this repository. Ubuntu v20 is the recommended OS for creating a dev environment. Other operating systems may cause issues.
- The PSF network operates as a private network. It does not connect or interact with the wider PSF network, relying instead on gateways to bridge the two networks, when they need to share content. This improves the performance and experience for everyone in the PSF network. To join the network, you'll need to add the [swarm.key](./swarm.key) file to the IPFS data folder. ```bash
git clone https://github.com/Permissionless-Software-Foundation/ipfs-service-provider
cd ipfs-service-provider
./install-mongo-sh
npm install
npm start
```
- [Instructions on setting up IPFS private networks.](https://github.com/ipfs/go-ipfs/blob/master/docs/experimental-features.md#private-networks) ## File Structure
- For external installations, the swarm.key file will typically go in `~/.ipfs/swarm.key`
- For production Docker containers, the key would go in `ipfs-service-provider/production/data/go-ipfs/data/swarm.key`
## Structure The file layout of this repository follows the file layout of [Clean Architecture](https://christroutner.github.io/trouts-blog/blog/clean-architecture). Understaning the principles laid out this article will help developers navigate the code base.
The file layout of this repository differs from the koa-api-boilerplate. Instead, it follows the file layout of [Clean Architecture](troutsblog.com/blog/clean-architecture).
## Usage ## Usage
- `npm start` Start server on live mode - `npm start` Start server on live mode
- `npm run docs` Generate API documentation - `npm run docs` Generate API documentation
- `npm test` Run mocha tests - `npm test` Run mocha tests
- `docker-compose build` Build a 'production' Docker container
- `docker-compose up` Run the docker container
## Documentation ## Documentation
API documentation is written inline and generated by [apidoc](http://apidocjs.com/). API documentation is written inline and generated by [apidoc](http://apidocjs.com/). Docs can be generated with this command:
- `npm run docs`
Visit `http://localhost:5000/docs/` to view docs Visit `http://localhost:5020/` to view docs
There is additional developer documentation in the [dev-docs directory](./dev-docs). There is additional developer documentation in the [dev-docs directory](./dev-docs).
+21 -17
View File
@@ -8,23 +8,26 @@
*/ */
// npm libraries // npm libraries
const Koa = require('koa') import Koa from 'koa'
const bodyParser = require('koa-bodyparser')
const convert = require('koa-convert') import bodyParser from 'koa-bodyparser'
const logger = require('koa-logger') import convert from 'koa-convert'
const mongoose = require('mongoose') import logger from 'koa-logger'
const session = require('koa-generic-session') import mongoose from 'mongoose'
const passport = require('koa-passport') import session from 'koa-generic-session'
const mount = require('koa-mount') import passport from 'koa-passport'
const serve = require('koa-static') import mount from 'koa-mount'
const cors = require('kcors') import serve from 'koa-static'
import cors from 'kcors'
// Local libraries // Local libraries
const config = require('../config') // this first. import config from '../config/index.js' // this first.
const AdminLib = require('../src/adapters/admin')
const errorMiddleware = require('../src/controllers/rest-api/middleware/error') import AdminLib from '../src/adapters/admin.js'
const { wlogger } = require('../src/adapters/wlogger') import errorMiddleware from '../src/controllers/rest-api/middleware/error.js'
const Controllers = require('../src/controllers') import wlogger from '../src/adapters/wlogger.js'
import Controllers from '../src/controllers/index.js'
import { applyPassportMods } from '../config/passport.js'
class Server { class Server {
constructor () { constructor () {
@@ -70,7 +73,8 @@ class Server {
app.use(mount('/logs', serve(`${process.cwd()}/config/logs`))) app.use(mount('/logs', serve(`${process.cwd()}/config/logs`)))
// User Authentication // User Authentication
require('../config/passport') // require('../config/passport')
applyPassportMods(passport)
app.use(passport.initialize()) app.use(passport.initialize())
app.use(passport.session()) app.use(passport.session())
@@ -137,4 +141,4 @@ class Server {
} }
} }
module.exports = Server export default Server
+27 -5
View File
@@ -1,12 +1,24 @@
/* /*
This file is used to store unsecure, application-specific data common to all This file is used to store unsecure, application-specific data common to all
environments. environments.
Additional Environent Variables:
- CONNECT_PREF: should have a value of 'cr' (default), or 'direct'. This is
used by helia-coord to select a connection preference between peers. Servers
with an ip4 or ip6 address should use 'direct'.
*/ */
/* eslint no-unneeded-ternary:0 */ /* eslint no-unneeded-ternary:0 */
// Hack to get __dirname back.
// https://blog.logrocket.com/alternatives-dirname-node-js-es-modules/
import * as url from 'url'
// Get the version from the package.json file. // Get the version from the package.json file.
const pkgInfo = require('../../package.json') import { readFileSync } from 'fs'
const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
const pkgInfo = JSON.parse(readFileSync(`${__dirname.toString()}/../../package.json`))
const version = pkgInfo.version const version = pkgInfo.version
const ipfsCoordName = process.env.COORD_NAME const ipfsCoordName = process.env.COORD_NAME
@@ -15,9 +27,9 @@ const ipfsCoordName = process.env.COORD_NAME
console.log('GET_JWT_AT_STARTUP: ', process.env.GET_JWT_AT_STARTUP) console.log('GET_JWT_AT_STARTUP: ', process.env.GET_JWT_AT_STARTUP)
module.exports = { export default {
// Configure TCP port. // Configure TCP port.
port: process.env.PORT || 5001, port: process.env.PORT || 5020,
// Password for HTML UI that displays logs. // Password for HTML UI that displays logs.
logPass: 'test', logPass: 'test',
@@ -49,6 +61,7 @@ module.exports = {
: 'demo', : 'demo',
// IPFS settings. // IPFS settings.
useIpfs: process.env.DISABLE_IPFS ? false : true, // Disable IPFS flag
isCircuitRelay: process.env.ENABLE_CIRCUIT_RELAY ? true : false, isCircuitRelay: process.env.ENABLE_CIRCUIT_RELAY ? true : false,
// SSL domain used for websocket connection via browsers. // SSL domain used for websocket connection via browsers.
crDomain: process.env.CR_DOMAIN ? process.env.CR_DOMAIN : '', crDomain: process.env.CR_DOMAIN ? process.env.CR_DOMAIN : '',
@@ -85,11 +98,20 @@ module.exports = {
debugLevel: process.env.DEBUG_LEVEL ? parseInt(process.env.DEBUG_LEVEL) : 2, debugLevel: process.env.DEBUG_LEVEL ? parseInt(process.env.DEBUG_LEVEL) : 2,
// Settings for production, using external go-ipfs node. // Settings for production, using external go-ipfs node.
isProduction: process.env.SVC_ENV === 'production' ? true : false, isProduction: process.env.SVC_ENV === 'prod' ? true : false,
ipfsHost: process.env.IPFS_HOST ? process.env.IPFS_HOST : 'localhost', ipfsHost: process.env.IPFS_HOST ? process.env.IPFS_HOST : 'localhost',
ipfsApiPort: process.env.IPFS_API_PORT ipfsApiPort: process.env.IPFS_API_PORT
? parseInt(process.env.IPFS_API_PORT) ? parseInt(process.env.IPFS_API_PORT)
: 5001, : 5001,
chatPubSubChan: 'psf-ipfs-chat-001' chatPubSubChan: 'psf-ipfs-chat-001',
// This can add specific Circuit Relay v2 servers to connect to.
bootstrapRelays: [
// v2 Circuit Relay (Token Tiger)
// '/ip4/137.184.93.145/tcp/8001/p2p/12D3KooWGMEKkdJfyZbwdH9EafZbRTtMn7FnhWPrE4MhRty2763g',
// v2 Circuit Relay server (FullStack.cash)
// '/ip4/78.46.129.7/tcp/4001/p2p/12D3KooWFQ11GQ5NubsJGhYZ4X3wrAGimLevxfm6HPExCrMYhpSL'
]
} }
+1 -1
View File
@@ -4,7 +4,7 @@
specified. specified.
*/ */
module.exports = { export default {
session: 'secret-boilerplate-token', session: 'secret-boilerplate-token',
token: 'secret-jwt-token', token: 'secret-jwt-token',
database: 'mongodb://localhost:27017/bch-service-dev', database: 'mongodb://localhost:27017/bch-service-dev',
+1 -1
View File
@@ -7,7 +7,7 @@
so as not to conflict with the default host port of 27017 for MongoDB. so as not to conflict with the default host port of 27017 for MongoDB.
*/ */
module.exports = { export default {
session: 'secret-boilerplate-token', session: 'secret-boilerplate-token',
token: 'secret-jwt-token', token: 'secret-jwt-token',
database: process.env.DBURL database: process.env.DBURL
+1 -1
View File
@@ -4,7 +4,7 @@
This is the environment run by the test suite. This is the environment run by the test suite.
*/ */
module.exports = { export default {
session: 'secret-boilerplate-token', session: 'secret-boilerplate-token',
token: 'secret-jwt-token', token: 'secret-jwt-token',
database: 'mongodb://localhost:27017/bch-service-test', database: 'mongodb://localhost:27017/bch-service-test',
+14 -3
View File
@@ -1,6 +1,17 @@
const common = require('./env/common') import common from './env/common.js'
import development from './env/development.js'
import production from './env/production.js'
import test from './env/test.js'
const env = process.env.SVC_ENV || 'development' const env = process.env.SVC_ENV || 'development'
const config = require(`./env/${env}`) console.log(`Loading config for this environment: ${env}`)
module.exports = Object.assign({}, common, config) let config = development
if (env === 'test') {
config = test
} else if (env === 'prod') {
config = production
}
export default Object.assign({}, common, config)
+32 -29
View File
@@ -1,30 +1,6 @@
const passport = require('koa-passport') // import passport from 'koa-passport';
const User = require('../src/adapters/localdb/models/users') import User from '../src/adapters/localdb/models/users.js'
const Strategy = require('passport-local') import Strategy from 'passport-local'
passport.serializeUser((user, done) => {
done(null, user.id)
})
passport.deserializeUser(async (id, done) => {
try {
const user = await User.findById(id, '-password')
done(null, user)
} catch (err) {
done(err)
}
})
passport.use(
'local',
new Strategy(
{
usernameField: 'email',
passwordField: 'password'
},
passportCallback
)
)
async function passportCallback (email, password, done) { async function passportCallback (email, password, done) {
try { try {
@@ -49,5 +25,32 @@ async function passportCallback (email, password, done) {
} }
} }
// For testing function applyPassportMods (passport) {
module.exports = { passport, passportCallback } passport.serializeUser((user, done) => {
done(null, user.id)
})
passport.deserializeUser(async (id, done) => {
try {
const user = await User.findById(id, '-password')
done(null, user)
} catch (err) {
done(err)
}
})
passport.use(
'local',
new Strategy(
{
usernameField: 'email',
passwordField: 'password'
},
passportCallback
)
)
return true
}
export { applyPassportMods, passportCallback }
+1 -1
View File
@@ -1,4 +1,4 @@
const Server = require('./bin/server.js') import Server from './bin/server.js'
const server = new Server() const server = new Server()
server.startServer() server.startServer()
+13800 -20693
View File
File diff suppressed because it is too large Load Diff
+25 -16
View File
@@ -1,20 +1,21 @@
{ {
"name": "ipfs-bch-wallet-service", "name": "ipfs-bch-wallet-service",
"version": "2.0.0", "version": "3.0.0",
"description": "IPFS and REST service providing blockchain access needed by a wallet. ", "description": "IPFS and REST service providing blockchain access needed by a wallet. ",
"main": "index.js", "main": "index.js",
"type": "module",
"scripts": { "scripts": {
"start": "node index.js", "start": "node index.js",
"test": "npm run test:all", "test": "npm run test:all",
"test:all": "export SVC_ENV=test && nyc --reporter=text mocha --exit --timeout 15000 --recursive test/unit test/e2e/automated/", "test:all": "export SVC_ENV=test && c8 --reporter=text mocha --exit --timeout 15000 --recursive test/unit test/e2e/automated/",
"test:unit": "export SVC_ENV=test && mocha --exit --timeout 15000 --recursive test/unit/", "test:unit": "export SVC_ENV=test && mocha --exit --timeout 15000 --recursive test/unit/",
"test:e2e:auto": "export SVC_ENV=test && mocha --exit --timeout 30000 test/e2e/automated/", "test:e2e:auto": "export SVC_ENV=test && mocha --exit --timeout 30000 test/e2e/automated/",
"test:integration": "export SVC_ENV=test && mocha --timeout 30000 --recursive test/integration/", "test:integration": "export SVC_ENV=test && mocha --timeout 30000 --recursive test/integration/",
"test:temp": "export SVC_ENV=test && mocha --exit --timeout 15000 -g '#rate-limit' test/unit/json-rpc/", "test:temp": "export SVC_ENV=test && mocha --exit --timeout 15000 -g '#rate-limit' test/unit/json-rpc/",
"lint": "standard --env mocha --fix", "lint": "standard --env mocha --fix",
"docs": "./node_modules/.bin/apidoc -i src/ -o docs", "docs": "./node_modules/.bin/apidoc -i src/ -o docs",
"coverage": "nyc report --reporter=text-lcov | coveralls", "coverage": "c8 report --reporter=text-lcov | coveralls",
"coverage:report": "export SVC_ENV=test && nyc --reporter=html mocha --exit --timeout 15000 --recursive test/unit/ test/e2e/automated/" "coverage:report": "export SVC_ENV=test && c8 --reporter=html mocha --exit --timeout 15000 --recursive test/unit/ test/e2e/automated/"
}, },
"author": "Chris Troutner <chris.troutner@gmail.com>", "author": "Chris Troutner <chris.troutner@gmail.com>",
"license": "MIT", "license": "MIT",
@@ -24,13 +25,25 @@
}, },
"repository": "Permissionless-Software-Foundation/ipfs-bch-wallet-service", "repository": "Permissionless-Software-Foundation/ipfs-bch-wallet-service",
"dependencies": { "dependencies": {
"@chainsafe/libp2p-gossipsub": "11.0.1",
"@chainsafe/libp2p-noise": "14.1.0",
"@chainsafe/libp2p-yamux": "6.0.1",
"@helia/unixfs": "3.0.1",
"@libp2p/bootstrap": "10.0.7",
"@libp2p/circuit-relay-v2": "1.0.13",
"@libp2p/identify": "1.0.6",
"@libp2p/keychain": "4.0.7",
"@libp2p/logger": "4.0.5",
"@libp2p/tcp": "9.0.7",
"@libp2p/webrtc": "4.0.17",
"@psf/bch-js": "6.7.3", "@psf/bch-js": "6.7.3",
"axios": "0.27.2", "axios": "0.27.2",
"bcryptjs": "2.4.3", "bcryptjs": "2.4.3",
"blockstore-fs": "1.1.8",
"datastore-fs": "9.1.6",
"glob": "7.1.6", "glob": "7.1.6",
"ipfs": "0.62.3", "helia": "2.1.0",
"ipfs-coord": "8.0.8", "helia-coord": "1.5.5",
"ipfs-http-client": "55.0.0",
"jsonrpc-lite": "2.2.0", "jsonrpc-lite": "2.2.0",
"jsonwebtoken": "8.5.1", "jsonwebtoken": "8.5.1",
"jwt-bch-lib": "1.3.0", "jwt-bch-lib": "1.3.0",
@@ -45,32 +58,28 @@
"koa-router": "10.0.0", "koa-router": "10.0.0",
"koa-static": "5.0.0", "koa-static": "5.0.0",
"koa2-ratelimit": "0.9.1", "koa2-ratelimit": "0.9.1",
"libp2p": "1.2.1",
"line-reader": "0.4.0", "line-reader": "0.4.0",
"minimal-slp-wallet": "5.11.1",
"mongoose": "5.13.14", "mongoose": "5.13.14",
"node-fetch": "npm:@achingbrain/node-fetch@2.6.7", "node-fetch": "npm:@achingbrain/node-fetch@2.6.7",
"nodemailer": "6.7.5", "nodemailer": "6.7.5",
"passport-local": "1.0.0", "passport-local": "1.0.0",
"public-ip": "4.0.4", "public-ip": "6.0.1",
"winston": "3.3.3", "winston": "3.3.3",
"winston-daily-rotate-file": "4.5.0" "winston-daily-rotate-file": "4.5.0"
}, },
"devDependencies": { "devDependencies": {
"apidoc": "0.51.1", "apidoc": "0.51.1",
"c8": "7.12.0",
"chai": "4.3.0", "chai": "4.3.0",
"coveralls": "3.1.0", "coveralls": "3.1.0",
"eslint": "7.19.0",
"eslint-config-prettier": "7.2.0",
"eslint-config-standard": "16.0.2",
"eslint-plugin-node": "11.1.0",
"eslint-plugin-prettier": "3.3.1",
"eslint-plugin-standard": "4.0.0",
"husky": "4.3.8", "husky": "4.3.8",
"lodash.clonedeep": "4.5.0", "lodash.clonedeep": "4.5.0",
"mocha": "10.0.0", "mocha": "10.0.0",
"nyc": "15.1.0",
"semantic-release": "19.0.3", "semantic-release": "19.0.3",
"sinon": "9.2.4", "sinon": "9.2.4",
"standard": "16.0.3", "standard": "17.0.0",
"uuid": "8.3.2" "uuid": "8.3.2"
}, },
"release": { "release": {
+7 -4
View File
@@ -10,9 +10,12 @@ RUN apt-get update
RUN apt-get install -y sudo git curl nano gnupg wget RUN apt-get install -y sudo git curl nano gnupg wget
#Install Node and NPM #Install Node and NPM
RUN curl -sL https://deb.nodesource.com/setup_16.x -o nodesource_setup.sh RUN sudo apt-get install -y ca-certificates curl gnupg
RUN bash nodesource_setup.sh RUN sudo mkdir -p /etc/apt/keyrings
RUN apt-get install -y nodejs build-essential RUN curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
RUN echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_16.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list
RUN sudo apt-get update
RUN sudo apt-get install -y nodejs build-essential
#Create the user 'safeuser' and add them to the sudo group. #Create the user 'safeuser' and add them to the sudo group.
RUN useradd -ms /bin/bash safeuser RUN useradd -ms /bin/bash safeuser
@@ -36,7 +39,7 @@ RUN npm install -g npm
# Update to the latest version of npm. # Update to the latest version of npm.
#RUN npm install -g npm@7.23.0 #RUN npm install -g npm@7.23.0
RUN npm install -g npm #RUN npm install -g npm
# npm mirror to prevent direct dependency on npm. # npm mirror to prevent direct dependency on npm.
RUN npm set registry http://94.130.170.209:4873/ RUN npm set registry http://94.130.170.209:4873/
+5 -31
View File
@@ -14,36 +14,9 @@ services:
restart: always restart: always
ipfs-bch-wallet: ipfs-bch-wallet:
image: christroutner/trickle-ipfs:v1.0.1 build: .
#build: #image: christroutner/ipfs-service-provider:v5.0.1
# context: ./ container_name: ipfs-service
# dockerfile: Dockerfile
container_name: ipfs-bch-wallet
environment:
IPFS_DAEMON_ARGUMENTS: '--enable-pubsub-experiment --migrate=true --agent-version-suffix=docker --routing=dhtclient'
UPLOAD_KBPS: '100'
DOWNLOAD_KBPS: '100'
logging:
driver: 'json-file'
options:
max-size: '10m'
max-file: '10'
mem_limit: 2000mb
ports:
- 4001:4001
- 4003:4003
- 172.17.0.1:5001:5001
- 172.17.0.1:8080:8080
command: [
'./start-ipfs.sh'
]
volumes:
- ../data/go-ipfs/data:/root/.ipfs
restart: always
bch-wallet-service:
build: ./
container_name: bch-wallet-service
logging: logging:
driver: 'json-file' driver: 'json-file'
options: options:
@@ -52,9 +25,10 @@ services:
mem_limit: 1000mb mem_limit: 1000mb
links: links:
- mongo-bch-service - mongo-bch-service
- ipfs-bch-wallet
ports: ports:
- '5010:5010' # <host port>:<container port> - '5010:5010' # <host port>:<container port>
- '4001:4001'
- '4003:4003'
volumes: volumes:
- ../data/ipfsdata:/home/safeuser/ipfs-bch-wallet-service/.ipfsdata - ../data/ipfsdata:/home/safeuser/ipfs-bch-wallet-service/.ipfsdata
- ./start-production.sh:/home/safeuser/ipfs-bch-wallet-service/start-production.sh - ./start-production.sh:/home/safeuser/ipfs-bch-wallet-service/start-production.sh
+5 -1
View File
@@ -38,10 +38,14 @@ export DBURL=mongodb://172.17.0.1:5555/ipfs-service-prod
export PORT=5010 export PORT=5010
# Production settings using external go-ipfs node. # Production settings using external go-ipfs node.
export SVC_ENV=production export SVC_ENV=prod
export IPFS_HOST=172.17.0.1 export IPFS_HOST=172.17.0.1
export IPFS_API_PORT=5001 export IPFS_API_PORT=5001
export IPFS_TCP_PORT=4001 export IPFS_TCP_PORT=4001
export IPFS_WS_PORT=4003 export IPFS_WS_PORT=4003
# Set the debug level for helia-coord. 0-3.
# 0 = no debug logs. 3 = maximum debug logs.
export DEBUG_LEVEL=0
npm start npm start
+1 -1
View File
@@ -13,7 +13,7 @@ export COORD_NAME=ipfs-service-provider-generic
export MNEMONIC="churn aisle shield silver ladder swear hunt slim pen demand spoil veteran" export MNEMONIC="churn aisle shield silver ladder swear hunt slim pen demand spoil veteran"
# 0 = less verbose. 3 = most verbose # 0 = less verbose. 3 = most verbose
export DEBUG_LEVEL=2 export DEBUG_LEVEL=0
# Production settings that use external IPFS node. # Production settings that use external IPFS node.
# https://github.com/christroutner/docker-ipfs # https://github.com/christroutner/docker-ipfs
+18 -8
View File
@@ -13,16 +13,24 @@
in the Adapter directory. in the Adapter directory.
*/ */
'use strict' // Global npm libraries
const axios = require('axios').default import axios from 'axios'
const mongoose = require('mongoose') import mongoose from 'mongoose'
const User = require('../adapters/localdb/models/users')
const config = require('../../config') // Local libraries
const JsonFiles = require('../adapters/json-files') import User from '../adapters/localdb/models/users.js'
import config from '../../config/index.js'
import JsonFiles from '../adapters/json-files.js'
// Hack to get __dirname back.
// https://blog.logrocket.com/alternatives-dirname-node-js-es-modules/
import * as url from 'url'
const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
const jsonFiles = new JsonFiles() const jsonFiles = new JsonFiles()
const JSON_FILE = `system-user-${config.env}.json` const JSON_FILE = `system-user-${config.env}.json`
const JSON_PATH = `${__dirname.toString()}/../../config/${JSON_FILE}` const JSON_PATH = `${__dirname.toString()}../../config/${JSON_FILE}`
const LOCALHOST = `http://localhost:${config.port}` const LOCALHOST = `http://localhost:${config.port}`
const context = {} const context = {}
@@ -82,6 +90,8 @@ class Admin {
// applications like the Task Manager and the test scripts can access. // applications like the Task Manager and the test scripts can access.
await jsonFiles.writeJSON(context, JSON_PATH) await jsonFiles.writeJSON(context, JSON_PATH)
// console.log('context: ', context)
// console.log('JSON_PATH: ', JSON_PATH)
return context return context
} catch (err) { } catch (err) {
@@ -169,4 +179,4 @@ class Admin {
} }
} }
module.exports = Admin export default Admin
+5 -6
View File
@@ -3,13 +3,12 @@
*/ */
/* eslint-disable no-useless-escape */ /* eslint-disable no-useless-escape */
import config from '../../config/index.js'
import NodeMailer from '../adapters/nodemailer.js'
import wlogger from '../adapters/wlogger.js'
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0' process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
const config = require('../../config')
const NodeMailer = require('../adapters/nodemailer')
const nodemailer = new NodeMailer() const nodemailer = new NodeMailer()
const { wlogger } = require('../adapters/wlogger')
let _this let _this
@@ -60,4 +59,4 @@ class ContactLib {
} }
} }
} }
module.exports = ContactLib export default ContactLib
+4 -3
View File
@@ -5,8 +5,9 @@
for this file. for this file.
*/ */
const JwtLib = require('jwt-bch-lib') import JwtLib from 'jwt-bch-lib'
const BCHJS = require('@psf/bch-js')
import BCHJS from '@psf/bch-js'
class FullStackJWT { class FullStackJWT {
constructor (localConfig = {}) { constructor (localConfig = {}) {
@@ -98,4 +99,4 @@ class FullStackJWT {
} }
} }
module.exports = FullStackJWT export default FullStackJWT
+19 -12
View File
@@ -5,19 +5,21 @@
*/ */
// Public NPM libraries // Public NPM libraries
const BCHJS = require('@psf/bch-js') import BCHJS from '@psf/bch-js'
// Load individual adapter libraries. // Load individual adapter libraries.
const IPFSAdapter = require('./ipfs') import IPFSAdapter from './ipfs/index.js'
const LocalDB = require('./localdb')
const LogsAPI = require('./logapi')
const Passport = require('./passport')
const Nodemailer = require('./nodemailer')
// const { wlogger } = require('./wlogger')
const JSONFiles = require('./json-files')
const FullStackJWT = require('./fullstack-jwt')
const config = require('../../config') import LocalDB from './localdb/index.js'
import LogsAPI from './logapi.js'
import Passport from './passport.js'
import Nodemailer from './nodemailer.js'
// const { wlogger } = require('./wlogger')
import JSONFiles from './json-files.js'
import FullStackJWT from './fullstack-jwt.js'
import config from '../../config/index.js'
class Adapters { class Adapters {
constructor (localConfig = {}) { constructor (localConfig = {}) {
@@ -53,7 +55,12 @@ class Adapters {
// Start the IPFS node. // Start the IPFS node.
// Do not start these adapters if this is an e2e test. // Do not start these adapters if this is an e2e test.
if (this.config.env !== 'test') { if (this.config.env !== 'test') {
await this.ipfs.start({ bchjs: this.bchjs }) if (this.config.useIpfs) {
await this.ipfs.start()
}
} else {
// These lines are here to ensure code coverage hits 100%.
console.log('Not starting IPFS node since this is an e2e test.')
} }
console.log('Async Adapters have been started.') console.log('Async Adapters have been started.')
@@ -66,4 +73,4 @@ class Adapters {
} }
} }
module.exports = Adapters export default Adapters
+139 -4
View File
@@ -3,9 +3,10 @@
*/ */
// Local libraries // Local libraries
const IpfsAdapter = require('./ipfs') import IpfsAdapter from './ipfs.js'
const IpfsCoordAdapter = require('./ipfs-coord')
const config = require('../../../config') import IpfsCoordAdapter from './ipfs-coord.js'
import config from '../../../config/index.js'
class IPFS { class IPFS {
constructor (localConfig = {}) { constructor (localConfig = {}) {
@@ -66,6 +67,140 @@ class IPFS {
throw err throw err
} }
} }
// Get the status of this IPFS node.
// Create an object with different metrics about this IPFS node, then return
// the object.
getStatus () {
try {
const statusObj = {
ipfsId: this.ipfsCoordAdapter.ipfsCoord.thisNode.ipfsId,
multiAddrs: this.ipfsCoordAdapter.ipfsCoord.thisNode.ipfsMultiaddrs,
bchAddr: this.ipfsCoordAdapter.ipfsCoord.thisNode.bchAddr,
slpAddr: this.ipfsCoordAdapter.ipfsCoord.thisNode.slpAddr,
pubKey: this.ipfsCoordAdapter.ipfsCoord.thisNode.pubKey,
peers: this.ipfsCoordAdapter.ipfsCoord.thisNode.peerList.length,
relays: this.ipfsCoordAdapter.ipfsCoord.thisNode.relayData.length
}
return statusObj
} catch (err) {
console.error('Error in ipfs-coord.js/getStatus()')
throw err
}
}
// Get details on the other peers this node is connected to.
async getPeers (showAll) {
try {
const peerData = this.ipfsCoordAdapter.ipfsCoord.thisNode.peerData
// console.log(`peerData: ${JSON.stringify(peerData, null, 2)}`)
let ipfsPeers =
await this.ipfsCoordAdapter.ipfsCoord.adapters.ipfs.getPeers()
// console.log('ipfsPeers: ', ipfsPeers)
ipfsPeers = this._removeDuplicatePeers(ipfsPeers)
// console.log('filtered ipfsPeers: ', ipfsPeers)
const peerDataOut = []
// Loop through each IPFS peer and hydrate it with data from the peerData.
for (let i = 0; i < ipfsPeers.length; i++) {
const thisPeer = {
peer: ipfsPeers[i]
}
// if (!showAll) {
// // Delete properties that don't contain good info.
// delete thisPeer.muxer
// delete thisPeer.latency
// delete thisPeer.streams
// }
// Get the ipfs-coord peer data for this peer.
let thisPeerData = peerData.filter((x) =>
x.from.includes(thisPeer.peer)
)
thisPeerData = thisPeerData[0]
// console.log('thisPeerData: ', thisPeerData)
// Skip if peerData for this IPFS peer could not be found.
// if (!thisPeerData) continue
try {
// console.log('thisPeerData: ', thisPeerData)
// Add data to the IPFS peer data.
thisPeer.name = thisPeerData.data.jsonLd.name
thisPeer.protocol = thisPeerData.data.jsonLd.protocol
thisPeer.version = thisPeerData.data.jsonLd.version
thisPeer.connectionAddr = thisPeerData.data.connectionAddr
if (showAll) {
// Add all the peer data.
thisPeer.peerData = thisPeerData
}
} catch (err) {
console.warn(
`warning: ${thisPeer.peer}: ${err.message}`
)
}
peerDataOut.push(thisPeer)
}
return peerDataOut
} catch (err) {
console.error('Error in getPeers(): ', err)
throw err
}
}
// Expects an array of peers and returns an array of peers with duplicates
// removed.
// This function is used by getPeers()
_removeDuplicatePeers (arr) {
// https://stackoverflow.com/questions/2218999/how-to-remove-all-duplicates-from-an-array-of-objects
return arr.filter((v, i, a) => a.findIndex((t) => t === v) === i)
}
// Get data about the known Circuit Relays. Hydrate with data from peers list.
getRelays () {
try {
const relayData = this.ipfsCoordAdapter.ipfsCoord.thisNode.relayData
const peerData = this.ipfsCoordAdapter.ipfsCoord.thisNode.peerData
// console.log(`relayData: ${JSON.stringify(relayData, null, 2)}`)
// console.log(`peerData: ${JSON.stringify(peerData, null, 2)}`)
for (let i = 0; i < relayData.length; i++) {
const thisRelay = relayData[i]
// Find the peer that corresponds to this relay.
const thisPeer = peerData.filter((x) =>
x.from.includes(thisRelay.ipfsId)
)
// console.log('thisPeer: ', thisPeer)
// If the peer couldn't be found, skip.
if (!thisPeer.length) {
thisRelay.name = ''
thisRelay.description = ''
continue
}
thisRelay.name = thisPeer[0].data.jsonLd.name
thisRelay.description = thisPeer[0].data.jsonLd.description
}
const v1Relays = this.config.v1Relays
return { v2Relays: relayData, v1Relays }
} catch (err) {
console.error('Error in getRelays(): ', err)
throw err
}
}
} }
module.exports = IPFS export default IPFS
+31 -15
View File
@@ -5,12 +5,16 @@
*/ */
// Global npm libraries // Global npm libraries
const IpfsCoord = require('ipfs-coord') // import IpfsCoord from 'ipfs-coord-esm'
// const BCHJS = require('@psf/bch-js') import IpfsCoord from 'helia-coord'
const publicIp = require('public-ip')
// import BCHJS from '@psf/bch-js';
import SlpWallet from 'minimal-slp-wallet'
import { publicIpv4 } from 'public-ip'
// Local libraries // Local libraries
const config = require('../../../config') import config from '../../../config/index.js'
// const JSONRPC = require('../../controllers/json-rpc/') // const JSONRPC = require('../../controllers/json-rpc/')
let _this let _this
@@ -34,9 +38,10 @@ class IpfsCoordAdapter {
// Encapsulate dependencies // Encapsulate dependencies
this.IpfsCoord = IpfsCoord this.IpfsCoord = IpfsCoord
this.ipfsCoord = {} this.ipfsCoord = {}
// this.rpc = new JSONRPC() // this.bchjs = new BCHJS()
this.wallet = new SlpWallet()
this.config = config this.config = config
this.publicIp = publicIp this.publicIp = publicIpv4
// Properties of this class instance. // Properties of this class instance.
this.isReady = false this.isReady = false
@@ -47,10 +52,13 @@ class IpfsCoordAdapter {
async start (localConfig = {}) { async start (localConfig = {}) {
const circuitRelayInfo = {} const circuitRelayInfo = {}
// Wait for the BCH wallet to create the wallet.
await this.wallet.walletInfoPromise
// If configured as a Circuit Relay, get the public IP addresses for this node. // If configured as a Circuit Relay, get the public IP addresses for this node.
if (this.config.isCircuitRelay) { if (this.config.isCircuitRelay) {
try { try {
const ip4 = await this.publicIp.v4() const ip4 = await this.publicIp()
// const ip6 = await publicIp.v6() // const ip6 = await publicIp.v6()
circuitRelayInfo.ip4 = ip4 circuitRelayInfo.ip4 = ip4
@@ -63,24 +71,29 @@ class IpfsCoordAdapter {
} }
} }
const nullLog = () => {}
const ipfsCoordOptions = { const ipfsCoordOptions = {
ipfs: this.ipfs, ipfs: this.ipfs,
type: 'node.js', type: 'node.js',
// type: 'browser', // type: 'browser',
bchjs: this.bchjs, wallet: this.wallet,
privateLog: console.log, // Default to console.log // privateLog: console.log, // Default to console.log
privateLog: nullLog,
isCircuitRelay: this.config.isCircuitRelay, isCircuitRelay: this.config.isCircuitRelay,
circuitRelayInfo, circuitRelayInfo,
apiInfo: this.config.apiInfo, apiInfo: this.config.apiInfo,
announceJsonLd: this.config.announceJsonLd, announceJsonLd: this.config.announceJsonLd,
mnemonic: this.config.mnemonic, mnemonic: this.config.mnemonic,
debugLevel: this.config.debugLevel debugLevel: this.config.debugLevel,
v1Relays: this.config.v1Relays,
tcpPort: this.config.ipfsTcpPort
} }
// Production env uses external go-ipfs node. // Production env uses external go-ipfs node.
if (this.config.isProduction) { // if (this.config.isProduction) {
ipfsCoordOptions.nodeType = 'external' // ipfsCoordOptions.nodeType = 'external'
} // }
this.ipfsCoord = new this.IpfsCoord(ipfsCoordOptions) this.ipfsCoord = new this.IpfsCoord(ipfsCoordOptions)
@@ -107,12 +120,15 @@ class IpfsCoordAdapter {
// Subscribe to the chat pubsub channel // Subscribe to the chat pubsub channel
async subscribeToChat () { async subscribeToChat () {
// TODO: Allow user to replace nullog with their own log handler at startup.
const nullLog = () => {}
await this.ipfsCoord.adapters.pubsub.subscribeToPubsubChannel( await this.ipfsCoord.adapters.pubsub.subscribeToPubsubChannel(
this.config.chatPubSubChan, this.config.chatPubSubChan,
console.log, nullLog,
this.ipfsCoord.thisNode this.ipfsCoord.thisNode
) )
} }
} }
module.exports = IpfsCoordAdapter export default IpfsCoordAdapter
+235 -82
View File
@@ -9,94 +9,95 @@
*/ */
// Global npm libraries // Global npm libraries
// const IPFS = require('ipfs') import { createHelia } from 'helia'
// const IPFS = require('@chris.troutner/ipfs') import fs from 'fs'
const IPFSembedded = require('ipfs') import { FsBlockstore } from 'blockstore-fs'
const IPFSexternal = require('ipfs-http-client') import { FsDatastore } from 'datastore-fs'
const fs = require('fs') import { createLibp2p } from 'libp2p'
const http = require('http') import { tcp } from '@libp2p/tcp'
import { noise } from '@chainsafe/libp2p-noise'
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, circuitRelayTransport } 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 { keychain } from '@libp2p/keychain'
import { defaultLogger } from '@libp2p/logger'
import { unixfs } from '@helia/unixfs'
// Local libraries // Local libraries
const config = require('../../../config') import config from '../../../config/index.js'
import JsonFiles from '../json-files.js'
const IPFS_DIR = './.ipfsdata/ipfs' // Hack to get __dirname back.
// https://blog.logrocket.com/alternatives-dirname-node-js-es-modules/
import * as url from 'url'
const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
// console.log('__dirname: ', __dirname)
const ROOT_DIR = `${__dirname}../../../`
const IPFS_DIR = `${__dirname}../../../.ipfsdata/ipfs`
class IpfsAdapter { class IpfsAdapter {
constructor (localConfig) { constructor (localConfig) {
// Encapsulate dependencies // Encapsulate dependencies
this.config = config this.config = config
this.fs = fs
// Choose the IPFS constructor based on the config settings. this.createLibp2p = createLibp2p
this.IPFS = IPFSembedded // default this.publicIp = publicIpv4
if (this.config.isProduction) { this.multiaddr = multiaddr
this.IPFS = IPFSexternal this.jsonFiles = new JsonFiles()
} this.keychain = keychain
this.createHelia = createHelia
// Properties of this class instance. // Properties of this class instance.
this.isReady = false this.isReady = false
this.fs = fs // Bind 'this' object to all subfunctions
this.start = this.start.bind(this)
this.createNode = this.createNode.bind(this)
this.stop = this.stop.bind(this)
this.ensureBlocksDir = this.ensureBlocksDir.bind(this)
this.getSeed = this.getSeed.bind(this)
this.getKeychain = this.getKeychain.bind(this)
} }
// Start an IPFS node. // Start an IPFS node.
async start () { async start () {
try { try {
// Ipfs Options // Ensure the directory structure exists that is needed by the IPFS node to store data.
const ipfsOptionsEmbedded = { this.ensureBlocksDir()
repo: IPFS_DIR,
start: true,
config: {
relay: {
enabled: true, // enable circuit relay dialer and listener
hop: {
enabled: config.isCircuitRelay // enable circuit relay HOP (make this node a relay)
}
},
pubsub: true, // enable pubsub
Swarm: {
ConnMgr: {
HighWater: 30,
LowWater: 10
}
},
Addresses: {
Swarm: [
`/ip4/0.0.0.0/tcp/${this.config.ipfsTcpPort}`,
`/ip4/0.0.0.0/tcp/${this.config.ipfsWsPort}/ws`
]
},
Datastore: {
StorageMax: '2GB',
StorageGCWatermark: 50,
GCPeriod: '15m'
}
}
}
const ipfsOptionsExternal = { // Create an IPFS node
host: this.config.ipfsHost, const ipfs = await this.createNode()
port: this.config.ipfsApiPort, // console.log('ipfs: ', ipfs)
agent: http.Agent({ keepAlive: true, maxSockets: 2000 })
}
let ipfsOptions = ipfsOptionsEmbedded this.id = ipfs.libp2p.peerId.toString()
if (this.config.isProduction) { console.log('IPFS ID: ', this.id)
ipfsOptions = ipfsOptionsExternal
}
// Create a new IPFS node. // Attempt to guess our ip4 IP address.
this.ipfs = await this.IPFS.create(ipfsOptions) const ip4 = await this.publicIp()
let detectedMultiaddr = `/ip4/${ip4}/tcp/${this.config.ipfsTcpPort}/p2p/${this.id}`
detectedMultiaddr = this.multiaddr(detectedMultiaddr)
// Set the 'server' profile so the node does not scan private networks. // Get the multiaddrs for the node.
await this.ipfs.config.profiles.apply('server') const multiaddrs = ipfs.libp2p.getMultiaddrs()
multiaddrs.push(detectedMultiaddr)
console.log('Multiaddrs: ', multiaddrs)
// Debugging: Display IPFS config settings. this.multiaddrs = multiaddrs
// const configSettings = await this.ipfs.config.getAll()
// console.log(`configSettings: ${JSON.stringify(configSettings, null, 2)}`)
// Signal that this adapter is ready. // Signal that this adapter is ready.
this.isReady = true this.isReady = true
this.ipfs = ipfs
return this.ipfs return this.ipfs
} catch (err) { } catch (err) {
console.error('Error in ipfs.js/start()') console.error('Error in ipfs.js/start()')
@@ -110,31 +111,183 @@ class IpfsAdapter {
} }
} }
async getKeychain (datastore) {
const keychainInit = {
pass: await this.getSeed()
}
const chain = this.keychain(keychainInit)({
datastore,
logger: defaultLogger()
})
return chain
}
// This function creates an IPFS node using Helia.
// It returns the node as an object.
async createNode () {
try {
// Create block and data stores.
const blockstore = new FsBlockstore(`${IPFS_DIR}/blockstore`)
const datastore = new FsDatastore(`${IPFS_DIR}/datastore`)
// const keychainInit = {
// pass: await this.getSeed()
// }
// Create an identity
let peerId
// console.log('this.keychain: ', this.keychain)
// const chain = this.keychain(keychainInit)({
// datastore,
// logger: defaultLogger()
// })
const chain = await this.getKeychain(datastore)
try {
peerId = await chain.exportPeerId('myKey')
} catch (err) {
await chain.createKey('myKey', 'Ed25519', 4096)
peerId = await chain.exportPeerId('myKey')
}
// Configure services
const services = {
identify: identify(),
pubsub: gossipsub({ allowPublishToZeroPeers: true })
}
if (this.config.isCircuitRelay) {
console.log('Helia (IPFS) node IS configured as Circuit Relay')
services.relay = circuitRelayServer({ // makes the node function as a relay server
hopTimeout: 30 * 1000, // incoming relay requests must be resolved within this time limit
advertise: true,
reservations: {
maxReservations: 15, // how many peers are allowed to reserve relay slots on this server
reservationClearInterval: 300 * 1000, // how often to reclaim stale reservations
applyDefaultLimit: true, // whether to apply default data/duration limits to each relayed connection
defaultDurationLimit: 2 * 60 * 1000, // the default maximum amount of time a relayed connection can be open for
defaultDataLimit: BigInt(2 << 7), // the default maximum number of bytes that can be transferred over a relayed connection
maxInboundHopStreams: 32, // how many inbound HOP streams are allow simultaneously
maxOutboundHopStreams: 64 // how many outbound HOP streams are allow simultaneously
}
})
} else {
console.log('Helia (IPFS) node IS NOT configured as Circuit Relay')
}
// Configure transports
let transports
if (process.env.CONNECT_PREF === 'direct') {
transports = [
tcp(),
webSockets(),
webRTC()
]
} else {
transports = [
tcp(),
webSockets(),
circuitRelayTransport({
discoverRelays: 3,
reservationConcurrency: 3
}),
webRTC()
]
}
// libp2p is the networking layer that underpins Helia
const libp2p = await this.createLibp2p({
peerId,
datastore,
addresses: {
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`,
'/webrtc'
]
},
transports,
connectionEncryption: [
noise()
],
streamMuxers: [
yamux()
],
services
})
// create a Helia node
const helia = await this.createHelia({
blockstore,
datastore,
libp2p
})
// Attach IPFS file system.
const fs = unixfs(helia)
helia.fs = fs
return helia
} catch (err) {
console.error('Error creating Helia node: ', err)
throw err
}
}
async stop () { async stop () {
await this.ipfs.stop() await this.ipfs.stop()
return true return true
} }
// Remove the '/blocks' directory that is used to store IPFS data. // Ensure that the directories exist to store blocks from the IPFS network.
// Dev Note: It's assumed this node is not pinning any data and that // This function is called at startup, before the IPFS node is started.
// everything in this directory is transient. This folder will regularly ensureBlocksDir () {
// fill up and prevent IPFS from starting. try {
// rmBlocksDir () { !this.fs.existsSync(`${ROOT_DIR}.ipfsdata`) && this.fs.mkdirSync(`${ROOT_DIR}.ipfsdata`)
// try {
// const dir = `${IPFS_DIR}/blocks` !this.fs.existsSync(`${IPFS_DIR}`) && this.fs.mkdirSync(`${IPFS_DIR}`)
// console.log(`Deleting ${dir} directory...`)
// !this.fs.existsSync(`${IPFS_DIR}/blockstore`) && this.fs.mkdirSync(`${IPFS_DIR}/blockstore`)
// this.fs.rmdirSync(dir, { recursive: true })
// !this.fs.existsSync(`${IPFS_DIR}/datastore`) && this.fs.mkdirSync(`${IPFS_DIR}/datastore`)
// console.log(`${dir} directory is deleted!`)
// return true
// return true // Signal successful execution. } catch (err) {
// } catch (err) { console.error('Error in adapters/ipfs.js/ensureBlocksDir(): ', err)
// console.log('Error in rmBlocksDir()') throw err
// throw err }
// } }
// }
// This function opens the seed used to generate the key for this IPFS peer.
// The seed is stored in a JSON file. If it doesn't exist, a new one is created.
async getSeed () {
try {
let seed
const filename = `${IPFS_DIR}/seed.json`
try {
// Try to read the JSON file containing the seed.
seed = await this.jsonFiles.readJSON(filename)
} catch (err) {
const seedNum = Math.floor(Math.random() * 1000000000000000000000)
seed = seedNum.toString()
// Save the newly generated seed
await this.jsonFiles.writeJSON(seed, filename)
}
// console.log('getSeed() seed: ', seed)
return seed
} catch (err) {
console.error('Error in adapters/ipfs/ipfs.js/getSeed()')
throw err
}
}
} }
module.exports = IpfsAdapter export default IpfsAdapter
+5 -4
View File
@@ -1,8 +1,8 @@
/* /*
A utility file for reading and writing JSON files. A utility file for reading and writing JSON files.
*/ */
'use strict'
const fs = require('fs') import fs from 'fs'
let _this let _this
@@ -57,7 +57,8 @@ class JsonFiles {
console.log(`err: ${JSON.stringify(err, null, 2)}`) console.log(`err: ${JSON.stringify(err, null, 2)}`)
} }
throw err // throw err
return reject(err)
} }
const obj = JSON.parse(data) const obj = JSON.parse(data)
@@ -72,4 +73,4 @@ class JsonFiles {
} }
} }
module.exports = JsonFiles export default JsonFiles
+2 -2
View File
@@ -3,7 +3,7 @@
*/ */
// Load Mongoose models. // Load Mongoose models.
const Users = require('./models/users') import Users from './models/users.js'
class LocalDB { class LocalDB {
constructor () { constructor () {
@@ -12,4 +12,4 @@ class LocalDB {
} }
} }
module.exports = LocalDB export default LocalDB
+8 -5
View File
@@ -1,7 +1,10 @@
const mongoose = require('mongoose') // Global npm libraries
const bcrypt = require('bcryptjs') import mongoose from 'mongoose'
const config = require('../../../../config') import bcrypt from 'bcryptjs'
const jwt = require('jsonwebtoken') import jwt from 'jsonwebtoken'
// Local libraries
import config from '../../../../config/index.js'
const User = new mongoose.Schema({ const User = new mongoose.Schema({
type: { type: String, default: 'user' }, type: { type: String, default: 'user' },
@@ -51,4 +54,4 @@ User.methods.generateToken = function generateToken () {
} }
// export default mongoose.model('user', User) // export default mongoose.model('user', User)
module.exports = mongoose.model('user', User) export default mongoose.model('user', User)
+9 -4
View File
@@ -1,9 +1,14 @@
const lineReader = require('line-reader') import lineReader from 'line-reader'
const fs = require('fs') import fs from 'fs'
const config = require('../../config') import config from '../../config/index.js'
// Hack to get __dirname back.
// https://blog.logrocket.com/alternatives-dirname-node-js-es-modules/
import * as url from 'url'
let _this let _this
const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
class LogsApi { class LogsApi {
constructor () { constructor () {
@@ -169,4 +174,4 @@ class LogsApi {
} }
} }
module.exports = LogsApi export default LogsApi
+4 -5
View File
@@ -3,11 +3,10 @@
*/ */
'use strict' 'use strict'
const nodemailer = require('nodemailer') import nodemailer from 'nodemailer'
const config = require('../../config') import config from '../../config/index.js'
import wlogger from './wlogger.js'
const { wlogger } = require('./wlogger')
let _this let _this
@@ -150,4 +149,4 @@ class NodeMailer {
} }
} }
module.exports = NodeMailer export default NodeMailer
+2 -2
View File
@@ -2,7 +2,7 @@
koa-passport is an authorization library used for different authentication schemes. koa-passport is an authorization library used for different authentication schemes.
*/ */
const passport = require('koa-passport') import passport from 'koa-passport'
let _this let _this
class Passport { class Passport {
@@ -32,4 +32,4 @@ class Passport {
} }
} }
module.exports = Passport export default Passport
+12 -4
View File
@@ -6,10 +6,17 @@
'use strict' 'use strict'
const winston = require('winston') // Global npm libraries
require('winston-daily-rotate-file') import winston from 'winston'
import 'winston-daily-rotate-file'
const config = require('../../config') // Local libraries
import config from '../../config/index.js'
// Hack to get __dirname back.
// https://blog.logrocket.com/alternatives-dirname-node-js-es-modules/
import * as url from 'url'
const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
class Wlogger { class Wlogger {
constructor (localConfig = {}) { constructor (localConfig = {}) {
@@ -69,4 +76,5 @@ logger.outputToConsole()
const wlogger = logger.wlogger const wlogger = logger.wlogger
module.exports = { wlogger, Wlogger } export { wlogger as default, Wlogger }
// export default wlogger
+17 -16
View File
@@ -6,23 +6,21 @@
// Public npm libraries. // Public npm libraries.
// Load the Clean Architecture Adapters library // Local libraries
const Adapters = require('../adapters') import Adapters from '../adapters/index.js'
import JSONRPC from './json-rpc/index.js'
// Load the JSON RPC Controller. import UseCases from '../use-cases/index.js'
const JSONRPC = require('./json-rpc') import RESTControllers from './rest-api/index.js'
import TimerControllers from './timer-controllers.js'
// Load the Clean Architecture Use Case libraries. import config from '../../config/index.js'
const UseCases = require('../use-cases')
// const useCases = new UseCases({ adapters })
// Load the REST API Controllers.
const RESTControllers = require('./rest-api')
class Controllers { class Controllers {
constructor (localConfig = {}) { constructor (localConfig = {}) {
// Encapsulate dependencies
this.adapters = new Adapters() this.adapters = new Adapters()
this.useCases = new UseCases({ adapters: this.adapters }) this.useCases = new UseCases({ adapters: this.adapters })
this.timerControllers = new TimerControllers({ adapters: this.adapters, useCases: this.useCases })
this.config = config
} }
// Spin up any adapter libraries that have async startup needs. // Spin up any adapter libraries that have async startup needs.
@@ -52,10 +50,13 @@ class Controllers {
// Wait for any startup processes to complete for the Adapters libraries. // Wait for any startup processes to complete for the Adapters libraries.
// await this.adapters.start() // await this.adapters.start()
// Attach the REST controllers to the Koa app. if (this.config.useIpfs) {
// this.attachRESTControllers(app) // Attach JSON RPC controllers
this.attachRPCControllers()
}
this.attachRPCControllers() // Attach and start the timer controllers
this.timerControllers.startTimers()
} }
// Add the JSON RPC router to the ipfs-coord adapter. // Add the JSON RPC router to the ipfs-coord adapter.
@@ -72,4 +73,4 @@ class Controllers {
} }
} }
module.exports = Controllers export default Controllers
+3 -3
View File
@@ -6,10 +6,10 @@
*/ */
// Public npm libraries // Public npm libraries
const jsonrpc = require('jsonrpc-lite') import jsonrpc from 'jsonrpc-lite'
// Local libraries // Local libraries
const config = require('../../../../config') import config from '../../../../config/index.js'
class AboutRPC { class AboutRPC {
constructor (localConfig) { constructor (localConfig) {
@@ -48,4 +48,4 @@ class AboutRPC {
} }
} }
module.exports = AboutRPC export default AboutRPC
+5 -4
View File
@@ -3,13 +3,14 @@
*/ */
// Public npm libraries // Public npm libraries
const jsonrpc = require('jsonrpc-lite') import jsonrpc from 'jsonrpc-lite'
// Local libraries // Local libraries
// const AuthLib = require('../../lib/auth') // const AuthLib = require('../../lib/auth')
// const UserLib = require('../../../use-cases/user') // const UserLib = require('../../../use-cases/user')
const { wlogger } = require('../../../adapters/wlogger') import wlogger from '../../../adapters/wlogger.js'
const RateLimit = require('../rate-limit')
import RateLimit from '../rate-limit.js'
class AuthRPC { class AuthRPC {
constructor (localConfig = {}) { constructor (localConfig = {}) {
@@ -173,4 +174,4 @@ class AuthRPC {
} }
} }
module.exports = AuthRPC export default AuthRPC
+7 -7
View File
@@ -3,14 +3,14 @@
*/ */
// Public npm libraries // Public npm libraries
const jsonrpc = require('jsonrpc-lite') import jsonrpc from 'jsonrpc-lite'
// Local support libraries // Local support libraries
const { wlogger } = require('../../adapters/wlogger') import wlogger from '../../adapters/wlogger.js'
const UserController = require('./users') import UserController from './users/index.js'
const AuthController = require('./auth') import AuthController from './auth/index.js'
const AboutController = require('./about') import AboutController from './about/index.js'
const BCHController = require('./bch') import BCHController from './bch/index.js'
let _this let _this
@@ -202,4 +202,4 @@ class JSONRPC {
} }
} }
module.exports = JSONRPC export default JSONRPC
+2 -2
View File
@@ -4,7 +4,7 @@
/* eslint no-useless-catch: 0 */ /* eslint no-useless-catch: 0 */
// Local libraries // Local libraries
const RateLimitLib = require('koa2-ratelimit').RateLimit import { RateLimit as RateLimitLib } from 'koa2-ratelimit'
class RateLimit { class RateLimit {
constructor (options) { constructor (options) {
@@ -80,4 +80,4 @@ class RateLimit {
} }
} }
module.exports = RateLimit export default RateLimit
+5 -4
View File
@@ -3,12 +3,13 @@
*/ */
// Public npm libraries // Public npm libraries
const jsonrpc = require('jsonrpc-lite') import jsonrpc from 'jsonrpc-lite'
// Local libraries // Local libraries
// const UserLib = require('../../../use-cases/user') // const UserLib = require('../../../use-cases/user')
const Validators = require('../validators') import Validators from '../validators.js'
const RateLimit = require('../rate-limit')
import RateLimit from '../rate-limit.js'
class UserRPC { class UserRPC {
constructor (localConfig = {}) { constructor (localConfig = {}) {
@@ -533,4 +534,4 @@ class UserRPC {
// TODO create deleteUser() // TODO create deleteUser()
} }
module.exports = UserRPC export default UserRPC
+4 -3
View File
@@ -4,10 +4,11 @@
/* eslint no-useless-catch: 0 */ /* eslint no-useless-catch: 0 */
// Public npm libraries // Public npm libraries
const jwt = require('jsonwebtoken') import jwt from 'jsonwebtoken'
// Local libraries // Local libraries
const config = require('../../../config') import config from '../../../config/index.js'
// const UserModel = require('../../adapters/localdb/models/users') // const UserModel = require('../../adapters/localdb/models/users')
class Validators { class Validators {
@@ -93,4 +94,4 @@ class Validators {
} }
} }
module.exports = Validators export default Validators
+2 -2
View File
@@ -1,4 +1,4 @@
const Passport = require('../../../adapters/passport') import Passport from '../../../adapters/passport.js'
const passport = new Passport() const passport = new Passport()
let _this let _this
@@ -96,4 +96,4 @@ class AuthRESTController {
} }
} }
module.exports = AuthRESTController export default AuthRESTController
+3 -3
View File
@@ -3,10 +3,10 @@
*/ */
// Public npm libraries. // Public npm libraries.
const Router = require('koa-router') import Router from 'koa-router'
// Local libraries. // Local libraries.
const AuthRESTController = require('./controller') import AuthRESTController from './controller.js'
class AuthRouter { class AuthRouter {
constructor (localConfig = {}) { constructor (localConfig = {}) {
@@ -48,4 +48,4 @@ class AuthRouter {
} }
} }
module.exports = AuthRouter export default AuthRouter
@@ -3,9 +3,9 @@
*/ */
/* eslint-disable no-useless-escape */ /* eslint-disable no-useless-escape */
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0' import ContactLib from '../../../adapters/contact.js'
const ContactLib = require('../../../adapters/contact') process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
const contactLib = new ContactLib() const contactLib = new ContactLib()
let _this let _this
@@ -74,4 +74,4 @@ class ContactController {
} }
} }
} }
module.exports = ContactController export default ContactController
+3 -3
View File
@@ -3,10 +3,10 @@
*/ */
// Public npm libraries. // Public npm libraries.
const Router = require('koa-router') import Router from 'koa-router'
// Local libraries. // Local libraries.
const ContactRESTControllerLib = require('./controller') import ContactRESTControllerLib from './controller.js'
class ContactRouter { class ContactRouter {
constructor (localConfig = {}) { constructor (localConfig = {}) {
@@ -53,4 +53,4 @@ class ContactRouter {
} }
} }
module.exports = ContactRouter export default ContactRouter
+12 -7
View File
@@ -6,12 +6,13 @@
// Public npm libraries. // Public npm libraries.
// Load the REST API Controllers. // Local libraries
const AuthRESTController = require('./auth') import AuthRESTController from './auth/index.js'
const UserRouter = require('./users') import UserRouter from './users/index.js'
const ContactRESTController = require('./contact') import ContactRESTController from './contact/index.js'
const LogsRESTController = require('./logs') import LogsRESTController from './logs/index.js'
const BCHRESTController = require('./bch') import IpfsRESTController from './ipfs/index.js'
import BCHRESTController from './bch/index.js'
class RESTControllers { class RESTControllers {
constructor (localConfig = {}) { constructor (localConfig = {}) {
@@ -54,10 +55,14 @@ class RESTControllers {
const logsRESTController = new LogsRESTController(dependencies) const logsRESTController = new LogsRESTController(dependencies)
logsRESTController.attach(app) logsRESTController.attach(app)
// 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 /fulcrum route // Attach the REST API Controllers associated with the /fulcrum route
const bchRESTController = new BCHRESTController(dependencies) const bchRESTController = new BCHRESTController(dependencies)
bchRESTController.attach(app) bchRESTController.attach(app)
} }
} }
module.exports = RESTControllers export default RESTControllers
+145
View File
@@ -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
+67
View File
@@ -0,0 +1,67 @@
/*
REST API library for the /ipfs 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
+2 -2
View File
@@ -1,4 +1,4 @@
const LogsApiLib = require('../../../adapters/logapi') import LogsApiLib from '../../../adapters/logapi.js'
const logsApiLib = new LogsApiLib() const logsApiLib = new LogsApiLib()
let _this let _this
@@ -62,4 +62,4 @@ class LogsApi {
} }
} }
module.exports = LogsApi export default LogsApi
+3 -3
View File
@@ -3,10 +3,10 @@
*/ */
// Public npm libraries. // Public npm libraries.
const Router = require('koa-router') import Router from 'koa-router'
// Local libraries. // Local libraries.
const LogsRESTControllerLib = require('./controller') import LogsRESTControllerLib from './controller.js'
class LogsRouter { class LogsRouter {
constructor (localConfig = {}) { constructor (localConfig = {}) {
@@ -52,4 +52,4 @@ class LogsRouter {
} }
} }
module.exports = LogsRouter export default LogsRouter
+2 -2
View File
@@ -1,4 +1,4 @@
module.exports = function errorMiddleware () { export default function errorMiddleware () {
return async (ctx, next) => { return async (ctx, next) => {
try { try {
await next() await next()
@@ -8,4 +8,4 @@ module.exports = function errorMiddleware () {
ctx.app.emit('error', err, ctx) ctx.app.emit('error', err, ctx)
} }
} }
} };
@@ -1,11 +1,24 @@
/* /*
REST API validator middleware. REST API validator middleware.
These are a series of functions that ensure the user making a REST API
matches a user in the database (or not). In can do fine-grain user control
such as telling the difference between an admin, a normal user, and an
anonymous user.
This middleware is used to gatekeep access to different REST API resources.
CT 9/17/22:
This library was lightly refactored to make it work with the new unit tests.
This not and the commented code below can be deleted once it is verified that
this refactor did not result in any breaking changes.
*/ */
const User = require('../../../adapters/localdb/models/users') import User from '../../../adapters/localdb/models/users.js'
const config = require('../../../../config')
const jwt = require('jsonwebtoken') import config from '../../../../config/index.js'
const { wlogger } = require('../../../adapters/wlogger') import jwt from 'jsonwebtoken'
import wlogger from '../../../adapters/wlogger.js'
let _this let _this
@@ -20,12 +33,10 @@ class Validators {
async ensureUser (ctx, next) { async ensureUser (ctx, next) {
try { try {
// console.log(`getToken: ${typeof (getToken)}`)
const token = _this.getToken(ctx) const token = _this.getToken(ctx)
if (!token) { if (!token) {
// console.log(`Err: Token not provided.`) throw new Error('Token could not be retrieved from header')
ctx.throw(401)
} }
let decoded = null let decoded = null
@@ -34,20 +45,23 @@ class Validators {
// console.log(`config: ${JSON.stringify(config, null, 2)}`) // console.log(`config: ${JSON.stringify(config, null, 2)}`)
decoded = _this.jwt.verify(token, config.token) decoded = _this.jwt.verify(token, config.token)
} catch (err) { } catch (err) {
// console.log(`Err: Token could not be decoded: ${err}`) throw new Error('Could not verify JWT')
ctx.throw(401)
} }
ctx.state.user = await _this.User.findById(decoded.id, '-password') ctx.state.user = await _this.User.findById(decoded.id, '-password')
if (!ctx.state.user) { if (!ctx.state.user) {
// console.log(`Err: Could not find user.`) // console.log('Err: Could not find user.')
ctx.throw(401) throw new Error('Could not find user')
} }
// return next() // return next()
return true return true
} catch (error) { } catch (error) {
ctx.throw(401) // console.log('Ensure user error: ', error)
// console.log('ctx: ', ctx)
ctx.status = 401
ctx.throw(401, error.message)
} }
} }
@@ -60,7 +74,8 @@ class Validators {
if (!token) { if (!token) {
// console.log(`Err: Token not provided.`) // console.log(`Err: Token not provided.`)
ctx.throw(401) // ctx.throw(401)
throw new Error('Token could not be retrieved from header')
} }
let decoded = null let decoded = null
@@ -70,22 +85,26 @@ class Validators {
decoded = _this.jwt.verify(token, config.token) decoded = _this.jwt.verify(token, config.token)
} catch (err) { } catch (err) {
// console.log(`Err: Token could not be decoded: ${err}`) // console.log(`Err: Token could not be decoded: ${err}`)
ctx.throw(401) // ctx.throw(401)
throw new Error('Could not verify JWT')
} }
ctx.state.user = await _this.User.findById(decoded.id, '-password') ctx.state.user = await _this.User.findById(decoded.id, '-password')
if (!ctx.state.user) { if (!ctx.state.user) {
// console.log(`Err: Could not find user.`) // console.log(`Err: Could not find user.`)
ctx.throw(401) // ctx.throw(401)
throw new Error('Could not find user')
} }
if (ctx.state.user.type !== 'admin') { if (ctx.state.user.type !== 'admin') {
ctx.throw(401, 'not admin') // ctx.throw(401, 'not admin')
throw new Error('User is not an admin')
} }
// return next() // return next()
return true return true
} catch (error) { } catch (error) {
ctx.status = 401
ctx.throw(401, error.message) ctx.throw(401, error.message)
} }
} }
@@ -101,7 +120,8 @@ class Validators {
if (!token) { if (!token) {
// console.log(`Err: Token not provided.`) // console.log(`Err: Token not provided.`)
ctx.throw(401) // ctx.throw(401)
throw new Error('Token could not be retrieved from header')
} }
// The user ID targeted in this API call. // The user ID targeted in this API call.
@@ -114,14 +134,16 @@ class Validators {
// console.log(`config: ${JSON.stringify(config, null, 2)}`) // console.log(`config: ${JSON.stringify(config, null, 2)}`)
decoded = _this.jwt.verify(token, config.token) decoded = _this.jwt.verify(token, config.token)
} catch (err) { } catch (err) {
// console.log(`Err: Token could not be decoded: ${err}`) console.log(`Err: Token could not be decoded: ${err}`)
ctx.throw(401) // ctx.throw(401)
throw new Error('Could not verify JWT')
} }
ctx.state.user = await _this.User.findById(decoded.id, '-password') ctx.state.user = await _this.User.findById(decoded.id, '-password')
if (!ctx.state.user) { if (!ctx.state.user) {
// console.log(`Err: Could not find user.`) // console.log(`Err: Could not find user.`)
ctx.throw(401) // ctx.throw(401)
throw new Error('Could not find user')
} }
// console.log('ctx.state.user: ', ctx.state.user) // console.log('ctx.state.user: ', ctx.state.user)
@@ -135,7 +157,8 @@ class Validators {
// If they don't match, then the calling user better be an admin. // If they don't match, then the calling user better be an admin.
if (ctx.state.user.type !== 'admin') { if (ctx.state.user.type !== 'admin') {
ctx.throw(401, 'not admin') // ctx.throw(401, 'not admin')
throw new Error('User is not an admin')
} else { } else {
wlogger.verbose("It's ok. The user is an admin.") wlogger.verbose("It's ok. The user is an admin.")
} }
@@ -144,6 +167,8 @@ class Validators {
// return next() // return next()
return true return true
} catch (error) { } catch (error) {
// console.log('Error in ensureTargetUserOrAdmin(): ', error)
ctx.status = 401
ctx.throw(401, error.message) ctx.throw(401, error.message)
} }
} }
@@ -166,4 +191,4 @@ class Validators {
} }
} }
module.exports = Validators export default Validators
+5 -5
View File
@@ -2,7 +2,7 @@
REST API Controller library for the /user route REST API Controller library for the /user route
*/ */
const { wlogger } = require('../../../adapters/wlogger') import wlogger from '../../../adapters/wlogger.js'
let _this let _this
@@ -36,7 +36,7 @@ class UserRESTControllerLib {
* @apiGroup REST Users * @apiGroup REST Users
* *
* @apiExample Example usage: * @apiExample Example usage:
* curl -H "Content-Type: application/json" -X POST -d '{ "user": { "email": "email@format.com", "name": "my name", "password": "secretpasas" } }' localhost:5001/users * curl -H "Content-Type: application/json" -X POST -d '{ "user": { "email": "email@format.com", "name": "my name", "password": "secretpasas" } }' localhost:5010/users
* *
* @apiParam {Object} user User object (required) * @apiParam {Object} user User object (required)
* @apiParam {String} user.email Email * @apiParam {String} user.email Email
@@ -124,7 +124,7 @@ class UserRESTControllerLib {
ctx.body = { users } ctx.body = { users }
} catch (err) { } catch (err) {
wlogger.error('Error in users/controller.js/getUsers(): '.err) wlogger.error('Error in users/controller.js/getUsers(): ', err)
ctx.throw(422, err.message) ctx.throw(422, err.message)
} }
} }
@@ -136,7 +136,7 @@ class UserRESTControllerLib {
* @apiGroup REST Users * @apiGroup REST Users
* *
* @apiExample Example usage: * @apiExample Example usage:
* curl -H "Content-Type: application/json" -X GET localhost:5000/users/56bd1da600a526986cf65c80 * curl -H "Content-Type: application/json" -X GET localhost:5010/users/56bd1da600a526986cf65c80
* *
* @apiSuccess {Object} users User object * @apiSuccess {Object} users User object
* @apiSuccess {ObjectId} users._id User id * @apiSuccess {ObjectId} users._id User id
@@ -281,4 +281,4 @@ class UserRESTControllerLib {
} }
} }
module.exports = UserRESTControllerLib export default UserRESTControllerLib
+5 -4
View File
@@ -3,11 +3,12 @@
*/ */
// Public npm libraries. // Public npm libraries.
const Router = require('koa-router') import Router from 'koa-router'
// Local libraries. // Local libraries.
const UserRESTControllerLib = require('./controller') import UserRESTControllerLib from './controller.js'
const Validators = require('../middleware/validators')
import Validators from '../middleware/validators.js'
let _this let _this
@@ -85,4 +86,4 @@ class UserRouter {
} }
} }
module.exports = UserRouter export default UserRouter
+65
View File
@@ -0,0 +1,65 @@
/*
This Controller library is concerned with timer-based functions that are
kicked off periodicially.
*/
import config from '../../config/index.js'
class TimerControllers {
constructor (localConfig = {}) {
// Dependency Injection.
this.adapters = localConfig.adapters
if (!this.adapters) {
throw new Error(
'Instance of Adapters library required when instantiating Timer Controller libraries.'
)
}
this.useCases = localConfig.useCases
if (!this.useCases) {
throw new Error(
'Instance of Use Cases library required when instantiating Timer Controller libraries.'
)
}
this.debugLevel = localConfig.debugLevel
// Encapsulate dependencies
this.config = config
// Bind 'this' object to all subfunctions.
this.exampleTimerFunc = this.exampleTimerFunc.bind(this)
// this.startTimers()
}
// Start all the time-based controllers.
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)
return true
}
stopTimers () {
clearInterval(this.optimizeWalletHandle)
}
// Replace this example function with your own timer handler.
exampleTimerFunc (negativeTest) {
try {
console.log('Example timer controller executed.')
if (negativeTest) throw new Error('test error')
return true
} catch (err) {
console.error('Error in exampleTimerFunc(): ', err)
// Note: Do not throw an error. This is a top-level function.
return false
}
}
}
export default TimerControllers
+1 -1
View File
@@ -21,4 +21,4 @@ class User {
} }
} }
module.exports = User export default User
+3 -9
View File
@@ -5,8 +5,8 @@
*/ */
// Local libraries // Local libraries
const UserUseCases = require('./user') import UserUseCases from './user.js'
const BCHUseCases = require('./bch') import BCHUseCases from './bch/index.js'
class UseCases { class UseCases {
constructor (localConfig = {}) { constructor (localConfig = {}) {
@@ -24,16 +24,10 @@ class UseCases {
// Run any startup Use Cases at the start of the app. // Run any startup Use Cases at the start of the app.
async start () { async start () {
// try {
console.log('Async Use Cases have been started.') console.log('Async Use Cases have been started.')
return true return true
// } catch (err) {
// console.error('Error in use-cases/index.js/start()')
// // console.log(err)
// throw err
// }
} }
} }
module.exports = UseCases export default UseCases
+4 -3
View File
@@ -3,8 +3,9 @@
functions are called by the /user REST API endpoints. functions are called by the /user REST API endpoints.
*/ */
const UserEntity = require('../entities/user') import UserEntity from '../entities/user.js'
const { wlogger } = require('../adapters/wlogger')
import wlogger from '../adapters/wlogger.js'
class UserLib { class UserLib {
constructor (localConfig = {}) { constructor (localConfig = {}) {
@@ -181,4 +182,4 @@ class UserLib {
} }
} }
module.exports = UserLib export default UserLib
+9 -6
View File
@@ -5,15 +5,18 @@
*/ */
// Public npm libraries // Public npm libraries
const assert = require('chai').assert import { assert } from 'chai'
const axios = require('axios').default
import axios from 'axios'
// const sinon = require('sinon') // const sinon = require('sinon')
// Local support libraries // Local support libraries
const config = require('../../../config') import config from '../../../config/index.js'
const Server = require('../../../bin/server')
const testUtils = require('../../utils/test-utils') import Server from '../../../bin/server.js'
const AdminLib = require('../../../src/adapters/admin') import testUtils from '../../utils/test-utils.js'
import AdminLib from '../../../src/adapters/admin.js'
const adminLib = new AdminLib() const adminLib = new AdminLib()
// const request = supertest.agent(app.listen()) // const request = supertest.agent(app.listen())
+10 -14
View File
@@ -1,20 +1,19 @@
const testUtils = require('../../utils/test-utils') import testUtils from '../../utils/test-utils.js'
const assert = require('chai').assert import { assert } from 'chai'
const config = require('../../../config') import config from '../../../config/index.js'
const axios = require('axios').default import axios from 'axios'
const sinon = require('sinon') import sinon from 'sinon'
import util from 'util'
const util = require('util') import UserController from '../../../src/controllers/rest-api/users/controller.js'
import Adapters from '../../../src/adapters/index.js'
import UseCases from '../../../src/use-cases/index.js'
util.inspect.defaultOptions = { depth: 1 } util.inspect.defaultOptions = { depth: 1 }
const LOCALHOST = `http://localhost:${config.port}` const LOCALHOST = `http://localhost:${config.port}`
const context = {} const context = {}
const UserController = require('../../../src/controllers/rest-api/users/controller')
const Adapters = require('../../../src/adapters')
const adapters = new Adapters() const adapters = new Adapters()
const UseCases = require('../../../src/use-cases/')
let uut let uut
let sandbox let sandbox
@@ -39,7 +38,7 @@ describe('Users', () => {
// Get the JWT used to log in as the admin 'system' user. // Get the JWT used to log in as the admin 'system' user.
const adminJWT = await testUtils.getAdminJWT() const adminJWT = await testUtils.getAdminJWT()
// console.log(`adminJWT: ${admi nJWT}`) console.log(`adminJWT: ${adminJWT}`)
context.adminJWT = adminJWT context.adminJWT = adminJWT
// const admin = await testUtils.loginAdminUser() // const admin = await testUtils.loginAdminUser()
@@ -71,9 +70,6 @@ describe('Users', () => {
await axios(options) await axios(options)
/* console.log(
`result stringified: ${JSON.stringify(result.data, null, 2)}`
) */
assert(false, 'Unexpected result') assert(false, 'Unexpected result')
} catch (err) { } catch (err) {
assert(err.response.status === 422, 'Error code 422 expected.') assert(err.response.status === 422, 'Error code 422 expected.')
+7 -7
View File
@@ -1,15 +1,15 @@
const config = require('../../../config') import config from '../../../config/index.js'
const axios = require('axios').default import axios from 'axios'
const assert = require('chai').assert import { assert } from 'chai'
const sinon = require('sinon') import sinon from 'sinon'
import { context as mockContext } from '../../unit/mocks/ctx-mock.js'
import ContactController from '../../../src/controllers/rest-api/contact/controller.js'
// Mock data // Mock data
// const mockData = require('./mocks/contact-mocks') // const mockData = require('./mocks/contact-mocks')
const LOCALHOST = `http://localhost:${config.port}` const LOCALHOST = `http://localhost:${config.port}`
const mockContext = require('../../unit/mocks/ctx-mock').context
const ContactController = require('../../../src/controllers/rest-api/contact/controller')
let uut let uut
let sandbox let sandbox
+7 -9
View File
@@ -1,17 +1,15 @@
const config = require('../../../config') import config from '../../../config/index.js'
const assert = require('chai').assert import { assert } from 'chai'
import axios from 'axios'
import sinon from 'sinon'
import util from 'util'
const axios = require('axios').default import LogsController from '../../../src/controllers/rest-api/logs/controller.js'
const sinon = require('sinon') import { context as mockContext } from '../../unit/mocks/ctx-mock.js'
const util = require('util')
util.inspect.defaultOptions = { depth: 1 } util.inspect.defaultOptions = { depth: 1 }
const LOCALHOST = `http://localhost:${config.port}` const LOCALHOST = `http://localhost:${config.port}`
const LogsController = require('../../../src/controllers/rest-api/logs/controller')
const mockContext = require('../../unit/mocks/ctx-mock').context
let sandbox let sandbox
let uut let uut
describe('LogsApi', () => { describe('LogsApi', () => {
@@ -1,12 +1,10 @@
const assert = require('chai').assert /*
const testUtils = require('../../utils/test-utils') import { assert } from 'chai';
import testUtils from '../../utils/test-utils.js';
const Validators = require('../../../src/controllers/rest-api/middleware/validators') import Validators from '../../../src/controllers/rest-api/middleware/validators.js';
import sinon from 'sinon';
const sinon = require('sinon') import { context as mockContext } from '../../unit/mocks/ctx-mock.js';
const mockContext = require('../../unit/mocks/ctx-mock').context import util from 'util';
const util = require('util')
util.inspect.defaultOptions = { depth: 1 } util.inspect.defaultOptions = { depth: 1 }
const context = {} const context = {}
@@ -303,3 +301,4 @@ describe('Validators', () => {
}) })
}) })
}) })
*/
+4 -7
View File
@@ -1,10 +1,7 @@
const assert = require('chai').assert import { assert } from 'chai'
import Admin from '../../../src/adapters/admin.js'
const Admin = require('../../../src/adapters/admin') import sinon from 'sinon'
import util from 'util'
const sinon = require('sinon')
const util = require('util')
util.inspect.defaultOptions = { depth: 1 } util.inspect.defaultOptions = { depth: 1 }
let sandbox let sandbox
+6 -3
View File
@@ -3,11 +3,12 @@
*/ */
// Global npm libraries // Global npm libraries
const assert = require('chai').assert import { assert } from 'chai'
const sinon = require('sinon')
import sinon from 'sinon'
// Local libraries // Local libraries
const Adapters = require('../../../src/adapters') import Adapters from '../../../src/adapters/index.js'
describe('#adapters', () => { describe('#adapters', () => {
let uut, sandbox let uut, sandbox
@@ -26,6 +27,8 @@ describe('#adapters', () => {
it('should start the async adapters', async () => { it('should start the async adapters', async () => {
// Mock dependencies // Mock dependencies
uut.config.getJwtAtStartup = true uut.config.getJwtAtStartup = true
uut.config.useIpfs = true
uut.config.env = 'not-a-test'
sandbox.stub(uut.fullStackJwt, 'getJWT').resolves() sandbox.stub(uut.fullStackJwt, 'getJWT').resolves()
sandbox.stub(uut.fullStackJwt, 'instanceBchjs').resolves() sandbox.stub(uut.fullStackJwt, 'instanceBchjs').resolves()
sandbox.stub(uut.ipfs, 'start').resolves() sandbox.stub(uut.ipfs, 'start').resolves()
+3 -4
View File
@@ -1,7 +1,6 @@
const assert = require('chai').assert import { assert } from 'chai'
const sinon = require('sinon') import sinon from 'sinon'
import ContactLib from '../../../src/adapters/contact.js'
const ContactLib = require('../../../src/adapters/contact')
let uut let uut
let sandbox let sandbox
@@ -3,10 +3,10 @@
*/ */
const assert = require('chai').assert import { assert } from 'chai'
const sinon = require('sinon')
const FullStackJWT = require('../../../src/adapters/fullstack-jwt') import sinon from 'sinon'
import FullStackJWT from '../../../src/adapters/fullstack-jwt.js'
describe('#FullStackJWT', () => { describe('#FullStackJWT', () => {
let sandbox let sandbox
+24 -9
View File
@@ -2,20 +2,20 @@
Unit tests for the IPFS Adapter. Unit tests for the IPFS Adapter.
*/ */
const assert = require('chai').assert import { assert } from 'chai'
const sinon = require('sinon')
const IPFSCoordAdapter = require('../../../src/adapters/ipfs/ipfs-coord') import sinon from 'sinon'
const IPFSMock = require('../mocks/ipfs-mock') import IPFSCoordAdapter from '../../../src/adapters/ipfs/ipfs-coord.js'
const IPFSCoordMock = require('../mocks/ipfs-coord-mock') import create from '../mocks/ipfs-mock.js'
const config = require('../../../config') import IPFSCoordMock from '../mocks/ipfs-coord-mock.js'
import config from '../../../config/index.js'
describe('#IPFS', () => { describe('#IPFS', () => {
let uut let uut
let sandbox let sandbox
beforeEach(() => { beforeEach(() => {
const ipfs = IPFSMock.create() const ipfs = create()
const bchjs = {} const bchjs = {}
uut = new IPFSCoordAdapter({ ipfs, bchjs }) uut = new IPFSCoordAdapter({ ipfs, bchjs })
@@ -40,7 +40,7 @@ describe('#IPFS', () => {
it('should throw an error if bchjs instance is not included', () => { it('should throw an error if bchjs instance is not included', () => {
try { try {
const ipfs = IPFSMock.create() const ipfs = create()
uut = new IPFSCoordAdapter({ ipfs }) uut = new IPFSCoordAdapter({ ipfs })
assert.fail('Unexpected code path') assert.fail('Unexpected code path')
@@ -67,7 +67,7 @@ describe('#IPFS', () => {
it('should get the public IP address if this node is a Circuit Relay', async () => { it('should get the public IP address if this node is a Circuit Relay', async () => {
// Mock dependencies. // Mock dependencies.
uut.IpfsCoord = IPFSCoordMock uut.IpfsCoord = IPFSCoordMock
sandbox.stub(uut.publicIp, 'v4').returns('123') sandbox.stub(uut, 'publicIp').resolves('123')
// Force Circuit Relay // Force Circuit Relay
uut.config.isCircuitRelay = true uut.config.isCircuitRelay = true
@@ -77,6 +77,21 @@ describe('#IPFS', () => {
assert.equal(result, true) assert.equal(result, true)
}) })
it('should exit quietly if this node is a Circuit Relay and there is an issue getting the IP address', async () => {
// Mock dependencies.
uut.IpfsCoord = IPFSCoordMock
sandbox.stub(uut, 'publicIp').rejects(new Error('test error'))
// Force Circuit Relay
uut.config.isCircuitRelay = true
const result = await uut.start()
// console.log('result: ', result)
assert.equal(result, true)
})
it('should return a promise that resolves into an instance of IPFS in production mode', async () => { it('should return a promise that resolves into an instance of IPFS in production mode', async () => {
uut.config.isProduction = true uut.config.isProduction = true
// Mock dependencies. // Mock dependencies.
+160 -7
View File
@@ -2,13 +2,13 @@
Unit tests for the index.js file for the IPFS and ipfs-coord libraries. Unit tests for the index.js file for the IPFS and ipfs-coord libraries.
*/ */
const assert = require('chai').assert import { assert } from 'chai'
const sinon = require('sinon') import BCHJS from '@psf/bch-js'
const BCHJS = require('@psf/bch-js')
const IPFSLib = require('../../../src/adapters/ipfs') import sinon from 'sinon'
const IPFSMock = require('../mocks/ipfs-mock') import IPFSLib from '../../../src/adapters/ipfs/index.js'
const IPFSCoordMock = require('../mocks/ipfs-coord-mock') // import create from '../mocks/ipfs-mock.js'
import IPFSCoordMock from '../mocks/ipfs-coord-mock.js'
describe('#IPFS-adapter-index', () => { describe('#IPFS-adapter-index', () => {
let uut let uut
@@ -41,7 +41,9 @@ describe('#IPFS-adapter-index', () => {
it('should return a promise that resolves into an instance of IPFS.', async () => { it('should return a promise that resolves into an instance of IPFS.', async () => {
// Mock dependencies. // Mock dependencies.
uut.ipfsAdapter = new IPFSMock() uut.ipfsAdapter = {
start: async () => {}
}
uut.IpfsCoordAdapter = IPFSCoordMock uut.IpfsCoordAdapter = IPFSCoordMock
const result = await uut.start({ bchjs: {} }) const result = await uut.start({ bchjs: {} })
@@ -81,4 +83,155 @@ describe('#IPFS-adapter-index', () => {
} }
}) })
}) })
describe('#getStatus', () => {
it('should return an object with node metrics', () => {
// Force uut to have the needed properties
uut.ipfsCoordAdapter = {
ipfsCoord: {
thisNode: {
ipfsId: 'fake-id',
ipfsMultiaddrs: [],
bchAddr: 'fake-bch-addr',
slpAddr: 'fake-slp-addr',
pubKey: 'fake-pubkey',
peerList: [],
relayData: []
}
}
}
const result = uut.getStatus()
// console.log('result: ', result)
assert.property(result, 'ipfsId')
assert.property(result, 'multiAddrs')
assert.property(result, 'bchAddr')
assert.property(result, 'slpAddr')
assert.property(result, 'pubKey')
assert.property(result, 'peers')
assert.property(result, 'relays')
})
it('should catch, report, and throw errors', () => {
try {
uut.getStatus()
assert.fail('Unexpected code path')
} catch (err) {
// console.log('err.message: ', err.message)
assert.include(err.message, 'Cannot read properties')
}
})
})
describe('#_removeDuplicatePeers', () => {
it('should return the same array when there are no duplicates', () => {
const inArray = [
'12D3KooWPpBXhhAeoCZCGuQ3KR4xwHzzvtP57f6zLmo8P7ZFBJFE',
'12D3KooWRBhwfeP2Y9CDkFRBAZ1pmxUadH36TKuk3KtKm5XXP8mA',
'12D3KooWGsgHWyDLKuV4ZSfRJfsxQJj77rxx3i8Px3qXKHsLN7a2',
'12D3KooWJyc54njjeZGbLew4D8u1ghrmZTTPyh3QpBF7dxtd3zGY',
'12D3KooWDtj9cfj1SKuLbDNKvKRKSsGN8qivq9M8CYpLPDpcD5pu'
]
const result = uut._removeDuplicatePeers(inArray)
// console.log('result: ', result)
assert.equal(result.length, inArray.length)
})
it('should remove duplicates from an array', () => {
const inArray = [
'12D3KooWPpBXhhAeoCZCGuQ3KR4xwHzzvtP57f6zLmo8P7ZFBJFE',
'12D3KooWRBhwfeP2Y9CDkFRBAZ1pmxUadH36TKuk3KtKm5XXP8mA',
'12D3KooWGsgHWyDLKuV4ZSfRJfsxQJj77rxx3i8Px3qXKHsLN7a2',
'12D3KooWJyc54njjeZGbLew4D8u1ghrmZTTPyh3QpBF7dxtd3zGY',
'12D3KooWDtj9cfj1SKuLbDNKvKRKSsGN8qivq9M8CYpLPDpcD5pu',
'12D3KooWPpBXhhAeoCZCGuQ3KR4xwHzzvtP57f6zLmo8P7ZFBJFE',
'12D3KooWRBhwfeP2Y9CDkFRBAZ1pmxUadH36TKuk3KtKm5XXP8mA'
]
const result = uut._removeDuplicatePeers(inArray)
// console.log('result: ', result)
assert.equal(result.length, inArray.length - 2)
})
})
describe('#getPeers', () => {
it('should return an array of current JSON data about each peer when showAll is true', async () => {
// Mock dependencies
uut.ipfsCoordAdapter.ipfsCoord = {
thisNode: {
peerData: [{ from: 'a', data: { jsonLd: { name: 'a', protocol: 'test', version: '1' } } }, { from: 'b' }]
},
adapters: {
ipfs: {
getPeers: async () => ['a', 'b']
}
}
}
const result = await uut.getPeers(true)
assert.isArray(result)
})
it('should catch, report, and throw errors', async () => {
try {
// Force an error
uut.ipfsCoordAdapter.ipfsCoord = {
thisNode: {},
adapters: {
ipfs: {
getPeers: async () => { throw new Error('test error') }
}
}
}
await uut.getPeers()
assert.fail('Unexpected code path')
} catch (err) {
// console.log('err.message: ', err.message)
assert.include(err.message, 'test error')
}
})
})
describe('#getRelays', () => {
it('should return known relays', () => {
uut.ipfsCoordAdapter.ipfsCoord = {
thisNode: {
peerData: [{ from: 'a', data: { jsonLd: { name: 'a', description: 'test' } } }, { from: 'b' }],
relayData: [{ ipfsId: 'c' }, { ipfsId: 'a' }]
}
}
const result = uut.getRelays()
// console.log('result: ', result)
assert.isArray(result.v2Relays)
assert.equal(result.v2Relays.length, 2)
assert.property(result.v2Relays[0], 'name')
assert.property(result.v2Relays[0], 'description')
assert.property(result.v2Relays[1], 'name')
assert.property(result.v2Relays[1], 'description')
})
it('should catch, report, and throw errors', () => {
try {
// Force an error
uut.ipfsCoordAdapter.ipfsCoord = { thisNode: {} }
uut.getRelays()
assert.fail('Unexpected code path')
} catch (err) {
// console.log('err.message: ', err.message)
assert.include(err.message, 'Cannot read')
}
})
})
}) })
+165 -41
View File
@@ -2,20 +2,29 @@
Unit tests for the IPFS Adapter. Unit tests for the IPFS Adapter.
*/ */
const assert = require('chai').assert // Global npm libraries
const sinon = require('sinon') import { assert } from 'chai'
const IPFSLib = require('../../../src/adapters/ipfs/ipfs') import sinon from 'sinon'
const IPFSMock = require('../mocks/ipfs-mock') import cloneDeep from 'lodash.clonedeep'
const config = require('../../../config') import { peerIdFromString } from '@libp2p/peer-id'
// Local libraries
import IPFSLib from '../../../src/adapters/ipfs/ipfs.js'
// import create from '../mocks/ipfs-mock.js'
import config from '../../../config/index.js'
import createHeliaLib from '../mocks/helia-mock.js'
// config.isProduction = true; // config.isProduction = true;
describe('#IPFS-adapter', () => { describe('#IPFS-adapter', () => {
let uut let uut
let sandbox let sandbox
let ipfs
beforeEach(() => { beforeEach(() => {
uut = new IPFSLib() uut = new IPFSLib()
ipfs = cloneDeep(createHeliaLib)
sandbox = sinon.createSandbox() sandbox = sinon.createSandbox()
}) })
@@ -44,35 +53,28 @@ describe('#IPFS-adapter', () => {
describe('#start', () => { describe('#start', () => {
it('should return a promise that resolves into an instance of IPFS.', async () => { it('should return a promise that resolves into an instance of IPFS.', async () => {
// Mock dependencies. // Mock dependencies.
uut.IPFS = IPFSMock sandbox.stub(uut, 'createNode').resolves(ipfs)
sandbox.stub(uut, 'publicIp').resolves('192.168.2.4')
sandbox.stub(uut, 'multiaddr').returns('/ip4/fake-multiaddr')
const result = await uut.start() const result = await uut.start()
// console.log('result: ', result) // console.log('result: ', result)
// Assert properties of the instance are set.
assert.equal(uut.isReady, true) assert.equal(uut.isReady, true)
assert.property(uut, 'multiaddrs')
assert.property(uut, 'id')
assert.property(result, 'config') // Output should be an instance of IPFS
}) assert.property(result, 'libp2p')
it('should return a promise that resolves into an instance of IPFS in production mode.', async () => {
// Mock dependencies.
uut.IPFS = IPFSMock
uut.config.isProduction = true
const result = await uut.start()
// console.log('result: ', result)
assert.equal(uut.isReady, true)
assert.property(result, 'config')
}) })
it('should catch and throw an error', async () => { it('should catch and throw an error', async () => {
try { try {
// Force an error // Force an error
sandbox.stub(uut.IPFS, 'create').rejects(new Error('test error')) sandbox.stub(uut, 'createNode').rejects(new Error('test error'))
await uut.start() await uut.start()
assert.fail('Unexpected code path.') assert.fail('Unexpected code path.')
} catch (err) { } catch (err) {
// console.log(err) // console.log(err)
@@ -95,24 +97,146 @@ describe('#IPFS-adapter', () => {
}) })
}) })
// describe('#rmBlocksDir', () => { describe('#ensureBlocksDir', () => {
// it('should delete the /blocks directory', () => { it('should create directory if it does not exist', () => {
// const result = uut.rmBlocksDir() // Force desired code path
// sandbox.stub(uut.fs, 'existsSync').returns(false)
// assert.equal(result, true) sandbox.stub(uut.fs, 'mkdirSync').returns(true)
// })
// const result = uut.ensureBlocksDir()
// it('should catch and throw an error', () => {
// try { assert.equal(result, true)
// // Force an error })
// sandbox.stub(uut.fs, 'rmdirSync').throws(new Error('test error'))
// it('should report and throw errors', () => {
// uut.rmBlocksDir() // Force an error
// sandbox.stub(uut.fs, 'existsSync').throws(new Error('test error'))
// assert.fail('Unexpected code path')
// } catch (err) { try {
// assert.equal(err.message, 'test error') uut.ensureBlocksDir()
// }
// }) assert.fail('Unexpected code path')
// }) } catch (err) {
assert.include(err.message, 'test error')
}
})
})
describe('#createNode', () => {
it('should report and throw errors', async () => {
// Force an error
sandbox.stub(uut, 'createLibp2p').rejects(new Error('test error'))
try {
await uut.createNode()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'test error')
}
})
it('should create an IPFS node from Helia', async () => {
uut.config.isCircuitRelay = false
const result = await uut.createNode()
// console.log('result: ', result)
// Assert the returned IPFS node has expected properties
assert.property(result, 'libp2p')
assert.property(result, 'blockstore')
// Stop the IPFS node
await result.stop()
})
it('should create a Circuit Relay if configured', async () => {
uut.config.isCircuitRelay = true
const result = await uut.createNode()
// Assert the returned IPFS node has expected properties
assert.property(result, 'libp2p')
assert.property(result, 'blockstore')
// Stop the IPFS node
await result.stop()
})
it('should create a new private key on first run', async () => {
uut.config.isCircuitRelay = false
// Mock dependencies and force desired code path.
let beenCalled = false
sandbox.stub(uut, 'getKeychain').resolves({
exportPeerId: async () => {
if (!beenCalled) {
beenCalled = true
throw new Error('test error')
}
return peerIdFromString('12D3KooWSXF1PnEfiA8bCG8SJduCvzdwHtvhVPK4WC6zzDoto2XP')
},
createKey: async () => {}
})
sandbox.stub(uut, 'createLibp2p').resolves()
sandbox.stub(uut, 'createHelia').resolves({})
const result = await uut.createNode()
// console.log('result: ', result)
assert.property(result, 'fs')
})
it('should not use circuit relay when CONNECT_PREF is set to direct', async () => {
process.env.CONNECT_PREF = 'direct'
uut.config.isCircuitRelay = false
const result = await uut.createNode()
// console.log('result: ', result)
delete process.env.CONNECT_PREF
// Assert the returned IPFS node has expected properties
assert.property(result, 'libp2p')
assert.property(result, 'blockstore')
// Stop the IPFS node
await result.stop()
})
})
describe('#getSeed', () => {
it('should read the seed from the JSON file', async () => {
// Mock dependencies and force desired code path
sandbox.stub(uut.jsonFiles, 'readJSON').resolves('12345678')
const result = await uut.getSeed()
// console.log('result: ', result)
assert.isString(result)
})
it('should generate a new seed if the JSON file is not found', async () => {
// Mock dependencies and force desired code path
sandbox.stub(uut.jsonFiles, 'readJSON').rejects(new Error('test error'))
sandbox.stub(uut.jsonFiles, 'writeJSON').resolves()
const result = await uut.getSeed()
// console.log('result: ', result)
assert.isString(result)
})
it('should catch, report, and throw errors', async () => {
try {
// Force an error
sandbox.stub(uut.jsonFiles, 'readJSON').rejects(new Error('test error'))
sandbox.stub(uut.jsonFiles, 'writeJSON').rejects(new Error('test error'))
await uut.getSeed()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'test error')
}
})
})
}) })
+10 -6
View File
@@ -1,11 +1,15 @@
const assert = require('chai').assert import { assert } from 'chai'
const fs = require('fs') import fs from 'fs'
const sinon = require('sinon') import sinon from 'sinon'
import util from 'util'
const util = require('util') import JsonFiles from '../../../src/adapters/json-files.js'
// Hack to get __dirname back.
// https://blog.logrocket.com/alternatives-dirname-node-js-es-modules/
import * as url from 'url'
util.inspect.defaultOptions = { depth: 1 } util.inspect.defaultOptions = { depth: 1 }
const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
const JsonFiles = require('../../../src/adapters/json-files')
const JSON_FILE = 'test-json-file.json' const JSON_FILE = 'test-json-file.json'
const JSON_PATH = `${__dirname.toString()}/${JSON_FILE}` const JSON_PATH = `${__dirname.toString()}/${JSON_FILE}`
+9 -6
View File
@@ -1,12 +1,15 @@
const assert = require('chai').assert import { assert } from 'chai'
import sinon from 'sinon'
import util from 'util'
const sinon = require('sinon') import LogsApiLib from '../../../src/adapters/logapi.js'
import mockData from '../mocks/log-api-mock.js'
const util = require('util') // Hack to get __dirname back.
// https://blog.logrocket.com/alternatives-dirname-node-js-es-modules/
import * as url from 'url'
util.inspect.defaultOptions = { depth: 1 } util.inspect.defaultOptions = { depth: 1 }
const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
const LogsApiLib = require('../../../src/adapters/logapi')
const mockData = require('../mocks/log-api-mock')
const context = {} const context = {}
let sandbox let sandbox
@@ -3,10 +3,10 @@
*/ */
// Public npm libraries // Public npm libraries
const assert = require('chai').assert import { assert } from 'chai'
const sinon = require('sinon')
const NodeMailer = require('../../../src/adapters/nodemailer') import sinon from 'sinon'
import NodeMailer from '../../../src/adapters/nodemailer.js'
let sandbox let sandbox
let uut let uut
+3 -4
View File
@@ -1,7 +1,6 @@
const assert = require('chai').assert import { assert } from 'chai'
const PassportLib = require('../../../src/adapters/passport') import PassportLib from '../../../src/adapters/passport.js'
import sinon from 'sinon'
const sinon = require('sinon')
let uut let uut
let sandbox let sandbox
+7 -6
View File
@@ -2,16 +2,17 @@
Unit tests for the users Mongoose model. Unit tests for the users Mongoose model.
*/ */
const assert = require('chai').assert import { assert } from 'chai'
const sinon = require('sinon')
const mongoose = require('mongoose') import sinon from 'sinon'
import mongoose from 'mongoose'
import User from '../../../src/adapters/localdb/models/users.js'
import config from '../../../config/index.js'
// Set the environment variable to signal this is a test. // Set the environment variable to signal this is a test.
process.env.SVC_ENV = 'test' process.env.SVC_ENV = 'test'
const User = require('../../../src/adapters/localdb/models/users')
const config = require('../../../config')
describe('#User-Adapter', () => { describe('#User-Adapter', () => {
// let uut // let uut
let sandbox let sandbox
+3 -4
View File
@@ -1,7 +1,6 @@
const assert = require('chai').assert import { assert } from 'chai'
const { Wlogger } = require('../../../src/adapters/wlogger') import { Wlogger } from '../../../src/adapters/wlogger.js'
import sinon from 'sinon'
const sinon = require('sinon')
let uut let uut
let sandbox let sandbox
+6 -3
View File
@@ -3,10 +3,10 @@
*/ */
// Public npm libraries // Public npm libraries
const assert = require('chai').assert import sinon from 'sinon'
const sinon = require('sinon')
const Controllers = require('../../../src/controllers') import Controllers from '../../../src/controllers/index.js'
const assert = require('chai').assert
describe('#Controllers', () => { describe('#Controllers', () => {
let uut let uut
@@ -33,6 +33,9 @@ describe('#Controllers', () => {
attachRPCRouter: () => {} attachRPCRouter: () => {}
} }
// Mock the timer controllers
sandbox.stub(uut.timerControllers, 'startTimers').returns()
const app = { const app = {
use: () => {} use: () => {}
} }
@@ -5,18 +5,20 @@
*/ */
// Public npm libraries // Public npm libraries
const jsonrpc = require('jsonrpc-lite') import jsonrpc from 'jsonrpc-lite'
const sinon = require('sinon')
const assert = require('chai').assert import sinon from 'sinon'
const { v4: uid } = require('uuid') import { assert } from 'chai'
import { v4 as uid } from 'uuid'
// Local libraries
import Validators from '../../../../src/controllers/json-rpc/validators.js'
import adapters from '../../mocks/adapters/index.js'
// Set the environment variable to signal this is a test. // Set the environment variable to signal this is a test.
process.env.SVC_ENV = 'test' process.env.SVC_ENV = 'test'
// Local libraries
const Validators = require('../../../../src/controllers/json-rpc/validators')
const adapters = require('../../mocks/adapters')
describe('#validators', () => { describe('#validators', () => {
let uut let uut
let sandbox let sandbox
@@ -5,15 +5,16 @@
*/ */
// Public npm libraries // Public npm libraries
const sinon = require('sinon') import sinon from 'sinon'
const assert = require('chai').assert
import { assert } from 'chai'
// Local libraries
import RateLimit from '../../../../src/controllers/json-rpc/rate-limit.js'
// Set the environment variable to signal this is a test. // Set the environment variable to signal this is a test.
process.env.SVC_ENV = 'test' process.env.SVC_ENV = 'test'
// Local libraries
const RateLimit = require('../../../../src/controllers/json-rpc/rate-limit')
describe('#rate-limit', () => { describe('#rate-limit', () => {
let uut let uut
let sandbox let sandbox
@@ -3,11 +3,12 @@
*/ */
// Public npm libraries // Public npm libraries
const sinon = require('sinon') import sinon from 'sinon'
const assert = require('chai').assert
import { assert } from 'chai'
// Local libraries // Local libraries
const AboutRPC = require('../../../../src/controllers/json-rpc/about') import AboutRPC from '../../../../src/controllers/json-rpc/about/index.js'
describe('#AboutRPC', () => { describe('#AboutRPC', () => {
let uut let uut
@@ -3,20 +3,22 @@
*/ */
// Public npm libraries // Public npm libraries
const jsonrpc = require('jsonrpc-lite') import jsonrpc from 'jsonrpc-lite'
const sinon = require('sinon')
const assert = require('chai').assert import sinon from 'sinon'
const { v4: uid } = require('uuid') import { assert } from 'chai'
import { v4 as uid } from 'uuid'
// Local libraries
import AuthRPC from '../../../../src/controllers/json-rpc/auth/index.js'
import RateLimit from '../../../../src/controllers/json-rpc/rate-limit.js'
import adapters from '../../mocks/adapters/index.js'
import UseCasesMock from '../../mocks/use-cases/index.js'
// Set the environment variable to signal this is a test. // Set the environment variable to signal this is a test.
process.env.SVC_ENV = 'test' process.env.SVC_ENV = 'test'
// Local libraries
const AuthRPC = require('../../../../src/controllers/json-rpc/auth')
const RateLimit = require('../../../../src/controllers/json-rpc/rate-limit')
const adapters = require('../../mocks/adapters')
const UseCasesMock = require('../../mocks/use-cases')
describe('#AuthRPC', () => { describe('#AuthRPC', () => {
let uut let uut
let sandbox let sandbox
@@ -3,19 +3,21 @@
*/ */
// Public npm libraries // Public npm libraries
const assert = require('chai').assert import { assert } from 'chai'
const jsonrpc = require('jsonrpc-lite')
const sinon = require('sinon') import jsonrpc from 'jsonrpc-lite'
const { v4: uid } = require('uuid') import sinon from 'sinon'
import { v4 as uid } from 'uuid'
// Local libraries.
import JSONRPC from '../../../../src/controllers/json-rpc/index.js'
import adapters from '../../mocks/adapters/index.js'
import UseCasesMock from '../../mocks/use-cases/index.js'
// Set the environment variable to signal this is a test. // Set the environment variable to signal this is a test.
process.env.SVC_ENV = 'test' process.env.SVC_ENV = 'test'
// Local libraries.
const JSONRPC = require('../../../../src/controllers/json-rpc')
const adapters = require('../../mocks/adapters')
const UseCasesMock = require('../../mocks/use-cases')
describe('#JSON RPC', () => { describe('#JSON RPC', () => {
let uut let uut
let sandbox let sandbox
@@ -3,19 +3,21 @@
*/ */
// Public npm libraries // Public npm libraries
const jsonrpc = require('jsonrpc-lite') import jsonrpc from 'jsonrpc-lite'
const sinon = require('sinon')
const assert = require('chai').assert import sinon from 'sinon'
const { v4: uid } = require('uuid') import { assert } from 'chai'
import { v4 as uid } from 'uuid'
// Local libraries
import UserRPC from '../../../../src/controllers/json-rpc/users/index.js'
import adapters from '../../mocks/adapters/index.js'
import UseCasesMock from '../../mocks/use-cases/index.js'
// Set the environment variable to signal this is a test. // Set the environment variable to signal this is a test.
process.env.SVC_ENV = 'test' process.env.SVC_ENV = 'test'
// Local libraries
const UserRPC = require('../../../../src/controllers/json-rpc/users')
const adapters = require('../../mocks/adapters')
const UseCasesMock = require('../../mocks/use-cases')
describe('#UserRPC', () => { describe('#UserRPC', () => {
let uut let uut
let sandbox let sandbox
@@ -3,21 +3,25 @@
*/ */
// Public npm libraries // Public npm libraries
const assert = require('chai').assert import { assert } from 'chai'
const sinon = require('sinon')
import sinon from 'sinon'
// Local support libraries // Local support libraries
const adapters = require('../../../mocks/adapters') import adapters from '../../../mocks/adapters/index.js'
const UseCasesMock = require('../../../mocks/use-cases')
import UseCasesMock from '../../../mocks/use-cases/index.js'
// const app = require('../../../mocks/app-mock') // const app = require('../../../mocks/app-mock')
const AuthRESTController = require('../../../../../src/controllers/rest-api/auth/controller') import AuthRESTController from '../../../../../src/controllers/rest-api/auth/controller.js'
import { context as mockContext } from '../../../../unit/mocks/ctx-mock.js'
let uut let uut
let sandbox let sandbox
let ctx let ctx
const mockContext = require('../../../../unit/mocks/ctx-mock').context
describe('#Auth-REST-Router', () => { describe('#Auth-REST-Router', () => {
// const testUser = {} // const testUser = {}
@@ -3,15 +3,19 @@
*/ */
// Public npm libraries // Public npm libraries
const assert = require('chai').assert import { assert } from 'chai'
const sinon = require('sinon')
import sinon from 'sinon'
// Local support libraries // Local support libraries
const adapters = require('../../../mocks/adapters') import adapters from '../../../mocks/adapters/index.js'
const UseCasesMock = require('../../../mocks/use-cases')
import UseCasesMock from '../../../mocks/use-cases/index.js'
// const app = require('../../../mocks/app-mock') // const app = require('../../../mocks/app-mock')
const AuthRouter = require('../../../../../src/controllers/rest-api/auth') import AuthRouter from '../../../../../src/controllers/rest-api/auth/index.js'
let uut let uut
let sandbox let sandbox
// let ctx // let ctx
@@ -3,16 +3,16 @@
*/ */
// Public npm libraries // Public npm libraries
const assert = require('chai').assert import { assert } from 'chai'
const sinon = require('sinon')
const ContactController = require('../../../../../src/controllers/rest-api/contact/controller') import sinon from 'sinon'
import ContactController from '../../../../../src/controllers/rest-api/contact/controller.js'
import { context as mockContext } from '../../../../unit/mocks/ctx-mock.js'
let uut let uut
let sandbox let sandbox
let ctx let ctx
const mockContext = require('../../../../unit/mocks/ctx-mock').context
describe('Contact', () => { describe('Contact', () => {
before(async () => { before(async () => {
}) })
@@ -3,15 +3,19 @@
*/ */
// Public npm libraries // Public npm libraries
const assert = require('chai').assert import { assert } from 'chai'
const sinon = require('sinon')
import sinon from 'sinon'
// Local support libraries // Local support libraries
const adapters = require('../../../mocks/adapters') import adapters from '../../../mocks/adapters/index.js'
const UseCasesMock = require('../../../mocks/use-cases')
import UseCasesMock from '../../../mocks/use-cases/index.js'
// const app = require('../../../mocks/app-mock') // const app = require('../../../mocks/app-mock')
const ContactRouter = require('../../../../../src/controllers/rest-api/contact') import ContactRouter from '../../../../../src/controllers/rest-api/contact/index.js'
let uut let uut
let sandbox let sandbox
// let ctx // let ctx
@@ -0,0 +1,251 @@
/*
Unit tests for the REST API handler for the /ipfs endpoints.
*/
// Public npm libraries
import { assert } from 'chai'
import sinon from 'sinon'
// Local libraries
import IpfsApiController from '../../../../../src/controllers/rest-api/ipfs/controller.js'
import adapters from '../../../mocks/adapters/index.js'
import UseCasesMock from '../../../mocks/use-cases/index.js'
import { context as mockContext } from '../../../mocks/ctx-mock.js'
let uut
let sandbox
let ctx
describe('#IPFS REST API', () => {
before(async () => {
})
beforeEach(() => {
const useCases = new UseCasesMock()
uut = new IpfsApiController({ adapters, useCases })
sandbox = sinon.createSandbox()
// Mock the context object.
ctx = mockContext()
})
afterEach(() => sandbox.restore())
describe('#constructor', () => {
it('should throw an error if adapters are not passed in', () => {
try {
uut = new IpfsApiController()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Adapters library required when instantiating /ipfs REST Controller.'
)
}
})
it('should throw an error if useCases are not passed in', () => {
try {
uut = new IpfsApiController({ adapters })
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Use Cases library required when instantiating /ipfs REST Controller.'
)
}
})
})
describe('#GET /status', () => {
it('should return 422 status on biz logic error', async () => {
try {
// Force an error
sandbox.stub(uut.adapters.ipfs, 'getStatus').rejects(new Error('test error'))
await uut.getStatus(ctx)
assert.fail('Unexpected result')
} catch (err) {
assert.equal(err.status, 422)
assert.include(err.message, 'test error')
}
})
it('should return 200 status on success', async () => {
// Mock dependencies
sandbox.stub(uut.adapters.ipfs, 'getStatus').resolves({ a: 'b' })
await uut.getStatus(ctx)
// console.log('ctx.body: ', ctx.body)
assert.property(ctx.body, 'status')
assert.equal(ctx.body.status.a, 'b')
})
})
describe('#POST /peers', () => {
it('should return 422 status on biz logic error', async () => {
try {
// Force an error
sandbox.stub(uut.adapters.ipfs, 'getPeers').rejects(new Error('test error'))
ctx.request.body = {
showAll: true
}
await uut.getPeers(ctx)
assert.fail('Unexpected result')
} catch (err) {
assert.equal(err.status, 422)
assert.include(err.message, 'test error')
}
})
it('should return 200 status on success', async () => {
// Mock dependencies
sandbox.stub(uut.adapters.ipfs, 'getPeers').resolves({ a: 'b' })
ctx.request.body = {
showAll: true
}
await uut.getPeers(ctx)
// console.log('ctx.body: ', ctx.body)
assert.property(ctx.body, 'peers')
assert.equal(ctx.body.peers.a, 'b')
})
})
describe('#POST /relays', () => {
it('should return 422 status on biz logic error', async () => {
try {
// Force an error
sandbox.stub(uut.adapters.ipfs, 'getRelays').rejects(new Error('test error'))
await uut.getRelays(ctx)
assert.fail('Unexpected result')
} catch (err) {
assert.equal(err.status, 422)
assert.include(err.message, 'test error')
}
})
it('should return 200 status on success', async () => {
// Mock dependencies
sandbox.stub(uut.adapters.ipfs, 'getRelays').resolves({ a: 'b' })
await uut.getRelays(ctx)
// console.log('ctx.body: ', ctx.body)
assert.property(ctx.body, 'relays')
assert.equal(ctx.body.relays.a, 'b')
})
})
describe('#POST /connect', () => {
it('should return 422 status on biz logic error', async () => {
try {
// Force an error
sandbox.stub(uut.adapters.ipfs.ipfsCoordAdapter.ipfsCoord.adapters.ipfs, 'connectToPeer').rejects(new Error('test error'))
ctx.request.body = {
multiaddr: '/ip4/161.35.99.207/tcp/4001/p2p/12D3KooWDtj9cfj1SKuLbDNKvKRKSsGN8qivq9M8CYpLPDpcD5pu'
}
await uut.connect(ctx)
assert.fail('Unexpected result')
} catch (err) {
assert.equal(err.status, 422)
assert.include(err.message, 'test error')
}
})
it('should return 200 status on success', async () => {
// Mock dependencies
sandbox.stub(uut.adapters.ipfs.ipfsCoordAdapter.ipfsCoord.adapters.ipfs, 'connectToPeer').resolves({ success: true })
ctx.request.body = {
multiaddr: '/ip4/161.35.99.207/tcp/4001/p2p/12D3KooWDtj9cfj1SKuLbDNKvKRKSsGN8qivq9M8CYpLPDpcD5pu'
}
await uut.connect(ctx)
// console.log('ctx.body: ', ctx.body)
assert.property(ctx.body, 'success')
assert.equal(ctx.body.success, true)
})
})
describe('#handleError', () => {
it('should still throw error if there is no message', () => {
try {
const err = {
status: 404
}
uut.handleError(ctx, err)
} catch (err) {
assert.include(err.message, 'Not Found')
}
})
it('should throw error with message', () => {
try {
const err = {
status: 422,
message: 'test error'
}
uut.handleError(ctx, err)
} catch (err) {
assert.include(err.message, 'test error')
}
})
})
describe('#getThisNode', () => {
it('should return 422 status on biz logic error', async () => {
try {
// Force an error
// sandbox.stub(uut.adapters.ipfs.ipfsCoordAdapter.ipfsCoord, 'thisNode').rejects(new Error('test error'))
uut.adapters.ipfs.ipfsCoordAdapter = {}
ctx.request.body = {}
await uut.getThisNode(ctx)
assert.fail('Unexpected result')
} catch (err) {
// console.log('err: ', err)
assert.equal(err.status, 422)
assert.include(err.message, 'Cannot read')
}
})
it('should return 200 status on success', async () => {
// Mock dependencies
// sandbox.stub(uut.adapters.ipfs.ipfsCoordAdapter.ipfsCoord.adapters.ipfs, 'connectToPeer').resolves({ success: true })
uut.adapters.ipfs.ipfsCoordAdapter = {
ipfsCoord: {
thisNode: {}
}
}
ctx.request.body = {}
await uut.getThisNode(ctx)
assert.property(ctx.body, 'thisNode')
})
})
})
@@ -0,0 +1,78 @@
/*
Unit tests for the REST API handler for the /ipfs endpoints.
*/
// Public npm libraries
import { assert } from 'chai'
import sinon from 'sinon'
// Local support libraries
import adapters from '../../../mocks/adapters/index.js'
import UseCasesMock from '../../../mocks/use-cases/index.js'
import IpfsRouter from '../../../../../src/controllers/rest-api/ipfs/index.js'
// const app = require('../../../mocks/app-mock')
let uut
let sandbox
// let ctx
// const mockContext = require('../../../../unit/mocks/ctx-mock').context
describe('#IPFS-REST-Router', () => {
// const testUser = {}
beforeEach(() => {
const useCases = new UseCasesMock()
uut = new IpfsRouter({ adapters, useCases })
sandbox = sinon.createSandbox()
// Mock the context object.
// ctx = mockContext()
})
afterEach(() => sandbox.restore())
describe('#constructor', () => {
it('should throw an error if adapters are not passed in', () => {
try {
uut = new IpfsRouter()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Adapters library required when instantiating IPFS REST Controller.'
)
}
})
it('should throw an error if useCases are not passed in', () => {
try {
uut = new IpfsRouter({ adapters })
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Use Cases library required when instantiating IPFS REST Controller.'
)
}
})
})
describe('#attach', () => {
it('should throw an error if app is not passed in.', () => {
try {
uut.attach()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Must pass app object when attaching REST API controllers.'
)
}
})
})
})
@@ -3,16 +3,16 @@
*/ */
// Public npm libraries // Public npm libraries
const assert = require('chai').assert import { assert } from 'chai'
const sinon = require('sinon')
const LogsApiController = require('../../../../../src/controllers/rest-api/logs/controller') import sinon from 'sinon'
import LogsApiController from '../../../../../src/controllers/rest-api/logs/controller.js'
import { context as mockContext } from '../../../../unit/mocks/ctx-mock.js'
let uut let uut
let sandbox let sandbox
let ctx let ctx
const mockContext = require('../../../../unit/mocks/ctx-mock').context
describe('Logapi', () => { describe('Logapi', () => {
before(async () => { before(async () => {
}) })
@@ -3,15 +3,19 @@
*/ */
// Public npm libraries // Public npm libraries
const assert = require('chai').assert import { assert } from 'chai'
const sinon = require('sinon')
import sinon from 'sinon'
// Local support libraries // Local support libraries
const adapters = require('../../../mocks/adapters') import adapters from '../../../mocks/adapters/index.js'
const UseCasesMock = require('../../../mocks/use-cases')
import UseCasesMock from '../../../mocks/use-cases/index.js'
// const app = require('../../../mocks/app-mock') // const app = require('../../../mocks/app-mock')
const LogsRouter = require('../../../../../src/controllers/rest-api/logs') import LogsRouter from '../../../../../src/controllers/rest-api/logs/index.js'
let uut let uut
let sandbox let sandbox
// let ctx // let ctx
@@ -0,0 +1,306 @@
/*
Unit tests for the REST API middleware that validates users.
*/
// Public npm libraries
import { assert } from 'chai'
import sinon from 'sinon'
// Local libraries
import Validators from '../../../../../src/controllers/rest-api/middleware/validators.js'
import { context as mockContext } from '../../../../unit/mocks/ctx-mock.js'
describe('#Validators', () => {
let uut
let ctx
let sandbox
beforeEach(() => {
uut = new Validators()
// Mock the context object.
ctx = mockContext()
sandbox = sinon.createSandbox()
})
afterEach(() => sandbox.restore())
describe('#getToken', () => {
it('should return null if no header is provided', () => {
const result = uut.getToken(ctx)
assert.equal(result, null)
})
it('should return null if header is not in two parts', () => {
ctx.request.header.authorization = 'Bearer'
const result = uut.getToken(ctx)
assert.equal(result, null)
})
it('should return null if first part of header does not container the word bearer', () => {
ctx.request.header.authorization = 'some thing'
const result = uut.getToken(ctx)
assert.equal(result, null)
})
it('should return the JWT token from the header', () => {
const jwt = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYzMjNiNTUwNzgxYWYzNTc4YzI0ZmU5YiIsImlhdCI6MTY2MzQ0NDczNCwiZXhwIjoxNjYzNTMxMTM0fQ.BY5sOfXc4z5axS98CdTfyqnO9y2wijOlwnv52rcvxHA'
ctx.request.header.authorization = `Bearer ${jwt}`
const result = uut.getToken(ctx)
assert.equal(result, jwt)
})
})
describe('#ensureUser', () => {
it('should throw error if token is not provided', async () => {
try {
// Mock dependencies and force desired code path
sandbox.stub(uut, 'getToken').returns()
await uut.ensureUser(ctx)
assert.fail('Unexpected result')
} catch (err) {
assert.equal(ctx.status, 401)
}
})
it('should throw error if token can not be verified', async () => {
try {
// Mock dependencies and force desired code path
sandbox.stub(uut, 'getToken').returns('fake-jwt')
sandbox.stub(uut.jwt, 'verify').throws(new Error('test error'))
await uut.ensureUser(ctx)
assert.fail('Unexpected result')
} catch (err) {
assert.equal(ctx.status, 401)
}
})
it('should throw error if user can not be found in database', async () => {
try {
// Mock dependencies and force desired code path
sandbox.stub(uut, 'getToken').returns('fake-jwt')
sandbox.stub(uut.jwt, 'verify').returns({})
sandbox.stub(uut.User, 'findById').resolves(false)
await uut.ensureUser(ctx)
assert.fail('Unexpected result')
} catch (err) {
assert.equal(ctx.status, 401)
}
})
it('should return true if the user is verified', async () => {
// Mock dependencies and force desired code path
sandbox.stub(uut, 'getToken').returns('fake-jwt')
sandbox.stub(uut.jwt, 'verify').returns({})
sandbox.stub(uut.User, 'findById').resolves({ user: 'alice' })
const result = await uut.ensureUser(ctx)
assert.equal(result, true)
})
})
describe('#ensureUser', () => {
it('should throw error if token is not provided', async () => {
try {
// Mock dependencies and force desired code path
sandbox.stub(uut, 'getToken').returns()
await uut.ensureAdmin(ctx)
assert.fail('Unexpected result')
} catch (err) {
assert.equal(ctx.status, 401)
}
})
it('should throw error if token can not be verified', async () => {
try {
// Mock dependencies and force desired code path
sandbox.stub(uut, 'getToken').returns('fake-jwt')
sandbox.stub(uut.jwt, 'verify').throws(new Error('test error'))
await uut.ensureAdmin(ctx)
assert.fail('Unexpected result')
} catch (err) {
assert.equal(ctx.status, 401)
}
})
it('should throw error if user can not be found in database', async () => {
try {
// Mock dependencies and force desired code path
sandbox.stub(uut, 'getToken').returns('fake-jwt')
sandbox.stub(uut.jwt, 'verify').returns({})
sandbox.stub(uut.User, 'findById').resolves(false)
await uut.ensureAdmin(ctx)
assert.fail('Unexpected result')
} catch (err) {
assert.equal(ctx.status, 401)
}
})
it('should throw error if user is not an admin', async () => {
try {
// Mock dependencies and force desired code path
sandbox.stub(uut, 'getToken').returns('fake-jwt')
sandbox.stub(uut.jwt, 'verify').returns({})
sandbox.stub(uut.User, 'findById').resolves({ type: 'user' })
await uut.ensureAdmin(ctx)
assert.fail('Unexpected result')
} catch (err) {
assert.equal(ctx.status, 401)
}
})
it('should return true if the user is an admin', async () => {
// Mock dependencies and force desired code path
sandbox.stub(uut, 'getToken').returns('fake-jwt')
sandbox.stub(uut.jwt, 'verify').returns({})
sandbox.stub(uut.User, 'findById').resolves({ type: 'admin' })
const result = await uut.ensureAdmin(ctx)
assert.equal(result, true)
})
})
describe('#ensureTargetUserOrAdmin', () => {
it('should throw error if token is not provided', async () => {
try {
// Mock dependencies and force desired code path
sandbox.stub(uut, 'getToken').returns()
await uut.ensureTargetUserOrAdmin(ctx)
assert.fail('Unexpected result')
} catch (err) {
assert.equal(ctx.status, 401)
}
})
it('should throw error if token can not be verified', async () => {
try {
// Mock dependencies and force desired code path
sandbox.stub(uut, 'getToken').returns('fake-jwt')
sandbox.stub(uut.jwt, 'verify').throws(new Error('test error'))
ctx.params = {
id: '456'
}
await uut.ensureTargetUserOrAdmin(ctx)
assert.fail('Unexpected result')
} catch (err) {
assert.equal(ctx.status, 401)
}
})
it('should throw error if user can not be found in database', async () => {
try {
// Mock dependencies and force desired code path
sandbox.stub(uut, 'getToken').returns('fake-jwt')
sandbox.stub(uut.jwt, 'verify').returns({})
sandbox.stub(uut.User, 'findById').resolves(false)
ctx.params = {
id: '456'
}
await uut.ensureTargetUserOrAdmin(ctx)
assert.fail('Unexpected result')
} catch (err) {
assert.equal(ctx.status, 401)
}
})
it('should throw error if user is not an admin', async () => {
try {
// Mock dependencies and force desired code path
sandbox.stub(uut, 'getToken').returns('fake-jwt')
sandbox.stub(uut.jwt, 'verify').returns({})
sandbox.stub(uut.User, 'findById').resolves({ type: 'user' })
ctx.params = {
id: '456'
}
await uut.ensureTargetUserOrAdmin(ctx)
assert.fail('Unexpected result')
} catch (err) {
assert.equal(ctx.status, 401)
}
})
it('should throw error is user is not admin or target user', async () => {
try {
// Mock dependencies and force desired code path
sandbox.stub(uut, 'getToken').returns('fake-jwt')
sandbox.stub(uut.jwt, 'verify').returns({})
sandbox.stub(uut.User, 'findById').resolves({ type: 'user', _id: '123' })
ctx.params = {
id: '456'
}
await uut.ensureTargetUserOrAdmin(ctx)
assert.fail('Unexpected result')
} catch (err) {
assert.equal(ctx.status, 401)
}
})
it('should return true if the user is an admin', async () => {
// Mock dependencies and force desired code path
sandbox.stub(uut, 'getToken').returns('fake-jwt')
sandbox.stub(uut.jwt, 'verify').returns({})
sandbox.stub(uut.User, 'findById').resolves({ type: 'admin', _id: '123' })
ctx.params = {
id: '456'
}
const result = await uut.ensureTargetUserOrAdmin(ctx)
assert.equal(result, true)
})
it('should return true if the user is the target user', async () => {
// Mock dependencies and force desired code path
sandbox.stub(uut, 'getToken').returns('fake-jwt')
sandbox.stub(uut.jwt, 'verify').returns({})
sandbox.stub(uut.User, 'findById').resolves({ type: 'user', _id: '123' })
ctx.params = {
id: '123'
}
const result = await uut.ensureTargetUserOrAdmin(ctx)
assert.equal(result, true)
})
})
})
@@ -3,14 +3,17 @@
*/ */
// Public npm libraries // Public npm libraries
const assert = require('chai').assert import { assert } from 'chai'
const sinon = require('sinon')
import sinon from 'sinon'
// Local libraries // Local libraries
const RESTControllers = require('../../../../src/controllers/rest-api/') import RESTControllers from '../../../../src/controllers/rest-api/index.js'
// const mockContext = require('../../../unit/mocks/ctx-mock').context // const mockContext = require('../../../unit/mocks/ctx-mock').context
const adapters = require('../../mocks/adapters') import adapters from '../../mocks/adapters/index.js'
const UseCasesMock = require('../../mocks/use-cases')
import UseCasesMock from '../../mocks/use-cases/index.js'
describe('#RESTControllers', () => { describe('#RESTControllers', () => {
let uut let uut
@@ -3,20 +3,19 @@
*/ */
// Public npm libraries // Public npm libraries
const assert = require('chai').assert import { assert } from 'chai'
const sinon = require('sinon') import sinon from 'sinon'
// Local support libraries // Local support libraries
const adapters = require('../../../mocks/adapters') import adapters from '../../../mocks/adapters/index.js'
const UseCasesMock = require('../../../mocks/use-cases') import UseCasesMock from '../../../mocks/use-cases/index.js'
import UserController from '../../../../../src/controllers/rest-api/users/controller.js'
const UserController = require('../../../../../src/controllers/rest-api/users/controller') import { context as mockContext } from '../../../../unit/mocks/ctx-mock.js'
let uut let uut
let sandbox let sandbox
let ctx let ctx
const mockContext = require('../../../../unit/mocks/ctx-mock').context
describe('#Users-REST-Controller', () => { describe('#Users-REST-Controller', () => {
// const testUser = {} // const testUser = {}
@@ -3,15 +3,19 @@
*/ */
// Public npm libraries // Public npm libraries
const assert = require('chai').assert import { assert } from 'chai'
const sinon = require('sinon')
import sinon from 'sinon'
// Local support libraries // Local support libraries
const adapters = require('../../../mocks/adapters') import adapters from '../../../mocks/adapters/index.js'
const UseCasesMock = require('../../../mocks/use-cases')
import UseCasesMock from '../../../mocks/use-cases/index.js'
// const app = require('../../../mocks/app-mock') // const app = require('../../../mocks/app-mock')
const UserRouter = require('../../../../../src/controllers/rest-api/users') import UserRouter from '../../../../../src/controllers/rest-api/users/index.js'
let uut let uut
let sandbox let sandbox
// let ctx // let ctx
@@ -0,0 +1,82 @@
/*
Unit tests for the timer-controller.js Controller library
*/
// Public npm libraries
import { assert } from 'chai'
import sinon from 'sinon'
// Local libraries
import TimerControllers from '../../../src/controllers/timer-controllers.js'
import adapters from '../mocks/adapters/index.js'
import UseCasesMock from '../mocks/use-cases/index.js'
describe('#Timer-Controllers', () => {
let uut
let sandbox
beforeEach(() => {
sandbox = sinon.createSandbox()
const useCases = new UseCasesMock()
uut = new TimerControllers({ adapters, useCases })
})
afterEach(() => {
sandbox.restore()
uut.stopTimers()
})
describe('#constructor', () => {
it('should throw an error if adapters are not passed in', () => {
try {
uut = new TimerControllers()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Adapters library required when instantiating Timer Controller libraries.'
)
}
})
it('should throw an error if useCases are not passed in', () => {
try {
uut = new TimerControllers({ adapters })
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Use Cases library required when instantiating Timer Controller libraries.'
)
}
})
})
describe('#startTimers', () => {
it('should start the timers', () => {
const result = uut.startTimers()
uut.stopTimers()
assert.equal(result, true)
})
})
describe('#exampleTimerFunc', () => {
it('should kick off the Use Case', async () => {
const result = await uut.exampleTimerFunc()
assert.equal(result, true)
})
it('should return false on error', async () => {
const result = await uut.exampleTimerFunc(true)
assert.equal(result, false)
})
})
})
+3 -3
View File
@@ -2,10 +2,10 @@
Unit tests for the User entity library. Unit tests for the User entity library.
*/ */
const assert = require('chai').assert import { assert } from 'chai'
const sinon = require('sinon')
const User = require('../../../src/entities/user') import sinon from 'sinon'
import User from '../../../src/entities/user.js'
let sandbox let sandbox
let uut let uut
+50
View File
@@ -0,0 +1,50 @@
/*
Unit tests for the config directory
*/
import { assert } from 'chai'
let currentEnv
describe('#config', () => {
before(() => {
// Backup the current environment setting.
currentEnv = process.env.SVC_ENV
})
after(() => {
// Restore the environment setting before starting these tests.
process.env.SVC_ENV = currentEnv
})
it('Should return development environment config by default', async () => {
const importedConfig = await import('../../../config/index.js')
const config = importedConfig.default
// console.log('config: ', config)
assert.equal(config.env, 'dev')
})
it('Should return test environment config', async () => {
// Hack to dynamically import a library multiple times:
// https://github.com/denoland/deno/issues/6946
process.env.SVC_ENV = 'test'
const importedConfig2 = await import('../../../config/index.js?foo=bar1')
const config = importedConfig2.default
// console.log('config: ', config)
assert.equal(config.env, 'test')
})
it('Should return test environment config', async () => {
process.env.SVC_ENV = 'prod'
const importedConfig3 = await import('../../../config/index.js?foo=bar2')
const config = importedConfig3.default
// console.log('config: ', config)
assert.equal(config.env, 'prod')
})
})
+48 -32
View File
@@ -3,13 +3,15 @@
*/ */
// Public npm libraries // Public npm libraries
// const assert = require('chai').assert import { assert } from 'chai'
const sinon = require('sinon') import sinon from 'sinon'
import passport from 'koa-passport'
// Local libraries // Local libraries
const User = require('../../../src/adapters/localdb/models/users') import User from '../../../src/adapters/localdb/models/users.js'
const { passport, passportCallback } = require('../../../config/passport')
const adaptersMock = require('../mocks/adapters') import { applyPassportMods, passportCallback } from '../../../config/passport.js'
import adaptersMock from '../mocks/adapters/index.js'
describe('#passport', () => { describe('#passport', () => {
let sandbox let sandbox
@@ -25,33 +27,6 @@ describe('#passport', () => {
afterEach(() => sandbox.restore()) afterEach(() => sandbox.restore())
describe('#serializeUser', () => {
it('should serialize a user', () => {
const user = {
id: 'abc123'
}
const done = () => {}
passport.serializeUser(user, done)
})
})
describe('#deserializeUser', () => {
it('should deserialize a user', () => {
// Mock Users model.
sandbox.stub(User, 'findById').resolves({ id })
passport.deserializeUser(id, done)
})
it('should catch and handle errors', () => {
// Force an error
sandbox.stub(User, 'findById').rejects(new Error('test error'))
passport.deserializeUser(id, done)
})
})
describe('#passportCallback', () => { describe('#passportCallback', () => {
it('should return if user is found', () => { it('should return if user is found', () => {
// Mock Users model. // Mock Users model.
@@ -74,4 +49,45 @@ describe('#passport', () => {
passportCallback(id, 'password', done) passportCallback(id, 'password', done)
}) })
}) })
describe('#applyPassportMods', () => {
it('should apply modifications to default passport behavior', () => {
const result = applyPassportMods(passport)
assert.equal(result, true)
})
})
describe('#serializeUser', () => {
it('should serialize a user', () => {
const user = {
id: 'abc123'
}
const done = () => {}
applyPassportMods(passport)
passport.serializeUser(user, done)
})
})
describe('#deserializeUser', () => {
it('should deserialize a user', () => {
// Mock Users model.
sandbox.stub(User, 'findById').resolves({ id })
applyPassportMods(passport)
passport.deserializeUser(id, done)
})
it('should catch and handle errors', () => {
// Force an error
sandbox.stub(User, 'findById').rejects(new Error('test error'))
applyPassportMods(passport)
passport.deserializeUser(id, done)
})
})
}) })
+3 -3
View File
@@ -3,11 +3,11 @@
*/ */
// Public npm libraries // Public npm libraries
const assert = require('chai').assert import { assert } from 'chai'
const sinon = require('sinon') import sinon from 'sinon'
// Local libraries // Local libraries
const Server = require('../../../bin/server') import Server from '../../../bin/server.js'
describe('#server', () => { describe('#server', () => {
let uut, sandbox let uut, sandbox
+12 -3
View File
@@ -15,18 +15,27 @@ class IpfsAdapter {
class IpfsCoordAdapter { class IpfsCoordAdapter {
constructor () { constructor () {
this.ipfsCoord = { this.ipfsCoord = {
adapters: {
ipfs: {
connectToPeer: async () => {}
}
},
useCases: { useCases: {
peer: { peer: {
sendPrivateMessage: () => {} sendPrivateMessage: () => {}
} }
} },
thisNode: {}
} }
} }
} }
const ipfs = { const ipfs = {
ipfsAdapter: new IpfsAdapter(), ipfsAdapter: new IpfsAdapter(),
ipfsCoordAdapter: new IpfsCoordAdapter() ipfsCoordAdapter: new IpfsCoordAdapter(),
getStatus: async () => {},
getPeers: async () => {},
getRelays: async () => {}
} }
ipfs.ipfs = ipfs.ipfsAdapter.ipfs ipfs.ipfs = ipfs.ipfsAdapter.ipfs
@@ -94,4 +103,4 @@ const bchjs = {
} }
} }
module.exports = { ipfs, localdb, bchjs } export default { ipfs, localdb, bchjs };
+1 -1
View File
@@ -6,4 +6,4 @@ const app = {
use: () => {} use: () => {}
} }
module.exports = app export default app;

Some files were not shown because too many files have changed in this diff Show More