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
7 changes: 5 additions & 2 deletions apps/sim/app/api/tools/supabase/storage-upload/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { processSingleFileToUserFile } from '@/lib/uploads/utils/file-utils'
import { downloadServableFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
import { docNotReadyResponse } from '@/lib/uploads/utils/servable-file-response'
import { assertToolFileAccess } from '@/app/api/files/authorization'
import { encodeStoragePath, encodeStorageSegment } from '@/tools/supabase/utils'

export const dynamic = 'force-dynamic'

Expand Down Expand Up @@ -185,7 +186,9 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
return NextResponse.json({ success: false, error: projectValidation.error }, { status: 400 })
}

const supabaseUrl = `https://${projectValidation.sanitized}.supabase.co/storage/v1/object/${validatedData.bucket}/${fullPath}`
const encodedBucket = encodeStorageSegment(validatedData.bucket)
const encodedPath = encodeStoragePath(fullPath)
const supabaseUrl = `https://${projectValidation.sanitized}.supabase.co/storage/v1/object/${encodedBucket}/${encodedPath}`

const headers: Record<string, string> = {
apikey: validatedData.apiKey,
Expand Down Expand Up @@ -248,7 +251,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
path: fullPath,
})

const publicUrl = `https://${projectValidation.sanitized}.supabase.co/storage/v1/object/public/${validatedData.bucket}/${fullPath}`
const publicUrl = `https://${projectValidation.sanitized}.supabase.co/storage/v1/object/public/${encodedBucket}/${encodedPath}`

return NextResponse.json({
success: true,
Expand Down
83 changes: 78 additions & 5 deletions apps/sim/blocks/blocks/supabase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ export const SupabaseBlock: BlockConfig<SupabaseResponse> = {
{ label: 'Storage: Copy File', id: 'storage_copy' },
{ label: 'Storage: Get Public URL', id: 'storage_get_public_url' },
{ label: 'Storage: Create Signed URL', id: 'storage_create_signed_url' },
{ label: 'Storage: Create Signed Upload URL', id: 'storage_create_signed_upload_url' },
{ label: 'Storage: Create Bucket', id: 'storage_create_bucket' },
{ label: 'Storage: Update Bucket', id: 'storage_update_bucket' },
{ label: 'Storage: Empty Bucket', id: 'storage_empty_bucket' },
{ label: 'Storage: List Buckets', id: 'storage_list_buckets' },
{ label: 'Storage: Delete Bucket', id: 'storage_delete_bucket' },
],
Expand Down Expand Up @@ -686,9 +689,12 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
'storage_move',
'storage_copy',
'storage_create_bucket',
'storage_update_bucket',
'storage_empty_bucket',
'storage_delete_bucket',
'storage_get_public_url',
'storage_create_signed_url',
'storage_create_signed_upload_url',
],
},
required: true,
Expand Down Expand Up @@ -919,19 +925,53 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
value: () => 'false',
condition: { field: 'operation', value: 'storage_create_bucket' },
},
{
id: 'updateIsPublic',
title: 'Public Bucket',
type: 'dropdown',
options: [
{ label: 'Keep Current', id: '' },
{ label: 'False (Private)', id: 'false' },
{ label: 'True (Public)', id: 'true' },
],
value: () => '',
condition: { field: 'operation', value: 'storage_update_bucket' },
},
{
id: 'fileSizeLimit',
title: 'File Size Limit (bytes)',
type: 'short-input',
placeholder: '52428800',
condition: { field: 'operation', value: 'storage_create_bucket' },
condition: { field: 'operation', value: ['storage_create_bucket', 'storage_update_bucket'] },
mode: 'advanced',
},
{
id: 'allowedMimeTypes',
title: 'Allowed MIME Types (JSON array)',
type: 'code',
placeholder: '["image/png", "image/jpeg"]',
condition: { field: 'operation', value: 'storage_create_bucket' },
condition: { field: 'operation', value: ['storage_create_bucket', 'storage_update_bucket'] },
mode: 'advanced',
},
{
id: 'path',
title: 'Destination Path',
type: 'short-input',
placeholder: 'folder/file.jpg',
condition: { field: 'operation', value: 'storage_create_signed_upload_url' },
required: true,
},
{
id: 'upsert',
title: 'Allow Overwrite',
type: 'dropdown',
options: [
{ label: 'False', id: 'false' },
{ label: 'True', id: 'true' },
],
value: () => 'false',
condition: { field: 'operation', value: 'storage_create_signed_upload_url' },
mode: 'advanced',
},
],
tools: {
Expand All @@ -955,10 +995,13 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
'supabase_storage_move',
'supabase_storage_copy',
'supabase_storage_create_bucket',
'supabase_storage_update_bucket',
'supabase_storage_empty_bucket',
'supabase_storage_list_buckets',
'supabase_storage_delete_bucket',
'supabase_storage_get_public_url',
'supabase_storage_create_signed_url',
'supabase_storage_create_signed_upload_url',
],
config: {
tool: (params) => {
Expand Down Expand Up @@ -1001,6 +1044,10 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
return 'supabase_storage_copy'
case 'storage_create_bucket':
return 'supabase_storage_create_bucket'
case 'storage_update_bucket':
return 'supabase_storage_update_bucket'
case 'storage_empty_bucket':
return 'supabase_storage_empty_bucket'
case 'storage_list_buckets':
return 'supabase_storage_list_buckets'
case 'storage_delete_bucket':
Expand All @@ -1009,6 +1056,8 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
return 'supabase_storage_get_public_url'
case 'storage_create_signed_url':
return 'supabase_storage_create_signed_url'
case 'storage_create_signed_upload_url':
return 'supabase_storage_create_signed_upload_url'
default:
throw new Error(`Invalid Supabase operation: ${params.operation}`)
}
Expand All @@ -1028,6 +1077,7 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
functionBody,
functionHeaders,
method,
updateIsPublic,
...rest
} = params

Expand Down Expand Up @@ -1200,6 +1250,16 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
result.isPublic = parsedIsPublic
}

// "Keep Current" (empty string) means the caller didn't choose to
// override visibility — omit `isPublic` entirely so the update
// tool preserves the bucket's existing public/private setting
// instead of defaulting to false.
if (operation === 'storage_update_bucket' && updateIsPublic !== undefined) {
if (updateIsPublic === 'true' || updateIsPublic === 'false') {
result.isPublic = updateIsPublic === 'true'
}
}

if (normalizedFileData !== undefined) {
result.fileData = normalizedFileData
}
Expand Down Expand Up @@ -1254,6 +1314,11 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
search: { type: 'string', description: 'Search term for filtering' },
expiresIn: { type: 'number', description: 'Expiration time in seconds for signed URL' },
isPublic: { type: 'boolean', description: 'Whether bucket should be public' },
updateIsPublic: {
type: 'string',
description:
'Visibility override for bucket update: "" keeps the current value, "true"/"false" overrides it',
},
fileSizeLimit: { type: 'number', description: 'Maximum file size in bytes' },
allowedMimeTypes: { type: 'array', description: 'Array of allowed MIME types' },
},
Expand Down Expand Up @@ -1281,7 +1346,15 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e
},
signedUrl: {
type: 'string',
description: 'Temporary signed URL for storage file',
description: 'Temporary signed URL for storage file (download or upload)',
},
token: {
type: 'string',
description: 'Upload token embedded in the signed upload URL',
},
path: {
type: 'string',
description: 'Destination object path for the signed upload URL',
},
tables: {
type: 'json',
Expand All @@ -1300,9 +1373,9 @@ export const SupabaseBlockMeta = {
templates: [
{
icon: SupabaseIcon,
title: 'Supabase user provisioning',
title: 'Supabase customer record sync',
prompt:
'Build a workflow that listens for Stripe new-customer events, provisions a Supabase user with the correct role and metadata, and emails the welcome login link.',
'Build a workflow that listens for Stripe new-customer events and upserts a row into a Supabase customers table with the correct plan and metadata, then emails a welcome message.',
modules: ['agent', 'workflows'],
category: 'operations',
tags: ['enterprise', 'automation'],
Expand Down
6 changes: 6 additions & 0 deletions apps/sim/tools/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3698,14 +3698,17 @@ import {
supabaseRpcTool,
supabaseStorageCopyTool,
supabaseStorageCreateBucketTool,
supabaseStorageCreateSignedUploadUrlTool,
supabaseStorageCreateSignedUrlTool,
supabaseStorageDeleteBucketTool,
supabaseStorageDeleteTool,
supabaseStorageDownloadTool,
supabaseStorageEmptyBucketTool,
supabaseStorageGetPublicUrlTool,
supabaseStorageListBucketsTool,
supabaseStorageListTool,
supabaseStorageMoveTool,
supabaseStorageUpdateBucketTool,
supabaseStorageUploadTool,
supabaseTextSearchTool,
supabaseUpdateTool,
Expand Down Expand Up @@ -5207,10 +5210,13 @@ export const tools: Record<string, ToolConfig> = {
supabase_storage_move: supabaseStorageMoveTool,
supabase_storage_copy: supabaseStorageCopyTool,
supabase_storage_create_bucket: supabaseStorageCreateBucketTool,
supabase_storage_update_bucket: supabaseStorageUpdateBucketTool,
supabase_storage_empty_bucket: supabaseStorageEmptyBucketTool,
supabase_storage_list_buckets: supabaseStorageListBucketsTool,
supabase_storage_delete_bucket: supabaseStorageDeleteBucketTool,
supabase_storage_get_public_url: supabaseStorageGetPublicUrlTool,
supabase_storage_create_signed_url: supabaseStorageCreateSignedUrlTool,
supabase_storage_create_signed_upload_url: supabaseStorageCreateSignedUploadUrlTool,
tailscale_list_devices: tailscaleListDevicesTool,
tailscale_get_device: tailscaleGetDeviceTool,
tailscale_delete_device: tailscaleDeleteDeviceTool,
Expand Down
2 changes: 1 addition & 1 deletion apps/sim/tools/supabase/count.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const countTool: ToolConfig<SupabaseCountParams, SupabaseCountResponse> =
id: 'supabase_count',
name: 'Supabase Count',
description: 'Count rows in a Supabase table',
version: '1.0',
version: '1.0.0',

params: {
projectId: {
Expand Down
2 changes: 1 addition & 1 deletion apps/sim/tools/supabase/delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const deleteTool: ToolConfig<SupabaseDeleteParams, SupabaseDeleteResponse
id: 'supabase_delete',
name: 'Supabase Delete Row',
description: 'Delete rows from a Supabase table based on filter criteria',
version: '1.0',
version: '1.0.0',

params: {
projectId: {
Expand Down
2 changes: 1 addition & 1 deletion apps/sim/tools/supabase/get_row.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const getRowTool: ToolConfig<SupabaseGetRowParams, SupabaseGetRowResponse
id: 'supabase_get_row',
name: 'Supabase Get Row',
description: 'Get a single row from a Supabase table based on filter criteria',
version: '1.0',
version: '1.0.0',

params: {
projectId: {
Expand Down
6 changes: 6 additions & 0 deletions apps/sim/tools/supabase/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,17 @@ import { queryTool } from '@/tools/supabase/query'
import { rpcTool } from '@/tools/supabase/rpc'
import { storageCopyTool } from '@/tools/supabase/storage_copy'
import { storageCreateBucketTool } from '@/tools/supabase/storage_create_bucket'
import { storageCreateSignedUploadUrlTool } from '@/tools/supabase/storage_create_signed_upload_url'
import { storageCreateSignedUrlTool } from '@/tools/supabase/storage_create_signed_url'
import { storageDeleteTool } from '@/tools/supabase/storage_delete'
import { storageDeleteBucketTool } from '@/tools/supabase/storage_delete_bucket'
import { storageDownloadTool } from '@/tools/supabase/storage_download'
import { storageEmptyBucketTool } from '@/tools/supabase/storage_empty_bucket'
import { storageGetPublicUrlTool } from '@/tools/supabase/storage_get_public_url'
import { storageListTool } from '@/tools/supabase/storage_list'
import { storageListBucketsTool } from '@/tools/supabase/storage_list_buckets'
import { storageMoveTool } from '@/tools/supabase/storage_move'
import { storageUpdateBucketTool } from '@/tools/supabase/storage_update_bucket'
import { storageUploadTool } from '@/tools/supabase/storage_upload'
import { textSearchTool } from '@/tools/supabase/text_search'
import { updateTool } from '@/tools/supabase/update'
Expand Down Expand Up @@ -45,3 +48,6 @@ export const supabaseStorageListBucketsTool = storageListBucketsTool
export const supabaseStorageDeleteBucketTool = storageDeleteBucketTool
export const supabaseStorageGetPublicUrlTool = storageGetPublicUrlTool
export const supabaseStorageCreateSignedUrlTool = storageCreateSignedUrlTool
export const supabaseStorageCreateSignedUploadUrlTool = storageCreateSignedUploadUrlTool
export const supabaseStorageUpdateBucketTool = storageUpdateBucketTool
export const supabaseStorageEmptyBucketTool = storageEmptyBucketTool
2 changes: 1 addition & 1 deletion apps/sim/tools/supabase/insert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const insertTool: ToolConfig<SupabaseInsertParams, SupabaseInsertResponse
id: 'supabase_insert',
name: 'Supabase Insert',
description: 'Insert data into a Supabase table',
version: '1.0',
version: '1.0.0',

params: {
projectId: {
Expand Down
Loading
Loading