From 8f8644faed486c0ea740e88ea969c679b0263040 Mon Sep 17 00:00:00 2001 From: bjkim Date: Tue, 5 Aug 2025 13:29:24 +0900 Subject: [PATCH] =?UTF-8?q?[ADD]=20=EC=9B=8C=ED=81=AC=ED=94=8C=EB=A1=9C?= =?UTF-8?q?=EC=9A=B0=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ProjectPermissionController.java | 2 +- .../controllers/WorkflowController.java | 68 ++++++++++++++++++ .../etri/autoflow/entity/WorkflowEntity.java | 42 +++++++++++ .../repository/WorkflowRepository.java | 7 ++ .../etri/autoflow/security/jwt/JwtUtils.java | 70 +++++++++---------- .../autoflow/service/WorkflowService.java | 36 ++++++++++ 6 files changed, 187 insertions(+), 38 deletions(-) create mode 100644 src/main/java/kr/re/etri/autoflow/controllers/WorkflowController.java create mode 100644 src/main/java/kr/re/etri/autoflow/entity/WorkflowEntity.java create mode 100644 src/main/java/kr/re/etri/autoflow/repository/WorkflowRepository.java create mode 100644 src/main/java/kr/re/etri/autoflow/service/WorkflowService.java diff --git a/src/main/java/kr/re/etri/autoflow/controllers/ProjectPermissionController.java b/src/main/java/kr/re/etri/autoflow/controllers/ProjectPermissionController.java index f77b93e..d1c379a 100644 --- a/src/main/java/kr/re/etri/autoflow/controllers/ProjectPermissionController.java +++ b/src/main/java/kr/re/etri/autoflow/controllers/ProjectPermissionController.java @@ -15,7 +15,7 @@ import java.util.Set; @RestController @RequestMapping("/api/projects") -@Tag(name = "Project Permissions", description = "프로젝트 사용자 권한 관리 API") +@Tag(name = "프로젝트 권한", description = "프로젝트 사용자 권한 관리 API") @RequiredArgsConstructor public class ProjectPermissionController { diff --git a/src/main/java/kr/re/etri/autoflow/controllers/WorkflowController.java b/src/main/java/kr/re/etri/autoflow/controllers/WorkflowController.java new file mode 100644 index 0000000..8130e2b --- /dev/null +++ b/src/main/java/kr/re/etri/autoflow/controllers/WorkflowController.java @@ -0,0 +1,68 @@ +package kr.re.etri.autoflow.controllers; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import kr.re.etri.autoflow.entity.WorkflowEntity; +import kr.re.etri.autoflow.service.WorkflowService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@Tag(name = "워크플로우", description = "워크플로우 API") +@RestController +@RequestMapping("/api/workflows") +@RequiredArgsConstructor +public class WorkflowController { + + private final WorkflowService workflowService; + + @Operation(summary = "모든 워크플로우 조회") + @GetMapping + public ResponseEntity> getAllWorkflows() { + return ResponseEntity.ok(workflowService.findAll()); + } + + @Operation(summary = "워크플로우 단건 조회") + @GetMapping("/{id}") + public ResponseEntity getWorkflow( + @Parameter(description = "워크플로우 ID", example = "1") @PathVariable("id") Long id) { + + return workflowService.findById(id) + .map(ResponseEntity::ok) + .orElse(ResponseEntity.notFound().build()); + } + + @Operation(summary = "워크플로우 등록") + @PostMapping + public ResponseEntity createWorkflow(@RequestBody WorkflowEntity workflow) { + return ResponseEntity.ok(workflowService.save(workflow)); + } + + @Operation(summary = "워크플로우 수정") + @PutMapping("/{id}") + public ResponseEntity updateWorkflow( + @Parameter(description = "워크플로우 ID", example = "1") @PathVariable("id") Long id, + @RequestBody WorkflowEntity workflow) { + + return workflowService.findById(id) + .map(existing -> { + workflow.setWorkflowId(id); + return ResponseEntity.ok(workflowService.save(workflow)); + }) + .orElse(ResponseEntity.notFound().build()); + } + + @Operation(summary = "워크플로우 삭제") + @DeleteMapping("/{id}") + public ResponseEntity deleteWorkflow( + @Parameter(description = "워크플로우 ID", example = "1") @PathVariable("id") Long id) { + + if (workflowService.deleteById(id)) { + return ResponseEntity.noContent().build(); + } + return ResponseEntity.notFound().build(); + } +} diff --git a/src/main/java/kr/re/etri/autoflow/entity/WorkflowEntity.java b/src/main/java/kr/re/etri/autoflow/entity/WorkflowEntity.java new file mode 100644 index 0000000..2416c1b --- /dev/null +++ b/src/main/java/kr/re/etri/autoflow/entity/WorkflowEntity.java @@ -0,0 +1,42 @@ +package kr.re.etri.autoflow.entity; + +import jakarta.persistence.*; +import lombok.*; +import java.time.LocalDateTime; +import io.swagger.v3.oas.annotations.media.Schema; + +@Entity +@Table(name = "tb_workflows") +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Schema(description = "워크플로우 엔티티") +public class WorkflowEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Schema(description = "워크플로우 ID", example = "null") + private Long workflowId; + + + @Schema(description = "워크플로우 이름", example = "데이터 전처리 워크플로우") + private String workflowName; + + @Schema(description = "워크플로우 설명", example = "ETL 파이프라인 정의") + private String workflowDescription; + + @Column(columnDefinition = "CHAR(1) DEFAULT 'N'") + @Schema(description = "업로드 여부", example = "Y") + private String uploadYn; + + @Schema(description = "등록자 ID", example = "admin") + private String regUserId; + + @Schema(description = "등록 일시", example = "2025-08-05T04:11:24.745") + private LocalDateTime regDt; + + @Schema(description = "수정 일시", example = "2025-08-05T04:11:24.745") + private LocalDateTime modDt; +} diff --git a/src/main/java/kr/re/etri/autoflow/repository/WorkflowRepository.java b/src/main/java/kr/re/etri/autoflow/repository/WorkflowRepository.java new file mode 100644 index 0000000..aedc3dd --- /dev/null +++ b/src/main/java/kr/re/etri/autoflow/repository/WorkflowRepository.java @@ -0,0 +1,7 @@ +package kr.re.etri.autoflow.repository; + +import kr.re.etri.autoflow.entity.WorkflowEntity; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface WorkflowRepository extends JpaRepository { +} diff --git a/src/main/java/kr/re/etri/autoflow/security/jwt/JwtUtils.java b/src/main/java/kr/re/etri/autoflow/security/jwt/JwtUtils.java index c05311c..c528215 100644 --- a/src/main/java/kr/re/etri/autoflow/security/jwt/JwtUtils.java +++ b/src/main/java/kr/re/etri/autoflow/security/jwt/JwtUtils.java @@ -6,8 +6,6 @@ import java.util.Date; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.ResponseCookie; import org.springframework.stereotype.Component; @@ -19,10 +17,11 @@ import kr.re.etri.autoflow.security.services.UserDetailsImpl; import io.jsonwebtoken.*; import io.jsonwebtoken.io.Decoders; import io.jsonwebtoken.security.Keys; +import lombok.extern.slf4j.Slf4j; @Component +@Slf4j public class JwtUtils { - private static final Logger logger = LoggerFactory.getLogger(JwtUtils.class); @Value("${bezkoder.app.jwtSecret}") private String jwtSecret; @@ -32,88 +31,85 @@ public class JwtUtils { @Value("${bezkoder.app.jwtCookieName}") private String jwtCookie; - + @Value("${bezkoder.app.jwtRefreshCookieName}") private String jwtRefreshCookie; public ResponseCookie generateJwtCookie(UserDetailsImpl userPrincipal) { - String jwt = generateTokenFromUsername(userPrincipal.getUsername()); + String jwt = generateTokenFromUsername(userPrincipal.getUsername()); return generateCookie(jwtCookie, jwt, "/api"); } - + public ResponseCookie generateJwtCookie(User user) { - String jwt = generateTokenFromUsername(user.getUsername()); + String jwt = generateTokenFromUsername(user.getUsername()); return generateCookie(jwtCookie, jwt, "/api"); } - + public ResponseCookie generateRefreshJwtCookie(String refreshToken) { return generateCookie(jwtRefreshCookie, refreshToken, "/api/auth/refreshtoken"); } - + public String getJwtFromCookies(HttpServletRequest request) { return getCookieValueByName(request, jwtCookie); } - + public String getJwtRefreshFromCookies(HttpServletRequest request) { return getCookieValueByName(request, jwtRefreshCookie); } public ResponseCookie getCleanJwtCookie() { - ResponseCookie cookie = ResponseCookie.from(jwtCookie, null).path("/api").build(); - return cookie; + return ResponseCookie.from(jwtCookie, null).path("/api").build(); } - + public ResponseCookie getCleanJwtRefreshCookie() { - ResponseCookie cookie = ResponseCookie.from(jwtRefreshCookie, null).path("/api/auth/refreshtoken").build(); - return cookie; + return ResponseCookie.from(jwtRefreshCookie, null).path("/api/auth/refreshtoken").build(); } public String getUserNameFromJwtToken(String token) { return Jwts.parserBuilder().setSigningKey(key()).build() - .parseClaimsJws(token).getBody().getSubject(); + .parseClaimsJws(token).getBody().getSubject(); } private Key key() { return Keys.hmacShaKeyFor(Decoders.BASE64.decode(jwtSecret)); } - + public boolean validateJwtToken(String authToken) { try { Jwts.parserBuilder().setSigningKey(key()).build().parse(authToken); return true; } catch (MalformedJwtException e) { - logger.error("Invalid JWT token: {}", e.getMessage()); + log.error("Invalid JWT token: {}", e.getMessage()); } catch (ExpiredJwtException e) { - logger.error("JWT token is expired: {}", e.getMessage()); + log.error("JWT token is expired: {}", e.getMessage()); } catch (UnsupportedJwtException e) { - logger.error("JWT token is unsupported: {}", e.getMessage()); + log.error("JWT token is unsupported: {}", e.getMessage()); } catch (IllegalArgumentException e) { - logger.error("JWT claims string is empty: {}", e.getMessage()); + log.error("JWT claims string is empty: {}", e.getMessage()); } return false; } - - public String generateTokenFromUsername(String username) { + + public String generateTokenFromUsername(String username) { return Jwts.builder() - .setSubject(username) - .setIssuedAt(new Date()) - .setExpiration(new Date((new Date()).getTime() + jwtExpirationMs)) - .signWith(key(), SignatureAlgorithm.HS256) - .compact(); + .setSubject(username) + .setIssuedAt(new Date()) + .setExpiration(new Date((new Date()).getTime() + jwtExpirationMs)) + .signWith(key(), SignatureAlgorithm.HS256) + .compact(); } - + private ResponseCookie generateCookie(String name, String value, String path) { - ResponseCookie cookie = ResponseCookie.from(name, value).path(path).maxAge(24 * 60 * 60).httpOnly(true).build(); - return cookie; + return ResponseCookie.from(name, value) + .path(path) + .maxAge(24 * 60 * 60) + .httpOnly(true) + .build(); } - + private String getCookieValueByName(HttpServletRequest request, String name) { Cookie cookie = WebUtils.getCookie(request, name); - if (cookie != null) { - return cookie.getValue(); - } else { - return null; - } + return cookie != null ? cookie.getValue() : null; } } diff --git a/src/main/java/kr/re/etri/autoflow/service/WorkflowService.java b/src/main/java/kr/re/etri/autoflow/service/WorkflowService.java new file mode 100644 index 0000000..45aa7a9 --- /dev/null +++ b/src/main/java/kr/re/etri/autoflow/service/WorkflowService.java @@ -0,0 +1,36 @@ +package kr.re.etri.autoflow.service; + +import kr.re.etri.autoflow.entity.WorkflowEntity; +import kr.re.etri.autoflow.repository.WorkflowRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Optional; + +@Service +@RequiredArgsConstructor +public class WorkflowService { + + private final WorkflowRepository workflowRepository; + + public List findAll() { + return workflowRepository.findAll(); + } + + public Optional findById(Long id) { + return workflowRepository.findById(id); + } + + public WorkflowEntity save(WorkflowEntity entity) { + return workflowRepository.save(entity); + } + + public boolean deleteById(Long id) { + if (workflowRepository.existsById(id)) { + workflowRepository.deleteById(id); + return true; + } + return false; + } +}