From c0d0a410f493f4bffe0c7d6cb4ca33e9f75a3258 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=EC=B2=9C=20=EC=A7=84=EA=B0=95?= <jjjjjk12@ajou.ac.kr>
Date: Sat, 15 Mar 2025 12:28:51 +0900
Subject: [PATCH 1/3] =?UTF-8?q?fix:=20conf=20=EC=88=98=EC=A0=95=EC=9D=B4?=
 =?UTF-8?q?=20=EB=8D=AE=EC=96=B4=EC=93=B0=EA=B8=B0=20=EB=90=98=EB=8D=98=20?=
 =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=EC=88=98=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../com/aolda/itda/service/forwarding/ForwardingService.java    | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main/java/com/aolda/itda/service/forwarding/ForwardingService.java b/src/main/java/com/aolda/itda/service/forwarding/ForwardingService.java
index e47001d..f1d2e46 100644
--- a/src/main/java/com/aolda/itda/service/forwarding/ForwardingService.java
+++ b/src/main/java/com/aolda/itda/service/forwarding/ForwardingService.java
@@ -95,7 +95,7 @@ public class ForwardingService {
 
         /* conf 파일 작성 및 예외 처리 */
         try {
-            BufferedWriter bw = new BufferedWriter(new FileWriter(file, true)); // 예외처리 필요
+            BufferedWriter bw = new BufferedWriter(new FileWriter(file, false)); // 예외처리 필요
             bw.write(content);
             bw.flush();
             bw.close();
-- 
GitLab


From f78d7851ea18441b994dddc97b3f4acd31546865 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=EC=B2=9C=20=EC=A7=84=EA=B0=95?= <jjjjjk12@ajou.ac.kr>
Date: Sat, 15 Mar 2025 16:26:46 +0900
Subject: [PATCH 2/3] =?UTF-8?q?feat:=20Nginx=20=ED=85=8C=EC=8A=A4=ED=8A=B8?=
 =?UTF-8?q?,=20=EB=A6=AC=EB=A1=9C=EB=93=9C=20=EB=A1=9C=EC=A7=81=20?=
 =?UTF-8?q?=EC=B6=94=EA=B0=80,=20conf=20=ED=8C=8C=EC=9D=BC=20=EA=B4=80?=
 =?UTF-8?q?=EB=A0=A8=20=EB=A1=A4=EB=B0=B1=20=EB=A1=9C=EC=A7=81=20=EC=B6=94?=
 =?UTF-8?q?=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../com/aolda/itda/exception/ErrorCode.java   |  11 +-
 .../service/forwarding/ForwardingService.java | 163 +++++++++++++++++-
 2 files changed, 166 insertions(+), 8 deletions(-)

diff --git a/src/main/java/com/aolda/itda/exception/ErrorCode.java b/src/main/java/com/aolda/itda/exception/ErrorCode.java
index 2fc313b..443f3a1 100644
--- a/src/main/java/com/aolda/itda/exception/ErrorCode.java
+++ b/src/main/java/com/aolda/itda/exception/ErrorCode.java
@@ -14,13 +14,20 @@ public enum ErrorCode {
     // Token
     INVALID_TOKEN(HttpStatus.BAD_REQUEST, "잘못된 토큰입니다"),
 
-    //Forwarding
+    // Forwarding
     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_SERVER_PORT(HttpStatus.BAD_REQUEST, "중복된 서버 포트입니다"),
+
+    // Nginx
+    FAIL_NGINX_CONF_TEST(HttpStatus.BAD_REQUEST, "Conf 파일 테스트에 실패했습니다"),
+    FAIL_NGINX_CONF_RELOAD(HttpStatus.BAD_REQUEST, "Nginx 재시작에 실패했습니다"),
+
+    FAIL_DELETE_CONF(HttpStatus.BAD_REQUEST, "Conf 파일을 삭제하지 못했습니다"),
+    FAIL_ROLL_BACK(HttpStatus.BAD_REQUEST, "롤백 실패");
 
     private final HttpStatus status;
     private final String message;
diff --git a/src/main/java/com/aolda/itda/service/forwarding/ForwardingService.java b/src/main/java/com/aolda/itda/service/forwarding/ForwardingService.java
index f1d2e46..4bfbce4 100644
--- a/src/main/java/com/aolda/itda/service/forwarding/ForwardingService.java
+++ b/src/main/java/com/aolda/itda/service/forwarding/ForwardingService.java
@@ -7,12 +7,20 @@ import com.aolda.itda.exception.CustomException;
 import com.aolda.itda.exception.ErrorCode;
 import com.aolda.itda.repository.forwarding.ForwardingRepository;
 import com.aolda.itda.template.ForwardingTemplate;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import jakarta.validation.ConstraintViolation;
 import jakarta.validation.Validation;
 import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.client.HttpClientErrorException;
+import org.springframework.web.client.HttpServerErrorException;
+import org.springframework.web.client.RestClientException;
+import org.springframework.web.client.RestTemplate;
 
 import java.io.BufferedWriter;
 import java.io.File;
@@ -21,16 +29,20 @@ import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.Map;
 
 @Service
 @Transactional
 @RequiredArgsConstructor
+@Slf4j
 public class ForwardingService {
 
     @Value("${spring.server.base-ip}")
     private String serverBaseIp;
     private final ForwardingTemplate forwardingTemplate;
     private final ForwardingRepository forwardingRepository;
+    private final RestTemplate restTemplate = new RestTemplate();
 
     /* 포트포워딩 정보 조회 */
     public ForwardingDTO getForwarding(Long forwardingId) {
@@ -101,12 +113,47 @@ public class ForwardingService {
             bw.close();
         } catch (Exception e) {
             e.printStackTrace();
-            if (file.exists()) {
-                file.delete();
+            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);
+        }
 
     }
 
@@ -140,8 +187,13 @@ public class ForwardingService {
             throw new CustomException(ErrorCode.NOT_FOUND_FORWARDING, "Conf 파일이 존재하지 않아 수정할 수 없습니다");
         }
 
+        Path backup;
         try {
-            BufferedWriter bw = new BufferedWriter(new FileWriter(file, true)); // 예외처리 필요
+            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();
@@ -150,6 +202,59 @@ public class ForwardingService {
             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 정보 수정 */
         forwardingRepository.save(forwarding);
     }
@@ -161,9 +266,55 @@ public class ForwardingService {
 
         /* 파일 삭제 */
         String confPath = "/data/nginx/stream/" + forwarding.getForwardingId() + ".conf";
-        File file = new File(confPath);
-        if (!file.delete()) {
-            throw new CustomException(ErrorCode.NOT_FOUND_FORWARDING, "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 */
-- 
GitLab


From 82fd3ae9115c9400f203c7be34d5cbb761823b64 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=EC=B2=9C=20=EC=A7=84=EA=B0=95?= <jjjjjk12@ajou.ac.kr>
Date: Sat, 15 Mar 2025 19:35:32 +0900
Subject: [PATCH 3/3] =?UTF-8?q?fix:=20conf=20=EC=A4=91=EB=B3=B5=20?=
 =?UTF-8?q?=EA=B2=80=EC=A6=9D=EC=9D=B4=20=EC=95=88=EB=90=98=EB=8D=98=20?=
 =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../service/forwarding/ForwardingService.java | 24 +++++++++++++------
 1 file changed, 17 insertions(+), 7 deletions(-)

diff --git a/src/main/java/com/aolda/itda/service/forwarding/ForwardingService.java b/src/main/java/com/aolda/itda/service/forwarding/ForwardingService.java
index 4bfbce4..9ec9343 100644
--- a/src/main/java/com/aolda/itda/service/forwarding/ForwardingService.java
+++ b/src/main/java/com/aolda/itda/service/forwarding/ForwardingService.java
@@ -162,21 +162,31 @@ public class ForwardingService {
         Forwarding forwarding = forwardingRepository.findByForwardingIdAndIsDeleted(forwardingId, false)
                 .orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_FORWARDING));
 
-        forwarding.edit(dto);
 
         /* 중복 검증 */
+        if (dto.getServerPort() != null && forwardingRepository.existsByServerPortAndIsDeleted(dto.getServerPort(), false)) {
+            System.out.println(dto.getServerPort());
+            System.out.println(forwarding.getServerPort());
+            forwardingRepository.existsByServerPortAndIsDeleted(dto.getServerPort(), false);
+            throw new CustomException(ErrorCode.DUPLICATED_SERVER_PORT);
+        }
+
         if (!(dto.getInstanceIp() == null && dto.getInstancePort() == null) &&
-                forwardingRepository.existsByInstanceIpAndInstancePortAndIsDeleted(forwarding.getInstanceIp()
-                , forwarding.getInstancePort()
+                forwardingRepository.existsByInstanceIpAndInstancePortAndIsDeleted(
+                        dto.getInstanceIp() == null ? forwarding.getInstanceIp() : dto.getInstanceIp()
+                , dto.getInstancePort() == null ? forwarding.getInstancePort() : dto.getInstancePort()
                 , false)) {
+            System.out.println(dto.getInstanceIp());
+            System.out.println(forwarding.getInstanceIp());
+            System.out.println(forwardingRepository.existsByInstanceIpAndInstancePortAndIsDeleted(
+                    dto.getInstanceIp() == null ? forwarding.getInstanceIp() : dto.getInstanceIp()
+                    , dto.getInstancePort() == null ? forwarding.getInstancePort() : dto.getInstancePort()
+                    , false));
             throw new CustomException(ErrorCode.DUPLICATED_INSTANCE_INFO);
         }
 
-        if (dto.getServerPort() != null && forwardingRepository.existsByServerPortAndIsDeleted(dto.getServerPort(), false)) {
-            throw new CustomException(ErrorCode.DUPLICATED_SERVER_PORT);
-        }
-
         /* 파일 수정 */
+        forwarding.edit(dto);
         String content = forwardingTemplate.getPortForwardingWithTCP(forwarding.getServerPort(),
                 forwarding.getInstanceIp(),
                 forwarding.getInstancePort(),
-- 
GitLab