Skip to content

Commit

Permalink
Merge pull request #10 from donggukthonN/feature/#7-photos
Browse files Browse the repository at this point in the history
Feature/#7 photos
  • Loading branch information
khoon9 authored Dec 19, 2023
2 parents 540cdf7 + 467cfa8 commit 3fba572
Show file tree
Hide file tree
Showing 16 changed files with 536 additions and 3 deletions.
5 changes: 4 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ dependencies {
runtimeOnly 'com.mysql:mysql-connector-j'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'

// spring cloud
implementation 'io.awspring.cloud:spring-cloud-starter-aws:2.4.4'
// security
implementation 'org.springframework.boot:spring-boot-starter-security'
// google cloud vision api
implementation 'com.google.cloud:spring-cloud-gcp-starter-vision:4.9.0'
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@SpringBootApplication
@SpringBootApplication(exclude = SecurityAutoConfiguration.class)
@EnableJpaAuditing
public class ServerApplication {

public static void main(String[] args) {
Expand Down
34 changes: 34 additions & 0 deletions src/main/java/com/snowmanvillage/server/config/S3Config.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.snowmanvillage.server.config;

import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class S3Config {

@Value("${cloud.aws.credentials.accessKey}")
private String accessKey;

@Value("${cloud.aws.credentials.secretKey}")
private String secretKey;

@Value("${cloud.aws.region.static}")
private String region;

@Bean
public AmazonS3Client amazonS3Client() {
AWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);

return (AmazonS3Client) AmazonS3ClientBuilder
.standard()
.withCredentials(new AWSStaticCredentialsProvider(credentials))
.withRegion(region)
.build();
}
}
50 changes: 50 additions & 0 deletions src/main/java/com/snowmanvillage/server/config/SecurityConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.snowmanvillage.server.config;

import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;
import org.springframework.security.config.annotation.web.configurers.HttpBasicConfigurer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

@EnableWebSecurity
@Configuration
@RequiredArgsConstructor
public class SecurityConfig {

@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(CsrfConfigurer::disable)
.cors(Customizer.withDefaults())
.httpBasic(HttpBasicConfigurer::disable);
return http.build();
}

@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.addExposedHeader("Authorization");
configuration.addAllowedOriginPattern("*");
configuration.addAllowedHeader("*");
configuration.addAllowedMethod("*");
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package com.snowmanvillage.server.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.snowmanvillage.server.dto.PhotoLikeRequestDto;
import com.snowmanvillage.server.dto.PhotoRequestDto;
import com.snowmanvillage.server.dto.PhotoResponseDto;
import com.snowmanvillage.server.dto.PhotoUploadRequestDto;
import com.snowmanvillage.server.entity.Photo;
import com.snowmanvillage.server.repository.PhotoRepository;
import com.snowmanvillage.server.service.PasswordBCryptService;
import com.snowmanvillage.server.service.PhotoService;
import jakarta.servlet.http.HttpServletRequest;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
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.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

@RestController
@RequestMapping("/photo")
@RequiredArgsConstructor
public class PhotoController {

private final PhotoService photoService;
private final PasswordBCryptService passwordBCryptService;
private final ObjectMapper objectMapper;
private final PhotoRepository photoRepository;

@PostMapping(value = "", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<String> uploadPhoto(HttpServletRequest httpServletRequest) {
if (!(httpServletRequest instanceof MultipartHttpServletRequest)) {
throw new IllegalArgumentException("MultipartHttpServletRequest is not valid");
}
try {
MultipartFile image = ((MultipartHttpServletRequest) httpServletRequest).getFile("image");
String request = httpServletRequest.getParameter("request");
PhotoUploadRequestDto requestDto = objectMapper.readValue(request, PhotoUploadRequestDto.class);
String encodedPassword = passwordBCryptService.encodePassword(requestDto.getPassword());
Photo savedPhoto = photoService.uploadPhoto(image, requestDto);
savedPhoto.setPassword(encodedPassword);
photoRepository.save(savedPhoto);
return ResponseEntity.ok("포토 등록 완료");
} catch (Exception e) {
return ResponseEntity.ok("포토 등록 실패");
}
}

@GetMapping("/{photoId}")
public ResponseEntity<PhotoResponseDto> getPhoto(@PathVariable(name = "photoId") Long photoId) {
return ResponseEntity.ok(photoService.getPhotoByPhotoId(photoId));
}

@GetMapping("/list")
public ResponseEntity<List<PhotoResponseDto>> getPhotoList(@RequestParam(name = "orderBy") String orderBy) {
return ResponseEntity.ok(photoService.getPhotoList(orderBy));
}

@PostMapping("/delete/{photoId}")
public ResponseEntity<String> deletePhoto(@PathVariable(name = "photoId") Long photoId, @RequestBody PhotoRequestDto requestDto) {
try {
return ResponseEntity.ok(photoService.deletePhoto(photoId, requestDto.getPassword()));
} catch (Exception e) {
return ResponseEntity.ok("포토 삭제 실패");
}
}

@PutMapping("/like")
public ResponseEntity<PhotoResponseDto> likePhoto(@RequestBody PhotoLikeRequestDto requestDto) {
return ResponseEntity.ok(photoService.likePhoto(requestDto.getPhoto_id()));
}

@PutMapping("/unlike")
public ResponseEntity<PhotoResponseDto> unlikePhoto(@RequestBody PhotoLikeRequestDto requestDto) {
return ResponseEntity.ok(photoService.unlikePhoto(requestDto.getPhoto_id()));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.snowmanvillage.server.dto;

import lombok.Getter;

@Getter
public class PhotoLikeRequestDto {
private Long photo_id;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.snowmanvillage.server.dto;

import lombok.Getter;

@Getter
public class PhotoRequestDto {
private String password;
}
37 changes: 37 additions & 0 deletions src/main/java/com/snowmanvillage/server/dto/PhotoResponseDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.snowmanvillage.server.dto;

import com.snowmanvillage.server.entity.Photo;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Builder
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class PhotoResponseDto {
private Long id;
private String imageUrl;
private String title;
private String username;
private Integer likeCount;

@Builder
public static PhotoResponseDto of(Photo photo) {
return PhotoResponseDto.builder()
.id(photo.getId())
.imageUrl(photo.getFilePath())
.title(photo.getTitle())
.username(photo.getUsername())
.likeCount(photo.getLikeCount())
.build();
}

public static List<PhotoResponseDto> listOf(List<Photo> photoList) {
return photoList.stream()
.map(PhotoResponseDto::of)
.toList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.snowmanvillage.server.dto;

import com.snowmanvillage.server.entity.Photo;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@AllArgsConstructor
@NoArgsConstructor
public class PhotoUploadRequestDto {

private String title;
private String username;
private String password;

public Photo toEntity(String photoUrl, PhotoUploadRequestDto requestDto) {
return Photo.builder()
.title(requestDto.getTitle())
.username(requestDto.getUsername())
.password(requestDto.getPassword())
.filePath(photoUrl)
.build();
}
}
51 changes: 51 additions & 0 deletions src/main/java/com/snowmanvillage/server/entity/Photo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.snowmanvillage.server.entity;

import com.snowmanvillage.server.entity.global.BaseTimeEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.web.ErrorResponse;

@Getter
@Entity
@NoArgsConstructor
public class Photo extends BaseTimeEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name = "file_path")
private String filePath;

private String title;

private String username;

private String password;

@Column(name = "like_count")
private Integer likeCount;

@Builder
public Photo(String filePath, String title, String username, String password) {
this.filePath = filePath;
this.title = title;
this.username = username;
this.password = password;
this.likeCount = 0;
}

public void setPassword(String encodedPassword) {
this.password = encodedPassword;
}

public void setLikeCount(int likeCount) {
this.likeCount = likeCount;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.snowmanvillage.server.entity.global;

import jakarta.persistence.EntityListeners;
import jakarta.persistence.MappedSuperclass;
import java.time.LocalDateTime;
import lombok.Getter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseTimeEntity {

@CreatedDate
private LocalDateTime createdDate;

@LastModifiedDate
private LocalDateTime modifiedDate;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.snowmanvillage.server.repository;

import com.snowmanvillage.server.entity.Photo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface PhotoRepository extends JpaRepository<Photo, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.snowmanvillage.server.service;

import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class PasswordBCryptService {

private final PasswordEncoder passwordEncoder;

public String encodePassword(String password) {
return passwordEncoder.encode(password);
}

public boolean matchPassword(String password, String encodedPassword) {
return passwordEncoder.matches(password, encodedPassword);
}
}
Loading

0 comments on commit 3fba572

Please sign in to comment.