diff --git a/src/backend/apigateway-server/src/main/resources/application-local.yml b/src/backend/apigateway-server/src/main/resources/application-local.yml index a3a332aa..262946f5 100644 --- a/src/backend/apigateway-server/src/main/resources/application-local.yml +++ b/src/backend/apigateway-server/src/main/resources/application-local.yml @@ -29,6 +29,14 @@ spring: - RemoveRequestHeader=Cookie - RewritePath=/users/(?.*), /$\{segment} + - id: chat-server-swagger + uri: lb://CHAT-SERVER + predicates: + - Path=/chats/swagger-ui/**, /chats/v3/api-docs/**, /chats/swagger-resources/**, /chats/webjars/** + filters: + - RemoveRequestHeader=Cookie + - RewritePath=/chats/(?.*), /$\{segment} + - id: guild-server uri: lb://GUILD-SERVER predicates: @@ -108,6 +116,15 @@ spring: - RemoveRequestHeader=Cookie - RewritePath=/signalings/(?.*), /$\{segment} + - id: chat-server + uri: lb://CHAT-SERVER + predicates: + - Path=/chats/** + filters: + - RemoveRequestHeader=Cookie + - RewritePath=/chats/(?.*), /$\{segment} + - AuthorizationHeaderFilter + default-filters: - DedupeResponseHeader=Access-Control-Allow-Origin Access-Control-Allow-Credentials globalcors: diff --git a/src/backend/apigateway-server/src/main/resources/application-prod.yml b/src/backend/apigateway-server/src/main/resources/application-prod.yml index d700e371..73ae727f 100644 --- a/src/backend/apigateway-server/src/main/resources/application-prod.yml +++ b/src/backend/apigateway-server/src/main/resources/application-prod.yml @@ -29,6 +29,14 @@ spring: - RemoveRequestHeader=Cookie - RewritePath=/users/(?.*), /$\{segment} + - id: chat-server-swagger + uri: lb://CHAT-SERVER + predicates: + - Path=/guilds/swagger-ui/**, /guilds/v3/api-docs/**, /guilds/swagger-resources/**, /guilds/webjars/** + filters: + - RemoveRequestHeader=Cookie + - RewritePath=/guilds/(?.*), /$\{segment} + - id: guild-server uri: lb://GUILD-SERVER predicates: @@ -108,6 +116,15 @@ spring: - RemoveRequestHeader=Cookie - RewritePath=/signalings/(?.*), /$\{segment} + - id: chat-server + uri: lb://CHAT-SERVER + predicates: + - Path=/chats/** + filters: + - RemoveRequestHeader=Cookie + - RewritePath=/users/(?.*), /$\{segment} + - AuthorizationHeaderFilter + default-filters: - DedupeResponseHeader=Access-Control-Allow-Origin Access-Control-Allow-Credentials globalcors: diff --git a/src/backend/chat-server/build.gradle.kts b/src/backend/chat-server/build.gradle.kts index 892e325d..c9f5380f 100644 --- a/src/backend/chat-server/build.gradle.kts +++ b/src/backend/chat-server/build.gradle.kts @@ -57,6 +57,9 @@ dependencies { implementation("org.springframework.kafka:spring-kafka") implementation("org.springframework.boot:spring-boot-starter-data-mongodb") + + // Swagger + implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.7.0") } dependencyManagement { diff --git a/src/backend/chat-server/src/main/kotlin/com/asyncgate/chat_server/controller/DirectController.kt b/src/backend/chat-server/src/main/kotlin/com/asyncgate/chat_server/controller/DirectController.kt index e7ff7cc6..96841d21 100644 --- a/src/backend/chat-server/src/main/kotlin/com/asyncgate/chat_server/controller/DirectController.kt +++ b/src/backend/chat-server/src/main/kotlin/com/asyncgate/chat_server/controller/DirectController.kt @@ -1,5 +1,6 @@ package com.asyncgate.chat_server.controller +import com.asyncgate.chat_server.controller.docs.DirectControllerDocs import com.asyncgate.chat_server.domain.ReadStatus import com.asyncgate.chat_server.filter.JwtTokenProvider import com.asyncgate.chat_server.service.DirectService @@ -20,7 +21,7 @@ import org.springframework.web.bind.annotation.ResponseBody class DirectController( private val directService: DirectService, private val jwtTokenProvider: JwtTokenProvider, -) { +) : DirectControllerDocs { @MessageMapping("/chat/direct/send-message") fun direct(@Payload directMessageCreate: DirectMessageCreate, message: Message<*>) { @@ -36,7 +37,7 @@ class DirectController( } @MessageMapping("/chat/direct/typing") - fun typing(@Payload directTyping: DirectMessageTyping, message: Message<*>) { + fun typing(@Payload directTyping: DirectMessageTypingRequest, message: Message<*>) { val jwtToken = CustomSecurityContext.extractJwtTokenForStomp(message) val userId = jwtTokenProvider.extract(jwtToken) val directMessage = directTyping.toDomain(userId) @@ -44,7 +45,7 @@ class DirectController( } @MessageMapping("/chat/direct/edit") - fun edit(@Payload directEdit: DirectMessageEdit, message: Message<*>) { + fun edit(@Payload directEdit: DirectMessageEditRequest, message: Message<*>) { val jwtToken = CustomSecurityContext.extractJwtTokenForStomp(message) val userId = jwtTokenProvider.extract(jwtToken) val directMessage = directEdit.toDomain(userId) @@ -52,7 +53,7 @@ class DirectController( } @MessageMapping("/chat/direct/delete") - fun sendDeleteMessage(@Payload directDelete: DirectMessageDelete, message: Message<*>) { + fun sendDeleteMessage(@Payload directDelete: DirectMessageDeleteRequest, message: Message<*>) { val jwtToken = CustomSecurityContext.extractJwtTokenForStomp(message) val userId = jwtTokenProvider.extract(jwtToken) val directMessage = directDelete.toDomain(userId) @@ -61,7 +62,7 @@ class DirectController( @ResponseBody @PostMapping("/chat/direct/file") - fun uploadFile(@ModelAttribute fileRequest: FileRequest, servletRequest: HttpServletRequest): SuccessResponse { + override fun uploadFile(@ModelAttribute fileRequest: FileRequest, servletRequest: HttpServletRequest): SuccessResponse { val jwtToken = CustomSecurityContext.extractJwtTokenForHttp(servletRequest) val userId = jwtTokenProvider.extract(jwtToken) val response = directService.upload(fileRequest, userId) @@ -70,7 +71,7 @@ class DirectController( @ResponseBody @GetMapping("chat/direct") - fun readPaging( + override fun readPaging( @RequestParam("page", defaultValue = "0") page: Int, @RequestParam("size", defaultValue = "10") size: Int, @RequestParam("channel-id") channelId: String, @@ -78,4 +79,17 @@ class DirectController( val response = directService.readPaging(channelId, page, size) return SuccessResponse.ok(response) } + + // Just Docs + override fun directJustDocs(directMessageCreate: DirectMessageCreate, message: Message<*>) { + } + + override fun typingJustDocs(directTyping: DirectMessageTypingRequest, message: Message<*>) { + } + + override fun editJustDocs(directEdit: DirectMessageEditRequest, message: Message<*>) { + } + + override fun sendDeleteMessageJustDocs(directDelete: DirectMessageDeleteRequest, message: Message<*>) { + } } diff --git a/src/backend/chat-server/src/main/kotlin/com/asyncgate/chat_server/controller/DirectRequest.kt b/src/backend/chat-server/src/main/kotlin/com/asyncgate/chat_server/controller/DirectRequest.kt index a220b8d5..c68ba618 100644 --- a/src/backend/chat-server/src/main/kotlin/com/asyncgate/chat_server/controller/DirectRequest.kt +++ b/src/backend/chat-server/src/main/kotlin/com/asyncgate/chat_server/controller/DirectRequest.kt @@ -4,21 +4,36 @@ import com.asyncgate.chat_server.domain.DirectMessage import com.asyncgate.chat_server.domain.DirectMessageType import com.fasterxml.jackson.databind.annotation.JsonDeserialize import com.fasterxml.jackson.databind.annotation.JsonSerialize +import io.swagger.v3.oas.annotations.media.Schema import org.springframework.web.multipart.MultipartFile import java.io.Serializable +@Schema(description = "Direct 메시지 생성 요청 DTO") @JsonSerialize @JsonDeserialize data class DirectMessageCreate( + @Schema(description = "채널 ID, 다이렉트 채팅방 생성 후 id. DirectId와 같음", example = "channel-12345") val channelId: String, + + @Schema(description = "프로필 이미지 URL", example = "http://example.com/image.png") val profileImage: String, + @Schema(description = "사용자 이름", example = "John Doe") val name: String, + + @Schema(description = "메시지 내용", example = "Hello!") val content: String, + + @Schema(description = "썸네일 URL", example = "http://example.com/thumbnail.png", nullable = true) val thumbnail: String? = null, + @Schema(description = "부모 메시지 ID", example = "msg-12345", nullable = true) val parentId: String? = null, + + @Schema(description = "부모 메시지 작성자 이름", example = "Jane Doe", nullable = true) val parentName: String? = null, + + @Schema(description = "부모 메시지 내용", example = "Previous message content", nullable = true) val parentContent: String? = null, ) : Serializable { fun toDomain(userId: String): DirectMessage { @@ -37,12 +52,22 @@ data class DirectMessageCreate( } } -data class DirectMessageEdit( +@Schema(description = "Direct 메시지 수정 요청 DTO") +@JsonSerialize +@JsonDeserialize +data class DirectMessageEditRequest( + @Schema(description = "메시지 ID", example = "msg-12345") val id: String, + + @Schema(description = "채널 ID", example = "channel-12345") val channelId: String, + + @Schema(description = "사용자 이름", example = "John Doe") val name: String, + + @Schema(description = "메시지 내용", example = "Hello, edited!") val content: String, -) { +) : Serializable { fun toDomain(userId: String): DirectMessage { return DirectMessage( channelId = channelId, @@ -54,10 +79,16 @@ data class DirectMessageEdit( } } -data class DirectMessageDelete( +@Schema(description = "Direct 메시지 삭제 요청 DTO") +@JsonSerialize +@JsonDeserialize +data class DirectMessageDeleteRequest( + @Schema(description = "채널 ID", example = "channel-12345") val channelId: String, + + @Schema(description = "메시지 ID", example = "msg-12345") val id: String, -) { +) : Serializable { fun toDomain(userId: String): DirectMessage { return DirectMessage( id = id, @@ -68,11 +99,19 @@ data class DirectMessageDelete( } } -data class DirectMessageTyping( +@Schema(description = "Direct 메시지 타이핑 요청 DTO") +@JsonSerialize +@JsonDeserialize +data class DirectMessageTypingRequest( + @Schema(description = "채널 ID", example = "channel-12345") val channelId: String, + + @Schema(description = "타이핑 중 메시지 내용", example = "User is typing...") val content: String, + + @Schema(description = "사용자 이름", example = "John Doe") val name: String, -) { +) : Serializable { fun toDomain(userId: String): DirectMessage { return DirectMessage( channelId = channelId, @@ -83,16 +122,34 @@ data class DirectMessageTyping( } } +@Schema(description = "파일 업로드 요청 DTO") +@JsonSerialize +@JsonDeserialize data class FileRequest( + @Schema(description = "업로드 이미지 파일", nullable = true) val image: MultipartFile? = null, + + @Schema(description = "썸네일 이미지 파일", nullable = true) val thumbnail: MultipartFile? = null, + + @Schema(description = "사용자 ID", example = "12345") val userId: Long, + + @Schema(description = "사용자 이름", example = "John Doe") val name: String, + + @Schema(description = "메시지 내용", example = "파일 메시지", nullable = true) val content: String? = null, + + @Schema(description = "프로필 이미지 URL", example = "http://example.com/profile.png") val profileImage: String, + + @Schema(description = "채널 ID", example = "channel-12345") val channelId: String, + + @Schema(description = "메시지 타입", example = "FILE") val fileType: DirectMessageType, -) { +) : Serializable { fun toDomain(userId: String, type: DirectMessageType): DirectMessage { return DirectMessage( userId = userId, diff --git a/src/backend/chat-server/src/main/kotlin/com/asyncgate/chat_server/controller/docs/DirectControllerDocs.kt b/src/backend/chat-server/src/main/kotlin/com/asyncgate/chat_server/controller/docs/DirectControllerDocs.kt new file mode 100644 index 00000000..912fd93d --- /dev/null +++ b/src/backend/chat-server/src/main/kotlin/com/asyncgate/chat_server/controller/docs/DirectControllerDocs.kt @@ -0,0 +1,165 @@ +package com.asyncgate.chat_server.controller.docs + +import com.asyncgate.chat_server.controller.DirectMessageCreate +import com.asyncgate.chat_server.controller.DirectMessageDeleteRequest +import com.asyncgate.chat_server.controller.DirectMessageEditRequest +import com.asyncgate.chat_server.controller.DirectMessageTypingRequest +import com.asyncgate.chat_server.controller.DirectPagingResponse +import com.asyncgate.chat_server.controller.FileRequest +import com.asyncgate.chat_server.controller.FileUploadResponse +import com.asyncgate.chat_server.support.response.SuccessResponse +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.parameters.RequestBody +import io.swagger.v3.oas.annotations.security.SecurityRequirement +import io.swagger.v3.oas.annotations.tags.Tag +import jakarta.servlet.http.HttpServletRequest +import org.springframework.messaging.Message +import org.springframework.messaging.handler.annotation.Payload +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.ModelAttribute +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.PutMapping +import org.springframework.web.bind.annotation.RequestParam + +@Tag(name = "Direct Message API", description = "Direct 메시지 관련 API (WebSocket 기반)") +@SecurityRequirement(name = "JWT TOKEN") +interface DirectControllerDocs { + + @Operation( + summary = "Direct 메시지 전송 (WebSocket)", + description = """ + WebSocket을 통해 Direct 메시지를 전송합니다. + **STOMP Destination:** `/chat/direct/send-message` + + **Example JSON:** + ```json + { + "channelId": "channel-12345", + "profileImage": "http://example.com/image.png", + "name": "John Doe", + "content": "Hello!", + "thumbnail": "http://example.com/thumbnail.png" + } + ``` + """ + ) + @PutMapping("/chat/direct/send-message") + fun directJustDocs( + @Parameter(description = "Direct 메시지 생성 DTO", required = true) + @Payload + directMessageCreate: DirectMessageCreate, + message: Message<*>, + ) + + @Operation( + summary = "Direct 메시지 타이핑 (WebSocket)", + description = """ + 사용자의 타이핑 상태를 WebSocket으로 전송합니다. + **STOMP Destination:** `/chat/direct/typing` + + **Example JSON:** + ```json + { + "channelId": "channel-12345", + "name": "John Doe", + "content": "Typing..." + } + ``` + """ + ) + @PutMapping("/chat/direct/typing") + fun typingJustDocs( + @Parameter(description = "타이핑 상태 DTO", required = true) + @Payload + directTyping: DirectMessageTypingRequest, + message: Message<*>, + ) + + @Operation( + summary = "Direct 메시지 수정 (WebSocket)", + description = """ + WebSocket을 통해 Direct 메시지를 수정합니다. + **STOMP Destination:** `/chat/direct/edit` + + **Example JSON:** + ```json + { + "id": "msg-12345", + "channelId": "channel-12345", + "name": "John Doe", + "content": "Edited message" + } + ``` + """ + ) + @PutMapping("/chat/direct/edit") + fun editJustDocs( + @Parameter(description = "메시지 수정 DTO", required = true) + @Payload + directEdit: DirectMessageEditRequest, + message: Message<*>, + ) + + @Operation( + summary = "Direct 메시지 삭제 (WebSocket)", + description = """ + WebSocket을 통해 Direct 메시지를 삭제합니다. + **STOMP Destination:** `/chat/direct/delete` + + **Example JSON:** + ```json + { + "channelId": "channel-12345", + "id": "msg-67890" + } + ``` + """ + ) + @PutMapping("/chat/direct/delete") + fun sendDeleteMessageJustDocs( + @Parameter(description = "메시지 삭제 DTO", required = true) + @Payload + directDelete: DirectMessageDeleteRequest, + message: Message<*>, + ) + + @Operation( + summary = "Direct 파일 업로드 (HTTP)", + description = "HTTP를 통해 파일을 업로드하고 Direct 메시지를 전송합니다.", + requestBody = RequestBody( + content = [ + Content( + mediaType = "multipart/form-data", + schema = Schema(implementation = FileRequest::class) + ) + ] + ) + ) + @PostMapping("/chat/direct/file") + fun uploadFile( + @Parameter(description = "파일 업로드 DTO", required = true) + @ModelAttribute + fileRequest: FileRequest, + servletRequest: HttpServletRequest, + ): SuccessResponse + + @Operation( + summary = "Direct 메시지 페이징 조회 (HTTP)", + description = "채널에 속한 Direct 메시지를 페이징하여 조회합니다." + ) + @GetMapping("/chat/direct") + fun readPaging( + @Parameter(description = "페이지 번호", example = "0") + @RequestParam("page", defaultValue = "0") + page: Int, + @Parameter(description = "페이지 크기", example = "10") + @RequestParam("size", defaultValue = "10") + size: Int, + @Parameter(description = "채널 ID", example = "channel-12345") + @RequestParam("channel-id") + channelId: String, + ): SuccessResponse +} diff --git a/src/backend/chat-server/src/main/kotlin/com/asyncgate/chat_server/filter/StompInterceptor.kt b/src/backend/chat-server/src/main/kotlin/com/asyncgate/chat_server/filter/StompInterceptor.kt index 907d1d0a..6ea7cc6e 100644 --- a/src/backend/chat-server/src/main/kotlin/com/asyncgate/chat_server/filter/StompInterceptor.kt +++ b/src/backend/chat-server/src/main/kotlin/com/asyncgate/chat_server/filter/StompInterceptor.kt @@ -1,5 +1,7 @@ package com.asyncgate.chat_server.filter +import com.asyncgate.chat_server.exception.ChatServerException +import com.asyncgate.chat_server.exception.FailType import org.slf4j.Logger import org.slf4j.LoggerFactory import org.springframework.http.HttpStatus @@ -8,6 +10,7 @@ import org.springframework.messaging.MessageChannel import org.springframework.messaging.simp.stomp.StompCommand import org.springframework.messaging.simp.stomp.StompHeaderAccessor import org.springframework.messaging.support.ChannelInterceptor +import org.springframework.messaging.support.MessageBuilder import org.springframework.stereotype.Component import org.springframework.web.server.ResponseStatusException @@ -44,11 +47,15 @@ class FilterChannelInterceptor( log.error("🚨 [STOMP] Access Token is missing or improperly formatted!") throw ResponseStatusException(HttpStatus.UNAUTHORIZED, "Access token is missing") } - if (!jwtTokenProvider.validate(jwtToken)) { - log.error("🚨 [STOMP] Access Token validation failed!") - throw ResponseStatusException(HttpStatus.UNAUTHORIZED) + try { + if (!jwtTokenProvider.validate(jwtToken)) { + log.error("🚨 [STOMP] Access Token validation failed!") + throw ResponseStatusException(HttpStatus.UNAUTHORIZED) + } + log.info("✅ [STOMP] CONNECT 요청 처리 완료") + } catch (e: ChatServerException) { + createErrorMessage(e.failType) } - log.info("✅ [STOMP] CONNECT 요청 처리 완료") } return message } @@ -101,6 +108,18 @@ class FilterChannelInterceptor( // 시그널링 서버에 전달 (주석) // messageSender.signaling(stateTopic, stateRequest) } + + /** + * STOMP ERROR 프레임 생성 메서드 + */ + private fun createErrorMessage(failType: FailType): Message<*> { + val errorAccessor = StompHeaderAccessor.create(StompCommand.ERROR) + errorAccessor.messageHeaders[StompHeaderAccessor.STOMP_MESSAGE_HEADER] = failType.message + errorAccessor.messageHeaders["errorCode"] = failType.errorCode + errorAccessor.messageHeaders["status"] = failType.status.value() + + return MessageBuilder.createMessage(ByteArray(0), errorAccessor.messageHeaders) + } } data class LoginSessionRequest( diff --git a/src/backend/chat-server/src/main/kotlin/com/asyncgate/chat_server/filter/WebSocketHandshakeInterceptor.kt b/src/backend/chat-server/src/main/kotlin/com/asyncgate/chat_server/filter/WebSocketHandshakeInterceptor.kt index 855ab606..b184669c 100644 --- a/src/backend/chat-server/src/main/kotlin/com/asyncgate/chat_server/filter/WebSocketHandshakeInterceptor.kt +++ b/src/backend/chat-server/src/main/kotlin/com/asyncgate/chat_server/filter/WebSocketHandshakeInterceptor.kt @@ -1,5 +1,6 @@ package com.asyncgate.chat_server.filter +import com.asyncgate.chat_server.exception.ChatServerException import org.slf4j.Logger import org.slf4j.LoggerFactory import org.springframework.http.HttpStatus @@ -60,13 +61,26 @@ class WebSocketHandshakeInterceptor( } val (_, jwtToken) = pair - if (!jwtTokenProvider.validate(jwtToken)) { - log.info("❌ WebSocket Handshake 실패: 유효하지 않은 JWT 토큰") - response.setStatusCode(HttpStatus.UNAUTHORIZED) + try { + if (!jwtTokenProvider.validate(jwtToken)) { + log.info("❌ WebSocket Handshake 실패: 유효하지 않은 JWT 토큰") + response.setStatusCode(HttpStatus.UNAUTHORIZED) + response.headers["WWW-Authenticate"] = + "Bearer error=\"invalid_token\", error_description=\"invalid JWT token\"" + return false + } + } catch (e: ChatServerException) { + log.info("❌ WebSocket Handshake 실패: {}", e.failType.message) + val message = e.failType.message + val status = e.failType.status + + response.setStatusCode(status) response.headers["WWW-Authenticate"] = - "Bearer error=\"invalid_token\", error_description=\"invalid JWT token\"" + "Bearer error=\"invalid_token\", error_description=\"" + return false } + val userId = jwtTokenProvider.extract(jwtToken) log.info("✅ WebSocket Handshake 성공 - userId: $userId") diff --git a/src/backend/chat-server/src/main/kotlin/com/asyncgate/chat_server/repository/DirectMessageMongoRepository.kt b/src/backend/chat-server/src/main/kotlin/com/asyncgate/chat_server/repository/DirectMessageMongoRepository.kt index 087d4c5d..4194b6d1 100644 --- a/src/backend/chat-server/src/main/kotlin/com/asyncgate/chat_server/repository/DirectMessageMongoRepository.kt +++ b/src/backend/chat-server/src/main/kotlin/com/asyncgate/chat_server/repository/DirectMessageMongoRepository.kt @@ -10,6 +10,6 @@ interface DirectMessageMongoRepository : MongoRepository } diff --git a/src/backend/chat-server/src/main/kotlin/com/asyncgate/chat_server/repository/DirectMessageRepository.kt b/src/backend/chat-server/src/main/kotlin/com/asyncgate/chat_server/repository/DirectMessageRepository.kt index 7356223b..5989e33c 100644 --- a/src/backend/chat-server/src/main/kotlin/com/asyncgate/chat_server/repository/DirectMessageRepository.kt +++ b/src/backend/chat-server/src/main/kotlin/com/asyncgate/chat_server/repository/DirectMessageRepository.kt @@ -8,6 +8,7 @@ import com.asyncgate.chat_server.support.utility.toDomain import com.asyncgate.chat_server.support.utility.toEntity import org.springframework.data.domain.Page import org.springframework.data.domain.PageImpl +import org.springframework.data.domain.Sort import org.springframework.stereotype.Repository interface DirectMessageRepository { @@ -42,7 +43,7 @@ class DirectMessageRepositoryImpl( } override fun findByChannelId(channelId: String, page: Int, size: Int): Page { - val pageable = org.springframework.data.domain.PageRequest.of(page, size) + val pageable = org.springframework.data.domain.PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "createdAt")) val data = directMessageMongoRepository.findByChannelId(channelId, pageable) val content = data.map { it.toDomain() }.toList() return PageImpl(content, pageable, data.totalElements) diff --git a/src/backend/chat-server/src/main/resources/application-local.yml b/src/backend/chat-server/src/main/resources/application-local.yml index e03ba8a7..da453f99 100644 --- a/src/backend/chat-server/src/main/resources/application-local.yml +++ b/src/backend/chat-server/src/main/resources/application-local.yml @@ -47,3 +47,14 @@ management: logging: level: com.asyncgate.chat_server: DEBUG + +springdoc: + api-docs: + path: /v3/api-docs + swagger-ui: + path: /swagger-ui.html + config-url: /chats/v3/api-docs/swagger-config + url: /chats/v3/api-docs + operationsSorter: method + tagsSorter: alpha + tryItOutEnabled: true diff --git a/src/backend/chat-server/src/main/resources/application-prod.yml b/src/backend/chat-server/src/main/resources/application-prod.yml index e03ba8a7..a0a5a59c 100644 --- a/src/backend/chat-server/src/main/resources/application-prod.yml +++ b/src/backend/chat-server/src/main/resources/application-prod.yml @@ -47,3 +47,14 @@ management: logging: level: com.asyncgate.chat_server: DEBUG + +springdoc: + api-docs: + path: /v3/api-docs + swagger-ui: + path: /swagger-ui.html + config-url: /chats/v3/api-docs/swagger-config + url: /chats/v3/api-docs + operationsSorter: method + tagsSorter: alpha + tryItOutEnabled: true