diff --git a/src/main/java/com/aolda/itda/config/WebConfig.java b/src/main/java/com/aolda/itda/config/WebConfig.java index fd0080766c4816b4257be9c7c19dd0741d1da648..71e134bebee94ab01c47d0a032e6026d9929b62b 100644 --- a/src/main/java/com/aolda/itda/config/WebConfig.java +++ b/src/main/java/com/aolda/itda/config/WebConfig.java @@ -25,7 +25,7 @@ public class WebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { - String[] excludeAuth = {"/error", "/api/auth/*" }; + String[] excludeAuth = {"/error", "/api/auth/*", "/api/*" }; registry.addInterceptor(authInterceptor) .addPathPatterns("/**") diff --git a/src/main/java/com/aolda/itda/controller/forwarding/ForwardingController.java b/src/main/java/com/aolda/itda/controller/forwarding/ForwardingController.java index 19786bd5a3c7d08eaa8d691ceea2d95e0ee62d15..a6b67ecbd0090ce8f8460d3fd8b08def88d520a8 100644 --- a/src/main/java/com/aolda/itda/controller/forwarding/ForwardingController.java +++ b/src/main/java/com/aolda/itda/controller/forwarding/ForwardingController.java @@ -2,6 +2,7 @@ package com.aolda.itda.controller.forwarding; import com.aolda.itda.dto.forwarding.ForwardingDTO; import com.aolda.itda.service.forwarding.ForwardingService; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; diff --git a/src/main/java/com/aolda/itda/controller/routing/RoutingController.java b/src/main/java/com/aolda/itda/controller/routing/RoutingController.java new file mode 100644 index 0000000000000000000000000000000000000000..894357188678e8f232a109e9aab99642ece56bce --- /dev/null +++ b/src/main/java/com/aolda/itda/controller/routing/RoutingController.java @@ -0,0 +1,39 @@ +package com.aolda.itda.controller.routing; + +import com.aolda.itda.dto.routing.RoutingDTO; +import com.aolda.itda.service.routing.RoutingService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api") +@RequiredArgsConstructor +public class RoutingController { + + private final RoutingService routingService; + + @GetMapping("/routing") + public ResponseEntity<Object> view(@RequestParam Long routingId) { + return ResponseEntity.ok(routingService.getRouting(routingId)); + } + + @GetMapping("/routings") + public ResponseEntity<Object> lists(@RequestParam String projectId) { + return ResponseEntity.ok(routingService.getRoutings(projectId)); + } + + @PatchMapping("/routing") + public ResponseEntity<Object> edit(@RequestParam Long routingId, + @RequestBody RoutingDTO dto) { + routingService.editRouting(routingId, dto); + return ResponseEntity.ok().build(); + } + + @DeleteMapping("/routing") + public ResponseEntity<Object> delete(@RequestParam Long routingId) { + routingService.deleteRouting(routingId); + return ResponseEntity.ok().build(); + } + +} diff --git a/src/main/java/com/aolda/itda/dto/routing/RoutingDTO.java b/src/main/java/com/aolda/itda/dto/routing/RoutingDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..e95ab70e8763ab7c3db0a5e7d884d6e50662b1bf --- /dev/null +++ b/src/main/java/com/aolda/itda/dto/routing/RoutingDTO.java @@ -0,0 +1,37 @@ +package com.aolda.itda.dto.routing; + +import com.fasterxml.jackson.annotation.JsonInclude; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +@JsonInclude(JsonInclude.Include.NON_NULL) +public class RoutingDTO { + @NotBlank + private String ip; + @NotBlank + private String domain; + @NotBlank + private String name; + @NotBlank + private String port; + private Long id; + @NotBlank + private Long certificateId; + + private LocalDateTime createdAt; + + private LocalDateTime updatedAt; + + @NotNull + private Boolean caching; +} 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 5ab56a9239325d5745e7255d00e79c9538a21698..188f05e106121abe6f26b70fde078e0bd8d50662 100644 --- a/src/main/java/com/aolda/itda/entity/certificate/Certificate.java +++ b/src/main/java/com/aolda/itda/entity/certificate/Certificate.java @@ -42,5 +42,7 @@ public class Certificate extends BaseTimeEntity { private String description; - + public String formatDomain() { + return domain == null ? null : domain.replace("*", "_"); + } } diff --git a/src/main/java/com/aolda/itda/entity/log/Log.java b/src/main/java/com/aolda/itda/entity/log/Log.java index fd8f460bba7940730772841c7451a8815412dd1b..fa78a6677f3cbb66322056a83dfd2a404eb5435c 100644 --- a/src/main/java/com/aolda/itda/entity/log/Log.java +++ b/src/main/java/com/aolda/itda/entity/log/Log.java @@ -35,6 +35,6 @@ public class Log extends BaseTimeEntity { @Enumerated(EnumType.STRING) private Action action; - private String metadata; + private String description; } diff --git a/src/main/java/com/aolda/itda/entity/routing/Routing.java b/src/main/java/com/aolda/itda/entity/routing/Routing.java index 0c6b2aab53b5cc4a4611aaff02316febad651257..be03dcd3edd27000c8c00aea2437ac65da1c72d5 100644 --- a/src/main/java/com/aolda/itda/entity/routing/Routing.java +++ b/src/main/java/com/aolda/itda/entity/routing/Routing.java @@ -1,5 +1,7 @@ package com.aolda.itda.entity.routing; +import com.aolda.itda.dto.forwarding.ForwardingDTO; +import com.aolda.itda.dto.routing.RoutingDTO; import com.aolda.itda.entity.BaseTimeEntity; import com.aolda.itda.entity.certificate.Certificate; import com.aolda.itda.entity.user.User; @@ -36,8 +38,38 @@ public class Routing extends BaseTimeEntity { private String instanceIp; + private String instancePort; + private Boolean isDeleted; - private String description; + private Boolean caching; + + private String name; + + public RoutingDTO toRoutingDTO() { + return RoutingDTO.builder() + .id(routingId) + .name(name) + .port(instancePort) + .ip(instanceIp) + .certificateId(certificate == null ? null : certificate.getCertificateId()) + .caching(caching) + .domain(domain) + .createdAt(getCreatedAt()) + .updatedAt(getUpdatedAt()) + .build(); + } + + public void edit(RoutingDTO dto, Certificate certificate) { + this.name = dto.getName() != null ? dto.getName() : this.name; + this.instanceIp = dto.getIp() != null ? dto.getIp() : this.instanceIp; + this.instancePort = dto.getPort() != null ? dto.getPort() : this.instancePort; + this.caching = dto.getCaching() != null ? dto.getCaching() : this.caching; + this.domain = dto.getDomain() != null ? dto.getDomain() : this.domain; + this.certificate = certificate; + } + public void delete() { + this.isDeleted = true; + } } diff --git a/src/main/java/com/aolda/itda/exception/ErrorCode.java b/src/main/java/com/aolda/itda/exception/ErrorCode.java index 443f3a10f7c7a2a5ac2f876187a2d88a2e911fe9..321bdaddd8b6eb5a3f523e8e56b5d738e3105319 100644 --- a/src/main/java/com/aolda/itda/exception/ErrorCode.java +++ b/src/main/java/com/aolda/itda/exception/ErrorCode.java @@ -15,12 +15,21 @@ public enum ErrorCode { INVALID_TOKEN(HttpStatus.BAD_REQUEST, "잘못된 토큰입니다"), // Forwarding + NOT_FOUND_FORWARDING(HttpStatus.BAD_REQUEST, "포트포워딩 파일이 존재하지 않습니다"), + + // Routing + NOT_FOUND_ROUTING(HttpStatus.BAD_REQUEST, "라우팅 파일이 존재하지 않습니다"), + + // Certificate + NOT_FOUND_CERTIFICATE(HttpStatus.BAD_REQUEST, "SSL 인증서가 존재하지 않습니다"), + + // CONF File FAIL_CREATE_CONF(HttpStatus.BAD_REQUEST, "Conf 파일을 생성하지 못했습니다"), FAIL_UPDATE_CONF(HttpStatus.BAD_REQUEST, "Conf 파일을 수정하지 못했습니다"), - NOT_FOUND_FORWARDING(HttpStatus.BAD_REQUEST, "포트포워딩 파일이 존재하지 않습니다"), INVALID_CONF_INPUT(HttpStatus.BAD_REQUEST, "잘못된 입력이 존재합니다"), DUPLICATED_INSTANCE_INFO(HttpStatus.BAD_REQUEST, "중복된 인스턴스 IP와 포트입니다"), DUPLICATED_SERVER_PORT(HttpStatus.BAD_REQUEST, "중복된 서버 포트입니다"), + DUPLICATED_DOMAIN_NAME(HttpStatus.BAD_REQUEST, "중복된 도메인 주소입니다"), // Nginx FAIL_NGINX_CONF_TEST(HttpStatus.BAD_REQUEST, "Conf 파일 테스트에 실패했습니다"), diff --git a/src/main/java/com/aolda/itda/repository/certificate/CertificateRepository.java b/src/main/java/com/aolda/itda/repository/certificate/CertificateRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..392c90b0aa31f7b445067f7db1ef490a379abf5d --- /dev/null +++ b/src/main/java/com/aolda/itda/repository/certificate/CertificateRepository.java @@ -0,0 +1,7 @@ +package com.aolda.itda.repository.certificate; + +import com.aolda.itda.entity.certificate.Certificate; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface CertificateRepository extends JpaRepository<Certificate, Long> { +} diff --git a/src/main/java/com/aolda/itda/repository/routing/RoutingRepository.java b/src/main/java/com/aolda/itda/repository/routing/RoutingRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..046296c2df3937d920b4816734d16ce8267b6a1f --- /dev/null +++ b/src/main/java/com/aolda/itda/repository/routing/RoutingRepository.java @@ -0,0 +1,14 @@ +package com.aolda.itda.repository.routing; + +import com.aolda.itda.entity.forwarding.Forwarding; +import com.aolda.itda.entity.routing.Routing; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; +import java.util.Optional; + +public interface RoutingRepository extends JpaRepository<Routing, Long> { + List<Routing> findByProjectIdAndIsDeleted(String projectId, Boolean isDeleted); + Optional<Routing> findByRoutingIdAndIsDeleted(Long routingId, Boolean isDeleted); + Boolean existsByDomainAndIsDeleted(String domain, Boolean isDeleted); +} diff --git a/src/main/java/com/aolda/itda/service/routing/RoutingService.java b/src/main/java/com/aolda/itda/service/routing/RoutingService.java new file mode 100644 index 0000000000000000000000000000000000000000..9cfad7d4ec75d158fce65ced051f8036749ef5b2 --- /dev/null +++ b/src/main/java/com/aolda/itda/service/routing/RoutingService.java @@ -0,0 +1,325 @@ +package com.aolda.itda.service.routing; + +import com.aolda.itda.dto.PageResp; +import com.aolda.itda.dto.routing.RoutingDTO; +import com.aolda.itda.entity.certificate.Certificate; +import com.aolda.itda.entity.routing.Routing; +import com.aolda.itda.exception.CustomException; +import com.aolda.itda.exception.ErrorCode; +import com.aolda.itda.repository.certificate.CertificateRepository; +import com.aolda.itda.repository.routing.RoutingRepository; +import com.aolda.itda.template.RoutingTemplate; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.Validation; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.client.HttpServerErrorException; +import org.springframework.web.client.RestTemplate; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; + +@Service +@Transactional +@RequiredArgsConstructor +@Slf4j +public class RoutingService { + + private final RoutingRepository routingRepository; + private final CertificateRepository certificateRepository; + private final RoutingTemplate routingTemplate; + private final RestTemplate restTemplate = new RestTemplate(); + + /* Routing 조회 */ + public RoutingDTO getRouting(Long routingId) { + // project id 확인 필요 + Routing routing = routingRepository.findByRoutingIdAndIsDeleted(routingId, false) + .orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_ROUTING)); + return routing.toRoutingDTO(); + } + + /* Routing 목록 조회 */ + public PageResp<RoutingDTO> getRoutings(String projectId) { + // project id 확인 필요 + return PageResp.<RoutingDTO>builder() + .contents(routingRepository.findByProjectIdAndIsDeleted(projectId, false) + .stream() + .map(Routing::toRoutingDTO) + .toList()).build(); + } + + /* Routing 생성 */ + public void createRouting(String projectId, RoutingDTO dto) { + /* 입력 DTO 검증 */ + validateDTO(dto); + + /* 중복 검증 */ + if (routingRepository.existsByDomainAndIsDeleted(dto.getDomain(), false)) { + throw new CustomException(ErrorCode.DUPLICATED_DOMAIN_NAME); + } + + /* SSL 인증서 조회 */ + Certificate certificate = dto.getCertificateId() == -1 ? null : + certificateRepository.findById(dto.getCertificateId()) + .orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_CERTIFICATE)); // isDeleted 확인 필요 + + /* 라우팅 엔티티 생성 */ + Routing routing = Routing.builder() + .isDeleted(false) + .projectId(projectId) + .name(dto.getName()) + .instanceIp(dto.getIp()) + .instancePort(dto.getPort()) + .domain(dto.getDomain()) + .certificate(certificate) + .caching(dto.getCaching()) + .build(); + + routingRepository.save(routing); + + /* nginx conf 파일 생성 및 예외 처리 */ + String content = routingTemplate.getRouting(dto, certificate == null ? null : certificate.formatDomain()); + String confPath = "/data/nginx/proxy_host/" + routing.getRoutingId() + ".conf"; + + File file = new File(confPath); + try { + Path path = Paths.get(confPath); + Files.createDirectories(path.getParent()); + if (!file.createNewFile()) { + throw new CustomException(ErrorCode.FAIL_CREATE_CONF, "중복된 라우팅 Conf 파일이 존재합니다"); + } + } catch (IOException e) { + e.printStackTrace(); + throw new CustomException(ErrorCode.FAIL_CREATE_CONF); + } + + /* conf 파일 작성 및 예외 처리 */ + try { + BufferedWriter bw = new BufferedWriter(new FileWriter(file, false)); // 예외처리 필요 + bw.write(content); + bw.flush(); + bw.close(); + } catch (Exception e) { + e.printStackTrace(); + if (file.delete()) { + throw new CustomException(ErrorCode.FAIL_DELETE_CONF); + } + throw new CustomException(ErrorCode.FAIL_CREATE_CONF, "포트포워딩 Conf 파일을 작성하지 못했습니다"); + } + + /* nginx test */ + String url = "http://nginx:8081/nginx-api/test"; + try { + restTemplate.getForEntity(url, String.class); + } catch (HttpServerErrorException.InternalServerError e) { + log.error("[nginxApiException] {} : {}", e.getResponseBodyAsString(), e.getMessage()); + if (file.delete()) { + throw new CustomException(ErrorCode.FAIL_NGINX_CONF_TEST, "(롤백 실패)"); + } + throw new CustomException(ErrorCode.FAIL_NGINX_CONF_TEST); + } catch (Exception e) { + log.error("[RestClientException] {} : {}", "Nginx Conf Test (forwarding)", e.getMessage()); + if (file.delete()) { + throw new CustomException(ErrorCode.FAIL_NGINX_CONF_TEST, "(롤백 실패)"); + } + throw new CustomException(ErrorCode.FAIL_NGINX_CONF_TEST); + } + + /* nginx reload */ + url = "http://nginx:8081/nginx-api/reload"; + try { + restTemplate.getForEntity(url, String.class); + } catch (HttpServerErrorException.InternalServerError e) { + log.error("[nginxApiException] {} : {}", e.getResponseBodyAsString(), e.getMessage()); + if (file.delete()) { + throw new CustomException(ErrorCode.FAIL_NGINX_CONF_TEST, "(롤백 실패)"); + } + throw new CustomException(ErrorCode.FAIL_NGINX_CONF_RELOAD); + } catch (Exception e) { + log.error("[RestClientException] {} : {}", "Nginx Conf Reload (forwarding)", e.getMessage()); + if (file.delete()) { + throw new CustomException(ErrorCode.FAIL_NGINX_CONF_TEST, "(롤백 실패)"); + } + throw new CustomException(ErrorCode.FAIL_NGINX_CONF_RELOAD); + } + + } + + /* Routing 수정 */ + public void editRouting(Long routingId, RoutingDTO dto) { + Routing routing = routingRepository.findByRoutingIdAndIsDeleted(routingId, false) + .orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_ROUTING)); + + /* 입력 DTO 검증 */ + validateDTO(dto); + + /* 중복 검증 */ + if (dto.getDomain() != null && routingRepository.existsByDomainAndIsDeleted(dto.getDomain(), false)) { + throw new CustomException(ErrorCode.DUPLICATED_DOMAIN_NAME); + } + + /* SSL 인증서 조회 */ + Certificate certificate = (dto.getCertificateId() == null) || (dto.getCertificateId() == -1 ) ? null : + certificateRepository.findById(dto.getCertificateId()) + .orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_CERTIFICATE)); // isDeleted 확인 필요 + + /* 파일 수정 */ + routing.edit(dto, certificate); + String content = routingTemplate.getRouting(routing.toRoutingDTO(), certificate == null ? null : certificate.formatDomain()); + String confPath = "/data/nginx/proxy_host/" + routing.getRoutingId() + ".conf"; + File file = new File(confPath); + if (!file.exists()) { + throw new CustomException(ErrorCode.NOT_FOUND_FORWARDING, "Conf 파일이 존재하지 않아 수정할 수 없습니다"); + } + + Path backup; + try { + backup = Files.createTempFile("temp_", ".tmp"); + Files.copy(Paths.get(confPath), backup, StandardCopyOption.REPLACE_EXISTING + , StandardCopyOption.COPY_ATTRIBUTES); + + BufferedWriter bw = new BufferedWriter(new FileWriter(file, false)); + bw.write(content); + bw.flush(); + bw.close(); + } catch (Exception e) { + e.printStackTrace(); + throw new CustomException(ErrorCode.FAIL_UPDATE_CONF, "라우팅 Conf 파일을 수정하지 못했습니다"); + } + + /* nginx test */ + String url = "http://nginx:8081/nginx-api/test"; + try { + restTemplate.getForEntity(url, String.class); + } catch (HttpServerErrorException.InternalServerError e) { + log.error("[nginxApiException] {} : {}", e.getResponseBodyAsString(), e.getMessage()); + try { + Files.copy(backup, Paths.get(confPath), StandardCopyOption.REPLACE_EXISTING + , StandardCopyOption.COPY_ATTRIBUTES); + Files.delete(backup); + } catch (IOException e1) { + throw new CustomException(ErrorCode.FAIL_UPDATE_CONF, "(라우팅 Conf 파일 수정)"); + } + + throw new CustomException(ErrorCode.FAIL_NGINX_CONF_TEST); + } catch (RuntimeException e) { + log.error("[RestClientException] {} : {}", "Nginx Conf Test (forwarding)", e.getMessage()); + try { + Files.copy(backup, Paths.get(confPath), StandardCopyOption.REPLACE_EXISTING + , StandardCopyOption.COPY_ATTRIBUTES); + Files.delete(backup); + } catch (IOException e1) { + throw new CustomException(ErrorCode.FAIL_UPDATE_CONF, "(라우팅 Conf 파일 수정)"); + } + throw new CustomException(ErrorCode.FAIL_NGINX_CONF_TEST); + } + + /* nginx reload */ + url = "http://nginx:8081/nginx-api/reload"; + try { + restTemplate.getForEntity(url, String.class); + } catch (HttpServerErrorException.InternalServerError e) { + log.error("[nginxApiException] {} : {}", e.getResponseBodyAsString(), e.getMessage()); + try { + Files.copy(backup, Paths.get(confPath), StandardCopyOption.REPLACE_EXISTING + , StandardCopyOption.COPY_ATTRIBUTES); + Files.delete(backup); + } catch (IOException e1) { + throw new CustomException(ErrorCode.FAIL_UPDATE_CONF, "(라우팅 Conf 파일 수정)"); + } + throw new CustomException(ErrorCode.FAIL_NGINX_CONF_RELOAD); + } catch (RuntimeException e) { + log.error("[RestClientException] {} : {}", "Nginx Conf Reload (forwarding)", e.getMessage()); + try { + Files.copy(backup, Paths.get(confPath), StandardCopyOption.REPLACE_EXISTING + , StandardCopyOption.COPY_ATTRIBUTES); + Files.delete(backup); + } catch (IOException e1) { + throw new CustomException(ErrorCode.FAIL_UPDATE_CONF, "(라우팅 Conf 파일 수정)"); + } + throw new CustomException(ErrorCode.FAIL_NGINX_CONF_RELOAD); + } + + /* DB 정보 수정 */ + routingRepository.save(routing); + } + + /* Routing 삭제 */ + public void deleteRouting(Long routingId) { + Routing routing = routingRepository.findByRoutingIdAndIsDeleted(routingId, false) + .orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_ROUTING)); + + /* 파일 삭제 */ + String confPath = "/data/nginx/proxy_host/" + routing.getRoutingId() + ".conf"; + String deletePath = confPath + ".deleted"; + try { + Files.move(Paths.get(confPath), Paths.get(deletePath)); + } catch (IOException e) { + throw new CustomException(ErrorCode.FAIL_DELETE_CONF); + } + + /* nginx test */ + String url = "http://nginx:8081/nginx-api/test"; + try { + restTemplate.getForEntity(url, String.class); + } catch (HttpServerErrorException.InternalServerError e) { + log.error("[nginxApiException] {} : {}", e.getResponseBodyAsString(), e.getMessage()); + try { + Files.move(Paths.get(deletePath), Paths.get(confPath)); + } catch (IOException e1) { + throw new CustomException(ErrorCode.FAIL_ROLL_BACK, "(라우팅 Conf 삭제)"); + } + throw new CustomException(ErrorCode.FAIL_NGINX_CONF_TEST); + } catch (Exception e) { + log.error("[RestClientException] {} : {}", "Nginx Conf Test (forwarding)", e.getMessage()); + try { + Files.move(Paths.get(deletePath), Paths.get(confPath)); + } catch (IOException e1) { + throw new CustomException(ErrorCode.FAIL_ROLL_BACK, "(라우팅 Conf 삭제)"); + } + throw new CustomException(ErrorCode.FAIL_NGINX_CONF_TEST); + } + + /* nginx reload */ + url = "http://nginx:8081/nginx-api/reload"; + try { + restTemplate.getForEntity(url, String.class); + } catch (HttpServerErrorException.InternalServerError e) { + log.error("[nginxApiException] {} : {}", e.getResponseBodyAsString(), e.getMessage()); + try { + Files.move(Paths.get(deletePath), Paths.get(confPath)); + } catch (IOException e1) { + throw new CustomException(ErrorCode.FAIL_ROLL_BACK, "(라우팅 Conf 삭제)"); + } + throw new CustomException(ErrorCode.FAIL_NGINX_CONF_RELOAD); + } catch (Exception e) { + log.error("[RestClientException] {} : {}", "Nginx Conf Reload (forwarding)", e.getMessage()); + try { + Files.move(Paths.get(deletePath), Paths.get(confPath)); + } catch (IOException e1) { + throw new CustomException(ErrorCode.FAIL_ROLL_BACK, "(라우팅 Conf 삭제)"); + } + throw new CustomException(ErrorCode.FAIL_NGINX_CONF_RELOAD); + } + + /* DB */ + routing.delete(); + routingRepository.save(routing); + } + + private void validateDTO(RoutingDTO dto) { + + for (ConstraintViolation<RoutingDTO> violation : Validation.buildDefaultValidatorFactory().getValidator().validate(dto)) { + throw new CustomException(ErrorCode.INVALID_CONF_INPUT, violation.getMessage()); + } + + } +} diff --git a/src/main/java/com/aolda/itda/template/OptionTemplate.java b/src/main/java/com/aolda/itda/template/OptionTemplate.java index 3e1e92c77efaf1eebec1af4cc1b283255c501a0e..945b66609b565f62bb380e61e29952ea0e9cad9e 100644 --- a/src/main/java/com/aolda/itda/template/OptionTemplate.java +++ b/src/main/java/com/aolda/itda/template/OptionTemplate.java @@ -5,22 +5,22 @@ import org.springframework.stereotype.Component; @Component public class OptionTemplate { - public String getSSL(Long certificateId) { + public String getSSL(String certificateDomain) { return "\nconf.d/include/letsencrypt-acme-challenge.conf;\n" + "include conf.d/include/ssl-ciphers.conf;\n" + - "ssl_certificate /etc/letsencrypt/live/npm-" + certificateId + "/fullchain.pem;\n" + - "ssl_certificate_key /etc/letsencrypt/live/npm-" + certificateId + "/privkey.pem;\n"; + "ssl_certificate /data/lego/certificates/" + certificateDomain + ".crt;\n" + + "ssl_certificate_key /data/lego/certificates/" + certificateDomain + ".key;\n"; } public String getAssetCaching() { - return "include conf.d/include/assets.conf;\n"; + return "\ninclude conf.d/include/assets.conf;\n"; } public String getBlockExploits() { - return "include conf.d/include/block-exploits.conf;\n"; + return "\ninclude conf.d/include/block-exploits.conf;\n"; } public String getForceSSL() { - return "include conf.d/include/force-ssl.conf;\n"; + return "\ninclude conf.d/include/force-ssl.conf;\n"; } } diff --git a/src/main/java/com/aolda/itda/template/RoutingTemplate.java b/src/main/java/com/aolda/itda/template/RoutingTemplate.java new file mode 100644 index 0000000000000000000000000000000000000000..46e6116ce6b1b9f6840a60cc9bd85d0fc8e8dde6 --- /dev/null +++ b/src/main/java/com/aolda/itda/template/RoutingTemplate.java @@ -0,0 +1,44 @@ +package com.aolda.itda.template; + +import com.aolda.itda.dto.routing.RoutingDTO; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class RoutingTemplate { + + private final OptionTemplate optionTemplate; + + public String getRouting(RoutingDTO dto, String certificateDomain) { + return "server { \n" + + "set $forward_scheme http;\n" + + "set $server \"" + dto.getIp() +"\";\n" + + "set $port "+ dto.getPort() +";\n" + + "\n" + + "listen 80;\n" + + "listen [::]:80;\n" + + (dto.getCertificateId() == -1 ? "" : + "listen 443 ssl;\n listen [::]:443 ssl;\n") + + "server_name " + dto.getDomain() + ";\n" + + (dto.getCertificateId() == -1 ? "" : + optionTemplate.getSSL(certificateDomain)) + + (dto.getCaching() ? "" : + optionTemplate.getAssetCaching() + ) + + "proxy_set_header Upgrade $http_upgrade;\n" + + "proxy_set_header Connection $http_connection;\n" + + "proxy_http_version 1.1;\n" + + "\n" + + "access_log /data/logs/proxy-host-" + dto.getId() + "_access.log proxy;\n" + + "error_log /data/logs/proxy-host-" + dto.getId() + "_error.log warn;\n" + + "location / { \n" + + "proxy_set_header Upgrade $http_upgrade;\n" + + "proxy_set_header Connection $http_connection;\n" + + "proxy_http_version 1.1;\n" + + "include conf.d/include/proxy.conf;\n" + + "}\n" + + "}\n"; + } + +}