From 1a3599443506bd860ec07012ab7c385ac86cb016 Mon Sep 17 00:00:00 2001 From: Franklin Waller Date: Wed, 21 Aug 2024 10:09:47 +0200 Subject: [PATCH] feat(header): add support for x-seda-json-path --- workspace/data-proxy-sdk/src/data-proxy.ts | 5 +- workspace/data-proxy/src/cli/run.ts | 2 +- workspace/data-proxy/src/constants.ts | 1 + workspace/data-proxy/src/proxy-server.ts | 60 +++++++++++++------- workspace/data-proxy/src/utils/query-json.ts | 10 +++- 5 files changed, 51 insertions(+), 27 deletions(-) diff --git a/workspace/data-proxy-sdk/src/data-proxy.ts b/workspace/data-proxy-sdk/src/data-proxy.ts index 794af34..217baa6 100644 --- a/workspace/data-proxy-sdk/src/data-proxy.ts +++ b/workspace/data-proxy-sdk/src/data-proxy.ts @@ -140,9 +140,8 @@ export class DataProxy { * * @param data */ - signData(data: unknown): SignedData { - const valueToSign = JSON.stringify(data); - const signResult = this.sign(Buffer.from(valueToSign)); + signData(data: string): SignedData { + const signResult = this.sign(Buffer.from(data)); return { publicKey: this.publicKey.toString("hex"), diff --git a/workspace/data-proxy/src/cli/run.ts b/workspace/data-proxy/src/cli/run.ts index 5533e19..bb7630f 100644 --- a/workspace/data-proxy/src/cli/run.ts +++ b/workspace/data-proxy/src/cli/run.ts @@ -10,10 +10,10 @@ import { PRIVATE_KEY_ENV_KEY, SERVER_PORT, } from "../constants"; +import logger from "../logger"; import { startProxyServer } from "../proxy-server"; import { tryAsync, trySync } from "../utils/try"; import { loadPrivateKey } from "./utils/private-key"; -import logger from "../logger"; export const runCommand = new Command("run") .description("Run the Data Proxy node") diff --git a/workspace/data-proxy/src/constants.ts b/workspace/data-proxy/src/constants.ts index df184ad..5b56fe4 100644 --- a/workspace/data-proxy/src/constants.ts +++ b/workspace/data-proxy/src/constants.ts @@ -11,6 +11,7 @@ export const DEFAULT_ENVIRONMENT: Environment = export const PROOF_HEADER_KEY = "x-seda-proof"; export const SIGNATURE_HEADER_KEY = "x-seda-signature"; export const PUBLIC_KEY_HEADER_KEY = "x-seda-publickey"; +export const JSON_PATH_HEADER_KEY = "x-seda-json-path"; export const PRIVATE_KEY_ENV_KEY = "SEDA_DATA_PROXY_PRIVATE_KEY"; export const PRIVATE_KEY = process.env[PRIVATE_KEY_ENV_KEY]; diff --git a/workspace/data-proxy/src/proxy-server.ts b/workspace/data-proxy/src/proxy-server.ts index 7e6cbec..5015909 100644 --- a/workspace/data-proxy/src/proxy-server.ts +++ b/workspace/data-proxy/src/proxy-server.ts @@ -4,6 +4,7 @@ import { Maybe } from "true-myth"; import { type Config, getHttpMethods } from "./config-parser"; import { DEFAULT_PROXY_ROUTE_GROUP, + JSON_PATH_HEADER_KEY, PROOF_HEADER_KEY, SERVER_PORT, } from "./constants"; @@ -48,6 +49,7 @@ export function startProxyServer( routeMethod, route.path, async ({ headers, params, body, query }) => { + // Verification with the SEDA chain that the overlay node is eligible if (!serverOptions.disableProof) { const proofHeader = Maybe.of(headers[PROOF_HEADER_KEY]); @@ -70,13 +72,13 @@ export function startProxyServer( // Add the request search params (?one=two) to the upstream url const requestSearchParams = createUrlSearchParams(query); - let proxyUrl = replaceParams(route.upstreamUrl, params); - proxyUrl = injectSearchParamsInUrl( - proxyUrl, + let upstreamUrl = replaceParams(route.upstreamUrl, params); + upstreamUrl = injectSearchParamsInUrl( + upstreamUrl, requestSearchParams, ).toString(); - const proxyHeaders = new Headers(); + const upstreamHeaders = new Headers(); // Redirect all headers given by the requester for (const [key, value] of Object.entries(headers)) { @@ -84,51 +86,54 @@ export function startProxyServer( continue; } - proxyHeaders.append(key, value); + upstreamHeaders.append(key, value); } // Inject all configured headers by the data proxy node configuration for (const [key, value] of Object.entries(route.headers)) { - proxyHeaders.append(key, replaceParams(value, params)); + upstreamHeaders.append(key, replaceParams(value, params)); } // Required to make it work... - proxyHeaders.delete("host"); + upstreamHeaders.delete("host"); logger.debug( - `${routeMethod} ${proxyGroup}${route.path} -> ${proxyUrl}`, + `${routeMethod} ${proxyGroup}${route.path} -> ${upstreamUrl}`, ); - const proxyResponse = await tryAsync(async () => - fetch(proxyUrl, { + const upstreamResponse = await tryAsync(async () => + fetch(upstreamUrl, { method: routeMethod, - headers: proxyHeaders, + headers: upstreamHeaders, body: body as BodyInit, }), ); - if (proxyResponse.isErr) { + if (upstreamResponse.isErr) { return createErrorResponse( - `Proxying URL ${route.path} failed: ${proxyResponse.error}`, + `Proxying URL ${route.path} failed: ${upstreamResponse.error}`, 500, ); } - const textResponse = await tryAsync( - async () => await proxyResponse.value.text(), + const upstreamTextResponse = await tryAsync( + async () => await upstreamResponse.value.text(), ); - if (textResponse.isErr) { + if (upstreamTextResponse.isErr) { return createErrorResponse( - `Parsing ${route.path} response to JSON failed: ${textResponse.error}`, + `Parsing ${route.path} response to JSON failed: ${upstreamTextResponse.error}`, 500, ); } - let responseData: string = textResponse.value; + let responseData: string = upstreamTextResponse.value; if (route.jsonPath) { - const data = queryJson(textResponse.value, route.jsonPath); + const data = queryJson( + upstreamTextResponse.value, + route.jsonPath, + ); if (data.isErr) { return createErrorResponse(data.error, 500); @@ -137,13 +142,28 @@ export function startProxyServer( responseData = JSON.stringify(data.value); } + const jsonPathRequestHeader = Maybe.of( + headers[JSON_PATH_HEADER_KEY], + ); + + if (jsonPathRequestHeader.isJust) { + // We query on the responseData since the proxy node can configure to not show all data + const data = queryJson(responseData, jsonPathRequestHeader.value); + + if (data.isErr) { + return createErrorResponse(data.error, 400); + } + + responseData = JSON.stringify(data.value); + } + const signature = dataProxy.signData(responseData); const responseHeaders = new Headers(); // Forward all headers that are configured in the config.json for (const forwardHeaderKey of route.forwardRepsonseHeaders) { const forwardHeaderValue = - proxyResponse.value.headers.get(forwardHeaderKey); + upstreamResponse.value.headers.get(forwardHeaderKey); if (forwardHeaderValue) { responseHeaders.append(forwardHeaderKey, forwardHeaderValue); diff --git a/workspace/data-proxy/src/utils/query-json.ts b/workspace/data-proxy/src/utils/query-json.ts index 1786766..1dfb46f 100644 --- a/workspace/data-proxy/src/utils/query-json.ts +++ b/workspace/data-proxy/src/utils/query-json.ts @@ -15,11 +15,15 @@ export function queryJson( return Result.err(`Parsing as JSON failed: ${jsonData.error}`); } - const data = JSONPath.query(jsonData.value, path); + const data = trySync(() => JSONPath.query(jsonData.value, path)); - if (!data.length) { + if (data.isErr) { + return Result.err(`Could not query JSON: ${data.error}`); + } + + if (!data.value.length) { return Result.err(`Quering JSON with ${path} returned null`); } - return Result.ok(data[0]); + return Result.ok(data.value[0]); }