mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
380 lines
12 KiB
Markdown
380 lines
12 KiB
Markdown
# 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-all-assets)
|
|
- [Get Single Asset](#get-single-asset)
|
|
- [Upload Asset](#upload-asset)
|
|
- [Update Asset Metadata](#update-asset-metadata)
|
|
- [Update Thumbnail](#update-thumbnail)
|
|
- [Delete Asset](#delete-asset)
|
|
- [Bulk Delete Assets](#bulk-delete-assets)
|
|
- [Restore Asset](#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`
|
|
```json
|
|
{
|
|
"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`
|
|
```json
|
|
{
|
|
"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`
|
|
```json
|
|
{
|
|
"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`
|
|
```json
|
|
{
|
|
"status": "success",
|
|
"message": "Asset uploaded.",
|
|
"data": { ...asset }
|
|
}
|
|
```
|
|
|
|
### Response `400`
|
|
```json
|
|
{
|
|
"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`
|
|
```json
|
|
{
|
|
"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`
|
|
```json
|
|
{
|
|
"status": "success",
|
|
"message": "Thumbnail updated.",
|
|
"data": { ...asset }
|
|
}
|
|
```
|
|
|
|
### Response `400`
|
|
```json
|
|
{
|
|
"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`
|
|
```json
|
|
{
|
|
"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`
|
|
```json
|
|
{
|
|
"status": "success",
|
|
"message": "3 asset(s) deleted."
|
|
}
|
|
```
|
|
|
|
### Response `400`
|
|
```json
|
|
{
|
|
"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`
|
|
```json
|
|
{
|
|
"status": "success",
|
|
"message": "Asset restored.",
|
|
"data": { ...asset }
|
|
}
|
|
```
|
|
|
|
### Response `404`
|
|
```json
|
|
{
|
|
"status": "error",
|
|
"message": "Asset not found or not deleted."
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Error Responses
|
|
|
|
All endpoints return the following on server error:
|
|
|
|
```json
|
|
{
|
|
"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.js` if 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_id` allows any entity (`Course`, `User`, `Post`, etc.) to own assets without a direct foreign key.
|
|
- **Resolution** is always auto-derived from `width` and `height` — never set manually.
|
|
- **Soft delete** sets `deletedAt` timestamp. Assets are excluded from all queries unless explicitly queried with `paranoid: false`. |