Skip to content

Commit

Permalink
feat: synchronization of chat info when the server is down
Browse files Browse the repository at this point in the history
  • Loading branch information
barabobBOB committed Jun 3, 2024
1 parent 282b0b3 commit 2b9e8d8
Show file tree
Hide file tree
Showing 12 changed files with 286 additions and 26 deletions.
17 changes: 15 additions & 2 deletions cmd/server/handler/chat_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/gorilla/websocket"
"github.com/labstack/echo/v4"
"github.com/pet-sitter/pets-next-door-api/internal/chat"
"github.com/pet-sitter/pets-next-door-api/internal/domain/user"
"github.com/pet-sitter/pets-next-door-api/internal/service"
)

Expand Down Expand Up @@ -37,7 +38,7 @@ var upgrader = websocket.Upgrader{
}

func (h *ChatHandler) ServerWebsocket(
c echo.Context, wsServer *chat.WsServer, w http.ResponseWriter, r *http.Request,
c echo.Context, w http.ResponseWriter, r *http.Request,
) error {
foundUser, err := h.authService.VerifyAuthAndGetUser(c.Request().Context(), c.Request().Header.Get("Authorization"))
if err != nil {
Expand All @@ -49,10 +50,22 @@ func (h *ChatHandler) ServerWebsocket(
log.Println(err2)
return err2
}
client := chat.NewClient(conn, wsServer, foundUser.Nickname, foundUser.FirebaseUID)

client := h.initializeOrUpdateClient(conn, foundUser)

go client.WritePump()
go client.ReadPump(&h.chatService)

return nil
}

func (h *ChatHandler) initializeOrUpdateClient(conn *websocket.Conn, userData *user.InternalView) *chat.Client {
client := h.wsServer.FindClientByUID(userData.FirebaseUID)
if client == nil {
client = chat.NewClient(conn, h.wsServer, userData.Nickname, userData.FirebaseUID)
h.wsServer.RegisterClient(client)
} else {
client.UpdateConn(conn)
}
return client
}
3 changes: 2 additions & 1 deletion cmd/server/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ func NewRouter(app *firebaseinfra.FirebaseApp) (*echo.Echo, error) {

wsServer := chat.NewWebsocketServer()
go wsServer.Run()
chat.InitializeWebSocketServer(ctx, wsServer, chatService)
chatHandler := handler.NewChatController(wsServer, authService, *chatService)

// Register middlewares
Expand Down Expand Up @@ -140,7 +141,7 @@ func NewRouter(app *firebaseinfra.FirebaseApp) (*echo.Echo, error) {
chatAPIGroup := apiRouteGroup.Group("/chat")
{
chatAPIGroup.GET("/ws", func(c echo.Context) error {
return chatHandler.ServerWebsocket(c, wsServer, c.Response().Writer, c.Request())
return chatHandler.ServerWebsocket(c, c.Response().Writer, c.Request())
})
}

Expand Down
32 changes: 19 additions & 13 deletions internal/chat/chat_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,22 @@ import (
)

type WsServer struct {
clients map[*Client]bool
register chan *Client
unregister chan *Client
broadcast chan []byte
rooms map[*Room]bool
clients map[*Client]bool
clientUIDMapping map[string]*Client
register chan *Client
unregister chan *Client
broadcast chan []byte
rooms map[*Room]bool
}

func NewWebsocketServer() *WsServer {
return &WsServer{
clients: make(map[*Client]bool),
register: make(chan *Client),
unregister: make(chan *Client),
broadcast: make(chan []byte),
rooms: make(map[*Room]bool),
clients: make(map[*Client]bool),
clientUIDMapping: make(map[string]*Client),
register: make(chan *Client),
unregister: make(chan *Client),
broadcast: make(chan []byte),
rooms: make(map[*Room]bool),
}
}

Expand All @@ -30,7 +32,7 @@ func (server *WsServer) Run() {
// 해당하는 채널에 메시지가 들어올 때 작동
select {
case client := <-server.register:
server.registerClient(client)
server.RegisterClient(client)

case client := <-server.unregister:
server.unregisterClient(client)
Expand All @@ -41,10 +43,11 @@ func (server *WsServer) Run() {
}
}

func (server *WsServer) registerClient(client *Client) {
func (server *WsServer) RegisterClient(client *Client) {
server.notifyClientJoined(client)
server.listOnlineClients(client)
server.clients[client] = true
server.clientUIDMapping[client.FbUID] = client
}

func (server *WsServer) unregisterClient(client *Client) {
Expand All @@ -54,6 +57,10 @@ func (server *WsServer) unregisterClient(client *Client) {
}
}

func (server *WsServer) FindClientByUID(uid string) *Client {
return server.clientUIDMapping[uid]
}

func (server *WsServer) notifyClientJoined(client *Client) {
message := &Message{
Action: UserJoinedAction,
Expand Down Expand Up @@ -88,7 +95,6 @@ func (server *WsServer) broadcastToClients(message []byte) {
}
}

// TODO: 메모리 조회 + DB 조회
func (server *WsServer) findRoomByID(roomID int64) *Room {
// 메모리 조회
var foundRoom *Room
Expand Down
5 changes: 4 additions & 1 deletion internal/chat/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ type Client struct {
rooms map[*Room]bool
}

// TODO: 이미 조인한 채팅방 조회해서 room에 추가하는 로직 추가
func NewClient(conn *websocket.Conn, wsServer *WsServer, name, fbUID string) *Client {
return &Client{
FbUID: fbUID,
Expand All @@ -39,6 +38,10 @@ func NewClient(conn *websocket.Conn, wsServer *WsServer, name, fbUID string) *Cl
}
}

func (client *Client) UpdateConn(conn *websocket.Conn) {
client.conn = conn
}

// 채팅 읽기
func (client *Client) ReadPump(chatService *service.ChatService) {
defer func() {
Expand Down
43 changes: 43 additions & 0 deletions internal/chat/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package chat

import (
"context"
"log"

"github.com/pet-sitter/pets-next-door-api/internal/service"
)

func InitializeWebSocketServer(ctx context.Context, wsServer *WsServer, chatService *service.ChatService) {
rows, err := chatService.FindUserChatRoom(ctx)
if err != nil {
log.Println("Error finding user chat rooms:", err)
return
}

// 클라이언트를 중복 생성하지 않도록 관리하는 맵
clientMap := make(map[string]*Client)

for _, row := range rows {
// 클라이언트를 생성하거나 기존 클라이언트를 재사용
client, exists := clientMap[row.UserInfo.FirebaseUID]
if !exists {
client = NewClient(nil, wsServer, row.UserInfo.Nickname, row.UserInfo.FirebaseUID)
wsServer.RegisterClient(client)
clientMap[row.UserInfo.FirebaseUID] = client
}

// 방을 생성하거나 기존 방을 불러옴
room := wsServer.findRoomByID(row.RoomInfo.ID)
if room == nil {
room = room.InitRoom(row.RoomInfo.ID, row.RoomInfo.Name, row.RoomInfo.RoomType)
wsServer.rooms[room] = true
go room.RunRoom(chatService)
}

// 클라이언트를 방에 등록
if !client.isInRoom(room) {
client.rooms[room] = true
room.register <- client
}
}
}
15 changes: 14 additions & 1 deletion internal/chat/room.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func NewRoom(name string, roomType chat.RoomType, roomService *service.ChatServi
}

return &Room{
ID: int64(row.ID),
ID: row.ID,
Name: row.Name,
RoomType: row.RoomType,
clients: make(map[*Client]bool),
Expand All @@ -39,6 +39,19 @@ func NewRoom(name string, roomType chat.RoomType, roomService *service.ChatServi
}, nil
}

// 채팅방 초기화
func (room *Room) InitRoom(roomID int64, name string, roomType chat.RoomType) *Room {
return &Room{
ID: roomID,
Name: name,
RoomType: roomType,
clients: make(map[*Client]bool),
register: make(chan *Client),
unregister: make(chan *Client),
broadcast: make(chan *Message),
}
}

func (room *Room) RunRoom(roomService *service.ChatService) {
for {
select {
Expand Down
8 changes: 5 additions & 3 deletions internal/domain/chat/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const (
)

type Room struct {
ID int32 `field:"id" json:"id"`
ID int64 `field:"id" json:"id"`
Name string `field:"name" json:"name"`
RoomType RoomType `field:"RoomType" json:"RoomType"`
CreatedAt time.Time `field:"createdAt" json:"createdAt"`
Expand All @@ -27,7 +27,7 @@ type Room struct {
}

type Message struct {
ID int32 `field:"id" json:"id"`
ID int64 `field:"id" json:"id"`
UserID int64 `field:"userID" json:"userID"`
RoomID int64 `field:"roomID" json:"roomID"`
MessageType MessageType `field:"messageType" json:"messageType"`
Expand All @@ -38,9 +38,11 @@ type Message struct {
}

type UserChatRoom struct {
ID int32 `field:"id" json:"id"`
ID int64 `field:"id" json:"id"`
UserID int64 `field:"userID" json:"userID"`
RoomID int64 `field:"roomID" json:"roomID"`
JoinedAt time.Time `field:"joinedAt" json:"joinedAt"`
LeftAt time.Time `field:"leftAt" json:"leftAt"`
}

type UserChatRoomList []*UserChatRoom
54 changes: 51 additions & 3 deletions internal/domain/chat/view.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package chat
import (
"time"

utils "github.com/pet-sitter/pets-next-door-api/internal/common"
"github.com/pet-sitter/pets-next-door-api/internal/domain/user"
databasegen "github.com/pet-sitter/pets-next-door-api/internal/infra/database/gen"
)

Expand All @@ -12,6 +14,17 @@ type JoinRoomView struct {
JoinedAt time.Time
}

type UserChatRoomView struct {
ID int64
UserID int64
RoomID int64
JoinedAt time.Time
UserInfo user.WithProfileImage
RoomInfo *Room
}

type UserChatRoomViewList []*UserChatRoomView

func ToJoinRoomView(row databasegen.JoinRoomRow) *JoinRoomView {
return &JoinRoomView{
UserID: row.UserID,
Expand All @@ -22,7 +35,7 @@ func ToJoinRoomView(row databasegen.JoinRoomRow) *JoinRoomView {

func ToCreateRoom(row databasegen.CreateRoomRow) *Room {
return &Room{
ID: row.ID,
ID: int64(row.ID),
Name: row.Name,
RoomType: RoomType(row.RoomType),
CreatedAt: row.CreatedAt,
Expand All @@ -32,7 +45,7 @@ func ToCreateRoom(row databasegen.CreateRoomRow) *Room {

func ToMessage(row databasegen.WriteMessageRow) *Message {
return &Message{
ID: row.ID,
ID: int64(row.ID),
RoomID: row.RoomID,
UserID: row.UserID,
Content: row.Content,
Expand All @@ -43,10 +56,45 @@ func ToMessage(row databasegen.WriteMessageRow) *Message {

func ToRoom(row databasegen.FindRoomByIDRow) *Room {
return &Room{
ID: row.ID,
ID: int64(row.ID),
Name: row.Name,
RoomType: RoomType(row.RoomType),
CreatedAt: row.CreatedAt,
UpdatedAt: row.UpdatedAt,
}
}

func ToUserChatRoom(row databasegen.FindUserChatRoomsRow) *UserChatRoomView {
return &UserChatRoomView{
ID: int64(row.ID),
UserID: row.UserID,
RoomID: row.RoomID,
JoinedAt: row.JoinedAt,
UserInfo: user.WithProfileImage{
ID: int64(row.ID),
Email: row.Email,
Nickname: row.Nickname,
Fullname: row.Fullname,
ProfileImageURL: utils.NullStrToStrPtr(row.ProfileImageUrl),
FirebaseProviderType: user.FirebaseProviderType(row.FbProviderType.String),
FirebaseUID: row.FbUid.String,
CreatedAt: row.CreatedAt,
UpdatedAt: row.UpdatedAt,
},
RoomInfo: &Room{
ID: row.RoomID,
Name: row.ChatRoomName,
RoomType: RoomType(row.ChatRoomType),
CreatedAt: row.ChatRoomCreatedAt,
UpdatedAt: row.ChatRoomUpdatedAt,
},
}
}

func ToUserChatRoomFromRows(rows []databasegen.FindUserChatRoomsRow) UserChatRoomViewList {
userChatRooms := make([]*UserChatRoomView, len(rows))
for i, row := range rows {
userChatRooms[i] = ToUserChatRoom(row)
}
return userChatRooms
}
Loading

0 comments on commit 2b9e8d8

Please sign in to comment.