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
70 changes: 70 additions & 0 deletions routes/devstoreaccount1/upload/[uploadId].put.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import type { ReadableStream } from 'node:stream/web'
import { Buffer } from 'node:buffer'
import { randomUUID } from 'node:crypto'

import { z } from 'zod'
import { logger } from '~/lib/logger'

import { getStorage } from '~/lib/storage'

const pathParamsSchema = z.object({
uploadId: z.coerce.number(),
})

export default defineEventHandler(async (event) => {
const parsedPathParams = pathParamsSchema.safeParse(event.context.params)
if (!parsedPathParams.success)
throw createError({
statusCode: 400,
statusMessage: `Invalid path parameters: ${parsedPathParams.error.message}`,
})

if (getQuery(event).comp === 'blocklist') {
setResponseStatus(event, 201)
// prevent random EOF error with in tonistiigi/go-actions-cache caused by missing request id
setHeader(event, 'x-ms-request-id', randomUUID())
return
}

const blockId = getQuery(event)?.blockid as string
// if no block id, upload smaller than chunk size
const chunkIndex = blockId ? getChunkIndexFromBlockId(blockId) : 0
if (chunkIndex === undefined)
throw createError({
statusCode: 400,
statusMessage: `Invalid block id: ${blockId}`,
})

const { uploadId } = parsedPathParams.data

const stream = getRequestWebStream(event)
if (!stream) {
logger.debug('Upload: Request body is not a stream')
throw createError({ statusCode: 400, statusMessage: 'Request body must be a stream' })
}

const storage = await getStorage()
await storage.uploadPart(uploadId, chunkIndex, stream as ReadableStream)

// prevent random EOF error with in tonistiigi/go-actions-cache caused by missing request id
setHeader(event, 'x-ms-request-id', randomUUID())
setResponseStatus(event, 201)
})

function getChunkIndexFromBlockId(blockIdBase64: string) {
const base64Decoded = Buffer.from(blockIdBase64, 'base64')

// 64 bytes used by docker buildx
// 48 bytes used by everything else
if (base64Decoded.length === 64) {
return base64Decoded.readUInt32BE(16)
} else if (base64Decoded.length === 48) {
const decoded = base64Decoded.toString('utf8')

// slice off uuid and convert to number
const index = Number.parseInt(decoded.slice(36))
if (Number.isNaN(index)) return

return index
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,6 @@ export default defineEventHandler(async (event) => {

return {
ok: true,
signed_upload_url: `${env.API_BASE_URL}/upload/${upload.id}`,
signed_upload_url: `${env.API_BASE_URL}/devstoreaccount1/upload/${upload.id}`,
}
})