Skip to content

Commit 3674e41

Browse files
committed
fix(files): skip malformed export assets
1 parent 7e7d523 commit 3674e41

2 files changed

Lines changed: 18 additions & 5 deletions

File tree

apps/sim/app/api/files/export/[id]/route.test.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ function request() {
5151
return createMockRequest('GET', undefined, {}, `http://localhost:3000/api/files/export/${DOC_ID}`)
5252
}
5353

54-
function assetRecord(id: string, size: number) {
54+
function assetRecord(id: string, size: number | null) {
5555
return {
5656
id,
5757
key: `workspace/ws-1/${id}`,
@@ -161,6 +161,21 @@ describe('markdown export bundling', () => {
161161
expect(zip.file('assets/bad.png')).toBeNull()
162162
})
163163

164+
it('drops an asset with missing canonical size metadata', async () => {
165+
embeds('good', 'missing-size')
166+
assetsResolveTo((id) => assetRecord(id, id === 'missing-size' ? null : 1 * MB))
167+
168+
const response = await GET(request(), context)
169+
170+
expect(response.status).toBe(200)
171+
const zip = await JSZip.loadAsync(Buffer.from(await response.arrayBuffer()))
172+
expect(zip.file('assets/good.png')).not.toBeNull()
173+
expect(zip.file('assets/missing-size.png')).toBeNull()
174+
expect(
175+
mockDownloadFile.mock.calls.some(([options]) => options.key.endsWith('missing-size'))
176+
).toBe(false)
177+
})
178+
164179
/**
165180
* The two id representations have to stay distinct: metadata resolves by the stored id, while the
166181
* rewrite finds the embed by the spelling the document used. Collapsing them either drops the

apps/sim/app/api/files/export/[id]/route.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ export const GET = withRouteHandler(
165165
const imgRecord = await getFileMetadataById(storedFileId(imageId))
166166
if (!imgRecord) return null
167167
if (!(await verifyFileAccess(imgRecord.key, userId))) return null
168-
return { imageId, record: imgRecord }
168+
return { imageId, record: imgRecord, size: getWorkspaceFileSize(imgRecord) }
169169
} catch (error) {
170170
logger.warn('Failed to resolve asset for export', {
171171
imageId,
@@ -178,9 +178,7 @@ export const GET = withRouteHandler(
178178

179179
// The body counts against the same budget as its assets — the zip holds both, so a
180180
// limit that measured only the attachments would not describe the archive produced.
181-
const bundleBytes =
182-
mdBuffer.length +
183-
assetTargets.reduce((sum, target) => sum + getWorkspaceFileSize(target.record), 0)
181+
const bundleBytes = mdBuffer.length + assetTargets.reduce((sum, target) => sum + target.size, 0)
184182
if (bundleBytes > MAX_EXPORT_TOTAL_BYTES) {
185183
return NextResponse.json(
186184
{

0 commit comments

Comments
 (0)