package net.gepafin.tendermanagement.service.impl; import com.amazonaws.AmazonServiceException; import com.amazonaws.SdkClientException; import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.AmazonS3Client; import com.amazonaws.services.s3.model.GetObjectRequest; import com.amazonaws.services.s3.model.ObjectMetadata; import lombok.extern.slf4j.Slf4j; import net.gepafin.tendermanagement.dao.DocumentDao; import net.gepafin.tendermanagement.dao.S3PathConfig; import net.gepafin.tendermanagement.entities.ApplicationEntity; import net.gepafin.tendermanagement.entities.DocumentEntity; import net.gepafin.tendermanagement.enums.DocumentSourceTypeEnum; import net.gepafin.tendermanagement.repositories.ApplicationAmendmentRequestRepository; import net.gepafin.tendermanagement.repositories.ApplicationRepository; import net.gepafin.tendermanagement.repositories.ApplicationSignedDocumentRepository; import net.gepafin.tendermanagement.repositories.DocumentRepository; import net.gepafin.tendermanagement.service.AmazonS3Service; import net.gepafin.tendermanagement.service.ApplicationService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.List; @Slf4j @Service public class S3ReUploadMigrationService { private static final String OLD_BUCKET = "mementoresources"; private static final String SECURE_KEY = "267163962963"; @Autowired private DocumentRepository documentRepository; @Autowired private AmazonS3Client s3Client; @Autowired private S3PathConfig s3ConfigBean; @Autowired private ApplicationRepository applicationRepository; @Autowired private ApplicationSignedDocumentRepository applicationSignedDocumentRepository; @Autowired private AmazonS3 amazonS3; @Autowired ApplicationService applicationService; @Value("${aws.s3.url}") private String s3Url; @Autowired AmazonS3Service amazonS3Service; @Value("${aws.s3.bucket.name}") private String bucketName; @Value("${aws.s3.region}") private String region; @Autowired private ApplicationAmendmentRequestRepository applicationAmendmentRequestRepository; @Autowired private DocumentDao documentDao; private boolean migrationCompleted = false; public String reUploadAndMigrateDocuments(String providedKey) { Long totalDocuments=0L; Long failedDocuments=0L; Long processDocuments=0L; if (migrationCompleted) { return "Migration already completed."; } // Validate the provided key if (!isValidKey(providedKey)) { return "Invalid or missing migration key."; } List documents = documentRepository.findAllByIsDeleteTrue(); totalDocuments = Long.valueOf(documents.size()); if (documents.isEmpty()) { return "No documents found to migrate."; } for (DocumentEntity document : documents) { log.info("Processing for the Document id and url:{} ",document.getId(),document.getFilePath()); try { Long callId = null; Long applicationId = null; Long amendmentId = null; if (DocumentSourceTypeEnum.CALL.getValue().equalsIgnoreCase(document.getSource())) { callId = document.getSourceId(); } else if (DocumentSourceTypeEnum.APPLICATION.getValue().equalsIgnoreCase(document.getSource())) { applicationId = document.getSourceId(); ApplicationEntity applicationEntity = applicationService.validateApplication(applicationId); callId = applicationEntity.getCall().getId(); } else if(DocumentSourceTypeEnum.AMENDMENT.getValue().equalsIgnoreCase(document.getSource())){ amendmentId = document.getSourceId(); ApplicationEntity applicationEntity = applicationAmendmentRequestRepository.findApplicationByAmendmentId(amendmentId); applicationId = applicationEntity.getId(); callId = applicationEntity.getCall().getId(); } documentDao.deleteFileFromS3(document,callId,applicationId,amendmentId); processDocuments++; } catch (Exception e) { log.error("Error processing document {}: {}", document.getId(), e.getMessage()); failedDocuments++; } } log.info("Total Documents Fetched ",totalDocuments); log.info("Total Process Documents :{}",processDocuments); log.info("Total Failed Documents :{}",failedDocuments); return "Migrated Successfully."; } private boolean isValidKey(String providedKey) { return providedKey != null && providedKey.equals(SECURE_KEY); } private File downloadFileFromS3(String fileUrl) throws Exception { String key = extractS3KeyFromUrl(fileUrl); // Get the S3 key from the URL File localFile = new File("/tmp/" + extractFileName(key)); // Save file locally GetObjectRequest getObjectRequest = new GetObjectRequest(OLD_BUCKET, key); // Use the key try (InputStream s3Stream = s3Client.getObject(getObjectRequest).getObjectContent(); FileOutputStream outputStream = new FileOutputStream(localFile)) { s3Stream.transferTo(outputStream); } log.info("Downloaded file from old S3 bucket: {}", key); return localFile; } private String extractS3KeyFromUrl(String url) { // Assuming the URL structure is consistent return url.replace("https://mementoresources.s3.eu-west-1.amazonaws.com/", ""); } private String uploadFileToNewBucket(File localFile, String s3Folder) { InputStream inputStream = null; // Declare the InputStream here for cleanup try { // Extract file name from the local file String fileName = extractFileName(localFile.getAbsolutePath()); // Get the file name String path = s3Folder + "/" + fileName; // Construct the S3 path // Create InputStream from the local file inputStream = new FileInputStream(localFile); // Set up object metadata ObjectMetadata objectMetadata = new ObjectMetadata(); objectMetadata.setContentType("application/octet-stream"); objectMetadata.setContentLength(localFile.length()); // Upload to S3 s3Client.putObject(OLD_BUCKET, path, inputStream, objectMetadata); // Construct the full S3 URL String fullUrl = String.format("https://%s.s3.%s.amazonaws.com/%s", OLD_BUCKET, "eu-west-1", path); log.info("File '{}' uploaded successfully to Amazon S3 with URL: {}", fileName, fullUrl); return fullUrl; } catch (IOException e) { log.error("IOException occurred during file upload for '{}': {}", localFile.getName(), e.getMessage()); throw new RuntimeException("Upload failed for: " + s3Folder + "/" + localFile.getName(), e); } catch (AmazonServiceException e) { log.error("Amazon service exception while uploading file '{}': {}", localFile.getName(), e.getMessage()); throw new RuntimeException("Upload failed for: " + s3Folder + "/" + localFile.getName(), e); } catch (SdkClientException e) { log.error("SDK client exception while uploading file '{}': {}", localFile.getName(), e.getMessage()); throw new RuntimeException("Upload failed for: " + s3Folder + "/" + localFile.getName(), e); } finally { // Close InputStream if it was opened if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { log.warn("Failed to close InputStream for file '{}': {}", localFile.getName(), e.getMessage()); } } } } private String generateNewS3Path(DocumentEntity document) { DocumentSourceTypeEnum sourceType = DocumentSourceTypeEnum.valueOf(document.getSource()); Long callId; if (sourceType.equals(DocumentSourceTypeEnum.CALL)) { return s3ConfigBean.generateDocumentPath(sourceType, document.getSourceId(), 0L,0L); } else { callId = applicationRepository.findCallIdById(document.getSourceId()); return s3ConfigBean.generateDocumentPath(sourceType, callId, document.getSourceId(),0L); } } private String extractFileName(String filePath) { String[] parts = filePath.split("/"); return parts[parts.length - 1]; } }