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.enums.ParameterIn; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.PostConstruct; import kr.re.etri.autoflow.payload.request.EdgeSWVO; import kr.re.etri.autoflow.service.EdgeSWUploadService; import kr.re.etri.autoflow.service.ExternalAuthService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.core.io.FileSystemResource; import org.springframework.http.*; import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.bind.annotation.*; import org.springframework.web.client.RestTemplate; import org.springframework.web.multipart.MultipartFile; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.List; import java.util.Map; @RestController @RequestMapping("/api/external-auth") @Tag(name = "ExternalAuthController", description = "외부 백엔드 로그인 및 Bearer 토큰 조회 API") @RequiredArgsConstructor @Slf4j public class ExternalAuthController { private final ExternalAuthService externalAuthService; private final EdgeSWUploadService edgeSWUploadService; private RestTemplate restTemplate; @PostConstruct public void init() { this.restTemplate = createUnsafeRestTemplate(); } /** * SSL 무시용 RestTemplate 생성 */ private RestTemplate createUnsafeRestTemplate() { try { TrustManager[] trustAllCerts = new TrustManager[]{ new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } public void checkClientTrusted(X509Certificate[] certs, String authType) { } public void checkServerTrusted(X509Certificate[] certs, String authType) { } } }; SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory()); HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true); SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); return new RestTemplate(requestFactory); } catch (Exception e) { throw new RuntimeException("Failed to create unsafe RestTemplate", e); } } @Operation(summary = "외부 로그인 후 사용자 정보 조회", description = "외부 Spring 백엔드에 로그인 요청 후 id, name, token을 반환합니다.") @PostMapping("/signin") public ResponseEntity signin(@RequestBody SigninRequest request) { try { Map userInfo = externalAuthService.getUserInfo(request.id(), request.password()); return ResponseEntity.ok(ApiResponse.success(userInfo)); } catch (Exception e) { return ResponseEntity.ok(ApiResponse.failure(e.getMessage())); } } @Operation(summary = "Edge 패키지 목록 조회", description = "로그인 후 받은 id와 token을 사용해 외부 Edge 패키지 검색 API를 호출합니다.") @GetMapping("/edge-search") public ResponseEntity edgeSearch( @RequestParam String id, @RequestParam String token ) { try { Map edgeResult = externalAuthService.getEdgePackageList(id, token); return ResponseEntity.ok(ApiResponse.success(edgeResult)); } catch (Exception e) { return ResponseEntity.ok(ApiResponse.failure(e.getMessage())); } } @Operation(summary = "차량관리 SW 패키지 목록 조회", description = "로그인 후 받은 id와 token을 사용해 외부 차령SW 패키지 검색 API를 호출합니다.") @GetMapping("/sw-search") public ResponseEntity swSearch( @RequestParam String id, @RequestParam String token ) { try { Map edgeResult = externalAuthService.getSWPackageList(id, token); return ResponseEntity.ok(ApiResponse.success(edgeResult)); } catch (Exception e) { return ResponseEntity.ok(ApiResponse.failure(e.getMessage())); } } // @Operation( // summary = "S3 업로드 + 외부 DB 등록", // description = "파일을 S3에 업로드하고 외부 DB에 등록합니다.", // security = @SecurityRequirement(name = "bearerAuth") // 여기서 SecurityScheme 연결 // ) // @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) // public ResponseEntity uploadEdgeSW( // @RequestParam("files") List files, // @RequestParam("sw_id") String swId, // @RequestParam("sw_version") String swVersion, // @RequestParam("sw_name") String swName, // @RequestParam("creation_datetime") String creationDatetime, // @RequestParam("auth_id") String authId // ) { // try { // EdgeSWVO edgeSWVO = new EdgeSWVO(); // edgeSWVO.setFiles(files); // edgeSWVO.setSw_id(swId); // edgeSWVO.setSw_version(swVersion); // edgeSWVO.setSw_name(swName); // edgeSWVO.setCreation_datetime(creationDatetime); // edgeSWVO.setAuth_id(authId); // // String result = edgeSWUploadService.uploadAndSend(edgeSWVO); // return ResponseEntity.ok(result); // // } catch (Exception e) { // return ResponseEntity.internalServerError().body("업로드 실패: " + e.getMessage()); // } // } @Operation( summary = "외부 DB 등록 + 파일 업로드", description = "S3 업로드된 파일 정보를 외부 DB에 등록합니다. 파일도 함께 업로드 가능", security = @SecurityRequirement(name = "bearerAuth") ) @PostMapping(value = "/register-with-file", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public ResponseEntity registerWithFile( @RequestParam("sw_id") String swId, @RequestParam("sw_version") int swVersion, @RequestParam("sw_name") String swName, @RequestParam("auth_id") String authId, @RequestParam @Schema(description = "엣지 패키지 정보", example = "14", required = true) Integer edPkgSerial, @RequestParam @Schema(description = "압축여부(0:단일,1:묶음)", example = "1", required = true) Integer archiveType, @RequestParam @Schema(description = "실행여부 (0:실행하지않음,1:실행)", example = "1", required = true) String execYn, @RequestParam @Schema(description = "비밀글 여부 (true/false)", example = "false", required = true) String secretAt, @RequestParam @Schema(description = "설치 위치", example = "/etc/test/", required = true) String downloadLocation, @RequestParam("user_id") @Schema(description = "사용자 ID", example = "admin", required = true) String userId, @RequestParam("sw_type") @Schema(description = "0 = sw 1= edge로 db전송", example = "1", required = true) int sw_type, @RequestParam("creation_datetime") @Schema( description = "생성 일시 (ISO-8601 형식, UTC)", example = "2025-10-20T11:46:49.848Z", required = true ) String creationDatetime, @RequestPart(value = "file", required = false) MultipartFile file ) { try { // VO 생성 EdgeSWVO edgeSWVO = new EdgeSWVO(); edgeSWVO.setSw_id(swId); edgeSWVO.setSw_version(swVersion); edgeSWVO.setSw_name(swName); edgeSWVO.setAuth_id(authId); edgeSWVO.setUser_id(userId); edgeSWVO.setCreation_datetime(creationDatetime); edgeSWVO.setEd_pkg_serial(edPkgSerial); edgeSWVO.setArchive_type(archiveType); edgeSWVO.setDownload_location(downloadLocation); edgeSWVO.setExec_yn(execYn); edgeSWVO.setSecret_at(secretAt); // 파일이 있으면 S3 업로드 if (file != null && !file.isEmpty()) { edgeSWUploadService.uploadFilesToS3Only(edgeSWVO, file); } // DB 등록 (파일 업로드 성공 후) String result = edgeSWUploadService.registerMetadata(edgeSWVO, sw_type); return ResponseEntity.ok(result); } catch (IOException ioe) { log.error("S3 파일 업로드 실패", ioe); return ResponseEntity.internalServerError().body("파일 업로드 실패: " + ioe.getMessage()); } catch (Exception e) { log.error("DB 등록 실패", e); return ResponseEntity.internalServerError().body("DB 등록 실패: " + e.getMessage()); } } // DTO: 요청 public static record SigninRequest(String id, String password) { } // DTO: 응답 public static class ApiResponse { private boolean success; private Object data; private String errorMessage; public ApiResponse(boolean success, Object data, String errorMessage) { this.success = success; this.data = data; this.errorMessage = errorMessage; } public static ApiResponse success(Object data) { return new ApiResponse(true, data, null); } public static ApiResponse failure(String errorMessage) { return new ApiResponse(false, null, errorMessage); } public boolean isSuccess() { return success; } public Object getData() { return data; } public String getErrorMessage() { return errorMessage; } } }