Skip to content

Commit 924145a

Browse files
committed
Fix CodeQL errors
1 parent 28439aa commit 924145a

2 files changed

Lines changed: 9 additions & 69 deletions

File tree

‎src/pages/api/social-card/__tests__/index.spec.ts‎

Lines changed: 4 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,6 @@
11
import { beforeEach, describe, expect, it, vi } from 'vitest'
22
import { GET } from '@pages/api/social-card'
3-
import { tmpdir } from 'node:os'
4-
import { join } from 'node:path'
5-
6-
const accessMock = vi.hoisted(() => vi.fn())
7-
const mkdirMock = vi.hoisted(() => vi.fn())
8-
const writeFileMock = vi.hoisted(() => vi.fn())
9-
const fetchMock = vi.hoisted(() => vi.fn())
10-
11-
vi.mock('node:fs/promises', () => ({
12-
access: accessMock,
13-
mkdir: mkdirMock,
14-
writeFile: writeFileMock,
15-
}))
3+
import { fileURLToPath } from 'node:url'
164

175
type CollectionFixture = Array<{
186
id: string
@@ -79,23 +67,13 @@ const seedCollections = () => {
7967
}
8068

8169
describe('Social Card API - GET /api/social-card', () => {
82-
const expectedAvatarPath = join(tmpdir(), 'webstackbuilders-social-cards', 'kevin-brown.webp')
70+
const expectedAvatarPath = fileURLToPath(
71+
new URL('../../../../assets/images/avatars/kevin-brown.webp', import.meta.url)
72+
)
8373

8474
beforeEach(() => {
8575
generateOpenGraphImageMock.mockReset()
8676
generateOpenGraphImageMock.mockResolvedValue(Buffer.from('mock-image'))
87-
accessMock.mockReset()
88-
mkdirMock.mockReset()
89-
writeFileMock.mockReset()
90-
fetchMock.mockReset()
91-
accessMock.mockRejectedValue(Object.assign(new Error('missing'), { code: 'ENOENT' }))
92-
mkdirMock.mockResolvedValue(undefined)
93-
writeFileMock.mockResolvedValue(undefined)
94-
fetchMock.mockResolvedValue({
95-
ok: true,
96-
arrayBuffer: vi.fn(async () => Uint8Array.from([1, 2, 3]).buffer),
97-
})
98-
vi.stubGlobal('fetch', fetchMock)
9977
seedCollections()
10078
})
10179

@@ -157,18 +135,6 @@ describe('Social Card API - GET /api/social-card', () => {
157135
}),
158136
})
159137
)
160-
161-
expect(fetchMock).toHaveBeenCalledWith(new URL('/assets/images/kevin-brown.webp', 'http://localhost/api/social-card?slug=home'))
162-
expect(writeFileMock).toHaveBeenCalledWith(expectedAvatarPath, expect.any(Buffer))
163-
})
164-
165-
it('reuses the cached avatar file when it already exists locally', async () => {
166-
accessMock.mockResolvedValueOnce(undefined)
167-
168-
await buildRequest('http://localhost/api/social-card?slug=home')
169-
170-
expect(fetchMock).not.toHaveBeenCalled()
171-
expect(writeFileMock).not.toHaveBeenCalled()
172138
})
173139

174140
it('returns an error response when generation fails', async () => {

‎src/pages/api/social-card/index.ts‎

Lines changed: 5 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
import type { APIRoute } from 'astro'
22
import { getCollection } from 'astro:content'
33
import { generateOpenGraphImage } from 'astro-og-canvas'
4-
import { access, mkdir, writeFile } from 'node:fs/promises'
5-
import { tmpdir } from 'node:os'
6-
import { join } from 'node:path'
4+
import { fileURLToPath } from 'node:url'
75
import { buildApiErrorResponse, handleApiFunctionError } from '@pages/api/_utils/errors'
86
import { createApiFunctionContext } from '@pages/api/_utils/requestContext'
97

108
export const prerender = false
119

1210
const ROUTE = '/api/social-card'
13-
const PUBLIC_AVATAR_PATH = '/assets/images/kevin-brown.webp'
14-
const CACHED_AVATAR_DIRECTORY = join(tmpdir(), 'webstackbuilders-social-cards')
15-
const CACHED_AVATAR_PATH = join(CACHED_AVATAR_DIRECTORY, 'kevin-brown.webp')
11+
const AVATAR_FILE_PATH = fileURLToPath(
12+
new URL('../../../assets/images/avatars/kevin-brown.webp', import.meta.url)
13+
)
1614
const DEFAULT_TITLE = 'Platform Engineering by Kevin Brown'
1715
const DEFAULT_DESCRIPTION =
1816
'Platform engineer helping teams harden delivery, modernize cloud platforms, and improve developer experience.'
@@ -61,29 +59,6 @@ const gradientPalette: Record<PaletteKey, [number, number, number][]> = {
6159
],
6260
}
6361

64-
const getAvatarFilePath = async (requestUrl: string): Promise<string> => {
65-
try {
66-
await access(CACHED_AVATAR_PATH)
67-
return CACHED_AVATAR_PATH
68-
} catch {
69-
// Cache miss; fetch from the public asset URL and persist locally for astro-og-canvas.
70-
}
71-
72-
const avatarUrl = new URL(PUBLIC_AVATAR_PATH, requestUrl)
73-
const response = await fetch(avatarUrl)
74-
75-
if (!response.ok) {
76-
throw new Error(`Unable to fetch Kevin Brown avatar from ${avatarUrl.toString()}`)
77-
}
78-
79-
const avatarBuffer = Buffer.from(await response.arrayBuffer())
80-
81-
await mkdir(CACHED_AVATAR_DIRECTORY, { recursive: true })
82-
await writeFile(CACHED_AVATAR_PATH, avatarBuffer)
83-
84-
return CACHED_AVATAR_PATH
85-
}
86-
8762
/** Normalize slug parameters to a consistent format */
8863
const normalizeSlug = (value: string | null): string => {
8964
if (!value) return 'home'
@@ -147,7 +122,6 @@ export const GET: APIRoute = async ({ request, clientAddress, cookies }) => {
147122
const slug = normalizeSlug(url.searchParams.get('slug'))
148123
const titleOverride = url.searchParams.get('title')
149124
const descriptionOverride = url.searchParams.get('description')
150-
const avatarPath = await getAvatarFilePath(request.url)
151125

152126
const contentIndex = await buildContentIndex()
153127
const matchedEntry = contentIndex[slug]
@@ -167,7 +141,7 @@ export const GET: APIRoute = async ({ request, clientAddress, cookies }) => {
167141
bgGradient: gradientPalette[palette],
168142
padding: 80,
169143
logo: {
170-
path: avatarPath,
144+
path: AVATAR_FILE_PATH,
171145
size: [140, 140],
172146
},
173147
font: {

0 commit comments

Comments
 (0)