diff --git a/content/concepts/files/_meta.js b/content/concepts/files/_meta.js new file mode 100644 index 0000000..15f8200 --- /dev/null +++ b/content/concepts/files/_meta.js @@ -0,0 +1,4 @@ +export default { + 'files-in-sharetribe': {}, + 'file-lifecycle': {} +}; diff --git a/content/concepts/files/file-lifecycle/index.mdx b/content/concepts/files/file-lifecycle/index.mdx new file mode 100644 index 0000000..bbc338b --- /dev/null +++ b/content/concepts/files/file-lifecycle/index.mdx @@ -0,0 +1,208 @@ +--- +title: File lifecycle in Sharetribe +sidebarTitle: File lifecycle +description: + This article describes the lifecycle of files on a Sharetribe + marketplace +--- + +import { Callout, Steps } from 'nextra/components'; + +# File lifecycle in Sharetribe + +Sharetribe has endpoints and mechanisms for uploading and downloading +digital files. Uploading and downloading files are both multi-step +operations, and there are a number of different entities and states +involved. + +## Uploading a file + + + +Files can be uploaded in two sequences. The sequence described here is +to first create the **ownFile** resource, and then create the +**fileUpload** resource and upload details with the file id of the +**ownFile**. + +It is also possible to first create the **fileUpload** resource and +upload the file, and then create the **ownFile** resource with the id of +the **fileUpload** resource. + + + + + +### Read file metadata + +Before we make any API calls, we need to get the metadata of the file in +the local file system. + +The SDK has a helper function for parsing the attributes of the file +metadata that are needed for uploading the file. + +```js +import * as sharetribeSdk from 'sharetribe-flex-sdk'; + +const { file: sdkFile } = sharetribeSdk; + +const metadata = sdkFile.metadata(file); +``` + +If you are using the APIs directly, you'll need to parse the necessary +metadata information from the File object: + +```js +const metadata = { + name: 'File to be uploaded.pdf', + mimeType: 'application/pdf', + size: 10697, +}; +``` + +### Create an ownFile resource + +With the metadata information, you can create an `ownFile` resource in +the Sharetribe backend with `ownFiles.create`. + +```js +const ownFileResource = await sdk.ownFiles.create({ ...metadata }).data; +``` + +This step also validates that the file mime type is supported. + +### Create a file upload URL + +The id of the `ownFile` resource is then used to create a signed file +upload URL. + +```js +const fileId = ownFileResource.data.id; +const fileUploadDetails = await sdk.fileUploads.create({ + fileId, +}); +``` + +```json +{ + "fileUploadDetails": { + "status": 200, + "statusText": "", + "data": { + "data": { + "id": { + "uuid": "63abdec4-85e7-4157-8e99-2e2c7465d8be" + }, + "type": "fileUpload", + "attributes": { + "fileId": { + "uuid": "69fd8859-8380-49c0-ab7e-0ee502e40811" + }, + "url": "https://unique-signed-sharetribe-upload-url.com", + "headers": { + "Content-Type": "application/pdf" + }, + "method": "PUT", + "expiresAt": "2026-05-08T08:16:30.988Z" + } + } + } + } +} +``` + +### Upload the file to storage with the file upload URL + +The actual file is uploaded directly to storage with the URL and other +details from the response, and the upload does not use Sharetribe APIs. +The SDK has another helper function to upload the file. + +When using the SDK, you can also pass in an upload progress tracking +callback to display the progress to the user. + +```js +const { + method = 'PUT', + url, + headers = {}, +} = fileUploadDetails?.data?.data?.attributes; + +const onUploadProgress = (progressEvent) => { + const loaded = progressEvent?.loaded || 0; + const total = progressEvent?.total || file.size; + const progress = total + ? Math.min(100, Math.round((loaded / total) * 100)) + : null; + console.log(`progress ${progress} %`); +}; + +sdkFile.upload({ + method, + url, + headers, + file, + onUploadProgress, +}); +``` + +### Attach the file to a resource + +The file id of the `ownFile` resource is then used to attach the file to +another resource, e.g. to a message: + +```js +sdk.messages.send({ + transactionId: '6985dfd3-34bc-4bc8-806f-03a0f29e056d', + content: 'This is a message with a file', + publicFileAttachments: [fileId], +}); +``` + +The file resource (`file` or `ownFile`) can be in one of several states: + +- pendingUpload +- pendingVerification +- available +- verificationFailed + +Depending on your use case, you may only want to attach files that are +available, or then allow attaching files that have completed upload. + + + +After the file is attached to a resource, it is visible according to the +scope of the `fileAttachment`. + +## Downloading a file + +To download a file, the user makes a POST request for a short-lived +download URL for the file from Sharetribe API, and uses that generated +URL to fetch the file from storage. + +Download links are only created for files in the `available` state. + +There are two different download endpoints that use different ids: + +- sdk.ownFileDownloads.create for ownFile resources, where the parameter + is the `file` id +- sdk.fileDownloads.create for file resources, where the parameter is + the `fileAttachment` id + +```js +const downloadDetails = isOwnFile + ? sdk.ownFileDownloads.create({ fileId }).data?.data + : sdk.fileDownloads.create({ fileAttachmentId }).data?.data; + +const { url } = downloadDetails?.attributes; +``` + +## Deleting a file + +An operator can delete a file in Console. When attaching files to a +message, a single file is only attached to a single message at a time, +so when an operator deletes a file associated with a message, both the +`fileAttachment` and the `file` are deleted. + +There is no file deletion endpoint in either Integration API or +Marketplace API. If you've uploaded a file but you have not yet attached +it to a message, leaving the file unattached will result in it being +eventually deleted. diff --git a/content/concepts/files/files-in-sharetribe/files-architecture.png b/content/concepts/files/files-in-sharetribe/files-architecture.png new file mode 100644 index 0000000..6be72e8 Binary files /dev/null and b/content/concepts/files/files-in-sharetribe/files-architecture.png differ diff --git a/content/concepts/files/files-in-sharetribe/index.mdx b/content/concepts/files/files-in-sharetribe/index.mdx new file mode 100644 index 0000000..5e3d6b7 --- /dev/null +++ b/content/concepts/files/files-in-sharetribe/index.mdx @@ -0,0 +1,339 @@ +--- +title: Files in Sharetribe +sidebarTitle: Files in Sharetribe +description: + This article describes how files and file attachments work on a + Sharetribe marketplace. +--- + +# Files in Sharetribe + +Sharetribe supports uploading and downloading files and attaching them +to different marketplace resources. + +The file information and relationships to other resources is stored in +the Sharetribe backend and available through the Sharetribe APIs. The +file entities themselves are stored in a third-party storage, and you +need to call the Sharetribe APIs to request unique signed upload and +download URLs to access the actual file entities. + +- Call Sharetribe APIs to + - create a file resource + - retrieve file information + - create upload and download URLs for a file resource + - manage resources with file attachments +- Call Sharetribe storage to + - upload file using a short-lived upload URL + - download file using a short-lived download URL + +![Files architecture in Sharetribe](./files-architecture.png) + +## Permissions + +Uploading and downloading files is enabled by default. Operators can +disable uploading and downloading files across the marketplace in Access +control. When uploads and downloads are disabled in Console > General > +Access control, +[file upload and download endpoints](/concepts/users-and-authentication/user-access-control-in-sharetribe/#disabling-file-uploads-and-downloads) +will throw a 403 Forbidden error. + +In addition, you can use for instance listing types to enable or disable +specific use cases for files in your marketplace, if files are enabled. + +For example, to view and use file attachment capabilities in transaction +messages when using the default Sharetribe Web Template, you'll need to +double check that files are enabled both on the marketplace level and in +the listing type of the transaction's listing. + +## File and FileAttachment + +In the Sharetribe system, `file` and `fileAttachment` represent two +different things: + +- A `file` represents a single uploaded digital file resource +- A `fileAttachment` is a file's connection to another resource, such as + a message. + +A `file` is a related resource to a `fileAttachment`, and a +`fileAttachment` is a related resource of another resource, for example +a message. In this structure, it is possible for a `file` to have +`fileAttachment` links to multiple different resources. + +When querying the resource, you'll need to include both the file +attachments relationship and the nested file relationship if you want to +retrieve both. + +```js +const messages = await sdk.messages.query({ + transaction_id: txId, + include: ['publicFileAttachments', 'publicFileAttachments.file'], +}); +``` + +```js +// messages.query API response with included +// publicFileAttachments and publicFileAttachments.file +{ + "data": [ + { + "id": { + "uuid": "69fd886a-96b2-4ba1-b458-cda5fdc0616e" + }, + "type": "message", + "attributes": { + "content": "message content", + "createdAt": "2026-05-08T06:53:30.043Z", + "deleted": false + }, + // publicFileAttachments is a relationship to message + "relationships": { + "publicFileAttachments": { + "data": [ + { + "id": { + "uuid": "69fd886a-1924-41c5-bfe1-a43deb7e1c3b" + }, + "type": "fileAttachment" + } + ] + } + } + } + ], + "included": [ + { + // this is the file resource that has a relationship to fileAttachment + "id": { + "uuid": "69fd8859-8380-49c0-ab7e-0ee502e40811" + }, + "type": "file", + "attributes": { + "name": "File to be uploaded.pdf", + "state": "available", + "mimeType": "application/pdf", + "size": 10697, + "deleted": false + } + }, + // This message has one fileAttachment in its publicFileAttachments array + { + "id": { + "uuid": "69fd886a-1924-41c5-bfe1-a43deb7e1c3b" + }, + "type": "fileAttachment", + "attributes": { + "scope": "public", + "deleted": false + }, + // file is a relationship to fileAttachment + "relationships": { + "file": { + "data": { + "id": { + "uuid": "69fd8859-8380-49c0-ab7e-0ee502e40811" + }, + "type": "file" + } + } + } + } + ], + "meta": { + "totalItems": 1, + "totalPages": 1, + "page": 1, + "perPage": 100 + } +} +``` + +An operator can delete a file attachment to disconnect a resource from a +file, or they can delete the file itself so that all resources linked to +the file lose access. When attaching files to messages, a single file is +only attached to a single message at a time, so when an operator deletes +a file associated with a message, both the `fileAttachment` and the +`file` are deleted. + +## file and ownFile + +The digital file resource can be represented by two resources: `file` +and `ownFile`. + +To fetch a public `file` resource, you'll need to use the id of the +associated `fileAttachment`, as the `fileAttachment` resource contains +the scope information that informs whether the API caller should have +access to the resource. + +```js +const file = await sdk.files.show({ fileAttachmentId }).data.data; +``` + +```json +"file": { + "id": { + "uuid": "69fd8859-8380-49c0-ab7e-0ee502e40811" + }, + "type": "file", + "attributes": { + "name": "File to be uploaded.pdf", + "state": "available", + "mimeType": "application/pdf", + "size": 10697, + "deleted": false + } +} +``` + +There's also an `ownFile` resource, which is the author's expanded +version of the file. In addition to the public attributes, the +`ownFile`resource also shows when the resource was created and when the +state was updated. + +To fetch an `ownFile`resource, you'll need to use the id of the `file` +resource, not the file attachment resource. + +```js +const ownFile = await sdk.ownFiles.show({ id: fileId }).data.data; +``` + +```json +"ownFile": { + "id": { + "uuid": "69fd8859-8380-49c0-ab7e-0ee502e40811" + }, + "type": "ownFile", + "attributes": { + "name": "File to be uploaded.pdf", + "mimeType": "application/pdf", + "size": 10697, + "state": "available", + "createdAt": "2026-05-08T06:53:13.091Z", + "stateUpdatedAt": "2026-05-08T06:53:25.880Z" + } +} +``` + +A `file` can be in one of several states: + +- pendingUpload +- pendingVerification +- available +- verificationFailed. + +Both `file` and `ownFile` resources are immutable, so updating an +existing file resource is not possible. + +## Visibility + +A file is first uploaded by creating an `ownFile` resource, which is +visible to the creator of the resource. An `ownFile` can then be +attached to another resource. + +Once a file has been attached to another resource, it becomes also +visible as a `file` resource according to the scope of the corresponding +fileAttachments attribute: + +- `publicFileAttachments` are visible to all users who have access to + the resource in question +- `protectedFileAttachments` are visible to the user who created the + resource, and additionally the file can be revealed in a transaction + to the other transaction participant +- `privateFileAttachments` are visible only to the user who created the + resource. + +For example, files attached to messages are visible to both participants +of the transaction in question, because they are attached via the +`publicFileAttachments` relationship of the message. + +You can review the +[API reference](https://www.sharetribe.com/api-reference/) to see which +resources have which scopes of file attachments available. + +In addition, operators have visibility to files and file attachments in +Console. For example, files attached to messages are visible in the +Console transaction details. + +## File storage + +Files are both uploaded to and downloaded from storage directly using +pre-signed URLs – not using Sharetribe API endpoints. + +For uploads, this means that a user first creates the file resource in +Sharetribe, and then fetches a time-limited signed URL connected to that +specific file resource that allows uploading the file to storage. + +```js +const uploadDetails = sdk.fileUploads.create({ fileId }).data.data; +``` + +```json +"uploadDetails": { + "id": { + "uuid": "63abdec4-85e7-4157-8e99-2e2c7465d8be" + }, + "type": "fileUpload", + "attributes": { + "fileId": { + "uuid": "69fd8859-8380-49c0-ab7e-0ee502e40811" + }, + "url": "https://unique-signed-sharetribe-upload-url.com", + "headers": { + "Content-Type": "application/pdf" + }, + "method": "PUT", + "expiresAt": "2026-05-08T08:16:30.988Z" + } +} +``` + +For downloads, the user makes a POST request for a short-lived download +URL for the file from Sharetribe API, and uses that generated URL to +fetch the file from storage. There are two different download endpoints +that use different ids, similarly to how files/show and own_files/show: + +- own_file_downloads/create (sdk.ownFileDownloads.create) for ownFile + resources, where the parameter is the `file` id +- file_downloads/create (sdk.fileDownloads.create) for file resources, + where the parameter is the `fileAttachment` id + +```js +const downloadDetails = isOwnFile + ? sdk.ownFileDownloads.create({ fileId }).data.data + : sdk.fileDownloads.create({ fileAttachmentId }).data.data; +``` + +```json +"downloadDetails": { + "id": { + "uuid": "fd76299c-1be6-4250-8de6-10b1d8f9cb9e" + }, + "type": "fileDownload", + "attributes": { + "fileId": { + "uuid": "69fd8859-8380-49c0-ab7e-0ee502e40811" + }, + "url": "https://unique-signed-sharetribe-download-url.com", + "expiresAt": "2026-05-08T08:29:28.089Z" + } +} +``` + +Since both upload URLs and download URLs are short-lived, it's not +possible to save them in a resource's extended data for reuse. Both +resources have an `expiresAt` attribute that indicates how long they are +valid. + +## Security + +When a file is uploaded, the Sharetribe backend runs a security +verification to ensure that the file is safe to handle and distribute, +for example that it does not have malware. In addition, the backend +verifies that the file mime type is acceptable and the size is less than +1GB. + +Files can be attached to resources once they've been uploaded, even if +they have not yet been verified. The Sharetribe backend processes the +verification asynchronously, and the file becomes available if the +verification succeeds. If you want to show the file as available +immediately when verification succeeds, your client will need to poll +the file to determine when it becomes available. diff --git a/content/concepts/index.mdx b/content/concepts/index.mdx index 377820f..64a873a 100644 --- a/content/concepts/index.mdx +++ b/content/concepts/index.mdx @@ -219,6 +219,19 @@ design decisions behind the platform. /> +## Files + + + + + + ## Integrations diff --git a/content/concepts/users-and-authentication/user-access-control-in-sharetribe/access_control_blank.png b/content/concepts/users-and-authentication/user-access-control-in-sharetribe/access_control_blank.png index b4fd56e..18ccd73 100644 Binary files a/content/concepts/users-and-authentication/user-access-control-in-sharetribe/access_control_blank.png and b/content/concepts/users-and-authentication/user-access-control-in-sharetribe/access_control_blank.png differ diff --git a/content/concepts/users-and-authentication/user-access-control-in-sharetribe/index.mdx b/content/concepts/users-and-authentication/user-access-control-in-sharetribe/index.mdx index 16e0f0c..2e7c565 100644 --- a/content/concepts/users-and-authentication/user-access-control-in-sharetribe/index.mdx +++ b/content/concepts/users-and-authentication/user-access-control-in-sharetribe/index.mdx @@ -44,6 +44,12 @@ See which endpoints are affected by this setting Read more about this feature in the [Help Center](https://www.sharetribe.com/help/en/articles/9503164-make-marketplace-private). +## Disable file uploads and downloads + +By default, Sharetribe marketplaces allow uploading and downloading +files and using them as file attachments on other resources. You can +also disable uploading and downloading files on a marketplace level. + ## Approve users who want to join User approval means that when a user signs up, they need to be approved @@ -162,6 +168,19 @@ When this setting is toggled, these endpoints will return a 403 Forbidden response, indicating that access is denied due to the marketplace's private status. +### Disabling file uploads and downloads + +Toggling this setting on prevents users from uploading and downloading +files to and from Sharetribe storage. This selection will restrict the +following endpoints: + +- `POST /file_uploads/create` +- `POST /file_downloads/create` + +In other words, endpoints showing file or file attachment information +are not restricted, only endpoints that create signed URLs to Sharetribe +file storage to upload or download a file entity. + ### Approve users who want to join Toggling this setting in Console will require users to be approved by an diff --git a/content/references/digital-files/index.mdx b/content/references/digital-files/index.mdx new file mode 100644 index 0000000..10ed04e --- /dev/null +++ b/content/references/digital-files/index.mdx @@ -0,0 +1,66 @@ +--- +title: Digital files management +sidebarTitle: Digital files management +description: Reference documentation for digital files management. +--- + +# Digital files management + +The digital files management features of Sharetribe allow listing +authors to upload and download digital files and attach them to other +resources. There are three key concepts related to digital files +management: + +- **file** is the Sharetribe representation of the uploaded digital file + item. +- **ownFile** is the Sharetribe representation of the uploaded digital + file item visible to the author of the upload. +- A **file attachment** is the association of a **file** to another + marketplace resource, such as a message. + +The actual digital file entity is uploaded to and downloaded from +Sharetribe storage with a direct URL, not through the Sharetribe APIs. +The URLs are managed through three other resource types: + +- **fileUpload** contains the URL and other details for uploading a file + directly to storage. +- **fileDownload** contains the URL for downloading a file directly from + storage. +- **ownFileDownload** contains the URL for downloading a user's own + uploaded file directly from storage. + +## File states + +A file can be in one of several possible states: + +- `pendingUpload` +- `pendingVerification` +- `available` +- `verificationFailed` + +Files change state as a result of the file verification process that +happens once a user uploads the digital file entity using the signed +upload URL. Download URLs are only generated for files in `available` +state. + +## File attachment scopes + +A **file** resource can be associated with another resource by adding it +to the other resource's file attachments. The scope of the file +attachment determines the visibility of the file: + +- **publicFileAttachments** are visible to all users who have access to + the resource in question +- **protectedFileAttachments** are visible to the user who created the + resource, and additionally the file can be revealed in a transaction + to the other transaction participant +- **privateFileAttachments** are visible only to the user who created + the resource. + +The [API reference](https://www.sharetribe.com/api-reference/) details +which resources have which scopes of file attachments available. + +## Further reading + +- Marketplace API reference +- Integration API reference diff --git a/content/references/index.mdx b/content/references/index.mdx index c652470..a851742 100644 --- a/content/references/index.mdx +++ b/content/references/index.mdx @@ -24,6 +24,10 @@ customizing your marketplace with code. title="Availability management" href="/references/availability/" /> +