13 KiB
13 KiB
API Usage Examples - Lesson Attachments
📎 Lesson Attachments Endpoints
1. Upload Attachment (Instructor)
POST /instructor/courses/:courseId/lessons/:lessonId/attachments
Request:
POST /api/instructor/courses/1/lessons/5/attachments
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: multipart/form-data
file: <slides.pdf>
description_th: "สไลด์ประกอบการสอน"
description_en: "Lecture slides"
sort_order: 1
Response 201:
{
"id": 10,
"lesson_id": 5,
"file_name": "slides.pdf",
"file_path": "lessons/5/slides.pdf",
"file_size": 2048576,
"mime_type": "application/pdf",
"description": {
"th": "สไลด์ประกอบการสอน",
"en": "Lecture slides"
},
"sort_order": 1,
"download_url": "/api/lessons/5/attachments/10/download",
"created_at": "2024-12-23T15:00:00Z"
}
Response 422 (File Too Large):
{
"error": {
"code": "FILE_TOO_LARGE",
"message": "File size exceeds maximum limit of 100 MB",
"file_size": 104857600,
"max_size": 104857600
}
}
Response 422 (Too Many Attachments):
{
"error": {
"code": "MAX_ATTACHMENTS_EXCEEDED",
"message": "Maximum 10 attachments per lesson",
"current_count": 10,
"max_count": 10
}
}
Response 422 (Invalid File Type):
{
"error": {
"code": "INVALID_FILE_TYPE",
"message": "File type not allowed",
"mime_type": "application/x-executable",
"allowed_types": ["application/pdf", "application/zip", "image/jpeg", "..."]
}
}
2. List Attachments (Instructor)
GET /instructor/courses/:courseId/lessons/:lessonId/attachments
Request:
GET /api/instructor/courses/1/lessons/5/attachments
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Response 200:
{
"lesson_id": 5,
"attachments": [
{
"id": 10,
"file_name": "slides.pdf",
"file_size": 2048576,
"mime_type": "application/pdf",
"description": {
"th": "สไลด์ประกอบการสอน",
"en": "Lecture slides"
},
"sort_order": 1,
"download_url": "/api/lessons/5/attachments/10/download",
"created_at": "2024-12-23T15:00:00Z"
},
{
"id": 11,
"file_name": "code.zip",
"file_size": 512000,
"mime_type": "application/zip",
"description": {
"th": "โค้ดตัวอย่าง",
"en": "Sample code"
},
"sort_order": 2,
"download_url": "/api/lessons/5/attachments/11/download",
"created_at": "2024-12-23T15:05:00Z"
},
{
"id": 12,
"file_name": "exercises.docx",
"file_size": 256000,
"mime_type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"description": {
"th": "แบบฝึกหัด",
"en": "Exercises"
},
"sort_order": 3,
"download_url": "/api/lessons/5/attachments/12/download",
"created_at": "2024-12-23T15:10:00Z"
}
],
"total": 3
}
3. Update Attachment Info (Instructor)
PUT /instructor/courses/:courseId/lessons/:lessonId/attachments/:attachmentId
Request:
PUT /api/instructor/courses/1/lessons/5/attachments/10
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
{
"description": {
"th": "สไลด์ประกอบการสอน (ฉบับแก้ไข)",
"en": "Lecture slides (revised)"
},
"sort_order": 2
}
Response 200:
{
"id": 10,
"lesson_id": 5,
"file_name": "slides.pdf",
"file_size": 2048576,
"mime_type": "application/pdf",
"description": {
"th": "สไลด์ประกอบการสอน (ฉบับแก้ไข)",
"en": "Lecture slides (revised)"
},
"sort_order": 2,
"updated_at": "2024-12-23T15:20:00Z"
}
4. Delete Attachment (Instructor)
DELETE /instructor/courses/:courseId/lessons/:lessonId/attachments/:attachmentId
Request:
DELETE /api/instructor/courses/1/lessons/5/attachments/10
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Response 204:
No Content
5. Reorder Attachments (Instructor)
PUT /instructor/courses/:courseId/lessons/:lessonId/attachments/reorder
Request:
PUT /api/instructor/courses/1/lessons/5/attachments/reorder
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
{
"attachments": [
{ "id": 11, "sort_order": 1 },
{ "id": 12, "sort_order": 2 },
{ "id": 10, "sort_order": 3 }
]
}
Response 200:
{
"message": "Attachments reordered successfully",
"attachments": [
{
"id": 11,
"file_name": "code.zip",
"sort_order": 1
},
{
"id": 12,
"file_name": "exercises.docx",
"sort_order": 2
},
{
"id": 10,
"file_name": "slides.pdf",
"sort_order": 3
}
]
}
6. Get Lesson with Attachments (Student)
GET /students/courses/:courseId/lessons/:lessonId
Changes: เพิ่ม attachments array
Request:
GET /api/students/courses/1/lessons/5
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Response 200:
{
"id": 5,
"title": "บทที่ 3: ทฤษฎี",
"content": "<p>เนื้อหาบทเรียน...</p>",
"type": "video",
"video_url": "https://cdn.example.com/videos/lesson-5.m3u8",
"duration": "00:25:00",
"is_completed": false,
"can_access": true,
"attachments": [
{
"id": 11,
"file_name": "code.zip",
"file_size": 512000,
"mime_type": "application/zip",
"description": "โค้ดตัวอย่าง",
"download_url": "/api/lessons/5/attachments/11/download"
},
{
"id": 12,
"file_name": "exercises.docx",
"file_size": 256000,
"mime_type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"description": "แบบฝึกหัด",
"download_url": "/api/lessons/5/attachments/12/download"
},
{
"id": 10,
"file_name": "slides.pdf",
"file_size": 2048576,
"mime_type": "application/pdf",
"description": "สไลด์ประกอบการสอน",
"download_url": "/api/lessons/5/attachments/10/download"
}
]
}
7. Download Attachment
GET /lessons/:lessonId/attachments/:attachmentId/download
Request:
GET /api/lessons/5/attachments/10/download
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Response 200:
HTTP/1.1 200 OK
Content-Type: application/pdf
Content-Disposition: attachment; filename="slides.pdf"
Content-Length: 2048576
<binary file data>
Response 403 (Not Enrolled):
{
"error": {
"code": "NOT_ENROLLED",
"message": "You must enroll in this course to download attachments",
"course_id": 1
}
}
Frontend Integration Examples
1. Upload Attachment Component
async function uploadAttachment(courseId, lessonId, file, description) {
const formData = new FormData();
formData.append('file', file);
formData.append('description_th', description.th);
formData.append('description_en', description.en);
formData.append('sort_order', 1);
try {
const response = await fetch(
`/api/instructor/courses/${courseId}/lessons/${lessonId}/attachments`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`
},
body: formData
}
);
if (!response.ok) {
const error = await response.json();
if (error.error.code === 'FILE_TOO_LARGE') {
showAlert('ไฟล์ใหญ่เกินไป (สูงสุด 100 MB)');
} else if (error.error.code === 'MAX_ATTACHMENTS_EXCEEDED') {
showAlert('แนบไฟล์ได้สูงสุด 10 ไฟล์');
} else if (error.error.code === 'INVALID_FILE_TYPE') {
showAlert('ประเภทไฟล์ไม่รองรับ');
}
return;
}
const data = await response.json();
showSuccess('อัปโหลดไฟล์สำเร็จ');
return data;
} catch (error) {
console.error('Upload error:', error);
showAlert('เกิดข้อผิดพลาดในการอัปโหลด');
}
}
2. Display Attachments List
function AttachmentsList({ attachments }) {
return (
<div className="attachments-list">
<h3>📎 ไฟล์แนบ ({attachments.length})</h3>
{attachments.map(attachment => (
<div key={attachment.id} className="attachment-item">
<div className="file-icon">
{getFileIcon(attachment.mime_type)}
</div>
<div className="file-info">
<div className="file-name">{attachment.file_name}</div>
<div className="file-description">{attachment.description}</div>
<div className="file-size">{formatFileSize(attachment.file_size)}</div>
</div>
<a
href={attachment.download_url}
download
className="download-button"
>
⬇ ดาวน์โหลด
</a>
</div>
))}
</div>
);
}
function getFileIcon(mimeType) {
if (mimeType.includes('pdf')) return '📄';
if (mimeType.includes('zip')) return '📦';
if (mimeType.includes('word')) return '📝';
if (mimeType.includes('excel')) return '📊';
if (mimeType.includes('powerpoint')) return '📊';
if (mimeType.includes('image')) return '🖼️';
return '📎';
}
function formatFileSize(bytes) {
if (bytes < 1024) return bytes + ' B';
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
}
3. Drag & Drop Reorder
function ReorderAttachments({ attachments, onReorder }) {
const [items, setItems] = useState(attachments);
const handleDragEnd = (result) => {
if (!result.destination) return;
const reordered = Array.from(items);
const [removed] = reordered.splice(result.source.index, 1);
reordered.splice(result.destination.index, 0, removed);
// Update sort_order
const updated = reordered.map((item, index) => ({
id: item.id,
sort_order: index + 1
}));
setItems(reordered);
onReorder(updated);
};
return (
<DragDropContext onDragEnd={handleDragEnd}>
<Droppable droppableId="attachments">
{(provided) => (
<div {...provided.droppableProps} ref={provided.innerRef}>
{items.map((item, index) => (
<Draggable key={item.id} draggableId={String(item.id)} index={index}>
{(provided) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
{item.file_name}
</div>
)}
</Draggable>
))}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
);
}
Allowed File Types
| Category | MIME Type | Extension |
|---|---|---|
| Documents | application/pdf |
|
application/msword |
.doc | |
application/vnd.openxmlformats-officedocument.wordprocessingml.document |
.docx | |
application/vnd.ms-excel |
.xls | |
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet |
.xlsx | |
application/vnd.ms-powerpoint |
.ppt | |
application/vnd.openxmlformats-officedocument.presentationml.presentation |
.pptx | |
| Archives | application/zip |
.zip |
application/x-rar-compressed |
.rar | |
application/x-7z-compressed |
.7z | |
| Images | image/jpeg |
.jpg |
image/png |
.png | |
image/gif |
.gif | |
| Text | text/plain |
.txt |
text/csv |
.csv |
File Limits
- Max file size: 100 MB per file
- Max attachments: 10 files per lesson
- Total size: 500 MB per lesson
Summary
New Endpoints: 6
POST /instructor/courses/:courseId/lessons/:lessonId/attachmentsGET /instructor/courses/:courseId/lessons/:lessonId/attachmentsPUT /instructor/courses/:courseId/lessons/:lessonId/attachments/:attachmentIdDELETE /instructor/courses/:courseId/lessons/:lessonId/attachments/:attachmentIdPUT /instructor/courses/:courseId/lessons/:lessonId/attachments/reorderGET /lessons/:lessonId/attachments/:attachmentId/download
Modified Endpoints: 2
GET /students/courses/:courseId/lessons/:lessonId- เพิ่ม attachments arrayGET /students/courses/:courseId/learn- เพิ่ม attachments_count