Skip to content

Commit

Permalink
Merge pull request #65 from KUIT-Space/feat/#49/voice-room-api
Browse files Browse the repository at this point in the history
  • Loading branch information
drbug2000 authored Aug 12, 2024
2 parents e204417 + ea64cad commit 3ce1cba
Show file tree
Hide file tree
Showing 25 changed files with 1,370 additions and 6 deletions.
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ dependencies {
implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5'
//S3
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
//LiveKit
implementation 'io.livekit:livekit-server:0.6.1'


}

test {
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/space/space_spring/config/InterceptorURL.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
public enum InterceptorURL {
SPACE("/space/**"),
TEST("/test/**"),
SPACE_LIST_FOR_USER("/user/space-choice");
SPACE_LIST_FOR_USER("/user/space-choice"),
VOICE_ROOM("/voiceRoom/**");

private final String urlPattern;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

import java.time.Duration;

@Configuration
public class RestTemplateConfig {

@Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
return restTemplateBuilder.build();
return restTemplateBuilder
.setConnectTimeout(Duration.ofMillis(5000))
.setReadTimeout(Duration.ofMillis(5000))
.build();
}
}
76 changes: 76 additions & 0 deletions src/main/java/space/space_spring/controller/LiveKitController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package space.space_spring.controller;

import io.livekit.server.AccessToken;
import jakarta.annotation.Nullable;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import space.space_spring.dto.VoiceRoom.LiveKitDto.LiveKitSession;
import space.space_spring.dto.VoiceRoom.ParticipantDto;
import space.space_spring.dto.VoiceRoom.RoomDto;
import space.space_spring.dto.VoiceRoom.LiveKitDto.GetTokenRequest;
import space.space_spring.service.LiveKitService;

import java.util.List;

@Controller
@RequestMapping("/live")
@RequiredArgsConstructor
public class LiveKitController {

private final LiveKitService liveKitService;

@GetMapping("/token")
public ResponseEntity<String> showUserForm(@RequestBody @Nullable GetTokenRequest getTokenRequest){
AccessToken token = new AccessToken("APIS272JhrVicJF", "b8q8i1TDsPi5jTd6gq0kDMJF58Bc0wtgg4DJfEwfCf9B");

// Fill in token information.
// token.setName("name");
// token.setIdentity("identity3");
// token.setMetadata("metadata3");
// token.addGrants(new RoomJoin(true), new RoomName("myroom"));
//liveKitService.getRoomToken(TokenRequest);
// Sign and create token string.
System.out.println("New access token: " + token.toJwt());
return ResponseEntity.ok(liveKitService.getRoomToken(getTokenRequest).toJwt());
}

@GetMapping("/sessions")
public ResponseEntity<List<LiveKitSession>> getLiveKitSessions() {
List<LiveKitSession> sessions = liveKitService.listLiveKitSessions();
if (sessions != null) {
return ResponseEntity.ok(sessions);
} else {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}

@GetMapping("/roomList")
public ResponseEntity<String> getLiveKitRoomList(){
List<RoomDto> roomList = liveKitService.getRoomDtoList();
if (roomList != null) {
return ResponseEntity.ok(roomList.toString());
} else {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}

}

@GetMapping("/participant")
public ResponseEntity<String> participantList(@RequestParam("roomName") String roomName){
System.out.print("participant Input : "+roomName);
List<ParticipantDto> participantList= liveKitService.getParticipantInfo(roomName);
if (participantList != null) {
//participantList.get(0).getTracksList().get(0).getMuted();
return ResponseEntity.ok(participantList.toString());
} else {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}

}
212 changes: 212 additions & 0 deletions src/main/java/space/space_spring/controller/VoiceRoomController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
package space.space_spring.controller;

import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import space.space_spring.argument_resolver.jwtLogin.JwtLoginAuth;
import space.space_spring.dao.UserSpaceDao;
import space.space_spring.dao.VoiceRoomRepository;
import space.space_spring.dto.VoiceRoom.*;
import space.space_spring.entity.Space;
import space.space_spring.entity.User;
import space.space_spring.entity.UserSpace;
import space.space_spring.exception.VoiceRoomException;
import space.space_spring.response.BaseResponse;
import space.space_spring.service.LiveKitService;
import space.space_spring.service.VoiceRoomService;
import space.space_spring.util.space.SpaceUtils;
import space.space_spring.util.user.UserUtils;
import space.space_spring.util.userSpace.UserSpaceUtils;

import javax.print.DocFlavor;
import java.util.List;
import java.util.Optional;

import static space.space_spring.entity.enumStatus.UserSpaceAuth.MANAGER;
import static space.space_spring.response.status.BaseExceptionResponseStatus.*;
import static space.space_spring.util.bindingResult.BindingResultUtils.getErrorMessage;

@RestController
@RequiredArgsConstructor
@RequestMapping("/space/{spaceId}/voiceRoom")
public class VoiceRoomController {
private final LiveKitService liveKitService;
private final VoiceRoomService voiceRoomService;
private final UserSpaceUtils userSpaceUtils;
private final VoiceRoomRepository voiceRoomRepository;
private final UserSpaceDao userSpaceDao;
private final UserUtils userUtils;
private final SpaceUtils spaceUtils;

//VoiceRoom ์ƒ์„ฑ/์ˆ˜์ •
@PostMapping("")
public BaseResponse<PostVoiceRoomDto.Response> createRoom(
@PathVariable("spaceId") @NotNull long spaceId,
@JwtLoginAuth Long userId,
@Validated @RequestBody PostVoiceRoomDto.Request voiceRoomRequest,
BindingResult bindingResult){
if(bindingResult.hasErrors()){
throw new VoiceRoomException(INVALID_VOICEROOM_REQUEST,getErrorMessage(bindingResult));
}

//ํ•ด๋‹น ์œ ์ €๊ฐ€ voice์ด ์žˆ๋Š” space์— ํฌํ•จ๋˜์–ด ์žˆ๋Š”์ง€(๊ถŒํ•œ์ด ์žˆ๋Š”์ง€) ํ™•์ธ
validateIsUserInSpace(spaceId,userId);
//ํ•ด๋‹น ์œ ์ €๊ฐ€ ํ˜„์žฌ space์— ๋Œ€ํ•ด ๊ด€๋ฆฌ์ž ๊ถŒํ•œ์„ ๊ฐ–๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธ
validateManagerPermission(spaceId,userId);
//Todo response ๋‚ด์šฉ์„ ๋ฌด์—‡์„ ์ฃผ๋ฉด ์ข‹์„์ง€ ( POST response ์ „์ฒด ๊ธฐ๋Šฅ ํ†ต์ผ ํ•˜๋Š” ๊ฒƒ์ผ ์ข‹์•„๋ณด์ž„ )
PostVoiceRoomDto.Response res = new PostVoiceRoomDto.Response(voiceRoomService.createVoiceRoom(spaceId,voiceRoomRequest));
return new BaseResponse<>(res);
}

//ํ˜„์žฌ space์— VoiceRoom ๋ชฉ๋ก
@GetMapping("")
public BaseResponse<GetVoiceRoomList.Response> getRoomList(
@PathVariable("spaceId") @NotNull long spaceId,
@JwtLoginAuth Long userId,
//@RequestBody GetVoiceRoomList.Request voiceRoomList,
@RequestParam(required = false,defaultValue = "0") Integer limit,
@RequestParam(required = false,defaultValue = "false") Boolean showParticipant){

boolean showParticipantValue = (showParticipant != null) ? showParticipant : false;

//ํ•ด๋‹น ์œ ์ €๊ฐ€, voiceRoom์ด ์žˆ๋Š” space์— ํฌํ•จ๋˜์–ด ์žˆ๋Š”์ง€(๊ถŒํ•œ์ด ์žˆ๋Š”์ง€) ํ™•์ธ
validateIsUserInSpace(spaceId,userId);

GetVoiceRoomList.Request voiceRoomList=new GetVoiceRoomList.Request(limit, showParticipant);

List<GetVoiceRoomList.VoiceRoomInfo> roomInfoList = voiceRoomService.getVoiceRoomInfoList(spaceId,voiceRoomList);
return new BaseResponse<GetVoiceRoomList.Response>(new GetVoiceRoomList.Response(roomInfoList));
}

//VoiceRoom์ž…์žฅ token ๋ฐœํ–‰
@GetMapping("/{voiceRoomId}/token")
public BaseResponse<String> getToken(
@PathVariable("spaceId") @NotNull long spaceId,
@JwtLoginAuth Long userId,
@PathVariable("voiceRoomId") @NotNull Long roomId,
HttpServletResponse response
){
//ํ•ด๋‹น ์œ ์ €๊ฐ€, voiceRoom์ด ์žˆ๋Š” space์— ํฌํ•จ๋˜์–ด ์žˆ๋Š”์ง€(๊ถŒํ•œ์ด ์žˆ๋Š”์ง€) ํ™•์ธ
validateIsUserInSpace(spaceId,userId);
//ํ•ด๋‹น voiceRoomId๊ฐ€ ์กด์žฌํ•˜๋Š”์ง€ ํ™•์ธ
validateVoiceRoom(roomId);
//ํ•ด๋‹น voiceRoom์ด ํ•ด๋‹น space์— ์†ํ•œ๊ฒƒ์ด ๋งž๋Š”์ง€ ํ™•์ธ
validateVoiceRoomInSpace(spaceId,roomId);

response.setHeader("Authorization", "Bearer " + voiceRoomService.getToken(spaceId, userId,roomId));
return new BaseResponse<String>(
"๋ณด์ด์Šค๋ฃธ ํ† ํฐ ์ƒ์„ฑ์— ์„ฑ๊ณตํ–ˆ์Šต๋‹ˆ๋‹ค."
);
}

//VoiceRoom์— ์ฐธ๊ฐ€์ž ์ •๋ณด
@GetMapping("/{voiceRoomId}/participant")
public BaseResponse<GetParticipantList.Response> getParticipants(
@PathVariable("spaceId") @NotNull long spaceId,
@JwtLoginAuth Long userId,
@PathVariable("voiceRoomId") @NotNull Long roomId
){
//ํ•ด๋‹น ์œ ์ €๊ฐ€ voice์ด ์žˆ๋Š” space์— ํฌํ•จ๋˜์–ด ์žˆ๋Š”์ง€(๊ถŒํ•œ์ด ์žˆ๋Š”์ง€) ํ™•์ธ
validateIsUserInSpace(spaceId,userId);
//ํ•ด๋‹น voiceRoomId๊ฐ€ ์กด์žฌํ•˜๋Š”์ง€ ํ™•์ธ
validateVoiceRoom(roomId);
//ํ•ด๋‹น voiceRoom์ด ํ•ด๋‹น space์— ์†ํ•œ๊ฒƒ์ด ๋งž๋Š”์ง€ ํ™•์ธ
validateVoiceRoomInSpace(spaceId,roomId);

List<GetParticipantList.ParticipantInfo> participantInfoList = voiceRoomService.getParticipantInfoListById(roomId);
return new BaseResponse<GetParticipantList.Response>(new GetParticipantList.Response(participantInfoList));
}

@PatchMapping("")
public BaseResponse<String> updateVoiceRoom(
@PathVariable("spaceId") @NotNull long spaceId,
@JwtLoginAuth Long userId,
@Validated @RequestBody PatchVoiceRoom patchVoiceRoom,
BindingResult bindingResult
){

if(bindingResult.hasErrors()){
throw new VoiceRoomException(INVALID_VOICEROOM_REQUEST,getErrorMessage(bindingResult));
}
//ํ•ด๋‹น ์œ ์ €๊ฐ€ voice์ด ์žˆ๋Š” space์— ํฌํ•จ๋˜์–ด ์žˆ๋Š”์ง€(๊ถŒํ•œ์ด ์žˆ๋Š”์ง€) ํ™•์ธ
validateIsUserInSpace(spaceId,userId);
//ํ•ด๋‹น ์œ ์ €๊ฐ€ ํ˜„์žฌ space์— ๋Œ€ํ•ด ๊ด€๋ฆฌ์ž ๊ถŒํ•œ์„ ๊ฐ–๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธ
validateManagerPermission(spaceId,userId);
//ํ•ด๋‹น voiceRoom์ด ํ•ด๋‹น space์— ์†ํ•œ๊ฒƒ์ด ๋งž๋Š”์ง€ ํ™•์ธ
for(PatchVoiceRoom.UpdateRoom updateRoom : patchVoiceRoom.getUpdateRoomList()) {
validateVoiceRoomInSpace(spaceId, updateRoom.getRoomId());
}

voiceRoomService.updateVoiceRoom(patchVoiceRoom.getUpdateRoomList());

return new BaseResponse<>("success");
}

@DeleteMapping("/{voiceRoomId}")
public BaseResponse<String> deleteVoiceRoom(
@PathVariable("spaceId") @NotNull long spaceId,
@JwtLoginAuth Long userId,
@PathVariable("voiceRoomId") @NotNull Long voiceRoomId
){
//ํ•ด๋‹น ์œ ์ €๊ฐ€ voice์ด ์žˆ๋Š” space์— ํฌํ•จ๋˜์–ด ์žˆ๋Š”์ง€(๊ถŒํ•œ์ด ์žˆ๋Š”์ง€) ํ™•์ธ
validateIsUserInSpace(spaceId,userId);
//ํ•ด๋‹น ์œ ์ €๊ฐ€ ํ˜„์žฌ space์— ๋Œ€ํ•ด ๊ด€๋ฆฌ์ž ๊ถŒํ•œ์„ ๊ฐ–๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธ
validateManagerPermission(spaceId,userId);
//ํ•ด๋‹น voiceRoom์ด ํ•ด๋‹น space์— ์†ํ•œ๊ฒƒ์ด ๋งž๋Š”์ง€ ํ™•์ธ
validateVoiceRoomInSpace(spaceId, voiceRoomId);

voiceRoomService.deleteVoiceRoom(voiceRoomId);
return new BaseResponse<>("success");
}

//VoiceRoom ๋ณ€๋™์‚ฌํ•ญ ์ „๋‹ฌ
@PostMapping("/status")
public BaseResponse<String> postRoomStatus(){
//Todo ํ•ด๋‹น ์œ ์ €๊ฐ€ voice์ด ์žˆ๋Š” space์— ํฌํ•จ๋˜์–ด ์žˆ๋Š”์ง€(๊ถŒํ•œ์ด ์žˆ๋Š”์ง€) ํ™•์ธ
return new BaseResponse<String>(null);
}

private void validateIsUserInSpace( Long spaceId,Long userId) {
// ์œ ์ €๊ฐ€ ์ŠคํŽ˜์ด์Šค์— ์†ํ•  ๊ฒฝ์šฐ exception์ด ํ„ฐ์ง€์ง€ ์•Š์„ ๊ฒƒ์ž„
// ๊ทธ๋ ‡์ง€ ์•Š์„ ๊ฒฝ์šฐ, USER_IS_NOT_IN_SPACE ์˜ˆ์™ธ๊ฐ€ ํ„ฐ์งˆ ๊ฒƒ์ž„ -> ์ถ”ํ›„ exception handling ๊ณผ์ • ํ•„์š”

//ํ˜„์žฌ๋Š” ์ŠคํŽ˜์ด์Šค ์ ‘๊ทผ ๊ถŒํ•œ์„ ์ผ๊ด„์ ์œผ๋กœ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ
//๋ถ„๋ฆฌ ๊ฐ€๋Šฅ์„ฑ ๋ฐ ํšจ์šฉ์„ฑ ๊ฒ€ํ†  ํ•„์š”
userSpaceUtils.isUserInSpace(userId, spaceId);
}
private boolean validateVoiceRoom(long voiceRoomId){
//Todo ํ•ด๋‹น ๋ณด์ด์Šค๋ฃธ์ด ์กด์žฌํ•˜๋Š”์ง€ ํ™•์ธ
if(!voiceRoomRepository.existsByVoiceRoomId(voiceRoomId)){
throw new VoiceRoomException(VOICEROOM_NOT_EXIST);
}
return true;
}
private boolean validateVoiceRoomNameExist(String voiceRoomName){
if(!voiceRoomRepository.existsByName(voiceRoomName)){
throw new VoiceRoomException(VOICEROOM_NAME_ALREADY_EXIST);
}
return true;
}
private boolean validateVoiceRoomInSpace(long spaceId,long voiceRoomId){
if(! (voiceRoomRepository.findById(voiceRoomId).getSpace().getSpaceId().equals(spaceId))){
throw new VoiceRoomException(VOICEROOM_NOT_IN_SPACE);
}
return true;
}
private boolean validateManagerPermission(long spaceId,long userId){
//ํ•ด๋‹น ์œ ์ €๊ฐ€ ํ˜„์žฌ space์— ๋Œ€ํ•ด ๊ด€๋ฆฌ์ž ๊ถŒํ•œ์„ ๊ฐ–๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธ
//TODO ๊ถŒํ•œ ํ™•์ธ ๊ณผ์ •์„ ์ผ๊ด„์ ์œผ๋กœ ์ฒ˜๋ฆฌ ํ•  ์ˆ˜ ์žˆ๋Š” ์ฝ”๋“œ๊ฐ€ ํ•„์š”ํ•ด ๋ณด์ž„
User user = userUtils.findUserByUserId(userId);
Space space = spaceUtils.findSpaceBySpaceId(spaceId);
//์ด๋ฏธ userSpace ์กด์žฌ ์—ฌ๋ถ€๋ฅผ ๊ฒ€์‚ฌํ•ด์„œ null ๊ฒ€์‚ฌ๋Š” ์ƒ๋žตํ•จ

if(!userSpaceDao.findUserSpaceByUserAndSpace(user,space).get().getUserSpaceAuth().toString().equals(MANAGER.getAuth())){
System.out.print("Author :" +userSpaceDao.findUserSpaceByUserAndSpace(user,space).get().getUserSpaceAuth().toString());
throw new VoiceRoomException(VOICEROOM_DO_NOT_HAVE_PERMISSION);
}
return true;
}
}
3 changes: 3 additions & 0 deletions src/main/java/space/space_spring/dao/UserDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,7 @@ public User getUserByEmail(String email) {
public User findUserByUserId(Long userId) {
return em.find(User.class, userId);
}
public String findProfileImageByUserId(Long userId){
return "Test ProfileImage URL";//findUserByUserId(userId).getProfileImage();
}
}
53 changes: 53 additions & 0 deletions src/main/java/space/space_spring/dao/VoiceRoomDao.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package space.space_spring.dao;

import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.transaction.Transactional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import space.space_spring.entity.Space;
import space.space_spring.entity.VoiceRoom;

import java.util.List;
import java.util.Optional;

@Repository
public class VoiceRoomDao {
@PersistenceContext
private EntityManager entityManager;
@Transactional
public Long createVoiceRoom(String name,int order,Space space){
try {
VoiceRoom voiceRoom = VoiceRoom.createVoiceRoom(name, order, space);
entityManager.persist(voiceRoom);
entityManager.flush();

return voiceRoom.getVoiceRoomId();
}catch (Exception e){
System.out.print("create voiceRoom exception:"+e);
//Todo ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ํ•„์š”
return null;
}
}
@Transactional
public Integer findMaxOrderBySpace(Space space) {
String jpql = "SELECT MAX(r.order) FROM voice_room r WHERE r.space = :space";

return entityManager.createQuery(jpql, Integer.class)
.setParameter("space", space)
.getSingleResult();
}

@Transactional
public VoiceRoom findRoomWithMaxOrderBySpace(Space space) {
String jpql = "SELECT r FROM VoiceRoom r WHERE r.space = :space AND r.order = (SELECT MAX(r2.order) FROM VoiceRoom r2 WHERE r2.space = :space)";

return entityManager.createQuery(jpql, VoiceRoom.class)
.setParameter("space", space)
.getSingleResult();
}


}


Loading

0 comments on commit 3ce1cba

Please sign in to comment.