Медиа и загрузки
Загрузка файлов (видео, изображения, PDF, документы) через presigned-URL в MinIO. Поток состоит из трёх шагов: 1) запрос presigned-URL, 2) прямая загрузка файла клиентом по полученному URL, 3) подтверждение загрузки. Доступно любому авторизованному участнику школы.
/api/v1/t/media/presignЗапрос presigned-URL
Шаг 1 из 3. Регистрирует медиа-актив в статусе pending и возвращает временный URL для прямой загрузки файла в MinIO. Поле filename используется только для отображения и НИКОГДА не участвует в построении ключа объекта. Заявленные declared_size и declared_mime фиксируются, но не считаются доверенными — реальный размер и MIME проверяются на шаге подтверждения.
Идентификатор загрузившего (uploader_id) берётся из JWT, а не из тела запроса. Размер ограничен лимитами на каждый тип файла; общий объём ограничен квотой хранилища школы.
Тело запроса
| Параметр | Тип | Описание |
|---|---|---|
kindобяз. | string | Тип файла. Допустимые значения: video, image, pdf, document.Пример: video |
filenameобяз. | string | Отображаемое имя файла (1–255 символов). Не используется в ключе хранения.Пример: lecture-01.mp4 |
declared_sizeобяз. | integer | Заявленный размер файла в байтах (минимум 1). MinIO дополнительно ограничивает Content-Length при загрузке.Пример: 10485760 |
declared_mimeобяз. | string | Заявленный MIME-тип (3–127 символов). Записывается, но не считается доверенным.Пример: video/mp4 |
Пример запроса
curl -X POST https://api.edumentor.kz/api/v1/t/media/presign \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme" \
-H "Content-Type: application/json" \
-d '{
"kind": "video",
"filename": "lecture-01.mp4",
"declared_size": 10485760,
"declared_mime": "video/mp4"
}'Пример ответа
{
"success": true,
"data": {
"media_asset_id": "8f3b1c2a-1d2e-4a5b-9c6d-7e8f9a0b1c2d",
"upload_url": "https://minio.edumentor.kz/lms-media/...",
"form_data": {},
"object_key": "acme/video/8f3b1c2a-....mp4",
"expires_in": 900
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_request_body | Тело запроса не прошло валидацию (например, недопустимый kind или нулевой размер). |
| 400 | invalid_kind | Тип файла не входит в список допустимых. |
| 400 | oversize_cap | Заявленный размер превышает лимит для данного типа файла. |
| 400 | storage_quota_exceeded | Превышена квота хранилища школы. |
| 401 | unauthorized | Токен отсутствует, истёк или недействителен. |
| 404 | tenant_not_found | Школа не найдена. |
/api/v1/t/media/:id/confirmПодтверждение загрузки
Шаг 3 из 3. Вызывается после успешной загрузки файла по presigned-URL. Сервер делает HEAD-запрос к объекту, повторно проверяет реальный размер и MIME-тип, и переводит актив в статус confirmed. Тело запроса пустое.
Поля duration_seconds (для видео) и total_pages (для pdf) извлекаются бэкендом асинхронно через воркер уже после подтверждения, поэтому сразу в ответе на confirm они обычно null. Они становятся доступны при последующем чтении актива (GET /api/v1/t/media/:id). Для типов, к которым метаданные неприменимы, значения остаются null.
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | Идентификатор медиа-актива, полученный на шаге presign. |
Пример запроса
curl -X POST https://api.edumentor.kz/api/v1/t/media/8f3b1c2a-1d2e-4a5b-9c6d-7e8f9a0b1c2d/confirm \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme"Пример ответа
{
"success": true,
"data": {
"id": "8f3b1c2a-1d2e-4a5b-9c6d-7e8f9a0b1c2d",
"kind": "video",
"filename": "lecture-01.mp4",
"size": 10485760,
"mime": "video/mp4",
"status": "confirmed",
"duration_seconds": null,
"total_pages": null,
"presigned_url": "https://minio.edumentor.kz/lms-media/...",
"expires_in": 900
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_id_format | Идентификатор актива не является валидным UUID. |
| 400 | confirm_failed | Не удалось подтвердить загрузку (объект отсутствует в хранилище). |
| 400 | oversize_cap | Реальный размер файла превышает лимит для типа. |
| 400 | mime_mismatch | Реальный MIME-тип файла не соответствует заявленному типу. |
| 401 | unauthorized | Токен отсутствует, истёк или недействителен. |
| 404 | media_not_found | Медиа-актив не найден в текущей школе. |
/api/v1/t/media/:idПолучить медиа-актив
Возвращает метаданные актива и свежий presigned-URL для скачивания, если статус confirmed. Для активов в статусе pending или failed поле presigned_url пустое, а ответ возвращается с HTTP 409 и кодом media_not_confirmed (метаданные при этом включены в поле data).
Поля duration_seconds (длительность для видео) и total_pages (число страниц для pdf) авто-извлекаются бэкендом асинхронно через воркер после подтверждения загрузки. Они равны null, пока метаданные ещё не извлечены или неприменимы к типу актива (например, duration_seconds всегда null для pdf, а total_pages — для видео).
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | Идентификатор медиа-актива. |
Пример запроса
curl https://api.edumentor.kz/api/v1/t/media/8f3b1c2a-1d2e-4a5b-9c6d-7e8f9a0b1c2d \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme"Пример ответа
{
"success": true,
"data": {
"id": "8f3b1c2a-1d2e-4a5b-9c6d-7e8f9a0b1c2d",
"kind": "video",
"filename": "lecture-01.mp4",
"size": 10485760,
"mime": "video/mp4",
"status": "confirmed",
"duration_seconds": 360,
"total_pages": null,
"presigned_url": "https://minio.edumentor.kz/lms-media/...",
"expires_in": 900
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_id_format | Идентификатор актива не является валидным UUID. |
| 401 | unauthorized | Токен отсутствует, истёк или недействителен. |
| 404 | media_not_found | Медиа-актив не найден в текущей школе. |
| 409 | media_not_confirmed | Актив ещё не подтверждён (статус pending/failed); presigned-URL недоступен. |