All files / src/storage StorageServer.ts

13.72% Statements 7/51
0% Branches 0/18
0% Functions 0/7
13.72% Lines 7/51

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 151 152 153 154              28x 28x 28x 28x           28x               28x                                                                                                                                                                                                             28x                                                    
/**
 * 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 express, { Request, Response } from "express"
import { AuthMiddlewareOptions, createAuthMiddleware } from "@bsv/auth-express-middleware"
import { createPaymentMiddleware } from "@bsv/payment-express-middleware"
import { ProtoWallet, Wallet } from '@bsv/sdk'
import { sdk } from '..'
 
// You have your local or imported `WalletStorage` interface:
import { WalletStorage } from "./WalletStorage" // adjust import path
// Or your known local implementation:
import { StorageKnex } from "./StorageKnex" // adjust path as needed
 
export interface WalletStorageServerOptions {
    port: number
    wallet: Wallet
    monetize: boolean
}
 
export class StorageServer {
    private app = express()
    private port: number
    private walletStorage: sdk.WalletStorage
    private wallet: Wallet
    private monetize: boolean
 
    constructor(walletStorage: sdk.WalletStorage, options: WalletStorageServerOptions) {
        this.walletStorage = walletStorage
        this.port = options.port
        this.wallet = options.wallet
        this.monetize = options.monetize
 
        this.setupRoutes()
    }
 
    private setupRoutes(): void {
        this.app.use(express.json())
        const options: AuthMiddlewareOptions = {
            wallet: this.wallet
        }
        this.app.use(createAuthMiddleware(options))
        Iif (this.monetize) {
            this.app.use(createPaymentMiddleware({
                wallet: this.wallet,
                calculateRequestPrice: () => 100
            }))
        }
 
        // A single POST endpoint for JSON-RPC:
        this.app.post("/", async (req: Request, res: Response) => {
            debugger
            let { jsonrpc, method, params, id } = req.body
            Iif (method !== 'getSettings') {
                Iif (typeof params[0] !== 'object' || !params[0]) {
                    params = [{}]
                }
            }
 
            // 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.walletStorage as any)[method] === "function") {
                    // method is on the walletStorage:
                    // Find user
                    Iif (method !== 'getSettings') {
                        const user = await this.walletStorage.findUserByIdentityKey(req.auth.identityKey)
                        if (!user) {
                            const userId = await this.walletStorage.insertUser({
                                identityKey: req.auth.identityKey as string,
                                userId: 0,
                                created_at: new Date(),
                                updated_at: new Date()
                            })
                            params[0].userId = userId
                        } else {
                            params[0].userId = user.userId
                        }
                    }
                    const result = await (this.walletStorage 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}`)
        })
    }
}
 
import Knex from 'knex'
 
async function main() {
    const knexInstance = Knex({
        client: 'sqlite3', // or 'mysql', etc.
        connection: { filename: './test.db' },
        useNullAsDefault: true
    })
 
    const storage = new StorageKnex({
        knex: knexInstance,
        chain: 'main',
        feeModel: { model: 'sat/kb', value: 1 },
        commissionSatoshis: 0
    })
 
    // Must init storage (migrate, or otherwise):
    await storage.migrate("MyRemoteStorage")
    await storage.makeAvailable()
 
    const serverOptions: WalletStorageServerOptions = { port: 3000, wallet: new ProtoWallet('anyone'), monetize: false }
    const server = new StorageServer(storage, serverOptions)
    server.start()
}
 
//main().catch(console.error)