This commit is contained in:
JakkrapartXD 2026-01-08 03:58:29 +00:00
parent a1f7ee057d
commit baaae9f4fa
4 changed files with 815 additions and 0 deletions

View file

@ -0,0 +1,685 @@
# Agent Skills - E-Learning Backend Development
> คู่มือสำหรับ AI Agent ในการพัฒนา Backend ของระบบ E-Learning Platform
> **Tech Stack**: Node.js + Express + Prisma + PostgreSQL + Redis + MinIO (S3)
---
## 🎯 Overview
ระบบ E-Learning Platform เป็น REST API ที่รองรับ:
- **3 บทบาทหลัก**: Admin, Instructor, Student
- **107+ endpoints** ครอบคลุมการจัดการคอร์ส, บทเรียน, แบบทดสอบ, ประกาศ, รายงาน
- **Multi-language support**: Thai (th) และ English (en)
- **File management**: Video streaming, attachments, certificates
- **Real-time features**: Video progress tracking, quiz attempts
---
## 📚 Core Concepts
### 1. Multi-Language Data Structure
ข้อมูลที่แสดงผลต้องรองรับ 2 ภาษา:
```javascript
{
"title": {
"th": "Python สำหรับผู้เริ่มต้น",
"en": "Python for Beginners"
}
}
```
### 2. Role-Based Access Control (RBAC)
```javascript
// 3 บทบาทหลัก
- ADMIN: จัดการระบบทั้งหมด
- INSTRUCTOR: สร้างและจัดการคอร์สของตนเอง
- STUDENT: ลงทะเบียนและเรียนคอร์ส
```
### 3. Course Hierarchy
```
Course
└─ Chapters (บท)
└─ Lessons (บทเรียน)
├─ Video (optional)
├─ Attachments (0-10 files)
└─ Quiz (optional)
```
### 4. Lesson Prerequisites
- Sequential lessons: ต้องเรียนตามลำดับ
- Prerequisite lessons: ระบุบทเรียนที่ต้องจบก่อน
- Quiz requirements: ต้องผ่านแบบทดสอบก่อน
---
## 🔐 Authentication & Authorization
### JWT Token Structure
```javascript
{
userId: 123,
username: "john_doe",
email: "john@example.com",
role: "STUDENT",
iat: 1234567890,
exp: 1234654290
}
```
### Login Methods
- **Username + Password**
- **Email + Password**
### Key Endpoints
```
POST /api/auth/register
POST /api/auth/login
POST /api/auth/logout
POST /api/auth/refresh
POST /api/auth/password/reset-request
POST /api/auth/password/reset
```
---
## 👥 User Management
### User Profile Structure
```javascript
{
id: 1,
username: "john_doe",
email: "john@example.com",
role: { code: "STUDENT", name: {...} },
profile: {
prefix: "Mr.",
first_name: "John",
last_name: "Doe",
phone: "0812345678",
avatar_url: "https://..."
}
}
```
### Key Endpoints
```
GET /api/users/me
GET /api/users/me/profile
PUT /api/users/me/profile
PUT /api/users/me/password
```
---
## 📚 Course Management
### Course Status Flow
```
DRAFT → PENDING → APPROVED/REJECTED → PUBLISHED
```
### Course Structure
```javascript
{
id: 1,
title: { th: "...", en: "..." },
description: { th: "...", en: "..." },
thumbnail: "https://...",
price: 990,
is_free: false,
have_certificate: true,
status: "APPROVED",
category_id: 1,
instructors: [
{ user_id: 5, is_primary: true },
{ user_id: 10, is_primary: false }
]
}
```
### Multi-Instructor Support
- **Primary Instructor**: สามารถจัดการทุกอย่างในคอร์ส
- **Co-Instructors**: ช่วยสอนและจัดการเนื้อหา
- ห้ามลบ instructor คนสุดท้าย
### Key Endpoints
**Public:**
```
GET /api/courses
GET /api/courses/:courseId
```
**Student:**
```
POST /api/students/courses/:courseId/enroll
GET /api/students/courses
GET /api/students/courses/:courseId/learn
```
**Instructor:**
```
POST /api/instructor/courses
GET /api/instructor/courses
PUT /api/instructor/courses/:courseId
DELETE /api/instructor/courses/:courseId
POST /api/instructor/courses/:courseId/submit
POST /api/instructor/courses/:courseId/clone
```
**Instructors Management:**
```
GET /api/instructor/courses/:courseId/instructors
POST /api/instructor/courses/:courseId/instructors
DELETE /api/instructor/courses/:courseId/instructors/:userId
PUT /api/instructor/courses/:courseId/instructors/:userId/primary
```
---
## 📖 Chapters & Lessons
### Lesson Types
- **video**: วีดีโอบทเรียน
- **text**: เนื้อหาข้อความ
- **pdf**: เอกสาร PDF
- **quiz**: แบบทดสอบ
### Create Lesson (One Request)
สามารถสร้าง lesson พร้อม video และ attachments ในครั้งเดียว:
```http
POST /api/instructor/courses/:courseId/chapters/:chapterId/lessons
Content-Type: multipart/form-data
title_th: "บทที่ 1"
title_en: "Lesson 1"
type: "video"
video: <file>
attachments[]: <file1>
attachments[]: <file2>
descriptions[0][th]: "สไลด์"
descriptions[0][en]: "Slides"
```
### Lesson Prerequisites
```javascript
{
is_sequential: true, // ต้องเรียนตามลำดับ
prerequisite_lesson_ids: [1, 2], // ต้องจบบทเรียนนี้ก่อน
require_pass_quiz: true // ต้องผ่านแบบทดสอบ
}
```
### Key Endpoints
**Chapters:**
```
POST /api/instructor/courses/:courseId/chapters
PUT /api/instructor/courses/:courseId/chapters/:chapterId
DELETE /api/instructor/courses/:courseId/chapters/:chapterId
PUT /api/instructor/courses/:courseId/chapters/reorder
```
**Lessons:**
```
POST /api/instructor/courses/:courseId/chapters/:chapterId/lessons
PUT /api/instructor/courses/:courseId/lessons/:lessonId
DELETE /api/instructor/courses/:courseId/lessons/:lessonId
PUT /api/instructor/courses/:courseId/lessons/reorder
```
**Prerequisites:**
```
GET /api/instructor/courses/:courseId/lessons/:lessonId/prerequisites
POST /api/instructor/courses/:courseId/lessons/:lessonId/prerequisites
```
---
## 📎 Attachments
### File Limits
- **Max file size**: 100 MB per file
- **Max attachments**: 10 files per lesson
- **Total size**: 500 MB per lesson
### Allowed File Types
- **Documents**: PDF, DOC, DOCX, XLS, XLSX, PPT, PPTX
- **Archives**: ZIP, RAR, 7Z
- **Images**: JPG, PNG, GIF
- **Text**: TXT, CSV
### Key Endpoints
```
POST /api/instructor/courses/:courseId/lessons/:lessonId/attachments
GET /api/instructor/courses/:courseId/lessons/:lessonId/attachments
PUT /api/instructor/courses/:courseId/lessons/:lessonId/attachments/:attachmentId
DELETE /api/instructor/courses/:courseId/lessons/:lessonId/attachments/:attachmentId
PUT /api/instructor/courses/:courseId/lessons/:lessonId/attachments/reorder
GET /api/students/lessons/:lessonId/attachments/:attachmentId/download
```
---
## 📹 Video Progress Tracking
### Auto-Save Progress
- บันทึกทุก **5 วินาที** ขณะดูวีดีโอ
- Auto-complete เมื่อดู **≥ 90%**
- Resume จากตำแหน่งล่าสุด
### Data Structure
```javascript
{
lesson_id: 5,
video_progress_seconds: 450,
video_duration_seconds: 900,
video_progress_percentage: 50.00,
is_completed: false,
last_watched_at: "2024-12-24T14:00:00Z"
}
```
### Key Endpoints
```
POST /api/students/lessons/:lessonId/progress
GET /api/students/lessons/:lessonId/progress
```
---
## 🎯 Quizzes & Assessments
### Quiz Configuration
```javascript
{
title: { th: "...", en: "..." },
passing_score: 70, // คะแนนผ่าน (%)
time_limit: 30, // นาที
max_attempts: 3, // จำนวนครั้งที่ทำได้
cooldown_minutes: 60, // ระยะเวลารอระหว่างครั้ง
score_policy: "HIGHEST", // HIGHEST, LATEST, FIRST, AVERAGE
shuffle_questions: true,
shuffle_choices: true,
show_answers_after_completion: true
}
```
### Question Types
- **multiple_choice**: เลือกตอบ (1 คำตอบ)
- **true_false**: จริง/เท็จ
- **multiple_select**: เลือกหลายคำตอบ
### Key Endpoints
**Instructor:**
```
POST /api/instructor/courses/:courseId/lessons/:lessonId/quiz
PUT /api/instructor/quizzes/:quizId
DELETE /api/instructor/quizzes/:quizId
POST /api/instructor/quizzes/:quizId/questions
PUT /api/instructor/quizzes/:quizId/questions/:questionId
DELETE /api/instructor/quizzes/:quizId/questions/:questionId
```
**Student:**
```
GET /api/students/quizzes/:quizId
POST /api/students/quizzes/:quizId/submit
GET /api/students/quizzes/:quizId/attempts
GET /api/students/quizzes/:quizId/attempts/:attemptId
```
---
## 📢 Announcements
### Announcement Structure
```javascript
{
id: 1,
course_id: 1,
title: { th: "...", en: "..." },
content: { th: "...", en: "..." },
is_pinned: true,
attachments: [...],
created_at: "2024-12-23T10:00:00Z"
}
```
### Key Endpoints
**Student:**
```
GET /api/students/courses/:courseId/announcements
GET /api/students/courses/:courseId/announcements/:announcementId
```
**Instructor:**
```
GET /api/instructor/courses/:courseId/announcements
POST /api/instructor/courses/:courseId/announcements
PUT /api/instructor/courses/:courseId/announcements/:announcementId
DELETE /api/instructor/courses/:courseId/announcements/:announcementId
```
---
## 📊 Progress & Certificates
### Course Progress Calculation
```javascript
progress_percentage = (completed_lessons / total_lessons) * 100
```
### Certificate Issuance
- ออกอัตโนมัติเมื่อจบคอร์ส 100%
- เฉพาะคอร์สที่ `have_certificate = true`
- เก็บไฟล์ PDF ใน S3
### Key Endpoints
```
GET /api/students/progress
GET /api/students/courses/:courseId/progress
GET /api/students/courses/:courseId/certificate
GET /api/students/certificates
```
---
## 🔄 Course Cloning
### What Gets Cloned
✅ **Copied:**
- Course info (title, description, price)
- All chapters and lessons
- All quizzes and questions
- All attachments (files copied to new location)
- Lesson prerequisites
- Sort orders
❌ **NOT Copied:**
- Enrollments
- Student progress
- Reviews/ratings
- Approval status (new course = DRAFT)
### Key Endpoint
```
POST /api/instructor/courses/:courseId/clone
```
---
## 🔒 Access Control & Permissions
### Lesson Access Check
```
GET /api/students/courses/:courseId/lessons/:lessonId/access-check
```
**Response (Locked):**
```javascript
{
can_access: false,
reason: "incomplete_prerequisites",
required_lessons: [1, 2],
missing_lessons: [2],
next_available_lesson: { id: 2, title: "..." }
}
```
### Ownership Validation
```javascript
// Middleware ตรวจสอบ
if (role === 'INSTRUCTOR' && course.instructorId !== userId) {
return 403;
}
```
---
## 📈 Reports & Analytics
### Student Reports
```
GET /api/students/progress
GET /api/students/courses/:courseId/progress
```
### Instructor Reports
```
GET /api/instructor/courses/:courseId/statistics
GET /api/instructor/courses/:courseId/students
GET /api/instructor/courses/:courseId/students/export
GET /api/instructor/dashboard
```
### Admin Dashboard
```
GET /api/admin/statistics
GET /api/admin/reports/revenue
GET /api/admin/reports/users
GET /api/admin/reports/courses
```
---
## ⚠️ Edge Cases & Best Practices
### 1. Soft Delete > Hard Delete
```javascript
// ใช้ flag แทนการลบจริง
{ isDeleted: true, deletedAt: "..." }
```
### 2. Concurrent Video Progress
```javascript
// ใช้ last_watched_at ตัดสินว่า session ไหนล่าสุด
if (newProgress.last_watched_at > currentProgress.last_watched_at) {
updateProgress(newProgress);
}
```
### 3. Quiz Attempt Validation
```javascript
// ตรวจสอบก่อนให้ทำแบบทดสอบ
if (attempts >= maxAttempts) return error;
if (lastAttempt + cooldown > now) return error;
```
### 4. Lesson Lock Check
```javascript
// ตรวจสอบที่ Backend เสมอ
if (!allPreviousCompleted) {
return { access: false, nextAvailableLesson };
}
```
### 5. Multi-Instructor Constraints
```javascript
// ห้ามลบ instructor คนสุดท้าย
if (instructors.length === 1) {
return error("Cannot remove last instructor");
}
```
---
## 🗄️ Database Schema (Prisma)
### Key Models
```prisma
model User {
id Int @id @default(autoincrement())
username String @unique
email String @unique
password String
role Role @relation(...)
profile Profile?
}
model Course {
id Int @id @default(autoincrement())
title Json // { th: "", en: "" }
description Json
price Decimal
is_free Boolean
have_certificate Boolean
status CourseStatus
chapters Chapter[]
instructors CourseInstructor[]
}
model Lesson {
id Int @id @default(autoincrement())
title Json
content Json
type LessonType
video_url String?
is_sequential Boolean
prerequisite_lessons LessonPrerequisite[]
attachments Attachment[]
quiz Quiz?
}
model Quiz {
id Int @id @default(autoincrement())
title Json
passing_score Int
time_limit Int?
max_attempts Int?
cooldown_minutes Int?
score_policy ScorePolicy
show_answers_after_completion Boolean
questions Question[]
}
```
---
## 🔧 Technical Implementation
### File Upload (MinIO/S3)
```javascript
// Video upload
const videoPath = `courses/${courseId}/lessons/${lessonId}/video.mp4`;
await s3.upload(videoPath, videoFile);
// Attachment upload
const attachmentPath = `lessons/${lessonId}/attachments/${filename}`;
await s3.upload(attachmentPath, file);
```
### Video Progress Auto-Save
```javascript
// Frontend: Save ทุก 5 วินาที
setInterval(() => {
if (!video.paused) {
saveProgress(video.currentTime, video.duration);
}
}, 5000);
```
### JWT Middleware
```javascript
const authMiddleware = async (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1];
const decoded = jwt.verify(token, JWT_SECRET);
req.user = await prisma.user.findUnique({ where: { id: decoded.userId } });
next();
};
```
### Role-Based Middleware
```javascript
const requireRole = (roles) => (req, res, next) => {
if (!roles.includes(req.user.role.code)) {
return res.status(403).json({ error: "Forbidden" });
}
next();
};
```
---
## 📋 Error Codes
| Code | Description |
|------|-------------|
| `INVALID_CREDENTIALS` | อีเมล/รหัสผ่านไม่ถูกต้อง |
| `ALREADY_ENROLLED` | ลงทะเบียนซ้ำ |
| `LESSON_LOCKED` | บทเรียนถูกล็อค |
| `INCOMPLETE_PREREQUISITES` | ยังไม่จบบทเรียนที่ต้องเรียนก่อน |
| `QUIZ_NOT_PASSED` | ยังไม่ผ่านแบบทดสอบ |
| `MAX_ATTEMPTS_EXCEEDED` | ทำแบบทดสอบเกินจำนวนครั้ง |
| `COOLDOWN_ACTIVE` | ต้องรอก่อนทำแบบทดสอบอีกครั้ง |
| `FILE_TOO_LARGE` | ไฟล์ใหญ่เกินไป |
| `INVALID_FILE_TYPE` | ประเภทไฟล์ไม่รองรับ |
| `COURSE_NOT_FOUND` | ไม่พบคอร์ส |
| `UNAUTHORIZED` | ไม่มีสิทธิ์เข้าถึง |
---
## 🎯 Development Checklist
### สำหรับทุก Endpoint
- [ ] Validate input data (Joi/Zod)
- [ ] Check authentication (JWT)
- [ ] Check authorization (Role/Ownership)
- [ ] Handle errors properly
- [ ] Return consistent response format
- [ ] Log important actions
- [ ] Add rate limiting
### สำหรับ File Upload
- [ ] Validate file type
- [ ] Validate file size
- [ ] Generate unique filename
- [ ] Upload to S3/MinIO
- [ ] Save metadata to database
- [ ] Handle upload errors
### สำหรับ Multi-Language
- [ ] Accept `lang` query parameter
- [ ] Return appropriate language
- [ ] Fallback to default language
- [ ] Validate both languages exist
---
## 📚 Related Documentation
- [API Endpoints Reference](../docs/api-docs/api_endpoints.md)
- [API Usage Examples](../docs/api-docs/api_usage_examples.md)
- [Edge Cases Quick Reference](../docs/api-docs/edge_cases_quick_reference.md)
- [Development Setup](../docs/development_setup.md)
---
## 🚀 Quick Start Commands
```bash
# Install dependencies
npm install
# Setup database
npx prisma migrate dev
# Seed database
npx prisma db seed
# Start development server
npm run dev
# Run tests
npm test
```
---
**Last Updated**: 2026-01-07
**Version**: 1.0.0

126
Backend/compose.yaml Normal file
View file

@ -0,0 +1,126 @@
services:
# PostgreSQL Database
postgres:
image: postgres:16-alpine
container_name: elearning-postgres
restart: unless-stopped
security_opt:
- apparmor=unconfined
ports:
- "5432:5432"
environment:
POSTGRES_DB: elearning_dev
POSTGRES_PASSWORD: 12345678
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- elearning-network
healthcheck:
test: [ "CMD-SHELL", "pg_isready -U elearning" ]
interval: 10s
timeout: 5s
retries: 5
# MinIO - S3 Compatible Storage
minio:
image: minio/minio:latest
deploy:
resources:
limits:
memory: 1G
container_name: elearning-minio
restart: unless-stopped
security_opt:
- apparmor=unconfined
ports:
- "9000:9000" # API
- "9001:9001" # Console
environment:
- TZ=Asia/Bangkok
- MINIO_ROOT_USER=admin
- MINIO_ROOT_PASSWORD=12345678
volumes:
- minio_data:/data
command: server /data --console-address ":9001"
networks:
- elearning-network
healthcheck:
test: [ "CMD", "curl", "-f", "http://localhost:9000/minio/health/live" ]
interval: 30s
timeout: 20s
retries: 3
# MinIO Client - Create buckets on startup
minio-init:
image: minio/mc:latest
container_name: elearning-minio-init
security_opt:
- apparmor=unconfined
depends_on:
- minio
entrypoint: >
/bin/sh -c " sleep 5; /usr/bin/mc alias set myminio http://minio:9000 admin 12345678; /usr/bin/mc mb myminio/courses --ignore-existing; /usr/bin/mc mb myminio/videos --ignore-existing; /usr/bin/mc mb myminio/documents --ignore-existing; /usr/bin/mc mb myminio/images --ignore-existing; /usr/bin/mc mb myminio/attachments --ignore-existing; /usr/bin/mc anonymous set download myminio/images; echo 'MinIO buckets created successfully'; exit 0; "
networks:
- elearning-network
# Redis - Cache & Session Store
redis:
image: redis:7-alpine
container_name: elearning-redis
restart: unless-stopped
security_opt:
- apparmor=unconfined
ports:
- "6379:6379"
command: redis-server --appendonly yes --requirepass dev_redis_password
volumes:
- redis_data:/data
networks:
- elearning-network
healthcheck:
test: [ "CMD", "redis-cli", "--raw", "incr", "ping" ]
interval: 10s
timeout: 3s
retries: 5
# Mailhog - Email Testing
mailhog:
image: mailhog/mailhog:latest
container_name: elearning-mailhog
restart: unless-stopped
security_opt:
- apparmor=unconfined
ports:
- "1025:1025" # SMTP
- "8025:8025" # Web UI
networks:
- elearning-network
# Adminer - Database Management UI
adminer:
image: adminer:latest
container_name: elearning-adminer
restart: unless-stopped
security_opt:
- apparmor=unconfined
ports:
- "8080:8080"
environment:
ADMINER_DEFAULT_SERVER: postgres
ADMINER_DESIGN: dracula
networks:
- elearning-network
depends_on:
- postgres
volumes:
postgres_data:
driver: local
minio_data:
driver: local
redis_data:
driver: local
networks:
elearning-network:
driver: bridge