From 6e8faf9c5a2896ffe8252c2dd847b26dece44346 Mon Sep 17 00:00:00 2001 From: alex Date: Thu, 18 May 2023 22:03:40 +0200 Subject: [PATCH] user avatar --- grouptasks/grouptasks.go | 2 +- modules/structs/socket.go | 9 +++++ modules/structs/user.go | 1 + modules/utils/globals.go | 7 +++- routers/router/api/v1/user/avatar.go | 60 ++++++++++++++++++++++++++++ routers/router/router.go | 5 +++ socketclients/socketclients.go | 47 ++++++++++++++++++++-- socketserver/hub.go | 6 ++- 8 files changed, 128 insertions(+), 9 deletions(-) create mode 100644 routers/router/api/v1/user/avatar.go diff --git a/grouptasks/grouptasks.go b/grouptasks/grouptasks.go index 97f1903..44ec6de 100644 --- a/grouptasks/grouptasks.go +++ b/grouptasks/grouptasks.go @@ -66,7 +66,7 @@ func LoadGroups(category string) { if category != "" { socketclients.BroadcastMessage(structs.SendSocketMessage{ - Cmd: utils.SendCmdGroupTasksReloaded, + Cmd: utils.SentCmdGroupTasksReloaded, Body: struct { Category string UpdatedGroups []structs.Group diff --git a/modules/structs/socket.go b/modules/structs/socket.go index 50cc9f6..366361e 100644 --- a/modules/structs/socket.go +++ b/modules/structs/socket.go @@ -88,9 +88,18 @@ type InitUserSocketConnection struct { CategoryGroups []CategoryGroup // config specific group tasks GroupTasks []GroupTasks // in database saved group tasks GroupTasksSteps []GroupTaskSteps + AllUsers []AllUsers +} + +type AllUsers struct { + Id string + Avatar string + Username string + ConnectionStatus uint8 } type UserData struct { + Id string Username string Email string Sessions []UserSessionSocket diff --git a/modules/structs/user.go b/modules/structs/user.go index d2f88e9..4b482be 100644 --- a/modules/structs/user.go +++ b/modules/structs/user.go @@ -6,6 +6,7 @@ import ( type User struct { Id string + Avatar string Username string Email string Password string diff --git a/modules/utils/globals.go b/modules/utils/globals.go index bef8c8c..d3cf491 100644 --- a/modules/utils/globals.go +++ b/modules/utils/globals.go @@ -13,6 +13,8 @@ const ( LenUserId = 36 HeaderXAuthorization = "X-Authorization" + + MaxAvatarSize = 5 * 1024 * 1024 // 5 MB ) // commands sent to web clients @@ -24,8 +26,9 @@ const ( SentCmdUpdateGroupTaskStep = 5 SentCmdUpdateGroupTask = 6 SentCmdReloadingGroupTasks = 7 - SendCmdGroupTasksReloaded = 8 - SendCmdUpdateUserSessions = 9 + SentCmdGroupTasksReloaded = 8 + SentCmdUpdateUserSessions = 9 + SentCmdUpdateAllUsersUserAvatar = 10 ) // commands received from web clients diff --git a/routers/router/api/v1/user/avatar.go b/routers/router/api/v1/user/avatar.go new file mode 100644 index 0000000..07cf72f --- /dev/null +++ b/routers/router/api/v1/user/avatar.go @@ -0,0 +1,60 @@ +package user + +import ( + "fmt" + "janex/admin-dashboard-backend/modules/database" + "janex/admin-dashboard-backend/modules/structs" + "janex/admin-dashboard-backend/modules/utils" + "janex/admin-dashboard-backend/socketclients" + "os" + "strings" + + "github.com/gofiber/fiber/v2" + "github.com/rs/zerolog/log" + "github.com/savsgio/gotils/uuid" +) + +func UpdateAvatar(c *fiber.Ctx) error { + log.Info().Msg("update avatar") + + fileHeader, err := c.FormFile("file") + + if err != nil { + log.Error().Msgf("User Avatar file error: %v", err) + return c.SendStatus(fiber.StatusBadRequest) + } + + if fileHeader.Size > utils.MaxAvatarSize { + log.Error().Msg("User Avatar file size limit exceeded") + return c.SendStatus(fiber.StatusRequestEntityTooLarge) + } + + log.Debug().Msgf("userid", c.Locals("userId")) + + user := structs.User{Id: c.Locals("userId").(string)} + + database.DB.First(&user) + + log.Debug().Msgf("Found user %s", user.Avatar) + + if user.Avatar != "" { + os.Remove("./public/avatars/" + user.Avatar) + } + + fileName := uuid.V4() + "." + strings.Split(fileHeader.Header["Content-Type"][0], "/")[1] + + database.DB.Model(&structs.User{}).Where("id = ?", user.Id).Updates(&structs.User{Avatar: fileName}) + + socketclients.BroadcastMessage(structs.SendSocketMessage{ + Cmd: utils.SentCmdUpdateAllUsersUserAvatar, + Body: struct { + UserId string + Avatar string + }{ + UserId: user.Id, + Avatar: fileName, + }, + }) + + return c.SaveFile(fileHeader, fmt.Sprintf("./public/avatars/%s", fileName)) +} diff --git a/routers/router/router.go b/routers/router/router.go index af47e49..c788e88 100644 --- a/routers/router/router.go +++ b/routers/router/router.go @@ -16,6 +16,9 @@ func SetupRoutes(app *fiber.App) { u.Post("/auth/login", user.UserLogin) u.Delete("/auth/logout", user.UserLogout) u.Delete("/session/:idForDeletion", userSessionValidation, user.SignOutSession) + u.Post("/avatar", userSessionValidation, user.UpdateAvatar) + + app.Static("/", "./public") } func userSessionValidation(c *fiber.Ctx) error { @@ -33,5 +36,7 @@ func userSessionValidation(c *fiber.Ctx) error { return fiber.ErrUnauthorized } + c.Locals("userId", userSession.UserId) + return c.Next() } diff --git a/socketclients/socketclients.go b/socketclients/socketclients.go index 7307cf6..0eabe37 100644 --- a/socketclients/socketclients.go +++ b/socketclients/socketclients.go @@ -13,10 +13,18 @@ func BroadcastMessage(sendSocketMessage structs.SendSocketMessage) { } } -func UpdateConnectedUsers() { +func UpdateConnectedUsers(userId string) { BroadcastMessage(structs.SendSocketMessage{ - Cmd: utils.SentCmdUpdateConnectedUsers, - Body: len(cache.GetSocketClients()), + Cmd: utils.SentCmdUpdateConnectedUsers, + Body: struct { + WebSocketUsersCount int + UserId string + ConnectionStatus uint8 + }{ + WebSocketUsersCount: len(cache.GetSocketClients()), + UserId: userId, + ConnectionStatus: isUserGenerallyConnected(userId), + }, }) } @@ -64,7 +72,7 @@ func UpdateUserSessionsForUser(userId string, ignoreUserSessionId string) { GetUserSessions(userId) SendMessageToUser(userId, ignoreUserSessionId, structs.SendSocketMessage{ - Cmd: utils.SendCmdUpdateUserSessions, + Cmd: utils.SentCmdUpdateUserSessions, Body: GetUserSessions(userId), }) } @@ -78,3 +86,34 @@ func isUserSessionConnected(userSessionId string, socketClients []*structs.Socke return 0 } + +// Used to determine if a user is connected regardless of the session used +func isUserGenerallyConnected(userId string) uint8 { + for _, socketClient := range cache.GetSocketClients() { + if socketClient.UserId == userId { + return 1 + } + } + + return 0 +} + +// Get all users from database. +// This is used in the UI to display all users. +func GetAllUsers() []structs.AllUsers { + var users []structs.User + var allUsers []structs.AllUsers + + database.DB.Find(&users) + + for _, user := range users { + allUsers = append(allUsers, structs.AllUsers{ + Id: user.Id, + Avatar: user.Avatar, + Username: user.Username, + ConnectionStatus: isUserGenerallyConnected(user.Id), + }) + } + + return allUsers +} diff --git a/socketserver/hub.go b/socketserver/hub.go index 0320c0a..ba672cc 100644 --- a/socketserver/hub.go +++ b/socketserver/hub.go @@ -49,6 +49,7 @@ func RunHub() { Cmd: utils.SentCmdInitUserSocketConnection, Body: structs.InitUserSocketConnection{ User: structs.UserData{ + Id: user.Id, Username: user.Username, Email: user.Email, Sessions: socketclients.GetUserSessions(userId), @@ -56,10 +57,11 @@ func RunHub() { CategoryGroups: cache.GetCategoryGroupsSorted(), GroupTasks: grouptasks.GetAllGroupTasks(), GroupTasksSteps: grouptasks.GetAllGroupTasksSteps(), + AllUsers: socketclients.GetAllUsers(), }, }) - socketclients.UpdateConnectedUsers() + socketclients.UpdateConnectedUsers(userId) socketclients.UpdateUserSessionsForUser(userId, sessionId) case data := <-broadcast: @@ -157,7 +159,7 @@ func RunHub() { socketclients.UpdateUserSessionsForUser(connection.Locals("userId").(string), connection.Locals("sessionId").(string)) - socketclients.UpdateConnectedUsers() + socketclients.UpdateConnectedUsers(connection.Locals("userId").(string)) } } }