diff --git a/Api/build.gradle b/Api/build.gradle index d074cd6..0ac1962 100644 --- a/Api/build.gradle +++ b/Api/build.gradle @@ -27,8 +27,7 @@ dependencies { test { useJUnitPlatform() } - bootJar{ archivesBaseName = 'app' archiveFileName = 'app.jar' -} \ No newline at end of file +} diff --git a/Api/src/main/java/playlist/server/config/security/SecurityUtils.java b/Api/src/main/java/playlist/server/config/security/SecurityUtils.java new file mode 100644 index 0000000..331133c --- /dev/null +++ b/Api/src/main/java/playlist/server/config/security/SecurityUtils.java @@ -0,0 +1,33 @@ +package playlist.server.config.security; + + +import java.util.List; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.util.CollectionUtils; +import playlist.server.exception.SecurityContextNotFoundException; + +public class SecurityUtils { + + private static SimpleGrantedAuthority swagger = new SimpleGrantedAuthority("ROLE_SWAGGER"); + + private static List notUserAuthority = List.of(swagger); + + public static Long getCurrentUserId() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication == null) { + throw SecurityContextNotFoundException.EXCEPTION; + } + + if (authentication.isAuthenticated() + && !CollectionUtils.containsAny( + authentication.getAuthorities(), notUserAuthority)) { + return authentication.getName().equals("anonymousUser") + ? 0L + : Long.valueOf(authentication.getName()); + } + // 스웨거 유저일시 유저 아이디 0 반환 + return 0L; + } +} diff --git a/Api/src/main/java/playlist/server/user/controller/UserController.java b/Api/src/main/java/playlist/server/user/controller/UserController.java new file mode 100644 index 0000000..89f8f63 --- /dev/null +++ b/Api/src/main/java/playlist/server/user/controller/UserController.java @@ -0,0 +1,32 @@ +package playlist.server.user.controller; + + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import playlist.server.user.model.dto.UserCreateDTO; +import playlist.server.user.service.devLoginService; + +@RestController +@RequestMapping("/users") +@RequiredArgsConstructor +@Slf4j +public class UserController { + + private final devLoginService devLoginService; + + @GetMapping("/create") + public void createDevUser(@RequestParam String email, @RequestParam String password) { + log.info("email, password : " + email + " " + password); + + UserCreateDTO userCreateDTO = new UserCreateDTO(email, password); + devLoginService.execute(userCreateDTO); + + // model.addAttribute("email", email); + // model.addAttribute("password",password); + + } +} diff --git a/Api/src/main/java/playlist/server/user/model/dto/UserCreateDTO.java b/Api/src/main/java/playlist/server/user/model/dto/UserCreateDTO.java new file mode 100644 index 0000000..ea034c5 --- /dev/null +++ b/Api/src/main/java/playlist/server/user/model/dto/UserCreateDTO.java @@ -0,0 +1,16 @@ +package playlist.server.user.model.dto; + + +import lombok.Getter; + +@Getter +public class UserCreateDTO { + private final String email; + private final String password; + + // @QueryProjection + public UserCreateDTO(String email, String password) { + this.email = email; + this.password = password; + } +} diff --git a/Api/src/main/java/playlist/server/user/service/devLoginService.java b/Api/src/main/java/playlist/server/user/service/devLoginService.java new file mode 100644 index 0000000..dfddd75 --- /dev/null +++ b/Api/src/main/java/playlist/server/user/service/devLoginService.java @@ -0,0 +1,27 @@ +package playlist.server.user.service; + + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import playlist.server.domain.domains.auth.adaptor.UserAdaptor; +import playlist.server.domain.domains.auth.domain.AccountRole; +import playlist.server.domain.domains.auth.domain.User; +import playlist.server.user.model.dto.UserCreateDTO; + +@Service +@RequiredArgsConstructor +@Transactional +public class devLoginService { + private final UserAdaptor userAdaptor; + + public void execute(UserCreateDTO userCreateDTO) { + User user = + User.builder() + .email(userCreateDTO.getEmail()) + .password(userCreateDTO.getPassword()) + .role(AccountRole.ADMIN) + .build(); + userAdaptor.save(user); + } +} diff --git a/Api/src/main/java/playlist/server/utils/UserUtils.java b/Api/src/main/java/playlist/server/utils/UserUtils.java new file mode 100644 index 0000000..43dbd62 --- /dev/null +++ b/Api/src/main/java/playlist/server/utils/UserUtils.java @@ -0,0 +1,21 @@ +package playlist.server.utils; + + +import lombok.RequiredArgsConstructor; +import playlist.server.config.security.SecurityUtils; +import playlist.server.domain.domains.auth.adaptor.UserAdaptor; +import playlist.server.domain.domains.auth.domain.User; + +@RequiredArgsConstructor +public class UserUtils { + + private final UserAdaptor userAdaptor; + + public Long getUserId() { + return SecurityUtils.getCurrentUserId(); + } + + public User getUser() { + return userAdaptor.query(SecurityUtils.getCurrentUserId()); + } +} diff --git a/Core/src/main/java/playlist/server/exception/GlobalException.java b/Core/src/main/java/playlist/server/exception/GlobalException.java index 175cce7..ee258b2 100644 --- a/Core/src/main/java/playlist/server/exception/GlobalException.java +++ b/Core/src/main/java/playlist/server/exception/GlobalException.java @@ -19,7 +19,8 @@ public enum GlobalException implements BaseErrorCode { EXPIRED_REFRESH_TOKEN_ERROR(UNAUTHORIZED.value(), "401-1", "리프레시 토큰이 만료되었습니다."), INVALID_TOKEN_ERROR(UNAUTHORIZED.value(), "401-2", "올바르지 않은 토큰입니다."), DATE_FORMAT_ERROR(BAD_REQUEST.value(), "400-2", "날짜 형식을 확인해주세요."), - ; + SECURITY_CONTEXT_NOT_FOUND_ERROR( + INTERNAL_SERVER_ERROR.value(), "500-2", "Security Context 에러입니다."); private final Integer statusCode; private final String errorCode; diff --git a/Core/src/main/java/playlist/server/exception/SecurityContextNotFoundException.java b/Core/src/main/java/playlist/server/exception/SecurityContextNotFoundException.java new file mode 100644 index 0000000..2a88cf6 --- /dev/null +++ b/Core/src/main/java/playlist/server/exception/SecurityContextNotFoundException.java @@ -0,0 +1,10 @@ +package playlist.server.exception; + +public class SecurityContextNotFoundException extends BaseException { + + public static final BaseException EXCEPTION = new SecurityContextNotFoundException(); + + private SecurityContextNotFoundException() { + super(GlobalException.SECURITY_CONTEXT_NOT_FOUND_ERROR); + } +} diff --git a/Domain/src/main/java/playlist/server/domain/domains/AbstractTimeStamp.java b/Domain/src/main/java/playlist/server/domain/domains/AbstractTimeStamp.java new file mode 100644 index 0000000..3ecae9f --- /dev/null +++ b/Domain/src/main/java/playlist/server/domain/domains/AbstractTimeStamp.java @@ -0,0 +1,34 @@ +package playlist.server.domain.domains; + + +import com.fasterxml.jackson.annotation.JsonFormat; +import jakarta.persistence.Column; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.MappedSuperclass; +import java.sql.Timestamp; +import lombok.Getter; +import org.hibernate.annotations.CreationTimestamp; +import org.hibernate.annotations.UpdateTimestamp; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +@Getter +@MappedSuperclass +@EntityListeners(value = {AuditingEntityListener.class}) +public abstract class AbstractTimeStamp { + + @Column( + name = "created_at", + nullable = false, + columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP") + @CreationTimestamp + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss") + private Timestamp createdAt; + + @Column( + name = "updated_at", + nullable = false, + columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP") + @UpdateTimestamp + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss") + private Timestamp updatedAt; +} diff --git a/Domain/src/main/java/playlist/server/domain/domains/auth/Exception/UserException.java b/Domain/src/main/java/playlist/server/domain/domains/auth/Exception/UserException.java new file mode 100644 index 0000000..c4a5dfc --- /dev/null +++ b/Domain/src/main/java/playlist/server/domain/domains/auth/Exception/UserException.java @@ -0,0 +1,21 @@ +package playlist.server.domain.domains.auth.Exception; + +import static org.springframework.http.HttpStatus.*; + +import lombok.RequiredArgsConstructor; +import playlist.server.dto.ErrorDetail; +import playlist.server.exception.BaseErrorCode; + +@RequiredArgsConstructor +public enum UserException implements BaseErrorCode { + USER_NOT_FOUND_ERROR(NOT_FOUND.value(), "User_404_1", "유저를 찾을 수 없습니다."); + + private final Integer statusCode; + private final String errorCode; + private final String reason; + + @Override + public ErrorDetail getErrorDetail() { + return ErrorDetail.of(statusCode, errorCode, reason); + } +} diff --git a/Domain/src/main/java/playlist/server/domain/domains/auth/Exception/UserNotFoundException.java b/Domain/src/main/java/playlist/server/domain/domains/auth/Exception/UserNotFoundException.java new file mode 100644 index 0000000..69e1621 --- /dev/null +++ b/Domain/src/main/java/playlist/server/domain/domains/auth/Exception/UserNotFoundException.java @@ -0,0 +1,12 @@ +package playlist.server.domain.domains.auth.Exception; + + +import playlist.server.exception.BaseException; + +public class UserNotFoundException extends BaseException { + public static final BaseException EXCEPTION = new UserNotFoundException(); + + private UserNotFoundException() { + super(UserException.USER_NOT_FOUND_ERROR); + } +} diff --git a/Domain/src/main/java/playlist/server/domain/domains/auth/adaptor/UserAdaptor.java b/Domain/src/main/java/playlist/server/domain/domains/auth/adaptor/UserAdaptor.java new file mode 100644 index 0000000..5019a86 --- /dev/null +++ b/Domain/src/main/java/playlist/server/domain/domains/auth/adaptor/UserAdaptor.java @@ -0,0 +1,23 @@ +package playlist.server.domain.domains.auth.adaptor; + + +import lombok.RequiredArgsConstructor; +import playlist.server.annotation.Adaptor; +import playlist.server.domain.domains.auth.Exception.UserNotFoundException; +import playlist.server.domain.domains.auth.domain.User; +import playlist.server.domain.domains.auth.repository.UserRepository; + +@Adaptor +@RequiredArgsConstructor +public class UserAdaptor { + + private final UserRepository userRepository; + + public User query(Long userId) { + return userRepository.findById(userId).orElseThrow(() -> UserNotFoundException.EXCEPTION); + } + + public User save(User user) { + return userRepository.save(user); + } +} diff --git a/Domain/src/main/java/playlist/server/domain/domains/auth/domain/AccountRole.java b/Domain/src/main/java/playlist/server/domain/domains/auth/domain/AccountRole.java new file mode 100644 index 0000000..5367808 --- /dev/null +++ b/Domain/src/main/java/playlist/server/domain/domains/auth/domain/AccountRole.java @@ -0,0 +1,6 @@ +package playlist.server.domain.domains.auth.domain; + +public enum AccountRole { + USER, + ADMIN; +} diff --git a/Domain/src/main/java/playlist/server/domain/domains/auth/domain/Follow.java b/Domain/src/main/java/playlist/server/domain/domains/auth/domain/Follow.java new file mode 100644 index 0000000..1a60c71 --- /dev/null +++ b/Domain/src/main/java/playlist/server/domain/domains/auth/domain/Follow.java @@ -0,0 +1,31 @@ +package playlist.server.domain.domains.auth.domain; + + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import playlist.server.domain.domains.AbstractTimeStamp; + +@Getter +@Entity +// @Table(name = "follow") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Follow extends AbstractTimeStamp { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + // 이 밑으로는 뭔가 기록할만한 요소 들 + + @Column(nullable = false) + private Long userId; + + @Column(nullable = false) + private Long followUserId; +} diff --git a/Domain/src/main/java/playlist/server/domain/domains/auth/domain/Following.java b/Domain/src/main/java/playlist/server/domain/domains/auth/domain/Following.java new file mode 100644 index 0000000..5488be7 --- /dev/null +++ b/Domain/src/main/java/playlist/server/domain/domains/auth/domain/Following.java @@ -0,0 +1,31 @@ +package playlist.server.domain.domains.auth.domain; + + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import playlist.server.domain.domains.AbstractTimeStamp; + +@Getter +@Entity +// @Table(name = "following") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Following extends AbstractTimeStamp { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + // 이 밑으로는 뭔가 기록할만한 요소 들 + + @Column(nullable = false) + private Long userId; + + @Column(nullable = false) + private Long followingUserId; +} diff --git a/Domain/src/main/java/playlist/server/domain/domains/auth/domain/History.java b/Domain/src/main/java/playlist/server/domain/domains/auth/domain/History.java new file mode 100644 index 0000000..84938d2 --- /dev/null +++ b/Domain/src/main/java/playlist/server/domain/domains/auth/domain/History.java @@ -0,0 +1,49 @@ +package playlist.server.domain.domains.auth.domain; + + +import com.fasterxml.jackson.annotation.JsonFormat; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import java.sql.Timestamp; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.CreationTimestamp; +import playlist.server.domain.domains.AbstractTimeStamp; + +@Getter +@Entity +@Table(name = "user_login_history") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class History extends AbstractTimeStamp { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "login_at", nullable = false) + @CreationTimestamp + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss") + private Timestamp loginAt; + + // 이 밑으로는 뭔가 기록할만한 요소 들 + + @Column(nullable = false, length = 200) + private String param1; + + @Column(nullable = false, length = 200) + private String param2; + + @Column(nullable = false, length = 200) + private String param3; + + @Column(nullable = false, length = 200) + private String param4; + + @Column(nullable = false, length = 200) + private String param5; +} diff --git a/Domain/src/main/java/playlist/server/domain/domains/auth/domain/LoginType.java b/Domain/src/main/java/playlist/server/domain/domains/auth/domain/LoginType.java new file mode 100644 index 0000000..c82973f --- /dev/null +++ b/Domain/src/main/java/playlist/server/domain/domains/auth/domain/LoginType.java @@ -0,0 +1,10 @@ +package playlist.server.domain.domains.auth.domain; + +public enum LoginType { + DEFAULT, + KAKAO, + NAVER, + GOOGLE, + APPLE, + GITHUB +} diff --git a/Domain/src/main/java/playlist/server/domain/domains/auth/domain/SuspensionDate.java b/Domain/src/main/java/playlist/server/domain/domains/auth/domain/SuspensionDate.java new file mode 100644 index 0000000..aa9c174 --- /dev/null +++ b/Domain/src/main/java/playlist/server/domain/domains/auth/domain/SuspensionDate.java @@ -0,0 +1,33 @@ +package playlist.server.domain.domains.auth.domain; + + +import com.fasterxml.jackson.annotation.JsonFormat; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import java.sql.Timestamp; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import playlist.server.domain.domains.AbstractTimeStamp; + +@Getter +@Entity +@Table(name = "suspension_date") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class SuspensionDate extends AbstractTimeStamp { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + private Long userId; + + @Column(nullable = false) + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss") + private Timestamp suspensionAt; +} diff --git a/Domain/src/main/java/playlist/server/domain/domains/auth/domain/User.java b/Domain/src/main/java/playlist/server/domain/domains/auth/domain/User.java new file mode 100644 index 0000000..b6f5ad4 --- /dev/null +++ b/Domain/src/main/java/playlist/server/domain/domains/auth/domain/User.java @@ -0,0 +1,58 @@ +package playlist.server.domain.domains.auth.domain; + + +import jakarta.persistence.*; +import lombok.*; +import org.hibernate.annotations.ColumnDefault; +import org.jetbrains.annotations.NotNull; +import playlist.server.domain.domains.AbstractTimeStamp; + +@Getter +@Entity +@Table(name = "tbl_user") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class User extends AbstractTimeStamp { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false, length = 200) + private String email; + + @Column(nullable = false, length = 100) + private String password; + + // @Embedded private Profile profile; + + @Enumerated(EnumType.ORDINAL) + @Column(nullable = false, name = "role", columnDefinition = "varchar(32) default 'USER'") + private AccountRole accountRole; + + @Enumerated(EnumType.ORDINAL) + @Column(length = 32, nullable = false, columnDefinition = "varchar(32) default 'DEFAULT'") + private LoginType loginType; + + @NotNull + @Column(nullable = false) + @ColumnDefault("0") + private int failCnt; + + @NotNull + @Column(nullable = false) + @ColumnDefault("0") + private int rankId; + + @NotNull + @ColumnDefault("0") + private int exp; + + @Column private long updateUser; + + @Builder + public User(String email, String password, AccountRole role) { + this.email = email; + this.accountRole = role; + this.password = password; + } +} diff --git a/Domain/src/main/java/playlist/server/domain/domains/auth/repository/UserRepository.java b/Domain/src/main/java/playlist/server/domain/domains/auth/repository/UserRepository.java new file mode 100644 index 0000000..a1a4cd4 --- /dev/null +++ b/Domain/src/main/java/playlist/server/domain/domains/auth/repository/UserRepository.java @@ -0,0 +1,10 @@ +package playlist.server.domain.domains.auth.repository; + + +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; +import playlist.server.domain.domains.auth.domain.User; + +public interface UserRepository extends JpaRepository { + Optional findUserByEmail(String email); +} diff --git a/Domain/src/main/java/playlist/server/domain/domains/notificate/domain/Setting.java b/Domain/src/main/java/playlist/server/domain/domains/notificate/domain/Setting.java new file mode 100644 index 0000000..2a502eb --- /dev/null +++ b/Domain/src/main/java/playlist/server/domain/domains/notificate/domain/Setting.java @@ -0,0 +1,49 @@ +package playlist.server.domain.domains.notificate.domain; + + +import com.fasterxml.jackson.annotation.JsonFormat; +import jakarta.persistence.*; +import java.sql.Timestamp; +import lombok.*; +import org.hibernate.annotations.UpdateTimestamp; +import playlist.server.domain.domains.AbstractTimeStamp; + +@Getter +@Entity +@Table(name = "setting") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Setting extends AbstractTimeStamp { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false, length = 200) + private String email; + + // boolean etc_param1; + // + // boolean etc_param2; + // + // boolean etc_param3; + // + // boolean etc_param4; + // + // boolean etc_param5; + // + // boolean etc_param6; + // + // boolean etc_param7; + // + // boolean etc_param8; + // + // boolean etc_param9; + + @Column( + name = "updated_at", + nullable = false, + columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP") + @UpdateTimestamp + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss") + private Timestamp createdAt; +}