diff --git a/application.properties_docker b/application.properties_docker index f6b43d9..fdd31ce 100644 --- a/application.properties_docker +++ b/application.properties_docker @@ -3,10 +3,6 @@ spring.data.mongodb.uri=mongodb://${DATABASE_USERID:-nw}:${DATABASE_USERPASS:-nw spring.jackson.default-property-inclusion=NON_NULL # logging.level.ROOT=DEBUG -# Redis connection properties -spring.data.redis.host=${REDIS_HOST:-nw-redis} -spring.data.redis.port=${REDIS_PORT:-8083} - #set your port server.port=8888 #server.error.include-message=always @@ -30,17 +26,14 @@ springdoc.swagger-ui.operations-sorter=alpha lab.cherry.nw.jwtSecret= bGFiLWNoZXJyeS1udy1wcm9qZWN0LXNlY3JldC1rZXkK lab.cherry.nw.jwtExpirationMs= 86400000 + #Email spring.mail.host=${SMTP_ADDRESS:-smtp.localhost.com} spring.mail.port=${SMTP_PORT:-587} -spring.mail.properties.debug=true -spring.mail.properties.mail.smtp.auth=${SMTP_AUTH:-true} -spring.mail.properties.mail.smtp.ssl.enable=${SMTP_SSL:-false} -spring.mail.properties.mail.smtp.starttls.enable=${SMTP_STARTTLS:-true} -spring.mail.properties.mail.smtp.starttls.required=${SMTP_STARTTLS_REQURED:-true} - spring.mail.username=${SMTP_USER:-userName} spring.mail.password=${SMTP_PASS:-passWord} +spring.mail.properties.mail.smtp.auth=${SMTP_AUTH:-true} +spring.mail.properties.mail.smtp.starttls.enable=${SMTP_STARTTLS:-true} #echo 'lab-cherry-nw-project-secret-key' | base64 lab.cherry.nw.jwtSecret= bGFiLWNoZXJyeS1udy1wcm9qZWN0LXNlY3JldC1rZXkK diff --git a/docker-compose.yml b/docker-compose.yml index e4a7e94..82566b2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,11 +15,17 @@ services: MAX_UPLOAD_SIZE: 3096MB #SPRING_PROFILES_ACTIVE: prod JAVA_OPTS: "-Xms256m -Xmx512m -XX:+UseG1GC" + SMTP_ADDRESS: nw-smtp + SMTP_PORT: 1025 + SMTP_USER: test@test.com + SMTP_PASS: test@test.com + SMTP_AUTH: false + SMTP_STARTTLS: false ports: - "8081:8888" depends_on: - nw-db - - nw-redis + - nw-smtp volumes: - ./nw_data:/data @@ -44,14 +50,9 @@ services: - './mongodb_data:/bitnami' - '/etc/localtime:/etc/localtime:ro' - - nw-redis: - image: 'redis:latest' - container_name: nw-redis - restart: unless-stopped - entrypoint: redis-server --appendonly yes --maxmemory 4g --maxmemory-policy allkeys-lru - security_opt: - - no-new-privileges:true - volumes: - - './redis_data:/data' - - '/etc/localtime:/etc/localtime:ro' \ No newline at end of file + nw-smtp: + image: jcalonso/mailhog + container_name: 'nw-smtp' + ports: + - "1025:1025" # smtp + - "8025:8025" # web-ui \ No newline at end of file diff --git a/docker-compose/.env b/docker-compose/.env index e6aadbf..bba7501 100644 --- a/docker-compose/.env +++ b/docker-compose/.env @@ -9,17 +9,13 @@ DATABASE_USERID=admin DATABASE_USERPASS=admin #SPRING_PROFILES_ACTIVE=prod MAX_UPLOAD_SIZE=3096MB -REDIS_HOST=nw-redis -REDIS_PORT=8083 JAVA_OPTS="-Xms256m -Xmx512m -XX:+UseG1GC" -SMTP_ADDRESS=nw-smtp -SMTP_PORT=1025 -SMTP_AUTH=false -SMTP_SSL=false -SMTP_STARTTLS=flse -SMTP_STARTTLS_REQURED=false -SMTP_USER=null -SMTP_PASS=null +SMTP_ADDRESS=smtp.naver.com +SMTP_PORT=465 +SMTP_USER=yourId@naver.com +SMTP_PASS=yourPass@naver.com +SMTP_AUTH=true +SMTP_STARTTLS=true # nw-db MONGO_INITDB_DATABASE=nw diff --git a/docker-compose/docker-compose.yml b/docker-compose/docker-compose.yml index ff310c9..4c72cd8 100644 --- a/docker-compose/docker-compose.yml +++ b/docker-compose/docker-compose.yml @@ -11,8 +11,6 @@ services: - "8081:8888" depends_on: - nw-db - - nw-redis - - nw-smtp nw-db: image: 'mongo:latest' @@ -28,22 +26,4 @@ services: env_file: - ./.env ports: - - '8082:27017' - - nw-redis: - image: 'redis:latest' - container_name: nw-redis - restart: unless-stopped - entrypoint: redis-server --appendonly yes --maxmemory 4g --maxmemory-policy allkeys-lru - security_opt: - - no-new-privileges:true - volumes: - - './redis_data:/data' - - '/etc/localtime:/etc/localtime:ro' - - mailhog: - image: jcalonso/mailhog - container_name: 'mailhog' - ports: - - "1025:1025" # smtp - - "8025:8025" # web-ui \ No newline at end of file + - '8082:27017' \ No newline at end of file diff --git a/nw/build.gradle b/nw/build.gradle index 8cc8aff..d29c462 100644 --- a/nw/build.gradle +++ b/nw/build.gradle @@ -16,7 +16,6 @@ dependencies { implementation 'org.springframework:spring-context-support:6.0.7' implementation 'jakarta.mail:jakarta.mail-api:2.1.2' implementation 'org.springframework.boot:spring-boot-starter-mail:3.0.2' - implementation 'org.springframework.boot:spring-boot-starter-data-redis:3.0.2' implementation 'io.lettuce:lettuce-core' implementation 'com.googlecode.json-simple:json-simple:1.1.1' implementation 'com.google.code.gson:gson:2.8.6' @@ -24,4 +23,5 @@ dependencies { implementation 'io.minio:minio-admin:8.5.5' implementation 'com.fasterxml.jackson.core:jackson-core:2.15.2' implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.2' + implementation 'commons-io:commons-io:2.15.0' } diff --git a/nw/src/main/java/lab/cherry/nw/configuration/Initalizer.java b/nw/src/main/java/lab/cherry/nw/configuration/Initalizer.java index c461f46..e2fee87 100644 --- a/nw/src/main/java/lab/cherry/nw/configuration/Initalizer.java +++ b/nw/src/main/java/lab/cherry/nw/configuration/Initalizer.java @@ -36,7 +36,6 @@ public void run(ApplicationArguments args) { roleRepository.save(roleEntity); } - if(roleRepository.findByName("ROLE_USER").isEmpty()) { @@ -46,6 +45,15 @@ public void run(ApplicationArguments args) { roleRepository.save(roleEntity); } + + if(roleRepository.findByName("ROLE_ORG").isEmpty()) { + + RoleEntity roleEntity = RoleEntity.builder() + .name("ROLE_ORG") + .build(); + + roleRepository.save(roleEntity); + } if(orgRepository.findByName("DEFAULT").isEmpty()) { @@ -71,7 +79,6 @@ public void run(ApplicationArguments args) { .username("관리자") .password(passwordEncoder.encode("admin")) .email("admin@localhost.com") - .type("org") .role(roleEntity) .enabled(true) .build()); @@ -87,7 +94,21 @@ public void run(ApplicationArguments args) { .username("체리랩") .password(passwordEncoder.encode("cherrylab")) .email("cherrylab@test.com") - .type("user") + .role(roleEntity) + .enabled(true) + .build()); + } + + // TODO: 최종 개발 완료 후 삭제 처리 예정 + if(userRepository.findByuserid("themoment").isEmpty()) { + + RoleEntity roleEntity = roleRepository.findByName("ROLE_ORG").get(); + + userRepository.save(UserEntity.builder() + .userid("themoment") + .username("더모멘트") + .password(passwordEncoder.encode("themoment")) + .email("themoment@test.com") .role(roleEntity) .enabled(true) .build()); diff --git a/nw/src/main/java/lab/cherry/nw/configuration/WebSecurityConfiguration.java b/nw/src/main/java/lab/cherry/nw/configuration/WebSecurityConfiguration.java index 10cd085..bb8d6ef 100644 --- a/nw/src/main/java/lab/cherry/nw/configuration/WebSecurityConfiguration.java +++ b/nw/src/main/java/lab/cherry/nw/configuration/WebSecurityConfiguration.java @@ -60,7 +60,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti "/swagger-resources/**", "/swagger-ui/**", "/swagger-ui.html", - "/api/v1/mail/**" + "/api/auth/confirm/**" ) .permitAll() .requestMatchers("/api/v1/**").hasAnyRole("ADMIN", "USER") // spring boot 에서 ROLE_ 은 자동으로 붙음 diff --git a/nw/src/main/java/lab/cherry/nw/configuration/email/EmailConfig.java b/nw/src/main/java/lab/cherry/nw/configuration/email/EmailConfig.java index 87a3594..829b573 100644 --- a/nw/src/main/java/lab/cherry/nw/configuration/email/EmailConfig.java +++ b/nw/src/main/java/lab/cherry/nw/configuration/email/EmailConfig.java @@ -8,28 +8,34 @@ import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.JavaMailSenderImpl; - -//@ConfigurationProperties(prefix = "mail") @Configuration @PropertySource("classpath:application.properties") public class EmailConfig { - @Value("${spring.mail.username}") - private String id; - @Value("${spring.mail.password}") - private String password; + @Value("${spring.mail.host}") private String host; @Value("${spring.mail.port}") private int port; + @Value("${spring.mail.username}") + private String username; + @Value("${spring.mail.password}") + private String password; + + @Value("${spring.mail.properties.debug:false}") + private String debug; + @Value("${spring.mail.properties.mail.smtp.auth}") + private String auth; + @Value("${spring.mail.properties.mail.smtp.starttls.enable}") + private String starttls; @Bean public JavaMailSender javaMailService() { JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl(); javaMailSender.setHost(host); // smtp 서버 주소 - javaMailSender.setUsername(id); // 설정(발신) 메일 아이디 + javaMailSender.setPort(port); //smtp 서버 포트 + javaMailSender.setUsername(username); // 설정(발신) 메일 아이디 javaMailSender.setPassword(password); // 설정(발신) 메일 패스워드 - javaMailSender.setPort(port); //smtp port javaMailSender.setJavaMailProperties(getMailProperties()); // 메일 인증서버 정보 가져온다. javaMailSender.setDefaultEncoding("UTF-8"); return javaMailSender; @@ -38,13 +44,9 @@ public JavaMailSender javaMailService() { private Properties getMailProperties() { Properties properties = new Properties(); properties.setProperty("mail.transport.protocol", "smtp"); // 프로토콜 설정 - properties.setProperty("mail.smtp.auth", "true"); // smtp 인증 - properties.setProperty("mail.smtp.starttls.enable", "true"); // smtp starttls 사용 - properties.setProperty("mail.debug", "true"); // 디버그 사용 -// properties.setProperty("mail.smtp.ssl.trust","smtp.mailplug.co.kr"); // ssl 인증 서버 주소 - properties.setProperty("mail.smtp.ssl.trust","smtp.naver.com"); // ssl 인증 서버 주소 - - properties.setProperty("mail.smtp.ssl.enable","false"); // ssl 사용 + properties.setProperty("mail.smtp.auth", auth); // smtp 인증 + properties.setProperty("mail.smtp.starttls.enable", starttls); // smtp starttls 사용 + properties.setProperty("mail.debug", debug); // 디버그 사용 return properties; } } \ No newline at end of file diff --git a/nw/src/main/java/lab/cherry/nw/controller/AuthController.java b/nw/src/main/java/lab/cherry/nw/controller/AuthController.java index 58ace4b..bc4bd11 100644 --- a/nw/src/main/java/lab/cherry/nw/controller/AuthController.java +++ b/nw/src/main/java/lab/cherry/nw/controller/AuthController.java @@ -61,9 +61,9 @@ public class AuthController { }) @Operation(summary = "회원가입", description = "사용자를 추가합니다.") public ResponseEntity register(@Valid @RequestBody UserEntity.UserRegisterDto userRegisterDto) { - - AccessToken accessToken = authService.register(userRegisterDto); - return new ResponseEntity<>(accessToken, new HttpHeaders(), HttpStatus.OK); + + ResultResponse result = ResultResponse.of(SuccessCode.REGISTER_SUCCESS, authService.register(userRegisterDto)); + return new ResponseEntity<>(result, new HttpHeaders(), HttpStatus.OK); } /** @@ -114,13 +114,11 @@ public ResponseEntity existUserId(@PathVariable("userid") String userid) { authService.checkExistsWithUserId(userid); - final ResultResponse response = ResultResponse.of(SuccessCode.OK); + final ResultResponse response = ResultResponse.of(SuccessCode.USERID_CHECK_OK); return new ResponseEntity<>(response, new HttpHeaders(), HttpStatus.OK); } - - - @GetMapping("myinfo") + @GetMapping("/myinfo") @Operation(summary = "사용자 정보 확인", description = "사용자 정보를 확인합니다.") public ResponseEntity myInfo() { log.info("[AuthController] myInfo...!"); @@ -129,4 +127,33 @@ public ResponseEntity myInfo() { return new ResponseEntity<>(authService.myInfo(), new HttpHeaders(), HttpStatus.OK); } + @GetMapping("/confirm") + @Operation(summary = "이메일 인증", description = "이메일 인증을 진행합니다.") + public ResponseEntity confirmEmail(@RequestParam(required = true) String email, @RequestParam(required = true) String token) { + log.info("[AuthController] confirmEmail...!"); + + authService.confirmEmail(email, token); + final ResultResponse response = ResultResponse.of(SuccessCode.EMAIL_CHECK_OK); + return new ResponseEntity<>(response, new HttpHeaders(), HttpStatus.OK); + } + + @GetMapping("/confirm/{userSeq}") + @Operation(summary = "이메일 재인증", description = "이메일 재인증 메일을 발송합니다.") + public ResponseEntity reConfirmEmail(@PathVariable("userSeq") String userSeq) { + log.info("[AuthController] reConfirmEmail...!"); + + authService.reConfirmEmail(userSeq); + final ResultResponse response = ResultResponse.of(SuccessCode.EMAIL_RESEND_OK); + return new ResponseEntity<>(response, new HttpHeaders(), HttpStatus.OK); + } + + // @GetMapping("/forgot-password") + // @Operation(summary = "비밀번호 찾기", description = "이메일로 비밀번호 초기화 메일을 발송합니다.") + // public ResponseEntity forgotPassword(@Valid @RequestBody UserEntity.UserForgotPassword userForgotPasswordDto) { + // log.info("[AuthController] forgotPassword...!"); + + // authService.forgotPassword(userForgotPasswordDto); + // final ResultResponse response = ResultResponse.of(SuccessCode.PASSWORD_RESET_OK); + // return new ResponseEntity<>(response, new HttpHeaders(), HttpStatus.OK); + // } } diff --git a/nw/src/main/java/lab/cherry/nw/controller/EmailController.java b/nw/src/main/java/lab/cherry/nw/controller/EmailController.java deleted file mode 100644 index 87631d5..0000000 --- a/nw/src/main/java/lab/cherry/nw/controller/EmailController.java +++ /dev/null @@ -1,35 +0,0 @@ -package lab.cherry.nw.controller; - -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.RestController; -import lab.cherry.nw.service.EmailService; -import lombok.RequiredArgsConstructor; - -@RestController -@RequiredArgsConstructor -@RequestMapping("/api/v1/user") -public class EmailController{ - - private final EmailService emailService; - - - - @PostMapping("/mail/send") - public String emailSend(@RequestParam String email) throws Exception { - - String confirm = emailService.sendSimpleMessage(email); - - return confirm; - } - - @PostMapping("/mail/check") - public Boolean emailAuth(@RequestParam String email, String code) { - - Boolean check = emailService.emailCheck(email,code); - return check; - } - -} - diff --git a/nw/src/main/java/lab/cherry/nw/controller/QsheetController.java b/nw/src/main/java/lab/cherry/nw/controller/QsheetController.java index 8f778d9..7610cd6 100644 --- a/nw/src/main/java/lab/cherry/nw/controller/QsheetController.java +++ b/nw/src/main/java/lab/cherry/nw/controller/QsheetController.java @@ -1,6 +1,7 @@ package lab.cherry.nw.controller; import java.util.List; +import java.util.Map; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; @@ -9,13 +10,11 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.util.CollectionUtils; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; -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.RequestPart; @@ -190,17 +189,19 @@ public ResponseEntity findByQsheetId(@PathVariable("id") String id) { * * Author : taking(taking@duck.com) */ - @PostMapping("/download") + @PostMapping("/download/{qsheetIds}") @Operation(summary = "큐시트 사용자 파일 다운로드", description = "큐시트 사용자 파일을 다운로드합니다.") - public ResponseEntity downloadQsheetBySeq(@RequestBody QsheetEntity.QsheetDownloadDto qsheetDownloadDto) { + public ResponseEntity downloadQsheetBySeq(@PathVariable("qsheetIds") String[] qsheetIds) { log.info("[QsheetController] downloadQsheetBySeq...!"); + Map val = qsheetService.download(qsheetIds); HttpHeaders headers = new HttpHeaders(); - headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + "download.zip"); - return new ResponseEntity<>(qsheetService.download(qsheetDownloadDto.getUser()), new HttpHeaders(), HttpStatus.OK); - + headers.setContentType(MediaType.parseMediaType("application/zip")); + headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + val.get("name") + "\""); + + return new ResponseEntity<>(val.get("data"), headers, HttpStatus.OK); } } \ No newline at end of file diff --git a/nw/src/main/java/lab/cherry/nw/error/ResultResponse.java b/nw/src/main/java/lab/cherry/nw/error/ResultResponse.java index 1771f66..48a702b 100644 --- a/nw/src/main/java/lab/cherry/nw/error/ResultResponse.java +++ b/nw/src/main/java/lab/cherry/nw/error/ResultResponse.java @@ -29,9 +29,15 @@ private ResultResponse(final SuccessCode code) { private ResultResponse(final SuccessCode code, final Object data) { this.status = code.getStatus(); + this.message = code.getMessage(); this.data = data; } + private ResultResponse(final SuccessCode code, String message) { + this.status = code.getStatus(); + this.message = message; + } + public static ResultResponse of(final SuccessCode code) { return new ResultResponse(code); } @@ -40,5 +46,9 @@ public static ResultResponse of(final SuccessCode code, final Object data) { return new ResultResponse(code, data); } + public static ResultResponse of(final SuccessCode code, final String message) { + return new ResultResponse(code, message); + } + } diff --git a/nw/src/main/java/lab/cherry/nw/error/enums/ErrorCode.java b/nw/src/main/java/lab/cherry/nw/error/enums/ErrorCode.java index 033ddb4..1819fbc 100644 --- a/nw/src/main/java/lab/cherry/nw/error/enums/ErrorCode.java +++ b/nw/src/main/java/lab/cherry/nw/error/enums/ErrorCode.java @@ -28,11 +28,14 @@ public enum ErrorCode { NOT_FOUND(404, "찾을 수 없습니다."), FORBIDDEN(403, "접근 권한이 없어 거부되었습니다."), ACCESS_DENIED_EXCEPTION(401, "인증 정보가 유효하지 않습니다."), + EXPIRED_EXCEPTION(401, "기간이 만료된 링크입니다."), + USER_DUPLICATE(409, "중복된 사용자가 있습니다."), + USER_NOTFOUND(409, "사용자를 찾을 수 없습니다."), + EMAIL_AUTH_ERROR(409, "이메일 인증이 진행 중이 아니거나, 이메일 코드가 유효하지 않습니다."), + REQUIRE_EMAIL_VERIFIED(409, "이메일 인증을 진행해주세요."), DUPLICATE(409, "중복된 데이터가 있습니다."), NO_BODY(400, "파라미터 값이 입력되지 않았습니다."), - URL_NOTFOUND(400, "Minio URL을 다시 확인해주세요."), - FILE_UPLOAD_FAILED(400, "파일 업로드에 실패했습니다."), - FOLDER_CREATE_FAILED(400, "uploadPath를 찾을 수 없습니다."); + FILE_UPLOAD_FAILED(400, "파일 업로드에 실패했습니다."); private final int status; private final String message; diff --git a/nw/src/main/java/lab/cherry/nw/error/enums/SuccessCode.java b/nw/src/main/java/lab/cherry/nw/error/enums/SuccessCode.java index 9d889a3..5696dca 100644 --- a/nw/src/main/java/lab/cherry/nw/error/enums/SuccessCode.java +++ b/nw/src/main/java/lab/cherry/nw/error/enums/SuccessCode.java @@ -18,7 +18,12 @@ public enum SuccessCode { // Common - OK(200, "Success"); + OK(200, "Success"), + REGISTER_SUCCESS(200, "회원가입 완료"), + USERID_CHECK_OK(200, "사용 가능합니다."), + PASSWORD_RESET_OK(200, "비밀번호 초기화 이메일 발송 완료"), + EMAIL_CHECK_OK(200, "이메일 인증 완료"), + EMAIL_RESEND_OK(200, "이메일 인증 메일 재발송 완료"); private final int status; private final String message; diff --git a/nw/src/main/java/lab/cherry/nw/model/EmailAuthEntity.java b/nw/src/main/java/lab/cherry/nw/model/EmailAuthEntity.java new file mode 100644 index 0000000..9cce17e --- /dev/null +++ b/nw/src/main/java/lab/cherry/nw/model/EmailAuthEntity.java @@ -0,0 +1,68 @@ +package lab.cherry.nw.model; + +import java.io.Serializable; +import java.time.LocalDateTime; +import org.bson.types.ObjectId; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.DBRef; +import org.springframework.data.mongodb.core.mapping.Document; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +/** + *
+ * ClassName : UserEntity
+ * Type : class
+ * Description : User와 관련된 Entity를 구성하고 있는 클래스입니다.
+ * Related : UserRepository, UserServiceImpl
+ * 
+ */ +@Getter +@Builder +@AllArgsConstructor +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Document(collection = "email_auth") +public class EmailAuthEntity implements Serializable { + + @Id + private String id; + @DBRef + private UserEntity user; + @Email + @Size(min = 3, max = 40) + private String email; + private String token; + private LocalDateTime expired; + + public void updateExpireDate() { + this.expired = LocalDateTime.now().plusMinutes(5L); + } + + public void updateToken() { + this.token = new ObjectId().toString(); + } + + @Getter + @Builder + @NoArgsConstructor @AllArgsConstructor + public static class EmailAuthRequestDto { + + @NotBlank + @Email + @Schema(title = "사용자 이메일", example = "admin@nw.com") + @Size(min = 3, max = 40) + private String email; + + @NotBlank + @Schema(title = "인증 토큰", example = "653b34d4ec37354cf3d0ea78") + private String token; + + } +} diff --git a/nw/src/main/java/lab/cherry/nw/model/RoleEntity.java b/nw/src/main/java/lab/cherry/nw/model/RoleEntity.java index 53d8834..84f30fb 100644 --- a/nw/src/main/java/lab/cherry/nw/model/RoleEntity.java +++ b/nw/src/main/java/lab/cherry/nw/model/RoleEntity.java @@ -52,7 +52,7 @@ public class RoleEntity implements Serializable { public static class RoleCreateDto { @Schema(title = "권한 이름", example = "ADMIN") - @Size(min = 4, max = 20, message = "Minimum name length: 4 characters") + @Size(min = 3, max = 20, message = "Minimum name length: 4 characters") private String name; } diff --git a/nw/src/main/java/lab/cherry/nw/model/UserEntity.java b/nw/src/main/java/lab/cherry/nw/model/UserEntity.java index c5718d9..5414b9c 100644 --- a/nw/src/main/java/lab/cherry/nw/model/UserEntity.java +++ b/nw/src/main/java/lab/cherry/nw/model/UserEntity.java @@ -31,7 +31,7 @@ @Builder @NoArgsConstructor @AllArgsConstructor @Document(collection = "users") -@JsonPropertyOrder({ "id", "userId", "userName", "userEmail", "userRole", "userType", "userEnabled", "userRole", "userOrgs", "created_at" }) +@JsonPropertyOrder({ "id", "userId", "userName", "userEmail", "userRole", "userType", "userEnabled", "emailVerified", "userRole", "userOrgs", "created_at" }) public class UserEntity implements Serializable { @Id @@ -66,11 +66,7 @@ public class UserEntity implements Serializable { @JsonProperty("userPhoto") @Schema(title = "사용자 사진") private Object photo; - - @JsonProperty("userType") - @Schema(title = "타입", example = "user | org") - private String type; - + @JsonProperty("userEnabled") @Schema(title = "사용자 활성화 여부", example = "true") private Boolean enabled; @@ -95,6 +91,11 @@ public class UserEntity implements Serializable { // private Set orgs = new HashSet<>(); private OrgEntity org; + + public void emailVerifiedSuccess() { + this.isEmailVerified = true; + } + ////////////////////////////////////////////////////////////////////////// @Getter diff --git a/nw/src/main/java/lab/cherry/nw/repository/EmailAuthRepository.java b/nw/src/main/java/lab/cherry/nw/repository/EmailAuthRepository.java new file mode 100644 index 0000000..f71d323 --- /dev/null +++ b/nw/src/main/java/lab/cherry/nw/repository/EmailAuthRepository.java @@ -0,0 +1,26 @@ +package lab.cherry.nw.repository; + +import java.util.Optional; +import org.springframework.data.mongodb.repository.MongoRepository; +import org.springframework.data.mongodb.repository.Query; +import lab.cherry.nw.model.EmailAuthEntity; + +/** + *
+ * ClassName : EmailAuthRepository
+ * Type : interface
+ * Descrption : 이메일 인증 관련 JPA 구현을 위한 인터페이스입니다.
+ * Related : spring-boot-starter-data-mongo
+ * 
+ */ +//@Repository +public interface EmailAuthRepository extends MongoRepository { + + @Query("{'email' : ?0}") + Optional findByEmail(String email); + @Query("{'userid' : ?0, 'email' : ?1}") + Optional findByUseridAndEmail(String userid, String email); + @Query("{'email' : ?0, 'token' : ?1}") + Optional findByEmailAndToken(String email, String token); + +} \ No newline at end of file diff --git a/nw/src/main/java/lab/cherry/nw/repository/EventRepository.java b/nw/src/main/java/lab/cherry/nw/repository/EventRepository.java index 782e3d0..d9cecd3 100644 --- a/nw/src/main/java/lab/cherry/nw/repository/EventRepository.java +++ b/nw/src/main/java/lab/cherry/nw/repository/EventRepository.java @@ -1,10 +1,7 @@ package lab.cherry.nw.repository; import java.util.Optional; - import org.springframework.data.mongodb.repository.MongoRepository; -import org.springframework.data.mongodb.repository.Query; - import lab.cherry.nw.model.EventEntity; /** @@ -18,13 +15,7 @@ //@Repository public interface EventRepository extends MongoRepository { - // Page findPageByName(String eventname, Pageable pageable); - // Optional findByName(String name); - - @Query("{'seq' : ?0}") Optional findBySeq(String seq); - - @Query("{'title' : ?0}") Optional findByTitle(String title); } \ No newline at end of file diff --git a/nw/src/main/java/lab/cherry/nw/repository/UserRepository.java b/nw/src/main/java/lab/cherry/nw/repository/UserRepository.java index 6677604..6d1a416 100644 --- a/nw/src/main/java/lab/cherry/nw/repository/UserRepository.java +++ b/nw/src/main/java/lab/cherry/nw/repository/UserRepository.java @@ -4,7 +4,6 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.mongodb.repository.MongoRepository; - import java.util.Optional; /** @@ -22,6 +21,5 @@ public interface UserRepository extends MongoRepository { Page findPageByUserid(String userid, Pageable pageable); Optional findById(String id); Optional findByuserid(String userid); - Optional findByUsername(String username); } diff --git a/nw/src/main/java/lab/cherry/nw/service/AuthService.java b/nw/src/main/java/lab/cherry/nw/service/AuthService.java index 066c5a2..378eb09 100644 --- a/nw/src/main/java/lab/cherry/nw/service/AuthService.java +++ b/nw/src/main/java/lab/cherry/nw/service/AuthService.java @@ -15,8 +15,10 @@ */ @Component public interface AuthService { - AccessToken register(UserEntity.UserRegisterDto userRegisterDto); + UserEntity register(UserEntity.UserRegisterDto userRegisterDto); AccessToken.Get login(UserEntity.UserLoginDto userLoginDto); void checkExistsWithUserId(String userid); UserEntity myInfo(); + void confirmEmail(String email, String token); + void reConfirmEmail(String userid); } diff --git a/nw/src/main/java/lab/cherry/nw/service/EmailAuthService.java b/nw/src/main/java/lab/cherry/nw/service/EmailAuthService.java new file mode 100644 index 0000000..f6ac8a8 --- /dev/null +++ b/nw/src/main/java/lab/cherry/nw/service/EmailAuthService.java @@ -0,0 +1,16 @@ +package lab.cherry.nw.service; + +import java.util.Optional; +import org.springframework.stereotype.Service; +import lab.cherry.nw.model.EmailAuthEntity; + + +@Service +public interface EmailAuthService { + + EmailAuthEntity updateExpired(String email); + Optional findValidAuthByEmail(String email, String token); + void ConfirmEmailSend(String email, String token); + void InviteOrgSend(String orgname, String email, String token); + +} \ No newline at end of file diff --git a/nw/src/main/java/lab/cherry/nw/service/EmailService.java b/nw/src/main/java/lab/cherry/nw/service/EmailService.java deleted file mode 100644 index 93d2f3e..0000000 --- a/nw/src/main/java/lab/cherry/nw/service/EmailService.java +++ /dev/null @@ -1,10 +0,0 @@ -package lab.cherry.nw.service; - -import org.springframework.stereotype.Service; - - -@Service -public interface EmailService { - String sendSimpleMessage(String to)throws Exception; - Boolean emailCheck(String email,String code); -} \ No newline at end of file diff --git a/nw/src/main/java/lab/cherry/nw/service/Impl/AuthServiceImpl.java b/nw/src/main/java/lab/cherry/nw/service/Impl/AuthServiceImpl.java index b052162..6946b20 100644 --- a/nw/src/main/java/lab/cherry/nw/service/Impl/AuthServiceImpl.java +++ b/nw/src/main/java/lab/cherry/nw/service/Impl/AuthServiceImpl.java @@ -2,23 +2,29 @@ import lab.cherry.nw.error.enums.ErrorCode; import lab.cherry.nw.error.exception.CustomException; +import lab.cherry.nw.error.exception.EntityNotFoundException; +import lab.cherry.nw.model.EmailAuthEntity; import lab.cherry.nw.model.RoleEntity; import lab.cherry.nw.model.UserEntity; +import lab.cherry.nw.repository.EmailAuthRepository; import lab.cherry.nw.repository.RoleRepository; import lab.cherry.nw.repository.UserRepository; import lab.cherry.nw.service.AuthService; +import lab.cherry.nw.service.EmailAuthService; import lab.cherry.nw.service.TokenService; +import lab.cherry.nw.service.UserService; import lab.cherry.nw.util.Security.AccessToken; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.bson.types.ObjectId; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; - import java.time.Instant; +import java.time.LocalDateTime; import java.util.*; /** @@ -39,6 +45,9 @@ public class AuthServiceImpl implements AuthService { private final PasswordEncoder passwordEncoder; private final UserRepository userRepository; private final RoleRepository roleRepository; + private final EmailAuthRepository emailAuthRepository; + private final EmailAuthService emailAuthService; + private final UserService userService; /** * [AuthServiceImpl] 회원가입 함수 @@ -52,30 +61,43 @@ public class AuthServiceImpl implements AuthService { * * Author : taking(taking@duck.com) */ - public AccessToken register(UserEntity.UserRegisterDto userRegisterDto) { + public UserEntity register(UserEntity.UserRegisterDto userRegisterDto) { Instant instant = Instant.now(); + ObjectId objectId = new ObjectId(); + ObjectId verifiedObjectId = new ObjectId(); checkExistsWithUserId(userRegisterDto.getUserId()); // 동일한 이름 중복체크 RoleEntity roleEntity = roleRepository.findByName("ROLE_USER").get(); UserEntity userEntity = UserEntity.builder() + .id(objectId.toString()) .userid(userRegisterDto.getUserId()) .username(userRegisterDto.getUserName()) .email(userRegisterDto.getUserEmail()) .password(passwordEncoder.encode(userRegisterDto.getUserPassword())) - .type((userRegisterDto.getUserType()) == null ? "user" : userRegisterDto.getUserType()) .role(roleEntity) .enabled(true) + .isEmailVerified(false) .created_at(instant) .build(); - userRepository.save(userEntity); + UserEntity user = userRepository.save(userEntity); + + + EmailAuthEntity emailAuthEntity = EmailAuthEntity.builder() + .id(objectId.toString()) + .user(user) + .email(userRegisterDto.getUserEmail()) + .token(verifiedObjectId.toString()) + .expired(LocalDateTime.now().plusMinutes(5L)) + .build(); + + emailAuthRepository.save(emailAuthEntity); - String userid = userEntity.getUserid(); - RoleEntity role = userEntity.getRole(); + emailAuthService.ConfirmEmailSend(emailAuthEntity.getEmail(), emailAuthEntity.getToken()); - return tokenService.generateJwtToken(userid,role); + return userEntity; } /** @@ -94,6 +116,7 @@ public AccessToken register(UserEntity.UserRegisterDto userRegisterDto) { public AccessToken.Get login(UserEntity.UserLoginDto userLoginDto) { authenticateByIdAndPassword(userLoginDto); + isValidEmailVertified(userLoginDto); Optional userEntity = userRepository.findByuserid(userLoginDto.getUserId()); AccessToken accessToken = tokenService.generateJwtToken(userLoginDto.getUserId(), userEntity.get().getRole()); @@ -128,7 +151,7 @@ public AccessToken.Get login(UserEntity.UserLoginDto userLoginDto) { @Transactional(readOnly = true) public void checkExistsWithUserId(String userid) { if (userRepository.findByuserid(userid).isPresent()) { - throw new CustomException(ErrorCode.DUPLICATE); // 사용자 아이디가 중복됨 + throw new CustomException(ErrorCode.USER_DUPLICATE); // 사용자 아이디가 중복됨 } } @@ -158,6 +181,32 @@ private void authenticateByIdAndPassword(UserEntity.UserLoginDto userLoginDto) { } } + /** + * [AuthServiceImpl] 이메일 인증 여부 확인 함수 + * + * @param userLoginDto userLoginDto 로그인 정보 검증에 필요한 사용자 등록 정보를 담은 개체입니다. + * @throws CustomException 사용자가 등록되지 않았거나, 비밀번호가 일치하지 않을 경우 예외 처리 발생 + *
+     * 입력된 사용자 로그인 정보를 검증합니다.
+     * 
+ * + * Author : taking(taking@duck.com) + */ + private void isValidEmailVertified(UserEntity.UserLoginDto userLoginDto) { + + if(userLoginDto == null) { // Body 값이 비어 있을 경우, 예외처리 + throw new CustomException(ErrorCode.INVALID_INPUT_VALUE); // 입력 값이 유효하지 않음 + } + + UserEntity user = userRepository.findByuserid(userLoginDto.getUserId()) + .orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND)); // 로그인 정보가 유효하지 않음 + + if(!user.getIsEmailVerified()) { + log.error("{}는 이메일 인증이 필요합니다.", userLoginDto.getUserId()); + throw new CustomException(ErrorCode.REQUIRE_EMAIL_VERIFIED); // 이메일 인증이 필요합니다. + } + } + /** * [AuthServiceImpl] 역할 정보 조회 함수 * @@ -188,7 +237,47 @@ public UserEntity myInfo() { SecurityContext securityContext = SecurityContextHolder.getContext(); Authentication authentication = securityContext.getAuthentication(); + log.error("authentication.getName() {}", authentication.getName()); + + if(authentication.getName() == null || authentication.getName() == "anonymousUser") { + throw new CustomException(ErrorCode.ENTITY_NOT_FOUND); + } + + UserEntity user = userRepository.findByuserid(authentication.getName()).get(); return user; + } + + @Transactional(readOnly = true) + public void confirmEmail(String email, String token) { + + EmailAuthEntity emailAuthEntity = emailAuthRepository.findByEmailAndToken(email, token) + .orElseThrow(() -> new CustomException(ErrorCode.EMAIL_AUTH_ERROR)); + + LocalDateTime currentDate = LocalDateTime.now(); + LocalDateTime expiredDate = emailAuthEntity.getExpired(); + + if (currentDate.isAfter(expiredDate)) { + throw new CustomException(ErrorCode.EXPIRED_EXCEPTION); + } + + userService.updateEmailVerifiedByid(emailAuthEntity.getUser().getId()); + } + + @Transactional(readOnly = true) + public UserEntity findByuserid(String userid) { + return userRepository.findByuserid(userid).orElseThrow(() -> new EntityNotFoundException("User with userId " + userid + " Not Found.")); + } + + @Transactional(readOnly = true) + public void reConfirmEmail(String userSeq) { + + EmailAuthEntity emailAuthEntity = emailAuthRepository.findById(userSeq) + .orElseThrow(() -> new CustomException(ErrorCode.EMAIL_AUTH_ERROR)); + + emailAuthEntity = emailAuthService.updateExpired(userSeq); + emailAuthService.ConfirmEmailSend(emailAuthEntity.getEmail(), emailAuthEntity.getToken()); + + } } diff --git a/nw/src/main/java/lab/cherry/nw/service/Impl/EmailAuthServiceImpl.java b/nw/src/main/java/lab/cherry/nw/service/Impl/EmailAuthServiceImpl.java new file mode 100644 index 0000000..b1a007c --- /dev/null +++ b/nw/src/main/java/lab/cherry/nw/service/Impl/EmailAuthServiceImpl.java @@ -0,0 +1,114 @@ +package lab.cherry.nw.service.Impl; + +import java.time.LocalDateTime; +import java.util.Optional; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.stereotype.Service; +import jakarta.mail.MessagingException; +import jakarta.mail.internet.MimeMessage; +import lab.cherry.nw.error.exception.EntityNotFoundException; +import lab.cherry.nw.model.EmailAuthEntity; +import lab.cherry.nw.repository.EmailAuthRepository; +import lab.cherry.nw.service.EmailAuthService; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; + + +@Slf4j +@AllArgsConstructor +@Service +public class EmailAuthServiceImpl implements EmailAuthService{ + + private final JavaMailSender javaMmailSender; + private final EmailAuthRepository emailAuthRepository; + + private static final String FROM_ADDRESS = "no-reply@nangmanwedding.com"; + + public Optional findValidAuthByEmail(String email, String token) { + + LocalDateTime currentTime = LocalDateTime.now(); + EmailAuthEntity emailAuthEntity = emailAuthRepository.findByEmail(email).get(); + + if(email == emailAuthEntity.getEmail() || token == emailAuthEntity.getToken() || emailAuthEntity.getExpired().isAfter(currentTime) ) { return Optional.ofNullable(emailAuthEntity); + } else { + return null; + } + } + + + /** + * [UserServiceImpl] 이메일 인증 부분 수정 함수 + * + * @param email 조회할 사용자의 고유번호입니다. + * @throws EntityNotFoundException 사용자 정보가 없을 경우 예외 처리 발생 + *
+     * 특정 사용자에 대해 이메일 인증 정보를 수정합니다.
+     * 
+ * + * Author : taking(taking@duck.com) + */ + public EmailAuthEntity updateExpired(String id) { + + EmailAuthEntity emailAuthEntity = emailAuthRepository.findById(id).get(); + emailAuthEntity.updateExpireDate(); + emailAuthEntity.updateToken(); + + return emailAuthRepository.save(emailAuthEntity); + } + + public void ConfirmEmailSend(String email, String token) { + + MimeMessage message = javaMmailSender.createMimeMessage(); + String _link = "/api/auth/confirm?email=" + email + "&token=" + token; + + try { + message.addRecipients(MimeMessage.RecipientType.TO, email); + message.setFrom(EmailAuthServiceImpl.FROM_ADDRESS); + message.setSubject("[낭만웨딩] 이메일 인증 요청 메일입니다."); //제목 + String text=""; + text+= "
"; + text+= "
"; + text+= "

다음 링크를 눌러 인증을 완료하세요.

"; + text+= "
"; + text+= "LINK : "; + text+= "인증하기" + "

"; + text+= "
"; + message.setText(text, "utf-8", "html"); + + javaMmailSender.send(message); + + } catch (MessagingException e) { + log.error("email Error {}", e); + } + + } + + public void InviteOrgSend(String orgname, String email, String token) { + + MimeMessage message = javaMmailSender.createMimeMessage(); + String _link = "/api/auth/register/confirm?email" + email + "&token=" + token; + + try { + message.addRecipients(MimeMessage.RecipientType.TO, email); + message.setFrom(EmailAuthServiceImpl.FROM_ADDRESS); + message.setSubject("[낭만웨딩] " + orgname + " 회원가입 요청 메일입니다."); //제목 + String text=""; + text+= "
"; + text+= "
"; + text+= "

다음 링크를 눌러 회원가입을 진행하세요.

"; + text+= "

회원이시라면 링크를 누르면 [" + orgname + "]에 소속됩니다.

"; + text+= "
"; + text+= "LINK : "; + text+= "진행하기" + "

"; + text+= "
"; + message.setText(text, "utf-8", "html"); + + javaMmailSender.send(message); + + + } catch (MessagingException e) { + log.error("email Error {}", e); + } + } + +} \ No newline at end of file diff --git a/nw/src/main/java/lab/cherry/nw/service/Impl/EmailServiceImpl.java b/nw/src/main/java/lab/cherry/nw/service/Impl/EmailServiceImpl.java deleted file mode 100644 index e5f7dc3..0000000 --- a/nw/src/main/java/lab/cherry/nw/service/Impl/EmailServiceImpl.java +++ /dev/null @@ -1,98 +0,0 @@ -package lab.cherry.nw.service.Impl; - -import java.util.Random; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.mail.MailException; -import org.springframework.mail.javamail.JavaMailSender; -import org.springframework.stereotype.Service; - -import jakarta.mail.internet.MimeMessage; -import lab.cherry.nw.service.EmailService; -import lab.cherry.nw.util.RedisUtil; -import lombok.AllArgsConstructor; - - -@AllArgsConstructor -@Service -public class EmailServiceImpl implements EmailService{ - - @Autowired - JavaMailSender emailSender; - private final RedisUtil redisUtil; - - private static final String FROM_ADDRESS = "haerin2222@naver.com"; - public static final String authkey = createKey(); - - - private MimeMessage createMessage(String to)throws Exception{ - - MimeMessage message = emailSender.createMimeMessage(); - - message.addRecipients(MimeMessage.RecipientType.TO, to); - message.setFrom(EmailServiceImpl.FROM_ADDRESS); - message.setSubject("낭만웨딩 회원가입을 위해 이메일 인증을 해주세요.");//제목 - - message.setText("인증번호를 확인해주세요 : " + authkey); -// message.setFrom(new InternetAddress("properties에 입력한 이메일","haeri"));//보내는 사람 - - - return message; - } - - - public static String createKey() { - StringBuffer key = new StringBuffer(); - Random rnd = new Random(); - - for (int i = 0; i < 8; i++) { // 인증코드 8자리 - int index = rnd.nextInt(3); // 0~2 까지 랜덤 - - switch (index) { - case 0: - key.append((char) ((int) (rnd.nextInt(26)) + 97)); - // a~z (ex. 1+97=98 => (char)98 = 'b') - break; - case 1: - key.append((char) ((int) (rnd.nextInt(26)) + 65)); - // A~Z - break; - case 2: - key.append((rnd.nextInt(10))); - // 0~9 - break; - } - } - return key.toString(); - } - - @Override - public String sendSimpleMessage(String to)throws Exception { - // TODO Auto-generated method stub - MimeMessage message = createMessage(to); - - redisUtil.setDataExpire(authkey,to,60 * 5L); - try{//예외처리 - emailSender.send(message); - }catch(MailException es){ - es.printStackTrace(); - throw new IllegalArgumentException(); - } - - return authkey; - } - - @Override - public Boolean emailCheck(String email,String code) { - - boolean result = false; - - System.out.println("redis에 저장 된 authkey : " + redisUtil.getData(email)); - - if (redisUtil.getData(email).equals(code)) { - return true; - } - - return result; - } -} \ No newline at end of file diff --git a/nw/src/main/java/lab/cherry/nw/service/Impl/FileServiceImpl.java b/nw/src/main/java/lab/cherry/nw/service/Impl/FileServiceImpl.java index 6a3348f..54c156f 100644 --- a/nw/src/main/java/lab/cherry/nw/service/Impl/FileServiceImpl.java +++ b/nw/src/main/java/lab/cherry/nw/service/Impl/FileServiceImpl.java @@ -33,6 +33,8 @@ import lab.cherry.nw.util.FormatConverter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.FilenameUtils; +import org.bson.types.ObjectId; @Slf4j @Service @@ -122,7 +124,9 @@ public Map downloadFiles(String key, String value) { ZipOutputStream zipOut = new ZipOutputStream(byteArrayOutputStream)) { for (GridFSFile file : allFiles) { - ZipEntry zipEntry = new ZipEntry(file.getFilename()); + String fileName = FilenameUtils.getBaseName(file.getFilename()) + "-" + new ObjectId() + "." + FilenameUtils.getExtension(file.getFilename()); + + ZipEntry zipEntry = new ZipEntry(fileName); zipOut.putNextEntry(zipEntry); byte[] objectData = IOUtils.toByteArray(operations.getResource(file).getInputStream()); diff --git a/nw/src/main/java/lab/cherry/nw/service/Impl/QsheetServiceImpl.java b/nw/src/main/java/lab/cherry/nw/service/Impl/QsheetServiceImpl.java index fb6c9d2..ec242a8 100644 --- a/nw/src/main/java/lab/cherry/nw/service/Impl/QsheetServiceImpl.java +++ b/nw/src/main/java/lab/cherry/nw/service/Impl/QsheetServiceImpl.java @@ -1,22 +1,27 @@ package lab.cherry.nw.service.Impl; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.time.Instant; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + import org.bson.types.ObjectId; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; + import lab.cherry.nw.error.enums.ErrorCode; import lab.cherry.nw.error.exception.CustomException; import lab.cherry.nw.error.exception.EntityNotFoundException; import lab.cherry.nw.model.OrgEntity; import lab.cherry.nw.model.QsheetEntity; -import lab.cherry.nw.model.QsheetLogEntity; import lab.cherry.nw.model.QsheetEntity.ItemData; import lab.cherry.nw.model.UserEntity; import lab.cherry.nw.repository.QsheetRepository; @@ -26,6 +31,7 @@ import lab.cherry.nw.service.QsheetLogService; import lab.cherry.nw.service.QsheetService; import lab.cherry.nw.service.UserService; +import lab.cherry.nw.util.FormatConverter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -317,57 +323,34 @@ public Page findPageByOrgId(String orgSeq, Pageable pageable) { return qsheetRepository.findPageByOrgid(orgSeq, pageable); } - public byte[] download(List users) { - return null; + public Map download(String[] qsheetIds) { - // List userList = new ArrayList<>(); - // List userData = new ArrayList<>(); - - // for(String user : users) { - - // if (userService.checkId(user)) { - // UserEntity _user = userService.findById(user); - // userList.add(_user); - - // // String objectName = _user.getId() + "/"; - // // userData.add(fileService.downloadZip("user", objectName)); - // } - // } - - // if(userList.size() > 1) { - - // log.error("userList 가 1명 초과인 경우 # ", userList.size()); + + try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + ZipOutputStream zipOut = new ZipOutputStream(byteArrayOutputStream)) { + for (String qsheetId : qsheetIds) { - // ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - // try (ZipOutputStream zipOut = new ZipOutputStream(byteArrayOutputStream)) { - // for (UserEntity user : userList) { + QsheetEntity qsheetEntity = findById(qsheetId); - // String objectName = user.getId() +"/"; + String qsheetName = qsheetEntity.getName() + "-" + new ObjectId() + ".zip"; - // byte[] objectData = fileService.downloadZip("user", objectName); + ZipEntry zipEntry = new ZipEntry(qsheetName); + zipOut.putNextEntry(zipEntry); - // // Zip 아카이브에 객체 추가 - // ZipArchiveEntry zipEntry = new ZipArchiveEntry(user.getUsername() + ".zip"); - // zipOut.putNextEntry(zipEntry); - // zipOut.write(objectData); - // zipOut.closeEntry(); + Object objectData = fileService.downloadFiles("seq", qsheetId).get("data"); + zipOut.write(FormatConverter.convertObjectToBytes(objectData)); + zipOut.closeEntry(); + } + zipOut.finish(); - // } - // } catch (IOException e) { - // log.error("{}", e); - // } + Map returnVal = new HashMap<>(); + returnVal.put("name", "download" + ".zip"); + returnVal.put("data", byteArrayOutputStream.toByteArray()); - // byte[] zipBytes = byteArrayOutputStream.toByteArray(); - - // return zipBytes; - - // } else { - - // UserEntity user = userService.findById(users.get(0)); - - // String objectName = "사용자/" + user.getId(); - - // return fileService.downloadZip("user", objectName); - // } + return returnVal; + } catch (IOException e) { + log.error("Error creating and sending the zip file: {}", e); + return null; + } } } \ No newline at end of file diff --git a/nw/src/main/java/lab/cherry/nw/service/Impl/UserServiceImpl.java b/nw/src/main/java/lab/cherry/nw/service/Impl/UserServiceImpl.java index 7566915..50f2ac0 100644 --- a/nw/src/main/java/lab/cherry/nw/service/Impl/UserServiceImpl.java +++ b/nw/src/main/java/lab/cherry/nw/service/Impl/UserServiceImpl.java @@ -108,6 +108,24 @@ public void updateById(String id, UserEntity.UserUpdateDto user) { } } + /** + * [UserServiceImpl] 사용자 이메일 인증 부분 수정 함수 + * + * @param id 조회할 사용자의 고유번호입니다. + * @throws EntityNotFoundException 사용자 정보가 없을 경우 예외 처리 발생 + *
+     * 특정 사용자에 대해 이메일 인증 정보를 수정합니다.
+     * 
+ * + * Author : taking(taking@duck.com) + */ + public void updateEmailVerifiedByid(String id) { + + UserEntity userEntity = findById(id); + userEntity.emailVerifiedSuccess(); + + userRepository.save(userEntity); + } /** * [UserServiceImpl] 사용자 조직 수정 함수 diff --git a/nw/src/main/java/lab/cherry/nw/service/QsheetService.java b/nw/src/main/java/lab/cherry/nw/service/QsheetService.java index 11cc89e..aa9a75b 100644 --- a/nw/src/main/java/lab/cherry/nw/service/QsheetService.java +++ b/nw/src/main/java/lab/cherry/nw/service/QsheetService.java @@ -2,6 +2,7 @@ import lab.cherry.nw.model.QsheetEntity; import java.util.List; +import java.util.Map; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Component; @@ -25,5 +26,5 @@ public interface QsheetService { Page findPageByUserId(String userSeq, Pageable pageable); Page findPageByOrgId(String orgSeq, Pageable pageable); // void updateOrgById(String id, List orgIds); -byte[] download(List users); +Map download(String[] qsheetSeq); } \ No newline at end of file diff --git a/nw/src/main/java/lab/cherry/nw/service/UserService.java b/nw/src/main/java/lab/cherry/nw/service/UserService.java index ee10570..2659727 100644 --- a/nw/src/main/java/lab/cherry/nw/service/UserService.java +++ b/nw/src/main/java/lab/cherry/nw/service/UserService.java @@ -1,7 +1,6 @@ package lab.cherry.nw.service; import lab.cherry.nw.model.UserEntity; -import lab.cherry.nw.model.WeddinghallEntity; import java.util.List; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -27,4 +26,5 @@ public interface UserService { Page findPageByUserId(String userid, Pageable pageable); void updateOrgById(String id, String orgId); void updateUserPhoto(String id, List image); + void updateEmailVerifiedByid(String id); } diff --git a/nw/src/main/java/lab/cherry/nw/util/FormatConverter.java b/nw/src/main/java/lab/cherry/nw/util/FormatConverter.java index 60a1cf4..9754a98 100644 --- a/nw/src/main/java/lab/cherry/nw/util/FormatConverter.java +++ b/nw/src/main/java/lab/cherry/nw/util/FormatConverter.java @@ -6,9 +6,11 @@ import lombok.extern.slf4j.Slf4j; import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.io.IOException; import java.io.InputStreamReader; +import java.io.ObjectOutputStream; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -98,5 +100,14 @@ public static Map convertDateFormat(String _date) { e.printStackTrace(); } return returnVal; - } + } + + public static byte[] convertObjectToBytes(Object obj) throws IOException { + ByteArrayOutputStream boas = new ByteArrayOutputStream(); + try (ObjectOutputStream ois = new ObjectOutputStream(boas)) { + ois.writeObject(obj); + return boas.toByteArray(); + } +} + } \ No newline at end of file diff --git a/nw/src/main/java/lab/cherry/nw/util/RedisUtil.java b/nw/src/main/java/lab/cherry/nw/util/RedisUtil.java deleted file mode 100644 index 850f138..0000000 --- a/nw/src/main/java/lab/cherry/nw/util/RedisUtil.java +++ /dev/null @@ -1,37 +0,0 @@ -package lab.cherry.nw.util; - -import lombok.RequiredArgsConstructor; -import org.springframework.data.redis.core.StringRedisTemplate; -import org.springframework.data.redis.core.ValueOperations; -import org.springframework.stereotype.Component; - -import java.time.Duration; - -@RequiredArgsConstructor -@Component -public class RedisUtil { - - private final StringRedisTemplate redisTemplate; - - public String getData(String key) { // key를 통해 value(데이터)를 얻는다. - ValueOperations valueOperations = redisTemplate.opsForValue(); - return valueOperations.get(key); - } - - public void setData(String key, String value) { - ValueOperations valueOperations = redisTemplate.opsForValue(); - valueOperations.set(key, value); - } - - public void setDataExpire(String key, String value, long duration) { - // duration 동안 (key, value)를 저장한다. - ValueOperations valueOperations = redisTemplate.opsForValue(); - Duration expireDuration = Duration.ofMillis(duration); - valueOperations.set(key, value, expireDuration); - } - - public void deleteData(String key) { - // 데이터 삭제 - redisTemplate.delete(key); - } -} \ No newline at end of file diff --git a/nw/src/main/resources/application.properties_sample b/nw/src/main/resources/application.properties_sample index 12bebb8..f1fcdda 100644 --- a/nw/src/main/resources/application.properties_sample +++ b/nw/src/main/resources/application.properties_sample @@ -3,10 +3,6 @@ spring.data.mongodb.uri=mongodb://databaseUserName:databaseUserPass@databaseURI: spring.jackson.default-property-inclusion=NON_NULL # logging.level.ROOT=DEBUG -# Redis connection properties -spring.data.redis.host=redisHostAddress -spring.data.redis.port=redisHostPort - #set your port server.port=8888 #server.error.include-message=always @@ -31,16 +27,14 @@ lab.cherry.nw.jwtSecret= bGFiLWNoZXJyeS1udy1wcm9qZWN0LXNlY3JldC1rZXkK lab.cherry.nw.jwtExpirationMs= 86400000 #Email -spring.mail.host=smtp.localhost.com -spring.mail.port=587 -spring.mail.properties.debug=true +spring.mail.host=smtp.naver.com +spring.mail.port=465 +spring.mail.username=userId@naver.com +spring.mail.password=userPassword spring.mail.properties.mail.smtp.auth=true -spring.mail.properties.mail.smtp.ssl.enable=false spring.mail.properties.mail.smtp.starttls.enable=true -spring.mail.properties.mail.smtp.starttls.required=true -spring.mail.username=userName -spring.mail.password=passWord + #echo 'lab-cherry-nw-project-secret-key' | base64 lab.cherry.nw.jwtSecret= bGFiLWNoZXJyeS1udy1wcm9qZWN0LXNlY3JldC1rZXkK