Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions src/module/src/runtime/server/routes/dev/public/[...path].ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { withLeadingSlash } from 'ufo'
// @ts-expect-error useStorage is not defined in .nuxt/imports.d.ts
import { useStorage } from '#imports'
import { VIRTUAL_MEDIA_COLLECTION_NAME } from '../../../../utils/constants'
import { parseMediaRequestBody } from '../../../utils/media/request'


export default eventHandler(async (event) => {
Expand Down Expand Up @@ -49,9 +50,7 @@ export default eventHandler(async (event) => {
}
else {
const value = await readRawBody(event, 'utf8')
const json = JSON.parse(value || '{}')
const data = json.raw.split(';base64,')[1]
await storage.setItemRaw(key, Buffer.from(data, 'base64'))
await storage.setItemRaw(key, parseMediaRequestBody(value))
}

return 'OK'
Expand Down
30 changes: 30 additions & 0 deletions src/module/src/runtime/server/utils/media/request.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
type MediaRequestBody = {
raw?: string
}

/**
* Parses a media upload request body into the file contents that should be written to storage.
*
* Folder creation sends a `.gitkeep` media item without a `raw` data URL, so that case must
* create an empty placeholder file instead of trying to decode binary content.
*
* @param value Raw JSON request body received by the media route.
* @returns The file contents to write to storage.
* @throws {Error} When a `raw` payload is present but is not a valid base64 data URL.
* @example
* parseMediaRequestBody(JSON.stringify({ raw: 'data:text/plain;base64,aGVsbG8=' }))
*/
export function parseMediaRequestBody(value: string | undefined): Buffer {
const body = JSON.parse(value || '{}') as MediaRequestBody

if (typeof body.raw !== 'string') {
return Buffer.alloc(0)
}

const [, data] = body.raw.split(';base64,')
if (!data) {
throw new Error('Invalid media payload: expected a base64 data URL')
}

return Buffer.from(data, 'base64')
}
22 changes: 22 additions & 0 deletions src/module/test/utils/media-request.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { describe, expect, it } from 'vitest'
import { parseMediaRequestBody } from '../../src/runtime/server/utils/media/request'

describe('parseMediaRequestBody', () => {
it('returns an empty buffer for folder placeholder files', () => {
const buffer = parseMediaRequestBody(JSON.stringify({ fsPath: 'hello/.gitkeep' }))

expect(buffer.length).toBe(0)
})

it('decodes base64 media payloads', () => {
const buffer = parseMediaRequestBody(JSON.stringify({ raw: 'data:text/plain;base64,aGVsbG8=' }))

expect(buffer.toString('utf8')).toBe('hello')
})

it('throws on malformed raw payloads', () => {
expect(() => parseMediaRequestBody(JSON.stringify({ raw: 'hello' }))).toThrow(
'Invalid media payload: expected a base64 data URL',
)
})
})
Loading