# API Usage Examples - Lesson Attachments ## 📎 Lesson Attachments Endpoints --- ## 1. Upload Attachment (Instructor) ### POST `/instructor/courses/:courseId/lessons/:lessonId/attachments` #### Request: ```http POST /api/instructor/courses/1/lessons/5/attachments Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... Content-Type: multipart/form-data file: description_th: "สไลด์ประกอบการสอน" description_en: "Lecture slides" sort_order: 1 ``` #### Response 201: ```json { "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): ```json { "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): ```json { "error": { "code": "MAX_ATTACHMENTS_EXCEEDED", "message": "Maximum 10 attachments per lesson", "current_count": 10, "max_count": 10 } } ``` #### Response 422 (Invalid File Type): ```json { "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: ```http GET /api/instructor/courses/1/lessons/5/attachments Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... ``` #### Response 200: ```json { "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: ```http 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: ```json { "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: ```http 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: ```http 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: ```json { "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: ```http GET /api/students/courses/1/lessons/5 Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... ``` #### Response 200: ```json { "id": 5, "title": "บทที่ 3: ทฤษฎี", "content": "

เนื้อหาบทเรียน...

", "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: ```http GET /api/lessons/5/attachments/10/download Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... ``` #### Response 200: ```http HTTP/1.1 200 OK Content-Type: application/pdf Content-Disposition: attachment; filename="slides.pdf" Content-Length: 2048576 ``` #### Response 403 (Not Enrolled): ```json { "error": { "code": "NOT_ENROLLED", "message": "You must enroll in this course to download attachments", "course_id": 1 } } ``` --- ## Frontend Integration Examples ### 1. Upload Attachment Component ```javascript 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 ```javascript function AttachmentsList({ attachments }) { return (

📎 ไฟล์แนบ ({attachments.length})

{attachments.map(attachment => (
{getFileIcon(attachment.mime_type)}
{attachment.file_name}
{attachment.description}
{formatFileSize(attachment.file_size)}
⬇ ดาวน์โหลด
))}
); } 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 ```javascript 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 ( {(provided) => (
{items.map((item, index) => ( {(provided) => (
{item.file_name}
)}
))} {provided.placeholder}
)}
); } ``` --- ## Allowed File Types | Category | MIME Type | Extension | |----------|-----------|-----------| | **Documents** | `application/pdf` | .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/attachments` - `GET /instructor/courses/:courseId/lessons/:lessonId/attachments` - `PUT /instructor/courses/:courseId/lessons/:lessonId/attachments/:attachmentId` - `DELETE /instructor/courses/:courseId/lessons/:lessonId/attachments/:attachmentId` - `PUT /instructor/courses/:courseId/lessons/:lessonId/attachments/reorder` - `GET /lessons/:lessonId/attachments/:attachmentId/download` **Modified Endpoints**: 2 - `GET /students/courses/:courseId/lessons/:lessonId` - เพิ่ม attachments array - `GET /students/courses/:courseId/learn` - เพิ่ม attachments_count