Shop It Docs

Admin Video Gallery

Unified paginated surface for browsing, managing, and reporting storage on all video assets

Admin Video Gallery

Admins can browse all video assets (course + training), filter by status or type, delete orphaned assets with automatic S3 cleanup, reassign assets to different lessons or sessions, and view storage usage totals.

All endpoints require a valid admin JWT (Authorization: Bearer <token>).


Endpoints

MethodPathPermissionDescription
GET/admin/video-galleryCourses_READPaginated asset list
GET/admin/video-gallery/storage-breakdownCourses_READPer-entity storage totals
POST/admin/video-gallery/:id/deleteCourses_DELETE or Training_SESSION_DELETESoft-delete + async S3 cleanup
PATCH/admin/video-gallery/:id/reassignCourses_READMove asset to a different entity

Returns a paginated list of all video assets with per-asset size, status, linked entity, and a totalStorageBytes header across all non-deleted assets.

Query Parameters

ParameterTypeDefaultDescription
pageinteger1Page number
sizeinteger20Items per page (max 100)
sortstringcreatedAtSort field: createdAt, fileSize, originalFilename, status
orderasc | descdescSort direction
searchstringSubstring match on originalFilename (SQL ILIKE)
statusstringFilter: uploading, processing, ready, failed
linkedTypestringFilter: course, training, orphan
includeDeletedbooleanfalseInclude soft-deleted assets (admin recovery view)

Response

{
  "message": "Video assets retrieved",
  "data": {
    "items": [
      {
        "id": "01961a2f-...",
        "originalFilename": "intro-lesson.mp4",
        "status": "ready",
        "fileSize": 524288000,
        "duration": 312,
        "thumbnailPath": "thumbs/01961a2f-.../thumb.jpg",
        "linkedType": "course",
        "linkedEntityName": "React Fundamentals",
        "linkedLessonId": "01961a2e-...",
        "linkedSessionId": null,
        "createdAt": "2026-04-21T10:00:00.000Z",
        "deletedAt": null
      }
    ],
    "totalStorageBytes": 10737418240
  },
  "count": 47,
  "currentPage": 1,
  "totalPage": 3
}

totalStorageBytes is the sum of fileSize across all non-deleted assets — not just the current page. Frontend can format bytes to human-readable (e.g. "10 GB").


GET /admin/video-gallery/storage-breakdown

Returns aggregated storage grouped by course and training program.

Response

{
  "message": "Storage breakdown retrieved",
  "data": {
    "courses": [
      { "id": 1, "title": "React Fundamentals", "totalBytes": 3221225472, "assetCount": 6 }
    ],
    "trainings": [
      { "id": 42, "title": "Advanced Python Bootcamp", "totalBytes": 2147483648, "assetCount": 4 }
    ],
    "totalBytes": 10737418240
  }
}

POST /admin/video-gallery/:id/delete

Soft-deletes the video asset row and enqueues an async BullMQ job to remove S3/RustFS objects.

Permission: Courses_DELETE for course-linked assets; Training_SESSION_DELETE for training-linked assets. The endpoint accepts either permission.

What happens

The API response returns immediately after the DB soft-delete. S3 cleanup is best-effort — if individual file deletes fail, the error is logged and the job completes (no BullMQ retry storm).

Response

{
  "message": "Video asset deleted and S3 cleanup enqueued",
  "data": {
    "id": "01961a2f-...",
    "deletedAt": "2026-04-21T12:00:00.000Z",
    "cascaded": { "lessons": 1, "trainingSessions": 0 }
  }
}

PATCH /admin/video-gallery/:id/reassign

Moves the video asset reference from its current entity to a new lesson or training session in a single transaction.

Request Body

{
  "targetType": "lesson",
  "targetId": "01961b00-..."
}
FieldTypeValuesDescription
targetTypestringlesson, training_sessionType of entity to assign to
targetIdstringUUID (lesson) or integer string (training_session)ID of target entity

Validation rules:

  • Asset must be ready status
  • Asset must not be soft-deleted
  • Target entity must exist
  • Target entity must not already have a different video asset assigned

Response

{
  "message": "Video asset reassigned",
  "data": {
    "videoAssetId": "01961a2f-...",
    "targetType": "lesson",
    "targetId": "01961b00-..."
  }
}

Error Codes

HTTPCodeTrigger
404VIDEO_ASSET_NOT_FOUNDNo video_asset row matches the given UUID
400VIDEO_ASSET_ALREADY_DELETEDAsset deletedAt is already set
400VIDEO_ASSET_NOT_READYReassign attempted on non-ready asset
404VIDEO_ASSET_REASSIGN_TARGET_NOT_FOUNDTarget lesson or training session does not exist
400VIDEO_ASSET_REASSIGN_TARGET_OCCUPIEDTarget entity already has a different asset assigned
400VIDEO_ASSET_REASSIGN_INVALID_TYPEtargetType is not lesson or training_session, or targetId is not a valid integer for training_session

All error responses follow the ResponseDto envelope:

{
  "message": "Video asset must be in ready status to reassign.",
  "errorCode": "VIDEO_ASSET_NOT_READY"
}

Requirements Coverage

RequirementEndpoint(s)
GAL-01GET /admin/video-gallery
GAL-02GET /admin/video-gallery (status, linkedType, search filters)
GAL-03POST /admin/video-gallery/:id/delete
GAL-04PATCH /admin/video-gallery/:id/reassign
GAL-05GET /admin/video-gallery (totalStorageBytes + per-asset fileSize)
GAL-06GET /admin/video-gallery/storage-breakdown