Skip to content

File Upload & Management

Overview

Files are stored in odp_files and managed by FilesService (src/services/files.ts). Storage drivers (local, S3, etc.) are configured via storageManager.

ODP supports two upload methods:

  1. Standard multipart upload — via POST /files
  2. TUS resumable upload — via POST /tus (for large files)
  3. URL import — via POST /files/import

Files with an incomplete TUS upload (tus_id IS NOT NULL) are hidden from list results.

Data Model

Table: odp_files

ColumnTypeDescription
idUUIDPrimary key
storagevarcharStorage driver name
filename_diskvarcharActual filename on disk/storage
filename_downloadvarcharOriginal filename for downloads
titlevarcharDisplay title
typevarcharMIME type (e.g., image/jpeg)
folderUUIDFK to odp_folders.id
uploaded_byUUIDFK to odp_users.id
created_ontimestampUpload timestamp
modified_ontimestampLast modification
modified_byUUIDFK to odp_users.id
charsetvarcharCharacter encoding (for text files)
filesizebigintFile size in bytes
widthintegerImage width (pixels)
heightintegerImage height (pixels)
durationintegerVideo/audio duration (seconds)
embedvarcharEmbed code (external media)
descriptiontextFile description
locationtextGPS location string
tagsjsonArray of tags
metadatajsonExtracted EXIF/other metadata
focal_point_xintegerX coordinate of focal point
focal_point_yintegerY coordinate of focal point
tus_idvarcharTUS upload ID (null = complete upload)
tus_datajsonTUS upload state

File Upload Endpoints

POST /files

Upload a single file via multipart form.

Auth required: Admin

Content-Type: multipart/form-data

Form Fields

FieldRequiredDescription
fileYesThe file binary
titleNoOverride the display title
folderNoUUID of the target folder
storageNoStorage driver name (default: first configured)

Example

bash
curl -X POST http://localhost:8055/files \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@photo.jpg" \
  -F "title=My Photo" \
  -F "folder=folder-uuid"

Response 200

json
{
  "data": {
    "id": "file-uuid",
    "storage": "local",
    "filename_disk": "abc123.jpg",
    "filename_download": "photo.jpg",
    "title": "My Photo",
    "type": "image/jpeg",
    "filesize": 245678,
    "width": 1920,
    "height": 1080,
    "created_on": "2026-03-26T10:00:00.000Z",
    "uploaded_by": "user-uuid"
  }
}

Limits

  • Max file size: FILES_MAX_UPLOAD_SIZE (default: 10mb)
  • Allowed MIME types: FILES_MIME_TYPE_ALLOW_LIST (default: *)

POST /files/import

Import a file from a URL.

Auth required: Admin

Request Body

json
{
  "url": "https://example.com/image.jpg",
  "data": {
    "title": "External Image",
    "folder": "folder-uuid"
  }
}

Response 200 — Same format as POST /files.


File Management Endpoints

GET /files

List all uploaded files.

Auth required: Yes (read permission)

Automatically excludes incomplete TUS uploads (tus_id IS NOT NULL).

Supports standard query system.

Response 200

json
{
  "data": [...],
  "meta": { "total_count": 150, "filter_count": 150 }
}

GET /files/:id

Read file metadata.

Auth required: Yes

Response 200 — Full file object.


PATCH /files/:id

Update file metadata or replace the file content.

Auth required: Admin

Metadata update (JSON)

bash
curl -X PATCH http://localhost:8055/files/file-uuid \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"title": "Updated Title", "description": "New description"}'

Response 200 — Updated file object.

File replacement (multipart)

Send Content-Type: multipart/form-data to replace the actual file content while keeping the same UUID.

bash
curl -X PATCH http://localhost:8055/files/file-uuid \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@new-version.jpg" \
  -F "title=Updated Image"

When replacing:

  • Old file is deleted from storage
  • New file is stored with the same id
  • filename_disk, type, filesize, width, height are updated
  • modified_on and modified_by are updated

Response 200 — Updated file object.


DELETE /files/:id

Delete a file.

Auth required: Admin

Response 204

Removes the file from storage and deletes the database row.


GET /files/storage-locations

List available storage driver names.

Auth required: Admin

Response 200

json
{
  "data": ["local", "s3"]
}

TUS Resumable Upload

For large files, use the TUS protocol endpoint.

POST /tus

Initialize a new TUS upload.

TUS-specific headers:

HeaderDescription
Tus-ResumableTUS version (must be 1.0.0)
Upload-LengthTotal file size in bytes
Upload-MetadataBase64-encoded metadata (filename, filetype, folder)

Response 201 — Returns Location header with upload URL.

PATCH /tus/:id

Upload a chunk.

Response 204

HEAD /tus/:id

Check upload progress.

Response 204 with Upload-Offset header.

Cleanup

Incomplete TUS uploads are cleaned up by a scheduled job:

  • Schedule: TUS_CLEANUP_SCHEDULE (default: 0 */6 * * * — every 6 hours)
  • Expiry: TUS_UPLOAD_EXPIRATION (default: 10m after last activity)

Asset Serving & Transformation

GET /assets/:id

Serve a file. Supports image transformation via query parameters.

Auth required: Depends on file permissions

Image Transform Parameters

ParameterDescription
widthTarget width in pixels
heightTarget height in pixels
fitResize mode: cover, contain, fill, inside, outside
qualityJPEG/WebP quality (1-100)
formatOutput format: jpg, png, webp, avif, tiff
withoutEnlargementDon't upscale smaller images
focal_point_x / focal_point_yFocal point for cover crop

Example

bash
# Thumbnail
GET /assets/file-uuid?width=200&height=200&fit=cover&quality=80

# WebP conversion
GET /assets/file-uuid?format=webp&quality=90

Configuration

VariableDefaultDescription
ASSETS_TRANSFORM_IMAGE_MAX_DIMENSION6000Max output dimension
ASSETS_TRANSFORM_MAX_CONCURRENT25Max concurrent transforms
ASSETS_TRANSFORM_TIMEOUT30Transform timeout (seconds)
ASSETS_CACHE_TTL30mTransformed asset cache TTL

Storage Configuration

VariableDefaultDescription
STORAGE_LOCAL_ROOT./uploadsLocal storage root path
FILES_MAX_UPLOAD_SIZE10mbMaximum file size
FILES_MIME_TYPE_ALLOW_LIST*Allowed MIME types (comma-separated or *)
FILES_MAX_UPLOAD_CONCURRENCY5Max simultaneous uploads

ODP Internal API Documentation