diff --git a/common/src/contract-seo.ts b/common/src/contract-seo.ts index cd3a2fc9c8..72b61cff44 100644 --- a/common/src/contract-seo.ts +++ b/common/src/contract-seo.ts @@ -17,6 +17,7 @@ export const getContractOGProps = ( creatorName, outcomeType, creatorAvatarUrl, + id, } = contract const topAnswer = @@ -58,6 +59,7 @@ export const getContractOGProps = ( resolution, topAnswer: topAnswer?.text, bountyLeft: bountyLeft, + contractId: id, } } @@ -73,6 +75,7 @@ export type OgCardProps = { topAnswer?: string bountyLeft?: string // number points?: string // base64ified points + contractId?: string } export function getSeoDescription(contract: Contract) { diff --git a/web/pages/api/og/market.tsx b/web/pages/api/og/market.tsx index 73c66667d2..c3d7d18837 100644 --- a/web/pages/api/og/market.tsx +++ b/web/pages/api/og/market.tsx @@ -3,12 +3,13 @@ import { ImageResponseOptions } from '@vercel/og/dist/types' import { NextRequest } from 'next/server' import { OgMarket } from 'web/components/og/og-market' import { classToTw } from 'web/components/og/utils' -import { OgCardProps } from 'common/contract-seo' +import { getContractOGProps, OgCardProps } from 'common/contract-seo' export const config = { runtime: 'edge' } -export const getCardOptions = async () => { +export const getCardOptions = async (): Promise => { const [light, med] = await Promise.all([figtreeLightData, figtreeMediumData]) + // https://vercel.com/docs/functions/og-image-generation/og-image-api return { width: 600, height: 315, @@ -24,6 +25,11 @@ export const getCardOptions = async () => { style: 'normal', }, ], + headers: { + // max-age is in seconds. Vercel defaults to a very large value, but + // we want to show fresh data, so we override here. + 'cache-control': 'public, no-transform, max-age=30', + }, } } @@ -36,16 +42,42 @@ const figtreeMediumData = fetch(FIGTREE_MED_URL).then((res) => res.arrayBuffer() ) +async function getFreshOgMarketProps( + contractId: string | undefined, + origin: string +): Promise { + if (contractId) { + try { + const resp = await fetch(`${origin}/api/v0/market/${contractId}`) + const contract = resp.ok && (await resp.json()) + + if (contract) { + return getContractOGProps(contract) + } + } catch (e) { + console.log( + `Failed to fetch contract ${contractId} when rendering OG image`, + e + ) + } + } + return undefined +} + export default async function handler(req: NextRequest) { try { - const { searchParams } = new URL(req.url) + const { searchParams, origin } = new URL(req.url) const options = await getCardOptions() - const OgMarketProps = Object.fromEntries( + const ogMarketPropsFromParams = Object.fromEntries( searchParams.entries() ) as OgCardProps - const image = OgMarket(OgMarketProps) + const { contractId } = ogMarketPropsFromParams + + const ogMarketPropsFromDb = await getFreshOgMarketProps(contractId, origin) + + const image = OgMarket(ogMarketPropsFromDb ?? ogMarketPropsFromParams) - return new ImageResponse(classToTw(image), options as ImageResponseOptions) + return new ImageResponse(classToTw(image), options) } catch (e: any) { console.log(`${e.message}`) return new Response(`Failed to generate the image`, {