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