feat: Implement lesson creation with file uploads (video, attachments) and quiz data, integrating MinIO for storage.
This commit is contained in:
parent
4851182f4a
commit
04e2da43c4
6 changed files with 715 additions and 4 deletions
121
Backend/src/middleware/upload.ts
Normal file
121
Backend/src/middleware/upload.ts
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
import multer from 'multer';
|
||||
import { Request } from 'express';
|
||||
import { config } from '../config';
|
||||
import { ValidationError } from './errorHandler';
|
||||
|
||||
/**
|
||||
* Allowed MIME types for different file categories
|
||||
*/
|
||||
export const ALLOWED_VIDEO_TYPES = [
|
||||
'video/mp4',
|
||||
'video/quicktime',
|
||||
'video/x-msvideo',
|
||||
'video/webm',
|
||||
'video/x-matroska',
|
||||
];
|
||||
|
||||
export const ALLOWED_ATTACHMENT_TYPES = [
|
||||
// Documents
|
||||
'application/pdf',
|
||||
'application/msword',
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'application/vnd.ms-excel',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
'application/vnd.ms-powerpoint',
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||
// Text
|
||||
'text/plain',
|
||||
'text/csv',
|
||||
// Images
|
||||
'image/jpeg',
|
||||
'image/png',
|
||||
'image/gif',
|
||||
'image/webp',
|
||||
'image/svg+xml',
|
||||
// Archives
|
||||
'application/zip',
|
||||
'application/x-rar-compressed',
|
||||
'application/x-7z-compressed',
|
||||
];
|
||||
|
||||
/**
|
||||
* Custom file filter for lesson uploads
|
||||
*/
|
||||
const fileFilter = (
|
||||
req: Request,
|
||||
file: Express.Multer.File,
|
||||
callback: multer.FileFilterCallback
|
||||
) => {
|
||||
if (file.fieldname === 'video') {
|
||||
if (ALLOWED_VIDEO_TYPES.includes(file.mimetype)) {
|
||||
callback(null, true);
|
||||
} else {
|
||||
callback(new ValidationError(`Invalid video file type: ${file.mimetype}. Allowed types: ${ALLOWED_VIDEO_TYPES.join(', ')}`));
|
||||
}
|
||||
} else if (file.fieldname === 'attachments') {
|
||||
if (ALLOWED_ATTACHMENT_TYPES.includes(file.mimetype)) {
|
||||
callback(null, true);
|
||||
} else {
|
||||
callback(new ValidationError(`Invalid attachment file type: ${file.mimetype}. Allowed types: PDF, Word, Excel, PowerPoint, images, and archives`));
|
||||
}
|
||||
} else {
|
||||
callback(new ValidationError(`Unknown field: ${file.fieldname}`));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Multer configuration for lesson file uploads
|
||||
* Stores files in memory for direct upload to MinIO
|
||||
*/
|
||||
export const lessonUpload = multer({
|
||||
storage: multer.memoryStorage(),
|
||||
limits: {
|
||||
fileSize: config.upload.maxVideoSize, // Max file size (500MB for videos)
|
||||
files: config.upload.maxAttachmentsPerLesson + 1, // +1 for video
|
||||
},
|
||||
fileFilter,
|
||||
}).fields([
|
||||
{ name: 'video', maxCount: 1 },
|
||||
{ name: 'attachments', maxCount: config.upload.maxAttachmentsPerLesson },
|
||||
]);
|
||||
|
||||
/**
|
||||
* Extended Request interface for uploaded files
|
||||
*/
|
||||
export interface LessonUploadRequest extends Request {
|
||||
files?: {
|
||||
video?: Express.Multer.File[];
|
||||
attachments?: Express.Multer.File[];
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate total attachment size
|
||||
*/
|
||||
export function validateTotalAttachmentSize(
|
||||
files: Express.Multer.File[] | undefined
|
||||
): void {
|
||||
if (!files) return;
|
||||
|
||||
const totalSize = files.reduce((sum, file) => sum + file.size, 0);
|
||||
const maxTotalSize = config.upload.maxAttachmentSize * config.upload.maxAttachmentsPerLesson;
|
||||
|
||||
if (totalSize > maxTotalSize) {
|
||||
throw new ValidationError(
|
||||
`Total attachment size (${(totalSize / 1024 / 1024).toFixed(2)} MB) exceeds maximum allowed (${(maxTotalSize / 1024 / 1024).toFixed(2)} MB)`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate video file size
|
||||
*/
|
||||
export function validateVideoSize(file: Express.Multer.File | undefined): void {
|
||||
if (!file) return;
|
||||
|
||||
if (file.size > config.upload.maxVideoSize) {
|
||||
throw new ValidationError(
|
||||
`Video file size (${(file.size / 1024 / 1024).toFixed(2)} MB) exceeds maximum allowed (${(config.upload.maxVideoSize / 1024 / 1024).toFixed(2)} MB)`
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue