elearning/docs/api-docs/api_attachments_examples.md

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 .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