Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | 50x 50x 50x 50x 50x | /**
* StorageServer.ts
*
* A server-side class that "has a" local WalletStorage (like a StorageKnex instance),
* and exposes it via a JSON-RPC POST endpoint using Express.
*/
import { WalletInterface } from '@bsv/sdk'
import express, { Request, Response } from 'express'
import { AuthMiddlewareOptions, createAuthMiddleware } from '@bsv/auth-express-middleware'
import { createPaymentMiddleware } from '@bsv/payment-express-middleware'
import { sdk, Wallet, StorageProvider } from '../../index.all'
import { StorageKnex } from '../StorageKnex'
export interface WalletStorageServerOptions {
port: number
wallet: Wallet
monetize: boolean
calculateRequestPrice?: (req: Request) => number | Promise<number>
}
export class StorageServer {
private app = express()
private port: number
private storage: StorageProvider
private wallet: Wallet
private monetize: boolean
private calculateRequestPrice?: (req: Request) => number | Promise<number>
constructor(storage: StorageProvider, options: WalletStorageServerOptions) {
this.storage = storage
this.port = options.port
this.wallet = options.wallet
this.monetize = options.monetize
this.calculateRequestPrice = options.calculateRequestPrice
this.setupRoutes()
}
private setupRoutes(): void {
this.app.use(express.json({ limit: '30mb' }))
// This allows the API to be used everywhere when CORS is enforced
this.app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*')
res.header('Access-Control-Allow-Headers', '*')
res.header('Access-Control-Allow-Methods', '*')
res.header('Access-Control-Expose-Headers', '*')
res.header('Access-Control-Allow-Private-Network', 'true')
if (req.method === 'OPTIONS') {
// Handle CORS preflight requests to allow cross-origin POST/PUT requests
res.sendStatus(200)
} else {
next()
}
})
const options: AuthMiddlewareOptions = {
wallet: this.wallet as WalletInterface
}
this.app.use(createAuthMiddleware(options))
Iif (this.monetize) {
this.app.use(
createPaymentMiddleware({
wallet: this.wallet,
calculateRequestPrice: this.calculateRequestPrice || (() => 100)
})
)
}
// A single POST endpoint for JSON-RPC:
this.app.post('/', async (req: Request, res: Response) => {
let { jsonrpc, method, params, id } = req.body
// Basic JSON-RPC protocol checks:
Iif (jsonrpc !== '2.0' || !method || typeof method !== 'string') {
return res.status(400).json({ error: { code: -32600, message: 'Invalid Request' } })
}
try {
// Dispatch the method call:
if (typeof (this as any)[method] === 'function') {
// if you wanted to handle certain methods on the server class itself
// e.g. this['someServerMethod'](params)
throw new Error('Server method dispatch not used in this approach.')
} else if (typeof (this.storage as any)[method] === 'function') {
// method is on the walletStorage:
// Find user
switch (method) {
case 'destroy': {
console.log(`StorageServer: method=${method} IGNORED`)
return res.json({ jsonrpc: '2.0', result: undefined, id })
}
case 'getSettings': {
/** */
} break;
case 'findOrInsertUser': {
Iif (params[0] !== req.auth.identityKey)
throw new sdk.WERR_UNAUTHORIZED('function may only access authenticated user.');
} break;
default: {
Iif (typeof params[0] !== 'object' || !params[0]) {
params = [{}]
}
Iif (params[0]['identityKey'] && params[0]['identityKey'] !== req.auth.identityKey)
throw new sdk.WERR_UNAUTHORIZED('identityKey does not match authentiation')
console.log('looking up user with identityKey:', req.auth.identityKey)
const { user, isNew } = await this.storage.findOrInsertUser(req.auth.identityKey)
params[0].reqAuthUserId = user.userId
Iif (params[0]['identityKey']) params[0].userId = user.userId;
} break;
}
console.log(`StorageServer: method=${method} params=${JSON.stringify(params).slice(0, 100)}`)
const result = await (this.storage as any)[method](...(params || []))
return res.json({ jsonrpc: '2.0', result, id })
} else {
// Unknown method
return res.status(400).json({
jsonrpc: '2.0',
error: { code: -32601, message: `Method not found: ${method}` },
id
})
}
} catch (error) {
// Catch any thrown errors from the local walletStorage method
const err = error as Error
return res.status(200).json({
jsonrpc: '2.0',
error: {
code: -32000,
message: err.message,
data: {
name: err.name,
stack: err.stack
}
},
id
})
}
})
}
public start(): void {
this.app.listen(this.port, () => {
console.log(`WalletStorageServer listening at http://localhost:${this.port}`)
})
}
}
|