|
| 1 | +import type { OgImageComponent } from '../../../types' |
| 2 | +import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs' |
| 3 | +// @ts-expect-error untyped |
| 4 | +import { componentNames } from '#og-image-virtual/component-names.mjs' |
| 5 | +import { join } from 'pathe' |
| 6 | +import { hashOgImageOptions } from '../../../shared/urlEncoding' |
| 7 | +import { useOgImageRuntimeConfig } from '../../utils' |
| 8 | + |
| 9 | +interface CachedImage { |
| 10 | + data: string // base64 |
| 11 | + expiresAt: number |
| 12 | +} |
| 13 | + |
| 14 | +/** |
| 15 | + * Get the component hash for a given component name |
| 16 | + */ |
| 17 | +export function getComponentHash(componentName: string): string { |
| 18 | + const components = componentNames as OgImageComponent[] |
| 19 | + const component = components.find( |
| 20 | + c => c.pascalName === componentName || c.kebabName === componentName, |
| 21 | + ) |
| 22 | + return component?.hash || '' |
| 23 | +} |
| 24 | + |
| 25 | +/** |
| 26 | + * Generate a cache key that includes options, component hash, and version |
| 27 | + */ |
| 28 | +export function generateBuildCacheKey( |
| 29 | + options: Record<string, any>, |
| 30 | + extension: string, |
| 31 | +): string { |
| 32 | + const { version } = useOgImageRuntimeConfig() |
| 33 | + const componentHash = getComponentHash(options.component || 'NuxtSeo') |
| 34 | + const hash = hashOgImageOptions(options, componentHash, version) |
| 35 | + return `${hash}.${extension}` |
| 36 | +} |
| 37 | + |
| 38 | +/** |
| 39 | + * Check if an image exists in the build cache |
| 40 | + */ |
| 41 | +export function getBuildCachedImage( |
| 42 | + options: Record<string, any>, |
| 43 | + extension: string, |
| 44 | +): Buffer | null { |
| 45 | + const { buildCacheDir } = useOgImageRuntimeConfig() |
| 46 | + if (!buildCacheDir) |
| 47 | + return null |
| 48 | + |
| 49 | + const cacheKey = generateBuildCacheKey(options, extension) |
| 50 | + const cachePath = join(buildCacheDir, cacheKey) |
| 51 | + |
| 52 | + if (!existsSync(cachePath)) |
| 53 | + return null |
| 54 | + |
| 55 | + const cached: CachedImage = JSON.parse(readFileSync(cachePath, 'utf-8')) |
| 56 | + |
| 57 | + // Check expiry |
| 58 | + if (cached.expiresAt && cached.expiresAt < Date.now()) { |
| 59 | + return null |
| 60 | + } |
| 61 | + |
| 62 | + return Buffer.from(cached.data, 'base64') |
| 63 | +} |
| 64 | + |
| 65 | +/** |
| 66 | + * Save an image to the build cache |
| 67 | + */ |
| 68 | +export function setBuildCachedImage( |
| 69 | + options: Record<string, any>, |
| 70 | + extension: string, |
| 71 | + data: Buffer | Uint8Array, |
| 72 | + maxAgeSeconds: number, |
| 73 | +): void { |
| 74 | + const { buildCacheDir } = useOgImageRuntimeConfig() |
| 75 | + if (!buildCacheDir) |
| 76 | + return |
| 77 | + |
| 78 | + const cacheKey = generateBuildCacheKey(options, extension) |
| 79 | + const cachePath = join(buildCacheDir, cacheKey) |
| 80 | + |
| 81 | + // Ensure cache directory exists |
| 82 | + if (!existsSync(buildCacheDir)) { |
| 83 | + mkdirSync(buildCacheDir, { recursive: true }) |
| 84 | + } |
| 85 | + |
| 86 | + const cached: CachedImage = { |
| 87 | + data: Buffer.from(data).toString('base64'), |
| 88 | + expiresAt: Date.now() + (maxAgeSeconds * 1000), |
| 89 | + } |
| 90 | + |
| 91 | + writeFileSync(cachePath, JSON.stringify(cached)) |
| 92 | +} |
0 commit comments