From cbea6bc2af2f721d8bbe56f43356b11a53bfc28e Mon Sep 17 00:00:00 2001 From: sdw086 Date: Wed, 27 May 2026 15:18:03 +0900 Subject: [PATCH] =?UTF-8?q?[api]=20tus=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modules/tusFile/TusFileController.java | 151 +++--- .../modules/tusFile/dto/FileDownloadDto.java | 41 -- .../modules/tusFile/dto/TusFileDeleteDto.java | 16 + .../{TusHookDto.java => TusFileHookDto.java} | 2 +- .../modules/tusFile/dto/TusFileListDto.java | 16 + ...veTaskDto.java => TusFileMoveTaskDto.java} | 2 +- ...okenDto.java => TusFileUploadAuthDto.java} | 7 +- ...teDto.java => TusFileUploadCancelDto.java} | 5 +- ...loadDto.java => TusFileUploadInitDto.java} | 4 +- ...Dto.java => TusFileUploadInitItemDto.java} | 2 +- ...MetaDto.java => TusFileUploadMetaDto.java} | 2 +- ...elDto.java => TusFileUploadStatusDto.java} | 6 +- .../tusFile/dto/TusFileViewOrDownloadDto.java | 28 ++ .../modules/tusFile/dto/UploadStatusDto.java | 28 -- .../modules/tusFile/form/FileDeleteForm.java | 26 - .../modules/tusFile/form/FileUploadForm.java | 62 --- .../tusFile/form/TusFileDeleteForm.java | 26 + ...entForm.java => TusFileHookEventForm.java} | 4 +- .../modules/tusFile/form/TusFileHookForm.java | 45 ++ ...adForm.java => TusFileHookUploadForm.java} | 2 +- .../tusFile/form/TusFileUploadCancelForm.java | 26 + .../tusFile/form/TusFileUploadInitForm.java | 62 +++ ...rm.java => TusFileUploadInitItemForm.java} | 14 +- .../tusFile/form/TusFileUploadStatusForm.java | 26 + .../api/modules/tusFile/form/TusHookForm.java | 45 -- .../tusFile/form/UploadCancelForm.java | 19 - .../tusFile/form/UploadStatusForm.java | 26 - .../modules/tusFile/mapper/TusFileMapper.java | 43 +- .../tusFile/service/TusFileService.java | 402 +++++++++------- .../modules/tusFile/vo/TusFileDeleteVo.java | 18 + .../TusFileListItemVo.java} | 4 +- .../TusFileListVo.java} | 6 +- .../tusFile/vo/TusFileUploadAuthVo.java | 15 + .../vo/TusFileUploadCancelProgressVo.java | 12 + .../tusFile/vo/TusFileUploadCancelVo.java | 19 + .../tusFile/vo/TusFileUploadInitItemVo.java | 28 ++ .../tusFile/vo/TusFileUploadInitVo.java | 21 + .../tusFile/vo/TusFileUploadStatusVo.java | 28 ++ .../tusFile/vo/TusFileViewOrDownloadVo.java | 49 ++ .../mapper/tusFile/TusFileMapper.xml | 448 +++++++++--------- 40 files changed, 1016 insertions(+), 770 deletions(-) delete mode 100644 src/main/java/com/alist/api/modules/tusFile/dto/FileDownloadDto.java create mode 100644 src/main/java/com/alist/api/modules/tusFile/dto/TusFileDeleteDto.java rename src/main/java/com/alist/api/modules/tusFile/dto/{TusHookDto.java => TusFileHookDto.java} (92%) create mode 100644 src/main/java/com/alist/api/modules/tusFile/dto/TusFileListDto.java rename src/main/java/com/alist/api/modules/tusFile/dto/{FileMoveTaskDto.java => TusFileMoveTaskDto.java} (94%) rename src/main/java/com/alist/api/modules/tusFile/dto/{UploadTokenDto.java => TusFileUploadAuthDto.java} (83%) rename src/main/java/com/alist/api/modules/tusFile/dto/{FileDeleteDto.java => TusFileUploadCancelDto.java} (63%) rename src/main/java/com/alist/api/modules/tusFile/dto/{FileUploadDto.java => TusFileUploadInitDto.java} (92%) rename src/main/java/com/alist/api/modules/tusFile/dto/{FileUploadItemDto.java => TusFileUploadInitItemDto.java} (96%) rename src/main/java/com/alist/api/modules/tusFile/dto/{FileUploadMetaDto.java => TusFileUploadMetaDto.java} (88%) rename src/main/java/com/alist/api/modules/tusFile/dto/{UploadCancelDto.java => TusFileUploadStatusDto.java} (51%) create mode 100644 src/main/java/com/alist/api/modules/tusFile/dto/TusFileViewOrDownloadDto.java delete mode 100644 src/main/java/com/alist/api/modules/tusFile/dto/UploadStatusDto.java delete mode 100644 src/main/java/com/alist/api/modules/tusFile/form/FileDeleteForm.java delete mode 100644 src/main/java/com/alist/api/modules/tusFile/form/FileUploadForm.java create mode 100644 src/main/java/com/alist/api/modules/tusFile/form/TusFileDeleteForm.java rename src/main/java/com/alist/api/modules/tusFile/form/{TusHookEventForm.java => TusFileHookEventForm.java} (82%) create mode 100644 src/main/java/com/alist/api/modules/tusFile/form/TusFileHookForm.java rename src/main/java/com/alist/api/modules/tusFile/form/{TusHookUploadForm.java => TusFileHookUploadForm.java} (96%) create mode 100644 src/main/java/com/alist/api/modules/tusFile/form/TusFileUploadCancelForm.java create mode 100644 src/main/java/com/alist/api/modules/tusFile/form/TusFileUploadInitForm.java rename src/main/java/com/alist/api/modules/tusFile/form/{FileUploadItemForm.java => TusFileUploadInitItemForm.java} (69%) create mode 100644 src/main/java/com/alist/api/modules/tusFile/form/TusFileUploadStatusForm.java delete mode 100644 src/main/java/com/alist/api/modules/tusFile/form/TusHookForm.java delete mode 100644 src/main/java/com/alist/api/modules/tusFile/form/UploadCancelForm.java delete mode 100644 src/main/java/com/alist/api/modules/tusFile/form/UploadStatusForm.java create mode 100644 src/main/java/com/alist/api/modules/tusFile/vo/TusFileDeleteVo.java rename src/main/java/com/alist/api/modules/tusFile/{dto/FileDownloadItemDto.java => vo/TusFileListItemVo.java} (92%) rename src/main/java/com/alist/api/modules/tusFile/{dto/FileDownloadListDto.java => vo/TusFileListVo.java} (79%) create mode 100644 src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadAuthVo.java create mode 100644 src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadCancelProgressVo.java create mode 100644 src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadCancelVo.java create mode 100644 src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadInitItemVo.java create mode 100644 src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadInitVo.java create mode 100644 src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadStatusVo.java create mode 100644 src/main/java/com/alist/api/modules/tusFile/vo/TusFileViewOrDownloadVo.java diff --git a/src/main/java/com/alist/api/modules/tusFile/TusFileController.java b/src/main/java/com/alist/api/modules/tusFile/TusFileController.java index 6f40664..37bbf04 100644 --- a/src/main/java/com/alist/api/modules/tusFile/TusFileController.java +++ b/src/main/java/com/alist/api/modules/tusFile/TusFileController.java @@ -6,6 +6,7 @@ import com.alist.api.common.utils.SecurityUtil; import com.alist.api.modules.tusFile.dto.*; import com.alist.api.modules.tusFile.form.*; import com.alist.api.modules.tusFile.service.TusFileService; +import com.alist.api.modules.tusFile.vo.*; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.servlet.http.HttpServletRequest; @@ -43,29 +44,29 @@ public class TusFileController { , description = "업로드 대상 파일 정보를 DB에 등록하고 fileUuid/uploadToken을 발급합니다." ) @PostMapping("/upload/init") - public ResponseEntity> tusFileUploadInit( - @Valid @RequestBody FileUploadForm fileUploadForm + public ResponseEntity> tusFileUploadInit( + @Valid @RequestBody TusFileUploadInitForm tusFileUploadInitForm ) { - FileUploadDto fileUploadDto = fileUploadForm.toFileUploadDto(); + TusFileUploadInitDto tusFileUploadInitDto = tusFileUploadInitForm.toDto(); Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx(); if (userTokenIdx == null) { - return ApiResponse.entity(new FileUploadDto(), ApiResponseCode.CODE_401); + return ApiResponse.entity(new TusFileUploadInitVo(), ApiResponseCode.CODE_401); } Integer userIdx = tusFileService.selectUserIdxByUserTokenIdx(userTokenIdx); if (userIdx == null) { - return ApiResponse.entity(new FileUploadDto(), ApiResponseCode.CODE_401); + return ApiResponse.entity(new TusFileUploadInitVo(), ApiResponseCode.CODE_401); } - fileUploadDto.setUserIdx(userIdx); - fileUploadDto.setUserTokenIdx(userTokenIdx); + tusFileUploadInitDto.setUserIdx(userIdx); + tusFileUploadInitDto.setUserTokenIdx(userTokenIdx); - FileUploadDto fileUploadResult = tusFileService.insertFileInit(fileUploadDto); + TusFileUploadInitVo tusFileUploadInitVo = tusFileService.insertTusFileUploadInit(tusFileUploadInitDto); - return ApiResponse.entity(fileUploadResult, ApiResponseCode.CODE_200); + return ApiResponse.entity(tusFileUploadInitVo, ApiResponseCode.CODE_200); } @Operation( @@ -86,17 +87,17 @@ public class TusFileController { return ApiResponse.entity("", ApiResponseCode.CODE_403); } - UploadTokenDto uploadTokenDto = new UploadTokenDto(); - uploadTokenDto.setUploadToken(uploadToken.substring(7).trim()); - uploadTokenDto.setFileUuid(fileUuid.trim()); - uploadTokenDto.setUploadMetadataRaw(uploadMetadata); - uploadTokenDto.setUploadLengthRaw(uploadLength); + TusFileUploadAuthDto tusFileUploadAuthDto = new TusFileUploadAuthDto(); + tusFileUploadAuthDto.setUploadToken(uploadToken.substring(7).trim()); + tusFileUploadAuthDto.setFileUuid(fileUuid.trim()); + tusFileUploadAuthDto.setUploadMetadataRaw(uploadMetadata); + tusFileUploadAuthDto.setUploadLengthRaw(uploadLength); - UploadTokenDto uploadTokenCheck = tusFileService.isUploadTokenCheck(uploadTokenDto); + TusFileUploadAuthVo tusFileUploadAuthVo = tusFileService.selectTusFileUploadAuth(tusFileUploadAuthDto); - if (uploadTokenCheck.getResultCode() == 401) { + if (tusFileUploadAuthVo.getResultCode() == 401) { return ApiResponse.entity("", ApiResponseCode.CODE_401); - } else if (uploadTokenCheck.getResultCode() == 403) { + } else if (tusFileUploadAuthVo.getResultCode() == 403) { return ApiResponse.entity("", ApiResponseCode.CODE_403); } @@ -108,14 +109,16 @@ public class TusFileController { , description = "fileUuid 기준으로 Redis/DB 업로드 상태(진행률, 상태값)를 조회합니다." ) @PostMapping("/upload/status") - public ResponseEntity> tusFileUploadStatus( - @Valid @RequestBody UploadStatusForm uploadStatusForm + public ResponseEntity> tusFileUploadStatus( + @Valid @RequestBody TusFileUploadStatusForm tusFileUploadStatusForm ) { - UploadStatusDto status = tusFileService.getUploadStatus(uploadStatusForm.toUploadStatusDto()); - if (status == null) { - return ApiResponse.entity(new UploadStatusDto(), ApiResponseCode.CODE_2003); + TusFileUploadStatusVo tusFileUploadStatusVo = tusFileService.loadTusFileUploadStatus(tusFileUploadStatusForm.toDto()); + + if (tusFileUploadStatusVo == null) { + return ApiResponse.entity(new TusFileUploadStatusVo(), ApiResponseCode.CODE_2003); } - return ApiResponse.entity(status, ApiResponseCode.CODE_200); + + return ApiResponse.entity(tusFileUploadStatusVo, ApiResponseCode.CODE_200); } @Operation( @@ -123,11 +126,13 @@ public class TusFileController { , description = "tusd에서 전달되는 pre-create/post-receive/post-finish/post-terminate 이벤트를 반영합니다." ) @PostMapping("/hook") - public ResponseEntity> tusFileHook(@RequestBody TusHookForm tusHookForm) { - boolean accepted = tusFileService.updateFileUploadStatus(tusHookForm.toTusHookDto()); + public ResponseEntity> tusFileHook(@RequestBody TusFileHookForm tusFileHookForm) { + TusFileHookDto tusFileHookDto = tusFileHookForm.toDto(); + + boolean accepted = tusFileService.updateTusFileHook(tusFileHookDto); if (accepted) { - tusFileService.saveUploadStatusRedisTusHook(tusHookForm.toTusHookDto()); + tusFileService.saveTusFileHookStatusRedis(tusFileHookDto); } return ApiResponse.entity("", ApiResponseCode.CODE_200); @@ -139,7 +144,7 @@ public class TusFileController { ) @PostMapping("/upload/cancel") public ResponseEntity> tusFileUploadCancel( - @Valid @RequestBody UploadCancelForm uploadCancelForm + @Valid @RequestBody TusFileUploadCancelForm tusFileUploadCancelForm ) { Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx(); @@ -147,14 +152,12 @@ public class TusFileController { return ApiResponse.entity("", ApiResponseCode.CODE_401); } - UploadCancelDto uploadCancelDto = new UploadCancelDto(); + TusFileUploadCancelDto tusFileUploadCancelDto = tusFileUploadCancelForm.toDto(); + tusFileUploadCancelDto.setUserTokenIdx(userTokenIdx); - uploadCancelDto.setFileUuid(uploadCancelForm.getFileUuid().trim()); - uploadCancelDto.setUserTokenIdx(userTokenIdx); + TusFileUploadCancelVo tusFileUploadCancelVo = tusFileService.updateTusFileUploadCancel(tusFileUploadCancelDto); - boolean canceled = tusFileService.updateUploadCancel(uploadCancelDto); - - if (!canceled) { + if (!tusFileUploadCancelVo.isCanceled()) { return ApiResponse.entity("", ApiResponseCode.CODE_2005, "업로드 취소"); } @@ -163,62 +166,63 @@ public class TusFileController { @Operation( summary = "파일 목록 조회" - , description = "FILE_MASTER_IDX 기준으로 완료된 파일 목록을 조회합니다.") + , description = "FILE_MASTER_IDX 기준으로 완료된 파일 목록을 조회합니다." + ) @GetMapping("/list/{fileMasterIdx}") - public ResponseEntity> tusFileList( + public ResponseEntity> tusFileList( @PathVariable Long fileMasterIdx ) { + TusFileListDto tusFileListDto = new TusFileListDto(); + tusFileListDto.setFileMasterIdx(fileMasterIdx); - FileDownloadDto fileDownloadDto = new FileDownloadDto(); - fileDownloadDto.setFileMasterIdx(fileMasterIdx); + TusFileListVo tusFileListVo = tusFileService.selectTusFileList(tusFileListDto); - FileDownloadListDto fileDownloadList = tusFileService.selectFileDownloadListByFileMasterIdx(fileDownloadDto); - - if (fileDownloadList.getResultCode() == 401) { - return ApiResponse.entity(fileDownloadList, ApiResponseCode.CODE_401); + if (tusFileListVo.getResultCode() == 401) { + return ApiResponse.entity(tusFileListVo, ApiResponseCode.CODE_401); } - return ApiResponse.entity(fileDownloadList, ApiResponseCode.CODE_2001, "파일"); + return ApiResponse.entity(tusFileListVo, ApiResponseCode.CODE_2001, "파일"); } @Operation( summary = "파일 보기" - , description = "권한 확인 후 Nginx X-Accel-Redirect로 파일을 inline 조회합니다.") + , description = "권한 확인 후 Nginx X-Accel-Redirect로 파일을 inline 조회합니다." + ) @GetMapping("/view/{fileUuid}") public ResponseEntity tusFileView( @PathVariable String fileUuid , HttpServletRequest request ) throws IOException { - FileDownloadDto fileDownloadDto = new FileDownloadDto(); - fileDownloadDto.setFileUuid(fileUuid); - fileDownloadDto.setEventType("VIEW"); - fileDownloadDto.setClientIp(request.getRemoteAddr()); - fileDownloadDto.setUserAgent(request.getHeader("User-Agent")); - fileDownloadDto.setReferer(request.getHeader("Referer")); + TusFileViewOrDownloadDto tusFileViewOrDownloadDto = new TusFileViewOrDownloadDto(); + tusFileViewOrDownloadDto.setFileUuid(fileUuid); + tusFileViewOrDownloadDto.setEventType("VIEW"); + tusFileViewOrDownloadDto.setClientIp(request.getRemoteAddr()); + tusFileViewOrDownloadDto.setUserAgent(request.getHeader("User-Agent")); + tusFileViewOrDownloadDto.setReferer(request.getHeader("Referer")); - FileDownloadDto target = tusFileService.selectFileViewOrDownload(fileDownloadDto); + TusFileViewOrDownloadVo tusFileViewOrDownloadVo = tusFileService.selectTusFileViewOrDownload(tusFileViewOrDownloadDto); - if (target == null) { + if (tusFileViewOrDownloadVo == null) { return ResponseEntity.notFound().build(); } - Path filePath = tusFileService.resolveStoredFilePath(target.getSavePath()); + Path filePath = tusFileService.resolveStoredFilePath(tusFileViewOrDownloadVo.getSavePath()); if (!Files.exists(filePath) || !Files.isRegularFile(filePath)) { return ResponseEntity.notFound().build(); } Resource resource = new InputStreamResource(Files.newInputStream(filePath)); - String contentType = target.getContentType() == null + String contentType = tusFileViewOrDownloadVo.getContentType() == null ? "application/octet-stream" - : target.getContentType(); + : tusFileViewOrDownloadVo.getContentType(); return ResponseEntity.ok() .contentType(MediaType.parseMediaType(contentType)) .contentLength(Files.size(filePath)) .header(HttpHeaders.CONTENT_DISPOSITION, ContentDisposition.inline() - .filename(target.getOriginName(), StandardCharsets.UTF_8) + .filename(tusFileViewOrDownloadVo.getOriginName(), StandardCharsets.UTF_8) .build() .toString()) .body(resource); @@ -226,58 +230,59 @@ public class TusFileController { @Operation( summary = "파일 다운로드" - , description = "권한 확인 후 파일 다운로드 로그를 남기고 redirect 또는 Nginx X-Accel-Redirect로 파일 다운로드 합니다.") + , description = "권한 확인 후 파일 다운로드 로그를 남기고 redirect 또는 Nginx X-Accel-Redirect로 파일 다운로드 합니다." + ) @GetMapping("/download/{fileUuid}") public ResponseEntity tusFileDownload( @PathVariable String fileUuid , HttpServletRequest request ) throws IOException { - FileDownloadDto fileDownloadDto = new FileDownloadDto(); - fileDownloadDto.setFileUuid(fileUuid); - fileDownloadDto.setEventType("DOWNLOAD"); - fileDownloadDto.setClientIp(request.getRemoteAddr()); - fileDownloadDto.setUserAgent(request.getHeader("User-Agent")); - fileDownloadDto.setReferer(request.getHeader("Referer")); + TusFileViewOrDownloadDto tusFileViewOrDownloadDto = new TusFileViewOrDownloadDto(); + tusFileViewOrDownloadDto.setFileUuid(fileUuid); + tusFileViewOrDownloadDto.setEventType("DOWNLOAD"); + tusFileViewOrDownloadDto.setClientIp(request.getRemoteAddr()); + tusFileViewOrDownloadDto.setUserAgent(request.getHeader("User-Agent")); + tusFileViewOrDownloadDto.setReferer(request.getHeader("Referer")); - FileDownloadDto target = tusFileService.selectFileViewOrDownload(fileDownloadDto); + TusFileViewOrDownloadVo tusFileViewOrDownloadVo = tusFileService.selectTusFileViewOrDownload(tusFileViewOrDownloadDto); - if (target == null) { + if (tusFileViewOrDownloadVo == null) { return ResponseEntity.notFound().build(); } - Path filePath = tusFileService.resolveStoredFilePath(target.getSavePath()); + Path filePath = tusFileService.resolveStoredFilePath(tusFileViewOrDownloadVo.getSavePath()); if (!Files.exists(filePath) || !Files.isRegularFile(filePath)) { return ResponseEntity.notFound().build(); } Resource resource = new InputStreamResource(Files.newInputStream(filePath)); - String contentType = target.getContentType() == null + String contentType = tusFileViewOrDownloadVo.getContentType() == null ? "application/octet-stream" - : target.getContentType(); + : tusFileViewOrDownloadVo.getContentType(); return ResponseEntity.ok() .contentType(MediaType.parseMediaType(contentType)) .contentLength(Files.size(filePath)) .header(HttpHeaders.CONTENT_DISPOSITION, ContentDisposition.attachment() - .filename(target.getOriginName(), StandardCharsets.UTF_8) + .filename(tusFileViewOrDownloadVo.getOriginName(), StandardCharsets.UTF_8) .build() .toString()) .body(resource); } @Operation( - summary = "파일 삭제 처리", - description = "fileUuid 기준으로 완료된 파일(STATUS = 3)을 논리 삭제 처리합니다. 실제 파일은 삭제하지 않고 DEL_YN = 'Y'만 반영합니다." + summary = "파일 삭제 처리" + , description = "fileUuid 기준으로 완료된 파일(STATUS = 3)을 논리 삭제 처리합니다. 실제 파일은 삭제하지 않고 DEL_YN = 'Y'만 반영합니다." ) @PostMapping("/delete") public ResponseEntity> tusFileDelete( - @Valid @RequestBody FileDeleteForm fileDeleteForm + @Valid @RequestBody TusFileDeleteForm tusFileDeleteForm ) { - boolean deleted = tusFileService.updateFileDelete(fileDeleteForm.toFileDeleteDto()); + TusFileDeleteVo tusFileDeleteVo = tusFileService.updateTusFileDelete(tusFileDeleteForm.toDto()); - if (!deleted) { + if (!tusFileDeleteVo.isDeleted()) { return ApiResponse.entity("", ApiResponseCode.CODE_2005, "파일 삭제"); } diff --git a/src/main/java/com/alist/api/modules/tusFile/dto/FileDownloadDto.java b/src/main/java/com/alist/api/modules/tusFile/dto/FileDownloadDto.java deleted file mode 100644 index 0c3aa3e..0000000 --- a/src/main/java/com/alist/api/modules/tusFile/dto/FileDownloadDto.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.alist.api.modules.tusFile.dto; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "파일 다운로드 처리 DTO") -@Getter -@Setter -public class FileDownloadDto { - @Schema(description = "파일 상세 PK", example = "1") - private Long fileDetailIdx; - - @Schema(description = "파일 마스터 PK", example = "1") - private Long fileMasterIdx; - - @Schema(description = "파일 UUID", example = "018f6f2d-7b5c-7d49-9c3a-8c0f4f9f9f10") - private String fileUuid; - - @Schema(description = "원본 파일명", example = "sample.png") - private String originName; - - @Schema(description = "Content-Type", example = "image/png") - private String contentType; - - @Schema(description = "파일 크기(byte)", example = "102400") - private Long sizeBytes; - - private String savePath; - private Integer userIdx; - private Integer userTokenIdx; - private String eventType; - private String clientIp; - private String userAgent; - private String referer; - - @JsonIgnore - @Schema(hidden = true) - private Integer resultCode; -} diff --git a/src/main/java/com/alist/api/modules/tusFile/dto/TusFileDeleteDto.java b/src/main/java/com/alist/api/modules/tusFile/dto/TusFileDeleteDto.java new file mode 100644 index 0000000..ab02748 --- /dev/null +++ b/src/main/java/com/alist/api/modules/tusFile/dto/TusFileDeleteDto.java @@ -0,0 +1,16 @@ +package com.alist.api.modules.tusFile.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "TUS 파일 삭제 DTO") +@Getter +@Setter +public class TusFileDeleteDto { + @Schema(description = "파일 UUID", example = "018f6f2d-7b5c-7d49-9c3a-8c0f4f9f9f10") + private String fileUuid; + + @Schema(hidden = true) + private Integer userTokenIdx; +} diff --git a/src/main/java/com/alist/api/modules/tusFile/dto/TusHookDto.java b/src/main/java/com/alist/api/modules/tusFile/dto/TusFileHookDto.java similarity index 92% rename from src/main/java/com/alist/api/modules/tusFile/dto/TusHookDto.java rename to src/main/java/com/alist/api/modules/tusFile/dto/TusFileHookDto.java index e5f4369..d7d21ec 100644 --- a/src/main/java/com/alist/api/modules/tusFile/dto/TusHookDto.java +++ b/src/main/java/com/alist/api/modules/tusFile/dto/TusFileHookDto.java @@ -5,7 +5,7 @@ import lombok.Setter; @Getter @Setter -public class TusHookDto { +public class TusFileHookDto { private String type; private String uploadId; private Long size; diff --git a/src/main/java/com/alist/api/modules/tusFile/dto/TusFileListDto.java b/src/main/java/com/alist/api/modules/tusFile/dto/TusFileListDto.java new file mode 100644 index 0000000..74540ee --- /dev/null +++ b/src/main/java/com/alist/api/modules/tusFile/dto/TusFileListDto.java @@ -0,0 +1,16 @@ +package com.alist.api.modules.tusFile.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "TUS 파일 목록 조회 DTO") +@Getter +@Setter +public class TusFileListDto { + @Schema(description = "파일 마스터 PK", example = "1") + private Long fileMasterIdx; + + @Schema(hidden = true) + private Integer userTokenIdx; +} diff --git a/src/main/java/com/alist/api/modules/tusFile/dto/FileMoveTaskDto.java b/src/main/java/com/alist/api/modules/tusFile/dto/TusFileMoveTaskDto.java similarity index 94% rename from src/main/java/com/alist/api/modules/tusFile/dto/FileMoveTaskDto.java rename to src/main/java/com/alist/api/modules/tusFile/dto/TusFileMoveTaskDto.java index 67f06b4..628c7e6 100644 --- a/src/main/java/com/alist/api/modules/tusFile/dto/FileMoveTaskDto.java +++ b/src/main/java/com/alist/api/modules/tusFile/dto/TusFileMoveTaskDto.java @@ -5,7 +5,7 @@ import lombok.Setter; @Getter @Setter -public class FileMoveTaskDto { +public class TusFileMoveTaskDto { private Long fileDetailIdx; private Long fileMasterIdx; private String fileUuid; diff --git a/src/main/java/com/alist/api/modules/tusFile/dto/UploadTokenDto.java b/src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadAuthDto.java similarity index 83% rename from src/main/java/com/alist/api/modules/tusFile/dto/UploadTokenDto.java rename to src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadAuthDto.java index f157edd..8d65702 100644 --- a/src/main/java/com/alist/api/modules/tusFile/dto/UploadTokenDto.java +++ b/src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadAuthDto.java @@ -1,6 +1,5 @@ package com.alist.api.modules.tusFile.dto; -import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; import lombok.Setter; @@ -8,7 +7,7 @@ import lombok.Setter; @Schema(description = "업로드 인증 토큰 정보") @Getter @Setter -public class UploadTokenDto { +public class TusFileUploadAuthDto { private Integer userTokenIdx; @Schema(description = "파일 UUID", example = "018f6f2d-7b5c-7d49-9c3a-8c0f4f9f9f10") @@ -28,8 +27,4 @@ public class UploadTokenDto { @Schema(description = "Content-Type", example = "image/png") private String contentType; - - @JsonIgnore - @Schema(hidden = true) - private Integer resultCode; } diff --git a/src/main/java/com/alist/api/modules/tusFile/dto/FileDeleteDto.java b/src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadCancelDto.java similarity index 63% rename from src/main/java/com/alist/api/modules/tusFile/dto/FileDeleteDto.java rename to src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadCancelDto.java index 39e5a5a..2420aa7 100644 --- a/src/main/java/com/alist/api/modules/tusFile/dto/FileDeleteDto.java +++ b/src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadCancelDto.java @@ -5,7 +5,10 @@ import lombok.Setter; @Getter @Setter -public class FileDeleteDto { +public class TusFileUploadCancelDto { private Integer userTokenIdx; private String fileUuid; + + private Long uploadedBytes; + private Long sizeBytes; } diff --git a/src/main/java/com/alist/api/modules/tusFile/dto/FileUploadDto.java b/src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadInitDto.java similarity index 92% rename from src/main/java/com/alist/api/modules/tusFile/dto/FileUploadDto.java rename to src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadInitDto.java index 4438eb8..ad91f86 100644 --- a/src/main/java/com/alist/api/modules/tusFile/dto/FileUploadDto.java +++ b/src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadInitDto.java @@ -10,7 +10,7 @@ import java.util.List; @Schema(description = "TUS 파일 업로드 초기화 응답") @Getter @Setter -public class FileUploadDto { +public class TusFileUploadInitDto { @Schema(description = "파일 마스터 PK", example = "1") private Long fileMasterIdx; @@ -42,5 +42,5 @@ public class FileUploadDto { private Integer totalCount; @Schema(description = "업로드 파일 항목 목록") - private List itemList; + private List itemList; } diff --git a/src/main/java/com/alist/api/modules/tusFile/dto/FileUploadItemDto.java b/src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadInitItemDto.java similarity index 96% rename from src/main/java/com/alist/api/modules/tusFile/dto/FileUploadItemDto.java rename to src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadInitItemDto.java index 2043cda..633297a 100644 --- a/src/main/java/com/alist/api/modules/tusFile/dto/FileUploadItemDto.java +++ b/src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadInitItemDto.java @@ -8,7 +8,7 @@ import lombok.Setter; @Schema(description = "TUS 업로드 파일 항목 응답") @Getter @Setter -public class FileUploadItemDto { +public class TusFileUploadInitItemDto { @Schema(description = "파일 순번", example = "1") private Integer fileSeq; diff --git a/src/main/java/com/alist/api/modules/tusFile/dto/FileUploadMetaDto.java b/src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadMetaDto.java similarity index 88% rename from src/main/java/com/alist/api/modules/tusFile/dto/FileUploadMetaDto.java rename to src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadMetaDto.java index ea0c886..477236e 100644 --- a/src/main/java/com/alist/api/modules/tusFile/dto/FileUploadMetaDto.java +++ b/src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadMetaDto.java @@ -5,7 +5,7 @@ import lombok.Setter; @Getter @Setter -public class FileUploadMetaDto { +public class TusFileUploadMetaDto { private String fileUuid; private String originName; private Long sizeBytes; diff --git a/src/main/java/com/alist/api/modules/tusFile/dto/UploadCancelDto.java b/src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadStatusDto.java similarity index 51% rename from src/main/java/com/alist/api/modules/tusFile/dto/UploadCancelDto.java rename to src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadStatusDto.java index dcf6dc6..4ea1285 100644 --- a/src/main/java/com/alist/api/modules/tusFile/dto/UploadCancelDto.java +++ b/src/main/java/com/alist/api/modules/tusFile/dto/TusFileUploadStatusDto.java @@ -5,10 +5,6 @@ import lombok.Setter; @Getter @Setter -public class UploadCancelDto { - private Integer userTokenIdx; +public class TusFileUploadStatusDto { private String fileUuid; - - private Long uploadedBytes; - private Long sizeBytes; } diff --git a/src/main/java/com/alist/api/modules/tusFile/dto/TusFileViewOrDownloadDto.java b/src/main/java/com/alist/api/modules/tusFile/dto/TusFileViewOrDownloadDto.java new file mode 100644 index 0000000..e0d4e43 --- /dev/null +++ b/src/main/java/com/alist/api/modules/tusFile/dto/TusFileViewOrDownloadDto.java @@ -0,0 +1,28 @@ +package com.alist.api.modules.tusFile.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "TUS 파일 보기/다운로드 조회 DTO") +@Getter +@Setter +public class TusFileViewOrDownloadDto { + @Schema(description = "파일 UUID", example = "018f6f2d-7b5c-7d49-9c3a-8c0f4f9f9f10") + private String fileUuid; + + @Schema(hidden = true) + private Integer userTokenIdx; + + @Schema(hidden = true) + private String eventType; + + @Schema(hidden = true) + private String clientIp; + + @Schema(hidden = true) + private String userAgent; + + @Schema(hidden = true) + private String referer; +} diff --git a/src/main/java/com/alist/api/modules/tusFile/dto/UploadStatusDto.java b/src/main/java/com/alist/api/modules/tusFile/dto/UploadStatusDto.java deleted file mode 100644 index 3aeb9a1..0000000 --- a/src/main/java/com/alist/api/modules/tusFile/dto/UploadStatusDto.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.alist.api.modules.tusFile.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "업로드 상태 조회 응답") -@Getter -@Setter -public class UploadStatusDto { - @Schema(description = "파일 UUID", example = "018f6f2d-7b5c-7d49-9c3a-8c0f4f9f9f10") - private String fileUuid; - - @Schema(description = "업로드 상태. PENDING, UPLOADING, DONE, FAILED", example = "UPLOADING") - private String status; // PENDING, UPLOADING, DONE, FAILED - - @Schema(description = "업로드 완료 byte", example = "51200") - private Long uploadedBytes; - - @Schema(description = "전체 파일 크기 byte", example = "102400") - private Long totalBytes; - - @Schema(description = "업로드 진행률", example = "50") - private Integer percent; - - @Schema(description = "상태 갱신 시각", example = "2026-05-19T10:30:00") - private String updatedAt; -} diff --git a/src/main/java/com/alist/api/modules/tusFile/form/FileDeleteForm.java b/src/main/java/com/alist/api/modules/tusFile/form/FileDeleteForm.java deleted file mode 100644 index 5e82c30..0000000 --- a/src/main/java/com/alist/api/modules/tusFile/form/FileDeleteForm.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.alist.api.modules.tusFile.form; - -import com.alist.api.modules.tusFile.dto.FileDeleteDto; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "파일 삭제 요청 폼") -@Getter -@Setter -public class FileDeleteForm { - @Schema( - description = "파일 UUID", - example = "018f6f2d-7b5c-7d49-9c3a-8c0f4f9f9f10", - requiredMode = Schema.RequiredMode.REQUIRED - ) - @NotBlank(message = "파일 UUID는 필수입니다.") - private String fileUuid; - - public FileDeleteDto toFileDeleteDto() { - FileDeleteDto fileDeleteDto = new FileDeleteDto(); - fileDeleteDto.setFileUuid(fileUuid == null ? null : fileUuid.trim()); - return fileDeleteDto; - } -} diff --git a/src/main/java/com/alist/api/modules/tusFile/form/FileUploadForm.java b/src/main/java/com/alist/api/modules/tusFile/form/FileUploadForm.java deleted file mode 100644 index f6ac1d5..0000000 --- a/src/main/java/com/alist/api/modules/tusFile/form/FileUploadForm.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.alist.api.modules.tusFile.form; - -import com.alist.api.modules.tusFile.dto.FileUploadDto; -import com.alist.api.modules.tusFile.dto.FileUploadItemDto; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.Valid; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotEmpty; -import lombok.Getter; -import lombok.Setter; - -import java.util.ArrayList; -import java.util.List; - -@Schema(description = "TUS 파일 업로드 초기화 요청 폼") -@Getter -@Setter -public class FileUploadForm { - @Schema( - description = "파일 카테고리", - example = "BOARD", - requiredMode = Schema.RequiredMode.REQUIRED - ) - @NotBlank(message = "파일 카테고리는 필수입니다.") - private String fileCategory; - - @Schema(description = "업로드 폴더 경로. 미전달 시 / 로 처리됩니다.", example = "/board") - private String folderPath; - - @Schema( - description = "업로드 파일 목록", - requiredMode = Schema.RequiredMode.REQUIRED - ) - @NotEmpty(message = "업로드 파일 목록은 필수입니다.") - @Valid - private List itemList; - - public FileUploadDto toFileUploadDto() { - FileUploadDto fileUploadDto = new FileUploadDto(); - - fileUploadDto.setFileCategory(this.fileCategory.trim()); - fileUploadDto.setFolderPath((this.folderPath == null || this.folderPath.isBlank()) ? "/" : this.folderPath.trim()); - - List fileUploadItemDtoList = new ArrayList<>(); - - int i = 1; - for (FileUploadItemForm item : this.itemList) { - FileUploadItemDto fileUploadItemDto = new FileUploadItemDto(); - - fileUploadItemDto.setFileSeq(i++); - fileUploadItemDto.setOriginName(item.getOriginName()); - fileUploadItemDto.setSizeBytes(item.getSizeBytes()); - fileUploadItemDto.setContentType(item.getContentType()); - - fileUploadItemDtoList.add(fileUploadItemDto); - } - - fileUploadDto.setItemList(fileUploadItemDtoList); - - return fileUploadDto; - } -} diff --git a/src/main/java/com/alist/api/modules/tusFile/form/TusFileDeleteForm.java b/src/main/java/com/alist/api/modules/tusFile/form/TusFileDeleteForm.java new file mode 100644 index 0000000..2d3337e --- /dev/null +++ b/src/main/java/com/alist/api/modules/tusFile/form/TusFileDeleteForm.java @@ -0,0 +1,26 @@ +package com.alist.api.modules.tusFile.form; + +import com.alist.api.modules.tusFile.dto.TusFileDeleteDto; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "파일 삭제 요청 폼") +@Getter +@Setter +public class TusFileDeleteForm { + @Schema( + description = "파일 UUID" + , example = "018f6f2d-7b5c-7d49-9c3a-8c0f4f9f9f10" + , requiredMode = Schema.RequiredMode.REQUIRED + ) + @NotBlank(message = "파일 UUID는 필수입니다.") + private String fileUuid; + + public TusFileDeleteDto toDto() { + TusFileDeleteDto tusFileDeleteDto = new TusFileDeleteDto(); + tusFileDeleteDto.setFileUuid(fileUuid == null ? null : fileUuid.trim()); + return tusFileDeleteDto; + } +} diff --git a/src/main/java/com/alist/api/modules/tusFile/form/TusHookEventForm.java b/src/main/java/com/alist/api/modules/tusFile/form/TusFileHookEventForm.java similarity index 82% rename from src/main/java/com/alist/api/modules/tusFile/form/TusHookEventForm.java rename to src/main/java/com/alist/api/modules/tusFile/form/TusFileHookEventForm.java index a16b343..0b6f1a2 100644 --- a/src/main/java/com/alist/api/modules/tusFile/form/TusHookEventForm.java +++ b/src/main/java/com/alist/api/modules/tusFile/form/TusFileHookEventForm.java @@ -10,8 +10,8 @@ import lombok.Setter; @Getter @Setter @JsonIgnoreProperties(ignoreUnknown = true) -public class TusHookEventForm { +public class TusFileHookEventForm { @Schema(description = "TUS 업로드 정보") @JsonProperty("Upload") - private TusHookUploadForm tusHookUploadForm; + private TusFileHookUploadForm tusFileHookUploadForm; } diff --git a/src/main/java/com/alist/api/modules/tusFile/form/TusFileHookForm.java b/src/main/java/com/alist/api/modules/tusFile/form/TusFileHookForm.java new file mode 100644 index 0000000..ff9a2c7 --- /dev/null +++ b/src/main/java/com/alist/api/modules/tusFile/form/TusFileHookForm.java @@ -0,0 +1,45 @@ +package com.alist.api.modules.tusFile.form; + +import com.alist.api.modules.tusFile.dto.TusFileHookDto; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "TUS 훅 요청 폼") +@Getter +@Setter +@JsonIgnoreProperties(ignoreUnknown = true) +public class TusFileHookForm { + @Schema(description = "TUS 훅 이벤트 타입", example = "post-finish") + @JsonProperty("Type") + private String type; + + @Schema(description = "TUS 훅 이벤트 정보") + @JsonProperty("Event") + private TusFileHookEventForm tusFileHookEventForm; + + public TusFileHookDto toDto() { + TusFileHookDto tusFileHookDto = new TusFileHookDto(); + + tusFileHookDto.setType(this.type); + + if (this.tusFileHookEventForm == null || this.tusFileHookEventForm.getTusFileHookUploadForm() == null) { + return tusFileHookDto; + } + + TusFileHookUploadForm upload = this.tusFileHookEventForm.getTusFileHookUploadForm(); + tusFileHookDto.setUploadId(upload.getId()); + tusFileHookDto.setSize(upload.getSize()); + tusFileHookDto.setOffset(upload.getOffset()); + + if (upload.getMetaData() != null) { + tusFileHookDto.setFileUuid(upload.getMetaData().get("fileUuid")); + tusFileHookDto.setFilename(upload.getMetaData().get("filename")); + tusFileHookDto.setFiletype(upload.getMetaData().get("filetype")); + } + + return tusFileHookDto; + } +} diff --git a/src/main/java/com/alist/api/modules/tusFile/form/TusHookUploadForm.java b/src/main/java/com/alist/api/modules/tusFile/form/TusFileHookUploadForm.java similarity index 96% rename from src/main/java/com/alist/api/modules/tusFile/form/TusHookUploadForm.java rename to src/main/java/com/alist/api/modules/tusFile/form/TusFileHookUploadForm.java index 3a5a483..c4bcf7f 100644 --- a/src/main/java/com/alist/api/modules/tusFile/form/TusHookUploadForm.java +++ b/src/main/java/com/alist/api/modules/tusFile/form/TusFileHookUploadForm.java @@ -12,7 +12,7 @@ import java.util.Map; @Getter @Setter @JsonIgnoreProperties(ignoreUnknown = true) -public class TusHookUploadForm { +public class TusFileHookUploadForm { @Schema(description = "TUS 업로드 ID", example = "018f6f2d7b5c7d499c3a8c0f4f9f9f10") @JsonProperty("ID") private String id; diff --git a/src/main/java/com/alist/api/modules/tusFile/form/TusFileUploadCancelForm.java b/src/main/java/com/alist/api/modules/tusFile/form/TusFileUploadCancelForm.java new file mode 100644 index 0000000..6ed4cba --- /dev/null +++ b/src/main/java/com/alist/api/modules/tusFile/form/TusFileUploadCancelForm.java @@ -0,0 +1,26 @@ +package com.alist.api.modules.tusFile.form; + +import com.alist.api.modules.tusFile.dto.TusFileUploadCancelDto; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "업로드 취소 요청 폼") +@Getter +@Setter +public class TusFileUploadCancelForm { + @Schema( + description = "파일 UUID" + , example = "018f6f2d-7b5c-7d49-9c3a-8c0f4f9f9f10" + , requiredMode = Schema.RequiredMode.REQUIRED + ) + @NotBlank(message = "파일 UUID는 필수입니다.") + private String fileUuid; + + public TusFileUploadCancelDto toDto() { + TusFileUploadCancelDto tusFileUploadCancelDto = new TusFileUploadCancelDto(); + tusFileUploadCancelDto.setFileUuid(this.fileUuid == null ? null : this.fileUuid.trim()); + return tusFileUploadCancelDto; + } +} diff --git a/src/main/java/com/alist/api/modules/tusFile/form/TusFileUploadInitForm.java b/src/main/java/com/alist/api/modules/tusFile/form/TusFileUploadInitForm.java new file mode 100644 index 0000000..d9a0d0b --- /dev/null +++ b/src/main/java/com/alist/api/modules/tusFile/form/TusFileUploadInitForm.java @@ -0,0 +1,62 @@ +package com.alist.api.modules.tusFile.form; + +import com.alist.api.modules.tusFile.dto.TusFileUploadInitDto; +import com.alist.api.modules.tusFile.dto.TusFileUploadInitItemDto; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import lombok.Getter; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; + +@Schema(description = "TUS 파일 업로드 초기화 요청 폼") +@Getter +@Setter +public class TusFileUploadInitForm { + @Schema( + description = "파일 카테고리" + , example = "BOARD" + , requiredMode = Schema.RequiredMode.REQUIRED + ) + @NotBlank(message = "파일 카테고리는 필수입니다.") + private String fileCategory; + + @Schema(description = "업로드 폴더 경로. 미전달 시 / 로 처리됩니다.", example = "/board") + private String folderPath; + + @Schema( + description = "업로드 파일 목록" + , requiredMode = Schema.RequiredMode.REQUIRED + ) + @NotEmpty(message = "업로드 파일 목록은 필수입니다.") + @Valid + private List itemList; + + public TusFileUploadInitDto toDto() { + TusFileUploadInitDto tusFileUploadInitDto = new TusFileUploadInitDto(); + + tusFileUploadInitDto.setFileCategory(this.fileCategory.trim()); + tusFileUploadInitDto.setFolderPath((this.folderPath == null || this.folderPath.isBlank()) ? "/" : this.folderPath.trim()); + + List tusFileUploadInitItemDtoList = new ArrayList<>(); + + int i = 1; + for (TusFileUploadInitItemForm item : this.itemList) { + TusFileUploadInitItemDto tusFileUploadInitItemDto = new TusFileUploadInitItemDto(); + + tusFileUploadInitItemDto.setFileSeq(i++); + tusFileUploadInitItemDto.setOriginName(item.getOriginName()); + tusFileUploadInitItemDto.setSizeBytes(item.getSizeBytes()); + tusFileUploadInitItemDto.setContentType(item.getContentType()); + + tusFileUploadInitItemDtoList.add(tusFileUploadInitItemDto); + } + + tusFileUploadInitDto.setItemList(tusFileUploadInitItemDtoList); + + return tusFileUploadInitDto; + } +} diff --git a/src/main/java/com/alist/api/modules/tusFile/form/FileUploadItemForm.java b/src/main/java/com/alist/api/modules/tusFile/form/TusFileUploadInitItemForm.java similarity index 69% rename from src/main/java/com/alist/api/modules/tusFile/form/FileUploadItemForm.java rename to src/main/java/com/alist/api/modules/tusFile/form/TusFileUploadInitItemForm.java index c96ad66..e0870c4 100644 --- a/src/main/java/com/alist/api/modules/tusFile/form/FileUploadItemForm.java +++ b/src/main/java/com/alist/api/modules/tusFile/form/TusFileUploadInitItemForm.java @@ -10,19 +10,19 @@ import lombok.Setter; @Schema(description = "TUS 업로드 파일 항목") @Getter @Setter -public class FileUploadItemForm { +public class TusFileUploadInitItemForm { @Schema( - description = "원본 파일명", - example = "sample.png", - requiredMode = Schema.RequiredMode.REQUIRED + description = "원본 파일명" + , example = "sample.png" + , requiredMode = Schema.RequiredMode.REQUIRED ) @NotBlank(message = "원본 파일명은 필수입니다.") private String originName; @Schema( - description = "파일 크기(byte)", - example = "102400", - requiredMode = Schema.RequiredMode.REQUIRED + description = "파일 크기(byte)" + , example = "102400" + , requiredMode = Schema.RequiredMode.REQUIRED ) @NotNull(message = "파일 크기는 필수입니다.") @Positive(message = "파일 크기는 0보다 커야 합니다.") diff --git a/src/main/java/com/alist/api/modules/tusFile/form/TusFileUploadStatusForm.java b/src/main/java/com/alist/api/modules/tusFile/form/TusFileUploadStatusForm.java new file mode 100644 index 0000000..240be16 --- /dev/null +++ b/src/main/java/com/alist/api/modules/tusFile/form/TusFileUploadStatusForm.java @@ -0,0 +1,26 @@ +package com.alist.api.modules.tusFile.form; + +import com.alist.api.modules.tusFile.dto.TusFileUploadStatusDto; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "업로드 상태 조회 요청 폼") +@Getter +@Setter +public class TusFileUploadStatusForm { + @Schema( + description = "파일 UUID" + , example = "018f6f2d-7b5c-7d49-9c3a-8c0f4f9f9f10" + , requiredMode = Schema.RequiredMode.REQUIRED + ) + @NotBlank(message = "파일 UUID는 필수입니다.") + private String fileUuid; + + public TusFileUploadStatusDto toDto() { + TusFileUploadStatusDto tusFileUploadStatusDto = new TusFileUploadStatusDto(); + tusFileUploadStatusDto.setFileUuid(this.fileUuid == null ? null : this.fileUuid.trim()); + return tusFileUploadStatusDto; + } +} diff --git a/src/main/java/com/alist/api/modules/tusFile/form/TusHookForm.java b/src/main/java/com/alist/api/modules/tusFile/form/TusHookForm.java deleted file mode 100644 index 45ed80d..0000000 --- a/src/main/java/com/alist/api/modules/tusFile/form/TusHookForm.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.alist.api.modules.tusFile.form; - -import com.alist.api.modules.tusFile.dto.TusHookDto; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "TUS 훅 요청 폼") -@Getter -@Setter -@JsonIgnoreProperties(ignoreUnknown = true) -public class TusHookForm { - @Schema(description = "TUS 훅 이벤트 타입", example = "post-finish") - @JsonProperty("Type") - private String type; - - @Schema(description = "TUS 훅 이벤트 정보") - @JsonProperty("Event") - private TusHookEventForm tusHookEventForm; - - public TusHookDto toTusHookDto() { - TusHookDto tusHookDto = new TusHookDto(); - - tusHookDto.setType(this.type); - - if (this.tusHookEventForm == null || this.tusHookEventForm.getTusHookUploadForm() == null) { - return tusHookDto; - } - - TusHookUploadForm upload = this.tusHookEventForm.getTusHookUploadForm(); - tusHookDto.setUploadId(upload.getId()); - tusHookDto.setSize(upload.getSize()); - tusHookDto.setOffset(upload.getOffset()); - - if (upload.getMetaData() != null) { - tusHookDto.setFileUuid(upload.getMetaData().get("fileUuid")); - tusHookDto.setFilename(upload.getMetaData().get("filename")); - tusHookDto.setFiletype(upload.getMetaData().get("filetype")); - } - - return tusHookDto; - } -} diff --git a/src/main/java/com/alist/api/modules/tusFile/form/UploadCancelForm.java b/src/main/java/com/alist/api/modules/tusFile/form/UploadCancelForm.java deleted file mode 100644 index 72724c8..0000000 --- a/src/main/java/com/alist/api/modules/tusFile/form/UploadCancelForm.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.alist.api.modules.tusFile.form; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "업로드 취소 요청 폼") -@Getter -@Setter -public class UploadCancelForm { - @Schema( - description = "파일 UUID", - example = "018f6f2d-7b5c-7d49-9c3a-8c0f4f9f9f10", - requiredMode = Schema.RequiredMode.REQUIRED - ) - @NotBlank(message = "파일 UUID는 필수입니다.") - private String fileUuid; -} diff --git a/src/main/java/com/alist/api/modules/tusFile/form/UploadStatusForm.java b/src/main/java/com/alist/api/modules/tusFile/form/UploadStatusForm.java deleted file mode 100644 index f7bf635..0000000 --- a/src/main/java/com/alist/api/modules/tusFile/form/UploadStatusForm.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.alist.api.modules.tusFile.form; - -import com.alist.api.modules.tusFile.dto.UploadStatusDto; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import lombok.Getter; -import lombok.Setter; - -@Schema(description = "업로드 상태 조회 요청 폼") -@Getter -@Setter -public class UploadStatusForm { - @Schema( - description = "파일 UUID", - example = "018f6f2d-7b5c-7d49-9c3a-8c0f4f9f9f10", - requiredMode = Schema.RequiredMode.REQUIRED - ) - @NotBlank(message = "파일 UUID는 필수입니다.") - private String fileUuid; - - public UploadStatusDto toUploadStatusDto() { - UploadStatusDto uploadStatusDto = new UploadStatusDto(); - uploadStatusDto.setFileUuid(this.fileUuid == null ? null : this.fileUuid.trim()); - return uploadStatusDto; - } -} diff --git a/src/main/java/com/alist/api/modules/tusFile/mapper/TusFileMapper.java b/src/main/java/com/alist/api/modules/tusFile/mapper/TusFileMapper.java index 09de2d8..98b4c8b 100644 --- a/src/main/java/com/alist/api/modules/tusFile/mapper/TusFileMapper.java +++ b/src/main/java/com/alist/api/modules/tusFile/mapper/TusFileMapper.java @@ -1,53 +1,56 @@ package com.alist.api.modules.tusFile.mapper; import com.alist.api.modules.tusFile.dto.*; +import com.alist.api.modules.tusFile.vo.TusFileListItemVo; +import com.alist.api.modules.tusFile.vo.TusFileUploadCancelProgressVo; +import com.alist.api.modules.tusFile.vo.TusFileViewOrDownloadVo; import org.apache.ibatis.annotations.Mapper; import java.util.List; @Mapper public interface TusFileMapper { - void insertFileMasterInit(FileUploadDto fileUploadDto); + void insertTusFileUploadInitMaster(TusFileUploadInitDto tusFileUploadInitDto); - void insertFileDetailInit(FileUploadItemDto item); + void insertTusFileUploadInitDetail(TusFileUploadInitItemDto tusFileUploadInitItemDto); - int selectFileDetailCountByFileUuidUserToKenIdx(UploadTokenDto uploadTokenDto); + int selectTusFileUploadAuthCount(TusFileUploadAuthDto tusFileUploadAuthDto); - void insertFileUploadEventLog(TusHookDto tusHookDto); + void insertTusFileHookEventLog(TusFileHookDto tusFileHookDto); - void updateFileDetailStatus(TusHookDto tusHookDto); + void updateTusFileHookDetailStatus(TusFileHookDto tusFileHookDto); void updateFileMasterAggregateByFileUuid(String fileUuid); - FileUploadMetaDto selectFileUploadMetaByFileUuid(String fileUuid); + TusFileUploadMetaDto selectTusFileUploadMeta(String fileUuid); - void updateTusUploadIdIfNull(FileUploadMetaDto fileUploadMetaDto); + void updateTusFileUploadIdIfNull(TusFileUploadMetaDto tusFileUploadMetaDto); void updateFileDetailCanceledByFileUuid(String fileUuid); - int selectFileDetailCountByFileUuidAndUserToKenIdx(UploadCancelDto uploadCancelDto); + int selectTusFileUploadCancelCount(TusFileUploadCancelDto tusFileUploadCancelDto); - int updateFileDetailCanceledByFileUuidAndUserTokenIdx(UploadCancelDto uploadCancelDto); + TusFileUploadCancelProgressVo selectTusFileUploadCancelProgress(TusFileUploadCancelDto tusFileUploadCancelDto); - UploadCancelDto selectUploadProgressByFileUuid(UploadCancelDto uploadCancelDto); + TusFileMoveTaskDto selectTusFileMoveTask(String fileUuid); - FileMoveTaskDto selectMoveTaskByFileUuid(String fileUuid); + int updateTusFileMoveSuccess(TusFileMoveTaskDto tusFileMoveTaskDto); - int updateFileMoveSuccess(FileMoveTaskDto task); + int updateTusFileMovePending(TusFileMoveTaskDto tusFileMoveTaskDto); - int updateFileMovePendingByFileUuid(FileMoveTaskDto task); + List selectTusFileList(TusFileListDto tusFileListDto); - List selectFileDownloadListByFileMasterIdx(FileDownloadDto fileDownloadDto); + TusFileViewOrDownloadVo selectTusFileViewOrDownload(TusFileViewOrDownloadDto tusFileViewOrDownloadDto); - FileDownloadDto selectFileDetailByFileUuid(FileDownloadDto fileDownloadDto); + void insertTusFileViewOrDownloadEventLog(TusFileViewOrDownloadVo tusFileViewOrDownloadVo); - void insertFileDownloadEventLog(FileDownloadDto fileDetailInfo); + int selectTusFileDeleteTargetCount(TusFileDeleteDto tusFileDeleteDto); - int selectFileDeleteTargetCount(FileDeleteDto fileDeleteDto); + int updateTusFileDetailDelete(TusFileDeleteDto tusFileDeleteDto); - int updateFileDetailDeleteByFileUuid(FileDeleteDto fileDeleteDto); - - int updateFileMasterDeleteByFileUuid(FileDeleteDto fileDeleteDto); + int updateTusFileMasterDelete(TusFileDeleteDto tusFileDeleteDto); Integer selectUserIdxByUserTokenIdx(Integer userTokenIdx); + + int updateTusFileUploadCancel(TusFileUploadCancelDto tusFileUploadCancelDto); } diff --git a/src/main/java/com/alist/api/modules/tusFile/service/TusFileService.java b/src/main/java/com/alist/api/modules/tusFile/service/TusFileService.java index 7635c69..4926498 100644 --- a/src/main/java/com/alist/api/modules/tusFile/service/TusFileService.java +++ b/src/main/java/com/alist/api/modules/tusFile/service/TusFileService.java @@ -4,6 +4,7 @@ import com.alist.api.common.utils.SecurityUtil; import com.alist.api.config.jwt.JwtTokenProvider; import com.alist.api.modules.tusFile.dto.*; import com.alist.api.modules.tusFile.mapper.TusFileMapper; +import com.alist.api.modules.tusFile.vo.*; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.HashOperations; @@ -19,10 +20,7 @@ import java.security.NoSuchAlgorithmException; import java.time.Duration; import java.time.Instant; import java.time.LocalDate; -import java.util.HexFormat; -import java.util.List; -import java.util.Map; -import java.util.UUID; +import java.util.*; @Slf4j @Service @@ -57,70 +55,102 @@ public class TusFileService { /*파일 초기화 작업*/ @Transactional - public FileUploadDto insertFileInit(FileUploadDto fileUploadDto) { - fileUploadDto.setStatus(0); - fileUploadDto.setTotalCount(fileUploadDto.getItemList().size()); + public TusFileUploadInitVo insertTusFileUploadInit(TusFileUploadInitDto tusFileUploadInitDto) { + tusFileUploadInitDto.setStatus(0); + tusFileUploadInitDto.setTotalCount(tusFileUploadInitDto.getItemList().size()); - tusFileMapper.insertFileMasterInit(fileUploadDto); - fileUploadDto.setTusEndpoint(tusEndpoint); + tusFileMapper.insertTusFileUploadInitMaster(tusFileUploadInitDto); + tusFileUploadInitDto.setTusEndpoint(tusEndpoint); - if (fileUploadDto.getItemList() == null || fileUploadDto.getItemList().isEmpty()) { - return fileUploadDto; + if (tusFileUploadInitDto.getItemList() == null || tusFileUploadInitDto.getItemList().isEmpty()) { + return toTusFileUploadInitVo(tusFileUploadInitDto); } - for (FileUploadItemDto item : fileUploadDto.getItemList()) { - item.setFileMasterIdx(fileUploadDto.getFileMasterIdx()); - item.setFileUuid(UUID.randomUUID().toString()); - item.setUploadToken(jwtTokenProvider.createUploadToken(fileUploadDto.getUserTokenIdx())); - item.setUserIdx(fileUploadDto.getUserIdx()); - item.setStatus(0); // PENDING - tusFileMapper.insertFileDetailInit(item); + for (TusFileUploadInitItemDto tusFileUploadInitItemDto : tusFileUploadInitDto.getItemList()) { + tusFileUploadInitItemDto.setFileMasterIdx(tusFileUploadInitDto.getFileMasterIdx()); + tusFileUploadInitItemDto.setFileUuid(UUID.randomUUID().toString()); + tusFileUploadInitItemDto.setUploadToken(jwtTokenProvider.createUploadToken(tusFileUploadInitDto.getUserTokenIdx())); + tusFileUploadInitItemDto.setUserIdx(tusFileUploadInitDto.getUserIdx()); + tusFileUploadInitItemDto.setStatus(0); + + tusFileMapper.insertTusFileUploadInitDetail(tusFileUploadInitItemDto); } - return fileUploadDto; + return toTusFileUploadInitVo(tusFileUploadInitDto); + } + + private TusFileUploadInitVo toTusFileUploadInitVo(TusFileUploadInitDto tusFileUploadInitDto) { + TusFileUploadInitVo tusFileUploadInitVo = new TusFileUploadInitVo(); + + tusFileUploadInitVo.setFileMasterIdx(tusFileUploadInitDto.getFileMasterIdx()); + tusFileUploadInitVo.setTusEndpoint(tusFileUploadInitDto.getTusEndpoint()); + + List tusFileUploadInitItemVoList = new ArrayList<>(); + + if (tusFileUploadInitDto.getItemList() != null) { + for (TusFileUploadInitItemDto tusFileUploadInitItemDto : tusFileUploadInitDto.getItemList()) { + TusFileUploadInitItemVo tusFileUploadInitItemVo = new TusFileUploadInitItemVo(); + + tusFileUploadInitItemVo.setFileSeq(tusFileUploadInitItemDto.getFileSeq()); + tusFileUploadInitItemVo.setFileUuid(tusFileUploadInitItemDto.getFileUuid()); + tusFileUploadInitItemVo.setUploadToken(tusFileUploadInitItemDto.getUploadToken()); + tusFileUploadInitItemVo.setOriginName(tusFileUploadInitItemDto.getOriginName()); + tusFileUploadInitItemVo.setSizeBytes(tusFileUploadInitItemDto.getSizeBytes()); + tusFileUploadInitItemVo.setContentType(tusFileUploadInitItemDto.getContentType()); + + tusFileUploadInitItemVoList.add(tusFileUploadInitItemVo); + } + } + + tusFileUploadInitVo.setItemList(tusFileUploadInitItemVoList); + + return tusFileUploadInitVo; } /*업로드 권한 체크*/ - public UploadTokenDto isUploadTokenCheck(UploadTokenDto uploadTokenDto) { - if (!jwtTokenProvider.validateToken(uploadTokenDto.getUploadToken())) { - uploadTokenDto.setResultCode(401); - return uploadTokenDto; + public TusFileUploadAuthVo selectTusFileUploadAuth(TusFileUploadAuthDto tusFileUploadAuthDto) { + TusFileUploadAuthVo tusFileUploadAuthVo = new TusFileUploadAuthVo(); + + if (!jwtTokenProvider.validateToken(tusFileUploadAuthDto.getUploadToken())) { + tusFileUploadAuthVo.setResultCode(401); + return tusFileUploadAuthVo; } - if (!jwtTokenProvider.hasUploadScope(uploadTokenDto.getUploadToken())) { - uploadTokenDto.setResultCode(403); - return uploadTokenDto; + if (!jwtTokenProvider.hasUploadScope(tusFileUploadAuthDto.getUploadToken())) { + tusFileUploadAuthVo.setResultCode(403); + return tusFileUploadAuthVo; } try { - uploadTokenDto.setUserTokenIdx(Integer.parseInt(jwtTokenProvider.getUserTokenIdx(uploadTokenDto.getUploadToken()))); + tusFileUploadAuthDto.setUserTokenIdx(Integer.parseInt(jwtTokenProvider.getUserTokenIdx(tusFileUploadAuthDto.getUploadToken()))); } catch (NumberFormatException e) { - uploadTokenDto.setResultCode(401); - return uploadTokenDto; + tusFileUploadAuthVo.setResultCode(401); + return tusFileUploadAuthVo; } - applyTusMeta(uploadTokenDto); + applyTusMeta(tusFileUploadAuthDto); - String cacheKey = buildAuthCacheKey(uploadTokenDto); + String cacheKey = buildAuthCacheKey(tusFileUploadAuthDto); try { String cached = stringRedisTemplate.opsForValue().get(cacheKey); if (cached != null) { try { - uploadTokenDto.setResultCode(Integer.parseInt(cached)); - return uploadTokenDto; + tusFileUploadAuthVo.setResultCode(Integer.parseInt(cached)); + return tusFileUploadAuthVo; } catch (NumberFormatException parseEx) { log.warn("uploadAuth cache parse fail. key={}, value={}", cacheKey, cached); - stringRedisTemplate.delete(cacheKey); // 오염 캐시 정리 + stringRedisTemplate.delete(cacheKey); } } } catch (Exception redisGetEx) { log.warn("uploadAuth cache get fail. fallback DB. key={}", cacheKey, redisGetEx); } - int isOk = tusFileMapper.selectFileDetailCountByFileUuidUserToKenIdx(uploadTokenDto); - int resultCode = (isOk > 0) ? 200 : 403; - uploadTokenDto.setResultCode(resultCode); + int tusFileUploadAuthCount = tusFileMapper.selectTusFileUploadAuthCount(tusFileUploadAuthDto); + int resultCode = (tusFileUploadAuthCount > 0) ? 200 : 403; + + tusFileUploadAuthVo.setResultCode(resultCode); try { stringRedisTemplate.opsForValue().set( @@ -132,16 +162,16 @@ public class TusFileService { log.warn("uploadAuth cache set fail. key={}, resultCode={}", cacheKey, resultCode, redisSetEx); } - return uploadTokenDto; + return tusFileUploadAuthVo; } - private void applyTusMeta(UploadTokenDto dto) { - String len = dto.getUploadLengthRaw(); + private void applyTusMeta(TusFileUploadAuthDto tusFileUploadAuthDto) { + String len = tusFileUploadAuthDto.getUploadLengthRaw(); if (len != null && !len.isBlank()) { - try { dto.setSizeBytes(Long.parseLong(len.trim())); } catch (Exception ignored) {} + try { tusFileUploadAuthDto.setSizeBytes(Long.parseLong(len.trim())); } catch (Exception ignored) {} } - String raw = dto.getUploadMetadataRaw(); + String raw = tusFileUploadAuthDto.getUploadMetadataRaw(); if (raw == null || raw.isBlank()) return; for (String part : raw.split(",")) { @@ -158,20 +188,22 @@ public class TusFileService { continue; } - if ("filename".equals(key)) dto.setOriginName(decoded); - else if ("filetype".equals(key)) dto.setContentType(decoded); + if ("filename".equals(key)) tusFileUploadAuthDto.setOriginName(decoded); + else if ("filetype".equals(key)) tusFileUploadAuthDto.setContentType(decoded); } } - public UploadStatusDto getUploadStatus(UploadStatusDto uploadStatusDto) { - String key = buildUploadStatusKey(uploadStatusDto.getFileUuid()); + public TusFileUploadStatusVo loadTusFileUploadStatus(TusFileUploadStatusDto tusFileUploadStatusDto) { + String key = buildUploadStatusKey(tusFileUploadStatusDto.getFileUuid()); Map map = stringRedisTemplate.opsForHash().entries(key); if (map.isEmpty()) { return null; } - String fileUuid = (String) map.getOrDefault("fileUuid", uploadStatusDto.getFileUuid()); + TusFileUploadStatusVo tusFileUploadStatusVo = new TusFileUploadStatusVo(); + + String fileUuid = (String) map.getOrDefault("fileUuid", tusFileUploadStatusDto.getFileUuid()); String status = (String) map.getOrDefault("status", "PENDING"); long uploadedBytes = parseLong(map.get("uploadedBytes")); long totalBytes = parseLong(map.get("totalBytes")); @@ -184,19 +216,19 @@ public class TusFileService { updatedAt = Instant.now().toString(); saveCanceledStatusRedis(fileUuid, uploadedBytes, totalBytes); - fileMapper.updateFileDetailCanceledByFileUuid(fileUuid); - fileMapper.updateFileMasterAggregateByFileUuid(fileUuid); + tusFileMapper.updateFileDetailCanceledByFileUuid(fileUuid); + tusFileMapper.updateFileMasterAggregateByFileUuid(fileUuid); } */ - uploadStatusDto.setFileUuid(fileUuid); - uploadStatusDto.setStatus(status); - uploadStatusDto.setUploadedBytes(uploadedBytes); - uploadStatusDto.setTotalBytes(totalBytes); - uploadStatusDto.setPercent(percent); - uploadStatusDto.setUpdatedAt(updatedAt); + tusFileUploadStatusVo.setFileUuid(fileUuid); + tusFileUploadStatusVo.setStatus(status); + tusFileUploadStatusVo.setUploadedBytes(uploadedBytes); + tusFileUploadStatusVo.setTotalBytes(totalBytes); + tusFileUploadStatusVo.setPercent(percent); + tusFileUploadStatusVo.setUpdatedAt(updatedAt); - return uploadStatusDto; + return tusFileUploadStatusVo; } private boolean isStaleUploading(String status, String updatedAt) { @@ -222,23 +254,23 @@ public class TusFileService { /*db에 상태값 저장*/ @Transactional - public boolean updateFileUploadStatus(TusHookDto tusHookDto) { - if (tusHookDto == null || tusHookDto.getFileUuid() == null || tusHookDto.getFileUuid().isBlank()) { + public boolean updateTusFileHook(TusFileHookDto tusFileHookDto) { + if (tusFileHookDto == null || tusFileHookDto.getFileUuid() == null || tusFileHookDto.getFileUuid().isBlank()) { return true; } - String fileUuid = tusHookDto.getFileUuid().trim(); - String type = tusHookDto.getType() == null ? "" : tusHookDto.getType().trim().toLowerCase(); - String uploadId = tusHookDto.getUploadId() == null ? "" : tusHookDto.getUploadId().trim(); - long offset = tusHookDto.getOffset() == null ? 0L : tusHookDto.getOffset(); - long size = tusHookDto.getSize() == null ? 0L : tusHookDto.getSize(); + String fileUuid = tusFileHookDto.getFileUuid().trim(); + String type = tusFileHookDto.getType() == null ? "" : tusFileHookDto.getType().trim().toLowerCase(); + String uploadId = tusFileHookDto.getUploadId() == null ? "" : tusFileHookDto.getUploadId().trim(); + long offset = tusFileHookDto.getOffset() == null ? 0L : tusFileHookDto.getOffset(); + long size = tusFileHookDto.getSize() == null ? 0L : tusFileHookDto.getSize(); - tusFileMapper.insertFileUploadEventLog(tusHookDto); + tusFileMapper.insertTusFileHookEventLog(tusFileHookDto); - FileUploadMetaDto fileUploadMetaInfo = tusFileMapper.selectFileUploadMetaByFileUuid(fileUuid); + TusFileUploadMetaDto fileUploadMetaInfo = tusFileMapper.selectTusFileUploadMeta(fileUuid); if (fileUploadMetaInfo == null) return true; - if (isMetadataMismatch(fileUploadMetaInfo, tusHookDto)) { + if (isMetadataMismatch(fileUploadMetaInfo, tusFileHookDto)) { tusFileMapper.updateFileDetailCanceledByFileUuid(fileUuid); tusFileMapper.updateFileMasterAggregateByFileUuid(fileUuid); saveCanceledStatusRedis(fileUuid, offset, size); @@ -247,12 +279,12 @@ public class TusFileService { if (!uploadId.isBlank()) { if (fileUploadMetaInfo.getTusUploadId() == null || fileUploadMetaInfo.getTusUploadId().isBlank()) { - FileUploadMetaDto fileUploadMetaDto = new FileUploadMetaDto(); - fileUploadMetaDto.setFileUuid(fileUuid); - fileUploadMetaDto.setTusUploadId(uploadId); - tusFileMapper.updateTusUploadIdIfNull(fileUploadMetaDto); + TusFileUploadMetaDto tusFileUploadMetaDto = new TusFileUploadMetaDto(); + tusFileUploadMetaDto.setFileUuid(fileUuid); + tusFileUploadMetaDto.setTusUploadId(uploadId); + tusFileMapper.updateTusFileUploadIdIfNull(tusFileUploadMetaDto); - fileUploadMetaInfo = tusFileMapper.selectFileUploadMetaByFileUuid(fileUuid); + fileUploadMetaInfo = tusFileMapper.selectTusFileUploadMeta(fileUuid); } if (fileUploadMetaInfo.getTusUploadId() == null || fileUploadMetaInfo.getTusUploadId().isBlank() || !fileUploadMetaInfo.getTusUploadId().equals(uploadId)) { @@ -280,8 +312,8 @@ public class TusFileService { return false; } - tusHookDto.setUploadedBytes(null); - tusHookDto.setStatus(1); // UPLOADING + tusFileHookDto.setUploadedBytes(null); + tusFileHookDto.setStatus(1); // UPLOADING break; case "post-receive": @@ -290,13 +322,13 @@ public class TusFileService { return false; } - tusHookDto.setUploadedBytes(null); - tusHookDto.setStatus(1); // UPLOADING + tusFileHookDto.setUploadedBytes(null); + tusFileHookDto.setStatus(1); // UPLOADING break; case "post-finish": - tusHookDto.setUploadedBytes(size); - tusHookDto.setStatus(3); // DONE + tusFileHookDto.setUploadedBytes(size); + tusFileHookDto.setStatus(3); // DONE break; case "post-terminate": @@ -304,31 +336,31 @@ public class TusFileService { if (currentStatus != null && currentStatus == 3) { return false; } - tusHookDto.setUploadedBytes(offset); - tusHookDto.setStatus(5); // CANCELED + tusFileHookDto.setUploadedBytes(offset); + tusFileHookDto.setStatus(5); // CANCELED break; default: return false; } - if (tusHookDto.getStatus() == 1 && currentStatus != null && currentStatus >= 3) { + if (tusFileHookDto.getStatus() == 1 && currentStatus != null && currentStatus >= 3) { return false; } boolean shouldUpdateDetail = !"post-receive".equals(type); if (shouldUpdateDetail) { - tusFileMapper.updateFileDetailStatus(tusHookDto); + tusFileMapper.updateTusFileHookDetailStatus(tusFileHookDto); } if ("post-finish".equals(type) || "post-terminate".equals(type)) { - tusFileMapper.updateFileMasterAggregateByFileUuid(tusHookDto.getFileUuid()); + tusFileMapper.updateFileMasterAggregateByFileUuid(tusFileHookDto.getFileUuid()); } if ("post-finish".equals(type)) { try { - tryMoveNowByFileUuid(tusHookDto.getFileUuid()); + tryMoveNowByFileUuid(tusFileHookDto.getFileUuid()); } catch (NoSuchFileException e) { try { Thread.sleep(200); @@ -336,14 +368,14 @@ public class TusFileService { Thread.currentThread().interrupt(); } try { - tryMoveNowByFileUuid(tusHookDto.getFileUuid()); // 1회 재시도 + tryMoveNowByFileUuid(tusFileHookDto.getFileUuid()); // 1회 재시도 } catch (Exception ex) { - log.error("Move failed after retry. fileUuid={}", tusHookDto.getFileUuid(), ex); - markMovePending(tusHookDto.getFileUuid(), truncateErr(ex.toString())); + log.error("Move failed after retry. fileUuid={}", tusFileHookDto.getFileUuid(), ex); + markMovePending(tusFileHookDto.getFileUuid(), truncateErr(ex.toString())); } } catch (Exception moveEx) { - log.error("Move failed. fileUuid={}", tusHookDto.getFileUuid(), moveEx); - markMovePending(tusHookDto.getFileUuid(), truncateErr(moveEx.toString())); + log.error("Move failed. fileUuid={}", tusFileHookDto.getFileUuid(), moveEx); + markMovePending(tusFileHookDto.getFileUuid(), truncateErr(moveEx.toString())); } } @@ -352,7 +384,7 @@ public class TusFileService { @Transactional public void tryMoveNowByFileUuid(String fileUuid) throws IOException { - FileMoveTaskDto task = tusFileMapper.selectMoveTaskByFileUuid(fileUuid); + TusFileMoveTaskDto task = tusFileMapper.selectTusFileMoveTask(fileUuid); if (task == null) return; if (task.getMoveYn() != null && !"N".equalsIgnoreCase(task.getMoveYn())) return; @@ -373,15 +405,15 @@ public class TusFileService { task.setSavePath(toSavePath(finalPath)); task.setSaveName(finalPath.getFileName().toString()); task.setExt(extractExt(task.getSaveName())); - tusFileMapper.updateFileMoveSuccess(task); + tusFileMapper.updateTusFileMoveSuccess(task); } @Transactional public void markMovePending(String fileUuid, String errMsg) { - FileMoveTaskDto task = new FileMoveTaskDto(); - task.setFileUuid(fileUuid); - task.setMoveLastError(errMsg == null ? "move failed" : errMsg); - tusFileMapper.updateFileMovePendingByFileUuid(task); // MOVE_YN='N', TRY_COUNT+1, LAST_ERROR + TusFileMoveTaskDto tusFileMoveTaskDto = new TusFileMoveTaskDto(); + tusFileMoveTaskDto.setFileUuid(fileUuid); + tusFileMoveTaskDto.setMoveLastError(errMsg == null ? "move failed" : errMsg); + tusFileMapper.updateTusFileMovePending(tusFileMoveTaskDto); // MOVE_YN='N', TRY_COUNT+1, LAST_ERROR } private String truncateErr(String s) { @@ -389,7 +421,7 @@ public class TusFileService { return s.length() > 500 ? s.substring(0, 500) : s; } - private Path resolveTmpPath(FileMoveTaskDto task) { + private Path resolveTmpPath(TusFileMoveTaskDto task) { // 예: /srv/project/alist/uploads/tmp/{tusUploadId} if (task.getTusUploadId() == null || task.getTusUploadId().isBlank()) { throw new IllegalStateException("tusUploadId is empty"); @@ -397,7 +429,7 @@ public class TusFileService { return Paths.get(uploadTmpRoot, task.getTusUploadId()); } - private Path buildFinalPath(FileMoveTaskDto task) { + private Path buildFinalPath(TusFileMoveTaskDto task) { String ext = extractExt(task.getOriginName()); String fileName = (task.getFileUuid() == null ? UUID.randomUUID().toString() : task.getFileUuid()) + (ext.isBlank() ? "" : "." + ext); @@ -449,15 +481,15 @@ public class TusFileService { } /*레이디스에 상태값 저장*/ - public void saveUploadStatusRedisTusHook(TusHookDto tusHookDto) { - if (tusHookDto == null || tusHookDto.getFileUuid() == null || tusHookDto.getFileUuid().isBlank()) { + public void saveTusFileHookStatusRedis(TusFileHookDto tusFileHookDto) { + if (tusFileHookDto == null || tusFileHookDto.getFileUuid() == null || tusFileHookDto.getFileUuid().isBlank()) { return; } - String key = buildUploadStatusKey(tusHookDto.getFileUuid().trim()); - String type = tusHookDto.getType() == null ? "" : tusHookDto.getType().trim().toLowerCase(); - long total = tusHookDto.getSize() == null ? 0L : tusHookDto.getSize(); - long uploaded = tusHookDto.getOffset() == null ? 0L : tusHookDto.getOffset(); + String key = buildUploadStatusKey(tusFileHookDto.getFileUuid().trim()); + String type = tusFileHookDto.getType() == null ? "" : tusFileHookDto.getType().trim().toLowerCase(); + long total = tusFileHookDto.getSize() == null ? 0L : tusFileHookDto.getSize(); + long uploaded = tusFileHookDto.getOffset() == null ? 0L : tusFileHookDto.getOffset(); String status = "UPLOADING"; if ("post-finish".equals(type)) { @@ -472,7 +504,7 @@ public class TusFileService { int percent = (total > 0) ? (int) Math.min(100, (uploaded * 100) / total) : 0; HashOperations ops = stringRedisTemplate.opsForHash(); - ops.put(key, "fileUuid", tusHookDto.getFileUuid()); + ops.put(key, "fileUuid", tusFileHookDto.getFileUuid()); ops.put(key, "status", status); ops.put(key, "uploadedBytes", String.valueOf(uploaded)); ops.put(key, "totalBytes", String.valueOf(total)); @@ -510,14 +542,14 @@ public class TusFileService { } } - private String buildAuthCacheKey(UploadTokenDto dto) { - String normalizedName = normalize(dto.getOriginName()); - String normalizedType = normalize(dto.getContentType()); - String normalizedSize = (dto.getSizeBytes() == null) ? "" : String.valueOf(dto.getSizeBytes()); + private String buildAuthCacheKey(TusFileUploadAuthDto tusFileUploadAuthDto) { + String normalizedName = normalize(tusFileUploadAuthDto.getOriginName()); + String normalizedType = normalize(tusFileUploadAuthDto.getContentType()); + String normalizedSize = (tusFileUploadAuthDto.getSizeBytes() == null) ? "" : String.valueOf(tusFileUploadAuthDto.getSizeBytes()); String raw = String.join("|", - normalize(dto.getFileUuid()), - dto.getUploadToken() == null ? "" : dto.getUploadToken(), + normalize(tusFileUploadAuthDto.getFileUuid()), + tusFileUploadAuthDto.getUploadToken() == null ? "" : tusFileUploadAuthDto.getUploadToken(), normalizedName, normalizedType, normalizedSize @@ -545,96 +577,117 @@ public class TusFileService { return (uploadAuthCacheTtlSeconds > 0) ? uploadAuthCacheTtlSeconds : 20L; } - private boolean isMetadataMismatch(FileUploadMetaDto fileUploadMetaDto, TusHookDto tusHookDto) { - if (tusHookDto.getSize() != null && fileUploadMetaDto.getSizeBytes() != null && !tusHookDto.getSize().equals(fileUploadMetaDto.getSizeBytes())) { + private boolean isMetadataMismatch(TusFileUploadMetaDto tusFileUploadMetaDto, TusFileHookDto tusFileHookDto) { + if (tusFileHookDto.getSize() != null && tusFileUploadMetaDto.getSizeBytes() != null && !tusFileHookDto.getSize().equals(tusFileUploadMetaDto.getSizeBytes())) { return true; } - if (tusHookDto.getFilename() != null && !tusHookDto.getFilename().isBlank() - && fileUploadMetaDto.getOriginName() != null && !fileUploadMetaDto.getOriginName().equals(tusHookDto.getFilename())) { + if (tusFileHookDto.getFilename() != null && !tusFileHookDto.getFilename().isBlank() + && tusFileUploadMetaDto.getOriginName() != null && !tusFileUploadMetaDto.getOriginName().equals(tusFileHookDto.getFilename())) { return true; } - if (tusHookDto.getFiletype() != null && !tusHookDto.getFiletype().isBlank() - && fileUploadMetaDto.getContentType() != null && !fileUploadMetaDto.getContentType().isBlank() - && !fileUploadMetaDto.getContentType().equalsIgnoreCase(tusHookDto.getFiletype())) { + if (tusFileHookDto.getFiletype() != null && !tusFileHookDto.getFiletype().isBlank() + && tusFileUploadMetaDto.getContentType() != null && !tusFileUploadMetaDto.getContentType().isBlank() + && !tusFileUploadMetaDto.getContentType().equalsIgnoreCase(tusFileHookDto.getFiletype())) { return true; } return false; } @Transactional - public boolean updateUploadCancel(UploadCancelDto uploadCancelDto) { - if (uploadCancelDto.getFileUuid() == null || uploadCancelDto.getFileUuid().isBlank() || uploadCancelDto.getUserTokenIdx() == null) { - return false; + public TusFileUploadCancelVo updateTusFileUploadCancel(TusFileUploadCancelDto tusFileUploadCancelDto) { + TusFileUploadCancelVo tusFileUploadCancelVo = new TusFileUploadCancelVo(); + + if (tusFileUploadCancelDto.getFileUuid() == null + || tusFileUploadCancelDto.getFileUuid().isBlank() + || tusFileUploadCancelDto.getUserTokenIdx() == null) { + tusFileUploadCancelVo.setCanceled(false); + tusFileUploadCancelVo.setResultCode(4003); + return tusFileUploadCancelVo; } - int allowed = tusFileMapper.selectFileDetailCountByFileUuidAndUserToKenIdx(uploadCancelDto); - if (allowed <= 0) { - return false; + int tusFileUploadCancelCount = tusFileMapper.selectTusFileUploadCancelCount(tusFileUploadCancelDto); + if (tusFileUploadCancelCount <= 0) { + tusFileUploadCancelVo.setCanceled(false); + tusFileUploadCancelVo.setResultCode(2003); + return tusFileUploadCancelVo; } - UploadCancelDto progress = tusFileMapper.selectUploadProgressByFileUuid(uploadCancelDto); - long uploaded = (progress != null && progress.getUploadedBytes() != null) ? progress.getUploadedBytes() : 0L; - long total = (progress != null && progress.getSizeBytes() != null) ? progress.getSizeBytes() : 0L; + TusFileUploadCancelProgressVo tusFileUploadCancelProgressVo = tusFileMapper.selectTusFileUploadCancelProgress(tusFileUploadCancelDto); - int updated = tusFileMapper.updateFileDetailCanceledByFileUuidAndUserTokenIdx(uploadCancelDto); - if (updated <= 0) { - return false; + long uploadedBytes = (tusFileUploadCancelProgressVo != null && tusFileUploadCancelProgressVo.getUploadedBytes() != null) + ? tusFileUploadCancelProgressVo.getUploadedBytes() + : 0L; + + long totalBytes = (tusFileUploadCancelProgressVo != null && tusFileUploadCancelProgressVo.getSizeBytes() != null) + ? tusFileUploadCancelProgressVo.getSizeBytes() + : 0L; + + int updateCount = tusFileMapper.updateTusFileUploadCancel(tusFileUploadCancelDto); + if (updateCount <= 0) { + tusFileUploadCancelVo.setCanceled(false); + tusFileUploadCancelVo.setResultCode(2003); + return tusFileUploadCancelVo; } - tusFileMapper.updateFileMasterAggregateByFileUuid(uploadCancelDto.getFileUuid()); - saveCanceledStatusRedis(uploadCancelDto.getFileUuid(), uploaded, total); // 필요하면 기존 bytes 조회해서 넣어도 됨 - return true; + tusFileMapper.updateFileMasterAggregateByFileUuid(tusFileUploadCancelDto.getFileUuid()); + saveCanceledStatusRedis(tusFileUploadCancelDto.getFileUuid(), uploadedBytes, totalBytes); + + tusFileUploadCancelVo.setCanceled(true); + tusFileUploadCancelVo.setResultCode(200); + return tusFileUploadCancelVo; } @Transactional(readOnly = true) - public FileDownloadListDto selectFileDownloadListByFileMasterIdx(FileDownloadDto fileDownloadDto) { - FileDownloadListDto fileDownloadList = new FileDownloadListDto(); + public TusFileListVo selectTusFileList(TusFileListDto tusFileListDto) { + TusFileListVo tusFileListVo = new TusFileListVo(); Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx(); if (userTokenIdx == null) { - fileDownloadList.setResultCode(401); - return fileDownloadList; + tusFileListVo.setResultCode(401); + return tusFileListVo; } - fileDownloadDto.setUserTokenIdx(userTokenIdx); + tusFileListDto.setUserTokenIdx(userTokenIdx); - fileDownloadList.setFileMasterIdx(fileDownloadDto.getFileMasterIdx()); + tusFileListVo.setFileMasterIdx(tusFileListDto.getFileMasterIdx()); - List itemList = tusFileMapper.selectFileDownloadListByFileMasterIdx(fileDownloadDto); + List tusFileList = tusFileMapper.selectTusFileList(tusFileListDto); - for (FileDownloadItemDto item : itemList) { + for (TusFileListItemVo item : tusFileList) { item.setViewUrl("/tus/file/view/" + item.getFileUuid()); item.setDownloadUrl("/tus/file/download/" + item.getFileUuid()); } - fileDownloadList.setItemList(itemList); - fileDownloadList.setResultCode(200); + tusFileListVo.setItemList(tusFileList); + tusFileListVo.setResultCode(200); - return fileDownloadList; + return tusFileListVo; } - public FileDownloadDto selectFileViewOrDownload(FileDownloadDto fileDownloadDto) { + @Transactional + public TusFileViewOrDownloadVo selectTusFileViewOrDownload(TusFileViewOrDownloadDto tusFileViewOrDownloadDto) { Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx(); if (userTokenIdx == null) { return null; } - fileDownloadDto.setUserTokenIdx(userTokenIdx); + tusFileViewOrDownloadDto.setUserTokenIdx(userTokenIdx); - FileDownloadDto fileDetailInfo = tusFileMapper.selectFileDetailByFileUuid(fileDownloadDto); + TusFileViewOrDownloadVo tusFileViewOrDownloadVo = tusFileMapper.selectTusFileViewOrDownload(tusFileViewOrDownloadDto); - if (fileDetailInfo == null) { + if (tusFileViewOrDownloadVo == null) { return null; } - fileDetailInfo.setUserTokenIdx(fileDownloadDto.getUserTokenIdx()); - fileDetailInfo.setEventType(fileDownloadDto.getEventType()); - fileDetailInfo.setClientIp(fileDownloadDto.getClientIp()); - fileDetailInfo.setUserAgent(fileDownloadDto.getUserAgent()); - fileDetailInfo.setReferer(fileDownloadDto.getReferer()); + tusFileViewOrDownloadVo.setUserTokenIdx(tusFileViewOrDownloadDto.getUserTokenIdx()); + tusFileViewOrDownloadVo.setEventType(tusFileViewOrDownloadDto.getEventType()); + tusFileViewOrDownloadVo.setClientIp(tusFileViewOrDownloadDto.getClientIp()); + tusFileViewOrDownloadVo.setUserAgent(tusFileViewOrDownloadDto.getUserAgent()); + tusFileViewOrDownloadVo.setReferer(tusFileViewOrDownloadDto.getReferer()); - tusFileMapper.insertFileDownloadEventLog(fileDetailInfo); - return fileDetailInfo; + tusFileMapper.insertTusFileViewOrDownloadEventLog(tusFileViewOrDownloadVo); + + return tusFileViewOrDownloadVo; } public Path resolveStoredFilePath(String savePath) { @@ -655,31 +708,46 @@ public class TusFileService { } @Transactional - public boolean updateFileDelete(FileDeleteDto fileDeleteDto) { - if (fileDeleteDto == null || fileDeleteDto.getFileUuid() == null || fileDeleteDto.getFileUuid().isBlank()) { - return false; + public TusFileDeleteVo updateTusFileDelete(TusFileDeleteDto tusFileDeleteDto) { + TusFileDeleteVo tusFileDeleteVo = new TusFileDeleteVo(); + + if (tusFileDeleteDto == null + || tusFileDeleteDto.getFileUuid() == null + || tusFileDeleteDto.getFileUuid().isBlank()) { + tusFileDeleteVo.setDeleted(false); + tusFileDeleteVo.setResultCode(4003); + return tusFileDeleteVo; } Integer userTokenIdx = SecurityUtil.getLoginUserTokenIdx(); if (userTokenIdx == null) { - return false; + tusFileDeleteVo.setDeleted(false); + tusFileDeleteVo.setResultCode(401); + return tusFileDeleteVo; } - fileDeleteDto.setUserTokenIdx(userTokenIdx); - fileDeleteDto.setFileUuid(fileDeleteDto.getFileUuid().trim()); + tusFileDeleteDto.setUserTokenIdx(userTokenIdx); + tusFileDeleteDto.setFileUuid(tusFileDeleteDto.getFileUuid().trim()); - int allowed = tusFileMapper.selectFileDeleteTargetCount(fileDeleteDto); - if (allowed <= 0) { - return false; + int tusFileDeleteTargetCount = tusFileMapper.selectTusFileDeleteTargetCount(tusFileDeleteDto); + if (tusFileDeleteTargetCount <= 0) { + tusFileDeleteVo.setDeleted(false); + tusFileDeleteVo.setResultCode(2003); + return tusFileDeleteVo; } - int detailUpdated = tusFileMapper.updateFileDetailDeleteByFileUuid(fileDeleteDto); - if (detailUpdated <= 0) { - return false; + int tusFileDetailDeleteCount = tusFileMapper.updateTusFileDetailDelete(tusFileDeleteDto); + if (tusFileDetailDeleteCount <= 0) { + tusFileDeleteVo.setDeleted(false); + tusFileDeleteVo.setResultCode(2003); + return tusFileDeleteVo; } - tusFileMapper.updateFileMasterDeleteByFileUuid(fileDeleteDto); - return true; + tusFileMapper.updateTusFileMasterDelete(tusFileDeleteDto); + + tusFileDeleteVo.setDeleted(true); + tusFileDeleteVo.setResultCode(200); + return tusFileDeleteVo; } @Transactional(readOnly = true) diff --git a/src/main/java/com/alist/api/modules/tusFile/vo/TusFileDeleteVo.java b/src/main/java/com/alist/api/modules/tusFile/vo/TusFileDeleteVo.java new file mode 100644 index 0000000..a623b9a --- /dev/null +++ b/src/main/java/com/alist/api/modules/tusFile/vo/TusFileDeleteVo.java @@ -0,0 +1,18 @@ +package com.alist.api.modules.tusFile.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "TUS 파일 삭제 응답") +@Getter +@Setter +public class TusFileDeleteVo { + @Schema(description = "파일 삭제 처리 여부", example = "true") + private boolean deleted; + + @JsonIgnore + @Schema(hidden = true) + private Integer resultCode; +} diff --git a/src/main/java/com/alist/api/modules/tusFile/dto/FileDownloadItemDto.java b/src/main/java/com/alist/api/modules/tusFile/vo/TusFileListItemVo.java similarity index 92% rename from src/main/java/com/alist/api/modules/tusFile/dto/FileDownloadItemDto.java rename to src/main/java/com/alist/api/modules/tusFile/vo/TusFileListItemVo.java index 2eca5a2..8a23cf3 100644 --- a/src/main/java/com/alist/api/modules/tusFile/dto/FileDownloadItemDto.java +++ b/src/main/java/com/alist/api/modules/tusFile/vo/TusFileListItemVo.java @@ -1,4 +1,4 @@ -package com.alist.api.modules.tusFile.dto; +package com.alist.api.modules.tusFile.vo; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; @@ -7,7 +7,7 @@ import lombok.Setter; @Schema(description = "다운로드 파일 항목") @Getter @Setter -public class FileDownloadItemDto { +public class TusFileListItemVo { @Schema(description = "파일 상세 PK", example = "1") private Long fileDetailIdx; diff --git a/src/main/java/com/alist/api/modules/tusFile/dto/FileDownloadListDto.java b/src/main/java/com/alist/api/modules/tusFile/vo/TusFileListVo.java similarity index 79% rename from src/main/java/com/alist/api/modules/tusFile/dto/FileDownloadListDto.java rename to src/main/java/com/alist/api/modules/tusFile/vo/TusFileListVo.java index f18d8fe..87ea278 100644 --- a/src/main/java/com/alist/api/modules/tusFile/dto/FileDownloadListDto.java +++ b/src/main/java/com/alist/api/modules/tusFile/vo/TusFileListVo.java @@ -1,4 +1,4 @@ -package com.alist.api.modules.tusFile.dto; +package com.alist.api.modules.tusFile.vo; import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.v3.oas.annotations.media.Schema; @@ -10,12 +10,12 @@ import java.util.List; @Schema(description = "파일 다운로드 목록 응답") @Getter @Setter -public class FileDownloadListDto { +public class TusFileListVo { @Schema(description = "파일 마스터 PK", example = "1") private Long fileMasterIdx; @Schema(description = "다운로드 파일 목록") - private List itemList; + private List itemList; @JsonIgnore @Schema(hidden = true) diff --git a/src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadAuthVo.java b/src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadAuthVo.java new file mode 100644 index 0000000..2bf1e6e --- /dev/null +++ b/src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadAuthVo.java @@ -0,0 +1,15 @@ +package com.alist.api.modules.tusFile.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "TUS 업로드 권한 검증 응답") +@Getter +@Setter +public class TusFileUploadAuthVo { + @JsonIgnore + @Schema(hidden = true) + private Integer resultCode; +} diff --git a/src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadCancelProgressVo.java b/src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadCancelProgressVo.java new file mode 100644 index 0000000..1bda56c --- /dev/null +++ b/src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadCancelProgressVo.java @@ -0,0 +1,12 @@ +package com.alist.api.modules.tusFile.vo; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TusFileUploadCancelProgressVo { + private String fileUuid; + private Long uploadedBytes; + private Long sizeBytes; +} diff --git a/src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadCancelVo.java b/src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadCancelVo.java new file mode 100644 index 0000000..f190b12 --- /dev/null +++ b/src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadCancelVo.java @@ -0,0 +1,19 @@ +package com.alist.api.modules.tusFile.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "TUS 업로드 취소 응답") +@Getter +@Setter +public class TusFileUploadCancelVo { + @JsonIgnore + @Schema(hidden = true) + private boolean canceled; + + @JsonIgnore + @Schema(hidden = true) + private Integer resultCode; +} diff --git a/src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadInitItemVo.java b/src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadInitItemVo.java new file mode 100644 index 0000000..3a854ac --- /dev/null +++ b/src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadInitItemVo.java @@ -0,0 +1,28 @@ +package com.alist.api.modules.tusFile.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "TUS 파일 업로드 초기화 파일 항목 응답") +@Getter +@Setter +public class TusFileUploadInitItemVo { + @Schema(description = "파일 순번", example = "1") + private Integer fileSeq; + + @Schema(description = "파일 UUID", example = "018f6f2d-7b5c-7d49-9c3a-8c0f4f9f9f10") + private String fileUuid; + + @Schema(description = "TUS 업로드 토큰") + private String uploadToken; + + @Schema(description = "원본 파일명", example = "sample.png") + private String originName; + + @Schema(description = "파일 크기(byte)", example = "102400") + private Long sizeBytes; + + @Schema(description = "Content-Type", example = "image/png") + private String contentType; +} diff --git a/src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadInitVo.java b/src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadInitVo.java new file mode 100644 index 0000000..4aa152b --- /dev/null +++ b/src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadInitVo.java @@ -0,0 +1,21 @@ +package com.alist.api.modules.tusFile.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Schema(description = "TUS 파일 업로드 초기화 응답") +@Getter +@Setter +public class TusFileUploadInitVo { + @Schema(description = "파일 마스터 PK", example = "1") + private Long fileMasterIdx; + + @Schema(description = "TUS 업로드 엔드포인트", example = "https://file-alist.pjt.kr/tus/file/") + private String tusEndpoint; + + @Schema(description = "업로드 파일 항목 목록") + private List itemList; +} diff --git a/src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadStatusVo.java b/src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadStatusVo.java new file mode 100644 index 0000000..75450b9 --- /dev/null +++ b/src/main/java/com/alist/api/modules/tusFile/vo/TusFileUploadStatusVo.java @@ -0,0 +1,28 @@ +package com.alist.api.modules.tusFile.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "TUS 업로드 상태 조회 응답") +@Getter +@Setter +public class TusFileUploadStatusVo { + @Schema(description = "파일 UUID", example = "018f6f2d-7b5c-7d49-9c3a-8c0f4f9f9f10") + private String fileUuid; + + @Schema(description = "업로드 상태. PENDING, UPLOADING, DONE, CANCELED", example = "UPLOADING") + private String status; + + @Schema(description = "업로드 완료 byte", example = "51200") + private Long uploadedBytes; + + @Schema(description = "전체 파일 크기 byte", example = "102400") + private Long totalBytes; + + @Schema(description = "업로드 진행률", example = "50") + private Integer percent; + + @Schema(description = "상태 갱신 시각", example = "2026-05-19T10:30:00") + private String updatedAt; +} diff --git a/src/main/java/com/alist/api/modules/tusFile/vo/TusFileViewOrDownloadVo.java b/src/main/java/com/alist/api/modules/tusFile/vo/TusFileViewOrDownloadVo.java new file mode 100644 index 0000000..56b8695 --- /dev/null +++ b/src/main/java/com/alist/api/modules/tusFile/vo/TusFileViewOrDownloadVo.java @@ -0,0 +1,49 @@ +package com.alist.api.modules.tusFile.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "TUS 파일 보기/다운로드 조회 결과") +@Getter +@Setter +public class TusFileViewOrDownloadVo { + @Schema(description = "파일 상세 PK", example = "1") + private Long fileDetailIdx; + + @Schema(description = "파일 마스터 PK", example = "1") + private Long fileMasterIdx; + + @Schema(description = "파일 UUID", example = "018f6f2d-7b5c-7d49-9c3a-8c0f4f9f9f10") + private String fileUuid; + + @Schema(description = "원본 파일명", example = "sample.png") + private String originName; + + @Schema(description = "Content-Type", example = "image/png") + private String contentType; + + @Schema(description = "파일 크기(byte)", example = "102400") + private Long sizeBytes; + + @Schema(hidden = true) + private String savePath; + + @Schema(hidden = true) + private Integer userIdx; + + @Schema(hidden = true) + private Integer userTokenIdx; + + @Schema(hidden = true) + private String eventType; + + @Schema(hidden = true) + private String clientIp; + + @Schema(hidden = true) + private String userAgent; + + @Schema(hidden = true) + private String referer; +} diff --git a/src/main/resources/mapper/tusFile/TusFileMapper.xml b/src/main/resources/mapper/tusFile/TusFileMapper.xml index e66575c..9f3629f 100644 --- a/src/main/resources/mapper/tusFile/TusFileMapper.xml +++ b/src/main/resources/mapper/tusFile/TusFileMapper.xml @@ -2,9 +2,147 @@ + - - /*FileMapper.insertFileMasterInit*/ + + + + + + + + + + + + + + + + + + /*TusFileMapper.insertTusFileUploadInitMaster*/ INSERT INTO ALISTLMS.FILE_MASTER ( USER_IDX, FILE_CATEGORY, FOLDER_PATH, TOTAL_COUNT, DONE_COUNT, FAIL_COUNT, STATUS, DEL_YN, CREATE_MEMBER, UPDATE_MEMBER ) VALUES ( @@ -12,8 +150,8 @@ ) - - /*FileMapper.insertFileDetailInit*/ + + /*TusFileMapper.insertTusFileUploadInitDetail*/ INSERT INTO ALISTLMS.FILE_DETAIL ( FILE_MASTER_IDX, FILE_SEQ, FILE_UUID, ORIGIN_NAME, CONTENT_TYPE, SIZE_BYTES, UPLOADED_BYTES, TUS_UPLOAD_ID, STATUS, DEL_YN, CREATE_MEMBER, UPDATE_MEMBER ) VALUES ( @@ -21,13 +159,12 @@ ) - - /*FileMapper.insertFileUploadEventLog*/ + + /*TusFileMapper.insertTusFileHookEventLog*/ INSERT INTO ALISTLMS.FILE_UPLOAD_EVENT_LOG ( FILE_DETAIL_IDX, EVENT_TYPE, OFFSET_BYTES, BYTES_WRITTEN, HTTP_STATUS, MESSAGE, RAW_JSON ) - SELECT - LFD.FILE_DETAIL_IDX + SELECT LFD.FILE_DETAIL_IDX , #{type} , #{offset} , NULL @@ -38,8 +175,9 @@ WHERE LFD.FILE_UUID = #{fileUuid} LIMIT 1 - - /*FileMapper.insertFileDownloadEventLog*/ + + + /*TusFileMapper.insertTusFileViewOrDownloadEventLog*/ INSERT INTO ALISTLMS.FILE_DOWNLOAD_EVENT_LOG ( FILE_MASTER_IDX , FILE_DETAIL_IDX @@ -67,8 +205,8 @@ ) - - /*FileMapper.updateFileDetailStatus*/ + + /*TusFileMapper.updateTusFileHookDetailStatus*/ UPDATE ALISTLMS.FILE_DETAIL @@ -87,193 +225,56 @@ - /*FileMapper.updateFileMasterAggregateByFileUuid*/ + /*TusFileMapper.updateFileMasterAggregateByFileUuid*/ UPDATE ALISTLMS.FILE_MASTER LFM JOIN ( - SELECT - LLFD.FILE_MASTER_IDX, - COUNT(*) AS total_count, - SUM(CASE WHEN LLFD.STATUS = 3 THEN 1 ELSE 0 END) AS done_count, - SUM(CASE WHEN LLFD.STATUS IN (4,5) THEN 1 ELSE 0 END) AS fail_count - FROM ALISTLMS.FILE_DETAIL LLFD - WHERE LLFD.FILE_MASTER_IDX = ( - SELECT FILE_MASTER_IDX - FROM ALISTLMS.FILE_DETAIL - WHERE FILE_UUID = #{fileUuid} - LIMIT 1 - ) - AND LLFD.DEL_YN = 'N' + SELECT LLFD.FILE_MASTER_IDX + , COUNT(*) AS total_count + , SUM(CASE WHEN LLFD.STATUS = 3 THEN 1 ELSE 0 END) AS done_count + , SUM(CASE WHEN LLFD.STATUS IN (4,5) THEN 1 ELSE 0 END) AS fail_count + FROM ALISTLMS.FILE_DETAIL LLFD + WHERE LLFD.FILE_MASTER_IDX = ( + SELECT FILE_MASTER_IDX + FROM ALISTLMS.FILE_DETAIL + WHERE FILE_UUID = #{fileUuid} + LIMIT 1 + ) + AND LLFD.DEL_YN = 'N' ) A ON A.FILE_MASTER_IDX = LFM.FILE_MASTER_IDX SET LFM.TOTAL_COUNT = A.total_count , LFM.DONE_COUNT = A.done_count , LFM.FAIL_COUNT = A.fail_count , LFM.STATUS = CASE - WHEN A.done_count = A.total_count THEN 3 - WHEN A.done_count = 0 AND A.fail_count = A.total_count THEN 4 - WHEN A.done_count > 0 AND A.fail_count > 0 THEN 5 - ELSE 1 - END + WHEN A.done_count = A.total_count THEN 3 + WHEN A.done_count = 0 AND A.fail_count = A.total_count THEN 4 + WHEN A.done_count > 0 AND A.fail_count > 0 THEN 5 + ELSE 1 + END , LFM.UPDATE_DATE = NOW() - - /*FileMapper.updateTusUploadIdIfNull*/ + + + /*TusFileMapper.updateTusFileUploadIdIfNull*/ UPDATE ALISTLMS.FILE_DETAIL SET TUS_UPLOAD_ID = #{tusUploadId} , UPDATE_DATE = NOW() WHERE FILE_UUID = #{fileUuid} - AND DEL_YN = 'N' - AND TUS_UPLOAD_ID IS NULL + AND DEL_YN = 'N' + AND TUS_UPLOAD_ID IS NULL + - /*FileMapper.updateFileDetailCanceledByFileUuid*/ + /*TusFileMapper.updateFileDetailCanceledByFileUuid*/ UPDATE ALISTLMS.FILE_DETAIL SET STATUS = 5 , UPDATE_DATE = NOW() WHERE FILE_UUID = #{fileUuid} - AND DEL_YN = 'N' - AND STATUS IN (0,1,2) - - - /*FileMapper.updateFileDetailCanceledByFileUuidAndUserTokenIdx*/ - UPDATE ALISTLMS.FILE_DETAIL LFD - INNER JOIN ALISTLMS.test_user_token LTUT - ON LTUT.user_idx = LFD.CREATE_MEMBER - SET LFD.STATUS = 5 - , LFD.UPDATE_DATE = NOW() - WHERE LFD.DEL_YN = 'N' - AND LFD.FILE_UUID = #{fileUuid} - AND LTUT.user_token_idx = #{userTokenIdx} - AND LFD.STATUS IN (0,1,2) + AND DEL_YN = 'N' + AND STATUS IN (0,1,2) - - - - - - - - - - - - - - - - /*FileMapper.updateFileMoveSuccess*/ + + /*TusFileMapper.updateTusFileMoveSuccess*/ UPDATE ALISTLMS.FILE_DETAIL SET SAVE_NAME = #{saveName} , SAVE_PATH = #{savePath} @@ -285,84 +286,71 @@ , MOVE_LOCK_OWNER = NULL , UPDATE_DATE = NOW() WHERE FILE_DETAIL_IDX = #{fileDetailIdx} - AND DEL_YN = 'N' + AND DEL_YN = 'N' - - /*FileMapper.updateFileMovePendingByFileUuid*/ + + /*TusFileMapper.updateTusFileMovePending*/ UPDATE ALISTLMS.FILE_DETAIL SET MOVE_YN = 'N' , MOVE_TRY_COUNT = COALESCE(MOVE_TRY_COUNT, 0) + 1 , MOVE_LAST_ERROR = #{moveLastError} , UPDATE_DATE = NOW() WHERE FILE_UUID = #{fileUuid} - AND DEL_YN = 'N' - AND STATUS = 3 + AND DEL_YN = 'N' + AND STATUS = 3 - - - - - /*FileMapper.updateFileDetailDeleteByFileUuid*/ + + /*TusFileMapper.updateTusFileDetailDelete*/ UPDATE ALISTLMS.FILE_DETAIL LFD - INNER JOIN ALISTLMS.FILE_MASTER LFM - ON LFM.FILE_MASTER_IDX = LFD.FILE_MASTER_IDX - AND LFM.DEL_YN = 'N' - INNER JOIN ALISTLMS.test_user_token LTUT - ON LTUT.user_idx = LFM.USER_IDX + INNER JOIN ALISTLMS.FILE_MASTER LFM ON LFM.FILE_MASTER_IDX = LFD.FILE_MASTER_IDX + AND LFM.DEL_YN = 'N' + INNER JOIN ALISTLMS.test_user_token LTUT ON LTUT.user_idx = LFM.USER_IDX SET LFD.DEL_YN = 'Y' , LFD.UPDATE_DATE = NOW() WHERE LFD.FILE_UUID = #{fileUuid} - AND LTUT.user_token_idx = #{userTokenIdx} - AND LFD.DEL_YN = 'N' - AND LFD.STATUS = 3 - AND LFM.STATUS = 3 + AND LTUT.user_token_idx = #{userTokenIdx} + AND LFD.DEL_YN = 'N' + AND LFD.STATUS = 3 + AND LFM.STATUS = 3 - - /*FileMapper.updateFileMasterDeleteByFileUuid*/ + + /*TusFileMapper.updateTusFileMasterDelete*/ UPDATE ALISTLMS.FILE_MASTER LFM INNER JOIN ( SELECT LFD.FILE_MASTER_IDX FROM ALISTLMS.FILE_DETAIL LFD WHERE LFD.FILE_UUID = #{fileUuid} - AND LFD.DEL_YN = 'Y' + AND LFD.DEL_YN = 'Y' LIMIT 1 - ) TARGET - ON TARGET.FILE_MASTER_IDX = LFM.FILE_MASTER_IDX + ) TARGET ON TARGET.FILE_MASTER_IDX = LFM.FILE_MASTER_IDX INNER JOIN ALISTLMS.test_user_token LTUT - ON LTUT.user_idx = LFM.USER_IDX + ON LTUT.user_idx = LFM.USER_IDX SET LFM.DEL_YN = 'Y' , LFM.UPDATE_DATE = NOW() WHERE LTUT.user_token_idx = #{userTokenIdx} - AND LFM.DEL_YN = 'N' - AND LFM.STATUS = 3 - AND NOT EXISTS ( - SELECT 1 - FROM ALISTLMS.FILE_DETAIL CHK - WHERE CHK.FILE_MASTER_IDX = LFM.FILE_MASTER_IDX - AND CHK.DEL_YN = 'N' - ) + AND LFM.DEL_YN = 'N' + AND LFM.STATUS = 3 + AND NOT EXISTS ( + SELECT 1 + FROM ALISTLMS.FILE_DETAIL CHK + WHERE CHK.FILE_MASTER_IDX = LFM.FILE_MASTER_IDX + AND CHK.DEL_YN = 'N' + ) + + + + /*TusFileMapper.updateTusFileUploadCancel*/ + UPDATE ALISTLMS.FILE_DETAIL LFD + INNER JOIN ALISTLMS.test_user_token LTUT ON LTUT.user_idx = LFD.CREATE_MEMBER + SET LFD.STATUS = 5 + , LFD.UPDATE_DATE = NOW() + WHERE LFD.DEL_YN = 'N' + AND LFD.FILE_UUID = #{fileUuid} + AND LTUT.user_token_idx = #{userTokenIdx} + AND LFD.STATUS IN (0,1,2) \ No newline at end of file