Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
692e20c
[feat] #118 Add port request table
kwdahun Aug 31, 2025
47c98d8
Update src/main/java/DGU_AI_LAB/admin_be/domain/portRequests/entity/P…
kwdahun Aug 31, 2025
904181e
[fix] #118 Add Id notation in PortRequests.java
kwdahun Aug 31, 2025
3b7ed79
[fix] #118 Add port range validation
kwdahun Sep 1, 2025
276c71d
[fix] #noissue 실수로 빠진 getAllRequests 추가
saokiritoni Sep 2, 2025
abc1158
[feat] #115 우분투 계정 삭제 API, 인프라 API 연결
saokiritoni Sep 2, 2025
74d679a
[refactor] #115 Admin Service 와 분리
saokiritoni Sep 2, 2025
0d76f3e
[feat] #115 계정 삭제 에러코드 추가
saokiritoni Sep 2, 2025
a446b24
[fix] #115 Config Server의 API 주소 수정
saokiritoni Sep 2, 2025
e032fcd
[feat] #115 Request 승인 시 우분투 사용자 생성 API 연결
saokiritoni Sep 2, 2025
51c8786
[feat] #115 우분투 유저 그룹 업데이트 dto 생성
saokiritoni Sep 2, 2025
cb05f58
Merge pull request #120 from CSID-DGU/feat/#115-pvc
saokiritoni Sep 2, 2025
1f436f4
[fix] #118 Modify id column name
kwdahun Sep 2, 2025
edbb3f0
Merge pull request #119 from CSID-DGU/feat/#118-add-port-request-table
saokiritoni Sep 2, 2025
52f00c8
[fix] Modify unique constraints from PortRequests entity
kwdahun Sep 2, 2025
61715ad
Merge pull request #122 from CSID-DGU/feat/#118-add-port-request-table
saokiritoni Sep 2, 2025
7b86430
[fix] #121 http method를 delete에서 post로 수정
saokiritoni Sep 2, 2025
b911d38
[feat] #121 Request를 soft delete하기 위해 Status에 DELETED 추가
saokiritoni Sep 2, 2025
5d325fd
[feat] #121 Request를 soft delete하는 비즈니스 메서드를 엔티티에 추가
saokiritoni Sep 2, 2025
e600bbe
[feat] #121 우분투 계정 삭제 시 Request를 soft delete하도록 수정
saokiritoni Sep 2, 2025
86cc205
[feat] #121 유저 탈퇴 soft delete로 수정
saokiritoni Sep 2, 2025
9192727
[docs] #121 개발한 내용대로 주석 수정
saokiritoni Sep 2, 2025
15edbf3
[chore] #121 관리자용 유저 정보 수정 API 삭제
saokiritoni Sep 2, 2025
e308a94
[docs] #121 관리자용 유저 API 명세서 작성
saokiritoni Sep 2, 2025
a9c9321
[docs] #121 유저용 유저 API 명세서 작성
saokiritoni Sep 2, 2025
dd9b8b6
[docs] #121 유저용 유저 API 명세서 작성
saokiritoni Sep 2, 2025
f077ed4
[docs] #121 분류 제목 수정
saokiritoni Sep 2, 2025
1eeebdc
[docs] #121 분류 제목 번호 붙이기
saokiritoni Sep 2, 2025
2568835
[docs] #121 관리자용 사용 신청 API 명세 작성 & DTO validation 추가
saokiritoni Sep 2, 2025
d7afad0
[docs] #121 Config Server용 API 명세서 작성
saokiritoni Sep 2, 2025
e1c2327
[docs] #121 제목 번호 수정
saokiritoni Sep 2, 2025
912f031
[docs] #121 그룹 API 명세서 작성
saokiritoni Sep 2, 2025
47e1de0
Merge pull request #123 from CSID-DGU/feat/#121-ubuntu
saokiritoni Sep 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import jakarta.validation.Valid;
import org.springframework.http.ResponseEntity;

@Tag(name = "Slack 및 E-mail", description = "Slack 및 Email 알림 API")
@Tag(name = "0. Slack 및 E-mail", description = "Slack 및 Email 알림 API")
public interface AlarmApi {

@Operation(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@

import java.util.List;

@Tag(name = "이미지 관리", description = "컨테이너 이미지 생성 및 조회 API")
@Tag(name = "2. 이미지 관리", description = "컨테이너 이미지 조회 API")
public interface ContainerImageApi {

@Operation(summary = "이미지 생성", description = "새로운 컨테이너 이미지를 등록합니다.")
@Operation(summary = "이미지 생성", description = "새로운 컨테이너 이미지를 등록합니다. -> 관리자용으로 만들어졌으니, 수정 필요합니다. ")
@ApiResponse(responseCode = "200", description = "이미지 생성 성공",
content = @Content(schema = @Schema(implementation = ContainerImageResponseDTO.class)))
ResponseEntity<ContainerImageResponseDTO> createImage(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import org.springframework.web.bind.annotation.RequestParam;


@Tag(name = "사용자 대시보드 API", description = "대시보드용 API")
@Tag(name = "2. 사용자 대시보드 API", description = "대시보드용 API")
@RequestMapping("/api/dashboard")
public interface DashBoardApi {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package DGU_AI_LAB.admin_be.domain.groups.controller;

import DGU_AI_LAB.admin_be.domain.groups.controller.docs.GroupApi;
import DGU_AI_LAB.admin_be.domain.groups.dto.request.CreateGroupRequestDTO;
import DGU_AI_LAB.admin_be.domain.groups.dto.response.GroupResponseDTO;
import DGU_AI_LAB.admin_be.domain.groups.service.GroupService;
Expand All @@ -16,7 +17,7 @@
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/groups")
public class GroupController {
public class GroupController implements GroupApi {

private final GroupService groupService;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package DGU_AI_LAB.admin_be.domain.groups.controller.docs;

import DGU_AI_LAB.admin_be.domain.groups.dto.request.CreateGroupRequestDTO;
import DGU_AI_LAB.admin_be.global.common.SuccessResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@Tag(name = "2. 그룹 API", description = "사용자용 그룹 목록 조회, 그룹 생성 API")
public interface GroupApi {

@Operation(summary = "모든 그룹 목록 조회", description = "시스템에 등록된 모든 그룹의 정보를 조회합니다. 사용 신청 단계에서 이 목록을 조회하고, 그룹을 선택할 수 있습니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "성공"),
@ApiResponse(responseCode = "404", description = "조회된 그룹 정보 없음")
})
@GetMapping
ResponseEntity<SuccessResponse<?>> getGroups();

@Operation(summary = "새로운 그룹 생성", description = "새로운 그룹을 생성하고 DB에 저장합니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "201", description = "생성 성공"),
@ApiResponse(responseCode = "400", description = "잘못된 요청 (필수 필드 누락 등)"),
@ApiResponse(responseCode = "409", description = "중복된 GID를 가진 그룹이 이미 존재함")
})
@PostMapping
ResponseEntity<SuccessResponse<?>> createGroup(@RequestBody @Valid CreateGroupRequestDTO dto);
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
package DGU_AI_LAB.admin_be.domain.groups.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import lombok.Builder;

@Schema(description = "1. 그룹 생성 요청 DTO")
@Builder
public record CreateGroupRequestDTO(
@Schema(description = "할당할 우분투 GID (Group ID)", example = "1001")
@NotNull @Positive
Long ubuntuGid,

@Schema(description = "생성할 그룹명", example = "developers")
@NotBlank
String groupName
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

import java.util.List;

@Tag(name = "Kubernetes Pods", description = "쿠버네티스 Pod 조회 API")
@Tag(name = "0. Kubernetes Pods", description = "쿠버네티스 Pod 조회 API")
public interface PodApi {

@Operation(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package DGU_AI_LAB.admin_be.domain.portRequests.entity;

import DGU_AI_LAB.admin_be.domain.requests.entity.Request;
import DGU_AI_LAB.admin_be.domain.resourceGroups.entity.ResourceGroup;
import DGU_AI_LAB.admin_be.global.common.BaseTimeEntity;
import jakarta.persistence.*;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import lombok.*;

@Entity
@Table(name = "port_requests", uniqueConstraints = {
@UniqueConstraint(columnNames = {"port_number", "rsgroup_id"})
})
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Builder
public class PortRequests extends BaseTimeEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "port_request_id")
private Long portRequestId;

@Column(name = "port_number", nullable = false)
@Min(1)
@Max(65535)
private Integer portNumber;

@Column(name = "usage_purpose", nullable = false, length = 1000)
private String usagePurpose;

@Column(name = "is_active", nullable = false)
@Builder.Default
private Boolean isActive = true;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "request_id", nullable = false)
private Request request;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "rsgroup_id", nullable = false)
private ResourceGroup resourceGroup;

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package DGU_AI_LAB.admin_be.domain.requests.controller;

import DGU_AI_LAB.admin_be.domain.requests.controller.docs.AdminRequestApi;
import DGU_AI_LAB.admin_be.domain.requests.dto.request.ApproveModificationDTO;
import DGU_AI_LAB.admin_be.domain.requests.dto.request.ApproveRequestDTO;
import DGU_AI_LAB.admin_be.domain.requests.dto.request.RejectRequestDTO;
Expand All @@ -20,7 +21,7 @@
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/admin/requests")
public class AdminRequestController {
public class AdminRequestController implements AdminRequestApi {

private final AdminRequestCommandService adminRequestCommandService;
private final AdminRequestQueryService adminRequestQueryService;
Expand All @@ -44,6 +45,15 @@ public ResponseEntity<List<ContainerInfoDTO>> getAllActiveContainers() {
return ResponseEntity.ok(adminRequestQueryService.getAllActiveContainers());
}

/**
* 모든 요청 목록 조회 (관리자용)
* 모든 상태의 Request 목록을 반환합니다.
*/
@GetMapping
public ResponseEntity<List<SaveRequestResponseDTO>> getAllRequests() {
return ResponseEntity.ok(adminRequestQueryService.getAllRequests());
}

/**
* 신규 신청 목록 조회 (관리자용)
* PENDING 상태의 Request 목록을 반환합니다.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package DGU_AI_LAB.admin_be.domain.requests.controller;

import DGU_AI_LAB.admin_be.domain.requests.controller.docs.ConfigRequestApi;
import DGU_AI_LAB.admin_be.domain.requests.dto.response.AcceptInfoResponseDTO;
import DGU_AI_LAB.admin_be.domain.requests.service.ConfigRequestService;
import lombok.RequiredArgsConstructor;
Expand All @@ -9,7 +10,7 @@
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/requests/config")
public class ConfigRequestController {
public class ConfigRequestController implements ConfigRequestApi {

private final ConfigRequestService configRequestService;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.http.ResponseEntity;

@Tag(name = "Config Server용 승인 정보 관리", description = "Ubuntu username별 승인 정보 조회 API")
@Tag(name = "0. Config Server용 승인 정보 관리", description = "Ubuntu username별 승인 정보 조회 API")
public interface AcceptInfoApi {

@Operation(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,58 +1,74 @@
package DGU_AI_LAB.admin_be.domain.requests.controller.docs;

import DGU_AI_LAB.admin_be.domain.requests.dto.request.ApproveModificationDTO;
import DGU_AI_LAB.admin_be.domain.requests.dto.request.ApproveRequestDTO;
import DGU_AI_LAB.admin_be.domain.requests.dto.request.RejectRequestDTO;
import DGU_AI_LAB.admin_be.domain.requests.dto.response.ChangeRequestResponseDTO;
import DGU_AI_LAB.admin_be.domain.requests.dto.response.ContainerInfoDTO;
import DGU_AI_LAB.admin_be.domain.requests.dto.response.ResourceUsageDTO;
import DGU_AI_LAB.admin_be.domain.requests.dto.response.SaveRequestResponseDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@Tag(name = "관리자용 서버 사용 신청 관리", description = "관리자용 서버 사용 신청 관리 API")
@Tag(name = "1. 관리자 서버 사용 신청 처리", description = "관리자용 서버 사용 신청 관리 API")
public interface AdminRequestApi {

@Operation(
summary = "모든 신청 목록 조회",
description = "상태에 상관없이 모든 사용 신청을 조회합니다."
)
@ApiResponse(
responseCode = "200", description = "조회 성공",
content = @Content(array = @ArraySchema(schema = @Schema(implementation = SaveRequestResponseDTO.class)))
)
@Operation(summary = "변경 요청 승인", description = "사용자의 변경 요청을 승인합니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "성공"),
@ApiResponse(responseCode = "404", description = "변경 요청을 찾을 수 없음"),
@ApiResponse(responseCode = "409", description = "요청 상태가 PENDING이 아님")
})
@PatchMapping("/change/approve")
ResponseEntity<Void> approveModification(
@AuthenticationPrincipal(expression = "userId") Long adminId,
@RequestBody @Valid ApproveModificationDTO dto
);

@Operation(summary = "모든 리소스 사용량 조회", description = "현재 사용 중인 컨테이너들의 리소스 사용량을 조회합니다.")
@GetMapping("/usage")
ResponseEntity<List<ResourceUsageDTO>> getAllResourceUsage();

@Operation(summary = "모든 컨테이너 정보 조회", description = "현재 활성화된 모든 컨테이너의 상세 정보를 조회합니다.")
@GetMapping("/containers")
ResponseEntity<List<ContainerInfoDTO>> getAllActiveContainers();

@Operation(summary = "모든 요청 목록 조회", description = "모든 상태의 사용자 요청 목록을 조회합니다.")
@GetMapping
ResponseEntity<List<SaveRequestResponseDTO>> getAllRequests();

@Operation(
summary = "신청 승인",
description = "해당 신청의 상태를 FULFILLED로 변경하고 approvedAt을 현재 시각으로 설정합니다. expiresAt, volumeSizeGiB, imageId, rsgroupId를 갱신합니다."
)
@ApiResponse(
responseCode = "200", description = "승인 성공",
content = @Content(schema = @Schema(implementation = SaveRequestResponseDTO.class))
)
ResponseEntity<SaveRequestResponseDTO> approve(
@io.swagger.v3.oas.annotations.parameters.RequestBody(
description = "승인 DTO", required = true
)
ApproveRequestDTO dto
);
@Operation(summary = "신규 신청 목록 조회", description = "PENDING 상태의 신규 사용자 신청 목록을 조회합니다.")
@GetMapping("/new")
ResponseEntity<List<SaveRequestResponseDTO>> getNewRequests();

@Operation(
summary = "신청 거절",
description = "해당 신청의 상태를 DENIED로 변경하고 관리 코멘트를 기록합니다."
)
@ApiResponse(
responseCode = "200", description = "거절 성공",
content = @Content(schema = @Schema(implementation = SaveRequestResponseDTO.class))
)
ResponseEntity<SaveRequestResponseDTO> reject(
@io.swagger.v3.oas.annotations.parameters.RequestBody(
description = "거절 DTO", required = true
)
RejectRequestDTO dto
);
@Operation(summary = "변경 요청 목록 조회", description = "PENDING 상태의 사용자 변경 요청 목록을 조회합니다.")
@GetMapping("/change")
ResponseEntity<List<ChangeRequestResponseDTO>> getChangeRequests();

@Operation(summary = "신규 신청 승인", description = "신규 사용자 신청을 승인하고, 계정 및 리소스를 할당합니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "성공"),
@ApiResponse(responseCode = "404", description = "요청을 찾을 수 없음"),
@ApiResponse(responseCode = "409", description = "요청 상태가 PENDING이 아님"),
@ApiResponse(responseCode = "502", description = "외부 서버 오류")
})
@PatchMapping("/approval")
ResponseEntity<SaveRequestResponseDTO> approve(@RequestBody @Valid ApproveRequestDTO dto);

@Operation(summary = "신규 신청 거절", description = "신규 사용자 신청을 거절합니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "성공"),
@ApiResponse(responseCode = "404", description = "요청을 찾을 수 없음"),
@ApiResponse(responseCode = "409", description = "요청 상태가 PENDING 또는 FULFILLED가 아님")
})
@PatchMapping("/reject")
ResponseEntity<SaveRequestResponseDTO> reject(@RequestBody @Valid RejectRequestDTO dto);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package DGU_AI_LAB.admin_be.domain.requests.controller.docs;

import DGU_AI_LAB.admin_be.domain.requests.dto.response.AcceptInfoResponseDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Tag(name = "0. Config Server용 API", description = "내부 Config Server 연동을 위한 API")
@RequestMapping("/api/requests/config")
public interface ConfigRequestApi {

@Operation(summary = "우분투 계정명 사용 가능 여부 확인", description = "지정된 우분투 계정명이 사용 가능한지 확인합니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "성공",
content = @io.swagger.v3.oas.annotations.media.Content(
mediaType = "application/json",
examples = {
@io.swagger.v3.oas.annotations.media.ExampleObject(
name = "사용 가능",
value = "{\"available\": true}"
),
@io.swagger.v3.oas.annotations.media.ExampleObject(
name = "사용 불가능",
value = "{\"available\": false}"
)
}
)
)
})
@GetMapping("/check-username")
ResponseEntity<?> checkUbuntuUsername(
@RequestParam @Parameter(description = "확인할 우분투 계정명", example = "toni") String username
);

@Operation(summary = "요청 승인 정보 조회", description = "지정된 우분투 계정명에 대한 승인된 요청 정보를 조회합니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "성공"),
@ApiResponse(responseCode = "404", description = "승인된 요청을 찾을 수 없음")
})
@GetMapping("/{username}")
ResponseEntity<AcceptInfoResponseDTO> getAcceptInfo(
@PathVariable @Parameter(description = "승인 정보 조회 대상 계정명", example = "toni") String username
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

import java.util.List;

@Tag(name = "서버 사용 신청", description = "서버 사용 신청 API")
@Tag(name = "2. 서버 사용 신청", description = "서버 사용 신청 API")
public interface RequestApi {

@Operation(
Expand Down
Loading