diff --git a/src/main/java/aplp/backend/lms/common/security/SecurityConfig.java b/src/main/java/aplp/backend/lms/common/security/SecurityConfig.java index f21dcba..1f903eb 100644 --- a/src/main/java/aplp/backend/lms/common/security/SecurityConfig.java +++ b/src/main/java/aplp/backend/lms/common/security/SecurityConfig.java @@ -5,16 +5,38 @@ import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; + +import java.util.List; @Configuration public class SecurityConfig { + @Bean + public CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration config = new CorsConfiguration(); + config.setAllowedOrigins(List.of( + "http://localhost:5173", + "http://localhost:3000" + )); + config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS")); + config.setAllowedHeaders(List.of("*")); + config.setAllowCredentials(true); + + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", config); + return source; + } + @Bean public SecurityFilterChain securityFilterChain( HttpSecurity http ) throws Exception { http + .cors(cors -> cors.configurationSource(corsConfigurationSource())) .csrf(AbstractHttpConfigurer::disable) .authorizeHttpRequests(auth -> auth .anyRequest().permitAll() diff --git a/src/main/java/aplp/backend/lms/course/application/dtos/CourseReqDto.java b/src/main/java/aplp/backend/lms/course/application/dtos/CourseReqDto.java index e6c842c..00867f1 100644 --- a/src/main/java/aplp/backend/lms/course/application/dtos/CourseReqDto.java +++ b/src/main/java/aplp/backend/lms/course/application/dtos/CourseReqDto.java @@ -1,6 +1,25 @@ package aplp.backend.lms.course.application.dtos; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + public record CourseReqDto( + @NotBlank(message = "Course code is required") + @Size(max = 50, message = "Course code must not exceed 50 characters") + String courseCode, + + @NotBlank(message = "Title is required") + @Size(max = 200, message = "Title must not exceed 200 characters") String title, - String description + + @Size(max = 255, message = "Slug must not exceed 255 characters") + String slug, + + @Size(max = 1000, message = "Description must not exceed 1000 characters") + String description, + + @Size(max = 500, message = "Image URL must not exceed 500 characters") + String image, + + Boolean isPublished ) {} diff --git a/src/main/java/aplp/backend/lms/course/application/dtos/CourseResDto.java b/src/main/java/aplp/backend/lms/course/application/dtos/CourseResDto.java index 89ab149..67da8f3 100644 --- a/src/main/java/aplp/backend/lms/course/application/dtos/CourseResDto.java +++ b/src/main/java/aplp/backend/lms/course/application/dtos/CourseResDto.java @@ -2,16 +2,30 @@ package aplp.backend.lms.course.application.dtos; import aplp.backend.lms.course.domain.entities.Course; +import java.time.LocalDateTime; + public record CourseResDto( Long id, + String courseCode, String title, - String description + String slug, + String description, + String image, + boolean isPublished, + LocalDateTime createdAt, + LocalDateTime updatedAt ) { public static CourseResDto from(Course course) { return new CourseResDto( course.getId(), + course.getCourseCode(), course.getTitle(), - course.getDescription() + course.getSlug(), + course.getDescription(), + course.getImage(), + course.isPublished(), + course.getCreatedAt(), + course.getUpdatedAt() ); } } \ No newline at end of file diff --git a/src/main/java/aplp/backend/lms/course/domain/entities/Course.java b/src/main/java/aplp/backend/lms/course/domain/entities/Course.java index 5414639..5e62fef 100644 --- a/src/main/java/aplp/backend/lms/course/domain/entities/Course.java +++ b/src/main/java/aplp/backend/lms/course/domain/entities/Course.java @@ -5,14 +5,8 @@ import jakarta.persistence.*; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import org.springframework.data.annotation.CreatedBy; -import org.springframework.data.annotation.CreatedDate; -import org.springframework.data.annotation.LastModifiedBy; -import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; -import java.time.Instant; - @Entity @Table(name = "courses") @EntityListeners(AuditingEntityListener.class) @@ -21,9 +15,21 @@ import java.time.Instant; @NoArgsConstructor public class Course extends BaseEntity { + @Column(name = "course_code", nullable = false, length = 50, unique = true) + private String courseCode; + @Column(nullable = false, length = 200) private String title; + @Column(nullable = false, unique = true, length = 255) + private String slug; + @Column(length = 1000) private String description; + + @Column(length = 500) + private String image; + + @Column(name = "is_published", nullable = false) + private boolean isPublished; } \ No newline at end of file diff --git a/src/main/java/aplp/backend/lms/lesson/application/dtos/LessonReqDto.java b/src/main/java/aplp/backend/lms/lesson/application/dtos/LessonReqDto.java index 8c4af4e..bdd5a27 100644 --- a/src/main/java/aplp/backend/lms/lesson/application/dtos/LessonReqDto.java +++ b/src/main/java/aplp/backend/lms/lesson/application/dtos/LessonReqDto.java @@ -1,17 +1,27 @@ package aplp.backend.lms.lesson.application.dtos; +import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; public record LessonReqDto( - @NotBlank(message = "Lesson code is required") - @Size(max = 50, message = "Lesson code must not exceed 50 characters") - String lessonCode, + @NotNull(message = "Course ID is required") + Long courseId, - @NotBlank(message = "Lesson name is required") - @Size(max = 200, message = "Lesson name must not exceed 200 characters") - String lessonName, + @NotBlank(message = "Title is required") + @Size(max = 200, message = "Title must not exceed 200 characters") + String title, + + @Size(max = 255, message = "Slug must not exceed 255 characters") + String slug, @Size(max = 1000, message = "Description must not exceed 1000 characters") - String description + String description, + + @NotNull(message = "Position is required") + @Min(value = 1, message = "Position must be at least 1") + Integer position, + + Boolean isPublished ) {} diff --git a/src/main/java/aplp/backend/lms/lesson/application/dtos/LessonResDto.java b/src/main/java/aplp/backend/lms/lesson/application/dtos/LessonResDto.java index fc7774b..e12e039 100644 --- a/src/main/java/aplp/backend/lms/lesson/application/dtos/LessonResDto.java +++ b/src/main/java/aplp/backend/lms/lesson/application/dtos/LessonResDto.java @@ -6,18 +6,24 @@ import java.time.LocalDateTime; public record LessonResDto( Long id, - String lessonCode, - String lessonName, + Long courseId, + String title, + String slug, String description, + int position, + boolean isPublished, LocalDateTime createdAt, LocalDateTime updatedAt ) { public static LessonResDto from(Lesson lesson) { return new LessonResDto( lesson.getId(), - lesson.getLessonCode(), - lesson.getLessonName(), + lesson.getCourse().getId(), + lesson.getTitle(), + lesson.getSlug(), lesson.getDescription(), + lesson.getPosition(), + lesson.isPublished(), lesson.getCreatedAt(), lesson.getUpdatedAt() ); diff --git a/src/main/java/aplp/backend/lms/lesson/application/mappers/LessonMapper.java b/src/main/java/aplp/backend/lms/lesson/application/mappers/LessonMapper.java index a655a76..288d809 100644 --- a/src/main/java/aplp/backend/lms/lesson/application/mappers/LessonMapper.java +++ b/src/main/java/aplp/backend/lms/lesson/application/mappers/LessonMapper.java @@ -1,20 +1,31 @@ package aplp.backend.lms.lesson.application.mappers; +import aplp.backend.lms.course.domain.entities.Course; import aplp.backend.lms.lesson.application.dtos.LessonReqDto; import aplp.backend.lms.lesson.application.dtos.LessonResDto; import aplp.backend.lms.lesson.domain.entities.Lesson; import org.mapstruct.Mapper; +import org.mapstruct.Mapping; import org.mapstruct.MappingTarget; @Mapper(componentModel = "spring") public interface LessonMapper { + @Mapping(source = "course.id", target = "courseId") LessonResDto toResponse(Lesson lesson); + @Mapping(source = "courseId", target = "course") Lesson toEntity(LessonReqDto request); void updateEntity( LessonReqDto request, @MappingTarget Lesson lesson ); + + default Course mapCourse(Long courseId) { + if (courseId == null) return null; + Course course = new Course(); + course.setId(courseId); + return course; + } } diff --git a/src/main/java/aplp/backend/lms/lesson/domain/entities/Lesson.java b/src/main/java/aplp/backend/lms/lesson/domain/entities/Lesson.java index dd69bf3..9408016 100644 --- a/src/main/java/aplp/backend/lms/lesson/domain/entities/Lesson.java +++ b/src/main/java/aplp/backend/lms/lesson/domain/entities/Lesson.java @@ -1,9 +1,8 @@ package aplp.backend.lms.lesson.domain.entities; import aplp.backend.core.common.entity.BaseEntity; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Table; +import aplp.backend.lms.course.domain.entities.Course; +import jakarta.persistence.*; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -15,12 +14,22 @@ import lombok.Setter; @NoArgsConstructor public class Lesson extends BaseEntity { - @Column(name = "lesson_code", nullable = false, length = 50, unique = true) - private String lessonCode; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "course_id", nullable = false) + private Course course; - @Column(name = "lesson_name", nullable = false, length = 200) - private String lessonName; + @Column(nullable = false, length = 200) + private String title; + + @Column(nullable = false, unique = true, length = 255) + private String slug; @Column(length = 1000) private String description; + + @Column(nullable = false) + private int position; + + @Column(name = "is_published", nullable = false) + private boolean isPublished; } diff --git a/src/main/java/aplp/backend/lms/lesson/domain/repositories/LessonRepository.java b/src/main/java/aplp/backend/lms/lesson/domain/repositories/LessonRepository.java index 808dcd6..5fb1675 100644 --- a/src/main/java/aplp/backend/lms/lesson/domain/repositories/LessonRepository.java +++ b/src/main/java/aplp/backend/lms/lesson/domain/repositories/LessonRepository.java @@ -13,6 +13,8 @@ public interface LessonRepository { List findAll(); + List findByCourseId(Long courseId); + Lesson save(Lesson lesson); void deleteById(Long id); diff --git a/src/main/java/aplp/backend/lms/lesson/infrastructure/persistence/LessonJpaRepository.java b/src/main/java/aplp/backend/lms/lesson/infrastructure/persistence/LessonJpaRepository.java index e0ae1ae..c2b5e50 100644 --- a/src/main/java/aplp/backend/lms/lesson/infrastructure/persistence/LessonJpaRepository.java +++ b/src/main/java/aplp/backend/lms/lesson/infrastructure/persistence/LessonJpaRepository.java @@ -4,6 +4,9 @@ import aplp.backend.lms.lesson.domain.entities.Lesson; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; +import java.util.List; + @Repository public interface LessonJpaRepository extends JpaRepository { + List findByCourseIdOrderByPositionAsc(Long courseId); } diff --git a/src/main/java/aplp/backend/lms/lesson/infrastructure/persistence/LessonRepositoryImpl.java b/src/main/java/aplp/backend/lms/lesson/infrastructure/persistence/LessonRepositoryImpl.java index 87efb8b..7571e9d 100644 --- a/src/main/java/aplp/backend/lms/lesson/infrastructure/persistence/LessonRepositoryImpl.java +++ b/src/main/java/aplp/backend/lms/lesson/infrastructure/persistence/LessonRepositoryImpl.java @@ -23,6 +23,11 @@ public class LessonRepositoryImpl implements LessonRepository { return repository.findAll(); } + @Override + public List findByCourseId(Long courseId) { + return repository.findByCourseIdOrderByPositionAsc(courseId); + } + @Override public Lesson save(Lesson lesson) { return repository.save(lesson); diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index a1ac880..52b7bd4 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -3,7 +3,7 @@ spring: name: lms datasource: - url: jdbc:postgresql://192.168.2.100:5432/aplp + url: jdbc:postgresql://pgsql.koda.id.vn:5432/aplp username: postgres password: Pa55w0rd