Skip to content

Commit 22a02aa

Browse files
committed
fix(uploads): write workspaceFiles row when issuing presigned URL
1 parent c7130c6 commit 22a02aa

2 files changed

Lines changed: 132 additions & 0 deletions

File tree

apps/sim/app/api/files/presigned/route.test.ts

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const {
2525
mockGetUserEntityPermissions,
2626
mockGenerateWorkspaceFileKey,
2727
mockGenerateExecutionFileKey,
28+
mockInsertFileMetadata,
2829
} = vi.hoisted(() => ({
2930
mockVerifyFileAccess: vi.fn().mockResolvedValue(true),
3031
mockVerifyWorkspaceFileAccess: vi.fn().mockResolvedValue(true),
@@ -50,6 +51,7 @@ const {
5051
(ctx: { workspaceId: string; workflowId: string; executionId: string }, fileName: string) =>
5152
`execution/${ctx.workspaceId}/${ctx.workflowId}/${ctx.executionId}/${fileName}`
5253
),
54+
mockInsertFileMetadata: vi.fn().mockResolvedValue({ id: 'wf_test' }),
5355
}))
5456

5557
vi.mock('@/app/api/files/authorization', () => ({
@@ -89,6 +91,10 @@ vi.mock('@/lib/uploads/contexts/execution/utils', () => ({
8991
generateExecutionFileKey: mockGenerateExecutionFileKey,
9092
}))
9193

94+
vi.mock('@/lib/uploads/server/metadata', () => ({
95+
insertFileMetadata: mockInsertFileMetadata,
96+
}))
97+
9298
vi.mock('@/lib/uploads/utils/file-utils', () => ({
9399
isImageFileType: mockIsImageFileType,
94100
}))
@@ -614,6 +620,37 @@ describe('/api/files/presigned', () => {
614620
const response = await POST(request)
615621
expect(response.status).toBe(403)
616622
})
623+
624+
it('inserts a workspaceFiles row with context=mothership so previews authorize', async () => {
625+
setupFileApiMocks({ cloudEnabled: true, storageProvider: 's3' })
626+
627+
const request = new NextRequest(
628+
'http://localhost:3000/api/files/presigned?type=mothership&workspaceId=ws-1',
629+
{
630+
method: 'POST',
631+
body: JSON.stringify({
632+
fileName: 'screenshot.png',
633+
contentType: 'image/png',
634+
fileSize: 4096,
635+
}),
636+
}
637+
)
638+
639+
const response = await POST(request)
640+
const data = await response.json()
641+
642+
expect(response.status).toBe(200)
643+
expect(mockInsertFileMetadata).toHaveBeenCalledTimes(1)
644+
expect(mockInsertFileMetadata).toHaveBeenCalledWith({
645+
key: data.fileInfo.key,
646+
userId: 'test-user-id',
647+
workspaceId: 'ws-1',
648+
context: 'mothership',
649+
originalName: 'screenshot.png',
650+
contentType: 'image/png',
651+
size: 4096,
652+
})
653+
})
617654
})
618655

619656
describe('execution uploads', () => {
@@ -682,6 +719,70 @@ describe('/api/files/presigned', () => {
682719
const response = await POST(request)
683720
expect(response.status).toBe(400)
684721
})
722+
723+
it('inserts a workspaceFiles row with context=execution so previews authorize', async () => {
724+
setupFileApiMocks({ cloudEnabled: true, storageProvider: 's3' })
725+
726+
const request = new NextRequest(
727+
'http://localhost:3000/api/files/presigned?type=execution&workspaceId=ws-1&workflowId=wf-1&executionId=exec-1',
728+
{
729+
method: 'POST',
730+
body: JSON.stringify({
731+
fileName: 'output.mp4',
732+
contentType: 'video/mp4',
733+
fileSize: 4096,
734+
}),
735+
}
736+
)
737+
738+
const response = await POST(request)
739+
const data = await response.json()
740+
741+
expect(response.status).toBe(200)
742+
expect(mockInsertFileMetadata).toHaveBeenCalledTimes(1)
743+
expect(mockInsertFileMetadata).toHaveBeenCalledWith({
744+
key: data.fileInfo.key,
745+
userId: 'test-user-id',
746+
workspaceId: 'ws-1',
747+
context: 'execution',
748+
originalName: 'output.mp4',
749+
contentType: 'video/mp4',
750+
size: 4096,
751+
})
752+
})
753+
})
754+
755+
describe('workspace-logos uploads', () => {
756+
it('inserts a workspaceFiles row with context=workspace-logos so logos authorize', async () => {
757+
setupFileApiMocks({ cloudEnabled: true, storageProvider: 's3' })
758+
759+
const request = new NextRequest(
760+
'http://localhost:3000/api/files/presigned?type=workspace-logos&workspaceId=ws-1',
761+
{
762+
method: 'POST',
763+
body: JSON.stringify({
764+
fileName: 'logo.png',
765+
contentType: 'image/png',
766+
fileSize: 4096,
767+
}),
768+
}
769+
)
770+
771+
const response = await POST(request)
772+
const data = await response.json()
773+
774+
expect(response.status).toBe(200)
775+
expect(mockInsertFileMetadata).toHaveBeenCalledTimes(1)
776+
expect(mockInsertFileMetadata).toHaveBeenCalledWith({
777+
key: data.fileInfo.key,
778+
userId: 'test-user-id',
779+
workspaceId: 'ws-1',
780+
context: 'workspace-logos',
781+
originalName: 'logo.png',
782+
contentType: 'image/png',
783+
size: 4096,
784+
})
785+
})
685786
})
686787

687788
describe('knowledge-base uploads', () => {

apps/sim/app/api/files/presigned/route.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { USE_BLOB_STORAGE } from '@/lib/uploads/config'
1010
import { generateExecutionFileKey } from '@/lib/uploads/contexts/execution/utils'
1111
import { generateWorkspaceFileKey } from '@/lib/uploads/contexts/workspace/workspace-file-manager'
1212
import { generatePresignedUploadUrl, hasCloudStorage } from '@/lib/uploads/core/storage-service'
13+
import { insertFileMetadata } from '@/lib/uploads/server/metadata'
1314
import { isImageFileType } from '@/lib/uploads/utils/file-utils'
1415
import { validateAttachmentFileType, validateFileType } from '@/lib/uploads/utils/validation'
1516
import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils'
@@ -157,6 +158,16 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
157158
expirationSeconds: 3600,
158159
metadata: { workspaceId },
159160
})
161+
162+
await insertFileMetadata({
163+
key: presignedUrlResponse.key,
164+
userId: sessionUserId,
165+
workspaceId,
166+
context: 'mothership',
167+
originalName: fileName,
168+
contentType,
169+
size: fileSize,
170+
})
160171
} else if (uploadType === 'execution') {
161172
const workflowId = request.nextUrl.searchParams.get('workflowId')
162173
const executionId = request.nextUrl.searchParams.get('executionId')
@@ -191,6 +202,16 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
191202
expirationSeconds: 3600,
192203
metadata: { workspaceId, workflowId, executionId },
193204
})
205+
206+
await insertFileMetadata({
207+
key: presignedUrlResponse.key,
208+
userId: sessionUserId,
209+
workspaceId,
210+
context: 'execution',
211+
originalName: fileName,
212+
contentType,
213+
size: fileSize,
214+
})
194215
} else if (uploadType === 'workspace-logos') {
195216
const workspaceId = request.nextUrl.searchParams.get('workspaceId')
196217
if (!workspaceId?.trim()) {
@@ -222,6 +243,16 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
222243
expirationSeconds: 3600,
223244
metadata: { workspaceId },
224245
})
246+
247+
await insertFileMetadata({
248+
key: presignedUrlResponse.key,
249+
userId: sessionUserId,
250+
workspaceId,
251+
context: 'workspace-logos',
252+
originalName: fileName,
253+
contentType,
254+
size: fileSize,
255+
})
225256
} else {
226257
if (uploadType === 'profile-pictures') {
227258
if (!sessionUserId?.trim()) {

0 commit comments

Comments
 (0)