-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #116 from TRIP-Side-Project/dev
pr for merge
- Loading branch information
Showing
14 changed files
with
1,507 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
package com.api.trip.common.security.oauth; | ||
|
||
import com.api.trip.common.exception.ErrorCode; | ||
import com.api.trip.common.exception.custom_exception.NotFoundException; | ||
import com.api.trip.common.security.jwt.JwtToken; | ||
import com.api.trip.common.security.jwt.JwtTokenProvider; | ||
import com.api.trip.domain.member.controller.dto.LoginResponse; | ||
|
@@ -39,33 +41,33 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo | |
|
||
OAuth2User oAuth2User = (OAuth2User) authentication.getPrincipal(); | ||
|
||
String email = oAuth2User.getAttribute("email"); | ||
String provider = oAuth2User.getAttribute("provider"); | ||
|
||
// [email protected] | ||
String email = provider + "_" + oAuth2User.getAttribute("email"); | ||
Optional<Member> findMember = memberRepository.findByEmail(email); | ||
|
||
// 회원이 아닌 경우에 회원 가입 진행 | ||
|
||
Long memberId = 0L; | ||
String role = ""; | ||
|
||
Member member = null; | ||
if (findMember.isEmpty()) { | ||
String name = oAuth2User.getAttribute("name"); | ||
// KAKAO_user123 | ||
String name = provider + "_" + oAuth2User.getAttribute("name"); | ||
String picture = oAuth2User.getAttribute("picture"); | ||
|
||
Member member = Member.of(email, "", name, picture); | ||
member = Member.of(email, "", name, picture); | ||
memberRepository.save(member); | ||
|
||
memberId = member.getId(); | ||
role = member.getRole().getValue(); | ||
} else { | ||
member = findMember.orElseThrow(() -> new NotFoundException(ErrorCode.NOT_FOUND_MEMBER)); | ||
} | ||
|
||
// OAuth2User 객체에서 권한 가져옴 | ||
JwtToken jwtToken = jwtTokenProvider.createJwtToken(email, role); | ||
JwtToken jwtToken = jwtTokenProvider.createJwtToken(member.getEmail(), member.getRole().getValue()); | ||
|
||
// 쿠키 세팅 | ||
response.addHeader(HttpHeaders.SET_COOKIE, createCookie("tokenType", "Bearer")); | ||
response.addHeader(HttpHeaders.SET_COOKIE, createCookie("accessToken", jwtToken.getAccessToken())); | ||
response.addHeader(HttpHeaders.SET_COOKIE, createCookie("refreshToken", jwtToken.getRefreshToken())); | ||
response.addHeader(HttpHeaders.SET_COOKIE, createCookie("memberId", String.valueOf(memberId))); | ||
response.addHeader(HttpHeaders.SET_COOKIE, createCookie("memberId", String.valueOf(member.getId()))); | ||
|
||
// TODO: 프론트 배포 주소로 변경 예정 | ||
response.sendRedirect("http://localhost:5173/home"); | ||
} | ||
|
||
|
@@ -74,8 +76,9 @@ private static String createCookie(String name, String value) { | |
.domain("localhost") | ||
.path("/") | ||
.httpOnly(true) | ||
//.sameSite("None") // https 환경에서 활성화 | ||
//.secure(false) // https 환경에서 활성화 | ||
.maxAge(60 * 60 * 6) | ||
// .sameSite("None") https 시 활성화 | ||
//.secure(true) https 시 활성화 | ||
.build() | ||
.toString(); | ||
} | ||
|
72 changes: 72 additions & 0 deletions
72
src/main/java/com/api/trip/domain/notification/controller/NotificationController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package com.api.trip.domain.notification.controller; | ||
|
||
import com.api.trip.common.exception.CustomException; | ||
import com.api.trip.common.exception.ErrorCode; | ||
import com.api.trip.domain.member.repository.MemberRepository; | ||
import com.api.trip.domain.notification.controller.dto.GetMyNotificationsResponse; | ||
import com.api.trip.domain.notification.emitter.SseEmitterMap; | ||
import com.api.trip.domain.notification.service.NotificationService; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.security.core.context.SecurityContextHolder; | ||
import org.springframework.web.bind.annotation.*; | ||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; | ||
|
||
import java.time.LocalDateTime; | ||
|
||
@RestController | ||
@RequestMapping("/api/notifications") | ||
@RequiredArgsConstructor | ||
public class NotificationController { | ||
|
||
private final MemberRepository memberRepository; | ||
private final NotificationService notificationService; | ||
private final SseEmitterMap sseEmitterMap; | ||
|
||
@GetMapping(value = "/connect", produces = MediaType.TEXT_EVENT_STREAM_VALUE) | ||
public ResponseEntity<SseEmitter> connect() { | ||
String email = SecurityContextHolder.getContext().getAuthentication().getName(); | ||
Long memberId = memberRepository.findByEmail(email) | ||
.orElseThrow(() -> new CustomException(ErrorCode.UNAUTHORIZED)) | ||
.getId(); | ||
|
||
SseEmitter sseEmitter = new SseEmitter(); | ||
sseEmitterMap.put(memberId, sseEmitter); | ||
sseEmitterMap.send(memberId, "connect", LocalDateTime.now()); | ||
return ResponseEntity.ok(sseEmitter); | ||
} | ||
|
||
@GetMapping("/send-to-all") | ||
public void sendToAll(@RequestParam String message) { | ||
String email = SecurityContextHolder.getContext().getAuthentication().getName(); | ||
sseEmitterMap.sendToAll("send-to-all", email + ": " + message); | ||
} | ||
|
||
@GetMapping("/me") | ||
public ResponseEntity<GetMyNotificationsResponse> getMyNotifications() { | ||
String email = SecurityContextHolder.getContext().getAuthentication().getName(); | ||
return ResponseEntity.ok(notificationService.getMyNotifications(email)); | ||
} | ||
|
||
@PatchMapping("/{notificationId}") | ||
public ResponseEntity<Void> readNotification(@PathVariable Long notificationId) { | ||
String email = SecurityContextHolder.getContext().getAuthentication().getName(); | ||
notificationService.readNotification(notificationId, email); | ||
return ResponseEntity.ok().build(); | ||
} | ||
|
||
@DeleteMapping("/{notificationId}") | ||
public ResponseEntity<Void> deleteNotification(@PathVariable Long notificationId) { | ||
String email = SecurityContextHolder.getContext().getAuthentication().getName(); | ||
notificationService.deleteNotification(notificationId, email); | ||
return ResponseEntity.ok().build(); | ||
} | ||
|
||
@DeleteMapping("/me") | ||
public ResponseEntity<Void> deleteMyNotifications() { | ||
String email = SecurityContextHolder.getContext().getAuthentication().getName(); | ||
notificationService.deleteMyNotifications(email); | ||
return ResponseEntity.ok().build(); | ||
} | ||
} |
36 changes: 36 additions & 0 deletions
36
...main/java/com/api/trip/domain/notification/controller/dto/GetMyNotificationsResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package com.api.trip.domain.notification.controller.dto; | ||
|
||
import com.api.trip.domain.itemtag.model.ItemTag; | ||
import com.api.trip.domain.notification.domain.Notification; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
|
||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.stream.Collectors; | ||
|
||
@Getter | ||
@Builder | ||
public class GetMyNotificationsResponse { | ||
|
||
private List<NotificationResponse> notifications; | ||
private int unread; | ||
|
||
public static GetMyNotificationsResponse of(List<Notification> notifications, List<ItemTag> itemTags) { | ||
Map<Long, List<ItemTag>> map = itemTags.stream() | ||
.collect(Collectors.groupingBy(itemTag -> itemTag.getItem().getId())); | ||
|
||
List<NotificationResponse> notificationResponses = notifications.stream() | ||
.map(notification -> NotificationResponse.of(notification, map.get(notification.getItem().getId()))) | ||
.toList(); | ||
|
||
int unread = (int) notifications.stream() | ||
.filter(notification -> !notification.isRead()) | ||
.count(); | ||
|
||
return builder() | ||
.notifications(notificationResponses) | ||
.unread(unread) | ||
.build(); | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
src/main/java/com/api/trip/domain/notification/controller/dto/NotificationResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package com.api.trip.domain.notification.controller.dto; | ||
|
||
import com.api.trip.domain.itemtag.model.ItemTag; | ||
import com.api.trip.domain.notification.domain.Notification; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
|
||
import java.time.LocalDateTime; | ||
import java.util.List; | ||
|
||
@Getter | ||
@Builder | ||
public class NotificationResponse { | ||
|
||
private Long notificationId; | ||
private Long itemId; | ||
private String itemTitle; | ||
private List<String> tags; | ||
private boolean read; | ||
private LocalDateTime createdAt; | ||
|
||
public static NotificationResponse of(Notification notification, List<ItemTag> itemTags) { | ||
List<String> tagNames = itemTags.stream().map(itemTag -> itemTag.getTag().getName()).toList(); | ||
return builder() | ||
.notificationId(notification.getId()) | ||
.itemId(notification.getItem().getId()) | ||
.itemTitle(notification.getItem().getTitle()) | ||
.tags(tagNames) | ||
.read(notification.isRead()) | ||
.createdAt(notification.getCreatedAt()) | ||
.build(); | ||
} | ||
} |
41 changes: 41 additions & 0 deletions
41
src/main/java/com/api/trip/domain/notification/domain/Notification.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package com.api.trip.domain.notification.domain; | ||
|
||
import com.api.trip.common.auditing.entity.BaseTimeEntity; | ||
import com.api.trip.domain.item.model.Item; | ||
import com.api.trip.domain.member.model.Member; | ||
import jakarta.persistence.*; | ||
import lombok.AccessLevel; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
@Entity | ||
@Table(uniqueConstraints = {@UniqueConstraint(columnNames = {"member_id", "item_id"})}) | ||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
@Getter | ||
public class Notification extends BaseTimeEntity { | ||
|
||
@Id | ||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
private Long id; | ||
|
||
@ManyToOne(fetch = FetchType.LAZY) | ||
private Member member; | ||
|
||
@ManyToOne(fetch = FetchType.LAZY) | ||
private Item item; | ||
|
||
@Column(nullable = false) | ||
private boolean read; | ||
|
||
@Builder | ||
private Notification(Member member, Item item, boolean read) { | ||
this.member = member; | ||
this.item = item; | ||
this.read = read; | ||
} | ||
|
||
public void read() { | ||
read = true; | ||
} | ||
} |
55 changes: 55 additions & 0 deletions
55
src/main/java/com/api/trip/domain/notification/emitter/SseEmitterMap.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package com.api.trip.domain.notification.emitter; | ||
|
||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; | ||
|
||
import java.io.IOException; | ||
import java.util.Map; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
|
||
import static org.springframework.web.servlet.mvc.method.annotation.SseEmitter.SseEventBuilder; | ||
import static org.springframework.web.servlet.mvc.method.annotation.SseEmitter.event; | ||
|
||
@Component | ||
@Slf4j | ||
public class SseEmitterMap { | ||
|
||
private final Map<Long, SseEmitter> sseEmitterMap = new ConcurrentHashMap<>(); | ||
|
||
public void put(Long memberId, SseEmitter sseEmitter) { | ||
sseEmitter.onCompletion(() -> remove(memberId)); | ||
sseEmitter.onTimeout(sseEmitter::complete); | ||
sseEmitterMap.put(memberId, sseEmitter); | ||
log.info("connected with {}, the number of connections is {}", memberId, sseEmitterMap.size()); | ||
} | ||
|
||
public void remove(Long memberId) { | ||
sseEmitterMap.remove(memberId); | ||
log.info("disconnected with {}, the number of connections is {}", memberId, sseEmitterMap.size()); | ||
} | ||
|
||
public void send(Long memberId, String eventName, Object eventData) { | ||
SseEmitter sseEmitter = sseEmitterMap.get(memberId); | ||
try { | ||
sseEmitter.send( | ||
event() | ||
.name(eventName) | ||
.data(eventData) | ||
); | ||
} catch (IOException | IllegalStateException e) { | ||
remove(memberId); | ||
} | ||
} | ||
|
||
public void sendToAll(String eventName, Object eventData) { | ||
SseEventBuilder sseEventBuilder = event().name(eventName).data(eventData); | ||
sseEmitterMap.forEach((memberId, sseEmitter) -> { | ||
try { | ||
sseEmitter.send(sseEventBuilder); | ||
} catch (IOException | IllegalStateException e) { | ||
remove(memberId); | ||
} | ||
}); | ||
} | ||
} |
Oops, something went wrong.