Skip to content
Merged
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
6 changes: 3 additions & 3 deletions docs/content/1.docs/2.storage/3.blob.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ It can used to handle file uploads in API routes.
```ts [server/api/blob.put.ts]
export default eventHandler(async (event) => {
return hubBlob().handleUpload(event, {
formKey: 'file', // read file or files form the `formKey` field of request body (body should be a `FormData` object)
formKey: 'files', // read file or files form the `formKey` field of request body (body should be a `FormData` object)
multiple: true, // when `true`, the `formKey` field will be an array of `Blob` objects
contentType: ['image/jpeg', 'images/png'], // allowed types of the file
})
Expand All @@ -264,7 +264,7 @@ async function onFileSelect(event: Event) {
::field-group
::field{name="formKey" type="string"}
The form key to read the file from. Defaults to `'file'`.
The form key to read the file from. Defaults to `'files'`.
::
::field{name="multiple" type="boolean"}
When `true`, the `formKey` field will be an array of `Blob` objects.
Expand Down Expand Up @@ -578,7 +578,7 @@ async function onFileSelect({ target }: Event) {
::field{name="options" type="Object" required}
Optionally, you can pass Fetch options to the request. Read more about Fetch API [here](https://developer.mozilla.org/en-US/docs/Web/API/fetch#options).
::field{name="formKey" type="string"}
The key to add the file/files to the request form. Defaults to `'file'`.
The key to add the file/files to the request form. Defaults to `'files'`.
::
::field{name="multiple" type="boolean"}
Whether to allow multiple files to be uploaded. Defaults to `true`.
Expand Down
2 changes: 1 addition & 1 deletion playground/server/api/blob/index.put.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export default eventHandler(async (event) => {
const { prefix } = getQuery(event)
return hubBlob().handleUpload(event, {
formKey: 'file', // default
formKey: 'files', // default
multiple: true, // default
prefix: String(prefix || '')
})
Expand Down
10 changes: 9 additions & 1 deletion src/module.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { mkdir, writeFile, readFile } from 'node:fs/promises'
import { execSync } from 'node:child_process'
import { argv } from 'node:process'
import { defineNuxtModule, createResolver, logger, addServerScanDir, installModule, addServerImportsDir, addImportsDir } from '@nuxt/kit'
import { defineNuxtModule, createResolver, logger, addServerScanDir, installModule, addServerImportsDir, addImportsDir, addServerHandler } from '@nuxt/kit'
import { join } from 'pathe'
import { defu } from 'defu'
import { findWorkspaceDir } from 'pkg-types'
Expand Down Expand Up @@ -155,6 +155,14 @@ export default defineNuxtModule<ModuleOptions>({
})
}

if (nuxt.options.dev) {
addServerHandler({
route: '/api/_hub',
middleware: true,
handler: resolve('./runtime/cors.dev')
})
}

// Fallback to custom placeholder when openAPI is disabled
nuxt.options.alias['#hub/openapi'] = nuxt.options.nitro?.experimental?.openAPI === true
? '#internal/nitro/routes/openapi'
Expand Down
4 changes: 2 additions & 2 deletions src/runtime/compsables/useUpload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { BlobObject } from '../server/utils/blob'
interface UploadOptions extends FetchOptions {
/**
* The key to add the file/files to the request form.
* @default 'file'
* @default 'files'
*/
formKey?: string

Expand All @@ -18,7 +18,7 @@ interface UploadOptions extends FetchOptions {
export function useUpload(apiBase: string, options: UploadOptions & { multiple: false }): (data: FileList | HTMLInputElement | File[] | File) => Promise<BlobObject>
export function useUpload(apiBase: string, options: UploadOptions): ((data: File) => Promise<BlobObject>) & ((data: FileList | HTMLInputElement | File[]) => Promise<BlobObject[]>)
export function useUpload(apiBase: string, options: UploadOptions = {}) {
const { formKey = 'file', multiple = true, method, ...fetchOptions } = options || {}
const { formKey = 'files', multiple = true, method, ...fetchOptions } = options || {}

async function upload(data: File): Promise<BlobObject>
async function upload(data: FileList | HTMLInputElement | File[] | File): Promise<BlobObject[] | BlobObject> {
Expand Down
11 changes: 11 additions & 0 deletions src/runtime/cors.dev.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { handleCors, eventHandler } from 'h3'

export default eventHandler((event) => {
// add cors for devtools embed
handleCors(event, {
methods: '*',
origin: [
'https://admin.hub.nuxt.com'
]
})
})
5 changes: 5 additions & 0 deletions src/runtime/server/api/_hub/blob/head/[...pathname].get.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { eventHandler, getValidatedRouterParams } from 'h3'
import { z } from 'zod'
import { hubBlob } from '../../../../utils/blob'
import { requireNuxtHubAuthorization } from '../../../../utils/auth'
import { requireNuxtHubFeature } from '../../../../utils/features'

export default eventHandler(async (event) => {
await requireNuxtHubAuthorization(event)
requireNuxtHubFeature('blob')

const { pathname } = await getValidatedRouterParams(event, z.object({
pathname: z.string().min(1)
}).parse)
Expand Down
3 changes: 3 additions & 0 deletions src/runtime/server/api/_hub/blob/index.post.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { eventHandler, getQuery } from 'h3'
import { hubBlob } from '../../../utils/blob'
import { requireNuxtHubAuthorization } from '../../../utils/auth'
import { requireNuxtHubFeature } from '../../../utils/features'

export default eventHandler(async (event) => {
await requireNuxtHubAuthorization(event)
requireNuxtHubFeature('blob')

const query = getQuery(event)

return hubBlob().handleUpload(event, {
Expand Down
9 changes: 1 addition & 8 deletions src/runtime/server/utils/auth.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,12 @@
import type { H3Event } from 'h3'
import { getHeader, createError, handleCors } from 'h3'
import { getHeader, createError } from 'h3'
import { $fetch } from 'ofetch'

const localCache: Map<string, boolean> = new Map()

export async function requireNuxtHubAuthorization(event: H3Event) {
// Skip if in development
if (import.meta.dev) {
// add cors for devtools embed
handleCors(event, {
methods: '*',
origin: [
'https://admin.hub.nuxt.com'
]
})
return
}

Expand Down
6 changes: 3 additions & 3 deletions src/runtime/server/utils/blob.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ export type HandleMPUResponse =
export interface BlobUploadOptions extends BlobPutOptions, BlobValidateOptions {
/**
* The key to get the file/files from the request form.
* @default 'file'
* @default 'files'
*/
formKey?: string
/**
Expand Down Expand Up @@ -393,10 +393,10 @@ export function hubBlob(): HubBlob {
return mapR2MpuToBlobMpu(mpu)
},
async handleUpload(event: H3Event, options: BlobUploadOptions = {}) {
const opts = { formKey: 'file', multiple: true, ...options } as BlobUploadOptions
const opts = { formKey: 'files', multiple: true, ...options } as BlobUploadOptions

const form = await readFormData(event)
const files = form.getAll(opts.formKey || 'file') as File[]
const files = form.getAll(opts.formKey || 'files') as File[]
if (!files) {
throw createError({ statusCode: 400, message: 'Missing files' })
}
Expand Down