diff --git a/.env b/.env index 22a3f3d..a10a5ae 100644 --- a/.env +++ b/.env @@ -1,27 +1,30 @@ -# OAuth2 Authentication Configuration -# Replace with your actual OAuth provider credentials - -# Google OAuth2 -GOOGLE_CLIENT_ID=760575932383-1ah7nvt9vi02pr5r20h5nm5g8p1pl87j.apps.googleusercontent.com -GOOGLE_CLIENT_SECRET=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 -REDIRECT_URL=http://localhost/api/login/oauth2/code -# Facebook OAuth2 -FACEBOOK_CLIENT_ID=your-facebook-app-id-here -FACEBOOK_CLIENT_SECRET=your-facebook-app-secret-here -# -# MONGO_URI=mongodb://root:password123@mongodb-primary:27017,mongodb-secondary:27017/StoneInscription?replicaSet=replicaset&authSource=admin -MONGO_URI=mongodb://localhost:27017/StoneInscription - -# Apple OAuth2 -APPLE_CLIENT_ID=your-apple-service-id-here -APPLE_CLIENT_SECRET=your-apple-client-secret-jwt-here - -CONTENT_MODERATION_WEBHOOK_URL=https://inscriptions.cdacb.in/n8n/webhook/content-moderation -CONTENT_MODERATION_SAFE_THRESHOLD=0.6 -CONTENT_MODERATION_CONNECT_TIMEOUT_MS=5000 -CONTENT_MODERATION_READ_TIMEOUT_MS=10000 - -# CONTENT_MODERATION_CONNECT_TIMEOUT_MS=300000 -# CONTENT_MODERATION_READ_TIMEOUT_MS=300000 - -# CONTENT_MODERATION_INSECURE_SSL=true +# OAuth2 Authentication Configuration +# Replace with your actual OAuth provider credentials + +# Google OAuth2 +GOOGLE_CLIENT_ID=760575932383-1ah7nvt9vi02pr5r20h5nm5g8p1pl87j.apps.googleusercontent.com +GOOGLE_CLIENT_SECRET=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 +REDIRECT_URL=http://localhost/api/login/oauth2/code +# Facebook OAuth2 +FACEBOOK_CLIENT_ID=your-facebook-app-id-here +FACEBOOK_CLIENT_SECRET=your-facebook-app-secret-here +# +# MONGO_URI=mongodb://root:password123@mongodb-primary:27017,mongodb-secondary:27017/StoneInscription?replicaSet=replicaset&authSource=admin +MONGO_URI=mongodb://localhost:27017/StoneInscription + +# Apple OAuth2 +APPLE_CLIENT_ID=your-apple-service-id-here +APPLE_CLIENT_SECRET=your-apple-client-secret-jwt-here + + +# CONTENT_MODERATION_CONNECT_TIMEOUT_MS=300000 +# CONTENT_MODERATION_READ_TIMEOUT_MS=300000 + + +CONTENT_MODERATION_WEBHOOK_URL=https://inscriptions.cdacb.in/n8n/webhook/content-moderation +# CONTENT_MODERATION_INSECURE_SSL=true + +CONTENT_MODERATION_SAFE_THRESHOLD=0.7 +CONTENT_MODERATION_CONNECT_TIMEOUT_MS=5000 +CONTENT_MODERATION_READ_TIMEOUT_MS=10000 +CONTENT_MODERATION_INSECURE_SSL=false diff --git a/src/main/java/com/cadac/stone_inscription/auth/JwtRequestFilter.java b/src/main/java/com/cadac/stone_inscription/auth/JwtRequestFilter.java index 8cc4bf7..7864819 100644 --- a/src/main/java/com/cadac/stone_inscription/auth/JwtRequestFilter.java +++ b/src/main/java/com/cadac/stone_inscription/auth/JwtRequestFilter.java @@ -87,7 +87,7 @@ public String extractJwtFromRequest(HttpServletRequest request) { } else { throw new StoneInscriptionException("Invalid Token Request Bearer not found ", HttpStatus.BAD_REQUEST); - + // return null; } } diff --git a/src/main/java/com/cadac/stone_inscription/entity/InscriptionPost.java b/src/main/java/com/cadac/stone_inscription/entity/InscriptionPost.java index af95540..9cd70dd 100644 --- a/src/main/java/com/cadac/stone_inscription/entity/InscriptionPost.java +++ b/src/main/java/com/cadac/stone_inscription/entity/InscriptionPost.java @@ -1,258 +1,263 @@ -package com.cadac.stone_inscription.entity; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; - -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; -import lombok.*; - -import org.bson.types.ObjectId; -import org.springframework.data.annotation.*; -import org.springframework.data.mongodb.core.index.*; -import org.springframework.data.mongodb.core.mapping.Document; -import org.springframework.data.mongodb.core.mapping.Field; - -import java.util.Date; -import java.util.LinkedList; -import java.util.List; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -@Document(collection = "inscriptionposts") -@CompoundIndexes({ - @CompoundIndex(name = "user_topic_idx", def = "{'user_id': 1, 'topic': 1}"), - @CompoundIndex(name = "created_at_idx", def = "{'createdAt': -1}") -}) - -public class InscriptionPost { - - @Id - @JsonProperty("_id") - @JsonSerialize(using = ToStringSerializer.class) - private ObjectId id; - - @Field("user_id") - @JsonProperty("user_id") - @NotBlank - @Indexed - @JsonSerialize(using = ToStringSerializer.class) - private ObjectId userId; - - @Transient - @JsonProperty("username") - private String userName; - - @CreatedDate - @Field("createdAt") - @JsonProperty("createdAt") - @Indexed - private Date createdAt; - - @LastModifiedDate - @Field("updatedAt") - @JsonProperty("updatedAt") - private Date updatedAt; - - @Field("images") - @JsonProperty("images") - private Images images; - - @Field("description") - @JsonProperty("description") - private Description description; - - @Field("topic") - @JsonProperty("topic") - @Indexed - private String topic; - - @Field("script") - @JsonProperty("script") - private List script; - - @Field("type") - @JsonProperty("type") - private String type; - - @Field("visiblity") - @JsonProperty("visiblity") - @Builder.Default - private Boolean visiblity = true; - - @Field("distance") - @JsonProperty("distance") - private Double distance; - - @Field("rating") - @JsonProperty("rating") - @Builder.Default - private Double rating = 0.0; - - @Field("userrating") - @JsonProperty("userrating") - @Builder.Default - private List userRating = new LinkedList<>(); - - // ---------- Nested Classes ---------- - - @Data - @Builder - @NoArgsConstructor - @AllArgsConstructor - public static class Images { - @Field("thumbnailImage") - @JsonProperty("thumbnailImage") - private String thumbnailImage; - - @Field("image") - @JsonProperty("image") - private List image; - - } - - @Data - @Builder - @NoArgsConstructor - @AllArgsConstructor - public static class Description { - @Field("title") - @JsonProperty("title") - private String title; - - @Field("subject") - @JsonProperty("subject") - private String subject; - - @Field("description") - @JsonProperty("description") - private String description; - - @Field("scriptLanguage") - @JsonProperty("scriptLanguage") - private List scriptLanguage; - - @Field("language") - @JsonProperty("language") - private List language; - - @Field("englishTranslation") - @JsonProperty("englishTranslation") - private String englishTranslation; - - @Field("upvote") - @JsonProperty("upvote") - @Builder.Default - private Integer upvote = 0; - - @Field("geolocation") - @JsonProperty("geolocation") - private GeoLocation geolocation; - - @CreatedDate - @Field("createdAt") - @JsonProperty("createdAt") - private Date createdAt; - - @LastModifiedDate - @Field("updatedAt") - @JsonProperty("updatedAt") - private Date updatedAt; - } - - @Data - @Builder - @NoArgsConstructor - @AllArgsConstructor - public static class GeoLocation { - @Field("lon") - @JsonProperty("lon") - @NotNull - @NotEmpty - private String lon; - - @Field("lat") - @JsonProperty("lat") - @NotNull - @NotEmpty - private String lat; - - private String amenity; - private String road; - private String neighbourhood; - private String suburb; - - @JsonProperty("city_district") - private String cityDistrict; - - private String city; - private String county; - - @JsonProperty("state_district") - private String stateDistrict; - - private String state; - - @JsonProperty("ISO3166-2-lvl4") - private String iso3166Lvl4; - - private String postcode; - private String country; - - @JsonProperty("country_code") - private String countryCode; - - @JsonProperty("place_id") - private Long placeId; - - private String licence; - - @JsonProperty("osm_type") - private String osmType; - - @JsonProperty("osm_id") - private Long osmId; - - @JsonProperty("class") - private String clazz; // "class" is reserved in Java, so renamed to clazz - - private String type; - - @JsonProperty("place_rank") - private Integer placeRank; - - private Double importance; - - @JsonProperty("addresstype") - private String addressType; - - private String name; - - @JsonProperty("display_name") - private String displayName; - - private List boundingbox; - - @GeoSpatialIndexed - private List coordinates; // [lon, lat] for geo queries - } - - @Data - @Builder - @NoArgsConstructor - @AllArgsConstructor - public static class UsersRating { - - @Field("userId") - @JsonProperty("userId") - private String userId; - - @Field("rating") - @JsonProperty("rating") - private double rating; - } -} +package com.cadac.stone_inscription.entity; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import com.cadac.stone_inscription.moderation.model.ContentModeration; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.*; + +import org.bson.types.ObjectId; +import org.springframework.data.annotation.*; +import org.springframework.data.mongodb.core.index.*; +import org.springframework.data.mongodb.core.mapping.Document; +import org.springframework.data.mongodb.core.mapping.Field; + +import java.util.Date; +import java.util.LinkedList; +import java.util.List; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Document(collection = "inscriptionposts") +@CompoundIndexes({ + @CompoundIndex(name = "user_topic_idx", def = "{'user_id': 1, 'topic': 1}"), + @CompoundIndex(name = "created_at_idx", def = "{'createdAt': -1}") +}) + +public class InscriptionPost { + + @Id + @JsonProperty("_id") + @JsonSerialize(using = ToStringSerializer.class) + private ObjectId id; + + @Field("user_id") + @JsonProperty("user_id") + @NotBlank + @Indexed + @JsonSerialize(using = ToStringSerializer.class) + private ObjectId userId; + + @Transient + @JsonProperty("username") + private String userName; + + @CreatedDate + @Field("createdAt") + @JsonProperty("createdAt") + @Indexed + private Date createdAt; + + @LastModifiedDate + @Field("updatedAt") + @JsonProperty("updatedAt") + private Date updatedAt; + + @Field("images") + @JsonProperty("images") + private Images images; + + @Field("description") + @JsonProperty("description") + private Description description; + + @Field("topic") + @JsonProperty("topic") + @Indexed + private String topic; + + @Field("script") + @JsonProperty("script") + private List script; + + @Field("type") + @JsonProperty("type") + private String type; + + @Field("visiblity") + @JsonProperty("visiblity") + @Builder.Default + private Boolean visiblity = true; + + @Field("distance") + @JsonProperty("distance") + private Double distance; + + @Field("rating") + @JsonProperty("rating") + @Builder.Default + private Double rating = 0.0; + + @Field("userrating") + @JsonProperty("userrating") + @Builder.Default + private List userRating = new LinkedList<>(); + + // ---------- Nested Classes ---------- + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class Images { + @Field("thumbnailImage") + @JsonProperty("thumbnailImage") + private String thumbnailImage; + + @Field("image") + @JsonProperty("image") + private List image; + + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class Description { + @Field("title") + @JsonProperty("title") + private String title; + + @Field("subject") + @JsonProperty("subject") + private String subject; + + @Field("description") + @JsonProperty("description") + private String description; + + @Field("scriptLanguage") + @JsonProperty("scriptLanguage") + private List scriptLanguage; + + @Field("language") + @JsonProperty("language") + private List language; + + @Field("englishTranslation") + @JsonProperty("englishTranslation") + private String englishTranslation; + + @Field("moderation") + @JsonProperty("moderation") + private ContentModeration moderation; + + @Field("upvote") + @JsonProperty("upvote") + @Builder.Default + private Integer upvote = 0; + + @Field("geolocation") + @JsonProperty("geolocation") + private GeoLocation geolocation; + + @CreatedDate + @Field("createdAt") + @JsonProperty("createdAt") + private Date createdAt; + + @LastModifiedDate + @Field("updatedAt") + @JsonProperty("updatedAt") + private Date updatedAt; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class GeoLocation { + @Field("lon") + @JsonProperty("lon") + @NotNull + @NotEmpty + private String lon; + + @Field("lat") + @JsonProperty("lat") + @NotNull + @NotEmpty + private String lat; + + private String amenity; + private String road; + private String neighbourhood; + private String suburb; + + @JsonProperty("city_district") + private String cityDistrict; + + private String city; + private String county; + + @JsonProperty("state_district") + private String stateDistrict; + + private String state; + + @JsonProperty("ISO3166-2-lvl4") + private String iso3166Lvl4; + + private String postcode; + private String country; + + @JsonProperty("country_code") + private String countryCode; + + @JsonProperty("place_id") + private Long placeId; + + private String licence; + + @JsonProperty("osm_type") + private String osmType; + + @JsonProperty("osm_id") + private Long osmId; + + @JsonProperty("class") + private String clazz; // "class" is reserved in Java, so renamed to clazz + + private String type; + + @JsonProperty("place_rank") + private Integer placeRank; + + private Double importance; + + @JsonProperty("addresstype") + private String addressType; + + private String name; + + @JsonProperty("display_name") + private String displayName; + + private List boundingbox; + + @GeoSpatialIndexed + private List coordinates; // [lon, lat] for geo queries + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class UsersRating { + + @Field("userId") + @JsonProperty("userId") + private String userId; + + @Field("rating") + @JsonProperty("rating") + private double rating; + } +} diff --git a/src/main/java/com/cadac/stone_inscription/entity/PublicPostDescription.java b/src/main/java/com/cadac/stone_inscription/entity/PublicPostDescription.java index a9adc45..3587f28 100644 --- a/src/main/java/com/cadac/stone_inscription/entity/PublicPostDescription.java +++ b/src/main/java/com/cadac/stone_inscription/entity/PublicPostDescription.java @@ -1,85 +1,90 @@ -package com.cadac.stone_inscription.entity; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; - -import lombok.*; -import org.bson.types.ObjectId; -import org.springframework.data.annotation.CreatedDate; -import org.springframework.data.annotation.Id; -import org.springframework.data.annotation.LastModifiedDate; -import org.springframework.data.mongodb.core.mapping.Document; -import org.springframework.data.mongodb.core.mapping.Field; - -import java.util.Date; -import java.util.LinkedList; -import java.util.List; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -@Document(collection = "postdescription") - -public class PublicPostDescription { - - @Id - @JsonProperty("id") - @JsonSerialize(using = ToStringSerializer.class) - private ObjectId id; - - @Field("postId") - @JsonProperty("postId") - @JsonSerialize(using = ToStringSerializer.class) - private ObjectId postId; - - @Field("userId") - @JsonProperty("userId") - @JsonSerialize(using = ToStringSerializer.class) - private ObjectId userId; - - @Field("username") - @JsonProperty("username") - private String username; - - @Field("userImageUrl") - @JsonProperty("userImageUrl") - private String userImageUrl; - - @Field("description") - @JsonProperty("description") - private String description; - - @Field("upvote") - @JsonProperty("upvote") - @Builder.Default - private Integer upvote = 0; - - @Field("uservote") - @JsonProperty("userVote") - @Builder.Default - private List userVote = new LinkedList<>(); - - @CreatedDate - @Field("createdAt") - @JsonProperty("createdAt") - private Date createdAt; - - @LastModifiedDate - @Field("updatedAt") - @JsonProperty("updatedAt") - private Date updatedAt; - - @Data - @Builder - @NoArgsConstructor - @AllArgsConstructor - public static class UserVote { - - @Field("userId") - @JsonProperty("userId") - private String userId; - - } -} +package com.cadac.stone_inscription.entity; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import com.cadac.stone_inscription.moderation.model.ContentModeration; + +import lombok.*; +import org.bson.types.ObjectId; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.Id; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.mongodb.core.mapping.Document; +import org.springframework.data.mongodb.core.mapping.Field; + +import java.util.Date; +import java.util.LinkedList; +import java.util.List; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Document(collection = "postdescription") + +public class PublicPostDescription { + + @Id + @JsonProperty("id") + @JsonSerialize(using = ToStringSerializer.class) + private ObjectId id; + + @Field("postId") + @JsonProperty("postId") + @JsonSerialize(using = ToStringSerializer.class) + private ObjectId postId; + + @Field("userId") + @JsonProperty("userId") + @JsonSerialize(using = ToStringSerializer.class) + private ObjectId userId; + + @Field("username") + @JsonProperty("username") + private String username; + + @Field("userImageUrl") + @JsonProperty("userImageUrl") + private String userImageUrl; + + @Field("description") + @JsonProperty("description") + private String description; + + @Field("moderation") + @JsonProperty("moderation") + private ContentModeration moderation; + + @Field("upvote") + @JsonProperty("upvote") + @Builder.Default + private Integer upvote = 0; + + @Field("uservote") + @JsonProperty("userVote") + @Builder.Default + private List userVote = new LinkedList<>(); + + @CreatedDate + @Field("createdAt") + @JsonProperty("createdAt") + private Date createdAt; + + @LastModifiedDate + @Field("updatedAt") + @JsonProperty("updatedAt") + private Date updatedAt; + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class UserVote { + + @Field("userId") + @JsonProperty("userId") + private String userId; + + } +} diff --git a/src/main/java/com/cadac/stone_inscription/post/controller/PostController.java b/src/main/java/com/cadac/stone_inscription/post/controller/PostController.java index c2fac30..1adc615 100644 --- a/src/main/java/com/cadac/stone_inscription/post/controller/PostController.java +++ b/src/main/java/com/cadac/stone_inscription/post/controller/PostController.java @@ -1,370 +1,374 @@ -package com.cadac.stone_inscription.post.controller; - -import java.io.IOException; -import java.util.Arrays; -import java.util.List; -import java.util.Locale; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.core.io.InputStreamResource; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.security.access.annotation.Secured; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RequestPart; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.multipart.MultipartFile; - -import com.cadac.stone_inscription.auth.JwtUtil; -import com.cadac.stone_inscription.exception.StoneInscriptionException; -import com.cadac.stone_inscription.post.dto.InscriptionPostDto; -import com.cadac.stone_inscription.post.service.PostService; - -import jakarta.servlet.http.HttpServletRequest; - -@RestController -@RequestMapping("/post") -public class PostController { - - @Autowired - private PostService postService; - - @Autowired - private JwtUtil jwtUtil; - - @Value("${file.extn}") - private String[] fileExt; - - @PostMapping("/addPostWithFile") - @Secured("user") - public ResponseEntity addPostWithFile( - @RequestPart(value = "post", required = false) InscriptionPostDto InscriptionPostDto, - HttpServletRequest request, - @RequestPart("files") MultipartFile... files) throws IOException { - files = getNonEmptyFiles(files); - - if (files.length == 0) { - throw new StoneInscriptionException("No File Uploaded", HttpStatus.BAD_REQUEST); - } - - validateFiles(files); - String token = request.getHeader("Authorization"); - if (token != null && token.startsWith("Bearer ")) { - token = token.substring(7); - } - - return postService.addPostWithFile(InscriptionPostDto, files, - jwtUtil.getUsernameFromToken(token)); - - } - - @PostMapping("/getAllPost") - @Secured("user") - public ResponseEntity getAllPost() { - - return postService.getAllPost(); - - } - - @GetMapping("/public/images/{id}") - public ResponseEntity getImage(@PathVariable String id) { - - return postService.getImages(id); - - } - - @PostMapping("/getAllUserPost") - @Secured("user") - public ResponseEntity getAllUserPost(HttpServletRequest request) { - - String token = request.getHeader("Authorization"); - if (token != null && token.startsWith("Bearer ")) { - token = token.substring(7); - } - - return postService.getAllUserPost( - jwtUtil.getUsernameFromToken(token)); - - } - - @PostMapping("/addPoastDiscription") - @Secured("user") - public ResponseEntity addPoastDiscription(HttpServletRequest request, String postId, String discription) { - - String token = request.getHeader("Authorization"); - if (token != null && token.startsWith("Bearer ")) { - token = token.substring(7); - } - - return postService.addPoastDiscription( - jwtUtil.getUsernameFromToken(token), postId, discription); - - } - - @PostMapping("/getPostDiscription") - @Secured("user") - public ResponseEntity getPostDiscription(String postId) { - - return postService.getPostDiscription( - postId); - - } - - @PostMapping("/updatePostDiscription") - @Secured("user") - public ResponseEntity updatePostDiscription(HttpServletRequest request, String discriptionId, - String discription) { - String token = request.getHeader("Authorization"); - if (token != null && token.startsWith("Bearer ")) { - token = token.substring(7); - } - - return postService.updatePostDiscription(jwtUtil.getUsernameFromToken(token), - discriptionId, discription); - - } - - @PostMapping("/addRating") - @Secured("user") - public ResponseEntity addRating(HttpServletRequest request, String postId, Double rating) { - - String token = request.getHeader("Authorization"); - if (token != null && token.startsWith("Bearer ")) { - token = token.substring(7); - } - - return postService.addRating(jwtUtil.getUsernameFromToken(token), - postId, rating); - - } - - @PostMapping("/addVote") - @Secured("user") - public ResponseEntity addVote(HttpServletRequest request, String descriptionId) { - - String token = request.getHeader("Authorization"); - if (token != null && token.startsWith("Bearer ")) { - token = token.substring(7); - } - - return postService.addVote(jwtUtil.getUsernameFromToken(token), - descriptionId); - - } - - @PostMapping("/userProfile") - @Secured("user") - public ResponseEntity userProfile(HttpServletRequest request) { - - String token = request.getHeader("Authorization"); - if (token != null && token.startsWith("Bearer ")) { - token = token.substring(7); - } - - return postService.userProfile(jwtUtil.getUsernameFromToken(token)); - } - - @PostMapping("/postDelete") - @Secured("user") - public ResponseEntity postDelete(HttpServletRequest request, String postId) { - - String token = request.getHeader("Authorization"); - if (token != null && token.startsWith("Bearer ")) { - token = token.substring(7); - } - - return postService.postDelete(jwtUtil.getUsernameFromToken(token), postId); - - } - - @PostMapping("/discriptionDelete") - @Secured("user") - public ResponseEntity descriptionDelete(HttpServletRequest request, String descriptionId) { - - String token = request.getHeader("Authorization"); - if (token != null && token.startsWith("Bearer ")) { - token = token.substring(7); - } - - return postService.descriptionDelete(jwtUtil.getUsernameFromToken(token), descriptionId); - - } -// Updated this function for all the updation -// Helps logged user to create a post and upload images - @PostMapping("/updatePost") - @Secured("user") - public ResponseEntity updatePost(HttpServletRequest request, - @RequestPart(value = "post", required = false) InscriptionPostDto InscriptionPostDto, - @RequestParam String postId, - @RequestParam(value = "deletedImageIds", required = false) List deletedImageIds, - @RequestPart(value = "files", required = false) MultipartFile... files) { - - files = getNonEmptyFiles(files); - validateFiles(files); - - String token = request.getHeader("Authorization"); - if (token != null && token.startsWith("Bearer ")) { - token = token.substring(7); - } - - return postService.updatePost(jwtUtil.getUsernameFromToken(token), InscriptionPostDto, postId, - deletedImageIds, files); - - } - - @PostMapping("/addImagesToPost") - @Secured("user") - public ResponseEntity addImagesToPost(HttpServletRequest request, - @RequestParam String postId, - @RequestPart("files") MultipartFile... files) { - - files = getNonEmptyFiles(files); - - if (files.length == 0) { - throw new StoneInscriptionException("No File Uploaded", HttpStatus.BAD_REQUEST); - } - - validateFiles(files); - - String token = request.getHeader("Authorization"); - if (token != null && token.startsWith("Bearer ")) { - token = token.substring(7); - } - - return postService.addImagesToPost(jwtUtil.getUsernameFromToken(token), postId, files); - } - - @PostMapping("/deleteImagesFromPost") - @Secured("user") - public ResponseEntity deleteImagesFromPost(HttpServletRequest request, - @RequestParam String postId, - @RequestParam(value = "deletedImageIds") List deletedImageIds) { - - String token = request.getHeader("Authorization"); - if (token != null && token.startsWith("Bearer ")) { - token = token.substring(7); - } - - return postService.deleteImagesFromPost(jwtUtil.getUsernameFromToken(token), postId, deletedImageIds); - } - - // ============================ - // TEST-ONLY ENDPOINTS (NO JWT) - // TODO: Remove these methods when testing is done to restore normal behavior. - // ============================ - - // @PostMapping("/test/updatePost/{email}") - // public ResponseEntity updatePostForTest( - // @PathVariable String email, - // @RequestPart(value = "post", required = false) InscriptionPostDto InscriptionPostDto, - // @RequestParam String postId, - // @RequestParam(value = "deletedImageIds", required = false) List deletedImageIds, - // @RequestPart(value = "files", required = false) MultipartFile... files) { - - // files = getNonEmptyFiles(files); - // validateFiles(files); - - // return postService.updatePost(email, InscriptionPostDto, postId, deletedImageIds, files); - // } - - // @PostMapping("/test/addPostWithFile/{email}") - // public ResponseEntity addPostWithFileForTest( - // @PathVariable String email, - // @RequestPart(value = "post", required = false) InscriptionPostDto InscriptionPostDto, - // @RequestPart("files") MultipartFile... files) throws IOException { - - // files = getNonEmptyFiles(files); - - // if (files.length == 0) { - // throw new StoneInscriptionException("No File Uploaded", HttpStatus.BAD_REQUEST); - // } - - // validateFiles(files); - - // return postService.addPostWithFile(InscriptionPostDto, files, email); - // } - - // @PostMapping("/test/addImagesToPost/{email}") - // public ResponseEntity addImagesToPostForTest( - // @PathVariable String email, - // @RequestParam String postId, - // @RequestPart("files") MultipartFile... files) { - - // files = getNonEmptyFiles(files); - - // if (files.length == 0) { - // throw new StoneInscriptionException("No File Uploaded", HttpStatus.BAD_REQUEST); - // } - - // validateFiles(files); - - // return postService.addImagesToPost(email, postId, files); - // } - - // @PostMapping("/test/deleteImagesFromPost/{email}") - // public ResponseEntity deleteImagesFromPostForTest( - // @PathVariable String email, - // @RequestParam String postId, - // @RequestParam(value = "deletedImageIds") List deletedImageIds) { - - // return postService.deleteImagesFromPost(email, postId, deletedImageIds); - // } - - // @PostMapping("/test/postDelete/{email}") - // public ResponseEntity postDeleteForTest( - // @PathVariable String email, - // @RequestParam String postId) { - - // return postService.postDelete(email, postId); - // } - -// end of test api's - private MultipartFile[] getNonEmptyFiles(MultipartFile[] files) { - if (files == null) { - return new MultipartFile[0]; - } - - return Arrays.stream(files) - .filter(file -> file != null && !file.isEmpty()) - .toArray(MultipartFile[]::new); - } - - private void validateFiles(MultipartFile[] files) { - Arrays.stream(files).forEach(file -> { - String originalFilename = file.getOriginalFilename(); - if (originalFilename == null || Arrays.stream(fileExt) - .map(ext -> ext.trim().toLowerCase(Locale.ROOT)) - .noneMatch(ext -> originalFilename.toLowerCase(Locale.ROOT).endsWith(ext))) { - throw new StoneInscriptionException("Invalid File format only allowed" + Arrays.toString(fileExt), - HttpStatus.BAD_REQUEST); - } - }); - } - - @PostMapping("/getCommentByUser") - // @Secured("user") - public ResponseEntity getCommentByUser(HttpServletRequest request) { - - String token = request.getHeader("Authorization"); - if (token != null && token.startsWith("Bearer ")) { - token = token.substring(7); - } - - return postService.getCommentByUser(jwtUtil.getUsernameFromToken(token)); - - } - - @GetMapping("/public/getDashboardCounts") - // @Secured("user") - public ResponseEntity getDashboardCounts() { - - return postService.getDashboardCounts(); - - } - -} +package com.cadac.stone_inscription.post.controller; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.InputStreamResource; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.annotation.Secured; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import com.cadac.stone_inscription.auth.JwtUtil; +import com.cadac.stone_inscription.exception.StoneInscriptionException; +import com.cadac.stone_inscription.post.dto.InscriptionPostDto; +import com.cadac.stone_inscription.post.service.PostService; + +import jakarta.servlet.http.HttpServletRequest; + +@RestController +@RequestMapping("/post") +public class PostController { + + @Autowired + private PostService postService; + + @Autowired + private JwtUtil jwtUtil; + + @Value("${file.extn}") + private String[] fileExt; + + @PostMapping("/addPostWithFile") + @Secured("user") + public ResponseEntity addPostWithFile( + @RequestPart(value = "post", required = false) InscriptionPostDto InscriptionPostDto, + HttpServletRequest request, + @RequestPart("files") MultipartFile... files) throws IOException { + files = getNonEmptyFiles(files); + + if (files.length == 0) { + throw new StoneInscriptionException("No File Uploaded", HttpStatus.BAD_REQUEST); + } + + validateFiles(files); + String token = request.getHeader("Authorization"); + if (token != null && token.startsWith("Bearer ")) { + token = token.substring(7); + } + + return postService.addPostWithFile(InscriptionPostDto, files, + jwtUtil.getUsernameFromToken(token)); + + } + + @PostMapping("/getAllPost") + @Secured("user") + public ResponseEntity getAllPost() { + + return postService.getAllPost(); + + } + + @GetMapping("/public/images/{id}") + public ResponseEntity getImage(@PathVariable String id) { + + return postService.getImages(id); + + } + + @PostMapping("/getAllUserPost") + @Secured("user") + public ResponseEntity getAllUserPost(HttpServletRequest request) { + + String token = request.getHeader("Authorization"); + if (token != null && token.startsWith("Bearer ")) { + token = token.substring(7); + } + + return postService.getAllUserPost( + jwtUtil.getUsernameFromToken(token)); + + } + + @PostMapping("/addPoastDiscription") + @Secured("user") + public ResponseEntity addPoastDiscription(HttpServletRequest request, @RequestParam("postId") String postId, + @RequestParam("discription") String discription) { + + String token = request.getHeader("Authorization"); + if (token != null && token.startsWith("Bearer ")) { + token = token.substring(7); + } + + return postService.addPoastDiscription( + jwtUtil.getUsernameFromToken(token), postId, discription); + + } + + @PostMapping("/getPostDiscription") + @Secured("user") + public ResponseEntity getPostDiscription(@RequestParam("postId") String postId) { + + return postService.getPostDiscription( + postId); + + } + + @PostMapping("/updatePostDiscription") + @Secured("user") + public ResponseEntity updatePostDiscription(HttpServletRequest request, + @RequestParam("discriptionId") String discriptionId, + @RequestParam("discription") String discription) { + String token = request.getHeader("Authorization"); + if (token != null && token.startsWith("Bearer ")) { + token = token.substring(7); + } + + return postService.updatePostDiscription(jwtUtil.getUsernameFromToken(token), + discriptionId, discription); + + } + + @PostMapping("/addRating") + @Secured("user") + public ResponseEntity addRating(HttpServletRequest request, @RequestParam("postId") String postId, + @RequestParam("rating") Double rating) { + + String token = request.getHeader("Authorization"); + if (token != null && token.startsWith("Bearer ")) { + token = token.substring(7); + } + + return postService.addRating(jwtUtil.getUsernameFromToken(token), + postId, rating); + + } + + @PostMapping("/addVote") + @Secured("user") + public ResponseEntity addVote(HttpServletRequest request, @RequestParam("descriptionId") String descriptionId) { + + String token = request.getHeader("Authorization"); + if (token != null && token.startsWith("Bearer ")) { + token = token.substring(7); + } + + return postService.addVote(jwtUtil.getUsernameFromToken(token), + descriptionId); + + } + + @PostMapping("/userProfile") + @Secured("user") + public ResponseEntity userProfile(HttpServletRequest request) { + + String token = request.getHeader("Authorization"); + if (token != null && token.startsWith("Bearer ")) { + token = token.substring(7); + } + + return postService.userProfile(jwtUtil.getUsernameFromToken(token)); + } + + @PostMapping("/postDelete") + @Secured("user") + public ResponseEntity postDelete(HttpServletRequest request, @RequestParam("postId") String postId) { + + String token = request.getHeader("Authorization"); + if (token != null && token.startsWith("Bearer ")) { + token = token.substring(7); + } + + return postService.postDelete(jwtUtil.getUsernameFromToken(token), postId); + + } + + @PostMapping("/discriptionDelete") + @Secured("user") + public ResponseEntity descriptionDelete(HttpServletRequest request, @RequestParam("descriptionId") String descriptionId) { + + String token = request.getHeader("Authorization"); + if (token != null && token.startsWith("Bearer ")) { + token = token.substring(7); + } + + return postService.descriptionDelete(jwtUtil.getUsernameFromToken(token), descriptionId); + + } +// Updated this function for all the updation +// Helps logged user to create a post and upload images + @PostMapping("/updatePost") + @Secured("user") + public ResponseEntity updatePost(HttpServletRequest request, + @RequestPart(value = "post", required = false) InscriptionPostDto InscriptionPostDto, + @RequestParam String postId, + @RequestParam(value = "deletedImageIds", required = false) List deletedImageIds, + @RequestPart(value = "files", required = false) MultipartFile... files) { + + files = getNonEmptyFiles(files); + validateFiles(files); + + String token = request.getHeader("Authorization"); + if (token != null && token.startsWith("Bearer ")) { + token = token.substring(7); + } + + return postService.updatePost(jwtUtil.getUsernameFromToken(token), InscriptionPostDto, postId, + deletedImageIds, files); + + } + + @PostMapping("/addImagesToPost") + @Secured("user") + public ResponseEntity addImagesToPost(HttpServletRequest request, + @RequestParam String postId, + @RequestPart("files") MultipartFile... files) { + + files = getNonEmptyFiles(files); + + if (files.length == 0) { + throw new StoneInscriptionException("No File Uploaded", HttpStatus.BAD_REQUEST); + } + + validateFiles(files); + + String token = request.getHeader("Authorization"); + if (token != null && token.startsWith("Bearer ")) { + token = token.substring(7); + } + + return postService.addImagesToPost(jwtUtil.getUsernameFromToken(token), postId, files); + } + + @PostMapping("/deleteImagesFromPost") + @Secured("user") + public ResponseEntity deleteImagesFromPost(HttpServletRequest request, + @RequestParam String postId, + @RequestParam(value = "deletedImageIds") List deletedImageIds) { + + String token = request.getHeader("Authorization"); + if (token != null && token.startsWith("Bearer ")) { + token = token.substring(7); + } + + return postService.deleteImagesFromPost(jwtUtil.getUsernameFromToken(token), postId, deletedImageIds); + } + + // ============================ + // TEST-ONLY ENDPOINTS (NO JWT) + // TODO: Remove these methods when testing is done to restore normal behavior. + // ============================ + + // @PostMapping("/test/updatePost/{email}") + // public ResponseEntity updatePostForTest( + // @PathVariable String email, + // @RequestPart(value = "post", required = false) InscriptionPostDto InscriptionPostDto, + // @RequestParam String postId, + // @RequestParam(value = "deletedImageIds", required = false) List deletedImageIds, + // @RequestPart(value = "files", required = false) MultipartFile... files) { + + // files = getNonEmptyFiles(files); + // validateFiles(files); + + // return postService.updatePost(email, InscriptionPostDto, postId, deletedImageIds, files); + // } + + // @PostMapping("/test/addPostWithFile/{email}") + // public ResponseEntity addPostWithFileForTest( + // @PathVariable String email, + // @RequestPart(value = "post", required = false) InscriptionPostDto InscriptionPostDto, + // @RequestPart("files") MultipartFile... files) throws IOException { + + // files = getNonEmptyFiles(files); + + // if (files.length == 0) { + // throw new StoneInscriptionException("No File Uploaded", HttpStatus.BAD_REQUEST); + // } + + // validateFiles(files); + + // return postService.addPostWithFile(InscriptionPostDto, files, email); + // } + + // @PostMapping("/test/addImagesToPost/{email}") + // public ResponseEntity addImagesToPostForTest( + // @PathVariable String email, + // @RequestParam String postId, + // @RequestPart("files") MultipartFile... files) { + + // files = getNonEmptyFiles(files); + + // if (files.length == 0) { + // throw new StoneInscriptionException("No File Uploaded", HttpStatus.BAD_REQUEST); + // } + + // validateFiles(files); + + // return postService.addImagesToPost(email, postId, files); + // } + + // @PostMapping("/test/deleteImagesFromPost/{email}") + // public ResponseEntity deleteImagesFromPostForTest( + // @PathVariable String email, + // @RequestParam String postId, + // @RequestParam(value = "deletedImageIds") List deletedImageIds) { + + // return postService.deleteImagesFromPost(email, postId, deletedImageIds); + // } + + // @PostMapping("/test/postDelete/{email}") + // public ResponseEntity postDeleteForTest( + // @PathVariable String email, + // @RequestParam String postId) { + + // return postService.postDelete(email, postId); + // } + + +// end of test api's + private MultipartFile[] getNonEmptyFiles(MultipartFile[] files) { + if (files == null) { + return new MultipartFile[0]; + } + + return Arrays.stream(files) + .filter(file -> file != null && !file.isEmpty()) + .toArray(MultipartFile[]::new); + } + + private void validateFiles(MultipartFile[] files) { + Arrays.stream(files).forEach(file -> { + String originalFilename = file.getOriginalFilename(); + if (originalFilename == null || Arrays.stream(fileExt) + .map(ext -> ext.trim().toLowerCase(Locale.ROOT)) + .noneMatch(ext -> originalFilename.toLowerCase(Locale.ROOT).endsWith(ext))) { + throw new StoneInscriptionException("Invalid File format only allowed" + Arrays.toString(fileExt), + HttpStatus.BAD_REQUEST); + } + }); + } + + @PostMapping("/getCommentByUser") + // @Secured("user") + public ResponseEntity getCommentByUser(HttpServletRequest request) { + + String token = request.getHeader("Authorization"); + if (token != null && token.startsWith("Bearer ")) { + token = token.substring(7); + } + + return postService.getCommentByUser(jwtUtil.getUsernameFromToken(token)); + + } + + @GetMapping("/public/getDashboardCounts") + // @Secured("user") + public ResponseEntity getDashboardCounts() { + + return postService.getDashboardCounts(); + + } + +} diff --git a/src/main/java/com/cadac/stone_inscription/post/service/PostServiceImp.java b/src/main/java/com/cadac/stone_inscription/post/service/PostServiceImp.java index 868a912..3cbd034 100644 --- a/src/main/java/com/cadac/stone_inscription/post/service/PostServiceImp.java +++ b/src/main/java/com/cadac/stone_inscription/post/service/PostServiceImp.java @@ -1,619 +1,742 @@ -package com.cadac.stone_inscription.post.service; - -import java.io.ByteArrayInputStream; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - -import org.bson.types.ObjectId; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.core.io.InputStreamResource; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Service; -import org.springframework.web.multipart.MultipartFile; - -import com.cadac.stone_inscription.entity.ImagesData; -import com.cadac.stone_inscription.entity.InscriptionPost; -import com.cadac.stone_inscription.entity.PublicPostDescription; -import com.cadac.stone_inscription.entity.User; -import com.cadac.stone_inscription.exception.StoneInscriptionException; -import com.cadac.stone_inscription.post.dto.InscriptionPostDto; -import com.cadac.stone_inscription.post.dto.PublicPostUserDescriptionDto; -import com.cadac.stone_inscription.post.mapper.PostMapper; -import com.cadac.stone_inscription.post.mapper.PublicPostDescriptionMapper; -import com.cadac.stone_inscription.post.util.ImageMetadataGeolocationWithPhash; -import com.cadac.stone_inscription.post.util.ImageMetadataGeolocationWithPhash.ImageMetaAndInfo; -import com.cadac.stone_inscription.repository.ImagesDataRepo; -import com.cadac.stone_inscription.repository.InscriptionPostRepo; -import com.cadac.stone_inscription.repository.PublicPostDescriptionRepo; -import com.cadac.stone_inscription.repository.UserRepository; -import com.cadac.stone_inscription.util.UserResponse; - -@Service -public class PostServiceImp implements PostService { - - @Autowired - private UserRepository userRepository; - - @Autowired - private InscriptionPostRepo inscriptionPostRepo; - - @Autowired - private ImageMetadataGeolocationWithPhash metadataGeolocationWithPhash; - - @Autowired - private ImagesDataRepo imagesDataRepo; - - @Autowired - private PublicPostDescriptionRepo publicPostDescriptionRepo; - - @Value("${app.backend.url}") - private String backendUrl; - -// Create Post + Process Images + Extract Location + Save Post + Update User Stats - - @Override - public ResponseEntity addPostWithFile(InscriptionPostDto inscriptionPostDto, MultipartFile[] files, - String usernameFromToken) { - - List ls = validateAndExtractImages(files, Collections.emptySet(), true); - - // Below Line To use for Threshold similarty - - // Double similarty = ImagePhash.imagePHashComparing(ls.get(0).getPHash(), - // ls.get(ls.size() - 1).getPHash()); - - Optional geoLocationInfoAndCordinates = ls.stream() - .filter(el -> el.getGeocCordinates() != null) - .findFirst(); - - Optional geoLocationCordinates = geoLocationInfoAndCordinates - .isPresent() - ? Optional - .of(geoLocationInfoAndCordinates.get().getGeocCordinates()) - : Optional.empty(); - // Optional - // geoLocationCordinates = ls.stream() - // .map(el -> el.getGeocCordinates()) - // .filter(Objects::nonNull) - // .findFirst(); - - User user = userRepository.findByEmail(usernameFromToken); - - adjustUserImagesUploaded(user, ls.size()); - userRepository.save(user); - - ObjectId postUserId = user.getId(); - - InscriptionPost inscriptionPost = new InscriptionPost(); - - if (inscriptionPostDto != null) { - inscriptionPost = PostMapper.toEntity(inscriptionPostDto); - } - - inscriptionPost.setUserId(postUserId); - - if (geoLocationCordinates.isPresent()) { - - inscriptionPost.getDescription().setGeolocation(InscriptionPost.GeoLocation.builder() - .lat(geoLocationCordinates.get().getLatitude()) - .lon(geoLocationCordinates.get().getLongitude()) - .city(geoLocationInfoAndCordinates.get().getGeoApiResponse().getAddress().getCity()) - .state(geoLocationInfoAndCordinates.get().getGeoApiResponse().getAddress().getState()) - .country(geoLocationInfoAndCordinates.get().getGeoApiResponse().getAddress().getCountry()) - .amenity(geoLocationInfoAndCordinates.get().getGeoApiResponse().getAddress().getAmenity()) - .road(geoLocationInfoAndCordinates.get().getGeoApiResponse().getAddress().getRoad()) - .neighbourhood( - geoLocationInfoAndCordinates.get().getGeoApiResponse().getAddress().getNeighbourhood()) - .suburb(geoLocationInfoAndCordinates.get().getGeoApiResponse().getAddress().getSuburb()) - .cityDistrict(geoLocationInfoAndCordinates.get().getGeoApiResponse().getAddress().getCityDistrict()) - .stateDistrict(geoLocationInfoAndCordinates.get().getGeoApiResponse().getAddress() - .getStateDistrict()) - .iso3166Lvl4(geoLocationInfoAndCordinates.get().getGeoApiResponse().getAddress() - .getIso3166Lvl4()) - .postcode(geoLocationInfoAndCordinates.get().getGeoApiResponse().getAddress().getPostcode()) - .county(geoLocationInfoAndCordinates.get().getGeoApiResponse().getAddress().getCounty()) - .countryCode(geoLocationInfoAndCordinates.get().getGeoApiResponse().getAddress() - .getCountryCode()) - .placeId(geoLocationInfoAndCordinates.get().getGeoApiResponse().getPlaceId()) - .licence(geoLocationInfoAndCordinates.get().getGeoApiResponse().getLicence()) - .osmType(geoLocationInfoAndCordinates.get().getGeoApiResponse().getOsmType()) - .osmId(geoLocationInfoAndCordinates.get().getGeoApiResponse().getOsmId()) - .clazz(geoLocationInfoAndCordinates.get().getGeoApiResponse().getClazz()) - .type(geoLocationInfoAndCordinates.get().getGeoApiResponse().getType()) - .placeRank(geoLocationInfoAndCordinates.get().getGeoApiResponse().getPlaceRank()) - .importance(geoLocationInfoAndCordinates.get().getGeoApiResponse().getImportance()) - .addressType(geoLocationInfoAndCordinates.get().getGeoApiResponse().getAddressType()) - .name(geoLocationInfoAndCordinates.get().getGeoApiResponse().getName()) - .displayName(geoLocationInfoAndCordinates.get().getGeoApiResponse().getDisplayName()) - .boundingbox(geoLocationInfoAndCordinates.get().getGeoApiResponse().getBoundingbox()) - .build()); - - } - - inscriptionPost = inscriptionPostRepo.save(inscriptionPost); - ObjectId postId = inscriptionPost.getId(); - - List imageId = saveImages(postId, ls); - - inscriptionPost - .setImages(InscriptionPost.Images.builder().image(imageId).thumbnailImage(imageId.get(0)).build()); - - if (inscriptionPostDto != null) { - inscriptionPost.setVisiblity(inscriptionPostDto.getVisiblity()); - } - - inscriptionPostRepo.save(inscriptionPost); - return UserResponse.responseHandler("Images Uploaded Sucessfully", HttpStatus.OK, true); - } - - @Override - public ResponseEntity getAllPost() { - - List allPost = inscriptionPostRepo.findAll(); - - allPost.forEach(elem -> { - - if (elem.getVisiblity() || elem.getVisiblity() == null) { - elem.setUserName(userRepository.findById(elem.getUserId()).get().getName()); - } - - elem.getImages().setImage(elem.getImages().getImage().stream().map( - el -> backendUrl + "/post/public/images/" + el) - .toList()); - - elem.getImages() - .setThumbnailImage(backendUrl + "/post/public/images/" + elem.getImages().getThumbnailImage()); - - }); - - return UserResponse.responseHandler("All Posts", HttpStatus.OK, allPost); - } - - @Override - public ResponseEntity getImages(String id) { - ImagesData image = imagesDataRepo.findById(id).orElseThrow(); - return ResponseEntity.ok() - .contentType(MediaType.parseMediaType(image.getMetadata().getContentType())) - .body(new InputStreamResource(new ByteArrayInputStream(image.getImageData()))); - } - - @Override - public ResponseEntity getAllUserPost(String usernameFromToken) { - ObjectId postUserId = userRepository.findByEmail(usernameFromToken).getId(); - List userPost = inscriptionPostRepo.findByUserId(postUserId); - - userPost.forEach(elem -> { - - elem.getImages().setImage(elem.getImages().getImage().stream().map( - el -> backendUrl + "/post/public/images/" + el) - .toList()); - - elem.getImages() - .setThumbnailImage(backendUrl + "/post/public/images/" + elem.getImages().getThumbnailImage()); - - }); - - return UserResponse.responseHandler("All Posts", HttpStatus.OK, userPost); - } - - @Override - public ResponseEntity addPoastDiscription(String usernameFromToken, String postId, String discription) { - - User user = userRepository.findByEmail(usernameFromToken); - - if (inscriptionPostRepo.findById(new ObjectId(postId)).isEmpty()) { - throw new StoneInscriptionException("Unprocesable request", HttpStatus.BAD_REQUEST); - } - - publicPostDescriptionRepo.save(PublicPostDescription.builder().description(discription) - .postId(new ObjectId(postId)).userId(user.getId()) - .userImageUrl(user.getProfileImage()).username(user.getName()).build()); - - return UserResponse.responseHandler("Discription Added sucessfully", HttpStatus.OK, true); - } - - @Override - public ResponseEntity getPostDiscription(String postId) { - - return UserResponse.responseHandler("Fetch All Discription for Post", HttpStatus.OK, - publicPostDescriptionRepo.findByPostId(new ObjectId(postId))); - } - - @Override - public ResponseEntity updatePostDiscription(String usernameFromToken, String postId, String discription) { - User user = userRepository.findByEmail(usernameFromToken); - Optional postDiscription = publicPostDescriptionRepo.findById(new ObjectId(postId)); - - if (postDiscription.isEmpty()) { - throw new StoneInscriptionException("Unprocesable request", HttpStatus.BAD_REQUEST); - } - - if (!postDiscription.get().getUserId().equals(user.getId())) { - throw new StoneInscriptionException("Unprocesable request Unauthorized", HttpStatus.UNAUTHORIZED); - } - - postDiscription.get().setDescription(discription); - - publicPostDescriptionRepo.save(postDiscription.get()); - - return UserResponse.responseHandler("Updated Discription", HttpStatus.OK, true); - - } - - @Override - public ResponseEntity addRating(String usernameFromToken, String postId, Double rating) { - - User user = userRepository.findByEmail(usernameFromToken); - Optional inscrptionPost = inscriptionPostRepo.findById(new ObjectId(postId)); - - if (inscrptionPost.isEmpty()) { - throw new StoneInscriptionException("Unprocesable request", HttpStatus.BAD_REQUEST); - } - - Optional existingRating = inscrptionPost.get().getUserRating() - .stream() - .filter(r -> r.getUserId().equals(user.getId().toString())) - .findFirst(); - - if (existingRating.isPresent()) { - existingRating.get().setRating(rating); - - } else { - inscrptionPost.get().getUserRating() - .add(InscriptionPost.UsersRating.builder().rating(rating).userId(user.getId().toString()).build()); - } - - inscrptionPost.get().setRating(inscrptionPost.get().getUserRating() - .stream() - .mapToDouble(InscriptionPost.UsersRating::getRating) - .average() - .orElse(0.0)); - - ; - - inscriptionPostRepo.save(inscrptionPost.get()); - return UserResponse.responseHandler("ratting added ", HttpStatus.OK, true); - } - - @Override - public ResponseEntity addVote(String usernameFromToken, String descriptionId) { - User user = userRepository.findByEmail(usernameFromToken); - Optional postDiscription = publicPostDescriptionRepo - .findById(new ObjectId(descriptionId)); - - User postUser = userRepository.findById(postDiscription.get().getUserId()).get(); - - if (postDiscription.get().getUserVote().stream() - .anyMatch(el -> el.getUserId().equals(user.getId().toString()))) { - postDiscription.get().setUserVote(postDiscription.get().getUserVote().stream() - .filter(elm -> !elm.getUserId().equals(user.getId().toString())).toList()); - - postUser.setUpvotesReceived(postUser.getUpvotesReceived() - 1); - ; - - } else { - postDiscription.get().getUserVote() - .add(PublicPostDescription.UserVote.builder().userId(user.getId().toString()).build()); - - postUser.setUpvotesReceived(postUser.getUpvotesReceived() + 1); - } - userRepository.save(postUser); - postDiscription.get().setUpvote(postDiscription.get().getUserVote().size()); - - publicPostDescriptionRepo.save(postDiscription.get()); - return UserResponse.responseHandler("Vote updated", HttpStatus.OK, true); - } - - @Override - public ResponseEntity userProfile(String usernameFromToken) { - return UserResponse.responseHandler("User Profile", HttpStatus.OK, - userRepository.findByEmail(usernameFromToken)); - } - - @Override - public ResponseEntity postDelete(String usernameFromToken, String postId) { - User user = userRepository.findByEmail(usernameFromToken); - - Optional postDelete = inscriptionPostRepo.findById(new ObjectId(postId)); - - if (postDelete.isEmpty()) { - throw new StoneInscriptionException("Unprocesable request", HttpStatus.BAD_REQUEST); - } - if (!user.getId().toString().equals(postDelete.get().getUserId().toString())) { - throw new StoneInscriptionException("Unprocesable request", HttpStatus.BAD_REQUEST); - } - int deletedImageCount = getExistingImageIds(postDelete.get()).size(); - getExistingImageIds(postDelete.get()).forEach(imagesDataRepo::deleteById); - adjustUserImagesUploaded(user, -deletedImageCount); - userRepository.save(user); - - publicPostDescriptionRepo.deleteAllByPostId(postId); - inscriptionPostRepo.deleteById(new ObjectId(postId)); - return UserResponse.responseHandler("post deleted", HttpStatus.OK, true); - } - - @Override - public ResponseEntity descriptionDelete(String usernameFromToken, String descriptionId) { - User user = userRepository.findByEmail(usernameFromToken); - Optional postDiscription = publicPostDescriptionRepo - .findById(new ObjectId(descriptionId)); - - if (postDiscription.isEmpty()) { - throw new StoneInscriptionException("Unprocesable request", HttpStatus.BAD_REQUEST); - } - - if (!postDiscription.get().getUserId().equals(user.getId())) { - throw new StoneInscriptionException("Unprocesable request Unauthorized", HttpStatus.UNAUTHORIZED); - } - - publicPostDescriptionRepo.deleteById(new ObjectId(descriptionId)); - - return UserResponse.responseHandler("description deleted", HttpStatus.OK, true); - } - -// Main function for the updation of the image -// edit post details -// delete some images -// upload new images -// keep at least one image in the post - - @Override - public ResponseEntity updatePost(String usernameFromToken, InscriptionPostDto inscriptionPostDto, - String postId, List deletedImageIds, MultipartFile[] files) { - - InscriptionPost post = getOwnedPost(usernameFromToken, postId); - User user = userRepository.findByEmail(usernameFromToken); - List existingImageIds = getExistingImageIds(post); - List imagesToDelete = validateDeletedImageIds(existingImageIds, deletedImageIds, false); - Set deletableImageIds = new HashSet<>(imagesToDelete); - List newImages = validateAndExtractImages(files, deletableImageIds, false); - - ensureMinimumImageCount(existingImageIds.size(), deletableImageIds.size(), newImages.size()); - - if (inscriptionPostDto != null) { - post.setDescription(PostMapper.toEntityDescription(inscriptionPostDto.getDescription())); - post.setScript(inscriptionPostDto.getScript()); - post.setTopic(inscriptionPostDto.getTopic()); - post.setType(inscriptionPostDto.getType()); - } - - List updatedImageIds = removeDeletedImageIds(existingImageIds, deletableImageIds); - updatedImageIds.addAll(saveImages(post.getId(), newImages)); - - updatePostImages(post, updatedImageIds, deletableImageIds); - inscriptionPostRepo.save(post); - deleteImagesByIds(deletableImageIds); - adjustUserImagesUploaded(user, newImages.size() - deletableImageIds.size()); - userRepository.save(user); - return UserResponse.responseHandler("post Updated ", HttpStatus.OK, true); - } - - @Override - public ResponseEntity addImagesToPost(String usernameFromToken, String postId, MultipartFile[] files) { - InscriptionPost post = getOwnedPost(usernameFromToken, postId); - User user = userRepository.findByEmail(usernameFromToken); - List newImages = validateAndExtractImages(files, Collections.emptySet(), true); - - List updatedImageIds = getExistingImageIds(post); - updatedImageIds.addAll(saveImages(post.getId(), newImages)); - - updatePostImages(post, updatedImageIds, Collections.emptySet()); - inscriptionPostRepo.save(post); - adjustUserImagesUploaded(user, newImages.size()); - userRepository.save(user); - - return UserResponse.responseHandler("Images Added To Post Successfully", HttpStatus.OK, true); - } - - @Override - public ResponseEntity deleteImagesFromPost(String usernameFromToken, String postId, List deletedImageIds) { - InscriptionPost post = getOwnedPost(usernameFromToken, postId); - User user = userRepository.findByEmail(usernameFromToken); - List existingImageIds = getExistingImageIds(post); - List imagesToDelete = validateDeletedImageIds(existingImageIds, deletedImageIds, true); - Set deletableImageIds = new HashSet<>(imagesToDelete); - - ensureMinimumImageCount(existingImageIds.size(), deletableImageIds.size(), 0); - - List updatedImageIds = removeDeletedImageIds(existingImageIds, deletableImageIds); - updatePostImages(post, updatedImageIds, deletableImageIds); - - inscriptionPostRepo.save(post); - deleteImagesByIds(deletableImageIds); - adjustUserImagesUploaded(user, -deletableImageIds.size()); - userRepository.save(user); - - return UserResponse.responseHandler("Images Deleted From Post Successfully", HttpStatus.OK, true); - } - - @Override - public ResponseEntity getCommentByUser(String usernameFromToken) { - User user = userRepository.findByEmail(usernameFromToken); - - List postDiscription = publicPostDescriptionRepo.findAllByUserId(user.getId()); - - List ls = postDiscription.stream() - .map(el -> { - - PublicPostUserDescriptionDto ref = PublicPostDescriptionMapper.toDto(el); - ref.setPostImageUrl(inscriptionPostRepo.findById(el.getPostId()) - .map(post -> post.getImages().getThumbnailImage()) - .map(imageId -> backendUrl + "/post/public/images/" + imageId) - .orElse(null)); - - return ref; - - }).collect(Collectors.toList()); - - return UserResponse.responseHandler("user all comment ", HttpStatus.OK, ls); - } - - @Override - public ResponseEntity getDashboardCounts() { - - Map counts = new HashMap<>(); - - List allPost = inscriptionPostRepo.findAll(); - - counts.put("totalUsers", Math.toIntExact(userRepository.count())); - counts.put("totalPosts", Math.toIntExact(inscriptionPostRepo.count())); - counts.put("totalImages", Math.toIntExact(imagesDataRepo.count())); - counts.put("totalGeoTaggedPosts", - (int) allPost.stream().filter(el -> el.getDescription().getGeolocation() != null).count()); - counts.put("totalTranslations", (int) allPost.stream() - .map(InscriptionPost::getDescription) - .filter(Objects::nonNull) - .map(InscriptionPost.Description::getEnglishTranslation) - .filter(Objects::nonNull) - .map(String::trim) - .filter(translation -> !translation.isEmpty()) - .count()); - - return UserResponse.responseHandler("Dashboard Counts", HttpStatus.OK, counts); - } - - private List validateAndExtractImages(MultipartFile[] files, Set replaceableImageIds, - boolean filesRequired) { - if (files == null || files.length == 0) { - if (filesRequired) { - throw new StoneInscriptionException("No File Uploaded", HttpStatus.BAD_REQUEST); - } - - return List.of(); - } - - List ls = metadataGeolocationWithPhash.getGeoLocationWithIamgeMetaandInfo(files); - - if (ls.size() == 0) { - throw new StoneInscriptionException("No Valid Image Found in the Request", HttpStatus.BAD_REQUEST); - } - - if (ls.size() != ls.stream().map(ImageMetaAndInfo::getPHash).distinct().count()) { - throw new StoneInscriptionException("Duplicate Image Uploaded", HttpStatus.BAD_REQUEST); - } - - boolean imageAlreadyExists = ls.stream().anyMatch(image -> { - Optional existingImage = imagesDataRepo - .findFirstByMetadata_ImageHashValue(image.getPHash().getHashValue().toString()); - return existingImage.isPresent() && !replaceableImageIds.contains(existingImage.get().getId()); - }); - - if (imageAlreadyExists) { - throw new StoneInscriptionException("Image Already Uploaded By some User", HttpStatus.CONFLICT); - } - - return ls; - } - - private List saveImages(ObjectId postId, List images) { - return images.stream().map(image -> imagesDataRepo.save(ImagesData.builder() - .imageData(image.getFile()) - .postId(postId) - .metadata(ImagesData.Metadata.builder() - .fileName(image.getFileName()) - .fileSize(image.getFileSize()) - .contentType(image.getContentType()) - .imageHashValue(image.getPHash().getHashValue().toString()) - .build()) - .build()).getId()).toList(); - } - - private InscriptionPost getOwnedPost(String usernameFromToken, String postId) { - User user = userRepository.findByEmail(usernameFromToken); - Optional inscriptionPost = inscriptionPostRepo.findById(new ObjectId(postId)); - - if (inscriptionPost.isEmpty()) { - throw new StoneInscriptionException("Unprocesable request", HttpStatus.BAD_REQUEST); - } - - if (!user.getId().equals(inscriptionPost.get().getUserId())) { - throw new StoneInscriptionException("Forbidden", HttpStatus.FORBIDDEN); - } - - return inscriptionPost.get(); - } - - private List getExistingImageIds(InscriptionPost post) { - if (post.getImages() == null || post.getImages().getImage() == null) { - return new LinkedList<>(); - } - - return new LinkedList<>(post.getImages().getImage()); - } - - private List validateDeletedImageIds(List existingImageIds, List deletedImageIds, - boolean deletionRequired) { - List imagesToDelete = sanitizeImageIds(deletedImageIds); - - if (deletionRequired && imagesToDelete.isEmpty()) { - throw new StoneInscriptionException("No image selected for deletion", HttpStatus.BAD_REQUEST); - } - - if (!existingImageIds.containsAll(imagesToDelete)) { - throw new StoneInscriptionException("Invalid image selected for deletion", HttpStatus.BAD_REQUEST); - } - - return imagesToDelete; - } - - private void ensureMinimumImageCount(int existingImageCount, int deletedImageCount, int newImageCount) { - int finalImageCount = existingImageCount - deletedImageCount + newImageCount; - - if (finalImageCount < 1) { - throw new StoneInscriptionException("Post should have at least one image", HttpStatus.BAD_REQUEST); - } - } - - private List removeDeletedImageIds(List existingImageIds, Set deletableImageIds) { - return existingImageIds.stream() - .filter(imageId -> !deletableImageIds.contains(imageId)) - .collect(Collectors.toCollection(LinkedList::new)); - } - - private void updatePostImages(InscriptionPost post, List updatedImageIds, Set removedImageIds) { - if (post.getImages() == null) { - post.setImages(InscriptionPost.Images.builder().build()); - } - - post.getImages().setImage(updatedImageIds); - - if (post.getImages().getThumbnailImage() == null - || removedImageIds.contains(post.getImages().getThumbnailImage())) { - post.getImages().setThumbnailImage(updatedImageIds.get(0)); - } - } - - private void deleteImagesByIds(Set imageIds) { - imageIds.forEach(imagesDataRepo::deleteById); - } - - private void adjustUserImagesUploaded(User user, int delta) { - int currentCount = user.getImagesUploaded() == null ? 0 : user.getImagesUploaded(); - user.setImagesUploaded(Math.max(0, currentCount + delta)); - } - - private List sanitizeImageIds(List imageIds) { - if (imageIds == null) { - return List.of(); - } - - return imageIds.stream() - .filter(Objects::nonNull) - .map(String::trim) - .filter(id -> !id.isEmpty()) - .distinct() - .toList(); - } - -} +package com.cadac.stone_inscription.post.service; + +import java.io.ByteArrayInputStream; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import org.bson.types.ObjectId; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.InputStreamResource; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import com.cadac.stone_inscription.entity.ImagesData; +import com.cadac.stone_inscription.entity.InscriptionPost; +import com.cadac.stone_inscription.entity.PublicPostDescription; +import com.cadac.stone_inscription.entity.User; +import com.cadac.stone_inscription.exception.StoneInscriptionException; +import com.cadac.stone_inscription.moderation.model.ContentModeration; +import com.cadac.stone_inscription.moderation.model.ContentModerationResult; +import com.cadac.stone_inscription.moderation.service.ContentModerationService; +import com.cadac.stone_inscription.post.dto.InscriptionPostDto; +import com.cadac.stone_inscription.post.dto.PublicPostUserDescriptionDto; +import com.cadac.stone_inscription.post.mapper.PostMapper; +import com.cadac.stone_inscription.post.mapper.PublicPostDescriptionMapper; +import com.cadac.stone_inscription.post.util.ImageMetadataGeolocationWithPhash; +import com.cadac.stone_inscription.post.util.ImageMetadataGeolocationWithPhash.ImageMetaAndInfo; +import com.cadac.stone_inscription.repository.ImagesDataRepo; +import com.cadac.stone_inscription.repository.InscriptionPostRepo; +import com.cadac.stone_inscription.repository.PublicPostDescriptionRepo; +import com.cadac.stone_inscription.repository.UserRepository; +import com.cadac.stone_inscription.util.UserResponse; + +@Service +public class PostServiceImp implements PostService { + + @Autowired + private UserRepository userRepository; + + @Autowired + private InscriptionPostRepo inscriptionPostRepo; + + @Autowired + private ImageMetadataGeolocationWithPhash metadataGeolocationWithPhash; + + @Autowired + private ImagesDataRepo imagesDataRepo; + + @Autowired + private PublicPostDescriptionRepo publicPostDescriptionRepo; + + @Autowired + private ContentModerationService contentModerationService; + + @Value("${app.backend.url}") + private String backendUrl; + +// Create Post + Process Images + Extract Location + Save Post + Update User Stats + + @Override + public ResponseEntity addPostWithFile(InscriptionPostDto inscriptionPostDto, MultipartFile[] files, + String usernameFromToken) { + + List ls = validateAndExtractImages(files, Collections.emptySet(), true); + + // Below Line To use for Threshold similarty + + // Double similarty = ImagePhash.imagePHashComparing(ls.get(0).getPHash(), + // ls.get(ls.size() - 1).getPHash()); + + Optional geoLocationInfoAndCordinates = ls.stream() + .filter(el -> el.getGeocCordinates() != null) + .findFirst(); + + Optional geoLocationCordinates = geoLocationInfoAndCordinates + .isPresent() + ? Optional + .of(geoLocationInfoAndCordinates.get().getGeocCordinates()) + : Optional.empty(); + // Optional + // geoLocationCordinates = ls.stream() + // .map(el -> el.getGeocCordinates()) + // .filter(Objects::nonNull) + // .findFirst(); + + User user = userRepository.findByEmail(usernameFromToken); + + InscriptionPost inscriptionPost = new InscriptionPost(); + + if (inscriptionPostDto != null) { + inscriptionPost = PostMapper.toEntity(inscriptionPostDto); + } + + ContentModeration moderation = moderatePostContent( + extractTitle(inscriptionPost), + inscriptionPost.getTopic(), + extractDescription(inscriptionPost)); + + if (inscriptionPost.getDescription() == null) { + inscriptionPost.setDescription(InscriptionPost.Description.builder().build()); + } + inscriptionPost.getDescription().setModeration(moderation); + + adjustUserImagesUploaded(user, ls.size()); + userRepository.save(user); + + ObjectId postUserId = user.getId(); + inscriptionPost.setUserId(postUserId); + + if (geoLocationCordinates.isPresent()) { + + inscriptionPost.getDescription().setGeolocation(InscriptionPost.GeoLocation.builder() + .lat(geoLocationCordinates.get().getLatitude()) + .lon(geoLocationCordinates.get().getLongitude()) + .city(geoLocationInfoAndCordinates.get().getGeoApiResponse().getAddress().getCity()) + .state(geoLocationInfoAndCordinates.get().getGeoApiResponse().getAddress().getState()) + .country(geoLocationInfoAndCordinates.get().getGeoApiResponse().getAddress().getCountry()) + .amenity(geoLocationInfoAndCordinates.get().getGeoApiResponse().getAddress().getAmenity()) + .road(geoLocationInfoAndCordinates.get().getGeoApiResponse().getAddress().getRoad()) + .neighbourhood( + geoLocationInfoAndCordinates.get().getGeoApiResponse().getAddress().getNeighbourhood()) + .suburb(geoLocationInfoAndCordinates.get().getGeoApiResponse().getAddress().getSuburb()) + .cityDistrict(geoLocationInfoAndCordinates.get().getGeoApiResponse().getAddress().getCityDistrict()) + .stateDistrict(geoLocationInfoAndCordinates.get().getGeoApiResponse().getAddress() + .getStateDistrict()) + .iso3166Lvl4(geoLocationInfoAndCordinates.get().getGeoApiResponse().getAddress() + .getIso3166Lvl4()) + .postcode(geoLocationInfoAndCordinates.get().getGeoApiResponse().getAddress().getPostcode()) + .county(geoLocationInfoAndCordinates.get().getGeoApiResponse().getAddress().getCounty()) + .countryCode(geoLocationInfoAndCordinates.get().getGeoApiResponse().getAddress() + .getCountryCode()) + .placeId(geoLocationInfoAndCordinates.get().getGeoApiResponse().getPlaceId()) + .licence(geoLocationInfoAndCordinates.get().getGeoApiResponse().getLicence()) + .osmType(geoLocationInfoAndCordinates.get().getGeoApiResponse().getOsmType()) + .osmId(geoLocationInfoAndCordinates.get().getGeoApiResponse().getOsmId()) + .clazz(geoLocationInfoAndCordinates.get().getGeoApiResponse().getClazz()) + .type(geoLocationInfoAndCordinates.get().getGeoApiResponse().getType()) + .placeRank(geoLocationInfoAndCordinates.get().getGeoApiResponse().getPlaceRank()) + .importance(geoLocationInfoAndCordinates.get().getGeoApiResponse().getImportance()) + .addressType(geoLocationInfoAndCordinates.get().getGeoApiResponse().getAddressType()) + .name(geoLocationInfoAndCordinates.get().getGeoApiResponse().getName()) + .displayName(geoLocationInfoAndCordinates.get().getGeoApiResponse().getDisplayName()) + .boundingbox(geoLocationInfoAndCordinates.get().getGeoApiResponse().getBoundingbox()) + .build()); + + } + + inscriptionPost = inscriptionPostRepo.save(inscriptionPost); + ObjectId postId = inscriptionPost.getId(); + + List imageId = saveImages(postId, ls); + + inscriptionPost + .setImages(InscriptionPost.Images.builder().image(imageId).thumbnailImage(imageId.get(0)).build()); + + if (inscriptionPostDto != null) { + inscriptionPost.setVisiblity(inscriptionPostDto.getVisiblity()); + } + + inscriptionPostRepo.save(inscriptionPost); + return UserResponse.responseHandler("Images Uploaded Sucessfully", HttpStatus.OK, true); + } + + @Override + public ResponseEntity getAllPost() { + + List allPost = inscriptionPostRepo.findAll(); + + allPost.forEach(elem -> { + + if (elem.getVisiblity() || elem.getVisiblity() == null) { + elem.setUserName(userRepository.findById(elem.getUserId()).get().getName()); + } + + elem.getImages().setImage(elem.getImages().getImage().stream().map( + el -> backendUrl + "/post/public/images/" + el) + .toList()); + + elem.getImages() + .setThumbnailImage(backendUrl + "/post/public/images/" + elem.getImages().getThumbnailImage()); + + }); + + return UserResponse.responseHandler("All Posts", HttpStatus.OK, allPost); + } + + @Override + public ResponseEntity getImages(String id) { + ImagesData image = imagesDataRepo.findById(id).orElseThrow(); + return ResponseEntity.ok() + .contentType(MediaType.parseMediaType(image.getMetadata().getContentType())) + .body(new InputStreamResource(new ByteArrayInputStream(image.getImageData()))); + } + + @Override + public ResponseEntity getAllUserPost(String usernameFromToken) { + ObjectId postUserId = userRepository.findByEmail(usernameFromToken).getId(); + List userPost = inscriptionPostRepo.findByUserId(postUserId); + + userPost.forEach(elem -> { + + elem.getImages().setImage(elem.getImages().getImage().stream().map( + el -> backendUrl + "/post/public/images/" + el) + .toList()); + + elem.getImages() + .setThumbnailImage(backendUrl + "/post/public/images/" + elem.getImages().getThumbnailImage()); + + }); + + return UserResponse.responseHandler("All Posts", HttpStatus.OK, userPost); + } + + @Override + public ResponseEntity addPoastDiscription(String usernameFromToken, String postId, String discription) { + + User user = userRepository.findByEmail(usernameFromToken); + InscriptionPost post = inscriptionPostRepo.findById(new ObjectId(postId)) + .orElseThrow(() -> new StoneInscriptionException("Unprocesable request", HttpStatus.BAD_REQUEST)); + + ContentModeration moderation = moderateCommentContent(post, discription); + + publicPostDescriptionRepo.save(PublicPostDescription.builder().description(discription) + .moderation(moderation) + .postId(new ObjectId(postId)).userId(user.getId()) + .userImageUrl(user.getProfileImage()).username(user.getName()).build()); + + return UserResponse.responseHandler("Discription Added sucessfully", HttpStatus.OK, true); + } + + @Override + public ResponseEntity getPostDiscription(String postId) { + + return UserResponse.responseHandler("Fetch All Discription for Post", HttpStatus.OK, + publicPostDescriptionRepo.findByPostId(new ObjectId(postId))); + } + + @Override + public ResponseEntity updatePostDiscription(String usernameFromToken, String postId, String discription) { + User user = userRepository.findByEmail(usernameFromToken); + Optional postDiscription = publicPostDescriptionRepo.findById(new ObjectId(postId)); + + if (postDiscription.isEmpty()) { + throw new StoneInscriptionException("Unprocesable request", HttpStatus.BAD_REQUEST); + } + + if (!postDiscription.get().getUserId().equals(user.getId())) { + throw new StoneInscriptionException("Unprocesable request Unauthorized", HttpStatus.UNAUTHORIZED); + } + + InscriptionPost post = inscriptionPostRepo.findById(postDiscription.get().getPostId()) + .orElseThrow(() -> new StoneInscriptionException("Unprocesable request", HttpStatus.BAD_REQUEST)); + ContentModeration moderation = moderateCommentContent(post, discription); + + postDiscription.get().setDescription(discription); + postDiscription.get().setModeration(moderation); + + publicPostDescriptionRepo.save(postDiscription.get()); + + return UserResponse.responseHandler("Updated Discription", HttpStatus.OK, true); + + } + + @Override + public ResponseEntity addRating(String usernameFromToken, String postId, Double rating) { + + User user = userRepository.findByEmail(usernameFromToken); + Optional inscrptionPost = inscriptionPostRepo.findById(new ObjectId(postId)); + + if (inscrptionPost.isEmpty()) { + throw new StoneInscriptionException("Unprocesable request", HttpStatus.BAD_REQUEST); + } + + Optional existingRating = inscrptionPost.get().getUserRating() + .stream() + .filter(r -> r.getUserId().equals(user.getId().toString())) + .findFirst(); + + if (existingRating.isPresent()) { + existingRating.get().setRating(rating); + + } else { + inscrptionPost.get().getUserRating() + .add(InscriptionPost.UsersRating.builder().rating(rating).userId(user.getId().toString()).build()); + } + + inscrptionPost.get().setRating(inscrptionPost.get().getUserRating() + .stream() + .mapToDouble(InscriptionPost.UsersRating::getRating) + .average() + .orElse(0.0)); + + ; + + inscriptionPostRepo.save(inscrptionPost.get()); + return UserResponse.responseHandler("ratting added ", HttpStatus.OK, true); + } + + @Override + public ResponseEntity addVote(String usernameFromToken, String descriptionId) { + User user = userRepository.findByEmail(usernameFromToken); + Optional postDiscription = publicPostDescriptionRepo + .findById(new ObjectId(descriptionId)); + + User postUser = userRepository.findById(postDiscription.get().getUserId()).get(); + + if (postDiscription.get().getUserVote().stream() + .anyMatch(el -> el.getUserId().equals(user.getId().toString()))) { + postDiscription.get().setUserVote(postDiscription.get().getUserVote().stream() + .filter(elm -> !elm.getUserId().equals(user.getId().toString())).toList()); + + postUser.setUpvotesReceived(postUser.getUpvotesReceived() - 1); + ; + + } else { + postDiscription.get().getUserVote() + .add(PublicPostDescription.UserVote.builder().userId(user.getId().toString()).build()); + + postUser.setUpvotesReceived(postUser.getUpvotesReceived() + 1); + } + userRepository.save(postUser); + postDiscription.get().setUpvote(postDiscription.get().getUserVote().size()); + + publicPostDescriptionRepo.save(postDiscription.get()); + return UserResponse.responseHandler("Vote updated", HttpStatus.OK, true); + } + + @Override + public ResponseEntity userProfile(String usernameFromToken) { + return UserResponse.responseHandler("User Profile", HttpStatus.OK, + userRepository.findByEmail(usernameFromToken)); + } + + @Override + public ResponseEntity postDelete(String usernameFromToken, String postId) { + User user = userRepository.findByEmail(usernameFromToken); + + Optional postDelete = inscriptionPostRepo.findById(new ObjectId(postId)); + + if (postDelete.isEmpty()) { + throw new StoneInscriptionException("Unprocesable request", HttpStatus.BAD_REQUEST); + } + if (!user.getId().toString().equals(postDelete.get().getUserId().toString())) { + throw new StoneInscriptionException("Unprocesable request", HttpStatus.BAD_REQUEST); + } + int deletedImageCount = getExistingImageIds(postDelete.get()).size(); + getExistingImageIds(postDelete.get()).forEach(imagesDataRepo::deleteById); + adjustUserImagesUploaded(user, -deletedImageCount); + userRepository.save(user); + + publicPostDescriptionRepo.deleteAllByPostId(postId); + inscriptionPostRepo.deleteById(new ObjectId(postId)); + return UserResponse.responseHandler("post deleted", HttpStatus.OK, true); + } + + @Override + public ResponseEntity descriptionDelete(String usernameFromToken, String descriptionId) { + User user = userRepository.findByEmail(usernameFromToken); + Optional postDiscription = publicPostDescriptionRepo + .findById(new ObjectId(descriptionId)); + + if (postDiscription.isEmpty()) { + throw new StoneInscriptionException("Unprocesable request", HttpStatus.BAD_REQUEST); + } + + if (!postDiscription.get().getUserId().equals(user.getId())) { + throw new StoneInscriptionException("Unprocesable request Unauthorized", HttpStatus.UNAUTHORIZED); + } + + publicPostDescriptionRepo.deleteById(new ObjectId(descriptionId)); + + return UserResponse.responseHandler("description deleted", HttpStatus.OK, true); + } + +// Main function for the updation of the image +// edit post details +// delete some images +// upload new images +// keep at least one image in the post + + @Override + public ResponseEntity updatePost(String usernameFromToken, InscriptionPostDto inscriptionPostDto, + String postId, List deletedImageIds, MultipartFile[] files) { + + InscriptionPost post = getOwnedPost(usernameFromToken, postId); + User user = userRepository.findByEmail(usernameFromToken); + List existingImageIds = getExistingImageIds(post); + List imagesToDelete = validateDeletedImageIds(existingImageIds, deletedImageIds, false); + Set deletableImageIds = new HashSet<>(imagesToDelete); + List newImages = validateAndExtractImages(files, deletableImageIds, false); + + ensureMinimumImageCount(existingImageIds.size(), deletableImageIds.size(), newImages.size()); + + if (inscriptionPostDto != null) { + ContentModeration moderation = moderatePostContent( + resolveTitle(post, inscriptionPostDto), + resolveTopic(post, inscriptionPostDto), + resolveDescription(post, inscriptionPostDto)); + + post.setDescription(mergeDescription(post.getDescription(), inscriptionPostDto.getDescription(), moderation)); + + if (inscriptionPostDto.getScript() != null) { + post.setScript(inscriptionPostDto.getScript()); + } + if (inscriptionPostDto.getTopic() != null) { + post.setTopic(inscriptionPostDto.getTopic()); + } + if (inscriptionPostDto.getType() != null) { + post.setType(inscriptionPostDto.getType()); + } + if (inscriptionPostDto.getVisiblity() != null) { + post.setVisiblity(inscriptionPostDto.getVisiblity()); + } + } + + List updatedImageIds = removeDeletedImageIds(existingImageIds, deletableImageIds); + updatedImageIds.addAll(saveImages(post.getId(), newImages)); + + updatePostImages(post, updatedImageIds, deletableImageIds); + inscriptionPostRepo.save(post); + deleteImagesByIds(deletableImageIds); + adjustUserImagesUploaded(user, newImages.size() - deletableImageIds.size()); + userRepository.save(user); + return UserResponse.responseHandler("post Updated ", HttpStatus.OK, true); + } + + @Override + public ResponseEntity addImagesToPost(String usernameFromToken, String postId, MultipartFile[] files) { + InscriptionPost post = getOwnedPost(usernameFromToken, postId); + User user = userRepository.findByEmail(usernameFromToken); + List newImages = validateAndExtractImages(files, Collections.emptySet(), true); + + List updatedImageIds = getExistingImageIds(post); + updatedImageIds.addAll(saveImages(post.getId(), newImages)); + + updatePostImages(post, updatedImageIds, Collections.emptySet()); + inscriptionPostRepo.save(post); + adjustUserImagesUploaded(user, newImages.size()); + userRepository.save(user); + + return UserResponse.responseHandler("Images Added To Post Successfully", HttpStatus.OK, true); + } + + @Override + public ResponseEntity deleteImagesFromPost(String usernameFromToken, String postId, List deletedImageIds) { + InscriptionPost post = getOwnedPost(usernameFromToken, postId); + User user = userRepository.findByEmail(usernameFromToken); + List existingImageIds = getExistingImageIds(post); + List imagesToDelete = validateDeletedImageIds(existingImageIds, deletedImageIds, true); + Set deletableImageIds = new HashSet<>(imagesToDelete); + + ensureMinimumImageCount(existingImageIds.size(), deletableImageIds.size(), 0); + + List updatedImageIds = removeDeletedImageIds(existingImageIds, deletableImageIds); + updatePostImages(post, updatedImageIds, deletableImageIds); + + inscriptionPostRepo.save(post); + deleteImagesByIds(deletableImageIds); + adjustUserImagesUploaded(user, -deletableImageIds.size()); + userRepository.save(user); + + return UserResponse.responseHandler("Images Deleted From Post Successfully", HttpStatus.OK, true); + } + + @Override + public ResponseEntity getCommentByUser(String usernameFromToken) { + User user = userRepository.findByEmail(usernameFromToken); + + List postDiscription = publicPostDescriptionRepo.findAllByUserId(user.getId()); + + List ls = postDiscription.stream() + .map(el -> { + + PublicPostUserDescriptionDto ref = PublicPostDescriptionMapper.toDto(el); + ref.setPostImageUrl(inscriptionPostRepo.findById(el.getPostId()) + .map(post -> post.getImages().getThumbnailImage()) + .map(imageId -> backendUrl + "/post/public/images/" + imageId) + .orElse(null)); + + return ref; + + }).collect(Collectors.toList()); + + return UserResponse.responseHandler("user all comment ", HttpStatus.OK, ls); + } + + @Override + public ResponseEntity getDashboardCounts() { + + Map counts = new HashMap<>(); + + List allPost = inscriptionPostRepo.findAll(); + + counts.put("totalUsers", Math.toIntExact(userRepository.count())); + counts.put("totalPosts", Math.toIntExact(inscriptionPostRepo.count())); + counts.put("totalImages", Math.toIntExact(imagesDataRepo.count())); + counts.put("totalGeoTaggedPosts", + (int) allPost.stream().filter(el -> el.getDescription().getGeolocation() != null).count()); + counts.put("totalTranslations", (int) allPost.stream() + .map(InscriptionPost::getDescription) + .filter(Objects::nonNull) + .map(InscriptionPost.Description::getEnglishTranslation) + .filter(Objects::nonNull) + .map(String::trim) + .filter(translation -> !translation.isEmpty()) + .count()); + + return UserResponse.responseHandler("Dashboard Counts", HttpStatus.OK, counts); + } + + private ContentModeration moderatePostContent(String title, String topic, String description) { + return moderateContent(title, topic, description); + } + + private ContentModeration moderateCommentContent(InscriptionPost post, String description) { + return moderateContent(extractTitle(post), post.getTopic(), description); + } + + private ContentModeration moderateContent(String title, String topic, String description) { + ContentModerationResult moderationResult = contentModerationService.moderate(title, topic, description); + + if (!moderationResult.isApproved()) { + throw new StoneInscriptionException(contentModerationService.buildRejectionMessage(moderationResult), + HttpStatus.BAD_REQUEST); + } + + return moderationResult.toContentModeration(); + } + + private String resolveTitle(InscriptionPost post, InscriptionPostDto inscriptionPostDto) { + if (inscriptionPostDto != null && inscriptionPostDto.getDescription() != null + && inscriptionPostDto.getDescription().getTitle() != null) { + return inscriptionPostDto.getDescription().getTitle(); + } + + return extractTitle(post); + } + + private String resolveTopic(InscriptionPost post, InscriptionPostDto inscriptionPostDto) { + if (inscriptionPostDto != null && inscriptionPostDto.getTopic() != null) { + return inscriptionPostDto.getTopic(); + } + + return post.getTopic(); + } + + private String resolveDescription(InscriptionPost post, InscriptionPostDto inscriptionPostDto) { + if (inscriptionPostDto != null && inscriptionPostDto.getDescription() != null + && inscriptionPostDto.getDescription().getDescription() != null) { + return inscriptionPostDto.getDescription().getDescription(); + } + + return extractDescription(post); + } + + private String extractTitle(InscriptionPost post) { + if (post == null || post.getDescription() == null) { + return null; + } + + return post.getDescription().getTitle(); + } + + private String extractDescription(InscriptionPost post) { + if (post == null || post.getDescription() == null) { + return null; + } + + return post.getDescription().getDescription(); + } + + private InscriptionPost.Description mergeDescription(InscriptionPost.Description existingDescription, + InscriptionPostDto.DescriptionDto incomingDescription, ContentModeration moderation) { + if (existingDescription == null) { + existingDescription = InscriptionPost.Description.builder().upvote(0).build(); + } + + if (incomingDescription == null) { + existingDescription.setModeration(moderation); + return existingDescription; + } + + return InscriptionPost.Description.builder() + .title(incomingDescription.getTitle() != null ? incomingDescription.getTitle() : existingDescription.getTitle()) + .subject(incomingDescription.getSubject() != null ? incomingDescription.getSubject() : existingDescription.getSubject()) + .description(incomingDescription.getDescription() != null ? incomingDescription.getDescription() : existingDescription.getDescription()) + .scriptLanguage(incomingDescription.getScriptLanguage() != null ? incomingDescription.getScriptLanguage() : existingDescription.getScriptLanguage()) + .language(incomingDescription.getLanguage() != null ? incomingDescription.getLanguage() : existingDescription.getLanguage()) + .englishTranslation(existingDescription.getEnglishTranslation()) + .moderation(moderation) + .upvote(existingDescription.getUpvote() == null ? 0 : existingDescription.getUpvote()) + .geolocation(existingDescription.getGeolocation()) + .createdAt(existingDescription.getCreatedAt()) + .updatedAt(existingDescription.getUpdatedAt()) + .build(); + } + + private List validateAndExtractImages(MultipartFile[] files, Set replaceableImageIds, + boolean filesRequired) { + if (files == null || files.length == 0) { + if (filesRequired) { + throw new StoneInscriptionException("No File Uploaded", HttpStatus.BAD_REQUEST); + } + + return List.of(); + } + + List ls = metadataGeolocationWithPhash.getGeoLocationWithIamgeMetaandInfo(files); + + if (ls.size() == 0) { + throw new StoneInscriptionException("No Valid Image Found in the Request", HttpStatus.BAD_REQUEST); + } + + if (ls.size() != ls.stream().map(ImageMetaAndInfo::getPHash).distinct().count()) { + throw new StoneInscriptionException("Duplicate Image Uploaded", HttpStatus.BAD_REQUEST); + } + + boolean imageAlreadyExists = ls.stream().anyMatch(image -> { + Optional existingImage = imagesDataRepo + .findFirstByMetadata_ImageHashValue(image.getPHash().getHashValue().toString()); + return existingImage.isPresent() && !replaceableImageIds.contains(existingImage.get().getId()); + }); + + if (imageAlreadyExists) { + throw new StoneInscriptionException("Image Already Uploaded By some User", HttpStatus.CONFLICT); + } + + return ls; + } + + private List saveImages(ObjectId postId, List images) { + return images.stream().map(image -> imagesDataRepo.save(ImagesData.builder() + .imageData(image.getFile()) + .postId(postId) + .metadata(ImagesData.Metadata.builder() + .fileName(image.getFileName()) + .fileSize(image.getFileSize()) + .contentType(image.getContentType()) + .imageHashValue(image.getPHash().getHashValue().toString()) + .build()) + .build()).getId()).toList(); + } + + private InscriptionPost getOwnedPost(String usernameFromToken, String postId) { + User user = userRepository.findByEmail(usernameFromToken); + Optional inscriptionPost = inscriptionPostRepo.findById(new ObjectId(postId)); + + if (inscriptionPost.isEmpty()) { + throw new StoneInscriptionException("Unprocesable request", HttpStatus.BAD_REQUEST); + } + + if (!user.getId().equals(inscriptionPost.get().getUserId())) { + throw new StoneInscriptionException("Forbidden", HttpStatus.FORBIDDEN); + } + + return inscriptionPost.get(); + } + + private List getExistingImageIds(InscriptionPost post) { + if (post.getImages() == null || post.getImages().getImage() == null) { + return new LinkedList<>(); + } + + return new LinkedList<>(post.getImages().getImage()); + } + + private List validateDeletedImageIds(List existingImageIds, List deletedImageIds, + boolean deletionRequired) { + List imagesToDelete = sanitizeImageIds(deletedImageIds); + + if (deletionRequired && imagesToDelete.isEmpty()) { + throw new StoneInscriptionException("No image selected for deletion", HttpStatus.BAD_REQUEST); + } + + if (!existingImageIds.containsAll(imagesToDelete)) { + throw new StoneInscriptionException("Invalid image selected for deletion", HttpStatus.BAD_REQUEST); + } + + return imagesToDelete; + } + + private void ensureMinimumImageCount(int existingImageCount, int deletedImageCount, int newImageCount) { + int finalImageCount = existingImageCount - deletedImageCount + newImageCount; + + if (finalImageCount < 1) { + throw new StoneInscriptionException("Post should have at least one image", HttpStatus.BAD_REQUEST); + } + } + + private List removeDeletedImageIds(List existingImageIds, Set deletableImageIds) { + return existingImageIds.stream() + .filter(imageId -> !deletableImageIds.contains(imageId)) + .collect(Collectors.toCollection(LinkedList::new)); + } + + private void updatePostImages(InscriptionPost post, List updatedImageIds, Set removedImageIds) { + if (post.getImages() == null) { + post.setImages(InscriptionPost.Images.builder().build()); + } + + post.getImages().setImage(updatedImageIds); + + if (post.getImages().getThumbnailImage() == null + || removedImageIds.contains(post.getImages().getThumbnailImage())) { + post.getImages().setThumbnailImage(updatedImageIds.get(0)); + } + } + + private void deleteImagesByIds(Set imageIds) { + imageIds.forEach(imagesDataRepo::deleteById); + } + + private void adjustUserImagesUploaded(User user, int delta) { + int currentCount = user.getImagesUploaded() == null ? 0 : user.getImagesUploaded(); + user.setImagesUploaded(Math.max(0, currentCount + delta)); + } + + private List sanitizeImageIds(List imageIds) { + if (imageIds == null) { + return List.of(); + } + + return imageIds.stream() + .filter(Objects::nonNull) + .map(String::trim) + .filter(id -> !id.isEmpty()) + .distinct() + .toList(); + } + +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 4715147..a784351 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -64,3 +64,15 @@ spring.servlet.multipart.max-request-size=75MB management.endpoints.web.exposure.include=health,info,prometheus management.endpoint.prometheus.enabled=true management.metrics.export.prometheus.enabled=true + +# content.moderation.webhook-url=${CONTENT_MODERATION_WEBHOOK_URL} +# content.moderation.safe-threshold=${CONTENT_MODERATION_SAFE_THRESHOLD:0.7} +# content.moderation.connect-timeout-ms=${CONTENT_MODERATION_CONNECT_TIMEOUT_MS:5000} +# content.moderation.read-timeout-ms=${CONTENT_MODERATION_READ_TIMEOUT_MS:10000} +# content.moderation.insecure-ssl=${CONTENT_MODERATION_INSECURE_SSL:false} + +content.moderation.webhook-url=${CONTENT_MODERATION_WEBHOOK_URL} +content.moderation.safe-threshold=${CONTENT_MODERATION_SAFE_THRESHOLD} +content.moderation.connect-timeout-ms=${CONTENT_MODERATION_CONNECT_TIMEOUT_MS} +content.moderation.read-timeout-ms=${CONTENT_MODERATION_READ_TIMEOUT_MS} +content.moderation.insecure-ssl=${CONTENT_MODERATION_INSECURE_SSL}