|  | 
|  | 1 | +// Cloudflare Worker proxy for fine-tx | 
|  | 2 | + | 
|  | 3 | +import { Hono } from 'hono'; | 
|  | 4 | +import { proxy } from 'hono/proxy'; | 
|  | 5 | +import { logger } from 'hono/logger'; | 
|  | 6 | +import convict from 'convict'; | 
|  | 7 | +import { LRUCache } from 'lru-cache'; | 
|  | 8 | + | 
|  | 9 | +const config = convict({ | 
|  | 10 | +  port: { | 
|  | 11 | +    doc: 'The port to bind the proxy server to', | 
|  | 12 | +    format: 'port', | 
|  | 13 | +    default: 5173, | 
|  | 14 | +    env: 'PORT', | 
|  | 15 | +  }, | 
|  | 16 | +  betterfrostUrl: { | 
|  | 17 | +    doc: 'URL for the Betterfrost API', | 
|  | 18 | +    format: String, | 
|  | 19 | +    default: 'http://0.0.0.0:3001', | 
|  | 20 | +    env: 'VITE_BETTERFROST_URL', | 
|  | 21 | +  }, | 
|  | 22 | +  blockfrostProjectId: { | 
|  | 23 | +    doc: 'Project ID for Blockfrost API (optional, if not using Betterfrost)', | 
|  | 24 | +    format: String, | 
|  | 25 | +    default: '', | 
|  | 26 | +    env: 'VITE_BLOCKFROST_PROJECT_ID', | 
|  | 27 | +  }, | 
|  | 28 | +  cacheSize: { | 
|  | 29 | +    doc: 'Size of the cache in bytes', | 
|  | 30 | +    format: Number, | 
|  | 31 | +    default: 0, | 
|  | 32 | +    env: 'PROXY_CACHE_SIZE', | 
|  | 33 | +  }, | 
|  | 34 | +  registryUrl: { | 
|  | 35 | +    doc: 'URL for the token registry service', | 
|  | 36 | +    format: String, | 
|  | 37 | +    default: 'https://public.liqwid.finance/v4', | 
|  | 38 | +    env: 'VITE_REGISTRY_URL', | 
|  | 39 | +  }, | 
|  | 40 | +}); | 
|  | 41 | + | 
|  | 42 | +config.validate({ allowed: 'strict' }); | 
|  | 43 | + | 
|  | 44 | +const app = new Hono(); | 
|  | 45 | + | 
|  | 46 | +app.use(logger()); | 
|  | 47 | + | 
|  | 48 | +const cache = new LRUCache<string, Blob>({ | 
|  | 49 | +  max: config.get('cacheSize'), | 
|  | 50 | +  ttl: 60 * 30 * 1000, | 
|  | 51 | +}); | 
|  | 52 | + | 
|  | 53 | +function key(url: string, options: RequestInit) { | 
|  | 54 | +  return `${url}-${options.body}`; | 
|  | 55 | +} | 
|  | 56 | + | 
|  | 57 | +async function betterfrostProxy( | 
|  | 58 | +  url: string, | 
|  | 59 | +  options: RequestInit, | 
|  | 60 | +): Promise<Response> { | 
|  | 61 | +  const k = key(url, options); | 
|  | 62 | +  const cachedResponse = cache.get(k); | 
|  | 63 | +  if (cachedResponse) { | 
|  | 64 | +    console.log(`--> CACHE HIT ${k}`); | 
|  | 65 | +    return new Response(cachedResponse); | 
|  | 66 | +  } | 
|  | 67 | + | 
|  | 68 | +  console.log(`--> CACHE MISS ${k}`); | 
|  | 69 | + | 
|  | 70 | +  const response = await proxy(url, { | 
|  | 71 | +    ...options, | 
|  | 72 | +  }); | 
|  | 73 | + | 
|  | 74 | +  const blob = await response.blob(); | 
|  | 75 | + | 
|  | 76 | +  console.log(`--> CACHE SET ${k} (${blob.size} bytes)`); | 
|  | 77 | + | 
|  | 78 | +  const res = new Response(blob); | 
|  | 79 | + | 
|  | 80 | +  cache.set(k, blob); | 
|  | 81 | + | 
|  | 82 | +  return res; | 
|  | 83 | +} | 
|  | 84 | + | 
|  | 85 | +// Proxy routes | 
|  | 86 | +app.all('/betterfrost/*', async (c) => { | 
|  | 87 | +  const targetUrl = c.req.path.replace('/betterfrost', ''); | 
|  | 88 | + | 
|  | 89 | +  const extraHeaders = config.get('blockfrostProjectId') | 
|  | 90 | +    ? ({ | 
|  | 91 | +        project_id: config.get('blockfrostProjectId') ?? '', | 
|  | 92 | +      } as Record<string, string>) | 
|  | 93 | +    : {}; | 
|  | 94 | + | 
|  | 95 | +  const response = await betterfrostProxy( | 
|  | 96 | +    `${config.get('betterfrostUrl')}${targetUrl}`, | 
|  | 97 | +    { | 
|  | 98 | +      method: c.req.method, | 
|  | 99 | +      body: c.req.raw.body, | 
|  | 100 | +      headers: { | 
|  | 101 | +        ...c.req.raw.headers, | 
|  | 102 | +        'User-Agent': c.req.raw.headers['User-Agent'], | 
|  | 103 | +        ...extraHeaders, | 
|  | 104 | +      }, | 
|  | 105 | +    }, | 
|  | 106 | +  ); | 
|  | 107 | + | 
|  | 108 | +  return response; | 
|  | 109 | +}); | 
|  | 110 | + | 
|  | 111 | +app.all('/ogmios/*', async (c) => { | 
|  | 112 | +  return c.json({ error: 'OGMIOS_URL not set. Not available!' }); | 
|  | 113 | +}); | 
|  | 114 | + | 
|  | 115 | +app.get('/registry-proxy/:path', async (c) => { | 
|  | 116 | +  return proxy(`${config.get('registryUrl')}/${c.req.param('path')}`); | 
|  | 117 | +}); | 
|  | 118 | + | 
|  | 119 | +console.log(` | 
|  | 120 | +    Proxy started on http://0.0.0.0:${config.get('port')} 🚀 | 
|  | 121 | +
 | 
|  | 122 | +    Requests for /betterfrost    => ${config.get('betterfrostUrl')} | 
|  | 123 | +    Requests for /ogmios         => Not available | 
|  | 124 | +    Requests for /registry-proxy => ${config.get('registryUrl')} | 
|  | 125 | +`); | 
|  | 126 | + | 
|  | 127 | +// Start the server | 
|  | 128 | +export default { | 
|  | 129 | +  port: config.get('port'), | 
|  | 130 | +  fetch: app.fetch, | 
|  | 131 | +}; | 
0 commit comments