From: Chris Duncan Date: Tue, 4 Aug 2026 17:27:12 +0000 (-0700) Subject: Refactor RPC class to reference psuedo-namespaced environment variables and utilize... X-Git-Url: https://git.codecow.com/?a=commitdiff_plain;h=36ce71919ba53a76e22aa11a608206cdbe894c01;p=libnemo.git Refactor RPC class to reference psuedo-namespaced environment variables and utilize standard HTTP header for Authorization. --- diff --git a/README.md b/README.md index 6ca67b2..17baf08 100644 --- a/README.md +++ b/README.md @@ -240,8 +240,10 @@ try { #### Requesting proof-of-work from an online service +Instantiating Rpc object with API key included for `Authorization` header + ```javascript -const node = new Rpc("https://nano-node.example.com/"); +const node = new Rpc("https://nano-node.example.com/", 'api-key-example-123456789'); try { await block.pow("https://nano-node.example.com/"); } catch (err) { @@ -251,8 +253,16 @@ try { #### Processing a block on the network +Using process environment to set `Authorization` header + +```console +#!/usr/env/bash + +export LIBNEMO_RPC_AUTHORIZATION = 'api-key-example-123456789' +``` + ```javascript -const node = new Rpc("https://nano-node.example.com", "nodes-api-key"); +const node = new Rpc("https://nano-node.example.com"); try { const hash = await block.process("https://nano-node.example.com/"); } catch (err) { diff --git a/sample.env b/sample.env index f2fd965..b1f6ab2 100644 --- a/sample.env +++ b/sample.env @@ -2,6 +2,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later # Save this file as `.env` and replace the following with real values -NODE_URL="https://rpc.example.com" -API_KEY_NAME="api_key" -LIBNEMO_RPC_API_KEY="fedcba9876543210fedcba9876543210" +LIBNEMO_RPC_URL="https://rpc.example.com" +LIBNEMO_RPC_AUTHORIZATION="fedcba9876543210fedcba9876543210" diff --git a/sample.env.mjs b/sample.env.mjs index 5e5284e..1b61f5f 100644 --- a/sample.env.mjs +++ b/sample.env.mjs @@ -5,7 +5,6 @@ // Save this file as `env.mjs` and replace the following with real values export const env = { - NODE_URL: "https://rpc.example.com", - API_KEY_NAME: "api_key", - LIBNEMO_RPC_API_KEY: "fedcba9876543210fedcba9876543210" + LIBNEMO_RPC_URL: "https://rpc.example.com", + LIBNEMO_RPC_AUTHORIZATION: "fedcba9876543210fedcba9876543210" } diff --git a/src/lib/rpc/index.ts b/src/lib/rpc/index.ts index c79e663..38c295b 100644 --- a/src/lib/rpc/index.ts +++ b/src/lib/rpc/index.ts @@ -6,23 +6,25 @@ export { Schema } /** * Represents a Nano network node. It primarily consists of a URL which will - * accept RPC calls, and an optional API key header construction can be passed if - * required by the node. Once instantiated, the Rpc object can be used to call - * any action supported by the Nano protocol. The URL protocol must be HTTPS; any - * other value will be changed automatically. + * accept RPC calls; an optional authentication header name can be passed if an + * API key is required by the node. Once instantiated, the Rpc object can be + * used to call any action supported by the Nano protocol. + * + * The URL protocol must be HTTPS; any other protocol will be changed to + * `https:` automatically. */ export class Rpc { - #u: URL - #n?: string + #url: URL + #auth?: string /** - * @param {(string|URL)} url - * @param {string} [apiKeyName] + * @param {(string|URL)} url Nano network node + * @param {string} [auth] optional API key */ - constructor (url: string | URL, apiKeyName?: string) { - this.#u = new URL(url) - this.#u.protocol = 'https:' - this.#n = apiKeyName + constructor (url: string | URL, auth?: string) { + this.#url = new URL(url) + this.#url.protocol = 'https:' + this.#auth = auth } /** @@ -36,10 +38,12 @@ export class Rpc { async post (action: unknown, data: unknown): Promise { const env = typeof process !== 'undefined' && 'env' in process ? process.env : null this.#validate(action) - const headers: Record = {} - headers['Content-Type'] = 'application/json' - if (this.#n && env?.LIBNEMO_RPC_API_KEY) { - headers[this.#n] = env.LIBNEMO_RPC_API_KEY + const headers: Record = { + 'Content-Type': 'application/json' + } + const auth = this.#auth ?? env?.LIBNEMO_RPC_AUTHORIZATION + if (auth) { + headers['Authorization'] = auth } if (data !== undefined && typeof data !== 'object') { throw new TypeError('Invalid RPC post data') @@ -51,7 +55,7 @@ export class Rpc { reqBody.action = action.toLowerCase() const aborter = new AbortController() - const req = new Request(this.#u, { + const req = new Request(this.#url, { signal: aborter.signal, method: 'POST', headers, @@ -63,17 +67,13 @@ export class Rpc { }, 10000) try { const res = await fetch(req) + const { status, statusText } = res const resBody = await res.json() - if (res.status !== 200) { - throw new Error(`${res.status} ${res.statusText}`, { cause: resBody }) - } - if (resBody.error != null) { - const msg = resBody.message == null - ? resBody.error - : `${resBody.error} ${resBody.message}` - throw new Error(msg) + const { error, message } = resBody + if (status !== 200 || error != null) { + throw new Error(`${status} ${statusText}`, { cause: { error, message } }) } - return resBody + return message } catch (err) { console.error(err) throw new Error(`RPC ${action} request failed`, { cause: err }) diff --git a/test/GLOBALS.mjs b/test/GLOBALS.mjs index b6159e2..c5fdc8b 100644 --- a/test/GLOBALS.mjs +++ b/test/GLOBALS.mjs @@ -2,7 +2,12 @@ //! SPDX-License-Identifier: GPL-3.0-or-later //@ts-nocheck -export { env } from '../env.mjs' +import { Rpc } from 'libnemo' +import { env } from '../env.mjs' + +export { env } +export const isNode = globalThis !== globalThis.window +export const rpc = new Rpc(env?.LIBNEMO_RPC_URL ?? '', env.LIBNEMO_RPC_AUTHORIZATION) if (globalThis.sessionStorage == null) { let _sessionStorage = {} @@ -55,8 +60,6 @@ const queue = { } } -export const isNode = globalThis !== globalThis.window - let NodeTestSuite, NodeTestTest if (isNode) { ({ suite: NodeTestSuite, test: NodeTestTest } = await import('node:test')) diff --git a/test/test.refresh-accounts.mjs b/test/test.refresh-accounts.mjs index 387ea15..a4f55ce 100644 --- a/test/test.refresh-accounts.mjs +++ b/test/test.refresh-accounts.mjs @@ -4,13 +4,12 @@ 'use strict' import { Account, Rpc, Wallet } from 'libnemo' -import { assert, env, suite, test } from './GLOBALS.mjs' +import { assert, rpc, suite, test } from './GLOBALS.mjs' import { NANO_TEST_VECTORS, TEST_PASSWORD } from './VECTORS.mjs' -const rpc = new Rpc(env.NODE_URL ?? '', env.API_KEY_NAME) - await Promise.all([ suite('Refreshing account info', { skip: false }, async () => { + await test('fetch balance, frontier, and representative', async () => { const wallet = await Wallet.load('BIP-44', TEST_PASSWORD, NANO_TEST_VECTORS.BIP39_SEED) await wallet.unlock(TEST_PASSWORD) diff --git a/test/test.tools.mjs b/test/test.tools.mjs index 8ce86c5..5f6a307 100644 --- a/test/test.tools.mjs +++ b/test/test.tools.mjs @@ -3,12 +3,10 @@ 'use strict' -import { Rpc, Tools, Wallet } from 'libnemo' -import { assert, click, env, suite, test } from './GLOBALS.mjs' +import { Tools, Wallet } from 'libnemo' +import { assert, click, rpc, suite, test } from './GLOBALS.mjs' import { MAX_RAW, MAX_SUPPLY, NANO_TEST_VECTORS, TEST_PASSWORD } from './VECTORS.mjs' -const rpc = new Rpc(env?.NODE_URL ?? '', env?.API_KEY_NAME) - await Promise.all([ suite('Tools unit conversion tests', async () => {