@tbd54566975/dwn-proxy-js
Version:
Bidirectional proxy between DWM's <> RESTful
81 lines (70 loc) • 2.87 kB
text/typescript
import express from 'express'
import type { Express, Request, Response } from 'express'
import cors from 'cors'
import { DwnRequest, DwnResponse } from './dwn-types.js'
import { Dwn } from '@tbd54566975/dwn-sdk-js'
import { parseRequest, createResponse } from './dwn-json-rpc.js'
export interface IHandler {
(req: DwnRequest): Promise<DwnResponse | void>
}
type Options = {
dwn: Dwn
handler?: (req: DwnRequest) => Promise<DwnResponse | void>
// [kw] can parameterize the json-rpc parsing
}
export const readReq = async <T = any>(req: Request): Promise<T | void> => {
let chunks = ''
for await (const chunk of req) chunks += chunk
if (chunks) return JSON.parse(chunks)
}
export class DwnHttpServer {
#options: Options
api: Express
constructor(options: Options) {
this.#options = options
this.api = express()
this.api.use(cors({ exposedHeaders: 'dwn-response' }))
this.api.get('/health', (_, res) => res.json({ ok: true }))
this.api.get('/', (_req, res) => {
// return a plain text string
res.setHeader('content-type', 'text/plain')
return res.send('please use a web5 client, for example: https://github.com/TBD54566975/web5-js ')
})
this.api.post('/', async (req: Request, res: Response) => {
try {
let dwnRequest: DwnRequest
try {
dwnRequest = parseRequest(JSON.parse(req.headers['dwn-request'] as string))
if (!dwnRequest.payload) {
const contentLength = req.headers['content-length']
const transferEncoding = req.headers['transfer-encoding']
const requestDataStream = (parseInt(contentLength) > 0 || transferEncoding !== undefined) ? req : undefined
dwnRequest.payload = requestDataStream
}
} catch (err) {
// todo integrate w/ web5-js expected response
console.error('Failed to parse client request', err)
res.status(400).end(err.message)
return
}
const dwnResponse = this.#options.handler ? await this.#options.handler(dwnRequest) : undefined
if (!dwnResponse) {
const reply = await this.#options.dwn.processMessage(dwnRequest.target, dwnRequest.message, dwnRequest.payload as any)
res.json(reply)
} else if (dwnResponse.payload) {
res.setHeader('content-type', 'application/octet-stream')
res.setHeader('dwn-response', JSON.stringify(createResponse(dwnResponse)))
dwnResponse.payload.pipe(res)
} else {
res.json(createResponse(dwnResponse))
}
} catch (err) {
// todo integrate w/ web5-js expected response
console.error('Failed to process message', err)
res.status(500).end(err.message)
return
}
})
}
listen = (port: number, callback?: () => void) => this.api.listen(port, callback)
}