Skip to content

Commit

Permalink
Merge pull request #61 from kko-bugi/develop
Browse files Browse the repository at this point in the history
[develop] Sprint #1 main merge
  • Loading branch information
JoongHyun-Kim authored Feb 6, 2024
2 parents 3bbcc62 + add4dd0 commit be7e2b3
Show file tree
Hide file tree
Showing 60 changed files with 2,625 additions and 0 deletions.
8 changes: 8 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,17 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0'
implementation 'io.jsonwebtoken:jjwt:0.9.1'
implementation 'javax.xml.bind:jaxb-api:2.4.0-b180830.0359'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter', version: '1.2.5.RELEASE'
implementation group: 'org.springframework.cloud', name: 'spring-cloud-gcp-storage', version: '1.2.5.RELEASE'

compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.mysql:mysql-connector-j'
annotationProcessor 'org.projectlombok:lombok'

testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

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

@EnableJpaAuditing
@SpringBootApplication
public class PuremarketApplication {

Expand Down
29 changes: 29 additions & 0 deletions src/main/java/com/kkobugi/puremarket/common/BaseEntity.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.kkobugi.puremarket.common;

import jakarta.persistence.Column;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.MappedSuperclass;
import lombok.Getter;
import lombok.Setter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import java.time.LocalDate;

@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class BaseEntity {

@Column(columnDefinition = "varchar(10) default 'active'")
@Setter
private String status;

@CreatedDate
@Column(updatable = false)
private LocalDate createdDate;

@LastModifiedDate
private LocalDate lastModifiedDate;
}
14 changes: 14 additions & 0 deletions src/main/java/com/kkobugi/puremarket/common/BaseException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.kkobugi.puremarket.common;

import com.kkobugi.puremarket.common.enums.BaseResponseStatus;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@AllArgsConstructor
public class BaseException extends Exception {
private BaseResponseStatus status;
}

45 changes: 45 additions & 0 deletions src/main/java/com/kkobugi/puremarket/common/BaseResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.kkobugi.puremarket.common;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.kkobugi.puremarket.common.enums.BaseResponseStatus;
import lombok.AllArgsConstructor;
import lombok.Getter;

import static com.kkobugi.puremarket.common.enums.BaseResponseStatus.SUCCESS;

@Getter
@AllArgsConstructor
@JsonPropertyOrder({"isSuccess", "code", "message", "result"})
public class BaseResponse<T> {

@JsonProperty("isSuccess")
private final Boolean isSuccess;
private final String message;
private final int code;

@JsonInclude(JsonInclude.Include.NON_NULL)
private T result;

public BaseResponse(T result) {
this.isSuccess = SUCCESS.isSuccess();
this.message = SUCCESS.getMessage();
this.code = SUCCESS.getCode();
this.result = result;
}

public BaseResponse(BaseResponseStatus status) {
this.isSuccess = status.isSuccess();
this.message = status.getMessage();
this.code = status.getCode();
}

// 요청은 성공했지만 데이터에 이상이 있는 경우
public BaseResponse(T result, BaseResponseStatus status) {
this.isSuccess = status.isSuccess();
this.message = status.getMessage();
this.code = status.getCode();
this.result = result;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.kkobugi.puremarket.common.configuration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
public class AppConfig {

@Bean
public BCryptPasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.kkobugi.puremarket.common.configuration;

import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
@EnableRedisRepositories
@RequiredArgsConstructor
public class RedisConfig {

@Value("${spring.data.redis.host}")
private String host;

@Value("${spring.data.redis.port}")
private int port;

@Bean
public RedisConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory(host, port);
}

// setKeySerializer, setValueSerializer 설정으로 redis-cli를 통해 직접 데이터를 보는게 가능하다.
@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.setConnectionFactory(redisConnectionFactory());
return redisTemplate;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.kkobugi.puremarket.common.configuration;

import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.info.Info;
import io.swagger.v3.oas.models.OpenAPI;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@OpenAPIDefinition(info = @Info(title = "Pure Market API", description = "2024 Google Solution Challenge Pure Market Project", version = "v1"))
public class SwaggerConfig {

@Bean
public OpenAPI openAPI() {
return new OpenAPI();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.kkobugi.puremarket.common.configuration;

import com.kkobugi.puremarket.user.application.AuthService;
import com.kkobugi.puremarket.user.application.UserService;
import com.kkobugi.puremarket.user.utils.JwtTokenFilter;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
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.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class WebSecurityConfig {
private final UserService userService;
private final AuthService authService;
private final RedisTemplate<String, String> redisTemplate;

@Bean
protected SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
return httpSecurity
.csrf(AbstractHttpConfigurer::disable)
.sessionManagement((sessionManagement) ->
sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests((authorizeRequests) -> authorizeRequests
.requestMatchers(
new AntPathRequestMatcher("/api/v1/users/login"),
new AntPathRequestMatcher("/api/v1/users/signup"),
new AntPathRequestMatcher("/api/v1/users/nickname"),
new AntPathRequestMatcher("/api/v1/users/loginId"),
new AntPathRequestMatcher("/api/v1/users/reissue-token")).permitAll()
.anyRequest().authenticated()) //TODO: 마이페이지 접근 권한 설정 추가
.addFilterBefore(new JwtTokenFilter(authService, userService, redisTemplate), UsernamePasswordAuthenticationFilter.class) // 필터 통과
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.kkobugi.puremarket.common.constants;

public class Constant {
public static final String ACTIVE = "active";
public static final String INACTIVE = "inactive"; // 탈퇴
public static final String LOGOUT = "logout"; // 로그아웃

public static class Produce {
public static final String FOR_SALE = "판매중";
public static final String SOLD_OUT= "판매완료";
}

public static class Giveaway {
public static final String GIVEAWAY = "나눔중";
public static final String DONE = "나눔완료";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.kkobugi.puremarket.common.constants;

public class RequestURI {
private final static String baseURI = "/api/v1";
public final static String user = baseURI + "/users";
public final static String produce = baseURI + "/produce";
public final static String giveaway = baseURI + "/giveaway";
public final static String recipe = baseURI + "/recipe";
public final static String home = baseURI + "/home";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package com.kkobugi.puremarket.common.enums;

import lombok.Getter;

@Getter
public enum BaseResponseStatus {
/**
* 1000: 요청 성공
*/
SUCCESS(true, 1000, "요청에 성공하였습니다."),

/**
* 2000: Request 오류
*/
// users(2000-2099)
DUPLICATED_LOGIN_ID(false, 2000, "이미 사용중인 아이디입니다."),
DUPLICATED_NICKNAME(false, 2001, "이미 사용중인 닉네임입니다."),
UNMATCHED_PASSWORD(false, 2002, "비밀번호가 일치하지 않습니다."),
INVALID_PASSWORD(false, 2003, "비밀번호가 잘못되었습니다."),
INVALID_LOGIN_ID(false, 2004, "잘못된 아이디입니다."),
INVALID_ACCESS_TOKEN(false, 2005, "잘못된 AccessToken 입니다."),
INVALID_REFRESH_TOKEN(false, 2006, "잘못된 Refresh Token 입니다."),
NULL_ACCESS_TOKEN(false, 2007, "Access Token을 입력해주세요."),

// produce(2100-2199)
INVALID_PRODUCE_IDX(false, 2100, "잘못된 판매글 idx 입니다."),
TITLE_EXCEEDED_MAX_LIMIT(false, 2101, "제목은 32자 이하여야 합니다."),
NO_PRODUCE_WRITER(false, 2102, "해당 판매글의 작성자가 아닙니다."),
ALREADY_DELETED_PRODUCE(false, 2103, "이미 삭제된 판매글입니다."),

// recipe(2200-2299)
INVALID_RECIPE_IDX(false, 2200, "잘못된 레시피글 idx 입니다."),
NO_RECIPE_WRITER(false, 2201, "해당 레시피글의 작성자가 아닙니다."),
ALREADY_DELETED_RECIPE(false, 2202, "이미 삭제된 레시피글입니다."),

// ingredient(2300-2399)

// giveaway(2400-2499)
INVALID_GIVEAWAY_IDX(false, 2400, "잘못된 나눔글 idx 입니다."),
NO_GIVEAWAY_WRITER(false, 2401, "해당 나눔글의 작성자가 아닙니다."),
ALREADY_DELETED_GIVEAWAY(false, 2402, "이미 삭제된 나눔글입니다."),


/**
* 3000: Response 오류
*/
// users(3000~3099)
INVALID_USER_IDX(false, 3000, "user idx가 잘못 되었습니다."),
NO_MATCH_USER(false, 3001, "해당 user를 찾을 수 없습니다."),

// produce(3100-3199)
NULL_PRODUCE_LIST(false, 3100, "판매글 목록이 비었습니다."),

// recipe(3200-3299)

// ingredient(3300-3399)

// giveaway(3400-3499)
NULL_GIVEAWAY_LIST(false, 3400, "나눔글 목록이 비었습니다."),


/**
* 4000: DB, Server 오류
*/
DATABASE_ERROR(false, 4000, "데이터베이스 연결에 실패했습니다.");

private final boolean isSuccess;
private final int code;
private final String message;

BaseResponseStatus(boolean isSuccess, int code, String message) {
this.isSuccess = isSuccess;
this.code = code;
this.message = message;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.kkobugi.puremarket.common.enums;

import lombok.Getter;

@Getter
public enum IngredientType {
INGREDIENT,
SAUCE
}
36 changes: 36 additions & 0 deletions src/main/java/com/kkobugi/puremarket/common/gcs/GCSConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.kkobugi.puremarket.common.gcs;

import com.google.auth.oauth2.GoogleCredentials;
import com.google.cloud.storage.Storage;
import com.google.cloud.storage.StorageOptions;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;

import java.io.IOException;

@Configuration
public class GCSConfig {
@Value("${spring.cloud.gcp.storage.project-id}")
private String projectId;

@Value("${spring.cloud.gcp.storage.credentials.location}")
private String location;

public GCSConfig() {
}

@Bean
public Storage storage() throws IOException {

String classPath = location.substring(10);
ClassPathResource resource = new ClassPathResource(classPath);
GoogleCredentials credentials = GoogleCredentials.fromStream(resource.getInputStream());
return StorageOptions.newBuilder()
.setProjectId(projectId)
.setCredentials(credentials)
.build()
.getService();
}
}
Loading

0 comments on commit be7e2b3

Please sign in to comment.