Appearance
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:
- Standard multipart upload — via
POST /files - TUS resumable upload — via
POST /tus(for large files) - 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
| Column | Type | Description |
|---|---|---|
id | UUID | Primary key |
storage | varchar | Storage driver name |
filename_disk | varchar | Actual filename on disk/storage |
filename_download | varchar | Original filename for downloads |
title | varchar | Display title |
type | varchar | MIME type (e.g., image/jpeg) |
folder | UUID | FK to odp_folders.id |
uploaded_by | UUID | FK to odp_users.id |
created_on | timestamp | Upload timestamp |
modified_on | timestamp | Last modification |
modified_by | UUID | FK to odp_users.id |
charset | varchar | Character encoding (for text files) |
filesize | bigint | File size in bytes |
width | integer | Image width (pixels) |
height | integer | Image height (pixels) |
duration | integer | Video/audio duration (seconds) |
embed | varchar | Embed code (external media) |
description | text | File description |
location | text | GPS location string |
tags | json | Array of tags |
metadata | json | Extracted EXIF/other metadata |
focal_point_x | integer | X coordinate of focal point |
focal_point_y | integer | Y coordinate of focal point |
tus_id | varchar | TUS upload ID (null = complete upload) |
tus_data | json | TUS upload state |
File Upload Endpoints
POST /files
Upload a single file via multipart form.
Auth required: Admin
Content-Type: multipart/form-data
Form Fields
| Field | Required | Description |
|---|---|---|
file | Yes | The file binary |
title | No | Override the display title |
folder | No | UUID of the target folder |
storage | No | Storage 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,heightare updatedmodified_onandmodified_byare 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:
| Header | Description |
|---|---|
Tus-Resumable | TUS version (must be 1.0.0) |
Upload-Length | Total file size in bytes |
Upload-Metadata | Base64-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:10mafter 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
| Parameter | Description |
|---|---|
width | Target width in pixels |
height | Target height in pixels |
fit | Resize mode: cover, contain, fill, inside, outside |
quality | JPEG/WebP quality (1-100) |
format | Output format: jpg, png, webp, avif, tiff |
withoutEnlargement | Don't upscale smaller images |
focal_point_x / focal_point_y | Focal 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=90Configuration
| Variable | Default | Description |
|---|---|---|
ASSETS_TRANSFORM_IMAGE_MAX_DIMENSION | 6000 | Max output dimension |
ASSETS_TRANSFORM_MAX_CONCURRENT | 25 | Max concurrent transforms |
ASSETS_TRANSFORM_TIMEOUT | 30 | Transform timeout (seconds) |
ASSETS_CACHE_TTL | 30m | Transformed asset cache TTL |
Storage Configuration
| Variable | Default | Description |
|---|---|---|
STORAGE_LOCAL_ROOT | ./uploads | Local storage root path |
FILES_MAX_UPLOAD_SIZE | 10mb | Maximum file size |
FILES_MIME_TYPE_ALLOW_LIST | * | Allowed MIME types (comma-separated or *) |
FILES_MAX_UPLOAD_CONCURRENCY | 5 | Max simultaneous uploads |