Skip to content

Commit 31d17c8

Browse files
committed
Fix font path in social card generator
1 parent 0eb585f commit 31d17c8

3 files changed

Lines changed: 81 additions & 91 deletions

File tree

.cache/pages.json

Lines changed: 23 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,29 @@
22
"about",
33
{
44
"articles": [
5+
"kubernetes-pod-disruption-budget-autoscaler-node-rotation"
6+
]
7+
},
8+
{
9+
"case-studies": [
10+
"cicd-pipeline-overhaul",
11+
"cloud-migration-logistics-platform",
12+
"compliance-automation-hipaa",
13+
"cost-optimization-saas-startup",
14+
"event-driven-architecture-migration",
15+
"incident-response-modernization",
16+
"infrastructure-as-code-transformation",
17+
"internal-developer-platform-backstage",
18+
"kubernetes-adoption-fintech",
19+
"observability-stack-implementation",
20+
"self-service-infrastructure",
21+
"zero-trust-network-architecture"
22+
]
23+
},
24+
"consent",
25+
"contact",
26+
{
27+
"deep-dive": [
528
"alert-fatigue-reduction-triage-actionable-alerts",
629
"api-deprecation-sunset-headers-consumer-migration",
730
"api-gateway-metrics-traces-logs-debugging",
@@ -37,7 +60,6 @@
3760
"kubernetes-hpa-autoscaling-metrics-tuning-latency",
3861
"kubernetes-ingress-gateway-api-comparison-migration",
3962
"kubernetes-multi-cluster-fleet-management-configuration",
40-
"kubernetes-pod-disruption-budget-autoscaler-node-rotation",
4163
"kubernetes-pod-resource-requests-limits-qos-classes",
4264
"kubernetes-secrets-external-secrets-operator-csi-vault",
4365
"legacy-code-testing-characterization-tests-seams",
@@ -70,71 +92,13 @@
7092
"workload-identity-federation-keyless-cloud-authentication"
7193
]
7294
},
73-
{
74-
"case-studies": [
75-
"cicd-pipeline-overhaul",
76-
"cloud-migration-logistics-platform",
77-
"compliance-automation-hipaa",
78-
"cost-optimization-saas-startup",
79-
"event-driven-architecture-migration",
80-
"incident-response-modernization",
81-
"infrastructure-as-code-transformation",
82-
"internal-developer-platform-backstage",
83-
"kubernetes-adoption-fintech",
84-
"observability-stack-implementation",
85-
"self-service-infrastructure",
86-
"zero-trust-network-architecture"
87-
]
88-
},
89-
"consent",
90-
"contact",
9195
"newsletter",
92-
"offline",
9396
{
9497
"privacy": [
9598
"my-data"
9699
]
97100
},
98101
"resume",
99-
"search",
100102
"services",
101-
{
102-
"tags": [
103-
"apis-and-gateways",
104-
"argo-cd",
105-
"aws",
106-
"aws/2",
107-
"aws/3",
108-
"azure",
109-
"backstage-idp",
110-
"build-and-deploy",
111-
"cloud-platforms",
112-
"cloud-platforms/2",
113-
"crossplane",
114-
"docker",
115-
"dotnet",
116-
"go",
117-
"grafana",
118-
"helm",
119-
"kubernetes",
120-
"kubernetes/2",
121-
"kubernetes/3",
122-
"observability-and-telemetry",
123-
"openstack",
124-
"platform-engineering",
125-
"prometheus",
126-
"prometheus/2",
127-
"python",
128-
"python/2",
129-
"react",
130-
"reliability-and-testing",
131-
"ruby",
132-
"system-modernization",
133-
"systems-and-development",
134-
"terraform",
135-
"typescript",
136-
"typescript/2"
137-
]
138-
},
139103
"terms"
140104
]

src/pages/api/social-card/_lib/assetLoaders.ts

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,16 +66,12 @@ class FontManager {
6666
hasNew = true
6767

6868
try {
69-
const fontFile = await fs.readFile(fontPath)
70-
const fontData = fontFile.buffer.slice(
71-
fontFile.byteOffset,
72-
fontFile.byteOffset + fontFile.byteLength
73-
)
69+
const fontData = await loadFont(fontPath)
7470
this.#cache.set(fontPath, fontData)
7571
} catch (cause) {
7672
throw new SocialCardGenerationError(
7773
'load-fonts',
78-
'Failed to load a local font file for social card generation',
74+
'Failed to load a font asset for social card generation',
7975
{
8076
fontPath,
8177
},
@@ -92,6 +88,27 @@ export const fontManager = new FontManager()
9288

9389
const imageCache = new Map<string, Buffer>()
9490

91+
const isHttpUrl = (value: string): boolean => /^https?:\/\//.test(value)
92+
93+
const loadFont = async (fontPath: string): Promise<ArrayBuffer> => {
94+
if (isHttpUrl(fontPath)) {
95+
const response = await fetch(fontPath)
96+
97+
if (!response.ok) {
98+
throw new SocialCardGenerationError('load-fonts', 'Failed to fetch a social card font asset', {
99+
fontPath,
100+
status: response.status,
101+
statusText: response.statusText,
102+
})
103+
}
104+
105+
return await response.arrayBuffer()
106+
}
107+
108+
const fontFile = await fs.readFile(fontPath)
109+
return fontFile.buffer.slice(fontFile.byteOffset, fontFile.byteOffset + fontFile.byteLength)
110+
}
111+
95112
export const loadImage = async (imageUrl: string): Promise<Buffer> => {
96113
const cachedImage = imageCache.get(imageUrl)
97114
if (cachedImage) {

src/pages/api/social-card/_lib/generateOpenGraphImage.ts

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
import { Buffer } from 'node:buffer'
2-
import { existsSync } from 'node:fs'
3-
import { fileURLToPath } from 'node:url'
42
import { decodeHTMLStrict } from 'entities'
53
import { getCanvasKit, fontManager, loadImage } from './assetLoaders'
64
import { SocialCardGenerationError } from './SocialCardGenerationError'
@@ -35,8 +33,8 @@ export async function generateOpenGraphImage({
3533
bgGradient = defaultGradient,
3634
avatarUrl,
3735
}: SocialCardImageOptions): Promise<Buffer> {
38-
const { titleFontPath, descriptionFontPath } = resolveFontPaths()
39-
const fontMgr = await fontManager.get([titleFontPath, descriptionFontPath])
36+
const { titleFontUrl, descriptionFontUrl } = resolveFontUrls(avatarUrl)
37+
const fontMgr = await fontManager.get([titleFontUrl, descriptionFontUrl])
4038
const avatarBuffer = await loadImage(avatarUrl)
4139
const CanvasKit = await getCanvasKit()
4240
const surface = CanvasKit.MakeSurface(width, height)
@@ -185,40 +183,51 @@ const buildTextStyle = (
185183
heightMultiplier: style.lineHeight,
186184
})
187185

188-
const resolveFontPaths = (): { descriptionFontPath: string; titleFontPath: string } => ({
189-
titleFontPath: resolveLocalAssetPath(
186+
const resolveFontUrls = (
187+
assetBaseUrl: string
188+
): { descriptionFontUrl: string; titleFontUrl: string } => ({
189+
titleFontUrl: resolvePublicAssetUrl(
190190
'resolve-title-font',
191-
[
192-
new URL('../../../../../public/fonts/Serif-Lora/Lora-Bold.ttf', import.meta.url),
193-
new URL('../../../../../public/fonts/Lora-Bold.ttf', import.meta.url),
194-
],
191+
['/fonts/Serif-Lora/Lora-Bold.ttf'],
192+
assetBaseUrl,
195193
'title font'
196194
),
197-
descriptionFontPath: resolveLocalAssetPath(
195+
descriptionFontUrl: resolvePublicAssetUrl(
198196
'resolve-description-font',
199-
[
200-
new URL('../../../../../public/fonts/Serif-Lora/Lora-Regular.ttf', import.meta.url),
201-
new URL('../../../../../public/fonts/Lora-Regular.ttf', import.meta.url),
202-
],
197+
['/fonts/Serif-Lora/Lora-Regular.ttf'],
198+
assetBaseUrl,
203199
'description font'
204200
),
205201
})
206202

207-
const resolveLocalAssetPath = (
203+
const resolvePublicAssetUrl = (
208204
stage: 'resolve-description-font' | 'resolve-title-font',
209-
candidateUrls: URL[],
205+
candidatePaths: string[],
206+
assetBaseUrl: string,
210207
label: string
211208
): string => {
212-
const candidatePaths = candidateUrls.map(candidateUrl => fileURLToPath(candidateUrl))
209+
try {
210+
const baseUrl = new URL(assetBaseUrl)
211+
const candidateUrls = candidatePaths.map(candidatePath => new URL(candidatePath, baseUrl).toString())
212+
const [resolvedUrl] = candidateUrls
213+
214+
if (!resolvedUrl) {
215+
throw new SocialCardGenerationError(stage, `Social card ${label} could not be resolved`, {
216+
candidatePaths,
217+
label,
218+
})
219+
}
213220

214-
for (const candidatePath of candidatePaths) {
215-
if (existsSync(candidatePath)) {
216-
return candidatePath
221+
return resolvedUrl
222+
} catch (cause) {
223+
if (cause instanceof SocialCardGenerationError) {
224+
throw cause
217225
}
218-
}
219226

220-
throw new SocialCardGenerationError(stage, `Social card ${label} could not be resolved`, {
221-
candidatePaths,
222-
label,
223-
})
227+
throw new SocialCardGenerationError(stage, `Social card ${label} could not be resolved`, {
228+
candidatePaths,
229+
label,
230+
assetBaseUrl,
231+
}, { cause })
232+
}
224233
}

0 commit comments

Comments
 (0)