From fee78971e94dc1854499e6d1fed2a2dc5f456768 Mon Sep 17 00:00:00 2001
From: NaHyun22 <nhle0217@ajou.ac.kr>
Date: Wed, 30 Apr 2025 19:10:55 +0900
Subject: [PATCH] =?UTF-8?q?feat:=20certificate=20=EC=9D=B8=EC=A6=9D?=
 =?UTF-8?q?=EC=84=9C=20=EB=B0=9C=EA=B8=89?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../certificate/CertificateController.java    |  2 +
 .../itda/dto/certificate/CertificateDTO.java  | 18 ++--
 .../itda/entity/certificate/Certificate.java  |  5 +-
 .../certificate/CertificateService.java       | 88 ++++++++++++++++++-
 4 files changed, 100 insertions(+), 13 deletions(-)

diff --git a/src/main/java/com/aolda/itda/controller/certificate/CertificateController.java b/src/main/java/com/aolda/itda/controller/certificate/CertificateController.java
index b6384c9..cf5e160 100644
--- a/src/main/java/com/aolda/itda/controller/certificate/CertificateController.java
+++ b/src/main/java/com/aolda/itda/controller/certificate/CertificateController.java
@@ -25,6 +25,7 @@ public class CertificateController {
     public ResponseEntity<Object> create(@RequestParam String projectId,
                                          @RequestBody CertificateDTO dto,
                                          HttpServletRequest request) {
+        System.out.println("1");
         certificateService.createCertificate(
                 projectId,
                 dto,
@@ -89,5 +90,6 @@ public class CertificateController {
         );
         return ResponseEntity.ok().build();
     }
+    /*인증서 검색*/
 
 }
diff --git a/src/main/java/com/aolda/itda/dto/certificate/CertificateDTO.java b/src/main/java/com/aolda/itda/dto/certificate/CertificateDTO.java
index 35aad94..e37b75e 100644
--- a/src/main/java/com/aolda/itda/dto/certificate/CertificateDTO.java
+++ b/src/main/java/com/aolda/itda/dto/certificate/CertificateDTO.java
@@ -1,5 +1,6 @@
 package com.aolda.itda.dto.certificate;
 
+import com.aolda.itda.entity.certificate.Challenge;
 import com.fasterxml.jackson.annotation.JsonInclude;
 import lombok.*;
 
@@ -12,10 +13,15 @@ import java.time.LocalDateTime;
 @JsonInclude(JsonInclude.Include.NON_NULL)
 public class CertificateDTO {
 
-    private Long certificateId;
-    private String projectId;
-    private String domain;
-    private LocalDateTime expiredAt;   // 필요 시
-    private Boolean isDeleted;
-    private String description;        // 추가 설명
+    private Long certificateId;          // 인증서 고유 ID
+    private String projectId;            // 프로젝트 식별자
+    private String domain;               // SSL 인증받을 도메인 주소
+    private String email;                // 도메인 소유자의 이메일
+    private LocalDateTime expiredAt;     // 인증서 만료일
+    private Challenge challenge;         // 챌린지 방식 (HTTP, DNS_CLOUDFLARE)
+    private Boolean isDeleted;           // 삭제 여부 (soft delete)
+    private String description;          // 설명 (필요시 자유롭게 작성)
 }
+/* 이메일, 챌린지 방식, http인지 dns인지... "*/
+//도메인, 소유자 이메일, 챌린지 방식 확실하게 들어가야함!!
+/*erd 보고 만들기*/
diff --git a/src/main/java/com/aolda/itda/entity/certificate/Certificate.java b/src/main/java/com/aolda/itda/entity/certificate/Certificate.java
index 469ea6a..489138f 100644
--- a/src/main/java/com/aolda/itda/entity/certificate/Certificate.java
+++ b/src/main/java/com/aolda/itda/entity/certificate/Certificate.java
@@ -23,10 +23,6 @@ public class Certificate extends BaseTimeEntity {
     @Column(nullable = false)
     private Long certificateId;
 
-    @ManyToOne
-    @JoinColumn(nullable = false, name = "user_id")
-    private User user;
-
     @Column(length = 64)
     private String projectId;
 
@@ -57,5 +53,6 @@ public class Certificate extends BaseTimeEntity {
     }
 
     public void setDescription(String description) {
+
     }
 }
diff --git a/src/main/java/com/aolda/itda/service/certificate/CertificateService.java b/src/main/java/com/aolda/itda/service/certificate/CertificateService.java
index 5c0dbaf..f6f83bb 100644
--- a/src/main/java/com/aolda/itda/service/certificate/CertificateService.java
+++ b/src/main/java/com/aolda/itda/service/certificate/CertificateService.java
@@ -3,6 +3,7 @@ package com.aolda.itda.service.certificate;
 import com.aolda.itda.dto.PageResp;
 import com.aolda.itda.dto.certificate.CertificateDTO;
 import com.aolda.itda.entity.certificate.Certificate;
+import com.aolda.itda.entity.certificate.Challenge;
 import com.aolda.itda.exception.CustomException;
 import com.aolda.itda.exception.ErrorCode;
 import com.aolda.itda.repository.certificate.CertificateRepository;
@@ -14,6 +15,7 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.util.ArrayList;
 import java.util.List;
 
 @Service
@@ -52,10 +54,12 @@ public class CertificateService {
     }
 
     /* 인증서 생성 */
-    public CertificateDTO createCertificate(String projectId, CertificateDTO dto, List<String> projects) {
+    /*public CertificateDTO createCertificate(String projectId,
+                                            CertificateDTO dto,
+                                            List<String> projects) {
         // 프로젝트 권한 검증
         authService.validateProjectAuth(projects, projectId);
-
+        System.out.println("2");
         // DTO 유효성 검사
         validateDTO(dto);
 
@@ -67,9 +71,52 @@ public class CertificateService {
                 .build();
 
         certificateRepository.save(certificate);
-
+        System.out.println("3");
         // 생성 로직 (certbot 호출 등) 필요 시 추가
 
+        return toDTO(certificate);
+    }*/
+    /* 인증서 생성 + lego 호출 */
+    public CertificateDTO createCertificate(String projectId, CertificateDTO dto, List<String> projects) {
+
+        // 1) 권한 체크
+        authService.validateProjectAuth(projects, projectId);
+
+        // 2) DTO 검증
+        validateDTO(dto);
+
+        // 3) lego 명령어 구성
+        ProcessBuilder pb = buildLegoProcess(dto);
+
+        // 4) lego 실행
+        int exitCode;
+        try {
+            Process process = pb.start();
+            exitCode = process.waitFor();
+            if (exitCode != 0) {
+                String err = new String(process.getErrorStream().readAllBytes());
+                log.error("[lego-error] {}", err);
+                throw new CustomException(ErrorCode.FAIL_CREATE_CONF,
+                        "lego 오류: " + err);
+            }
+        } catch (Exception e) {
+            log.error("[lego-exec] {}", e.getMessage());
+            throw new CustomException(ErrorCode.FAIL_CREATE_CONF,
+                    "lego 실행 실패");
+        }
+
+        // 5) 엔티티 저장
+        Certificate certificate = Certificate.builder()
+                .projectId(projectId)
+                .domain(dto.getDomain())
+                .email(dto.getEmail())
+                .challenge(dto.getChallenge())
+                .expiredAt(dto.getExpiredAt())   // 필요 시 lego 출력 파싱
+                .isDeleted(false)
+                .description(dto.getDescription())
+                .build();
+
+        certificateRepository.save(certificate);
         return toDTO(certificate);
     }
 
@@ -134,4 +181,39 @@ public class CertificateService {
                 .expiredAt(certificate.getExpiredAt())
                 .build();
     }
+
+
+
+    /* lego ProcessBuilder 생성 */
+    private ProcessBuilder buildLegoProcess(CertificateDTO dto) {
+
+        String basePath = "/data/lego"; // 인증서 저장 루트(볼륨)
+        List<String> cmd = new ArrayList<>();
+        cmd.add("/usr/local/bin/lego");
+        cmd.add("--accept-tos");
+        cmd.add("--email");        cmd.add(dto.getEmail());
+        cmd.add("--path");         cmd.add(basePath);
+
+        if (dto.getChallenge() == Challenge.HTTP) {
+            cmd.add("--http");
+            cmd.add("--http.webroot");
+            cmd.add("/data/letsencrypt-acme-challenge");
+        } else if (dto.getChallenge() == Challenge.DNS_CLOUDFLARE) {
+            cmd.add("--dns");
+            cmd.add("cloudflare");
+            // CLOUDFLARE_API_TOKEN 환경변수를 컨테이너에 세팅했다고 가정
+        }
+
+        cmd.add("--domains");      cmd.add(dto.getDomain());
+        cmd.add("run");            // 최초 발급(run) / renew(갱신)
+
+        return new ProcessBuilder(cmd)
+                .redirectErrorStream(true);
+    }
 }
+
+
+
+// 여기서 매소드를 create로 해서 lego --accept-tos --email "email@example.com" --http --http.webroot data/letsencrypt-acme-challenge --path /data/lego --domains www.example.com run
+// 이거 이메일 . 도메인 으로 넣어서 실제 인증서 연동하기!!!
+// 그리고 Dto에 관리자 이메일, 인증서 완료일, 챌린지 방식 추가하기
\ No newline at end of file
-- 
GitLab