psf-bch-api 1.3.0 → 7.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env-local +26 -5
- package/bin/server.js +60 -9
- package/package.json +5 -4
- package/production/docker/.env-local +34 -0
- package/production/docker/Dockerfile +8 -25
- package/production/docker/docker-compose.yml +4 -3
- package/production/docker/temp.js +7 -0
- package/src/config/env/common.js +10 -3
- package/src/config/x402.js +7 -0
- package/src/controllers/rest-api/encryption/controller.js +100 -0
- package/src/controllers/rest-api/encryption/router.js +51 -0
- package/src/controllers/rest-api/fulcrum/controller.js +2 -1
- package/src/controllers/rest-api/index.js +8 -0
- package/src/controllers/rest-api/price/controller.js +96 -0
- package/src/controllers/rest-api/price/router.js +52 -0
- package/src/controllers/rest-api/slp/controller.js +3 -1
- package/src/middleware/basic-auth.js +61 -0
- package/src/use-cases/encryption-use-cases.js +120 -0
- package/src/use-cases/fulcrum-use-cases.js +10 -2
- package/src/use-cases/index.js +9 -0
- package/src/use-cases/price-use-cases.js +83 -0
- package/src/use-cases/slp-use-cases.js +5 -1
- package/test/unit/controllers/encryption-controller-unit.js +203 -0
- package/test/unit/controllers/price-controller-unit.js +116 -0
- package/test/unit/controllers/rest-api-index-unit.js +15 -0
- package/test/unit/use-cases/encryption-use-cases-unit.js +247 -0
- package/test/unit/use-cases/fulcrum-use-cases-unit.js +1 -1
- package/test/unit/use-cases/price-use-cases-unit.js +103 -0
- package/test/unit/use-cases/slp-use-cases-unit.js +1 -1
- /package/{index.js → psf-bch-api.js} +0 -0
package/.env-local
CHANGED
|
@@ -1,13 +1,34 @@
|
|
|
1
|
+
# START INFRASTRUCTURE SETUP
|
|
2
|
+
|
|
1
3
|
# Full Node Connection
|
|
2
4
|
RPC_BASEURL=http://172.17.0.1:8332
|
|
3
5
|
RPC_USERNAME=bitcoin
|
|
4
6
|
RPC_PASSWORD=password
|
|
5
7
|
|
|
6
|
-
# x402 payments required to access this API?
|
|
7
|
-
X402_ENABLED=false
|
|
8
|
-
|
|
9
8
|
# Fulcrum Indexer
|
|
10
|
-
FULCRUM_API=http://
|
|
9
|
+
FULCRUM_API=http://172.17.0.1:3001/v1
|
|
11
10
|
|
|
12
11
|
# SLP Indexer
|
|
13
|
-
SLP_INDEXER_API=http://
|
|
12
|
+
SLP_INDEXER_API=http://localhost:5010
|
|
13
|
+
|
|
14
|
+
# REST API URL for wallet operations
|
|
15
|
+
LOCAL_RESTURL=http://localhost:5942/v6
|
|
16
|
+
|
|
17
|
+
# END INFRASTRUCTURE SETUP
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# START ACCESS CONTROL
|
|
21
|
+
|
|
22
|
+
PORT=5942
|
|
23
|
+
|
|
24
|
+
# x402 payments required to access this API?
|
|
25
|
+
X402_ENABLED=true
|
|
26
|
+
SERVER_BCH_ADDRESS=bitcoincash:qqlrzp23w08434twmvr4fxw672whkjy0py26r63g3d
|
|
27
|
+
FACILITATOR_URL=http://localhost:4345/facilitator
|
|
28
|
+
|
|
29
|
+
# Basic Authentication required to access this API?
|
|
30
|
+
USE_BASIC_AUTH=true
|
|
31
|
+
BASIC_AUTH_TOKEN=some-random-token
|
|
32
|
+
|
|
33
|
+
# END ACCESS CONTROL
|
|
34
|
+
|
package/bin/server.js
CHANGED
|
@@ -15,7 +15,8 @@ import { dirname, join } from 'path'
|
|
|
15
15
|
import config from '../src/config/index.js'
|
|
16
16
|
import Controllers from '../src/controllers/index.js'
|
|
17
17
|
import wlogger from '../src/adapters/wlogger.js'
|
|
18
|
-
import { buildX402Routes, getX402Settings } from '../src/config/x402.js'
|
|
18
|
+
import { buildX402Routes, getX402Settings, getBasicAuthSettings } from '../src/config/x402.js'
|
|
19
|
+
import { basicAuthMiddleware } from '../src/middleware/basic-auth.js'
|
|
19
20
|
|
|
20
21
|
// Load environment variables
|
|
21
22
|
dotenv.config()
|
|
@@ -60,6 +61,7 @@ class Server {
|
|
|
60
61
|
const app = express()
|
|
61
62
|
|
|
62
63
|
const x402Settings = getX402Settings()
|
|
64
|
+
const basicAuthSettings = getBasicAuthSettings()
|
|
63
65
|
|
|
64
66
|
// MIDDLEWARE START
|
|
65
67
|
app.use(express.json())
|
|
@@ -72,23 +74,72 @@ class Server {
|
|
|
72
74
|
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With']
|
|
73
75
|
}))
|
|
74
76
|
|
|
75
|
-
//
|
|
76
|
-
|
|
77
|
+
// Apply basic auth middleware if enabled
|
|
78
|
+
// This must run before x402 middleware to set req.locals.basicAuthValid
|
|
79
|
+
if (basicAuthSettings.enabled) {
|
|
80
|
+
wlogger.info('Basic auth middleware enabled')
|
|
81
|
+
app.use(basicAuthMiddleware)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Apply x402 middleware based on configuration
|
|
85
|
+
// Logic:
|
|
86
|
+
// - If X402_ENABLED=false OR USE_BASIC_AUTH=false: Don't apply x402 (no rate limits)
|
|
87
|
+
// - If X402_ENABLED=true AND USE_BASIC_AUTH=true: Apply x402 conditionally (bypass if basic auth valid)
|
|
88
|
+
|
|
89
|
+
// Apply access control middleware based on configuration
|
|
90
|
+
if (x402Settings.enabled && basicAuthSettings.enabled) {
|
|
91
|
+
// X402_ENABLED=true AND USE_BASIC_AUTH=true: Apply x402 conditionally
|
|
77
92
|
const routes = buildX402Routes(this.config.apiPrefix)
|
|
78
93
|
const facilitatorOptions = x402Settings.facilitatorUrl
|
|
79
94
|
? { url: x402Settings.facilitatorUrl }
|
|
80
95
|
: undefined
|
|
81
96
|
|
|
82
|
-
wlogger.info(`x402 middleware enabled; enforcing ${x402Settings.priceSat} satoshis per request`)
|
|
83
|
-
|
|
84
|
-
|
|
97
|
+
wlogger.info(`x402 middleware enabled with basic auth bypass; enforcing ${x402Settings.priceSat} satoshis per request (unless basic auth provided)`)
|
|
98
|
+
|
|
99
|
+
// Create conditional x402 middleware that bypasses if basic auth is valid
|
|
100
|
+
const conditionalX402Middleware = (req, res, next) => {
|
|
101
|
+
// If basic auth is valid, bypass x402
|
|
102
|
+
if (req.locals?.basicAuthValid === true) {
|
|
103
|
+
return next()
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Otherwise, apply x402 middleware
|
|
107
|
+
return x402PaymentMiddleware(
|
|
85
108
|
x402Settings.serverAddress,
|
|
86
109
|
routes,
|
|
87
110
|
facilitatorOptions
|
|
88
|
-
)
|
|
89
|
-
|
|
111
|
+
)(req, res, next)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
app.use(conditionalX402Middleware)
|
|
115
|
+
} else if (basicAuthSettings.enabled && !x402Settings.enabled) {
|
|
116
|
+
// USE_BASIC_AUTH=true AND X402_ENABLED=false: Require basic auth, reject unauthenticated requests
|
|
117
|
+
wlogger.info('Basic auth enforcement enabled (x402 disabled)')
|
|
118
|
+
|
|
119
|
+
// Middleware that rejects requests without valid basic auth
|
|
120
|
+
const requireBasicAuthMiddleware = (req, res, next) => {
|
|
121
|
+
// Skip auth check for health endpoint and root
|
|
122
|
+
if (req.path === '/health' || req.path === '/') {
|
|
123
|
+
return next()
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// If basic auth is valid, allow the request
|
|
127
|
+
if (req.locals?.basicAuthValid === true) {
|
|
128
|
+
return next()
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Reject unauthenticated requests
|
|
132
|
+
wlogger.warn(`Unauthenticated request rejected: ${req.method} ${req.path}`)
|
|
133
|
+
return res.status(401).json({
|
|
134
|
+
error: 'Unauthorized',
|
|
135
|
+
message: 'Valid Bearer token required in Authorization header'
|
|
136
|
+
})
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
app.use(requireBasicAuthMiddleware)
|
|
90
140
|
} else {
|
|
91
|
-
|
|
141
|
+
// X402_ENABLED=false AND USE_BASIC_AUTH=false: No access control middleware
|
|
142
|
+
wlogger.info('No access control middleware enabled')
|
|
92
143
|
}
|
|
93
144
|
|
|
94
145
|
// Endpoint logging middleware
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "psf-bch-api",
|
|
3
|
-
"version": "
|
|
4
|
-
"main": "
|
|
3
|
+
"version": "7.2.0",
|
|
4
|
+
"main": "psf-bch-api.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"start": "node bin/server.js",
|
|
@@ -15,12 +15,13 @@
|
|
|
15
15
|
"license": "MIT",
|
|
16
16
|
"description": "REST API proxy to Bitcoin Cash infrastructure",
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@psf/bch-js": "
|
|
18
|
+
"@psf/bch-js": "7.1.0",
|
|
19
19
|
"axios": "1.7.7",
|
|
20
20
|
"cors": "2.8.5",
|
|
21
21
|
"dotenv": "16.3.1",
|
|
22
22
|
"express": "5.1.0",
|
|
23
|
-
"minimal-slp-wallet": "
|
|
23
|
+
"minimal-slp-wallet": "7.0.1",
|
|
24
|
+
"psffpp": "1.2.0",
|
|
24
25
|
"slp-token-media": "1.2.10",
|
|
25
26
|
"winston": "3.11.0",
|
|
26
27
|
"winston-daily-rotate-file": "4.7.1",
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# START INFRASTRUCTURE SETUP
|
|
2
|
+
|
|
3
|
+
# Full Node Connection
|
|
4
|
+
RPC_BASEURL=http://172.17.0.1:8332
|
|
5
|
+
RPC_USERNAME=bitcoin
|
|
6
|
+
RPC_PASSWORD=password
|
|
7
|
+
|
|
8
|
+
# Fulcrum Indexer
|
|
9
|
+
FULCRUM_API=http://172.17.0.1:3001/v1
|
|
10
|
+
|
|
11
|
+
# SLP Indexer
|
|
12
|
+
SLP_INDEXER_API=http://localhost:5010
|
|
13
|
+
|
|
14
|
+
# REST API URL for wallet operations
|
|
15
|
+
LOCAL_RESTURL=http://localhost:5942/v6
|
|
16
|
+
|
|
17
|
+
# END INFRASTRUCTURE SETUP
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# START ACCESS CONTROL
|
|
21
|
+
|
|
22
|
+
PORT=5942
|
|
23
|
+
|
|
24
|
+
# x402 payments required to access this API?
|
|
25
|
+
X402_ENABLED=true
|
|
26
|
+
SERVER_BCH_ADDRESS=bitcoincash:qqlrzp23w08434twmvr4fxw672whkjy0py26r63g3d
|
|
27
|
+
FACILITATOR_URL=http://localhost:4345/facilitator
|
|
28
|
+
|
|
29
|
+
# Basic Authentication required to access this API?
|
|
30
|
+
USE_BASIC_AUTH=true
|
|
31
|
+
BASIC_AUTH_TOKEN=some-random-token
|
|
32
|
+
|
|
33
|
+
# END ACCESS CONTROL
|
|
34
|
+
|
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
#
|
|
3
3
|
|
|
4
4
|
#IMAGE BUILD COMMANDS
|
|
5
|
-
# ct-base-ubuntu = ubuntu 18.04 + nodejs v10 LTS
|
|
6
|
-
#FROM christroutner/ct-base-ubuntu
|
|
7
5
|
FROM ubuntu:22.04
|
|
8
6
|
MAINTAINER Chris Troutner <chris.troutner@gmail.com>
|
|
9
7
|
|
|
@@ -47,39 +45,24 @@ RUN runuser -l safeuser -c "npm config set prefix '~/.npm-global'"
|
|
|
47
45
|
|
|
48
46
|
# Clone the rest.bitcoin.com repository
|
|
49
47
|
WORKDIR /home/safeuser
|
|
50
|
-
RUN git clone https://github.com/
|
|
48
|
+
RUN git clone https://github.com/Permissionless-Software-Foundation/psf-bch-api
|
|
51
49
|
|
|
52
50
|
# Switch to the desired branch. `master` is usually stable,
|
|
53
51
|
# and `stage` has the most up-to-date changes.
|
|
54
|
-
WORKDIR /home/safeuser/
|
|
55
|
-
|
|
56
|
-
# For development: switch to unstable branch
|
|
57
|
-
#RUN git checkout pin-ipfs
|
|
52
|
+
WORKDIR /home/safeuser/psf-bch-api
|
|
58
53
|
|
|
59
54
|
# Install dependencies
|
|
60
55
|
RUN npm install
|
|
56
|
+
RUN npm install minimal-slp-wallet
|
|
61
57
|
|
|
62
58
|
# Generate the API docs
|
|
63
59
|
RUN npm run docs
|
|
64
60
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
# Make leveldb folders
|
|
68
|
-
#RUN mkdir leveldb
|
|
69
|
-
#WORKDIR /home/safeuser/psf-slp-indexer/leveldb
|
|
70
|
-
#RUN mkdir current
|
|
71
|
-
#RUN mkdir zips
|
|
72
|
-
#RUN mkdir backup
|
|
73
|
-
#WORKDIR /home/safeuser/psf-slp-indexer/leveldb/zips
|
|
74
|
-
#COPY restore-auto.sh restore-auto.sh
|
|
75
|
-
#WORKDIR /home/safeuser/psf-slp-indexer
|
|
61
|
+
COPY .env-local .env
|
|
76
62
|
|
|
77
|
-
# Expose the port the API will be served on.
|
|
78
|
-
#EXPOSE 5011
|
|
79
63
|
|
|
80
|
-
|
|
81
|
-
#COPY start-production.sh start-production.sh
|
|
82
|
-
VOLUME start-rest2nostr.sh
|
|
83
|
-
CMD ["./start-rest2nostr.sh"]
|
|
64
|
+
CMD ["npm", "start"]
|
|
84
65
|
|
|
85
|
-
#
|
|
66
|
+
# Used to debug the container.
|
|
67
|
+
#COPY temp.js temp.js
|
|
68
|
+
#CMD ["node", "temp.js"]
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# Start the service with the command 'docker-compose up -d'
|
|
2
2
|
|
|
3
3
|
services:
|
|
4
|
-
|
|
4
|
+
psf-bch-api:
|
|
5
5
|
build: .
|
|
6
|
-
container_name:
|
|
6
|
+
container_name: psf-bch-api
|
|
7
7
|
logging:
|
|
8
8
|
driver: 'json-file'
|
|
9
9
|
options:
|
|
@@ -15,5 +15,6 @@ services:
|
|
|
15
15
|
ports:
|
|
16
16
|
- '5942:5942' # <host port>:<container port>
|
|
17
17
|
volumes:
|
|
18
|
-
|
|
18
|
+
#- ./start-rest2nostr.sh:/home/safeuser/REST2NOSTR/start-rest2nostr.sh
|
|
19
|
+
- ./.env:/home/safeuser/.env
|
|
19
20
|
restart: always
|
package/src/config/env/common.js
CHANGED
|
@@ -34,13 +34,18 @@ const priceSat = Number.isFinite(parsedPriceSat) && parsedPriceSat > 0 ? parsedP
|
|
|
34
34
|
const x402Defaults = {
|
|
35
35
|
enabled: normalizeBoolean(process.env.X402_ENABLED, true),
|
|
36
36
|
facilitatorUrl: process.env.FACILITATOR_URL || 'http://localhost:4345/facilitator',
|
|
37
|
-
serverAddress: process.env.SERVER_BCH_ADDRESS || 'bitcoincash:
|
|
37
|
+
serverAddress: process.env.SERVER_BCH_ADDRESS || 'bitcoincash:qqsrke9lh257tqen99dkyy2emh4uty0vky9y0z0lsr',
|
|
38
38
|
priceSat
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
const basicAuthDefaults = {
|
|
42
|
+
enabled: normalizeBoolean(process.env.USE_BASIC_AUTH, false),
|
|
43
|
+
token: process.env.BASIC_AUTH_TOKEN || ''
|
|
44
|
+
}
|
|
45
|
+
|
|
41
46
|
export default {
|
|
42
47
|
// Server port
|
|
43
|
-
port: process.env.PORT || 5942,
|
|
48
|
+
port: parseInt(process.env.PORT, 10) || 5942,
|
|
44
49
|
|
|
45
50
|
// Environment
|
|
46
51
|
env: process.env.NODE_ENV || 'development',
|
|
@@ -73,13 +78,15 @@ export default {
|
|
|
73
78
|
},
|
|
74
79
|
|
|
75
80
|
// REST API URL for wallet operations
|
|
76
|
-
restURL: process.env.REST_URL || process.env.LOCAL_RESTURL || 'http://127.0.0.1:
|
|
81
|
+
restURL: process.env.REST_URL || process.env.LOCAL_RESTURL || 'http://127.0.0.1:5942/v6/',
|
|
77
82
|
|
|
78
83
|
// IPFS Gateway URL
|
|
79
84
|
ipfsGateway: process.env.IPFS_GATEWAY || 'p2wdb-gateway-678.fullstack.cash',
|
|
80
85
|
|
|
81
86
|
x402: x402Defaults,
|
|
82
87
|
|
|
88
|
+
basicAuth: basicAuthDefaults,
|
|
89
|
+
|
|
83
90
|
// Version
|
|
84
91
|
version
|
|
85
92
|
}
|
package/src/config/x402.js
CHANGED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/*
|
|
2
|
+
REST API Controller for the /encryption routes.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import wlogger from '../../../adapters/wlogger.js'
|
|
6
|
+
|
|
7
|
+
class EncryptionRESTController {
|
|
8
|
+
constructor (localConfig = {}) {
|
|
9
|
+
this.adapters = localConfig.adapters
|
|
10
|
+
if (!this.adapters) {
|
|
11
|
+
throw new Error(
|
|
12
|
+
'Instance of Adapters library required when instantiating Encryption REST Controller.'
|
|
13
|
+
)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
this.useCases = localConfig.useCases
|
|
17
|
+
if (!this.useCases || !this.useCases.encryption) {
|
|
18
|
+
throw new Error(
|
|
19
|
+
'Instance of Encryption use cases required when instantiating Encryption REST Controller.'
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
this.encryptionUseCases = this.useCases.encryption
|
|
24
|
+
|
|
25
|
+
// Bind functions
|
|
26
|
+
this.root = this.root.bind(this)
|
|
27
|
+
this.getPublicKey = this.getPublicKey.bind(this)
|
|
28
|
+
this.handleError = this.handleError.bind(this)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @api {get} /v6/encryption/ Service status
|
|
33
|
+
* @apiName EncryptionRoot
|
|
34
|
+
* @apiGroup Encryption
|
|
35
|
+
*
|
|
36
|
+
* @apiDescription Returns the status of the encryption service.
|
|
37
|
+
*
|
|
38
|
+
* @apiSuccess {String} status Service identifier
|
|
39
|
+
*/
|
|
40
|
+
async root (req, res) {
|
|
41
|
+
return res.status(200).json({ status: 'encryption' })
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @api {get} /v6/encryption/publickey/:address Get public key for a BCH address
|
|
46
|
+
* @apiName GetPublicKey
|
|
47
|
+
* @apiGroup Encryption
|
|
48
|
+
* @apiDescription Searches the blockchain for a public key associated with a
|
|
49
|
+
* BCH address. Returns an object. If successful, the publicKey property will
|
|
50
|
+
* contain a hexadecimal representation of the public key.
|
|
51
|
+
*
|
|
52
|
+
* @apiParam {String} address BCH address (cash address or legacy format)
|
|
53
|
+
*
|
|
54
|
+
* @apiExample Example usage:
|
|
55
|
+
* curl -X GET "https://api.fullstack.cash/v6/encryption/publickey/bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf" -H "accept: application/json"
|
|
56
|
+
*
|
|
57
|
+
* @apiSuccess {Boolean} success Indicates if the operation was successful
|
|
58
|
+
* @apiSuccess {String} publicKey The public key in hexadecimal format, or "not found"
|
|
59
|
+
*/
|
|
60
|
+
async getPublicKey (req, res) {
|
|
61
|
+
try {
|
|
62
|
+
const address = req.params.address
|
|
63
|
+
|
|
64
|
+
// Reject if address is an array
|
|
65
|
+
if (Array.isArray(address)) {
|
|
66
|
+
res.status(400)
|
|
67
|
+
return res.json({
|
|
68
|
+
success: false,
|
|
69
|
+
error: 'address can not be an array.'
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Reject if address is missing
|
|
74
|
+
if (!address) {
|
|
75
|
+
res.status(400)
|
|
76
|
+
return res.json({
|
|
77
|
+
success: false,
|
|
78
|
+
error: 'address is required.'
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const result = await this.encryptionUseCases.getPublicKey({ address })
|
|
83
|
+
|
|
84
|
+
return res.status(200).json(result)
|
|
85
|
+
} catch (err) {
|
|
86
|
+
return this.handleError(err, res)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
handleError (err, res) {
|
|
91
|
+
wlogger.error('Error in EncryptionRESTController:', err)
|
|
92
|
+
|
|
93
|
+
const status = err.status || 500
|
|
94
|
+
const message = err.message || 'Internal server error'
|
|
95
|
+
|
|
96
|
+
return res.status(status).json({ success: false, error: message })
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export default EncryptionRESTController
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/*
|
|
2
|
+
REST API router for /encryption routes.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import express from 'express'
|
|
6
|
+
import EncryptionRESTController from './controller.js'
|
|
7
|
+
|
|
8
|
+
class EncryptionRouter {
|
|
9
|
+
constructor (localConfig = {}) {
|
|
10
|
+
this.adapters = localConfig.adapters
|
|
11
|
+
if (!this.adapters) {
|
|
12
|
+
throw new Error(
|
|
13
|
+
'Instance of Adapters library required when instantiating Encryption REST Router.'
|
|
14
|
+
)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
this.useCases = localConfig.useCases
|
|
18
|
+
if (!this.useCases) {
|
|
19
|
+
throw new Error(
|
|
20
|
+
'Instance of Use Cases library required when instantiating Encryption REST Router.'
|
|
21
|
+
)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const dependencies = {
|
|
25
|
+
adapters: this.adapters,
|
|
26
|
+
useCases: this.useCases
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
this.encryptionController = new EncryptionRESTController(dependencies)
|
|
30
|
+
|
|
31
|
+
this.apiPrefix = (localConfig.apiPrefix || '').replace(/\/$/, '')
|
|
32
|
+
this.baseUrl = `${this.apiPrefix}/encryption`
|
|
33
|
+
if (!this.baseUrl.startsWith('/')) {
|
|
34
|
+
this.baseUrl = `/${this.baseUrl}`
|
|
35
|
+
}
|
|
36
|
+
this.router = express.Router()
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
attach (app) {
|
|
40
|
+
if (!app) {
|
|
41
|
+
throw new Error('Must pass app object when attaching REST API controllers.')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
this.router.get('/', this.encryptionController.root)
|
|
45
|
+
this.router.get('/publickey/:address', this.encryptionController.getPublicKey)
|
|
46
|
+
|
|
47
|
+
app.use(this.baseUrl, this.router)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export default EncryptionRouter
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
import wlogger from '../../../adapters/wlogger.js'
|
|
6
6
|
import BCHJS from '@psf/bch-js'
|
|
7
|
+
import config from '../../../config/index.js'
|
|
7
8
|
|
|
8
|
-
const bchjs = new BCHJS()
|
|
9
|
+
const bchjs = new BCHJS({ restURL: config.restURL })
|
|
9
10
|
|
|
10
11
|
class FulcrumRESTController {
|
|
11
12
|
constructor (localConfig = {}) {
|
|
@@ -10,8 +10,10 @@
|
|
|
10
10
|
import BlockchainRouter from './full-node/blockchain/router.js'
|
|
11
11
|
import ControlRouter from './full-node/control/router.js'
|
|
12
12
|
import DSProofRouter from './full-node/dsproof/router.js'
|
|
13
|
+
import EncryptionRouter from './encryption/router.js'
|
|
13
14
|
import FulcrumRouter from './fulcrum/router.js'
|
|
14
15
|
import MiningRouter from './full-node/mining/router.js'
|
|
16
|
+
import PriceRouter from './price/router.js'
|
|
15
17
|
import RawTransactionsRouter from './full-node/rawtransactions/router.js'
|
|
16
18
|
import SlpRouter from './slp/router.js'
|
|
17
19
|
import config from '../../config/index.js'
|
|
@@ -69,12 +71,18 @@ class RESTControllers {
|
|
|
69
71
|
const dsproofRouter = new DSProofRouter(dependencies)
|
|
70
72
|
dsproofRouter.attach(app)
|
|
71
73
|
|
|
74
|
+
const encryptionRouter = new EncryptionRouter(dependencies)
|
|
75
|
+
encryptionRouter.attach(app)
|
|
76
|
+
|
|
72
77
|
const fulcrumRouter = new FulcrumRouter(dependencies)
|
|
73
78
|
fulcrumRouter.attach(app)
|
|
74
79
|
|
|
75
80
|
const miningRouter = new MiningRouter(dependencies)
|
|
76
81
|
miningRouter.attach(app)
|
|
77
82
|
|
|
83
|
+
const priceRouter = new PriceRouter(dependencies)
|
|
84
|
+
priceRouter.attach(app)
|
|
85
|
+
|
|
78
86
|
const rawtransactionsRouter = new RawTransactionsRouter(dependencies)
|
|
79
87
|
rawtransactionsRouter.attach(app)
|
|
80
88
|
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/*
|
|
2
|
+
REST API Controller for the /price routes.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import wlogger from '../../../adapters/wlogger.js'
|
|
6
|
+
|
|
7
|
+
class PriceRESTController {
|
|
8
|
+
constructor (localConfig = {}) {
|
|
9
|
+
this.adapters = localConfig.adapters
|
|
10
|
+
if (!this.adapters) {
|
|
11
|
+
throw new Error(
|
|
12
|
+
'Instance of Adapters library required when instantiating Price REST Controller.'
|
|
13
|
+
)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
this.useCases = localConfig.useCases
|
|
17
|
+
if (!this.useCases || !this.useCases.price) {
|
|
18
|
+
throw new Error(
|
|
19
|
+
'Instance of Price use cases required when instantiating Price REST Controller.'
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
this.priceUseCases = this.useCases.price
|
|
24
|
+
|
|
25
|
+
// Bind functions
|
|
26
|
+
this.root = this.root.bind(this)
|
|
27
|
+
this.getBCHUSD = this.getBCHUSD.bind(this)
|
|
28
|
+
this.getPsffppWritePrice = this.getPsffppWritePrice.bind(this)
|
|
29
|
+
this.handleError = this.handleError.bind(this)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @api {get} /v6/price/ Service status
|
|
34
|
+
* @apiName PriceRoot
|
|
35
|
+
* @apiGroup Price
|
|
36
|
+
*
|
|
37
|
+
* @apiDescription Returns the status of the price service.
|
|
38
|
+
*
|
|
39
|
+
* @apiSuccess {String} status Service identifier
|
|
40
|
+
*/
|
|
41
|
+
async root (req, res) {
|
|
42
|
+
return res.status(200).json({ status: 'price' })
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @api {get} /v6/price/bchusd Get the USD price of BCH
|
|
47
|
+
* @apiName GetBCHUSD
|
|
48
|
+
* @apiGroup Price
|
|
49
|
+
* @apiDescription Get the USD price of BCH from Coinex.
|
|
50
|
+
*
|
|
51
|
+
* @apiExample Example usage:
|
|
52
|
+
* curl -X GET "https://api.fullstack.cash/v6/price/bchusd" -H "accept: application/json"
|
|
53
|
+
*
|
|
54
|
+
* @apiSuccess {Number} usd The USD price of BCH
|
|
55
|
+
*/
|
|
56
|
+
async getBCHUSD (req, res) {
|
|
57
|
+
try {
|
|
58
|
+
const price = await this.priceUseCases.getBCHUSD()
|
|
59
|
+
return res.status(200).json({ usd: price })
|
|
60
|
+
} catch (err) {
|
|
61
|
+
return this.handleError(err, res)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @api {get} /v6/price/psffpp Get the PSF price for writing to the PSFFPP
|
|
67
|
+
* @apiName GetPsffppWritePrice
|
|
68
|
+
* @apiGroup Price
|
|
69
|
+
* @apiDescription Get the price to pin 1MB of content to the PSFFPP pinning
|
|
70
|
+
* network on IPFS. The price is denominated in PSF tokens.
|
|
71
|
+
*
|
|
72
|
+
* @apiExample Example usage:
|
|
73
|
+
* curl -X GET "https://api.fullstack.cash/v6/price/psffpp" -H "accept: application/json"
|
|
74
|
+
*
|
|
75
|
+
* @apiSuccess {Number} writePrice The price in PSF tokens to write 1MB to PSFFPP
|
|
76
|
+
*/
|
|
77
|
+
async getPsffppWritePrice (req, res) {
|
|
78
|
+
try {
|
|
79
|
+
const writePrice = await this.priceUseCases.getPsffppWritePrice()
|
|
80
|
+
return res.status(200).json({ writePrice })
|
|
81
|
+
} catch (err) {
|
|
82
|
+
return this.handleError(err, res)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
handleError (err, res) {
|
|
87
|
+
wlogger.error('Error in PriceRESTController:', err)
|
|
88
|
+
|
|
89
|
+
const status = err.status || 500
|
|
90
|
+
const message = err.message || 'Internal server error'
|
|
91
|
+
|
|
92
|
+
return res.status(status).json({ error: message })
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export default PriceRESTController
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/*
|
|
2
|
+
REST API router for /price routes.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import express from 'express'
|
|
6
|
+
import PriceRESTController from './controller.js'
|
|
7
|
+
|
|
8
|
+
class PriceRouter {
|
|
9
|
+
constructor (localConfig = {}) {
|
|
10
|
+
this.adapters = localConfig.adapters
|
|
11
|
+
if (!this.adapters) {
|
|
12
|
+
throw new Error(
|
|
13
|
+
'Instance of Adapters library required when instantiating Price REST Router.'
|
|
14
|
+
)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
this.useCases = localConfig.useCases
|
|
18
|
+
if (!this.useCases) {
|
|
19
|
+
throw new Error(
|
|
20
|
+
'Instance of Use Cases library required when instantiating Price REST Router.'
|
|
21
|
+
)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const dependencies = {
|
|
25
|
+
adapters: this.adapters,
|
|
26
|
+
useCases: this.useCases
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
this.priceController = new PriceRESTController(dependencies)
|
|
30
|
+
|
|
31
|
+
this.apiPrefix = (localConfig.apiPrefix || '').replace(/\/$/, '')
|
|
32
|
+
this.baseUrl = `${this.apiPrefix}/price`
|
|
33
|
+
if (!this.baseUrl.startsWith('/')) {
|
|
34
|
+
this.baseUrl = `/${this.baseUrl}`
|
|
35
|
+
}
|
|
36
|
+
this.router = express.Router()
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
attach (app) {
|
|
40
|
+
if (!app) {
|
|
41
|
+
throw new Error('Must pass app object when attaching REST API controllers.')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
this.router.get('/', this.priceController.root)
|
|
45
|
+
this.router.get('/bchusd', this.priceController.getBCHUSD)
|
|
46
|
+
this.router.get('/psffpp', this.priceController.getPsffppWritePrice)
|
|
47
|
+
|
|
48
|
+
app.use(this.baseUrl, this.router)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export default PriceRouter
|