package net.gepafin.tendermanagement.service.impl; import com.amazonaws.AmazonServiceException; import com.amazonaws.SdkClientException; 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.S3PathConfig; import net.gepafin.tendermanagement.entities.ApplicationSignedDocumentEntity; import net.gepafin.tendermanagement.entities.UserCompanyDelegationEntity; import net.gepafin.tendermanagement.enums.DocOtherSourceTypeEnum; import net.gepafin.tendermanagement.repositories.ApplicationRepository; import net.gepafin.tendermanagement.repositories.ApplicationSignedDocumentRepository; import net.gepafin.tendermanagement.repositories.S3ConfigRepository; import net.gepafin.tendermanagement.repositories.UserCompanyDelegationRepository; 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.ArrayList; import java.util.List; @Slf4j @Service public class UserSignedAndDelegationServiceImpl { private static final String OLD_BUCKET = "mementoresources"; private static final String NEW_BUCKET = "mementoresources"; private static final String SECURE_KEY = "267163962963"; @Autowired private UserCompanyDelegationRepository userCompanyDelegationRepository; @Autowired private AmazonS3Client s3Client; @Autowired private S3PathConfig s3ConfigBean; @Autowired private ApplicationSignedDocumentRepository applicationSignedDocumentRepository; @Autowired ApplicationRepository applicationRepository; @Autowired S3ConfigRepository s3ConfigRepository; @Value("${aws.s3.url}") private String s3Url; private boolean migrationCompleted = false; public String migrateUserDelegatedDocuments(String providedKey) { if (migrationCompleted) { return "Migration already completed."; } // Validate the provided key if (isValidKey(providedKey)) { return "Invalid or missing migration key."; } List documents = userCompanyDelegationRepository.findAllByStatus("ACTIVE"); if (documents.isEmpty()) { return "No documents found to migrate."; } for (UserCompanyDelegationEntity document : documents) { String oldUrl = document.getFilePath(); log.info("Processing user designated document: {}", oldUrl); try { File localFile = downloadFileFromS3(oldUrl); String newKey = generateNewS3PathForDelegationDoc(); String uploadedPath = uploadFileToNewBucket(localFile, newKey); updateDelegatedDocumentPathAndNameEntry(document, uploadedPath); } catch (Exception e) { log.error("Error processing user designated document {}: {}", document.getId(), e.getMessage()); } } return "Migrated"; } public String migrateUserSignedDocuments(String providedKey) { if (migrationCompleted) { return "Migration already completed."; } // Validate the provided key if (isValidKey(providedKey)) { return "Invalid or missing migration key."; } List documents = applicationSignedDocumentRepository.findAllByIsStatus("ACTIVE"); if (documents.isEmpty()) { return "No documents found to migrate."; } for (ApplicationSignedDocumentEntity document : documents) { String oldUrl = document.getFilePath(); log.info("Processing user signed document: {}", oldUrl); try { File localFile = downloadFileFromS3(oldUrl); String newKey = generateNewS3PathForUserSignedDoc(document); String uploadedPath = uploadFileToNewBucket(localFile, newKey); updateDocumentPathAndNameEntry(document, uploadedPath); } catch (Exception e) { log.error("Error processing user signed document {}: {}", document.getId(), e.getMessage()); } } return "Migrated."; } private boolean isValidKey(String providedKey) { return providedKey == null || !providedKey.equals(SECURE_KEY); } private String generateNewS3PathForDelegationDoc() { return s3ConfigBean.generateDocumentPathForOther(DocOtherSourceTypeEnum.USER_DELEGATION, 0L, 0L,0L); } private String generateNewS3PathForUserSignedDoc(ApplicationSignedDocumentEntity document) { // Fetch the list of application IDs associated with the document List applicationIds = applicationSignedDocumentRepository.findApplicationIdIdsById(document.getId()); List paths = new ArrayList<>(); // Loop through the application IDs and generate paths for (Long applicationId : applicationIds) { Long callId = applicationRepository.findCallIdById(applicationId); // Construct the path for the current application and call ID String newPath = String.format("%s/call/call_%d/application/application_%d/user_signed_document", s3ConfigRepository.getPathByTypeOther( String.valueOf(DocOtherSourceTypeEnum.USER_SIGNED_DOCUMENT)) , callId, applicationId); log.info("Generated new S3 path: {}", newPath); paths.add(newPath); } return String.join(",", paths); } private File downloadFileFromS3(String fileUrl) throws Exception { String key = extractS3KeyFromUrl(fileUrl); File localFile = new File("/tmp/" + extractFileName(key)); GetObjectRequest getObjectRequest = new GetObjectRequest(OLD_BUCKET, 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) { return url.replace("https://mementoresources.s3.eu-west-1.amazonaws.com/", ""); } private String uploadFileToNewBucket(File localFile, String s3Path) { InputStream inputStream = null; try { String fileName = extractFileName(localFile.getAbsolutePath()); // Extract file name String fullPath = String.format("%s/%s", s3Path, fileName); // Construct full path inputStream = new FileInputStream(localFile); // Create InputStream // Set metadata for the file ObjectMetadata objectMetadata = new ObjectMetadata(); objectMetadata.setContentLength(localFile.length()); objectMetadata.setContentType("application/octet-stream"); // Upload the file to S3 with the constructed path s3Client.putObject(NEW_BUCKET, fullPath, inputStream, objectMetadata); // Construct the full S3 URL for the uploaded file String fullUrl = String.format("https://%s.s3.%s.amazonaws.com/%s", NEW_BUCKET, "eu-west-1", fullPath); 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: " + localFile, e); } catch (AmazonServiceException e) { log.error("Amazon service exception while uploading file '{}': {}", localFile.getName(), e.getMessage()); throw new RuntimeException("Upload failed for: " + localFile, e); } catch (SdkClientException e) { log.error("SDK client exception while uploading file '{}': {}", localFile.getName(), e.getMessage()); throw new RuntimeException("Upload failed for: " + localFile, e); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { log.warn("Failed to close InputStream for file '{}': {}", localFile.getName(), e.getMessage()); } } } } private String extractFileName(String filePath) { String[] parts = filePath.split("/"); return parts[parts.length - 1]; } private String extractFileNameFromPath(String path) { return path.substring(path.lastIndexOf('/') + 1); } private void updateDocumentPathAndNameEntry(ApplicationSignedDocumentEntity document, String newPath) { String fileName = extractFileNameFromPath(newPath); document.setFilePath(newPath); document.setFileName(fileName); applicationSignedDocumentRepository.save(document); log.info("Migrated document ID: {} to new path: {}", document.getId(), newPath); } private void updateDelegatedDocumentPathAndNameEntry(UserCompanyDelegationEntity document, String newPath) { String fileName = extractFileNameFromPath(newPath); document.setFilePath(newPath); document.setFileName(fileName); userCompanyDelegationRepository.save(document); log.info("Migrated document ID: {} to new path: {}", document.getId(), newPath); } }