diff --git a/keyserver/flow-typed/npm/hono_vx.x.x.js b/keyserver/flow-typed/npm/hono_vx.x.x.js --- a/keyserver/flow-typed/npm/hono_vx.x.x.js +++ b/keyserver/flow-typed/npm/hono_vx.x.x.js @@ -13,3 +13,12 @@ } } +declare module 'hono/cors' { + import type { MiddlewareHandler } from 'hono'; + + declare export var cors: ({ + +origin?: string, + +allowMethods?: $ReadOnlyArray, + }) => MiddlewareHandler; +} + diff --git a/keyserver/src/frog/frog.js b/keyserver/src/frog/frog.js --- a/keyserver/src/frog/frog.js +++ b/keyserver/src/frog/frog.js @@ -7,15 +7,42 @@ // eslint-disable-next-line import/extensions import { serveStatic } from '@hono/node-server/serve-static'; import { Button, Frog } from 'frog'; +// eslint-disable-next-line import/extensions +import { cors } from 'hono/cors'; import { inviteLinkURL } from 'lib/facts/links.js'; import { ignorePromiseRejections } from 'lib/utils/promises.js'; import { neynarClient } from '../utils/fc-cache.js'; import { redisCache } from '../utils/redis-cache.js'; +import { getKeyserverURLFacts } from '../utils/urls.js'; + +const keyserverURLFacts = getKeyserverURLFacts(); +const frogHonoPort: number = parseInt(process.env.FROG_HONO_PORT, 10) || 3001; +const frogHonoURL: string = (() => { + if (process.env.FROG_HONO_URL) { + return process.env.FROG_HONO_URL; + } else { + return `http://localhost:${frogHonoPort}`; + } +})(); function startFrogHonoServer() { - const frogApp = new Frog({ title: 'Comm Frame' }); + const frogApp = new Frog({ + title: 'Comm Frame', + // Mirrors the express server's reverse proxy path so that + // frog constructs URLs with the correct path in the HTML response, + // ensuring frame requests are forwarded to the frog hono server. + basePath: `${keyserverURLFacts?.baseRoutePath ?? '/'}frog/`, + }); + + frogApp.hono.use( + '*', + cors({ + origin: '*', + allowMethods: ['GET'], + }), + ); frogApp.hono.use( '/default_farcaster_channel_cover.png', @@ -151,8 +178,8 @@ serve({ fetch: frogApp.fetch, - port: parseInt(process.env.FROG_PORT, 10) || 3001, + port: frogHonoPort, }); } -export { startFrogHonoServer }; +export { startFrogHonoServer, frogHonoURL }; diff --git a/keyserver/src/keyserver.js b/keyserver/src/keyserver.js --- a/keyserver/src/keyserver.js +++ b/keyserver/src/keyserver.js @@ -9,6 +9,7 @@ import express from 'express'; import type { $Request, $Response } from 'express'; import expressWs from 'express-ws'; +import fetch from 'node-fetch'; import os from 'os'; import qrcode from 'qrcode'; import stoppable from 'stoppable'; @@ -23,7 +24,7 @@ import { latestWrapInTransactionAndBlockRequestsVersion } from './database/migration-config.js'; import { migrate } from './database/migrations.js'; import { jsonEndpoints } from './endpoints.js'; -import { startFrogHonoServer } from './frog/frog.js'; +import { startFrogHonoServer, frogHonoURL } from './frog/frog.js'; import { logEndpointMetrics } from './middleware/endpoint-profiling.js'; import { emailSubscriptionResponder } from './responders/comm-landing-responders.js'; import { taggedCommFarcasterResponder } from './responders/farcaster-webhook-responders.js'; @@ -299,6 +300,29 @@ if (keyserverCorsOptions) { keyserverRouter.use(cors(keyserverCorsOptions)); } + keyserverRouter.use('/frog', async (req: $Request, res: $Response) => { + try { + const targetUrl = `${frogHonoURL}${req.originalUrl}`; + + const fetchResponse = await fetch(targetUrl, { + method: req.method, + headers: req.headers, + body: + req.method !== 'GET' && req.method !== 'HEAD' + ? JSON.stringify(req.body) + : undefined, + }); + + for (const [key, value] of fetchResponse.headers.entries()) { + res.setHeader(key, value); + } + + fetchResponse.body.pipe(res); + } catch (error) { + console.log('Error forwarding request to frog hono server:', error); + res.status(500).send('Internal Server Error'); + } + }); keyserverRouter.post( '/fc_comm_tagged',