Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion src/main/java/redot/redot_server/RedotServerApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import redot.redot_server.domain.redot.consultation.config.ConsultationNotificationProperties;
import redot.redot_server.global.email.EmailVerificationProperties;
import redot.redot_server.global.s3.config.ImageUploadProperties;
import redot.redot_server.global.security.social.config.AuthRedirectProperties;

@SpringBootApplication
@EnableConfigurationProperties({AuthRedirectProperties.class, EmailVerificationProperties.class, ImageUploadProperties.class})
@EnableConfigurationProperties({AuthRedirectProperties.class, EmailVerificationProperties.class, ImageUploadProperties.class, ConsultationNotificationProperties.class})
public class RedotServerApplication {
public static void main(String[] args) {
SpringApplication.run(RedotServerApplication.class, args);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import redot.redot_server.domain.admin.controller.docs.AdminConsultationControllerDocs;
import redot.redot_server.domain.admin.dto.ConsultationSearchCondition;
import redot.redot_server.domain.admin.dto.request.ConsultationUpdateRequest;
import redot.redot_server.domain.admin.service.AdminConsultationService;
Expand All @@ -21,17 +22,19 @@
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/redot/admin/consultations")
public class AdminConsultationController {
public class AdminConsultationController implements AdminConsultationControllerDocs {

private final AdminConsultationService adminConsultationService;

@GetMapping("/{consultationId}")
@Override
public ResponseEntity<ConsultationResponse> getConsultationInfo(
@PathVariable("consultationId") Long consultationId) {
return ResponseEntity.ok(adminConsultationService.getConsultationInfo(consultationId));
}

@GetMapping
@Override
public ResponseEntity<PageResponse<ConsultationResponse>> getAllConsultations(
ConsultationSearchCondition searchCondition,
@PageableDefault(sort = "createdAt", direction = Sort.Direction.DESC) Pageable pageable) {
Expand All @@ -41,6 +44,7 @@ public ResponseEntity<PageResponse<ConsultationResponse>> getAllConsultations(
}

@PutMapping("/{consultationId}")
@Override
public ResponseEntity<ConsultationResponse> updateConsultationInfo(
@RequestBody @Valid ConsultationUpdateRequest request,
@PathVariable("consultationId") Long consultationId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import redot.redot_server.domain.admin.controller.docs.AdminControllerDocs;
import redot.redot_server.domain.admin.dto.request.AdminCreateRequest;
import redot.redot_server.domain.admin.dto.request.AdminResetPasswordRequest;
import redot.redot_server.domain.admin.dto.response.AdminResponse;
Expand All @@ -33,45 +34,52 @@
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/redot/admin")
public class AdminController {
public class AdminController implements AdminControllerDocs {

private final AdminService adminService;
private final TokenCookieFactory tokenCookieFactory;

@GetMapping("/{adminId}")
@Override
public ResponseEntity<AdminResponse> getAdminInfo(@PathVariable("adminId") Long adminId) {
return ResponseEntity.ok(adminService.getAdminInfo(adminId));
}

@PostMapping
@Override
public ResponseEntity<AdminResponse> createAdmin(@Valid @RequestBody AdminCreateRequest request) {
return ResponseEntity.ok(adminService.createAdmin(request));
}

@GetMapping
@Override
public ResponseEntity<PageResponse<AdminResponse>> getAdminInfoList(Pageable pageable) {
return ResponseEntity.ok(adminService.getAdminInfoList(pageable));
}

@PutMapping("/{adminId}")
@Override
public ResponseEntity<AdminResponse> updateAdmin(@PathVariable("adminId") Long adminId, @Valid @RequestBody AdminUpdateRequest request) {
return ResponseEntity.ok(adminService.updateAdmin(adminId, request));
}

@PostMapping("/{adminId}/reset-password")
@Override
public ResponseEntity<Void> resetAdminPassword(@PathVariable("adminId") Long adminId, @Valid @RequestBody AdminResetPasswordRequest request) {
adminService.resetAdminPassword(adminId, request);
return ResponseEntity.noContent().build();
}

@DeleteMapping("/{adminId}")
@Override
public ResponseEntity<Void> deleteAdmin(@PathVariable("adminId") Long adminId, @AuthenticationPrincipal
JwtPrincipal jwtPrincipal) {
adminService.deleteAdmin(jwtPrincipal.id(), adminId);
return ResponseEntity.noContent().build();
}

@DeleteMapping
@Override
public ResponseEntity<Void> deleteCurrentAdmin(HttpServletRequest request, @AuthenticationPrincipal JwtPrincipal jwtPrincipal) {
adminService.deleteCurrentAdmin(jwtPrincipal.id());
ResponseCookie deleteAccess = tokenCookieFactory.deleteAccessTokenCookie(request, TokenType.ADMIN.getAccessCookieName());
Expand All @@ -84,6 +92,7 @@ public ResponseEntity<Void> deleteCurrentAdmin(HttpServletRequest request, @Auth
}

@PostMapping("/upload-profile-image")
@Override
public ResponseEntity<UploadedImageUrlResponse> uploadProfileImage(
@AuthenticationPrincipal JwtPrincipal jwtPrincipal,
@RequestPart("image") @NotNull MultipartFile image
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,20 @@
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import redot.redot_server.domain.admin.controller.docs.AdminRedotAppControllerDocs;
import redot.redot_server.domain.admin.service.AdminRedotAppService;
import redot.redot_server.domain.redot.app.dto.request.RedotAppCreateRequest;
import redot.redot_server.domain.redot.app.dto.response.RedotAppInfoResponse;

@RestController
@RequestMapping("/api/v1/redot/admin/app")
@RequiredArgsConstructor
public class AdminRedotAppController {
public class AdminRedotAppController implements AdminRedotAppControllerDocs {

private final AdminRedotAppService redotAppService;

@PostMapping
@Override
public ResponseEntity<RedotAppInfoResponse> createRedotApp(@Valid @RequestBody RedotAppCreateRequest request) {
return ResponseEntity.ok(redotAppService.createRedotApp(request));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package redot.redot_server.domain.admin.controller.docs;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springdoc.core.annotations.ParameterObject;
import redot.redot_server.domain.admin.dto.ConsultationSearchCondition;
import redot.redot_server.domain.admin.dto.request.ConsultationUpdateRequest;
import redot.redot_server.domain.redot.consultation.dto.response.ConsultationResponse;
import redot.redot_server.global.util.dto.response.PageResponse;

@Tag(name = "Admin Consultation", description = "관리자 상담 관리 API")
public interface AdminConsultationControllerDocs {

@Operation(summary = "상담 단건 조회", description = "상담 ID로 상담 정보를 조회합니다.")
@ApiResponse(responseCode = "200", description = "조회 성공",
content = @Content(schema = @Schema(implementation = ConsultationResponse.class)))
ResponseEntity<ConsultationResponse> getConsultationInfo(@Parameter(description = "상담 ID", example = "1") Long consultationId);

@Operation(summary = "상담 목록 조회", description = "`email`, `phone`, `status`, `type`, `currentWebsiteUrl` 검색 조건을 조합해 조회하며 `sort=createdAt,desc` 와 같은 Pageable 쿼리 파라미터를 사용합니다.")
@ApiResponse(responseCode = "200", description = "조회 성공",
content = @Content(schema = @Schema(implementation = PageResponse.class)))
ResponseEntity<PageResponse<ConsultationResponse>> getAllConsultations(
@ParameterObject ConsultationSearchCondition searchCondition,
@Parameter(description = "기본 정렬은 createdAt DESC 입니다.") @ParameterObject Pageable pageable);

@Operation(summary = "상담 정보 수정", description = "상담 상태나 담당자 정보를 수정합니다.")
@ApiResponse(responseCode = "200", description = "수정 성공",
content = @Content(schema = @Schema(implementation = ConsultationResponse.class)))
ResponseEntity<ConsultationResponse> updateConsultationInfo(ConsultationUpdateRequest request,
@Parameter(description = "상담 ID", example = "1") Long consultationId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package redot.redot_server.domain.admin.controller.docs;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.web.multipart.MultipartFile;
import org.springdoc.core.annotations.ParameterObject;
import redot.redot_server.domain.admin.dto.request.AdminCreateRequest;
import redot.redot_server.domain.admin.dto.request.AdminResetPasswordRequest;
import redot.redot_server.domain.admin.dto.request.AdminUpdateRequest;
import redot.redot_server.domain.admin.dto.response.AdminResponse;
import redot.redot_server.global.s3.dto.UploadedImageUrlResponse;
import redot.redot_server.global.security.principal.JwtPrincipal;
import redot.redot_server.global.util.dto.response.PageResponse;

@Tag(name = "Admin", description = "Redot 관리자 API")
public interface AdminControllerDocs {

@Operation(summary = "관리자 단건 조회", description = "지정한 ID의 관리자 정보를 조회합니다.")
@ApiResponse(responseCode = "200", description = "조회 성공",
content = @Content(schema = @Schema(implementation = AdminResponse.class)))
ResponseEntity<AdminResponse> getAdminInfo(@Parameter(description = "관리자 ID", example = "1") Long adminId);

@Operation(summary = "관리자 생성", description = "새로운 관리자를 생성합니다.")
@ApiResponse(responseCode = "200", description = "생성 성공",
content = @Content(schema = @Schema(implementation = AdminResponse.class)))
ResponseEntity<AdminResponse> createAdmin(AdminCreateRequest request);

@Operation(summary = "관리자 목록 조회", description = "`page`, `size`, `sort` 파라미터를 사용해 페이징/정렬하며 `sort=필드명,정렬방향` (예: `sort=createdAt,desc`) 형식을 따릅니다.")
@ApiResponse(responseCode = "200", description = "조회 성공",
content = @Content(schema = @Schema(implementation = PageResponse.class)))
ResponseEntity<PageResponse<AdminResponse>> getAdminInfoList(@Parameter(description = "기본적으로 page=0, size=20이며 sort 파라미터는 `createdAt,desc`와 같이 전달합니다.")
@ParameterObject Pageable pageable);

@Operation(summary = "관리자 정보 수정", description = "지정된 관리자의 프로필 정보를 수정합니다.")
@ApiResponse(responseCode = "200", description = "수정 성공",
content = @Content(schema = @Schema(implementation = AdminResponse.class)))
ResponseEntity<AdminResponse> updateAdmin(@Parameter(description = "관리자 ID", example = "1") Long adminId,
AdminUpdateRequest request);

@Operation(summary = "관리자 비밀번호 초기화", description = "관리자의 비밀번호를 재설정합니다.")
@ApiResponse(responseCode = "204", description = "재설정 완료")
ResponseEntity<Void> resetAdminPassword(@Parameter(description = "관리자 ID", example = "1") Long adminId,
AdminResetPasswordRequest request);

@Operation(summary = "다른 관리자 삭제", description = "현재 로그인한 관리자가 다른 관리자를 삭제합니다.")
@ApiResponse(responseCode = "204", description = "삭제 완료")
ResponseEntity<Void> deleteAdmin(@Parameter(description = "삭제 대상 관리자 ID", example = "2") Long adminId,
@Parameter(hidden = true) JwtPrincipal jwtPrincipal);

@Operation(summary = "현재 관리자 탈퇴", description = "본인의 계정을 삭제하고 토큰 쿠키를 제거합니다.")
@ApiResponse(responseCode = "204", description = "탈퇴 완료")
ResponseEntity<Void> deleteCurrentAdmin(@Parameter(hidden = true) HttpServletRequest request,
@Parameter(hidden = true) JwtPrincipal jwtPrincipal);

@Operation(summary = "관리자 프로필 이미지 업로드", description = "관리자 프로필 이미지를 업로드하고 경로를 반환합니다.")
@ApiResponse(responseCode = "200", description = "업로드 성공",
content = @Content(schema = @Schema(implementation = UploadedImageUrlResponse.class)))
ResponseEntity<UploadedImageUrlResponse> uploadProfileImage(@Parameter(hidden = true) JwtPrincipal jwtPrincipal,
MultipartFile image);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package redot.redot_server.domain.admin.controller.docs;

import io.swagger.v3.oas.annotations.Operation;
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.tags.Tag;
import org.springframework.http.ResponseEntity;
import redot.redot_server.domain.redot.app.dto.request.RedotAppCreateRequest;
import redot.redot_server.domain.redot.app.dto.response.RedotAppInfoResponse;

@Tag(name = "Admin Redot App", description = "관리자 앱 관리 API")
public interface AdminRedotAppControllerDocs {

@Operation(summary = "Redot 앱 생성", description = "관리자 권한으로 신규 Redot 앱을 생성합니다.")
@ApiResponse(responseCode = "200", description = "생성 성공",
content = @Content(schema = @Schema(implementation = RedotAppInfoResponse.class)))
ResponseEntity<RedotAppInfoResponse> createRedotApp(RedotAppCreateRequest request);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import redot.redot_server.domain.auth.controller.docs.AdminAuthControllerDocs;
import redot.redot_server.domain.auth.dto.request.PasswordResetConfirmRequest;
import redot.redot_server.domain.auth.dto.request.SignInRequest;
import redot.redot_server.domain.auth.dto.response.AuthResult;
Expand All @@ -27,13 +28,14 @@
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/auth/redot/admin")
public class AdminAuthController {
public class AdminAuthController implements AdminAuthControllerDocs {

private final AdminAuthService adminAuthService;
private final AdminService adminService;
private final TokenCookieFactory tokenCookieFactory;

@PostMapping("/sign-in")
@Override
public ResponseEntity<TokenResponse> signIn(HttpServletRequest request, @Valid @RequestBody SignInRequest signInRequest) {
AuthResult authResult = adminAuthService.signIn(request, signInRequest);
return ResponseEntity.ok()
Expand All @@ -43,11 +45,13 @@ public ResponseEntity<TokenResponse> signIn(HttpServletRequest request, @Valid @
}

@PostMapping("/sign-up")
@Override
public ResponseEntity<AdminResponse> createAdmin(@Valid @RequestBody AdminCreateRequest request) {
return ResponseEntity.ok(adminService.createAdmin(request));
}

@PostMapping("/reissue" )
@Override
public ResponseEntity<TokenResponse> refreshToken(HttpServletRequest request) {
AuthResult authResult = adminAuthService.reissueToken(request);

Expand All @@ -58,12 +62,14 @@ public ResponseEntity<TokenResponse> refreshToken(HttpServletRequest request) {
}

@GetMapping("/me")
@Override
public ResponseEntity<AdminResponse> getCurrentAdminInfo(@AuthenticationPrincipal JwtPrincipal jwtPrincipal) {
AdminResponse adminResponse = adminAuthService.getCurrentAdminInfo(jwtPrincipal.id());
return ResponseEntity.ok(adminResponse);
}

@PostMapping("/sign-out")
@Override
public ResponseEntity<Void> signOut(HttpServletRequest request) {
ResponseCookie deleteAccess = tokenCookieFactory.deleteAccessTokenCookie(request, TokenType.ADMIN.getAccessCookieName());
ResponseCookie deleteRefresh = tokenCookieFactory.deleteRefreshTokenCookie(request, TokenType.ADMIN.getRefreshCookieName());
Expand All @@ -75,6 +81,7 @@ public ResponseEntity<Void> signOut(HttpServletRequest request) {
}

@PostMapping("/password-reset")
@Override
public ResponseEntity<Void> confirmPasswordReset(@RequestBody @Valid PasswordResetConfirmRequest request) {
adminAuthService.resetPassword(request);
return ResponseEntity.noContent().build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import redot.redot_server.domain.auth.controller.docs.AdminImpersonationControllerDocs;
import redot.redot_server.domain.auth.dto.response.AuthResult;
import redot.redot_server.domain.auth.dto.request.CMSAdminImpersonationRequest;
import redot.redot_server.domain.auth.dto.response.TokenResponse;
Expand All @@ -19,10 +20,11 @@
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/auth/redot/admin/impersonation")
public class AdminImpersonationController {
public class AdminImpersonationController implements AdminImpersonationControllerDocs {
private final AdminImpersonationService adminImpersonationService;

@PostMapping("/cms-admin")
@Override
public ResponseEntity<TokenResponse> impersonateAsCMSAdmin(HttpServletRequest request, @Valid @RequestBody CMSAdminImpersonationRequest cmsAdminImpersonationRequest, @AuthenticationPrincipal JwtPrincipal jwtPrincipal) {
Long adminId = jwtPrincipal.id();

Expand Down
Loading