From 25b6dbcd7eedfae42db6cfc65bad84fc065e5e52 Mon Sep 17 00:00:00 2001 From: Nicolas Froger Date: Sat, 27 Jul 2024 02:47:56 +0200 Subject: [PATCH] store asset content type in db, add video support Signed-off-by: Nicolas Froger --- .../fr/kektus/summer2024/data/model/Asset.java | 1 + .../summer2024/domain/entity/AssetEntity.java | 1 + .../summer2024/domain/service/AssetService.java | 11 ++++++++--- .../summer2024/domain/service/PostService.java | 2 ++ .../presentation/rest/AdminAssetApi.java | 15 ++++++++------- .../presentation/rest/AdminAuthApi.java | 8 ++++---- .../summer2024/presentation/rest/LocationApi.java | 6 +++--- .../summer2024/presentation/rest/PostApi.java | 12 +++++------- .../db/migration/V2.0.0__AddAssetContentType.sql | 2 ++ .../src/components/PostComponent.vue | 11 +++++++++++ summer2024-frontend/src/stores/adminPosts.js | 2 +- summer2024-frontend/src/views/CreatePostView.vue | 3 +++ summer2024-frontend/src/views/EditPostView.vue | 8 +++++++- 13 files changed, 56 insertions(+), 26 deletions(-) create mode 100644 summer2024-backend/src/main/resources/db/migration/V2.0.0__AddAssetContentType.sql diff --git a/summer2024-backend/src/main/java/fr/kektus/summer2024/data/model/Asset.java b/summer2024-backend/src/main/java/fr/kektus/summer2024/data/model/Asset.java index a6baf2e..3fd8af7 100644 --- a/summer2024-backend/src/main/java/fr/kektus/summer2024/data/model/Asset.java +++ b/summer2024-backend/src/main/java/fr/kektus/summer2024/data/model/Asset.java @@ -18,5 +18,6 @@ import lombok.With; public class Asset { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) public Long id; public String filename; + public String contentType; @ManyToOne @JoinColumn(name = "post_id") public Post post; } diff --git a/summer2024-backend/src/main/java/fr/kektus/summer2024/domain/entity/AssetEntity.java b/summer2024-backend/src/main/java/fr/kektus/summer2024/domain/entity/AssetEntity.java index 43eca24..07a425c 100644 --- a/summer2024-backend/src/main/java/fr/kektus/summer2024/domain/entity/AssetEntity.java +++ b/summer2024-backend/src/main/java/fr/kektus/summer2024/domain/entity/AssetEntity.java @@ -7,5 +7,6 @@ import lombok.With; @NoArgsConstructor @AllArgsConstructor @With public class AssetEntity { public Long id; + public String contentType; public String presignedUrl; } diff --git a/summer2024-backend/src/main/java/fr/kektus/summer2024/domain/service/AssetService.java b/summer2024-backend/src/main/java/fr/kektus/summer2024/domain/service/AssetService.java index 933b97e..c3cea1d 100644 --- a/summer2024-backend/src/main/java/fr/kektus/summer2024/domain/service/AssetService.java +++ b/summer2024-backend/src/main/java/fr/kektus/summer2024/domain/service/AssetService.java @@ -12,6 +12,7 @@ import io.minio.http.Method; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import jakarta.transaction.Transactional; +import jakarta.ws.rs.BadRequestException; import jakarta.ws.rs.InternalServerErrorException; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.wildfly.common.Assert; @@ -32,15 +33,19 @@ public class AssetService { @ConfigProperty(name = "kektus.assets.bucket") String bucketName; @Transactional - public PresignedAsset createAsset(String filename) { + public PresignedAsset createAsset(String filename, String contentType) { + if (!contentType.startsWith("image/") && !contentType.startsWith("video/")) { + throw new BadRequestException("forbidden asset content type"); + } + String actualFilename = UUID.randomUUID() + "_" + filename; PostPolicy policy = new PostPolicy(bucketName, ZonedDateTime.now().plusMinutes(60)); policy.addEqualsCondition("key", actualFilename); - policy.addStartsWithCondition("Content-Type", ""); + policy.addEqualsCondition("Content-Type", contentType); policy.addContentLengthRangeCondition(64 * 1024, 200 * 1024 * 1024); try { Map formData = minioClient.getPresignedPostFormData(policy); - Asset asset = new Asset().withFilename(actualFilename); + Asset asset = new Asset().withFilename(actualFilename).withContentType(contentType); assetRepository.persist(asset); return new PresignedAsset().withId(asset.id).withFilename(actualFilename).withFormData(formData); } catch (MinioException | InvalidKeyException | IOException | NoSuchAlgorithmException e) { diff --git a/summer2024-backend/src/main/java/fr/kektus/summer2024/domain/service/PostService.java b/summer2024-backend/src/main/java/fr/kektus/summer2024/domain/service/PostService.java index d7cd0b7..4abc5fb 100644 --- a/summer2024-backend/src/main/java/fr/kektus/summer2024/domain/service/PostService.java +++ b/summer2024-backend/src/main/java/fr/kektus/summer2024/domain/service/PostService.java @@ -36,6 +36,7 @@ public class PostService { .withCountry(post.country) .withAssets(post.assets.stream().map(asset -> new AssetEntity() .withId(asset.id) + .withContentType(asset.contentType) .withPresignedUrl(assetService.getPresignedUrlForAsset(asset)) ).toList())).toList(); } @@ -86,6 +87,7 @@ public class PostService { .withCountry(post.country) .withAssets(post.assets.stream().map(asset -> new AssetEntity() .withId(asset.id) + .withContentType(asset.contentType) .withPresignedUrl(assetService.getPresignedUrlForAsset(asset)) ).toList()); } diff --git a/summer2024-backend/src/main/java/fr/kektus/summer2024/presentation/rest/AdminAssetApi.java b/summer2024-backend/src/main/java/fr/kektus/summer2024/presentation/rest/AdminAssetApi.java index 34f0a2b..b5ee299 100644 --- a/summer2024-backend/src/main/java/fr/kektus/summer2024/presentation/rest/AdminAssetApi.java +++ b/summer2024-backend/src/main/java/fr/kektus/summer2024/presentation/rest/AdminAssetApi.java @@ -25,29 +25,30 @@ public class AdminAssetApi { @Inject AuthService authService; @POST - public CreateAssets.Response createAsset(@RestHeader("X-admin-token") final String adminToken, - CreateAssets.Request request) { + public DTOs.CreateAssetResponse createAsset(@RestHeader("X-admin-token") final String adminToken, + DTOs.CreateAssetRequest request) { if (!authService.isAdminTokenValid(adminToken)) throw new NotAuthorizedException("provided admin token is invalid"); Assert.assertNotNull(request); Assert.assertNotNull(request.filename); - final var asset = assetService.createAsset(request.filename); - return new CreateAssets.Response() + final var asset = assetService.createAsset(request.filename, request.contentType); + return new DTOs.CreateAssetResponse() .withId(asset.id) .withFilename(asset.filename) .withFormData(asset.formData); } - public static class CreateAssets { + public static class DTOs { @NoArgsConstructor @AllArgsConstructor @With - public static class Request { + public static class CreateAssetRequest { public String filename; + public String contentType; } @NoArgsConstructor @AllArgsConstructor @With - public static class Response { + public static class CreateAssetResponse { public Long id; public String filename; public Map formData; diff --git a/summer2024-backend/src/main/java/fr/kektus/summer2024/presentation/rest/AdminAuthApi.java b/summer2024-backend/src/main/java/fr/kektus/summer2024/presentation/rest/AdminAuthApi.java index 25f5156..ce714bd 100644 --- a/summer2024-backend/src/main/java/fr/kektus/summer2024/presentation/rest/AdminAuthApi.java +++ b/summer2024-backend/src/main/java/fr/kektus/summer2024/presentation/rest/AdminAuthApi.java @@ -18,16 +18,16 @@ public class AdminAuthApi { @Inject AuthService authService; @GET @Path("/check") - public CheckAuth.Response checkAuth(@RestHeader("X-admin-token") final String adminToken) { + public DTOs.CheckAuthResponse checkAuth(@RestHeader("X-admin-token") final String adminToken) { if (!authService.isAdminTokenValid(adminToken)) throw new NotAuthorizedException("provided admin token is invalid"); - return new CheckAuth.Response().withStatus("ok"); + return new DTOs.CheckAuthResponse().withStatus("ok"); } - public static class CheckAuth { + public static class DTOs { @AllArgsConstructor @NoArgsConstructor @With - public static class Response { + public static class CheckAuthResponse { public String status; } } diff --git a/summer2024-backend/src/main/java/fr/kektus/summer2024/presentation/rest/LocationApi.java b/summer2024-backend/src/main/java/fr/kektus/summer2024/presentation/rest/LocationApi.java index 0a91f67..e4e9153 100644 --- a/summer2024-backend/src/main/java/fr/kektus/summer2024/presentation/rest/LocationApi.java +++ b/summer2024-backend/src/main/java/fr/kektus/summer2024/presentation/rest/LocationApi.java @@ -30,7 +30,7 @@ public class LocationApi { @POST public LocationEntity createPost(@RestHeader("X-admin-token") final String adminToken, - CreateLocation.Request request) { + DTOs.CreateLocationRequest request) { if (!authService.isAdminTokenValid(adminToken)) throw new NotAuthorizedException("provided admin token is invalid"); @@ -41,9 +41,9 @@ public class LocationApi { return locationService.createLocation(request.latitude, request.longitude); } - public static class CreateLocation { + public static class DTOs { @NoArgsConstructor @AllArgsConstructor @With - public static class Request { + public static class CreateLocationRequest { public Float latitude; public Float longitude; } diff --git a/summer2024-backend/src/main/java/fr/kektus/summer2024/presentation/rest/PostApi.java b/summer2024-backend/src/main/java/fr/kektus/summer2024/presentation/rest/PostApi.java index 11be66c..10388cc 100644 --- a/summer2024-backend/src/main/java/fr/kektus/summer2024/presentation/rest/PostApi.java +++ b/summer2024-backend/src/main/java/fr/kektus/summer2024/presentation/rest/PostApi.java @@ -35,7 +35,7 @@ public class PostApi { } @POST - public PostEntity createPost(@RestHeader("X-admin-token") final String adminToken, CreatePost.Request request) { + public PostEntity createPost(@RestHeader("X-admin-token") final String adminToken, DTOs.CreatePostRequest request) { if (!authService.isAdminTokenValid(adminToken)) throw new NotAuthorizedException("provided admin token is invalid"); @@ -74,7 +74,7 @@ public class PostApi { @Path("/{id}") public PostEntity updatePost(@RestHeader("X-admin-token") final String adminToken, @PathParam("id") final Long id, - final UpdatePost.Request request) { + final DTOs.UpdatePostRequest request) { if (!authService.isAdminTokenValid(adminToken)) throw new NotAuthorizedException("provided admin token is invalid"); @@ -89,9 +89,9 @@ public class PostApi { ); } - public static class CreatePost { + public static class DTOs { @NoArgsConstructor @AllArgsConstructor @With - public static class Request { + public static class CreatePostRequest { public String description; public Float latitude; public Float longitude; @@ -99,11 +99,9 @@ public class PostApi { public String country; public List assets; } - } - public static class UpdatePost { @NoArgsConstructor @AllArgsConstructor @With - public static class Request { + public static class UpdatePostRequest { public String description; public Float latitude; public Float longitude; diff --git a/summer2024-backend/src/main/resources/db/migration/V2.0.0__AddAssetContentType.sql b/summer2024-backend/src/main/resources/db/migration/V2.0.0__AddAssetContentType.sql new file mode 100644 index 0000000..949f5fa --- /dev/null +++ b/summer2024-backend/src/main/resources/db/migration/V2.0.0__AddAssetContentType.sql @@ -0,0 +1,2 @@ +alter table if exists Asset + add column contentType varchar(255); \ No newline at end of file diff --git a/summer2024-frontend/src/components/PostComponent.vue b/summer2024-frontend/src/components/PostComponent.vue index 341603c..7c50414 100644 --- a/summer2024-frontend/src/components/PostComponent.vue +++ b/summer2024-frontend/src/components/PostComponent.vue @@ -62,7 +62,18 @@ function postDataToggle() { +
diff --git a/summer2024-frontend/src/stores/adminPosts.js b/summer2024-frontend/src/stores/adminPosts.js index 62007e0..7f0f54f 100644 --- a/summer2024-frontend/src/stores/adminPosts.js +++ b/summer2024-frontend/src/stores/adminPosts.js @@ -28,7 +28,7 @@ export const useAdminPostsStore = defineStore('adminPosts', () => { 'Content-Type': 'application/json', 'X-admin-token': authStore.adminToken }, - body: JSON.stringify({ filename: file.file.name }) + body: JSON.stringify({ filename: file.file.name, contentType: file.file.type }) }) .catch((e) => { console.log('Contact API asset failed: ' + e) diff --git a/summer2024-frontend/src/views/CreatePostView.vue b/summer2024-frontend/src/views/CreatePostView.vue index 794f8a9..c8a0571 100644 --- a/summer2024-frontend/src/views/CreatePostView.vue +++ b/summer2024-frontend/src/views/CreatePostView.vue @@ -215,6 +215,9 @@ function updateCityAndCountry() { +
diff --git a/summer2024-frontend/src/views/EditPostView.vue b/summer2024-frontend/src/views/EditPostView.vue index e4e6f90..4b92986 100644 --- a/summer2024-frontend/src/views/EditPostView.vue +++ b/summer2024-frontend/src/views/EditPostView.vue @@ -225,7 +225,10 @@ function removeAsset(id) {
- + +
@@ -244,6 +247,9 @@ function removeAsset(id) { +