12 KiB
Assets Controller Documentation
File: controllers/admin/assets.controller.js
Base URL: /api/admin/assets
Guards: authenticate → requireAdmin() → adminLimiter
Table of Contents
- Get All Assets
- Get Single Asset
- Upload Asset
- Update Asset Metadata
- Update Thumbnail
- Delete Asset
- Bulk Delete Assets
- Restore Asset
Get All Assets
GET /api/admin/assets
Returns a paginated list of non-deleted assets with optional filtering.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| page | number | No | Page number. Default: 1 |
| limit | number | No | Records per page. Default: 20 |
| file_type | string | No | Filter by type: image, video, document, other |
| owner_type | string | No | Filter by owner type e.g. User, Course |
| owner_id | number | No | Filter by owner ID |
| uploadedBy | number | No | Filter by uploader user ID |
| is_public | boolean | No | Filter by visibility: true or false |
| resolution | string | No | Filter by resolution e.g. 1080p, 720p |
| search | string | No | Search by display_name, original_name, description |
| sort_by | string | No | Column to sort by. Default: createdAt |
| sort_dir | string | No | Sort direction: ASC or DESC. Default: DESC |
Response 200
{
"status": "success",
"message": "Assets retrieved.",
"data": {
"rows": [...],
"pagination": {
"total": 100,
"page": 1,
"limit": 20,
"totalPages": 5
}
}
}
Get Single Asset
GET /api/admin/assets/:assetId
Returns a single non-deleted asset by ID.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| assetId | number | Yes | Asset ID |
Response 200
{
"status": "success",
"message": "Asset found.",
"data": {
"asset_id": 1,
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"original_name": "intro.mp4",
"display_name": "Course Intro Video",
"file_url": "/uploads/intro.mp4",
"file_size": 104857600,
"mime_type": "video/mp4",
"extension": "mp4",
"checksum": "a3f5...",
"file_type": "video",
"width": 1920,
"height": 1080,
"duration": 120.5,
"resolution": "1080p",
"frame_rate": 29.97,
"bitrate": 8000000,
"video_codec": "H.264",
"audio_codec": "AAC",
"thumbnail_url": "/uploads/thumbnails/intro.jpg",
"description": "Introduction to the course.",
"storage_provider": "local",
"storage_bucket": null,
"storage_key": "intro.mp4",
"is_public": true,
"access_level": "public",
"owner_type": "Course",
"owner_id": 3,
"uploadedBy": 1,
"deletedBy": null,
"createdAt": "2025-01-01T00:00:00.000Z",
"updatedAt": "2025-01-01T00:00:00.000Z",
"deletedAt": null
}
}
Response 404
{
"status": "error",
"message": "Asset not found."
}
Upload Asset
POST /api/admin/assets/upload
Uploads a new asset. Expects multipart/form-data.
Video metadata (width, height, duration, etc.) should be extracted via ffprobe server-side or passed from the client.
resolution is auto-derived from width and height — do not pass it manually.
Request multipart/form-data
| Field | Type | Required | Description |
|---|---|---|---|
| file | File | Yes | The file to upload |
| uploadedBy | number | Yes | User ID of the uploader |
| display_name | string | No | Display name shown on platform. Defaults to filename |
| description | string | No | Description of the asset |
| owner_type | string | No | Owning entity type e.g. Course, User |
| owner_id | number | No | Owning entity ID |
| is_public | boolean | No | Whether asset is publicly accessible. Default: false |
| access_level | string | No | public, private, restricted. Default: private |
| storage_provider | string | No | local, s3, gcs, cloudinary, chibisafe, other. Default: local |
| storage_bucket | string | No | Bucket/container name for cloud storage |
| storage_key | string | No | Object key/path in bucket |
| file_url | string | No* | Required for non-local storage providers |
| width | number | No | Video/image width in px |
| height | number | No | Video/image height in px |
| duration | number | No | Video duration in seconds |
| frame_rate | number | No | Video frame rate in fps |
| bitrate | number | No | Video bitrate in bps |
| video_codec | string | No | Video codec e.g. H.264, H.265 |
| audio_codec | string | No | Audio codec e.g. AAC, MP3 |
| thumbnail_url | string | No | URL of the video/document preview thumbnail |
Resolution Auto-Derivation
| Height (px) | Derived Resolution |
|---|---|
| ≥ 2160 | 4K |
| ≥ 1440 | 1440p |
| ≥ 1080 | 1080p |
| ≥ 720 | 720p |
| ≥ 480 | 480p |
| ≥ 360 | 360p |
| ≥ 240 | 240p |
| Other | {width}x{height} |
Response 201
{
"status": "success",
"message": "Asset uploaded.",
"data": { ...asset }
}
Response 400
{
"status": "error",
"message": "No file uploaded."
}
Update Asset Metadata
PUT /api/admin/assets/:assetId
Updates metadata of an existing asset. File replacement is not supported — upload a new asset instead.
resolution is auto-re-derived if width or height is updated.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| assetId | number | Yes | Asset ID |
Request Body application/json
| Field | Type | Required | Description |
|---|---|---|---|
| display_name | string | No | Updated display name |
| description | string | No | Updated description |
| owner_type | string | No | Updated owner type |
| owner_id | number | No | Updated owner ID |
| is_public | boolean | No | Updated visibility |
| access_level | string | No | Updated access level |
| thumbnail_url | string | No | Updated thumbnail URL |
| width | number | No | Updated width — re-derives resolution |
| height | number | No | Updated height — re-derives resolution |
| duration | number | No | Updated duration |
| frame_rate | number | No | Updated frame rate |
| bitrate | number | No | Updated bitrate |
| video_codec | string | No | Updated video codec |
| audio_codec | string | No | Updated audio codec |
Response 200
{
"status": "success",
"message": "Asset updated.",
"data": { ...asset }
}
Update Thumbnail
PATCH /api/admin/assets/:assetId/thumbnail
Updates only the thumbnail of an asset. Useful for video platforms where users frequently change the video cover independently.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| assetId | number | Yes | Asset ID |
Request Body application/json
| Field | Type | Required | Description |
|---|---|---|---|
| thumbnail_url | string | Yes | New thumbnail URL |
Response 200
{
"status": "success",
"message": "Thumbnail updated.",
"data": { ...asset }
}
Response 400
{
"status": "error",
"message": "thumbnail_url is required."
}
Delete Asset
DELETE /api/admin/assets/:assetId
Soft deletes a single asset by setting deletedAt and deletedBy.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| assetId | number | Yes | Asset ID |
Request Body application/json
| Field | Type | Required | Description |
|---|---|---|---|
| deletedBy | number | No | User ID of who deleted the asset |
Response 200
{
"status": "success",
"message": "Asset deleted."
}
Bulk Delete Assets
DELETE /api/admin/assets/bulk
Soft deletes multiple assets at once.
Request Body application/json
| Field | Type | Required | Description |
|---|---|---|---|
| ids | number[] | Yes | Array of asset IDs to delete |
| deletedBy | number | No | User ID of who deleted the assets |
Response 200
{
"status": "success",
"message": "3 asset(s) deleted."
}
Response 400
{
"status": "error",
"message": "ids must be a non-empty array."
}
Restore Asset
PATCH /api/admin/assets/:assetId/restore
Restores a soft-deleted asset by clearing deletedAt and deletedBy.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| assetId | number | Yes | Asset ID |
Response 200
{
"status": "success",
"message": "Asset restored.",
"data": { ...asset }
}
Response 404
{
"status": "error",
"message": "Asset not found or not deleted."
}
Error Responses
All endpoints return the following on server error:
{
"status": "error",
"message": "Internal server error."
}
File Size Limits
| Type | Max Size |
|---|---|
| Images | 10 GB |
| Videos | 10 GB |
| Documents | 10 GB |
Limit is applied at the multer middleware level. Adjust in
assets.routes.jsif needed.
Notes
- File replacement is not supported. To replace a file, delete the old asset and upload a new one.
- Checksum (SHA-256) is computed on upload for duplicate detection.
- Polymorphic ownership via
owner_type+owner_idallows any entity (Course,User,Post, etc.) to own assets without a direct foreign key. - Resolution is always auto-derived from
widthandheight— never set manually. - Soft delete sets
deletedAttimestamp. Assets are excluded from all queries unless explicitly queried withparanoid: false.