Skip to content

Commit

Permalink
[BE] ✨ feat : chat 기능 merge
Browse files Browse the repository at this point in the history
[BE] ✨ feat : chat 기능 #512
  • Loading branch information
hobeen-kim authored Oct 2, 2023
2 parents 50055be + cd4ef7a commit 66b5262
Show file tree
Hide file tree
Showing 37 changed files with 1,420 additions and 12 deletions.
4 changes: 4 additions & 0 deletions Server/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-batch'
//quartz
implementation 'org.springframework.boot:spring-boot-starter-quartz'
//socket (stomp)
implementation 'org.springframework.boot:spring-boot-starter-websocket'
//embedded redis
testImplementation group: 'org.testcontainers', name: 'testcontainers', version: '1.17.2'
}

jar {
Expand Down
5 changes: 5 additions & 0 deletions Server/src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ include::overview.adoc[]
=== Video API
* link:snippets/admin/videolist.html[비디오 목록 조회 API, onclick="window.location.href='snippets/admin/videolist.html'"]

=== Chat API
* link:snippets/chat/adminchat.html[관리자용 채팅 API, onclick="window.location.href='snippets/chat/adminchat.html'"]

[[API-List]]
== 일반 APIs
Expand Down Expand Up @@ -81,4 +83,7 @@ include::overview.adoc[]
=== Search API
* link:snippets/search/search.html[비디오/채널 통합 검색 API, onclick="window.location.href='snippets/search/search.html'"]

=== Chat API
* link:snippets/chat/userchat.html[사용자 채팅 API, onclick="window.location.href='snippets/chat/userchat.html'"]


56 changes: 56 additions & 0 deletions Server/src/docs/asciidoc/snippets/chat/adminchat.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
:doctype: book
:icons: font
:source-highlighter: highlightjs
:toc: left
:toclevels: 2
:sectlinks:
:docinfo: shared-head

[[AdminChat]]
= 관리자용 채팅 API

== 미할당 채팅방 조회
=== HTTP Request
include::{snippets}/adminchat/rooms/http-request.adoc[]
==== Request Header
include::{snippets}/adminchat/rooms/request-headers.adoc[]
=== HTTP Response
include::{snippets}/adminchat/rooms/http-response.adoc[]
==== Response Fields
include::{snippets}/adminchat/rooms/response-fields.adoc[]

== 자신이 속한 채팅방 조회
=== HTTP Request
include::{snippets}/adminchat/myrooms/http-request.adoc[]
==== Request Header
include::{snippets}/adminchat/myrooms/request-headers.adoc[]
=== HTTP Response
include::{snippets}/adminchat/myrooms/http-response.adoc[]
==== Response Fields
include::{snippets}/adminchat/myrooms/response-fields.adoc[]

== 채팅방 대화 조회 API
=== HTTP Request
include::{snippets}/adminchat/getmessages/http-request.adoc[]
==== Request Header
include::{snippets}/adminchat/getmessages/request-headers.adoc[]
==== Path Parameters
include::{snippets}/adminchat/getmessages/path-parameters.adoc[]
==== Query Parameters
include::{snippets}/adminchat/getmessages/request-parameters.adoc[]
=== HTTP Response
include::{snippets}/adminchat/getmessages/http-response.adoc[]
==== Response Fields
include::{snippets}/adminchat/getmessages/response-fields.adoc[]

== 상담 완료 처리 API
=== HTTP Request
include::{snippets}/adminchat/completechat/http-request.adoc[]
==== Request Header
include::{snippets}/adminchat/completechat/request-headers.adoc[]
==== Path Parameters
include::{snippets}/adminchat/completechat/path-parameters.adoc[]
=== HTTP Response
include::{snippets}/adminchat/completechat/http-response.adoc[]


36 changes: 36 additions & 0 deletions Server/src/docs/asciidoc/snippets/chat/docinfo.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<script>
function ready(callbackFunc) {
if (document.readyState !== 'loading') {
// Document is already ready, call the callback directly
callbackFunc();
} else if (document.addEventListener) {
// All modern browsers to register DOMContentLoaded
document.addEventListener('DOMContentLoaded', callbackFunc);
} else {
// Old IE browsers
document.attachEvent('onreadystatechange', function () {
if (document.readyState === 'complete') {
callbackFunc();
}
});
}
}

function openPopup(event) {

const target = event.target;
if (target.className !== "popup") {
return;
}

event.preventDefault();
const screenX = event.screenX;
const screenY = event.screenY;
window.open(target.href, target.text, `left=${screenX}, top=${screenY}, width=500, height=600, status=no, menubar=no, toolbar=no, resizable=no`);
}

ready(function () {
const el = document.getElementById("content");
el.addEventListener("click", event => openPopup(event), false);
});
</script>
32 changes: 32 additions & 0 deletions Server/src/docs/asciidoc/snippets/chat/userchat.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
:doctype: book
:icons: font
:source-highlighter: highlightjs
:toc: left
:toclevels: 2
:sectlinks:
:docinfo: shared-head

[[UserChat]]
= 사용자용 채팅 API

== 채팅방 대화 조회 API
=== HTTP Request
include::{snippets}/userchat/getmessages/http-request.adoc[]
==== Request Header
include::{snippets}/userchat/getmessages/request-headers.adoc[]
==== Query Parameters
include::{snippets}/userchat/getmessages/request-parameters.adoc[]
=== HTTP Response
include::{snippets}/userchat/getmessages/http-response.adoc[]
==== Response Fields
include::{snippets}/userchat/getmessages/response-fields.adoc[]

== 상담방 나가기 (삭제) API
=== HTTP Request
include::{snippets}/userchat/exitchat/http-request.adoc[]
==== Request Header
include::{snippets}/userchat/exitchat/request-headers.adoc[]
=== HTTP Response
include::{snippets}/userchat/exitchat/http-response.adoc[]


9 changes: 7 additions & 2 deletions Server/src/main/java/com/server/auth/SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import com.server.auth.jwt.filter.JwtAuthenticationFilter;
Expand Down Expand Up @@ -58,7 +59,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.accessDeniedHandler((request, response, accessDeniedException) -> response.sendError(HttpServletResponse.SC_FORBIDDEN))
.authenticationEntryPoint(new MemberAuthenticationEntryPoint())
.and()
.authorizeRequests(getAuthorizeRequests());
.authorizeRequests(getAuthorizeRequests())
;

return http.build();
}
Expand All @@ -71,7 +73,8 @@ public Customizer<CorsConfigurer<HttpSecurity>> getCors() {
configuration.setAllowedOrigins(List.of(
"http://localhost:3000",
"https://www.itprometheus.net",
"https://admin.itprometheus.net"));
"https://admin.itprometheus.net",
"file://", "http://jxy.me"));
configuration.addAllowedMethod("*");
configuration.addAllowedHeader("*");
configuration.setAllowCredentials(true);
Expand Down Expand Up @@ -110,6 +113,8 @@ private Customizer<ExpressionUrlAuthorizationConfigurer<HttpSecurity>.Expression

.antMatchers("/admin/**").hasAnyRole("ADMIN")

.antMatchers("/user/**").hasAnyRole("USER", "ADMIN")

.antMatchers("/auth/**").permitAll()
.anyRequest().permitAll();
}
Expand Down
35 changes: 35 additions & 0 deletions Server/src/main/java/com/server/auth/util/SecurityUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.server.auth.util;

import com.server.auth.jwt.service.CustomUserDetails;
import com.server.domain.member.entity.Authority;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;

import java.util.stream.Collectors;

public class SecurityUtil {

public static String getEmail() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

if(authentication == null || authentication.getPrincipal() == null) {
return "미로그인 사용자";
}

CustomUserDetails principal = (CustomUserDetails) authentication.getPrincipal();
return principal.getUsername();
}

public static boolean isAdmin() {

if(SecurityContextHolder.getContext().getAuthentication() == null) {
return false;
}

CustomUserDetails principal = (CustomUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
return Authority.valueOf(principal.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.joining())).equals(Authority.ROLE_ADMIN);
}
}
58 changes: 58 additions & 0 deletions Server/src/main/java/com/server/chat/config/RedisConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.server.chat.config;

import com.server.chat.entity.ChatMessage;
import com.server.chat.sub.RedisSubscriber;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {

@Bean
public ChannelTopic channelTopic() {
return new ChannelTopic("chatroom");
}

/**
* redis pub/sub 메시지를 처리하는 listener 설정
*/
@Bean
public RedisMessageListenerContainer redisMessageListener(RedisConnectionFactory connectionFactory,
MessageListenerAdapter listenerAdapter,
ChannelTopic channelTopic) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.addMessageListener(listenerAdapter, channelTopic);
return container;
}

@Bean
public MessageListenerAdapter listenerAdapter(RedisSubscriber redisSubscriber) {
return new MessageListenerAdapter(redisSubscriber, "sendMessage");
}

@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(String.class));
return redisTemplate;
}

@Bean
public RedisTemplate<String, ChatMessage> redisTemplateChatMessage(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, ChatMessage> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(ChatMessage.class));
return redisTemplate;
}
}
38 changes: 38 additions & 0 deletions Server/src/main/java/com/server/chat/config/WebSocketConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.server.chat.config;

import com.server.chat.interceptor.StompHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.*;

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

private final StompHandler stompHandler;

public WebSocketConfig(StompHandler stompHandler) {
this.stompHandler = stompHandler;
}

@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/sub");
config.setApplicationDestinationPrefixes("/pub");
}

@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws")
.setAllowedOriginPatterns("*")
.withSockJS()
;
}

@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(stompHandler);
}
}
Loading

0 comments on commit 66b5262

Please sign in to comment.