package com.kamco.cd.training.filemanager; import com.kamco.cd.training.config.api.ApiResponseDto; import com.kamco.cd.training.filemanager.dto.FileManagerDto; import com.kamco.cd.training.filemanager.service.FileManagerService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.ExampleObject; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import java.util.UUID; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @Slf4j @Tag(name = "파일 관리", description = "/data 디렉토리 파일 관리 API") @RestController @RequestMapping("/api/file-manager") @RequiredArgsConstructor public class FileManagerApiController { private final FileManagerService fileManagerService; @Operation( summary = "파일 목록 조회", description = "/data 디렉토리 내 파일 및 디렉토리 목록을 조회합니다. recursive=true로 설정하면 하위 디렉토리까지 조회합니다.") @ApiResponses( value = { @ApiResponse( responseCode = "200", description = "조회 성공", content = @Content( mediaType = "application/json", schema = @Schema(implementation = FileManagerDto.ListFilesRes.class))), @ApiResponse(responseCode = "400", description = "잘못된 요청 (유효하지 않은 경로)", content = @Content), @ApiResponse(responseCode = "404", description = "디렉토리를 찾을 수 없음", content = @Content), @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) }) @GetMapping("/files") public ApiResponseDto listFiles( @Parameter(description = "조회할 디렉토리 경로 (기본값: /data)", example = "/data/request") @RequestParam(required = false) String directoryPath, @Parameter(description = "하위 디렉토리 포함 여부", example = "false") @RequestParam(required = false, defaultValue = "false") Boolean recursive) { FileManagerDto.ListFilesReq request = FileManagerDto.ListFilesReq.builder() .directoryPath(directoryPath) .recursive(recursive) .build(); FileManagerDto.ListFilesRes response = fileManagerService.listFiles(request); return ApiResponseDto.ok(response); } @Operation( summary = "파일/디렉토리 삭제", description = "지정된 파일 또는 디렉토리를 삭제합니다. recursive=true로 설정하면 디렉토리 내 모든 파일을 삭제합니다.", requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody( content = @Content( mediaType = "application/json", schema = @Schema(implementation = FileManagerDto.DeleteFileReq.class), examples = { @ExampleObject( name = "단일 파일 삭제", value = """ { "filePaths": ["/data/request/old_file.zip"], "recursive": false } """), @ExampleObject( name = "여러 파일 삭제", value = """ { "filePaths": ["/data/file1.txt", "/data/file2.txt"], "recursive": false } """), @ExampleObject( name = "디렉토리 전체 삭제", value = """ { "filePaths": ["/data/old_folder"], "recursive": true } """) }))) @ApiResponses( value = { @ApiResponse( responseCode = "200", description = "삭제 성공", content = @Content( mediaType = "application/json", schema = @Schema(implementation = FileManagerDto.DeleteFileRes.class))), @ApiResponse(responseCode = "400", description = "잘못된 요청 (유효하지 않은 경로)", content = @Content), @ApiResponse(responseCode = "404", description = "파일을 찾을 수 없음", content = @Content), @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) }) @DeleteMapping("/files") public ApiResponseDto deleteFiles( @RequestBody FileManagerDto.DeleteFileReq request) { FileManagerDto.DeleteFileRes response = fileManagerService.deleteFiles(request); return ApiResponseDto.ok(response); } @Operation( summary = "모델 파일 경로 조회", description = "특정 모델 UUID로 파일 위치 경로와 하위 파일 목록을 조회합니다. " + "request_path(심볼릭 링크 디렉토리)와 response_path(모델 결과)를 동시에 반환합니다.") @ApiResponses( value = { @ApiResponse( responseCode = "200", description = "조회 성공", content = @Content( mediaType = "application/json", schema = @Schema(implementation = FileManagerDto.ModelFilePathRes.class))), @ApiResponse(responseCode = "404", description = "모델을 찾을 수 없음", content = @Content), @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) }) @GetMapping("/model-path/{modelUuid}") public ApiResponseDto getModelFilePath( @Parameter(description = "모델 UUID", example = "df284755-c0b7-4070-bfee-ef554e8d0fe4") @PathVariable UUID modelUuid) { FileManagerDto.ModelFilePathRes response = fileManagerService.getModelFilePath(modelUuid); return ApiResponseDto.ok(response); } @Operation( summary = "데이터셋 파일 경로 조회", description = "특정 데이터셋 UUID로 파일 위치 경로와 하위 파일 목록을 조회합니다. " + "dataset_path 컬럼의 request_dir 경로를 반환합니다.") @ApiResponses( value = { @ApiResponse( responseCode = "200", description = "조회 성공", content = @Content( mediaType = "application/json", schema = @Schema(implementation = FileManagerDto.DatasetFilePathRes.class))), @ApiResponse(responseCode = "404", description = "데이터셋을 찾을 수 없음", content = @Content), @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) }) @GetMapping("/dataset-path/{datasetUuid}") public ApiResponseDto getDatasetFilePath( @Parameter(description = "데이터셋 UUID", example = "037b09a0-b315-4e2e-b88d-b9011f9eaa15") @PathVariable UUID datasetUuid) { FileManagerDto.DatasetFilePathRes response = fileManagerService.getDatasetFilePath(datasetUuid); return ApiResponseDto.ok(response); } @Operation( summary = "디렉토리 용량 체크", description = "특정 디렉토리의 총 용량, 파일 개수, 디렉토리 개수를 조회합니다. " + "basepath 하위 폴더의 용량을 재귀적으로 계산합니다.") @ApiResponses( value = { @ApiResponse( responseCode = "200", description = "조회 성공", content = @Content( mediaType = "application/json", schema = @Schema(implementation = FileManagerDto.DirectoryCapacityRes.class))), @ApiResponse(responseCode = "400", description = "잘못된 경로", content = @Content), @ApiResponse(responseCode = "404", description = "디렉토리를 찾을 수 없음", content = @Content), @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) }) @GetMapping("/directory-capacity") public ApiResponseDto checkDirectoryCapacity( @Parameter( description = "디렉토리 경로", example = "/home/kcomu/data/request/037b09a0-b315-4e2e-b88d-b9011f9eaa15") @RequestParam String directoryPath) { FileManagerDto.DirectoryCapacityRes response = fileManagerService.checkDirectoryCapacity(directoryPath); return ApiResponseDto.ok(response); } @Operation( summary = "모델별 학습 실행 상태 조회", description = "G1~G4 모델의 현재 학습 실행 상태를 조회합니다. " + "step1_state, step2_state를 체크하여 어떤 모델이 학습 중인지 확인합니다. " + "step1과 step2는 동시 진행되지 않습니다.") @ApiResponses( value = { @ApiResponse( responseCode = "200", description = "조회 성공", content = @Content( mediaType = "application/json", schema = @Schema( implementation = FileManagerDto.AllModelsExecutionStatusRes.class))), @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) }) @GetMapping("/models-execution-status") public ApiResponseDto getModelsExecutionStatus() { FileManagerDto.AllModelsExecutionStatusRes response = fileManagerService.getModelsExecutionStatus(); return ApiResponseDto.ok(response); } @Operation( summary = "저장공간 정보 조회", description = "/home/kcomu/data 경로의 사용 중인 용량, 전체 디스크 용량, 남은 저장공간을 조회합니다. " + "파라미터 없이 호출하면 자동으로 /home/kcomu/data 경로 정보를 반환합니다.") @ApiResponses( value = { @ApiResponse( responseCode = "200", description = "조회 성공", content = @Content( mediaType = "application/json", schema = @Schema(implementation = FileManagerDto.StorageSpaceRes.class))), @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) }) @GetMapping("/storage-space") public ApiResponseDto getStorageSpaceInfo() { FileManagerDto.StorageSpaceRes response = fileManagerService.getStorageSpaceInfo(); return ApiResponseDto.ok(response); } }