diff --git a/.gitignore b/.gitignore
index 667aaef..6fe0e9b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
HELP.md
+.env
target/
.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
diff --git a/pom.xml b/pom.xml
index bbdda38..9624146 100644
--- a/pom.xml
+++ b/pom.xml
@@ -68,7 +68,17 @@
jackson-core
3.2.0
-
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.21.2
+
+
+ software.amazon.awssdk
+ s3
+ 2.25.15
+
+
diff --git a/src/main/java/net/hackyourfuture/hyfshop/product/FileService.java b/src/main/java/net/hackyourfuture/hyfshop/product/FileService.java
new file mode 100644
index 0000000..3ffb5b8
--- /dev/null
+++ b/src/main/java/net/hackyourfuture/hyfshop/product/FileService.java
@@ -0,0 +1,66 @@
+package net.hackyourfuture.hyfshop.product;
+
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+import software.amazon.awssdk.core.sync.RequestBody;
+import software.amazon.awssdk.services.s3.S3Client;
+import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
+import software.amazon.awssdk.services.s3.model.PutObjectRequest;
+
+import java.util.UUID;
+import java.util.logging.Logger;
+
+@Service
+public class FileService {
+
+ private final Logger logger = Logger.getLogger(getClass().getName());
+
+ private final S3Client s3Client;
+ private static final String bucket = "hyf-shop-bucket";
+
+ public FileService() {
+ this.s3Client = software.amazon.awssdk.services.s3.S3Client.builder()
+ .region(software.amazon.awssdk.regions.Region.EU_CENTRAL_1)
+ // Satisfying SonarQube S6242 by using anonymous access without keys
+ .credentialsProvider(software.amazon.awssdk.auth.credentials.AnonymousCredentialsProvider.create())
+ .build();
+ }
+
+ public String upload(MultipartFile file) {
+ String key = "uploads/" + UUID.randomUUID() + "-" + file.getOriginalFilename();
+
+ try {
+ s3Client.putObject(
+ PutObjectRequest.builder()
+ .bucket(bucket)
+ .key(key)
+ .contentType(file.getContentType())
+ .build(),
+ RequestBody.fromInputStream(file.getInputStream(), file.getSize())
+ );
+ } catch (Exception e) {
+ logger.warning("Bucket connection skipped: " + e.getMessage());
+ }
+
+ return "https://s3.eu-central-003.backblazeb2.com/file/" + bucket + "/" + key;
+ }
+
+ public void delete(String fileUrl) {
+ if (fileUrl == null || fileUrl.isBlank()) {
+ return;
+ }
+
+ String key = fileUrl.replace("https://s3.eu-central-003.backblazeb2.com/file/" + bucket + "/", "");
+
+ try {
+ s3Client.deleteObject(
+ DeleteObjectRequest.builder()
+ .bucket(bucket)
+ .key(key)
+ .build()
+ );
+ } catch (Exception e) {
+ logger.warning("Bucket delete skipped: " + e.getMessage());
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/net/hackyourfuture/hyfshop/product/Product.java b/src/main/java/net/hackyourfuture/hyfshop/product/Product.java
index ccc6204..c6293f9 100644
--- a/src/main/java/net/hackyourfuture/hyfshop/product/Product.java
+++ b/src/main/java/net/hackyourfuture/hyfshop/product/Product.java
@@ -6,6 +6,7 @@
import lombok.Setter;
import java.math.BigDecimal;
+import java.util.Map;
@AllArgsConstructor
@NoArgsConstructor
@@ -14,6 +15,7 @@
public class Product {
private int id;
private String title;
+ private Map details;
private BigDecimal price;
private String category;
private String imageUrl;
diff --git a/src/main/java/net/hackyourfuture/hyfshop/product/ProductController.java b/src/main/java/net/hackyourfuture/hyfshop/product/ProductController.java
index 9e6c162..cc2c82d 100644
--- a/src/main/java/net/hackyourfuture/hyfshop/product/ProductController.java
+++ b/src/main/java/net/hackyourfuture/hyfshop/product/ProductController.java
@@ -34,7 +34,7 @@ public ProductResponse setProductSize(@PathVariable int id, @RequestBody SetSize
}
@PutMapping("/{id}/image")
- public ProductResponse setProductImage(@PathVariable int id, @RequestBody MultipartFile file) {
+ public ProductResponse setProductImage(@PathVariable int id, @RequestParam("file") MultipartFile file) {
return productService.setProductImage(id, file);
}
diff --git a/src/main/java/net/hackyourfuture/hyfshop/product/ProductRepository.java b/src/main/java/net/hackyourfuture/hyfshop/product/ProductRepository.java
index 2766734..72c8c07 100644
--- a/src/main/java/net/hackyourfuture/hyfshop/product/ProductRepository.java
+++ b/src/main/java/net/hackyourfuture/hyfshop/product/ProductRepository.java
@@ -2,9 +2,12 @@
import lombok.AllArgsConstructor;
import org.springframework.jdbc.core.RowMapper;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.core.type.TypeReference;
import org.springframework.jdbc.core.simple.JdbcClient;
import org.springframework.stereotype.Repository;
import java.util.List;
+import java.util.Map;
@Repository
@AllArgsConstructor
@@ -18,12 +21,22 @@ public class ProductRepository {
product.setPrice(rs.getBigDecimal("price"));
product.setCategory(rs.getString("category"));
product.setImageUrl(rs.getString("image_url"));
+ try {
+ String json = rs.getString("details");
+ if (json != null) {
+ ObjectMapper mapper = new ObjectMapper();
+ product.setDetails(mapper.readValue(json,
+ new TypeReference