@@ -7,7 +7,9 @@ import { type PageParams, fetchPageData } from '@/components/SitePage';
7
7
import { getFontSourcesToPreload } from '@/fonts/custom' ;
8
8
import { getAssetURL } from '@/lib/assets' ;
9
9
import { filterOutNullable } from '@/lib/typescript' ;
10
+ import { getCacheTag } from '@gitbook/cache-tags' ;
10
11
import type { GitBookSiteContext } from '@v2/lib/context' ;
12
+ import { getCloudflareContext } from '@v2/lib/data/cloudflare' ;
11
13
import { getResizedImageURL } from '@v2/lib/images' ;
12
14
13
15
const googleFontsMap : { [ fontName in CustomizationDefaultFont ] : string } = {
@@ -169,8 +171,12 @@ export async function serveOGImage(baseContext: GitBookSiteContext, params: Page
169
171
{ String . fromCodePoint ( Number . parseInt ( `0x${ customization . favicon . emoji } ` ) ) }
170
172
</ span >
171
173
) ;
172
- const src = linker . toAbsoluteURL (
173
- linker . toPathInSpace ( `~gitbook/icon?size=medium&theme=${ customization . themes . default } ` )
174
+ const src = await readSelfImage (
175
+ linker . toAbsoluteURL (
176
+ linker . toPathInSpace (
177
+ `~gitbook/icon?size=medium&theme=${ customization . themes . default } `
178
+ )
179
+ )
174
180
) ;
175
181
return < img src = { src } alt = "Icon" width = { 40 } height = { 40 } tw = "mr-4" /> ;
176
182
} ) ( ) ;
@@ -192,7 +198,11 @@ export async function serveOGImage(baseContext: GitBookSiteContext, params: Page
192
198
/>
193
199
194
200
{ /* Grid */ }
195
- < img tw = "absolute inset-0 w-[100vw] h-[100vh]" src = { gridAsset } alt = "Grid" />
201
+ < img
202
+ tw = "absolute inset-0 w-[100vw] h-[100vh]"
203
+ src = { await readStaticImage ( gridAsset ) }
204
+ alt = "Grid"
205
+ />
196
206
197
207
{ /* Logo */ }
198
208
{ customization . header . logo ? (
@@ -228,6 +238,18 @@ export async function serveOGImage(baseContext: GitBookSiteContext, params: Page
228
238
width : 1200 ,
229
239
height : 630 ,
230
240
fonts : fonts . length ? fonts : undefined ,
241
+ headers : {
242
+ 'cache-tag' : [
243
+ getCacheTag ( {
244
+ tag : 'site' ,
245
+ site : baseContext . site . id ,
246
+ } ) ,
247
+ getCacheTag ( {
248
+ tag : 'space' ,
249
+ space : baseContext . space . id ,
250
+ } ) ,
251
+ ] . join ( ',' ) ,
252
+ } ,
231
253
}
232
254
) ;
233
255
}
@@ -285,3 +307,55 @@ async function loadCustomFont(input: { url: string; weight: 400 | 700 }) {
285
307
weight,
286
308
} ;
287
309
}
310
+
311
+ /**
312
+ * Fetch a resource from the function itself.
313
+ * To avoid error with worker to worker requests in the same zone, we use the `WORKER_SELF_REFERENCE` binding.
314
+ */
315
+ async function fetchSelf ( url : string ) {
316
+ const cloudflare = getCloudflareContext ( ) ;
317
+ if ( cloudflare ?. env . WORKER_SELF_REFERENCE ) {
318
+ return await cloudflare . env . WORKER_SELF_REFERENCE . fetch ( url ) ;
319
+ }
320
+
321
+ return await fetch ( url ) ;
322
+ }
323
+
324
+ /**
325
+ * Read an image from a response as a base64 encoded string.
326
+ */
327
+ async function readImage ( response : Response ) {
328
+ const contentType = response . headers . get ( 'content-type' ) ;
329
+ if ( ! contentType || ! contentType . startsWith ( 'image/' ) ) {
330
+ throw new Error ( `Invalid content type: ${ contentType } ` ) ;
331
+ }
332
+
333
+ const arrayBuffer = await response . arrayBuffer ( ) ;
334
+ const base64 = Buffer . from ( arrayBuffer ) . toString ( 'base64' ) ;
335
+ return `data:${ contentType } ;base64,${ base64 } ` ;
336
+ }
337
+
338
+ const staticImagesCache = new Map < string , string > ( ) ;
339
+
340
+ /**
341
+ * Read a static image and cache it in memory.
342
+ */
343
+ async function readStaticImage ( url : string ) {
344
+ const cached = staticImagesCache . get ( url ) ;
345
+ if ( cached ) {
346
+ return cached ;
347
+ }
348
+
349
+ const image = await readSelfImage ( url ) ;
350
+ staticImagesCache . set ( url , image ) ;
351
+ return image ;
352
+ }
353
+
354
+ /**
355
+ * Read an image from GitBook itself.
356
+ */
357
+ async function readSelfImage ( url : string ) {
358
+ const response = await fetchSelf ( url ) ;
359
+ const image = await readImage ( response ) ;
360
+ return image ;
361
+ }
0 commit comments