@tbd54566975/dwn-proxy-js
Version:
Bidirectional proxy between DWM's <> RESTful
8 lines (7 loc) • 12.8 kB
Source Map (JSON)
{
"version": 3,
"sources": ["../../src/dwn-http-client.ts", "../../src/dwn-json-rpc.ts", "../../src/dwn-http-server.ts", "../../src/dwn-did.ts", "../../src/dwn-proxy.ts"],
"sourcesContent": ["import { RecordsWriteMessage } from '@tbd54566975/dwn-sdk-js'\nimport { DidIonApi, DwnServiceEndpoint } from '@tbd54566975/dids'\nimport { createRequest, parseResponse } from './dwn-json-rpc.js'\nimport { DwnResponse } from './dwn-types.js'\n\nexport class DwnHttpClient {\n // TODO did resolver cache member variable\n\n #resolveEndpoint = async (did: string): Promise<string> => {\n // TODO use resolver cache\n const doc = (await new DidIonApi().resolve(did)).didDocument\n if (!doc) throw new Error() // TODO\n const service = doc.service?.find(x => x.type === 'DecentralizedWebNode')\n if (!service) throw new Error() // TODO\n return (service.serviceEndpoint as DwnServiceEndpoint).nodes[0]\n }\n\n send = async (target: string, message: RecordsWriteMessage, payload?: string): Promise<DwnResponse> => {\n const endpoint = await this.#resolveEndpoint(target)\n\n const fetchOpts = {\n method : 'POST',\n headers : {\n 'dwn-request': JSON.stringify(createRequest(target, message))\n }\n }\n if (payload) {\n fetchOpts.headers['content-type'] = 'application/octet-stream'\n fetchOpts['body'] = payload\n }\n\n const res = await fetch(endpoint, fetchOpts)\n\n let dwnResponse: DwnResponse\n if (res.headers.has('dwn-response'))\n dwnResponse = parseResponse(JSON.parse(res.headers.get('dwn-response')))\n else\n dwnResponse = parseResponse(JSON.parse(await res.text()))\n\n return dwnResponse\n }\n}", "import type { DwnMessage, DwnRequest, DwnResponse } from './dwn-types.js'\n\nexport type JsonRpcId = string | number | null\nexport type JsonRpcParams = DwnRequest // todo\nexport type JsonRpcVersion = '2.0'\n\nexport interface JsonRpcRequest {\n jsonrpc: JsonRpcVersion\n id?: JsonRpcId\n method: string\n params?: JsonRpcParams\n}\n\nexport interface JsonRpcResponse {\n jsonrpc: JsonRpcVersion\n id: JsonRpcId\n result: any\n error?: undefined\n}\n\nexport const parseRequest = (req: JsonRpcRequest): DwnRequest => {\n return req.params\n}\n\nexport const createRequest = (target: string, message: DwnMessage): JsonRpcRequest => {\n return {\n jsonrpc : '2.0',\n id : '', // TODO\n method : 'dwn.processMessage',\n params : {\n target,\n message\n }\n }\n}\n\nexport const parseResponse = (res: JsonRpcResponse): DwnResponse => {\n return res.result\n}\n\nexport const createResponse = (res: DwnResponse): JsonRpcResponse => {\n return {\n jsonrpc : '2.0',\n id : '',\n result : {\n reply: res.reply\n }\n }\n}", "import express from 'express'\nimport type { Express, Request, Response } from 'express'\nimport cors from 'cors'\nimport { DwnRequest, DwnResponse } from './dwn-types.js'\nimport { Dwn } from '@tbd54566975/dwn-sdk-js'\nimport { parseRequest, createResponse } from './dwn-json-rpc.js'\n\nexport interface IHandler {\n (req: DwnRequest): Promise<DwnResponse | void>\n}\n\ntype Options = {\n dwn: Dwn\n handler?: (req: DwnRequest) => Promise<DwnResponse | void>\n // [kw] can parameterize the json-rpc parsing\n}\n\nexport const readReq = async <T = any>(req: Request): Promise<T | void> => {\n let chunks = ''\n for await (const chunk of req) chunks += chunk\n if (chunks) return JSON.parse(chunks)\n}\n\nexport class DwnHttpServer {\n #options: Options\n api: Express\n\n constructor(options: Options) {\n this.#options = options\n this.api = express()\n\n this.api.use(cors({ exposedHeaders: 'dwn-response' }))\n this.api.get('/health', (_, res) => res.json({ ok: true }))\n\n this.api.get('/', (_req, res) => {\n // return a plain text string\n res.setHeader('content-type', 'text/plain')\n return res.send('please use a web5 client, for example: https://github.com/TBD54566975/web5-js ')\n })\n\n this.api.post('/', async (req: Request, res: Response) => {\n try {\n let dwnRequest: DwnRequest\n try {\n dwnRequest = parseRequest(JSON.parse(req.headers['dwn-request'] as string))\n if (!dwnRequest.payload) {\n const contentLength = req.headers['content-length']\n const transferEncoding = req.headers['transfer-encoding']\n const requestDataStream = (parseInt(contentLength) > 0 || transferEncoding !== undefined) ? req : undefined\n dwnRequest.payload = requestDataStream\n }\n } catch (err) {\n // todo integrate w/ web5-js expected response\n console.error('Failed to parse client request', err)\n res.status(400).end(err.message)\n return\n }\n\n const dwnResponse = this.#options.handler ? await this.#options.handler(dwnRequest) : undefined\n\n if (!dwnResponse) {\n const reply = await this.#options.dwn.processMessage(dwnRequest.target, dwnRequest.message, dwnRequest.payload as any)\n res.json(reply)\n } else if (dwnResponse.payload) {\n res.setHeader('content-type', 'application/octet-stream')\n res.setHeader('dwn-response', JSON.stringify(createResponse(dwnResponse)))\n dwnResponse.payload.pipe(res)\n } else {\n res.json(createResponse(dwnResponse))\n }\n } catch (err) {\n // todo integrate w/ web5-js expected response\n console.error('Failed to process message', err)\n res.status(500).end(err.message)\n return\n }\n })\n }\n\n listen = (port: number, callback?: () => void) => this.api.listen(port, callback)\n}", "import { DidIonApi } from '@tbd54566975/dids'\nimport { DidStateWithSignatureInput } from './dwn-types.js'\n\nexport const generateDidState = async (endpoint: string): Promise<DidStateWithSignatureInput> => {\n const didState = await new DidIonApi().create({\n services: [{\n id : 'dwn',\n type : 'DecentralizedWebNode',\n serviceEndpoint : {\n nodes: [ endpoint ]\n }\n }]\n })\n\n console.log(`Created DID & hosting service endpoint at ${endpoint}`)\n console.log(didState.id)\n\n const { keys } = didState\n const [ key ] = keys\n const { privateKeyJwk } = key\n const kidFragment = privateKeyJwk.kid || key.id\n const kid = `${didState.id}#${kidFragment}`\n return {\n ...didState,\n signatureInput: {\n privateJwk : privateKeyJwk,\n protectedHeader : { alg: privateKeyJwk.crv, kid }\n }\n }\n}", "import { DwnHttpClient } from './dwn-http-client.js'\nimport { DwnHttpServer, readReq } from './dwn-http-server.js'\nimport { generateDidState } from './dwn-did.js'\nimport type { DwnRequest, DwnResponse, DidStateWithSignatureInput } from './dwn-types.js'\nimport { Dwn } from '@tbd54566975/dwn-sdk-js'\n\ninterface IMatch {\n (req: DwnRequest): boolean\n}\ninterface IHandler {\n (dwnRequest: DwnRequest): Promise<void | DwnResponse>\n}\ninterface IMatchHandler {\n match: IMatch\n handler: IHandler\n}\n\nexport type DwnProxyOptions = Partial<{\n serviceEndpoint: string\n didState: DidStateWithSignatureInput\n}>\n\nexport class DwnProxy {\n options: DwnProxyOptions\n server: DwnHttpServer\n client: DwnHttpClient\n dwn: Dwn\n #handlers: Array<IMatchHandler> = []\n\n constructor(options: DwnProxyOptions) {\n this.options = options\n }\n\n #inbound = async (request: DwnRequest): Promise<DwnResponse | void> => {\n // [kw] could use a has map of sorts instead of iterating every time\n for (const { match, handler } of this.#handlers) {\n const isMatch = match(request)\n if (isMatch) {\n if (request.payload) // go ahead and read the payload into an object\n request.payload = await readReq(request.payload)\n return await handler(request)\n }\n }\n\n throw new Error('Unable to find middleware')\n }\n\n addHandler = (match: IMatch, handler: IHandler) => this.#handlers.push({ match, handler })\n\n async listen(port: number) {\n if (!this.options.didState)\n this.options.didState = await generateDidState(this.options.serviceEndpoint ?? `http://0.0.0.0:${port}`)\n\n this.dwn = await Dwn.create()\n\n this.server = new DwnHttpServer({\n dwn : this.dwn,\n handler : this.#inbound\n })\n\n this.server.listen(port, () => {\n console.log(`server listening on port ${port}`)\n })\n\n this.client = new DwnHttpClient()\n }\n}"],
"mappings": "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",
"names": ["DidIonApi", "DidIonApi"]
}