Skip to content

Commit

Permalink
feat: custom file size limit and mime types at bucket level (#151)
Browse files Browse the repository at this point in the history
  • Loading branch information
fenos authored Apr 6, 2023
1 parent c695143 commit e40c1be
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 6 deletions.
2 changes: 1 addition & 1 deletion infra/storage/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
FROM supabase/storage-api:v0.28.0
FROM supabase/storage-api:v0.29.1

RUN apk add curl --no-cache
2 changes: 2 additions & 0 deletions src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ export interface Bucket {
id: string
name: string
owner: string
file_size_limit?: number
allowed_mime_types?: string[]
created_at: string
updated_at: string
public: boolean
Expand Down
26 changes: 22 additions & 4 deletions src/packages/StorageBucketApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,15 @@ export default class StorageBucketApi {
*
* @param id A unique identifier for the bucket you are creating.
* @param options.public The visibility of the bucket. Public buckets don't require an authorization token to download objects, but still require a valid token for all other operations. By default, buckets are private.
* @param options.fileSizeLimit specifies the file size limit that this bucket can accept during upload
* @param options.allowedMimeTypes specifies the allowed mime types that this bucket can accept during upload
* @returns newly created bucket id
*/
async createBucket(
id: string,
options: { public: boolean } = { public: false }
options: { public: boolean; fileSizeLimit?: number | string; allowedMimeTypes?: string[] } = {
public: false,
}
): Promise<
| {
data: Pick<Bucket, 'name'>
Expand All @@ -93,7 +97,13 @@ export default class StorageBucketApi {
const data = await post(
this.fetch,
`${this.url}/bucket`,
{ id, name: id, public: options.public },
{
id,
name: id,
public: options.public,
file_size_limit: options.fileSizeLimit,
allowed_mime_types: options.allowedMimeTypes,
},
{ headers: this.headers }
)
return { data, error: null }
Expand All @@ -111,10 +121,12 @@ export default class StorageBucketApi {
*
* @param id A unique identifier for the bucket you are updating.
* @param options.public The visibility of the bucket. Public buckets don't require an authorization token to download objects, but still require a valid token for all other operations.
* @param options.fileSizeLimit specifies the file size limit that this bucket can accept during upload
* @param options.allowedMimeTypes specifies the allowed mime types that this bucket can accept during upload
*/
async updateBucket(
id: string,
options: { public: boolean }
options: { public: boolean; fileSizeLimit?: number | string; allowedMimeTypes?: string[] }
): Promise<
| {
data: { message: string }
Expand All @@ -129,7 +141,13 @@ export default class StorageBucketApi {
const data = await put(
this.fetch,
`${this.url}/bucket/${id}`,
{ id, name: id, public: options.public },
{
id,
name: id,
public: options.public,
file_size_limit: options.fileSizeLimit,
allowed_mime_types: options.allowedMimeTypes,
},
{ headers: this.headers }
)
return { data, error: null }
Expand Down
8 changes: 8 additions & 0 deletions test/__snapshots__/storageApi.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

exports[`bucket api Get bucket by id 1`] = `
Object {
"allowed_mime_types": null,
"created_at": "2021-02-17T04:43:32.770206+00:00",
"file_size_limit": 0,
"id": "bucket2",
"name": "bucket2",
"owner": "4d56e902-f0a0-4662-8448-a4d9e643c142",
Expand All @@ -25,6 +27,12 @@ Object {
}
`;

exports[`bucket api partially update bucket 1`] = `
Object {
"message": "Successfully updated",
}
`;

exports[`bucket api update bucket 1`] = `
Object {
"message": "Successfully updated",
Expand Down
26 changes: 25 additions & 1 deletion test/storageApi.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,35 @@ describe('bucket api', () => {
})

test('update bucket', async () => {
const updateRes = await storage.updateBucket(newBucketName, { public: true })
const newBucketName = `my-new-bucket-${Date.now()}`
await storage.createBucket(newBucketName)
const updateRes = await storage.updateBucket(newBucketName, {
public: true,
fileSizeLimit: '20mb',
allowedMimeTypes: ['image/jpeg'],
})
expect(updateRes.error).toBeNull()
expect(updateRes.data).toMatchSnapshot()
const getRes = await storage.getBucket(newBucketName)
expect(getRes.data!.public).toBe(true)
expect(getRes.data!.file_size_limit).toBe(20000000)
expect(getRes.data!.allowed_mime_types).toEqual(['image/jpeg'])
})

test('partially update bucket', async () => {
const newBucketName = `my-new-bucket-${Date.now()}`
await storage.createBucket(newBucketName, {
public: true,
fileSizeLimit: '20mb',
allowedMimeTypes: ['image/jpeg'],
})
const updateRes = await storage.updateBucket(newBucketName, { public: false })
expect(updateRes.error).toBeNull()
expect(updateRes.data).toMatchSnapshot()
const getRes = await storage.getBucket(newBucketName)
expect(getRes.data!.public).toBe(false)
expect(getRes.data!.file_size_limit).toBe(20000000)
expect(getRes.data!.allowed_mime_types).toEqual(['image/jpeg'])
})

test('empty bucket', async () => {
Expand Down
56 changes: 56 additions & 0 deletions test/storageFileApi.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,62 @@ describe('Object API', () => {
expect(updateRes.error).toBeNull()
expect(updateRes.data?.path).toEqual(uploadPath)
})

test('can upload a file within the file size limit', async () => {
const bucketName = 'with-limit' + Date.now()
await storage.createBucket(bucketName, {
public: true,
fileSizeLimit: '1mb',
})

const res = await storage.from(bucketName).upload(uploadPath, file)
expect(res.error).toBeNull()
})

test('cannot upload a file that exceed the file size limit', async () => {
const bucketName = 'with-limit' + Date.now()
await storage.createBucket(bucketName, {
public: true,
fileSizeLimit: '1kb',
})

const res = await storage.from(bucketName).upload(uploadPath, file)
expect(res.error).toEqual({
error: 'Payload too large',
message: 'The object exceeded the maximum allowed size',
statusCode: '413',
})
})

test('can upload a file with a valid mime type', async () => {
const bucketName = 'with-limit' + Date.now()
await storage.createBucket(bucketName, {
public: true,
allowedMimeTypes: ['image/png'],
})

const res = await storage.from(bucketName).upload(uploadPath, file, {
contentType: 'image/png',
})
expect(res.error).toBeNull()
})

test('cannot upload a file an invalid mime type', async () => {
const bucketName = 'with-limit' + Date.now()
await storage.createBucket(bucketName, {
public: true,
allowedMimeTypes: ['image/png'],
})

const res = await storage.from(bucketName).upload(uploadPath, file, {
contentType: 'image/jpeg',
})
expect(res.error).toEqual({
error: 'invalid_mime_type',
message: 'mime type not supported',
statusCode: '422',
})
})
})

describe('File operations', () => {
Expand Down

0 comments on commit e40c1be

Please sign in to comment.