mirror of
https://github.com/Permissionless-Software-Foundation/ipfs-bch-wallet-consumer.git
synced 2026-09-22 01:02:04 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b0fac4301 | ||
|
|
9584810b16 | ||
|
|
83f16709d8 | ||
|
|
fe91f4b875 | ||
|
|
2d9a2b811c | ||
|
|
9360cf6f77 | ||
|
|
514b65ed24 | ||
|
|
0c7f3088a8 | ||
|
|
aef88eb369 | ||
|
|
2f6cc72f41 | ||
|
|
12cab02300 | ||
|
|
04a908261a | ||
|
|
f16eacc3f2 | ||
|
|
83898742f1 | ||
|
|
1795918719 | ||
|
|
6894217078 | ||
|
|
3ffbed27b5 | ||
|
|
c30b697bc6 | ||
|
|
df2f69050b | ||
|
|
b49979971c | ||
|
|
7a620892d7 | ||
|
|
b372e3d260 | ||
|
|
707b385b8a | ||
|
|
6cb140a491 | ||
|
|
c43ee8c25a | ||
|
|
0a7ec11510 | ||
|
|
4a1524629c | ||
|
|
f7aaba71d1 | ||
|
|
418a2e2ad2 | ||
|
|
74248ea0c7 | ||
|
|
70a69e98bb | ||
|
|
8cffd7fae3 | ||
|
|
a1023e8224 | ||
|
|
a32ed63d47 | ||
|
|
934fb46ffc | ||
|
|
b27136d03b | ||
|
|
9837d6a7fb | ||
|
|
60a3d67c0b | ||
|
|
a03c5d8c91 | ||
|
|
0e7afb5f51 | ||
|
|
bcc90da49b | ||
|
|
b473a3803c | ||
|
|
38ed9a8dac | ||
|
|
0ca46df43e | ||
|
|
b5f09fbab9 | ||
|
|
e3e928b33d | ||
|
|
9614f186b4 | ||
|
|
0390f2d956 |
+1
-1
@@ -62,7 +62,7 @@ docs
|
||||
.nyc_output
|
||||
coverage
|
||||
database/
|
||||
system-user-*.json
|
||||
#system-user-*.json
|
||||
orbitdb
|
||||
ipfsdata
|
||||
.ipfsdata
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
The MIT License (MIT)
|
||||
Copyright (c) 2021-2023 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:
|
||||
|
||||
|
||||
@@ -12,9 +12,9 @@ More details are available at [CashStack.info](https://CashStack.info).
|
||||
|
||||
## Requirements
|
||||
|
||||
- node **^16.20.2**
|
||||
- npm **^8.19.4**
|
||||
- Docker **^20.10.8**
|
||||
- node **^20.16.0**
|
||||
- npm **^10.8.1**
|
||||
- Docker **^24.0.7**
|
||||
- Docker Compose **^1.27.4**
|
||||
|
||||
## Installation
|
||||
|
||||
+17
-13
@@ -45,16 +45,18 @@ class Server {
|
||||
const app = new Koa()
|
||||
app.keys = [this.config.session]
|
||||
|
||||
// Connect to the Mongo Database.
|
||||
this.mongoose.Promise = global.Promise
|
||||
this.mongoose.set('useCreateIndex', true) // Stop deprecation warning.
|
||||
console.log(
|
||||
`Connecting to MongoDB with this connection string: ${this.config.database}`
|
||||
)
|
||||
await this.mongoose.connect(this.config.database, {
|
||||
useUnifiedTopology: true,
|
||||
useNewUrlParser: true
|
||||
})
|
||||
if (!this.config.noMongo) {
|
||||
// Connect to the Mongo Database.
|
||||
this.mongoose.Promise = global.Promise
|
||||
this.mongoose.set('useCreateIndex', true) // Stop deprecation warning.
|
||||
console.log(
|
||||
`Connecting to MongoDB with this connection string: ${this.config.database}`
|
||||
)
|
||||
await this.mongoose.connect(this.config.database, {
|
||||
useUnifiedTopology: true,
|
||||
useNewUrlParser: true
|
||||
})
|
||||
}
|
||||
|
||||
console.log(`Starting environment: ${this.config.env}`)
|
||||
console.log(`Debug level: ${this.config.debugLevel}`)
|
||||
@@ -102,9 +104,11 @@ class Server {
|
||||
this.server = await app.listen(this.config.port)
|
||||
console.log(`Server started on ${this.config.port}`)
|
||||
|
||||
// Create the system admin user.
|
||||
const success = await this.adminLib.createSystemUser()
|
||||
if (success) console.log('System admin user created.')
|
||||
if (!this.config.noMongo) {
|
||||
// Create the system admin user.
|
||||
const success = await this.adminLib.createSystemUser()
|
||||
if (success) console.log('System admin user created.')
|
||||
}
|
||||
|
||||
// Attach the other IPFS controllers.
|
||||
// Skip if this is a test environment.
|
||||
|
||||
Vendored
+7
-1
@@ -43,6 +43,9 @@ export default {
|
||||
? process.env.EMAILPASS
|
||||
: 'emailpassword',
|
||||
|
||||
// Enable or Disable the usage of Mongo DB.
|
||||
noMongo: process.env.NO_MONGO ? true : false,
|
||||
|
||||
// BEGIN WALLET CONFIGURATION
|
||||
|
||||
// BCH Mnemonic for generating encryption keys and payment address
|
||||
@@ -119,7 +122,10 @@ export default {
|
||||
'@type': 'Organization',
|
||||
name: 'Permissionless Software Foundation',
|
||||
url: 'https://PSFoundation.cash'
|
||||
}
|
||||
},
|
||||
|
||||
// If this node has an IP4 address or domain name used to provide a REST API.
|
||||
web2Api: process.env.WEB2_API ? process.env.WEB2_API : null
|
||||
},
|
||||
|
||||
// IPFS Ports
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"password": "DwaDw2YaKnuFxsSIm32B",
|
||||
"email": "system@system.com",
|
||||
"id": "66d9e3a45648506e44a77d53",
|
||||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY2ZDllM2E0NTY0ODUwNmU0NGE3N2Q1MyIsImlhdCI6MTcyNTU1NTYyMH0.S_K_06SN7bO-qoG4fi-Q8PHW2NSFBVvHgOG2K0V-CWY"
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"password": "02PjvhoMGoM7pJylp80Z",
|
||||
"email": "system@system.com",
|
||||
"id": "645e49a45a9f5a8e0a54a083",
|
||||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY0NWU0OWE0NWE5ZjVhOGUwYTU0YTA4MyIsImlhdCI6MTY4MzkwMDgzNn0.sXeT-G9kS5Nu1dnZUGsNm6FhxrcNaCHhnQ-tLqsJc9E"
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"password": "RCJqq5FjgYzvTXOyS1vo",
|
||||
"email": "system@system.com",
|
||||
"id": "645e59c19d49119bbb8329e7",
|
||||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY0NWU1OWMxOWQ0OTExOWJiYjgzMjllNyIsImlhdCI6MTY4MzkwNDk2MX0.2yi-hkY31nkgMm_S1QrPs4dIm5DBnJ4GsJEPiSPYHDA"
|
||||
}
|
||||
Generated
+7482
-5744
File diff suppressed because it is too large
Load Diff
+20
-17
@@ -24,24 +24,26 @@
|
||||
},
|
||||
"repository": "Permissionless-Software-Foundation/ipfs-bch-wallet-consumer",
|
||||
"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",
|
||||
"@chainsafe/libp2p-gossipsub": "13.1.1",
|
||||
"@chainsafe/libp2p-noise": "15.1.1",
|
||||
"@chainsafe/libp2p-yamux": "6.0.2",
|
||||
"@helia/unixfs": "3.0.7",
|
||||
"@libp2p/bootstrap": "10.1.5",
|
||||
"@libp2p/circuit-relay-v2": "1.1.5",
|
||||
"@libp2p/identify": "2.1.5",
|
||||
"@libp2p/keychain": "4.1.5",
|
||||
"@libp2p/logger": "4.0.20",
|
||||
"@libp2p/tcp": "9.1.5",
|
||||
"@libp2p/webrtc": "4.0.34",
|
||||
"@libp2p/websockets": "8.2.0",
|
||||
"@multiformats/multiaddr": "12.3.0",
|
||||
"axios": "0.27.2",
|
||||
"bcryptjs": "2.4.3",
|
||||
"blockstore-fs": "1.1.8",
|
||||
"datastore-fs": "9.1.6",
|
||||
"blockstore-fs": "2.0.0",
|
||||
"datastore-fs": "9.1.7",
|
||||
"glob": "7.1.6",
|
||||
"helia": "2.1.0",
|
||||
"helia-coord": "1.5.6",
|
||||
"helia": "4.2.5",
|
||||
"helia-coord": "1.5.12",
|
||||
"jsonrpc-lite": "2.2.0",
|
||||
"jsonwebtoken": "8.5.1",
|
||||
"jwt-bch-lib": "1.3.0",
|
||||
@@ -56,14 +58,15 @@
|
||||
"koa-router": "10.0.0",
|
||||
"koa-static": "5.0.0",
|
||||
"koa2-ratelimit": "0.9.1",
|
||||
"libp2p": "1.2.1",
|
||||
"libp2p": "1.9.1",
|
||||
"line-reader": "0.4.0",
|
||||
"mime-types": "2.1.35",
|
||||
"minimal-slp-wallet": "5.11.2",
|
||||
"minimal-slp-wallet": "5.12.0",
|
||||
"mongoose": "5.13.14",
|
||||
"node-fetch": "npm:@achingbrain/node-fetch@2.6.7",
|
||||
"nodemailer": "6.7.5",
|
||||
"passport-local": "1.0.0",
|
||||
"psffpp": "1.1.9",
|
||||
"public-ip": "6.0.1",
|
||||
"semver": "7.3.5",
|
||||
"winston": "3.3.3",
|
||||
|
||||
@@ -15,7 +15,7 @@ RUN apt-get install -y sudo git curl nano gnupg wget
|
||||
RUN sudo apt-get install -y ca-certificates curl gnupg
|
||||
RUN sudo mkdir -p /etc/apt/keyrings
|
||||
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 echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.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
|
||||
|
||||
@@ -66,8 +66,6 @@ RUN npm install
|
||||
# Generate the API docs
|
||||
RUN npm run docs
|
||||
|
||||
VOLUME /home/safeuser/keys
|
||||
|
||||
# Expose the port the API will be served on.
|
||||
#EXPOSE 5010
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ services:
|
||||
options:
|
||||
max-size: '10m'
|
||||
max-file: '10'
|
||||
#mem_limit: 500mb
|
||||
mem_limit: 1000mb
|
||||
links:
|
||||
- mongo-bch-consumer
|
||||
ports:
|
||||
|
||||
@@ -176,6 +176,48 @@ class IpfsFilesAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
// Get an array of the latest 20 pinned files.
|
||||
async getPins (inObj = {}) {
|
||||
try {
|
||||
// Throw an error if this IPFS node has not yet made a connection to a
|
||||
// wallet service provider.
|
||||
const selectedProvider =
|
||||
this.ipfs.ipfsCoordAdapter.state.selectedIpfsFileProvider
|
||||
if (!selectedProvider) {
|
||||
throw new Error('No IPFS File Pin Service provider available yet.')
|
||||
}
|
||||
|
||||
const rpcData = {
|
||||
endpoint: 'getPins'
|
||||
}
|
||||
|
||||
// Generate a UUID for the call.
|
||||
const rpcId = this.uid()
|
||||
|
||||
// Generate a JSON RPC command.
|
||||
const cmd = this.jsonrpc.request(rpcId, 'file-pin', rpcData)
|
||||
const cmdStr = JSON.stringify(cmd)
|
||||
// console.log('cmdStr: ', cmdStr)
|
||||
|
||||
// Send the RPC command to selected wallet service.
|
||||
const thisNode = this.ipfs.ipfsCoordAdapter.ipfsCoord.thisNode
|
||||
await this.ipfs.ipfsCoordAdapter.ipfsCoord.useCases.peer.sendPrivateMessage(
|
||||
selectedProvider,
|
||||
cmdStr,
|
||||
thisNode
|
||||
)
|
||||
|
||||
// Wait for data to come back from the wallet service.
|
||||
const data = await this.waitForRPCResponse(rpcId)
|
||||
// console.log('getFileMetadata() data: ', data)
|
||||
|
||||
return data
|
||||
} catch (err) {
|
||||
wlogger.error('Error in adapters/files/getPins()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a promise that resolves to data when the RPC response is recieved.
|
||||
async waitForRPCResponse (rpcId) {
|
||||
try {
|
||||
|
||||
@@ -20,7 +20,7 @@ import config from '../../../config/index.js'
|
||||
|
||||
// The minimum version of ipfs-bch-wallet-service that this wallet can work with.
|
||||
const MIN_BCH_WALLET_VERSION = '1.11.11'
|
||||
const WALLET_PROTOCOL = 'bch-wallet'
|
||||
const WALLET_PROTOCOL = 'bch-wallet-service'
|
||||
const MIN_P2WDB_VERSION = '1.4.0'
|
||||
const P2WDB_PROTOCOL = 'p2wdb'
|
||||
const MIN_FILE_PIN_VERSION = '1.0.0'
|
||||
|
||||
@@ -154,7 +154,7 @@ class IpfsAdapter {
|
||||
// Configure services
|
||||
const services = {
|
||||
identify: identify(),
|
||||
pubsub: gossipsub({ allowPublishToZeroPeers: true })
|
||||
pubsub: gossipsub({ allowPublishToZeroTopicPeers: true })
|
||||
}
|
||||
if (this.config.isCircuitRelay) {
|
||||
console.log('Helia (IPFS) node IS configured as Circuit Relay')
|
||||
@@ -254,6 +254,8 @@ class IpfsAdapter {
|
||||
|
||||
!this.fs.existsSync(`${IPFS_DIR}/datastore`) && this.fs.mkdirSync(`${IPFS_DIR}/datastore`)
|
||||
|
||||
// !fs.existsSync(`${IPFS_DIR}/datastore/peers`) && fs.mkdirSync(`${IPFS_DIR}/datastore/peers`)
|
||||
|
||||
return true
|
||||
} catch (err) {
|
||||
console.error('Error in adapters/ipfs.js/ensureBlocksDir(): ', err)
|
||||
|
||||
@@ -15,6 +15,7 @@ import BchRESTController from './bch/index.js'
|
||||
import IpfsRESTController from './ipfs/index.js'
|
||||
import P2wdbRESTController from './p2wdb/index.js'
|
||||
import PriceRESTController from './price/index.js'
|
||||
import config from '../../../config/index.js'
|
||||
|
||||
class RESTControllers {
|
||||
constructor (localConfig = {}) {
|
||||
@@ -32,7 +33,8 @@ class RESTControllers {
|
||||
)
|
||||
}
|
||||
|
||||
// console.log('Controllers localConfig: ', localConfig)
|
||||
// Encapsulate dependencies
|
||||
this.config = config
|
||||
}
|
||||
|
||||
attachRESTControllers (app) {
|
||||
@@ -41,13 +43,15 @@ class RESTControllers {
|
||||
useCases: this.useCases
|
||||
}
|
||||
|
||||
// Attach the REST API Controllers associated with the /auth route
|
||||
const authRESTController = new AuthRESTController(dependencies)
|
||||
authRESTController.attach(app)
|
||||
if (!this.config.noMongo) {
|
||||
// Attach the REST API Controllers associated with the /auth route
|
||||
const authRESTController = new AuthRESTController(dependencies)
|
||||
authRESTController.attach(app)
|
||||
|
||||
// Attach the REST API Controllers associated with the /user route
|
||||
const userRouter = new UserRouter(dependencies)
|
||||
userRouter.attach(app)
|
||||
// Attach the REST API Controllers associated with the /user route
|
||||
const userRouter = new UserRouter(dependencies)
|
||||
userRouter.attach(app)
|
||||
}
|
||||
|
||||
// Attach the REST API Controllers associated with the /contact route
|
||||
const contactRESTController = new ContactRESTController(dependencies)
|
||||
|
||||
@@ -38,6 +38,7 @@ class IpfsRESTControllerLib {
|
||||
this.viewFile = this.viewFile.bind(this)
|
||||
this.getService = this.getService.bind(this)
|
||||
this.getFileInfo = this.getFileInfo.bind(this)
|
||||
this.getPins = this.getPins.bind(this)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -224,6 +225,34 @@ class IpfsRESTControllerLib {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} /ipfs/pins Get info on latest 20 pinned files
|
||||
* @apiPermission public
|
||||
* @apiName GetPins
|
||||
* @apiGroup REST IPFS
|
||||
* @apiDescription Returns an array of the latest 20 pinned files.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -H "Content-Type: application/json" -X GET localhost:5015/ipfs/pins
|
||||
*
|
||||
*/
|
||||
async getPins (ctx) {
|
||||
try {
|
||||
// const { cid } = ctx.params
|
||||
|
||||
const ipfsFiles = this.adapters.ipfsFiles
|
||||
|
||||
const pinData = await ipfsFiles.getPins()
|
||||
console.log('getPins() pinData: ', pinData)
|
||||
|
||||
ctx.body = pinData
|
||||
} catch (err) {
|
||||
// wlogger.error('Error in ipfs/controller.js/viewFile(): ', err)
|
||||
console.log('Error in ipfs/controller.js/getPins(): ', err)
|
||||
this.handleError(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
// DRY error handler
|
||||
handleError (ctx, err) {
|
||||
// If an HTTP status is specified by the buisiness logic, use that.
|
||||
|
||||
@@ -59,6 +59,7 @@ class IpfsRouter {
|
||||
this.router.get('/view/:cid', this.ipfsRESTController.viewFile)
|
||||
this.router.get('/service', this.ipfsRESTController.getService)
|
||||
this.router.get('/file-info/:cid', this.ipfsRESTController.getFileInfo)
|
||||
this.router.get('/pins', this.ipfsRESTController.getPins)
|
||||
|
||||
// Attach the Controller routes to the Koa app.
|
||||
app.use(this.router.routes())
|
||||
|
||||
@@ -28,7 +28,10 @@ class PriceRESTControllerLib {
|
||||
// this.UserModel = this.adapters.localdb.Users
|
||||
// this.userUseCases = this.useCases.user
|
||||
|
||||
// _this = this
|
||||
// Bind 'this' object to all subfunctions
|
||||
this.getUSD = this.getUSD.bind(this)
|
||||
this.getXecPrice = this.getXecPrice.bind(this)
|
||||
this.getPsffppWritePrice = this.getPsffppWritePrice.bind(this)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,6 +92,22 @@ class PriceRESTControllerLib {
|
||||
}
|
||||
}
|
||||
|
||||
// Get the write price in PSF tokens to pin 1MB to the PSFFPP.
|
||||
// TODO: Add API docs.
|
||||
// curl -X GET http://localhost:5015/price/psffpp
|
||||
async getPsffppWritePrice (ctx) {
|
||||
try {
|
||||
const psfPrice = await this.useCases.ipfs.getWritePrice()
|
||||
|
||||
ctx.body = { psfPrice }
|
||||
} catch (err) {
|
||||
// Write out error to error log.
|
||||
wlogger.error('Error in GET /price/getPsffppWritePrice', err)
|
||||
|
||||
this.handleError(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
// DRY error handler
|
||||
handleError (ctx, err) {
|
||||
// If an HTTP status is specified by the buisiness logic, use that.
|
||||
|
||||
@@ -53,6 +53,7 @@ class PriceRouter {
|
||||
// Define the routes and attach the controller.
|
||||
this.router.get('/usd', this.getPrice)
|
||||
this.router.get('/xecusd', this.getXecPrice)
|
||||
this.router.get('/psffpp', this.getPsffppWritePrice)
|
||||
|
||||
// Attach the Controller routes to the Koa app.
|
||||
app.use(this.router.routes())
|
||||
@@ -66,6 +67,10 @@ class PriceRouter {
|
||||
async getXecPrice (ctx, next) {
|
||||
await _this.priceRESTController.getXecPrice(ctx, next)
|
||||
}
|
||||
|
||||
async getPsffppWritePrice (ctx, next) {
|
||||
await _this.priceRESTController.getPsffppWritePrice(ctx, next)
|
||||
}
|
||||
}
|
||||
|
||||
// module.exports = PriceRouter
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
// import RetryQueue from '@chris.troutner/retry-queue'
|
||||
// import { exporter } from 'ipfs-unixfs-exporter'
|
||||
import { Duplex } from 'stream'
|
||||
import PSFFPP from 'psffpp'
|
||||
|
||||
// Local libraries
|
||||
// import PinEntity from '../entities/pin.js'
|
||||
@@ -26,11 +27,15 @@ class IpfsUseCases {
|
||||
}
|
||||
|
||||
// Encapsulate dependencies
|
||||
// this.exporter = exporter
|
||||
this.psffpp = null // placeholder
|
||||
|
||||
// Bind 'this' object to all class subfunctions.
|
||||
this.downloadCid = this.downloadCid.bind(this)
|
||||
// this.downloadCid2 = this.downloadCid2.bind(this)
|
||||
this.getWritePrice = this.getWritePrice.bind(this)
|
||||
|
||||
// State
|
||||
this.lastWritePriceUpdate = null // Used to periodically update write price.
|
||||
}
|
||||
|
||||
// Download a pinned file, given its CID.
|
||||
@@ -83,6 +88,43 @@ class IpfsUseCases {
|
||||
}
|
||||
}
|
||||
|
||||
// Get the price (in PSF tokens) to pin 1MB to the PSFFPP network. This price
|
||||
// is set on-chain by the PSF Minting Council. PSFoundation.cash
|
||||
async getWritePrice (inObj = {}) {
|
||||
try {
|
||||
const wallet = this.adapters.wallet.bchWallet
|
||||
// console.log('getWritePrice() wallet: ', wallet)
|
||||
|
||||
// Instantiate the psffpp library if it hasn't already been.
|
||||
// Dev Note: Important to only instantiate once, since the write price
|
||||
// is cached by the psffpp library. Instantiating only once improves
|
||||
// performance of price lookup.
|
||||
if (!this.psffpp) {
|
||||
this.psffpp = new PSFFPP({ wallet })
|
||||
|
||||
const now = new Date()
|
||||
this.lastWritePriceUpdate = now.getTime()
|
||||
}
|
||||
|
||||
// Periodically re-validate the write-price (by re-instantiating the
|
||||
// PSFFPP library) in case it's changed.
|
||||
const now = new Date()
|
||||
const sixHours = now.getTime() * 1000 * 60 * 60 * 6
|
||||
if (this.lastWritePriceUpdate + sixHours < now.getTime()) {
|
||||
this.psffpp = new PSFFPP({ wallet })
|
||||
|
||||
this.lastWritePriceUpdate = now.getTime()
|
||||
}
|
||||
|
||||
const writePrice = await this.psffpp.getMcWritePrice()
|
||||
|
||||
return writePrice
|
||||
} catch (err) {
|
||||
console.error('Error in use-cases/ipfs-use-cases.js/getWritePrice()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Given a CID, this function will retrieve file metadata from the
|
||||
// ipfs-file-pin-service, using the IPFS JSON-RPC. This metadata includes
|
||||
// the filename, which can be used to infer mime-type, so that it can be
|
||||
@@ -96,6 +138,10 @@ class IpfsUseCases {
|
||||
const metadata = await ipfsFiles.getFileMetadata({ cid })
|
||||
console.log('getCidMetadata() metadata: ', metadata)
|
||||
|
||||
if (metadata.success === false) {
|
||||
throw new Error(metadata.message)
|
||||
}
|
||||
|
||||
const filename = metadata.fileMetadata.filename
|
||||
|
||||
return filename
|
||||
|
||||
@@ -13,7 +13,6 @@ import axios from 'axios'
|
||||
|
||||
// Local support libraries
|
||||
import config from '../../../config/index.js'
|
||||
|
||||
import Server from '../../../bin/server.js'
|
||||
import testUtils from '../../utils/test-utils.js'
|
||||
import AdminLib from '../../../src/adapters/admin.js'
|
||||
@@ -24,105 +23,107 @@ const context = {}
|
||||
|
||||
const LOCALHOST = `http://localhost:${config.port}`
|
||||
|
||||
describe('Auth', () => {
|
||||
before(async () => {
|
||||
const app = new Server()
|
||||
if (!config.noMongo) {
|
||||
describe('Auth', () => {
|
||||
before(async () => {
|
||||
const app = new Server()
|
||||
|
||||
// This should be the first instruction. It starts the REST API server.
|
||||
await app.startServer()
|
||||
// This should be the first instruction. It starts the REST API server.
|
||||
await app.startServer()
|
||||
|
||||
// Stop the IPFS node for the rest of the e2e tests.
|
||||
// await app.controllers.adapters.ipfs.stop()
|
||||
// Stop the IPFS node for the rest of the e2e tests.
|
||||
// await app.controllers.adapters.ipfs.stop()
|
||||
|
||||
// Delete all previous users in the database.
|
||||
await testUtils.deleteAllUsers()
|
||||
// Delete all previous users in the database.
|
||||
await testUtils.deleteAllUsers()
|
||||
|
||||
// Create a new admin user.
|
||||
await adminLib.createSystemUser()
|
||||
// Create a new admin user.
|
||||
await adminLib.createSystemUser()
|
||||
|
||||
const userObj = {
|
||||
email: 'test@test.com',
|
||||
password: 'pass',
|
||||
name: 'test'
|
||||
}
|
||||
const testUser = await testUtils.createUser(userObj)
|
||||
// console.log('TestUser: ', testUser)
|
||||
|
||||
context.user = testUser.user
|
||||
context.token = testUser.token
|
||||
})
|
||||
|
||||
describe('POST /auth', () => {
|
||||
it('should throw 401 if credentials are incorrect', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'post',
|
||||
url: `${LOCALHOST}/auth`,
|
||||
data: {
|
||||
email: 'test@test.com',
|
||||
password: 'wrongpassword'
|
||||
}
|
||||
}
|
||||
|
||||
const result = await axios(options)
|
||||
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
console.log(
|
||||
`result stringified: ${JSON.stringify(result.data, null, 2)}`
|
||||
)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert(err.response.status === 401, 'Error code 401 expected.')
|
||||
const userObj = {
|
||||
email: 'test@test.com',
|
||||
password: 'pass',
|
||||
name: 'test'
|
||||
}
|
||||
const testUser = await testUtils.createUser(userObj)
|
||||
// console.log('TestUser: ', testUser)
|
||||
|
||||
context.user = testUser.user
|
||||
context.token = testUser.token
|
||||
})
|
||||
|
||||
it('should throw 401 if email is wrong format', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'post',
|
||||
url: `${LOCALHOST}/auth`,
|
||||
data: {
|
||||
email: 'wrongEmail',
|
||||
password: 'wrongpassword'
|
||||
describe('POST /auth', () => {
|
||||
it('should throw 401 if credentials are incorrect', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'post',
|
||||
url: `${LOCALHOST}/auth`,
|
||||
data: {
|
||||
email: 'test@test.com',
|
||||
password: 'wrongpassword'
|
||||
}
|
||||
}
|
||||
|
||||
const result = await axios(options)
|
||||
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
console.log(
|
||||
`result stringified: ${JSON.stringify(result.data, null, 2)}`
|
||||
)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert(err.response.status === 401, 'Error code 401 expected.')
|
||||
}
|
||||
})
|
||||
|
||||
await axios(options)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert(err.response.status === 401, 'Error code 401 expected.')
|
||||
}
|
||||
})
|
||||
|
||||
it('should auth user', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'post',
|
||||
url: `${LOCALHOST}/auth`,
|
||||
data: {
|
||||
email: 'test@test.com',
|
||||
password: 'pass'
|
||||
it('should throw 401 if email is wrong format', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'post',
|
||||
url: `${LOCALHOST}/auth`,
|
||||
data: {
|
||||
email: 'wrongEmail',
|
||||
password: 'wrongpassword'
|
||||
}
|
||||
}
|
||||
}
|
||||
const result = await axios(options)
|
||||
// console.log(`result: ${JSON.stringify(result.data, null, 2)}`)
|
||||
|
||||
assert(result.status === 200, 'Status Code 200 expected.')
|
||||
assert(
|
||||
result.data.user.email === 'test@test.com',
|
||||
'Email of test expected'
|
||||
)
|
||||
assert(
|
||||
result.data.user.password === undefined,
|
||||
'Password expected to be omited'
|
||||
)
|
||||
} catch (err) {
|
||||
console.log(
|
||||
'Error authenticating test user: ' + JSON.stringify(err, null, 2)
|
||||
)
|
||||
throw err
|
||||
}
|
||||
await axios(options)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert(err.response.status === 401, 'Error code 401 expected.')
|
||||
}
|
||||
})
|
||||
|
||||
it('should auth user', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'post',
|
||||
url: `${LOCALHOST}/auth`,
|
||||
data: {
|
||||
email: 'test@test.com',
|
||||
password: 'pass'
|
||||
}
|
||||
}
|
||||
const result = await axios(options)
|
||||
// console.log(`result: ${JSON.stringify(result.data, null, 2)}`)
|
||||
|
||||
assert(result.status === 200, 'Status Code 200 expected.')
|
||||
assert(
|
||||
result.data.user.email === 'test@test.com',
|
||||
'Email of test expected'
|
||||
)
|
||||
assert(
|
||||
result.data.user.password === undefined,
|
||||
'Password expected to be omited'
|
||||
)
|
||||
} catch (err) {
|
||||
console.log(
|
||||
'Error authenticating test user: ' + JSON.stringify(err, null, 2)
|
||||
)
|
||||
throw err
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,8 @@ import { assert } from 'chai'
|
||||
import Admin from '../../../src/adapters/admin.js'
|
||||
import sinon from 'sinon'
|
||||
import util from 'util'
|
||||
import config from '../../../config/index.js'
|
||||
|
||||
util.inspect.defaultOptions = { depth: 1 }
|
||||
|
||||
let sandbox
|
||||
@@ -15,101 +17,103 @@ describe('Admin', () => {
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('loginAdmin()', () => {
|
||||
it('should logind admin', async () => {
|
||||
try {
|
||||
const error = new Error('test error')
|
||||
error.response = {
|
||||
status: 422
|
||||
if (!config.noMongo) {
|
||||
describe('loginAdmin()', () => {
|
||||
it('should logind admin', async () => {
|
||||
try {
|
||||
const error = new Error('test error')
|
||||
error.response = {
|
||||
status: 422
|
||||
}
|
||||
// sandbox.stub(uut.axios, 'request').onFirstCall().throws(error)
|
||||
|
||||
const result = await uut.loginAdmin()
|
||||
const user = result.data.user
|
||||
|
||||
assert.property(user, '_id')
|
||||
assert.property(user, 'email')
|
||||
assert.property(user, 'type')
|
||||
|
||||
assert.isString(user._id)
|
||||
assert.isString(user.email)
|
||||
assert.isString(user.type)
|
||||
|
||||
assert.equal(user.type, 'admin')
|
||||
} catch (err) {
|
||||
assert(false, 'Unexpected result')
|
||||
}
|
||||
// sandbox.stub(uut.axios, 'request').onFirstCall().throws(error)
|
||||
})
|
||||
|
||||
const result = await uut.loginAdmin()
|
||||
const user = result.data.user
|
||||
it('should handle axios error', async () => {
|
||||
try {
|
||||
// Returns an erroneous password to force
|
||||
// an auth error
|
||||
sandbox.stub(uut.jsonFiles, 'readJSON').resolves({ password: 'wrong' })
|
||||
|
||||
assert.property(user, '_id')
|
||||
assert.property(user, 'email')
|
||||
assert.property(user, 'type')
|
||||
|
||||
assert.isString(user._id)
|
||||
assert.isString(user.email)
|
||||
assert.isString(user.type)
|
||||
|
||||
assert.equal(user.type, 'admin')
|
||||
} catch (err) {
|
||||
assert(false, 'Unexpected result')
|
||||
}
|
||||
})
|
||||
|
||||
it('should handle axios error', async () => {
|
||||
try {
|
||||
// Returns an erroneous password to force
|
||||
// an auth error
|
||||
sandbox.stub(uut.jsonFiles, 'readJSON').resolves({ password: 'wrong' })
|
||||
|
||||
await uut.loginAdmin()
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 401)
|
||||
assert.include(err.response.data, 'Unauthorized')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('createSystemUser()', () => {
|
||||
it('should create admin', async () => {
|
||||
try {
|
||||
const result = await uut.createSystemUser()
|
||||
|
||||
assert.property(result, 'email')
|
||||
assert.property(result, 'password')
|
||||
assert.property(result, 'id')
|
||||
assert.property(result, 'token')
|
||||
} catch (err) {
|
||||
assert(false, 'Unexpected result')
|
||||
}
|
||||
})
|
||||
|
||||
it('should handle axios error', async () => {
|
||||
try {
|
||||
const error1 = new Error('test error')
|
||||
error1.response = {
|
||||
status: 422
|
||||
await uut.loginAdmin()
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 401)
|
||||
assert.include(err.response.data, 'Unauthorized')
|
||||
}
|
||||
const error2 = new Error('test error')
|
||||
error1.response = {
|
||||
status: 500
|
||||
}
|
||||
// The loginAdmin() function in some use cases is recursive
|
||||
// after handling the 422 error, it gets called again
|
||||
sandbox
|
||||
.stub(uut.axios, 'request')
|
||||
.onFirstCall()
|
||||
.throws(error1)
|
||||
.onSecondCall()
|
||||
.throws(error2)
|
||||
|
||||
await uut.createSystemUser()
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('should handle errors when remove user', async () => {
|
||||
try {
|
||||
const error1 = new Error('test error')
|
||||
error1.response = {
|
||||
status: 422
|
||||
}
|
||||
sandbox.stub(uut.axios, 'request').throws(error1)
|
||||
sandbox.stub(uut.User, 'deleteOne').throws(new Error('test error'))
|
||||
describe('createSystemUser()', () => {
|
||||
it('should create admin', async () => {
|
||||
try {
|
||||
const result = await uut.createSystemUser()
|
||||
|
||||
await uut.createSystemUser()
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
assert.property(result, 'email')
|
||||
assert.property(result, 'password')
|
||||
assert.property(result, 'id')
|
||||
assert.property(result, 'token')
|
||||
} catch (err) {
|
||||
assert(false, 'Unexpected result')
|
||||
}
|
||||
})
|
||||
|
||||
it('should handle axios error', async () => {
|
||||
try {
|
||||
const error1 = new Error('test error')
|
||||
error1.response = {
|
||||
status: 422
|
||||
}
|
||||
const error2 = new Error('test error')
|
||||
error1.response = {
|
||||
status: 500
|
||||
}
|
||||
// The loginAdmin() function in some use cases is recursive
|
||||
// after handling the 422 error, it gets called again
|
||||
sandbox
|
||||
.stub(uut.axios, 'request')
|
||||
.onFirstCall()
|
||||
.throws(error1)
|
||||
.onSecondCall()
|
||||
.throws(error2)
|
||||
|
||||
await uut.createSystemUser()
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
|
||||
it('should handle errors when remove user', async () => {
|
||||
try {
|
||||
const error1 = new Error('test error')
|
||||
error1.response = {
|
||||
status: 422
|
||||
}
|
||||
sandbox.stub(uut.axios, 'request').throws(error1)
|
||||
sandbox.stub(uut.User, 'deleteOne').throws(new Error('test error'))
|
||||
|
||||
await uut.createSystemUser()
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -47,7 +47,7 @@ const peerData = [
|
||||
'@type': 'WebAPI',
|
||||
name: 'trout-bch-wallet-service-dev',
|
||||
version: '1.11.12',
|
||||
protocol: 'bch-wallet',
|
||||
protocol: 'bch-wallet-service',
|
||||
description:
|
||||
'IPFS service providing BCH blockchain access needed by a wallet.',
|
||||
documentation: 'https://ipfs-bch-wallet-service.fullstack.cash/',
|
||||
|
||||
Reference in New Issue
Block a user