mirror of
https://github.com/Permissionless-Software-Foundation/ipfs-bch-wallet-service.git
synced 2026-09-21 16:52:03 -07:00
feat(adapters): Refactored the adapters into a class
This commit is contained in:
+1
-1
@@ -55,7 +55,7 @@ async function startServer () {
|
||||
// Attach REST API and JSON RPC controllers to the app.
|
||||
const Controllers = require('../src/controllers')
|
||||
const controllers = new Controllers()
|
||||
controllers.attachControllers(app)
|
||||
await controllers.attachControllers(app)
|
||||
|
||||
// Enable CORS for testing
|
||||
// THIS IS A SECURITY RISK. COMMENT OUT FOR PRODUCTION
|
||||
|
||||
Vendored
+15
@@ -31,6 +31,21 @@ module.exports = {
|
||||
? process.env.EMAILPASS
|
||||
: 'emailpassword',
|
||||
|
||||
// FullStack.cash account information, used for automatic JWT handling.
|
||||
getJwtAtStartup: false,
|
||||
authServer: process.env.AUTHSERVER
|
||||
? process.env.AUTHSERVER
|
||||
: 'https://auth.fullstack.cash',
|
||||
apiServer: process.env.APISERVER
|
||||
? process.env.APISERVER
|
||||
: 'https://api.fullstack.cash/v5/',
|
||||
fullstackLogin: process.env.FULLSTACKLOGIN
|
||||
? process.env.FULLSTACKLOGIN
|
||||
: 'demo@demo.com',
|
||||
fullstackPassword: process.env.FULLSTACKPASS
|
||||
? process.env.FULLSTACKPASS
|
||||
: 'demo',
|
||||
|
||||
// IPFS settings.
|
||||
isCircuitRelay: process.env.ENABLE_CIRCUIT_RELAY ? true : false,
|
||||
|
||||
|
||||
Generated
+26
-9
@@ -1,14 +1,14 @@
|
||||
{
|
||||
"name": "ipfs-service-provider",
|
||||
"version": "1.0.0",
|
||||
"version": "1.3.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"version": "1.0.0",
|
||||
"version": "1.3.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@psf/bch-js": "^4.20.1",
|
||||
"@psf/bch-js": "^4.20.3",
|
||||
"axios": "^0.21.1",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"glob": "^7.1.6",
|
||||
@@ -16,6 +16,7 @@
|
||||
"ipfs-coord": "^3.2.0",
|
||||
"jsonrpc-lite": "^2.2.0",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"jwt-bch-lib": "^1.3.0",
|
||||
"kcors": "^2.2.2",
|
||||
"koa": "^2.13.1",
|
||||
"koa-bodyparser": "^4.3.0",
|
||||
@@ -1649,9 +1650,9 @@
|
||||
"integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA="
|
||||
},
|
||||
"node_modules/@psf/bch-js": {
|
||||
"version": "4.20.1",
|
||||
"resolved": "https://registry.npmjs.org/@psf/bch-js/-/bch-js-4.20.1.tgz",
|
||||
"integrity": "sha512-U/saVHfHA0535w5Z68mHH+8emPJE6LulTcX40kWBjk3Qd3etb9S9j5aqxK0axcwN0s5o2O7AYkfF0NFwPEoa9Q==",
|
||||
"version": "4.20.3",
|
||||
"resolved": "https://registry.npmjs.org/@psf/bch-js/-/bch-js-4.20.3.tgz",
|
||||
"integrity": "sha512-GLuRhTnqVg+cyydEXf+Y2wNOHyJ1+q2/nyt283D9Xq9tQodGBxfT9tydhktWoLVuzlOujdiLxL4KwXDHeaUkhQ==",
|
||||
"dependencies": {
|
||||
"@psf/bip21": "^2.0.1",
|
||||
"@psf/bip32-utils": "^1.0.0",
|
||||
@@ -13544,6 +13545,14 @@
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/jwt-bch-lib": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/jwt-bch-lib/-/jwt-bch-lib-1.3.0.tgz",
|
||||
"integrity": "sha512-CZs3jUHJlWvConvoTeLDogDEwUysKeojYJxfUhbwJKJrtDCSfi3ZWbsZZ8WN30Xs28aZPDB+qlHPJlTXQmup0w==",
|
||||
"dependencies": {
|
||||
"axios": "^0.21.1"
|
||||
}
|
||||
},
|
||||
"node_modules/k-bucket": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/k-bucket/-/k-bucket-5.1.0.tgz",
|
||||
@@ -27603,9 +27612,9 @@
|
||||
"integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA="
|
||||
},
|
||||
"@psf/bch-js": {
|
||||
"version": "4.20.1",
|
||||
"resolved": "https://registry.npmjs.org/@psf/bch-js/-/bch-js-4.20.1.tgz",
|
||||
"integrity": "sha512-U/saVHfHA0535w5Z68mHH+8emPJE6LulTcX40kWBjk3Qd3etb9S9j5aqxK0axcwN0s5o2O7AYkfF0NFwPEoa9Q==",
|
||||
"version": "4.20.3",
|
||||
"resolved": "https://registry.npmjs.org/@psf/bch-js/-/bch-js-4.20.3.tgz",
|
||||
"integrity": "sha512-GLuRhTnqVg+cyydEXf+Y2wNOHyJ1+q2/nyt283D9Xq9tQodGBxfT9tydhktWoLVuzlOujdiLxL4KwXDHeaUkhQ==",
|
||||
"requires": {
|
||||
"@psf/bip21": "^2.0.1",
|
||||
"@psf/bip32-utils": "^1.0.0",
|
||||
@@ -37767,6 +37776,14 @@
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"jwt-bch-lib": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/jwt-bch-lib/-/jwt-bch-lib-1.3.0.tgz",
|
||||
"integrity": "sha512-CZs3jUHJlWvConvoTeLDogDEwUysKeojYJxfUhbwJKJrtDCSfi3ZWbsZZ8WN30Xs28aZPDB+qlHPJlTXQmup0w==",
|
||||
"requires": {
|
||||
"axios": "^0.21.1"
|
||||
}
|
||||
},
|
||||
"k-bucket": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/k-bucket/-/k-bucket-5.1.0.tgz",
|
||||
|
||||
+2
-1
@@ -23,7 +23,7 @@
|
||||
},
|
||||
"repository": "Permissionless-Software-Foundation/ipfs-service-provider",
|
||||
"dependencies": {
|
||||
"@psf/bch-js": "^4.20.1",
|
||||
"@psf/bch-js": "^4.20.3",
|
||||
"axios": "^0.21.1",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"glob": "^7.1.6",
|
||||
@@ -31,6 +31,7 @@
|
||||
"ipfs-coord": "^3.2.0",
|
||||
"jsonrpc-lite": "^2.2.0",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"jwt-bch-lib": "^1.3.0",
|
||||
"kcors": "^2.2.2",
|
||||
"koa": "^2.13.1",
|
||||
"koa-bodyparser": "^4.3.0",
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
A library of utility functions for working with FullStack.cash JWT tokens.
|
||||
|
||||
Feel free to copy this library into your own app, as well as the unit tests
|
||||
for this file.
|
||||
*/
|
||||
|
||||
const JwtLib = require('jwt-bch-lib')
|
||||
const BCHJS = require('@psf/bch-js')
|
||||
|
||||
class FullStackJWT {
|
||||
constructor (localConfig = {}) {
|
||||
// Input Validation
|
||||
this.authServer = localConfig.authServer
|
||||
if (!this.authServer || typeof this.authServer !== 'string') {
|
||||
throw new Error(
|
||||
'Must pass a url for the AUTH server when instantiating FullStackJWT class.'
|
||||
)
|
||||
}
|
||||
this.apiServer = localConfig.apiServer
|
||||
if (!this.apiServer || typeof this.apiServer !== 'string') {
|
||||
throw new Error(
|
||||
'Must pass a url for the API server when instantiating FullStackJWT class.'
|
||||
)
|
||||
}
|
||||
this.login = localConfig.fullstackLogin
|
||||
if (!this.login || typeof this.login !== 'string') {
|
||||
throw new Error(
|
||||
'Must pass a FullStack.cash login (email) instantiating FullStackJWT class.'
|
||||
)
|
||||
}
|
||||
this.password = localConfig.fullstackPassword
|
||||
if (!this.password || typeof this.password !== 'string') {
|
||||
throw new Error(
|
||||
'Must pass a FullStack.cash account password when instantiating FullStackJWT class.'
|
||||
)
|
||||
}
|
||||
|
||||
// Encapsulate dependencies
|
||||
this.jwtLib = new JwtLib({
|
||||
// Overwrite default values with the values in the config file.
|
||||
server: this.authServer,
|
||||
login: this.login,
|
||||
password: this.password
|
||||
})
|
||||
|
||||
// State
|
||||
this.apiToken = '' // Default value.
|
||||
this.bchjs = {}
|
||||
}
|
||||
|
||||
// Get's a JWT token from FullStack.cash.
|
||||
async getJWT () {
|
||||
try {
|
||||
// Skip connecting FullStack.cash auth server to the network if this is an E2E test.
|
||||
if (process.env.TEST_TYPE === 'e2e') {
|
||||
this.apiToken = 'faketoken'
|
||||
return this.apiToken
|
||||
}
|
||||
|
||||
// Log into the auth server.
|
||||
await this.jwtLib.register()
|
||||
|
||||
this.apiToken = this.jwtLib.userData.apiToken
|
||||
if (!this.apiToken) {
|
||||
throw new Error('This account does not have a JWT')
|
||||
}
|
||||
console.log(`Retrieved JWT token: ${this.apiToken}\n`)
|
||||
|
||||
// Ensure the JWT token is valid to use.
|
||||
const isValid = await this.jwtLib.validateApiToken()
|
||||
|
||||
// Get a new token with the same API level, if the existing token is not
|
||||
// valid (probably expired).
|
||||
if (!isValid.isValid) {
|
||||
this.apiToken = await this.jwtLib.getApiToken(
|
||||
this.jwtLib.userData.apiLevel
|
||||
)
|
||||
console.log(
|
||||
`The JWT token was not valid. Retrieved new JWT token: ${this.apiToken}\n`
|
||||
)
|
||||
} else {
|
||||
console.log('JWT token is valid.\n')
|
||||
}
|
||||
|
||||
return this.apiToken
|
||||
} catch (err) {
|
||||
console.error(
|
||||
`Error trying to log into ${this.server} and retrieve JWT token.`
|
||||
)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Create an instance of bchjs with the validated JWT token. Returns this
|
||||
// instance of bch-js.
|
||||
instanceBchjs () {
|
||||
this.bchjs = new BCHJS({
|
||||
restURL: this.apiServer,
|
||||
apiToken: this.apiToken
|
||||
})
|
||||
|
||||
return this.bchjs
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = FullStackJWT
|
||||
+41
-16
@@ -4,29 +4,54 @@
|
||||
https://troutsblog.com/blog/clean-architecture
|
||||
*/
|
||||
|
||||
// Public NPM libraries
|
||||
const BCHJS = require('@psf/bch-js')
|
||||
|
||||
// Load individual adapter libraries.
|
||||
const IPFSAdapter = require('./ipfs')
|
||||
const LocalDB = require('./localdb')
|
||||
const LogsAPI = require('./logapi')
|
||||
const Passport = require('./passport')
|
||||
const Nodemailer = require('./nodemailer')
|
||||
const { wlogger } = require('./wlogger')
|
||||
// const { wlogger } = require('./wlogger')
|
||||
const JSONFiles = require('./json-files')
|
||||
const FullStackJWT = require('./fullstack-jwt')
|
||||
|
||||
// Instantiate adapter libraries.
|
||||
const ipfs = new IPFSAdapter()
|
||||
const localdb = new LocalDB()
|
||||
const logapi = new LogsAPI()
|
||||
const passport = new Passport()
|
||||
const nodemailer = new Nodemailer()
|
||||
const jsonFiles = new JSONFiles()
|
||||
const config = require('../../config')
|
||||
|
||||
module.exports = {
|
||||
ipfs,
|
||||
localdb,
|
||||
logapi,
|
||||
passport,
|
||||
nodemailer,
|
||||
wlogger,
|
||||
jsonFiles
|
||||
class Adapters {
|
||||
constructor (localConfig = {}) {
|
||||
// Encapsulate dependencies
|
||||
this.ipfs = new IPFSAdapter()
|
||||
this.localdb = new LocalDB()
|
||||
this.logapi = new LogsAPI()
|
||||
this.passport = new Passport()
|
||||
this.nodemailer = new Nodemailer()
|
||||
this.jsonFiles = new JSONFiles()
|
||||
this.bchjs = new BCHJS()
|
||||
this.config = config
|
||||
|
||||
// Get a valid JWT API key and instance bch-js.
|
||||
this.fullStackJwt = new FullStackJWT(config)
|
||||
}
|
||||
|
||||
async start () {
|
||||
try {
|
||||
if (this.config.getJwtAtStartup) {
|
||||
// Get a JWT token and instantiate bch-js with it. Then pass that instance
|
||||
// to all the rest of the apps controllers and adapters.
|
||||
await this.fullStackJwt.getJWT()
|
||||
// Instantiate bch-js with the JWT token, and overwrite the placeholder for bch-js.
|
||||
this.bchjs = await this.fullStackJwt.instanceBchjs()
|
||||
}
|
||||
|
||||
// Start the IPFS node.
|
||||
await this.ipfs.start()
|
||||
} catch (err) {
|
||||
console.error('Error in adapters/index.js/start()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Adapters
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
// Public npm libraries.
|
||||
|
||||
// Load the Clean Architecture Adapters library
|
||||
const adapters = require('../adapters')
|
||||
const Adapters = require('../adapters')
|
||||
|
||||
// Load the JSON RPC Controller.
|
||||
const JSONRPC = require('./json-rpc')
|
||||
@@ -21,17 +21,17 @@ const RESTControllers = require('./rest-api')
|
||||
|
||||
class Controllers {
|
||||
constructor (localConfig = {}) {
|
||||
this.adapters = adapters
|
||||
this.useCases = new UseCases({ adapters })
|
||||
this.adapters = new Adapters()
|
||||
this.useCases = new UseCases({ adapters: this.adapters })
|
||||
}
|
||||
|
||||
async attachControllers (app) {
|
||||
// Wait for any startup processes to complete for the Adapters libraries.
|
||||
await this.adapters.start()
|
||||
|
||||
// Attach the REST controllers to the Koa app.
|
||||
this.attachRESTControllers(app)
|
||||
|
||||
// Start IPFS.
|
||||
await this.adapters.ipfs.start()
|
||||
|
||||
this.attachRPCControllers()
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
Unit tests for the jwt-bch-lib and fullstack-jwt.js adapter library.
|
||||
|
||||
*/
|
||||
|
||||
const assert = require('chai').assert
|
||||
const sinon = require('sinon')
|
||||
|
||||
const FullStackJWT = require('../../../src/adapters/fullstack-jwt')
|
||||
|
||||
describe('#FullStackJWT', () => {
|
||||
let sandbox
|
||||
let uut
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox()
|
||||
|
||||
const localConfig = {
|
||||
authServer: 'someserver',
|
||||
apiServer: 'someserver',
|
||||
login: 'somelogin',
|
||||
password: 'somepassword'
|
||||
}
|
||||
uut = new FullStackJWT(localConfig)
|
||||
})
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('#constructor', () => {
|
||||
it('should throw an error if auth server is not specified', () => {
|
||||
try {
|
||||
uut = new FullStackJWT()
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
console.log(uut) // For linting.
|
||||
} catch (err) {
|
||||
assert.include(
|
||||
err.message,
|
||||
'Must pass a url for the AUTH server when instantiating FullStackJWT class.'
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw an error if api server is not specified', () => {
|
||||
try {
|
||||
const localConfig = {
|
||||
authServer: 'someserver'
|
||||
}
|
||||
|
||||
uut = new FullStackJWT(localConfig)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
console.log(uut) // For linting.
|
||||
} catch (err) {
|
||||
assert.include(
|
||||
err.message,
|
||||
'Must pass a url for the API server when instantiating FullStackJWT class.'
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw an error if login is not specified', () => {
|
||||
try {
|
||||
const localConfig = {
|
||||
authServer: 'someserver',
|
||||
apiServer: 'someserver'
|
||||
}
|
||||
uut = new FullStackJWT(localConfig)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
console.log(uut) // For linting.
|
||||
} catch (err) {
|
||||
assert.include(
|
||||
err.message,
|
||||
'Must pass a FullStack.cash login (email) instantiating FullStackJWT class.'
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw an error if login is not specified', () => {
|
||||
try {
|
||||
const localConfig = {
|
||||
authServer: 'someserver',
|
||||
apiServer: 'someserver',
|
||||
login: 'somelogin'
|
||||
}
|
||||
uut = new FullStackJWT(localConfig)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
console.log(uut) // For linting.
|
||||
} catch (err) {
|
||||
assert.include(
|
||||
err.message,
|
||||
'Must pass a FullStack.cash account password when instantiating FullStackJWT class.'
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#getJWT', () => {
|
||||
it('should return the JWT token', async () => {
|
||||
// Mock dependencies to force a code path.
|
||||
sandbox.stub(uut.jwtLib, 'register').resolves({})
|
||||
uut.jwtLib.userData.apiToken = 'abc123'
|
||||
sandbox.stub(uut.jwtLib, 'validateApiToken').resolves({ isValid: true })
|
||||
|
||||
const result = await uut.getJWT()
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.equal(result, 'abc123')
|
||||
})
|
||||
|
||||
it('should catch and throw an error', async () => {
|
||||
try {
|
||||
// Force an error
|
||||
sandbox.stub(uut.jwtLib, 'register').rejects(new Error('test error'))
|
||||
|
||||
await uut.getJWT()
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw an error if user does not have a JWT', async () => {
|
||||
try {
|
||||
// Mock dependencies to force a code path.
|
||||
sandbox.stub(uut.jwtLib, 'register').resolves({})
|
||||
|
||||
await uut.getJWT()
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
// console.log('err.message: ', err.message)
|
||||
assert.include(err.message, 'This account does not have a JWT')
|
||||
}
|
||||
})
|
||||
|
||||
it('should retrieve a new JWT token if the old one invalid', async () => {
|
||||
// Mock dependencies to force a code path.
|
||||
sandbox.stub(uut.jwtLib, 'register').resolves({})
|
||||
uut.jwtLib.userData.apiToken = 'abc123'
|
||||
uut.jwtLib.userData.apiLevel = 30
|
||||
sandbox.stub(uut.jwtLib, 'validateApiToken').resolves({ isValid: false })
|
||||
sandbox.stub(uut.jwtLib, 'getApiToken').resolves('xyz789')
|
||||
|
||||
const result = await uut.getJWT()
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.equal(result, 'xyz789')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#instanceBchjs', () => {
|
||||
it('should return an instance of bch-js', () => {
|
||||
const result = uut.instanceBchjs()
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.property(result, 'restURL')
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user