diff --git a/.changeset/fluffy-panthers-unite.md b/.changeset/fluffy-panthers-unite.md new file mode 100644 index 000000000..6f7ef7668 --- /dev/null +++ b/.changeset/fluffy-panthers-unite.md @@ -0,0 +1,5 @@ +--- +'gitbook': patch +--- + +Try to fix error on og image generation diff --git a/.changeset/perfect-laws-pay.md b/.changeset/perfect-laws-pay.md new file mode 100644 index 000000000..68754db95 --- /dev/null +++ b/.changeset/perfect-laws-pay.md @@ -0,0 +1,5 @@ +--- +'gitbook': patch +--- + +Fix server actions stability leading to no results found sometimes on search diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 1a84f872f..f25c1f5a2 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -41,6 +41,7 @@ jobs: run: bun run build:cloudflare env: SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + NEXT_SERVER_ACTIONS_ENCRYPTION_KEY: ${{ secrets.NEXT_SERVER_ACTIONS_ENCRYPTION_KEY }} SENTRY_ORG: ${{ vars.SENTRY_ORG }} SENTRY_PROJECT: ${{ vars.SENTRY_PROJECT }} SENTRY_DSN: ${{ vars.SENTRY_DSN }} diff --git a/packages/gitbook/next.config.js b/packages/gitbook/next.config.js index e2a136fad..032f72b6b 100644 --- a/packages/gitbook/next.config.js +++ b/packages/gitbook/next.config.js @@ -10,6 +10,7 @@ module.exports = withSentryConfig( GITBOOK_ASSETS_PREFIX: process.env.GITBOOK_ASSETS_PREFIX, GITBOOK_ICONS_URL: process.env.GITBOOK_ICONS_URL, GITBOOK_ICONS_TOKEN: process.env.GITBOOK_ICONS_TOKEN, + NEXT_SERVER_ACTIONS_ENCRYPTION_KEY: process.env.NEXT_SERVER_ACTIONS_ENCRYPTION_KEY, }, webpack(config, { dev, webpack }) { diff --git a/packages/gitbook/src/app/(site)/(core)/~gitbook/ogimage/[pageId]/route.tsx b/packages/gitbook/src/app/(site)/(core)/~gitbook/ogimage/[pageId]/route.tsx index 9de958c31..1bf5e7dff 100644 --- a/packages/gitbook/src/app/(site)/(core)/~gitbook/ogimage/[pageId]/route.tsx +++ b/packages/gitbook/src/app/(site)/(core)/~gitbook/ogimage/[pageId]/route.tsx @@ -5,15 +5,31 @@ import { NextRequest } from 'next/server'; import colorContrast from 'postcss-color-contrast/js'; import React from 'react'; +import { googleFontsMap } from '@/fonts'; import { getGitBookContextFromHeaders } from '@/lib/gitbook-context'; import { getAbsoluteHref } from '@/lib/links'; -import { tcls } from '@/lib/tailwind'; +import { filterOutNullable } from '@/lib/typescript'; import { getContentTitle } from '@/lib/utils'; import { PageIdParams, fetchPageData } from '../../../../fetch'; export const runtime = 'edge'; +async function loadGoogleFont(font: string, text: string) { + const url = `https://fonts.googleapis.com/css2?family=${font}&text=${encodeURIComponent(text)}`; + const css = await (await fetch(url)).text(); + const resource = css.match(/src: url\((.+)\) format\('(opentype|truetype)'\)/); + + if (resource) { + const response = await fetch(resource[1]); + if (response.status == 200) { + return await response.arrayBuffer(); + } + } + + throw new Error('failed to load font data'); +} + /** * Render the OpenGraph image for a space. */ @@ -21,30 +37,60 @@ export async function GET(req: NextRequest, { params }: { params: Promise res.arrayBuffer()); - const interBold = await fetch( - new URL('../../../../../../fonts/Inter/Inter-Bold.ttf', import.meta.url), - ).then((res) => res.arrayBuffer()); + // Compute all text to load only the necessary fonts + const contentTitle = customization.header.logo + ? '' + : getContentTitle(space, customization, site ?? null); + const pageTitle = page + ? page.title.length > 64 + ? page.title.slice(0, 64) + '...' + : page.title + : 'Not found'; + const pageDescription = + page?.description && page?.title.length <= 64 + ? page.description.length > 164 + ? page.description.slice(0, 164) + '...' + : page.description + : ''; + + const fontFamily = googleFontsMap[customization.styling.font] ?? 'Inter'; + + const regularText = pageDescription; + const boldText = `${contentTitle}${pageTitle}`; + + const fonts = ( + await Promise.all([ + regularText + ? loadGoogleFont(`${fontFamily}:wght@400`, regularText).then((data) => ({ + name: fontFamily, + data, + style: 'normal' as const, + weight: 400 as const, + })) + : null, + boldText + ? loadGoogleFont(`${fontFamily}:wght@700`, `${contentTitle}${pageTitle}`).then( + (data) => ({ + name: fontFamily, + data, + style: 'normal' as const, + weight: 700 as const, + }), + ) + : null, + ]) + ).filter(filterOutNullable); const theme = customization.themes.default; const useLightTheme = theme === 'light'; // We have no access to CSS variables, so we'll have to hardcode some values - const baseColors = { - light: '#ffffff', - dark: '#111827', - }; + const baseColors = { light: '#ffffff', dark: '#111827' }; let colors = { background: baseColors[theme], @@ -92,49 +138,17 @@ export async function GET(req: NextRequest, { params }: { params: Promise { - if ('icon' in customization.favicon) - return ( - Icon - ); - if ('emoji' in customization.favicon) - return ( - - {String.fromCodePoint(parseInt('0x' + customization.favicon.emoji))} - - ); - const src = getAbsoluteHref( - ctx, - `~gitbook/icon?size=medium&theme=${customization.themes.default}`, - true, - ); - return Icon; - })(); - return new ImageResponse( (
{/* Gradient */}
{/* Grid */} - Grid + Grid {/* Logo */} {customization.header.logo ? ( @@ -160,39 +170,46 @@ export async function GET(req: NextRequest, { params }: { params: Promise ) : ( -
- {favicon} -

- {getContentTitle(space, customization, site ?? null)} -

+
+ {(() => { + if ('icon' in customization.favicon) + return ( + Icon + ); + if ('emoji' in customization.favicon) + return ( + + {String.fromCodePoint( + parseInt('0x' + customization.favicon.emoji), + )} + + ); + const src = getAbsoluteHref( + ctx, + `~gitbook/icon?size=medium&theme=${customization.themes.default}`, + true, + ); + return Icon; + })()} +

{contentTitle}

)} {/* Title and description */} -
+

- {page - ? page.title.length > 64 - ? page.title.slice(0, 64) + '...' - : page.title - : 'Not found'} + {pageTitle}

- {page?.description && page?.title.length <= 64 ? ( -

- {page.description.length > 164 - ? page.description.slice(0, 164) + '...' - : page.description} -

+ {pageDescription ? ( +

{pageDescription}

) : null}
@@ -200,20 +217,7 @@ export async function GET(req: NextRequest, { params }: { params: Promise ); diff --git a/packages/gitbook/src/fonts/index.ts b/packages/gitbook/src/fonts/index.ts index 3bec43cd2..f3a555e09 100644 --- a/packages/gitbook/src/fonts/index.ts +++ b/packages/gitbook/src/fonts/index.ts @@ -203,3 +203,21 @@ export const fonts: { [fontName in CustomizationFont]: { className: string } } = [CustomizationFont.Ubuntu]: ubuntu, [CustomizationFont.ABCFavorit]: abcFavorit, }; + +export const googleFontsMap: { [fontName in CustomizationFont]: string } = { + [CustomizationFont.Inter]: 'Inter', + [CustomizationFont.FiraSans]: 'Fira Sans Extra Condensed', + [CustomizationFont.IBMPlexSerif]: 'IBM Plex Serif', + [CustomizationFont.Lato]: 'Lato', + [CustomizationFont.Merriweather]: 'Merriweather', + [CustomizationFont.NotoSans]: 'Noto Sans', + [CustomizationFont.OpenSans]: 'Open Sans', + [CustomizationFont.Overpass]: 'Overpass', + [CustomizationFont.Poppins]: 'Poppins', + [CustomizationFont.Raleway]: 'Raleway', + [CustomizationFont.Roboto]: 'Roboto', + [CustomizationFont.RobotoSlab]: 'Roboto Slab', + [CustomizationFont.SourceSansPro]: 'Source Sans 3', + [CustomizationFont.Ubuntu]: 'Ubuntu', + [CustomizationFont.ABCFavorit]: 'Inter', +}; diff --git a/packages/gitbook/src/lib/cache/backends.ts b/packages/gitbook/src/lib/cache/backends.ts index 20c54dc53..59379cfd2 100644 --- a/packages/gitbook/src/lib/cache/backends.ts +++ b/packages/gitbook/src/lib/cache/backends.ts @@ -1,5 +1,6 @@ import { cloudflareCache } from './cloudflare-cache'; import { cloudflareDOCache } from './cloudflare-do'; +import { cloudflareKVCache } from './cloudflare-kv'; import { memoryCache } from './memory'; export const cacheBackends = [ @@ -9,6 +10,8 @@ export const cacheBackends = [ // Cache local to the datacenter // It can't be purged globally but it's faster cloudflareCache, + // Cache global, but with slow replication + cloudflareKVCache, // Global cache with slower performances cloudflareDOCache, ]; diff --git a/turbo.json b/turbo.json index 687684328..bc3bdf90b 100644 --- a/turbo.json +++ b/turbo.json @@ -19,7 +19,8 @@ }, // Build the package for Cloudflare Pages "build:cloudflare": { - "dependsOn": ["^build", "generate"] + "dependsOn": ["^build", "generate"], + "env": ["NEXT_SERVER_ACTIONS_ENCRYPTION_KEY"] }, // Check the package for type errors "typecheck": {