- GET /api/auth/profile: 아바타·포인트 포함 전체 프로필(재로그인 없이 최신화) - 자기 글/댓글 추천·비추천 차단(400) - 인기 글: 등록 1일 이내 + 추천 50건 이상이면 목록 상단 최대 3건 노출(hot) - 인기 댓글: 동일 조건으로 댓글 목록 상단 정렬(hot) - PostSummary 에 downCount 추가(비추천 20+ 블라인드 판정용) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
130 lines
5.4 KiB
Java
130 lines
5.4 KiB
Java
package com.sb.web.auth.controller;
|
|
|
|
import com.sb.web.auth.dto.LoginRequest;
|
|
import com.sb.web.auth.dto.LoginResponse;
|
|
import com.sb.web.auth.dto.MemberResponse;
|
|
import com.sb.web.auth.dto.PasswordChangeRequest;
|
|
import com.sb.web.auth.dto.SessionUser;
|
|
import com.sb.web.auth.dto.SignupRequest;
|
|
import com.sb.web.auth.service.AuthService;
|
|
import com.sb.web.auth.web.AuthInterceptor;
|
|
import jakarta.servlet.http.HttpServletRequest;
|
|
import jakarta.validation.Valid;
|
|
import lombok.RequiredArgsConstructor;
|
|
import org.springframework.http.HttpStatus;
|
|
import org.springframework.http.ResponseEntity;
|
|
import org.springframework.web.bind.annotation.*;
|
|
|
|
/**
|
|
* 인증 API.
|
|
* POST /api/auth/signup 회원가입 (비보호)
|
|
* POST /api/auth/login 로그인 (비보호)
|
|
* POST /api/auth/logout 로그아웃 (보호)
|
|
* GET /api/auth/me 내 정보 (보호)
|
|
*/
|
|
@RestController
|
|
@RequestMapping("/api/auth")
|
|
@RequiredArgsConstructor
|
|
public class AuthController {
|
|
|
|
private final AuthService authService;
|
|
private final com.sb.web.admin.service.AppSettingService appSettingService;
|
|
private final com.sb.web.point.PointService pointService;
|
|
|
|
/** 회원가입 허용 여부 (비보호) — 프론트가 가입 진입 전에 차단 표시용 */
|
|
@GetMapping("/signup-enabled")
|
|
public java.util.Map<String, Boolean> signupEnabled() {
|
|
return java.util.Map.of("enabled", appSettingService.isSignupEnabled());
|
|
}
|
|
|
|
@PostMapping("/signup")
|
|
public ResponseEntity<MemberResponse> signup(@Valid @RequestBody SignupRequest req,
|
|
HttpServletRequest request) {
|
|
return ResponseEntity.status(HttpStatus.CREATED).body(authService.signup(req, clientIp(request)));
|
|
}
|
|
|
|
/** 클라이언트 IP (nginx 프록시 뒤 → X-Forwarded-For 우선) */
|
|
private String clientIp(HttpServletRequest request) {
|
|
String xff = request.getHeader("X-Forwarded-For");
|
|
if (xff != null && !xff.isBlank()) {
|
|
return xff.split(",")[0].trim();
|
|
}
|
|
return request.getRemoteAddr();
|
|
}
|
|
|
|
@PostMapping("/login")
|
|
public LoginResponse login(@Valid @RequestBody LoginRequest req, HttpServletRequest request) {
|
|
return authService.login(req, clientIp(request));
|
|
}
|
|
|
|
/** 구글 OAuth 클라이언트 ID (비보호) — 프론트가 구글 버튼 노출/초기화에 사용. 미설정 시 빈 문자열 */
|
|
@GetMapping("/google-client-id")
|
|
public java.util.Map<String, String> googleClientId() {
|
|
return java.util.Map.of("clientId", authService.googleClientId());
|
|
}
|
|
|
|
/** 구글 로그인/가입 (비보호) — ID 토큰 검증 후 세션 발급 */
|
|
@PostMapping("/google")
|
|
public LoginResponse googleLogin(@Valid @RequestBody com.sb.web.auth.dto.GoogleLoginRequest req) {
|
|
return authService.googleLogin(req.getIdToken(), req.isRememberMe());
|
|
}
|
|
|
|
@PostMapping("/logout")
|
|
public ResponseEntity<Void> logout(HttpServletRequest request) {
|
|
authService.logout(AuthInterceptor.resolveToken(request));
|
|
return ResponseEntity.noContent().build();
|
|
}
|
|
|
|
@GetMapping("/me")
|
|
public SessionUser me(@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
|
|
return current;
|
|
}
|
|
|
|
/** 현재 사용자 활동 포인트 (최신값 — 게시판 작성으로 수시 변동) */
|
|
@GetMapping("/points")
|
|
public java.util.Map<String, Long> points(
|
|
@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
|
|
return java.util.Map.of("points", pointService.getPoints(current.getId()));
|
|
}
|
|
|
|
@PutMapping("/password")
|
|
public ResponseEntity<Void> changePassword(
|
|
@Valid @RequestBody PasswordChangeRequest req,
|
|
@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
|
|
authService.changePassword(current.getId(), req);
|
|
return ResponseEntity.noContent().build();
|
|
}
|
|
|
|
/** 가입정보 변경 진입 전 비밀번호 재인증 */
|
|
@PostMapping("/verify-password")
|
|
public ResponseEntity<Void> verifyPassword(
|
|
@Valid @RequestBody com.sb.web.auth.dto.PasswordVerifyRequest req,
|
|
@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
|
|
authService.verifyPassword(current.getId(), req.getPassword());
|
|
return ResponseEntity.noContent().build();
|
|
}
|
|
|
|
/** 현재 회원 전체 프로필 (아바타·포인트 포함) — 재로그인 없이 최신값 동기화 */
|
|
@GetMapping("/profile")
|
|
public MemberResponse profile(@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
|
|
return authService.getProfile(current.getId());
|
|
}
|
|
|
|
/** 가입정보(이름/이메일) 변경 */
|
|
@PutMapping("/profile")
|
|
public MemberResponse updateProfile(
|
|
@Valid @RequestBody com.sb.web.auth.dto.ProfileUpdateRequest req,
|
|
@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current,
|
|
HttpServletRequest request) {
|
|
return authService.updateProfile(current.getId(), AuthInterceptor.resolveToken(request), req);
|
|
}
|
|
|
|
/** 프로필 사진(사용자 지정) 변경/해제 — image 가 비면 해제(구글 사진으로 폴백) */
|
|
@PutMapping("/profile-image")
|
|
public MemberResponse updateProfileImage(
|
|
@RequestBody com.sb.web.auth.dto.ProfileImageRequest req,
|
|
@RequestAttribute(AuthInterceptor.CURRENT_MEMBER) SessionUser current) {
|
|
return authService.updateProfileImage(current.getId(), req.getImage());
|
|
}
|
|
}
|