2026-01-15 17:57:32 +07:00
|
|
|
import { prisma } from '../config/database';
|
|
|
|
|
import { Prisma } from '@prisma/client';
|
|
|
|
|
import { config } from '../config';
|
|
|
|
|
import { logger } from '../config/logger';
|
2026-01-20 07:13:59 +00:00
|
|
|
import { listCourseResponse, getCourseResponse } from '../types/courses.types';
|
2026-01-15 17:57:32 +07:00
|
|
|
import { UnauthorizedError, ValidationError, ForbiddenError } from '../middleware/errorHandler';
|
2026-01-28 15:31:21 +07:00
|
|
|
import { getPresignedUrl } from '../config/minio';
|
2026-01-15 17:57:32 +07:00
|
|
|
|
|
|
|
|
export class CoursesService {
|
2026-01-23 10:37:19 +07:00
|
|
|
async ListCourses(category_id?: number): Promise<listCourseResponse> {
|
2026-01-15 17:57:32 +07:00
|
|
|
try {
|
2026-01-23 11:29:46 +07:00
|
|
|
const where: Prisma.CourseWhereInput = {
|
|
|
|
|
status: 'APPROVED',
|
|
|
|
|
};
|
2026-01-23 10:37:19 +07:00
|
|
|
|
|
|
|
|
if (category_id) {
|
|
|
|
|
where.category_id = category_id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const courses = await prisma.course.findMany({ where });
|
2026-01-28 15:31:21 +07:00
|
|
|
|
|
|
|
|
// Generate presigned URLs for thumbnails
|
|
|
|
|
const coursesWithUrls = await Promise.all(
|
|
|
|
|
courses.map(async (course) => {
|
|
|
|
|
let thumbnail_presigned_url: string | null = null;
|
|
|
|
|
if (course.thumbnail_url) {
|
|
|
|
|
try {
|
|
|
|
|
thumbnail_presigned_url = await getPresignedUrl(course.thumbnail_url, 3600);
|
|
|
|
|
} catch (err) {
|
|
|
|
|
logger.warn(`Failed to generate presigned URL for thumbnail: ${err}`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return {
|
|
|
|
|
...course,
|
|
|
|
|
thumbnail_url: thumbnail_presigned_url,
|
|
|
|
|
};
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
|
2026-01-15 17:57:32 +07:00
|
|
|
return {
|
|
|
|
|
code: 200,
|
|
|
|
|
message: 'Courses fetched successfully',
|
2026-01-28 15:31:21 +07:00
|
|
|
total: coursesWithUrls.length,
|
|
|
|
|
data: coursesWithUrls,
|
2026-01-15 17:57:32 +07:00
|
|
|
};
|
|
|
|
|
} catch (error) {
|
|
|
|
|
logger.error('Failed to fetch courses', { error });
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async GetCourseById(id: number): Promise<getCourseResponse> {
|
|
|
|
|
try {
|
2026-01-23 11:29:46 +07:00
|
|
|
const course = await prisma.course.findFirst({
|
|
|
|
|
where: {
|
|
|
|
|
id,
|
|
|
|
|
status: 'APPROVED' // Only show approved courses to students
|
|
|
|
|
}
|
|
|
|
|
});
|
2026-01-28 15:31:21 +07:00
|
|
|
|
|
|
|
|
if (!course) {
|
|
|
|
|
return {
|
|
|
|
|
code: 200,
|
2026-01-28 17:08:44 +07:00
|
|
|
message: 'no Course fetched successfully',
|
2026-01-28 15:31:21 +07:00
|
|
|
data: null,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Generate presigned URL for thumbnail
|
|
|
|
|
let thumbnail_presigned_url: string | null = null;
|
|
|
|
|
if (course.thumbnail_url) {
|
|
|
|
|
try {
|
|
|
|
|
thumbnail_presigned_url = await getPresignedUrl(course.thumbnail_url, 3600);
|
|
|
|
|
} catch (err) {
|
|
|
|
|
logger.warn(`Failed to generate presigned URL for thumbnail: ${err}`);
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-01-15 17:57:32 +07:00
|
|
|
return {
|
|
|
|
|
code: 200,
|
|
|
|
|
message: 'Course fetched successfully',
|
2026-01-28 15:31:21 +07:00
|
|
|
data: {
|
|
|
|
|
...course,
|
|
|
|
|
thumbnail_url: thumbnail_presigned_url,
|
|
|
|
|
},
|
2026-01-15 17:57:32 +07:00
|
|
|
};
|
|
|
|
|
} catch (error) {
|
|
|
|
|
logger.error('Failed to fetch course', { error });
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|