diff --git a/packages/server/src/api/controllers/row/index.ts b/packages/server/src/api/controllers/row/index.ts index 3466a4491d..aa401a5f16 100644 --- a/packages/server/src/api/controllers/row/index.ts +++ b/packages/server/src/api/controllers/row/index.ts @@ -1,4 +1,5 @@ import { quotas } from "@budibase/pro" +import { objectStore } from "@budibase/backend-core" import * as internal from "./internal" import * as external from "./external" import { isExternalTableID } from "../../../integrations/utils" @@ -13,6 +14,7 @@ import { PatchRowRequest, PatchRowResponse, Row, + RowAttachment, SearchParams, SearchRowRequest, SearchRowResponse, @@ -251,3 +253,21 @@ export const exportRows = async ( ctx.attachment(fileName) ctx.body = apiFileReturn(content) } + +export async function downloadAttachment(ctx: UserCtx) { + const { columnName } = ctx.params + + const tableId = utils.getTableId(ctx) + const row = await pickApi(tableId).find(ctx) + + if (!row[columnName]) { + ctx.throw(400, `'${columnName}' is not valid`) + } + + const attachment: RowAttachment = row[columnName][0] + ctx.attachment(attachment.name) + ctx.body = await objectStore.getReadStream( + objectStore.ObjectStoreBuckets.APPS, + attachment.key + ) +} diff --git a/packages/server/src/api/routes/row.ts b/packages/server/src/api/routes/row.ts index 516bfd20c6..f1aa39a461 100644 --- a/packages/server/src/api/routes/row.ts +++ b/packages/server/src/api/routes/row.ts @@ -77,6 +77,12 @@ router authorized(PermissionType.TABLE, PermissionLevel.WRITE), rowController.exportRows ) + .get( + "/api/:sourceId/rows/:rowId/attachment/:columnName", + paramSubResource("sourceId", "rowId"), + authorized(PermissionType.TABLE, PermissionLevel.READ), + rowController.downloadAttachment + ) router.post( "/api/v2/views/:viewId/search",