From b62035003a590577c8df50b08f4cccc75222dbb3 Mon Sep 17 00:00:00 2001 From: Yesitha Sathsara <60166952+yesitha@users.noreply.github.com> Date: Sun, 9 Mar 2025 22:46:54 +0530 Subject: [PATCH] video upload to AWS S3 implemented --- .../main/java/com/itgura/util/URIPrefix.java | 3 + .../main/java/com/itgura/config/S3Config.java | 33 +++++ .../itgura/controller/MaterialController.java | 45 +++++- .../src/main/resources/application.properties | 7 +- .../main/java/com/itgura/entity/Material.java | 20 ++- .../java/com/itgura/entity/MaterialType.java | 2 +- .../itgura/repository/MaterialRepository.java | 5 + .../repository/MaterialTypeRepository.java | 3 + .../itgura/request/VideoUploadRequest.java | 34 +++++ .../PreSignedUrlToUploadVideoResponseDto.java | 22 +++ .../resource-management-service/pom.xml | 14 ++ .../com/itgura/service/MaterialService.java | 11 +- .../service/impl/MaterialServiceImpl.java | 133 ++++++++++++++---- 13 files changed, 294 insertions(+), 38 deletions(-) create mode 100644 resource-management/resource-management-all/src/main/java/com/itgura/config/S3Config.java create mode 100644 resource-management/resource-management-dao/src/main/java/com/itgura/request/VideoUploadRequest.java create mode 100644 resource-management/resource-management-dao/src/main/java/com/itgura/response/dto/PreSignedUrlToUploadVideoResponseDto.java diff --git a/lib-global/src/main/java/com/itgura/util/URIPrefix.java b/lib-global/src/main/java/com/itgura/util/URIPrefix.java index 995ba5f..68483a0 100644 --- a/lib-global/src/main/java/com/itgura/util/URIPrefix.java +++ b/lib-global/src/main/java/com/itgura/util/URIPrefix.java @@ -28,4 +28,7 @@ public class URIPrefix { public static final String GET_PRICE = "/get-price"; public static final String UPDATE_TAGS = "/update-tags"; public static final String GET_VIDEO_Signed_Url = "/get-video-signed-url"; + public static final String Upload_Video_Material = "/upload-video"; + public static final String GET_Pre_Signed_Url_To_Upload_Video = "/get-pre-signed-url-to-upload-video"; + public static final String MARKED_VIDEO_AS_UPLOADED = "/marked-video-as-uploaded"; } diff --git a/resource-management/resource-management-all/src/main/java/com/itgura/config/S3Config.java b/resource-management/resource-management-all/src/main/java/com/itgura/config/S3Config.java new file mode 100644 index 0000000..4f914ff --- /dev/null +++ b/resource-management/resource-management-all/src/main/java/com/itgura/config/S3Config.java @@ -0,0 +1,33 @@ +package com.itgura.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.presigner.S3Presigner; + +@Configuration +public class S3Config { + + @Value("${aws.s3.accessKey}") + private String S3_ACCESS_KEY; + + @Value("${aws.s3.secretKey}") + private String S3_SECRET_KEY; + + + @Bean + public S3Presigner s3Presigner() { + return S3Presigner.builder() + .region(Region.AP_SOUTH_1) // Replace with your AWS region + .credentialsProvider(StaticCredentialsProvider.create( + AwsBasicCredentials.create( + S3_ACCESS_KEY, + S3_SECRET_KEY + ) + )) + .build(); + } +} diff --git a/resource-management/resource-management-all/src/main/java/com/itgura/controller/MaterialController.java b/resource-management/resource-management-all/src/main/java/com/itgura/controller/MaterialController.java index 37e77e8..4864ab5 100644 --- a/resource-management/resource-management-all/src/main/java/com/itgura/controller/MaterialController.java +++ b/resource-management/resource-management-all/src/main/java/com/itgura/controller/MaterialController.java @@ -2,13 +2,18 @@ import com.itgura.dto.AppRequest; import com.itgura.dto.AppResponse; +import com.itgura.exception.ValueNotExistException; import com.itgura.request.MaterialRequest; import com.itgura.request.SignedUrlRequest; +import com.itgura.request.VideoUploadRequest; +import com.itgura.response.dto.PreSignedUrlToUploadVideoResponseDto; import com.itgura.service.MaterialService; import com.itgura.util.ResourceManagementURI; import com.itgura.util.URIPathVariable; import com.itgura.util.URIPrefix; +import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; import java.util.UUID; @@ -19,6 +24,7 @@ public class MaterialController { @Autowired private MaterialService materialService; + @PreAuthorize("hasAuthority('ROLE_ADMIN') or hasAuthority('ROLE_TEACHER')") @PostMapping( ResourceManagementURI.MATERIAL+URIPrefix.CREATE+ResourceManagementURI.SESSION_ID) public AppResponse createMaterial(@PathVariable UUID sessionId, @RequestBody AppRequest request) { try { @@ -29,6 +35,7 @@ public AppResponse createMaterial(@PathVariable UUID sessionId, @Request return AppResponse.error(null, e.getMessage(), "Server Error", "500", ""); } } + @PreAuthorize("hasAuthority('ROLE_ADMIN') or hasAuthority('ROLE_TEACHER')") @PatchMapping( ResourceManagementURI.MATERIAL+URIPrefix.UPDATE+ResourceManagementURI.MATERIAL_ID) public AppResponse updateMaterial(@PathVariable UUID materialId, @RequestBody AppRequest request) { try { @@ -39,6 +46,7 @@ public AppResponse updateMaterial(@PathVariable UUID materialId, @Reques return AppResponse.error(null, e.getMessage(), "Server Error", "500", ""); } } + @PreAuthorize("hasAuthority('ROLE_ADMIN') or hasAuthority('ROLE_TEACHER')") @DeleteMapping( ResourceManagementURI.MATERIAL+URIPrefix.DELETE+ResourceManagementURI.MATERIAL_ID) public AppResponse deleteMaterial(@PathVariable UUID materialId) { try { @@ -50,7 +58,7 @@ public AppResponse deleteMaterial(@PathVariable UUID materialId) { } } - @PostMapping (ResourceManagementURI.MATERIAL + URIPrefix.GET_VIDEO_Signed_Url) + @PostMapping (ResourceManagementURI.MATERIAL+URIPrefix.GET_VIDEO_Signed_Url) public AppResponse getVideoMaterialSignedUrl(@RequestBody AppRequest request) { try { String s = materialService.getVideoMaterialSignedUrl(request.getData()); @@ -61,6 +69,41 @@ public AppResponse getVideoMaterialSignedUrl(@RequestBody AppRequest getPreSignedUrlToUploadVideo(@RequestBody AppRequest request) { + try { + PreSignedUrlToUploadVideoResponseDto responseDto = materialService.getPreSignedUrlToUploadVideo(request.getData()); + return AppResponse.ok(responseDto); + } catch (ValueNotExistException e) { + e.printStackTrace(); + return AppResponse.error(null, e.getMessage(), "Value Not Found", "404", ""); + }catch (Exception e) { + e.printStackTrace(); + return AppResponse.error(null, e.getMessage(), "Server Error", "500", ""); + } + } + + @PreAuthorize("hasAuthority('ROLE_ADMIN') or hasAuthority('ROLE_TEACHER')") + @PostMapping (ResourceManagementURI.MATERIAL+URIPrefix.MARKED_VIDEO_AS_UPLOADED+ResourceManagementURI.MATERIAL_ID) + public AppResponse markedVideoAsUploaded( @PathVariable UUID materialId) { + try { + String s = materialService.markedVideoAsUploaded(materialId); + return AppResponse.ok(s); + + } catch (ValueNotExistException ex) { + ex.printStackTrace(); + return AppResponse.error(null, ex.getMessage(), "Value Not Found", "404", ""); + + } catch (Exception e) { + e.printStackTrace(); + return AppResponse.error(null, e.getMessage(), "Server Error", "500", ""); + } + } + + + + } diff --git a/resource-management/resource-management-all/src/main/resources/application.properties b/resource-management/resource-management-all/src/main/resources/application.properties index 5defccc..af9a71c 100644 --- a/resource-management/resource-management-all/src/main/resources/application.properties +++ b/resource-management/resource-management-all/src/main/resources/application.properties @@ -14,6 +14,7 @@ spring.servlet.multipart.max-request-size=10MB #cloudfront.keyPairId=YOUR_KEY_PAIR_ID #cloudfront.privateKeyPath=classpath:private-key.pem # Ensure the private key file is in the classpath -cloudfront.domain=https://yourdistribution.cloudfront.net -cloudfront.keyPairId=YOUR_KEY_PAIR_ID -cloudfront.privateKeyPath=classpath:private-key.pem # Ensure the private key file is in the classpath \ No newline at end of file +#AWS s3 +aws.s3.bucketName= YourBucketName +aws.s3.accessKey=YOUR_ACCESS_KEY +aws.s3.secretKey=YOUR_SECRET_KEY \ No newline at end of file diff --git a/resource-management/resource-management-dao/src/main/java/com/itgura/entity/Material.java b/resource-management/resource-management-dao/src/main/java/com/itgura/entity/Material.java index ebcce45..24b30a3 100644 --- a/resource-management/resource-management-dao/src/main/java/com/itgura/entity/Material.java +++ b/resource-management/resource-management-dao/src/main/java/com/itgura/entity/Material.java @@ -6,6 +6,7 @@ import lombok.NoArgsConstructor; import lombok.Setter; +import java.time.Instant; import java.util.UUID; @Entity @@ -14,13 +15,19 @@ @NoArgsConstructor @Getter @Setter -@PrimaryKeyJoinColumn(name = "material_id") +@PrimaryKeyJoinColumn(name = "material_id") // For video this was video_id public class Material extends Content { - @Column(name = "material_name") + @Column(name = "material_name") // For video this was original video name private String materialName; - @Column(name = "reference") + @Column(name = "reference") // For video this was S3 Key private String reference; + @Column(name="signedUrlExpireTime") + private int signedUrlExpireTime; + @Column(name="status") + private String status; // For video this was video status (pending,uploaded) + @Column(name="created_at") + private Instant createdAt; // For video this was video created at @Column(name = "description",columnDefinition = "TEXT") private String description; @ManyToOne @@ -32,4 +39,11 @@ public class Material extends Content { @Column(name = "is_available_for_students") private Boolean isAvailableForStudents; + @PrePersist + public void prePersist() { + if (this.createdAt == null) { + this.createdAt = Instant.now(); + } + } + } diff --git a/resource-management/resource-management-dao/src/main/java/com/itgura/entity/MaterialType.java b/resource-management/resource-management-dao/src/main/java/com/itgura/entity/MaterialType.java index 28f3bc0..7c8f6a2 100644 --- a/resource-management/resource-management-dao/src/main/java/com/itgura/entity/MaterialType.java +++ b/resource-management/resource-management-dao/src/main/java/com/itgura/entity/MaterialType.java @@ -21,7 +21,7 @@ public class MaterialType { @GeneratedValue(strategy = GenerationType.UUID) @Column(name = "material_type_id") private UUID materialTypeId; - @Column(name = "material_type") + @Column(name = "material_type") // e.g. VIDEO, PDF, DOCX, PPT private String materialType; @OneToMany(mappedBy = "materialType",cascade = {CascadeType.PERSIST, CascadeType.MERGE}) private List materialList; diff --git a/resource-management/resource-management-dao/src/main/java/com/itgura/repository/MaterialRepository.java b/resource-management/resource-management-dao/src/main/java/com/itgura/repository/MaterialRepository.java index d6b7765..2413b6c 100644 --- a/resource-management/resource-management-dao/src/main/java/com/itgura/repository/MaterialRepository.java +++ b/resource-management/resource-management-dao/src/main/java/com/itgura/repository/MaterialRepository.java @@ -2,12 +2,17 @@ import com.itgura.entity.Material; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.stereotype.Repository; +import java.time.Instant; +import java.util.List; import java.util.UUID; @Repository @EnableJpaRepositories public interface MaterialRepository extends JpaRepository { + @Query("SELECT m FROM Material m WHERE m.status = ?1 AND m.createdAt < ?2") + List findAllByStatusAndCreatedAtBefore(String pending, Instant cutoffTime); } diff --git a/resource-management/resource-management-dao/src/main/java/com/itgura/repository/MaterialTypeRepository.java b/resource-management/resource-management-dao/src/main/java/com/itgura/repository/MaterialTypeRepository.java index c40cc92..8dd70e7 100644 --- a/resource-management/resource-management-dao/src/main/java/com/itgura/repository/MaterialTypeRepository.java +++ b/resource-management/resource-management-dao/src/main/java/com/itgura/repository/MaterialTypeRepository.java @@ -2,6 +2,7 @@ import com.itgura.entity.MaterialType; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.stereotype.Repository; @@ -10,4 +11,6 @@ @Repository @EnableJpaRepositories public interface MaterialTypeRepository extends JpaRepository { + @Query("SELECT mt FROM MaterialType mt WHERE mt.materialType = :name") + MaterialType findByName(String name); } diff --git a/resource-management/resource-management-dao/src/main/java/com/itgura/request/VideoUploadRequest.java b/resource-management/resource-management-dao/src/main/java/com/itgura/request/VideoUploadRequest.java new file mode 100644 index 0000000..7a2de7c --- /dev/null +++ b/resource-management/resource-management-dao/src/main/java/com/itgura/request/VideoUploadRequest.java @@ -0,0 +1,34 @@ +package com.itgura.request; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.UUID; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class VideoUploadRequest { + + @NotNull(message = "Original file name is required") + @JsonProperty("original_file_name") + private String OriginalFileName; + + @NotNull(message = "Signed URL expire time required") + @JsonProperty("signed_url_expire_time") + private Integer signedUrlExpireTime; + + @JsonProperty("Description") + private String description; + + @JsonProperty("session_id") + private UUID sessionId; + + @JsonProperty("is_available_for_students") + private Boolean isAvailableForStudents; +} diff --git a/resource-management/resource-management-dao/src/main/java/com/itgura/response/dto/PreSignedUrlToUploadVideoResponseDto.java b/resource-management/resource-management-dao/src/main/java/com/itgura/response/dto/PreSignedUrlToUploadVideoResponseDto.java new file mode 100644 index 0000000..8e9b152 --- /dev/null +++ b/resource-management/resource-management-dao/src/main/java/com/itgura/response/dto/PreSignedUrlToUploadVideoResponseDto.java @@ -0,0 +1,22 @@ +package com.itgura.response.dto; + + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.UUID; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class PreSignedUrlToUploadVideoResponseDto { + + @JsonProperty("video_uuid") + private UUID id; + @JsonProperty("pre_signed_url") + private String preSignedUrl; +} diff --git a/resource-management/resource-management-service/pom.xml b/resource-management/resource-management-service/pom.xml index ebfb0e6..9dcac47 100644 --- a/resource-management/resource-management-service/pom.xml +++ b/resource-management/resource-management-service/pom.xml @@ -29,6 +29,20 @@ cloudfront 2.29.15 + + + + + + + + + software.amazon.awssdk + s3 + 2.29.39 + compile + + diff --git a/resource-management/resource-management-service/src/main/java/com/itgura/service/MaterialService.java b/resource-management/resource-management-service/src/main/java/com/itgura/service/MaterialService.java index 08f6f18..c3faf99 100644 --- a/resource-management/resource-management-service/src/main/java/com/itgura/service/MaterialService.java +++ b/resource-management/resource-management-service/src/main/java/com/itgura/service/MaterialService.java @@ -1,16 +1,15 @@ package com.itgura.service; -import com.itgura.dto.AppRequest; import com.itgura.exception.ApplicationException; import com.itgura.exception.BadRequestRuntimeException; import com.itgura.exception.ValueNotExistException; import com.itgura.request.MaterialRequest; import com.itgura.request.SignedUrlRequest; -import com.itgura.response.dto.MaterialResponseDto; +import com.itgura.request.VideoUploadRequest; +import com.itgura.response.dto.PreSignedUrlToUploadVideoResponseDto; import javax.security.auth.login.CredentialNotFoundException; -import java.util.List; import java.util.UUID; public interface MaterialService { @@ -19,6 +18,12 @@ public interface MaterialService { String deleteMaterial(UUID materialId) throws ApplicationException, CredentialNotFoundException, BadRequestRuntimeException; String getVideoMaterialSignedUrl(SignedUrlRequest signedUrlRequest) throws Exception; + + PreSignedUrlToUploadVideoResponseDto getPreSignedUrlToUploadVideo(VideoUploadRequest videoUploadRequest) throws ValueNotExistException,RuntimeException, ApplicationException; + + String markedVideoAsUploaded(UUID uuid) throws ValueNotExistException; + + // MaterialResponseDto findMaterialById(UUID materialId) throws ApplicationException, CredentialNotFoundException, BadRequestRuntimeException; // List findAllMaterial(UUID sessionId) throws ApplicationException, CredentialNotFoundException, BadRequestRuntimeException; } diff --git a/resource-management/resource-management-service/src/main/java/com/itgura/service/impl/MaterialServiceImpl.java b/resource-management/resource-management-service/src/main/java/com/itgura/service/impl/MaterialServiceImpl.java index c883012..482d612 100644 --- a/resource-management/resource-management-service/src/main/java/com/itgura/service/impl/MaterialServiceImpl.java +++ b/resource-management/resource-management-service/src/main/java/com/itgura/service/impl/MaterialServiceImpl.java @@ -11,7 +11,9 @@ import com.itgura.repository.SessionRepository; import com.itgura.request.MaterialRequest; import com.itgura.request.SignedUrlRequest; +import com.itgura.request.VideoUploadRequest; import com.itgura.request.dto.UserResponseDto; +import com.itgura.response.dto.PreSignedUrlToUploadVideoResponseDto; import com.itgura.service.MaterialService; import com.itgura.service.UserDetailService; import com.itgura.util.UserUtil; @@ -22,12 +24,15 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.ClassPathResource; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import software.amazon.awssdk.services.cloudfront.CloudFrontUtilities; import software.amazon.awssdk.services.cloudfront.internal.utils.SigningUtils; -import software.amazon.awssdk.services.cloudfront.model.CannedSignerRequest; import software.amazon.awssdk.services.cloudfront.model.CustomSignerRequest; import software.amazon.awssdk.services.cloudfront.url.SignedUrl; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; +import software.amazon.awssdk.services.s3.presigner.S3Presigner; +import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest; import javax.security.auth.login.CredentialNotFoundException; import java.nio.file.Files; @@ -37,16 +42,16 @@ import java.security.Security; import java.security.Signature; import java.security.spec.PKCS8EncodedKeySpec; +import java.time.Duration; import java.time.Instant; -import java.time.temporal.ChronoUnit; import java.util.*; - - +import static com.google.common.io.Files.getFileExtension; @Service public class MaterialServiceImpl implements MaterialService { + private final S3Presigner s3Presigner; @Autowired private UserDetailService userDetailService; @Autowired @@ -55,17 +60,18 @@ public class MaterialServiceImpl implements MaterialService { private MaterialTypeRepository materialTypeRepository; @Autowired private MaterialRepository materialRepository; - @Value("${cloudfront.domain}") private String cloudFrontDomain; - @Value("${cloudfront.keyPairId}") private String keyPairId; - @Value("${cloudfront.privateKeyPath}") private String privateKeyPath; + @Value("${aws.s3.bucketName}") + private String bucketName; - + public MaterialServiceImpl(S3Presigner s3Presigner) { + this.s3Presigner = s3Presigner; + } @Override @Transactional @@ -95,12 +101,11 @@ public String addMaterial(UUID sessionId, MaterialRequest request) throws Applic } else { throw new ForbiddenException("You are not allowed to add material"); } - }catch (Exception e) { + } catch (Exception e) { throw new RuntimeException(e); } - } @Override @@ -113,25 +118,25 @@ public String updateMaterial(UUID materialId, MaterialRequest request) throws Ap } if (loggedUserDetails.getUserRoles().equals("ADMIN") || loggedUserDetails.getUserRoles().equals("TEACHER")) { - Material material = materialRepository.findById(materialId) - .orElseThrow(() -> new ValueNotExistException("Material not found with id " + materialId)); - material.setMaterialName(request.getMaterialName()); - material.setDescription(request.getDescription()); - material.setIsAvailableForStudents(request.getIsAvailableForStudent()); - material.setReference(request.getReference()); - MaterialType materialType = materialTypeRepository.findById(request.getMaterialType()) - .orElseThrow(() -> new ValueNotExistException("Material Type not found with id " + request.getMaterialType())); - material.setMaterialType(materialType); - material.setLastModifiedBy(loggedUserDetails.getUserId()); - material.setLastModifiedOn(new Date(System.currentTimeMillis())); - materialRepository.save(material); - return "Material updated successfully"; + Material material = materialRepository.findById(materialId) + .orElseThrow(() -> new ValueNotExistException("Material not found with id " + materialId)); + material.setMaterialName(request.getMaterialName()); + material.setDescription(request.getDescription()); + material.setIsAvailableForStudents(request.getIsAvailableForStudent()); + material.setReference(request.getReference()); + MaterialType materialType = materialTypeRepository.findById(request.getMaterialType()) + .orElseThrow(() -> new ValueNotExistException("Material Type not found with id " + request.getMaterialType())); + material.setMaterialType(materialType); + material.setLastModifiedBy(loggedUserDetails.getUserId()); + material.setLastModifiedOn(new Date(System.currentTimeMillis())); + materialRepository.save(material); + return "Material updated successfully"; } else { throw new ForbiddenException("You are not allowed to update material"); } - }catch (Exception e) { + } catch (Exception e) { throw new RuntimeException(e); } } @@ -152,11 +157,12 @@ public String deleteMaterial(UUID materialId) throws ApplicationException, Crede } else { throw new ForbiddenException("You are not allowed to delete material"); } - }catch (Exception e) { + } catch (Exception e) { throw new RuntimeException(e); } } + // Generate signed url for video material @Override public String getVideoMaterialSignedUrl(SignedUrlRequest signedUrlRequest) throws Exception { @@ -178,7 +184,6 @@ public String getVideoMaterialSignedUrl(SignedUrlRequest signedUrlRequest) throw return signedUrl.url(); - // long expirationTime = Instant.now().getEpochSecond() + (signedUrlRequest.getExpiresInHours() * 3600L); // // // Define policy with IP restriction and expiration time @@ -210,6 +215,66 @@ public String getVideoMaterialSignedUrl(SignedUrlRequest signedUrlRequest) throw } + @Override + public PreSignedUrlToUploadVideoResponseDto getPreSignedUrlToUploadVideo(VideoUploadRequest videoUploadRequest) throws ValueNotExistException, RuntimeException { + + + + try { + + Material material = new Material(); + + material.setMaterialName(videoUploadRequest.getOriginalFileName()); + + String fileExtension = getFileExtension(videoUploadRequest.getOriginalFileName()); + + material.setStatus("PENDING"); + material.setSignedUrlExpireTime(videoUploadRequest.getSignedUrlExpireTime()); + material.setSession(sessionRepository.findById(videoUploadRequest.getSessionId()) + .orElseThrow(() -> new ValueNotExistException("Session not found with id " + videoUploadRequest.getSessionId()))); + material.setMaterialType(materialTypeRepository.findByName("VIDEO")); + material.setIsAvailableForStudents(videoUploadRequest.getIsAvailableForStudents()); + material.setCreatedOn(new Date(System.currentTimeMillis())); + material.setCreatedBy(userDetailService.getLoggedUserDetails(UserUtil.extractToken()).getUserId()); + + Material materialInstance = materialRepository.save(material); + UUID uuid = materialInstance.getContentId(); + String s3Key = "videos/" + uuid.toString()+"."+fileExtension; + materialInstance.setReference(s3Key); + materialRepository.save(materialInstance); + + // Generate pre-signed URL + PutObjectRequest putObjectRequest = PutObjectRequest.builder() + .bucket(bucketName) + .key(s3Key) + .build(); + + PutObjectPresignRequest presignRequest = PutObjectPresignRequest.builder() + .putObjectRequest(putObjectRequest) + .signatureDuration(Duration.ofMinutes(60)) + .build(); + + return new PreSignedUrlToUploadVideoResponseDto(uuid, s3Presigner.presignPutObject(presignRequest).url().toString()); + + } catch (ValueNotExistException e) { + throw new ValueNotExistException("Session not found with id " + videoUploadRequest.getSessionId()); + } catch (Exception e) { + throw new RuntimeException(e); + } + + + } + + @Override + public String markedVideoAsUploaded(UUID uuid) throws ValueNotExistException { + Material material = materialRepository.findById(uuid) + .orElseThrow(() -> new ValueNotExistException("Material not found with id " + uuid)); + material.setStatus("UPLOADED"); + materialRepository.save(material); + return "Video marked as uploaded"; + } + + private String signPolicyWithPrivateKey(String base64EncodedPolicy) throws Exception { // Add the Bouncy Castle provider Security.addProvider(new BouncyCastleProvider()); @@ -278,7 +343,6 @@ private String urlEncode(String value) { // } - public String generateCustomSignedUrl(String resourcePath, Instant activeDate, Instant expirationDate, String ipAddress) throws Exception { // Load private key PrivateKey privateKey = loadPrivateKey(); @@ -313,4 +377,19 @@ private PrivateKey loadPrivateKey() throws Exception { private String urlSafeEncode(String value) { return value.replace("+", "-").replace("=", "_").replace("/", "~"); } + + @Scheduled(fixedRate = 86400000) // Run daily + public void cleanupOrphanedMaterials() { + Instant cutoffTime = Instant.now().minusSeconds(86400);// 24 hours + List materials = materialRepository.findAllByStatusAndCreatedAtBefore("PENDING", cutoffTime); + for (Material material : materials) { + + try { + materialRepository.delete(material); + System.out.println("Deleted orphaned materials: " + material.getMaterialName()); + } catch (Exception e) { + e.printStackTrace(); + } + } + } }