diff --git a/main.go b/main.go index 298c3ff..596c13b 100644 --- a/main.go +++ b/main.go @@ -21,7 +21,7 @@ func init() { config.LoadConfig() logger.InitLogger() utils.ValidatorInit() - logger.ReadLanguageLogMessages() + logger.InitLanguageLogMessages() systempermissions.InitSystemPermissions() grouptasks.LoadGroups("") database.InitDatabase() diff --git a/modules/logger/logger.go b/modules/logger/logger.go index cfb734c..c9c6fe3 100644 --- a/modules/logger/logger.go +++ b/modules/logger/logger.go @@ -7,6 +7,7 @@ import ( "io/ioutil" "janex/admin-dashboard-backend/modules/config" "janex/admin-dashboard-backend/modules/structs" + "janex/admin-dashboard-backend/modules/utils" "os" "sort" "strconv" @@ -179,9 +180,14 @@ func GetAllLogMessagesDates(systemLogs bool) []string { return sortedDates } -func ReadLanguageLogMessages() { +func InitLanguageLogMessages() { readLanguageLogMessages(true) readLanguageLogMessages(false) + + AddSystemLog(structs.LogMessage{ + Id: 8, + Type: utils.LogTypeInfo, + }) } func readLanguageLogMessages(systemLogs bool) { @@ -209,3 +215,14 @@ func readLanguageLogMessages(systemLogs bool) { grouptasksLanguageLogMessages = langLogMessages } } + +func FormatMapInterface(data map[string]interface{}) string { + jsonData, err := json.Marshal(data) + + if err != nil { + log.Error().Msgf("Failed to marshal map interface: %s", err.Error()) + return "" + } + + return string(jsonData) +} diff --git a/routers/router/api/v1/jxscanner/scanner.go b/routers/router/api/v1/jxscanner/scanner.go index 40d8386..41cbe64 100644 --- a/routers/router/api/v1/jxscanner/scanner.go +++ b/routers/router/api/v1/jxscanner/scanner.go @@ -2,6 +2,7 @@ package jxscanner import ( "janex/admin-dashboard-backend/modules/database" + "janex/admin-dashboard-backend/modules/logger" "janex/admin-dashboard-backend/modules/structs" "janex/admin-dashboard-backend/modules/utils" "janex/admin-dashboard-backend/routers/router/api/v1/user" @@ -52,6 +53,21 @@ func AddScanner(c *fiber.Ctx) error { Body: scanner, }) + logger.AddSystemLog(structs.LogMessage{ + Id: 14, + Type: utils.LogTypeInfo, + Messages: []structs.LogData{ + { + Type: "scannerId", + Value: scanner.Id, + }, + { + Type: "scannerName", + Value: scanner.Name, + }, + }, + }) + return c.JSON(structs.ScannerResponse{Id: id, Session: session}) } @@ -88,6 +104,21 @@ func ScanResult(c *fiber.Ctx) error { Body: body.ScanResult, }) + logger.AddSystemLog(structs.LogMessage{ + Id: 15, + Type: utils.LogTypeInfo, + Messages: []structs.LogData{ + { + Type: "scannerId", + Value: scannerId, + }, + { + Type: "scanResult", + Value: body.ScanResult, + }, + }, + }) + return c.SendStatus(fiber.StatusOK) } @@ -109,5 +140,16 @@ func DeleteScanner(c *fiber.Ctx) error { }, }) + logger.AddSystemLog(structs.LogMessage{ + Id: 16, + Type: utils.LogTypeInfo, + Messages: []structs.LogData{ + { + Type: "scannerId", + Value: scanner.Id, + }, + }, + }) + return c.SendStatus(fiber.StatusOK) } diff --git a/routers/router/api/v1/logger/logger.go b/routers/router/api/v1/logger/logger.go index 09c1dc4..d03a12b 100644 --- a/routers/router/api/v1/logger/logger.go +++ b/routers/router/api/v1/logger/logger.go @@ -21,6 +21,31 @@ func GetSystemLog(c *fiber.Ctx) error { return c.SendStatus(fiber.StatusUnauthorized) } + logType := "GroupTasks" + + if t == "s" { + logType = "System" + } + + logger.AddSystemLog(structs.LogMessage{ + Id: 17, + Type: utils.LogTypeInfo, + Messages: []structs.LogData{ + { + Type: "userId", + Value: c.Locals("userId").(string), + }, + { + Type: "logType", + Value: logType, + }, + { + Type: "logDate", + Value: date, + }, + }, + }) + if t == "g" { // grouptasks logs return c.JSON(struct { Logs []structs.LogMessageResponse diff --git a/routers/router/api/v1/user/auth.go b/routers/router/api/v1/user/auth.go index a494545..77e34a4 100644 --- a/routers/router/api/v1/user/auth.go +++ b/routers/router/api/v1/user/auth.go @@ -4,6 +4,7 @@ import ( "crypto/rand" "encoding/base64" "janex/admin-dashboard-backend/modules/database" + "janex/admin-dashboard-backend/modules/logger" "janex/admin-dashboard-backend/modules/structs" "janex/admin-dashboard-backend/modules/utils" "janex/admin-dashboard-backend/socketclients" @@ -70,6 +71,17 @@ func UserLogin(c *fiber.Ctx) error { UserAgent: string(c.Context().UserAgent()), ExpiresAt: utils.GetSessionExpiresAtTime()}) + logger.AddSystemLog(structs.LogMessage{ + Id: 19, + Type: utils.LogTypeInfo, + Messages: []structs.LogData{ + { + Type: "userId", + Value: user.Id, + }, + }, + }) + return c.JSON(structs.UserLoginResponse{Session: session}) } @@ -80,6 +92,17 @@ func UserLogout(c *fiber.Ctx) error { socketclients.CloseAllUserSessionConnections(session) + logger.AddSystemLog(structs.LogMessage{ + Id: 20, + Type: utils.LogTypeInfo, + Messages: []structs.LogData{ + { + Type: "userId", + Value: c.Locals("userId").(string), + }, + }, + }) + return c.SendStatus(fiber.StatusCreated) } diff --git a/routers/router/api/v1/user/avatar.go b/routers/router/api/v1/user/avatar.go index 28a95d7..37c8f40 100644 --- a/routers/router/api/v1/user/avatar.go +++ b/routers/router/api/v1/user/avatar.go @@ -3,6 +3,7 @@ package user import ( "fmt" "janex/admin-dashboard-backend/modules/database" + "janex/admin-dashboard-backend/modules/logger" "janex/admin-dashboard-backend/modules/structs" "janex/admin-dashboard-backend/modules/utils" "janex/admin-dashboard-backend/socketclients" @@ -50,5 +51,16 @@ func UpdateAvatar(c *fiber.Ctx) error { }, }) + logger.AddSystemLog(structs.LogMessage{ + Id: 21, + Type: utils.LogTypeInfo, + Messages: []structs.LogData{ + { + Type: "userId", + Value: user.Id, + }, + }, + }) + return c.SaveFile(fileHeader, fmt.Sprintf("./public/avatars/%s", fileName)) } diff --git a/routers/router/api/v1/user/session.go b/routers/router/api/v1/user/session.go index 4894be5..c79a2cf 100644 --- a/routers/router/api/v1/user/session.go +++ b/routers/router/api/v1/user/session.go @@ -2,7 +2,9 @@ package user import ( "janex/admin-dashboard-backend/modules/database" + "janex/admin-dashboard-backend/modules/logger" "janex/admin-dashboard-backend/modules/structs" + "janex/admin-dashboard-backend/modules/utils" "janex/admin-dashboard-backend/socketclients" "github.com/gofiber/fiber/v2" @@ -27,6 +29,17 @@ func SignOutSession(c *fiber.Ctx) error { socketclients.CloseAllUserSessionConnections(userSession.Id) socketclients.UpdateUserSessionsForUser(userSession.UserId, userSession.Id) + + logger.AddSystemLog(structs.LogMessage{ + Id: 22, + Type: utils.LogTypeInfo, + Messages: []structs.LogData{ + { + Type: "userId", + Value: c.Locals("userId").(string), + }, + }, + }) } return c.SendStatus(fiber.StatusOK) diff --git a/routers/router/router.go b/routers/router/router.go index 1117c84..6f2f648 100644 --- a/routers/router/router.go +++ b/routers/router/router.go @@ -16,7 +16,7 @@ func SetupRoutes(app *fiber.App) { u := v1.Group("/user") u.Post("/auth/login", user.UserLogin) - u.Delete("/auth/logout", user.UserLogout) + u.Delete("/auth/logout", userSessionValidation, user.UserLogout) u.Delete("/session/:idForDeletion", userSessionValidation, user.SignOutSession) u.Post("/avatar", userSessionValidation, user.UpdateAvatar) diff --git a/socketclients/socketclients.go b/socketclients/socketclients.go index 1da3c43..94fb6de 100644 --- a/socketclients/socketclients.go +++ b/socketclients/socketclients.go @@ -4,10 +4,12 @@ import ( "encoding/base64" "janex/admin-dashboard-backend/modules/cache" "janex/admin-dashboard-backend/modules/database" + "janex/admin-dashboard-backend/modules/logger" "janex/admin-dashboard-backend/modules/structs" "janex/admin-dashboard-backend/modules/systempermissions" "janex/admin-dashboard-backend/modules/utils" "os" + "strconv" "time" "github.com/gofiber/websocket/v2" @@ -318,8 +320,6 @@ func UpdateUserProfile(conn *websocket.Conn, changes map[string]interface{}) { } } - log.Debug().Msgf("changes %v %v", len(changes), changes) - if len(changes) > 0 { user.UpdatedAt = time.Now() @@ -329,19 +329,31 @@ func UpdateUserProfile(conn *websocket.Conn, changes map[string]interface{}) { if changes["oldPassword"] != nil && changes["newPassword"] != nil { // user has changed password - logout all his sessions CloseAndDeleteAllUserConnections(userId) - } else { - SendMessageOnlyToSessionId(sessionId, structs.SendSocketMessage{ - Cmd: utils.SentCmdUserProfileUpdated, - Body: struct { - UserId string - Changes map[string]interface{} - }{ - UserId: userId, - Changes: updates, + + logger.AddSystemLog(structs.LogMessage{ + Id: 12, + Type: utils.LogTypeInfo, + Messages: []structs.LogData{ + { + Type: "userId", + Value: userId, + }, }, }) + return } + SendMessageOnlyToSessionId(sessionId, structs.SendSocketMessage{ + Cmd: utils.SentCmdUserProfileUpdated, + Body: struct { + UserId string + Changes map[string]interface{} + }{ + UserId: userId, + Changes: updates, + }, + }) + BroadcastMessageExceptUserSessionId(sessionId, structs.SendSocketMessage{ Cmd: utils.SentCmdUserProfileUpdated, Body: struct { @@ -352,6 +364,21 @@ func UpdateUserProfile(conn *websocket.Conn, changes map[string]interface{}) { Changes: updates, }, }) + + logger.AddSystemLog(structs.LogMessage{ + Id: 13, + Type: utils.LogTypeInfo, + Messages: []structs.LogData{ + { + Type: "userId", + Value: userId, + }, + { + Type: "changes", + Value: logger.FormatMapInterface(updates), + }, + }, + }) } } } @@ -418,7 +445,7 @@ func isRoleDisplayNameAvailable(roleDisplayName string) bool { return role.DisplayName == "" } -func AdminAreaUpdateRole(sessionId string, body map[string]interface{}) { +func AdminAreaUpdateRole(conn *websocket.Conn, body map[string]interface{}) { if body["RoleId"] == nil { log.Error().Msgf("No role id specified in update role %v", body) return @@ -486,6 +513,8 @@ func AdminAreaUpdateRole(sessionId string, body map[string]interface{}) { database.DB.Model(&structs.Role{}).Where("id = ?", roleId).Updates(&updatedRole) + sessionId := conn.Locals("sessionId").(string) + SendMessageOnlyToSessionId(sessionId, structs.SendSocketMessage{ Cmd: utils.SentCmdAdminAreaRoleUpdated, Body: struct { @@ -514,9 +543,28 @@ func AdminAreaUpdateRole(sessionId string, body map[string]interface{}) { Cmd: utils.SentCmdRolePermissionsUpdated, Body: changes, }) + + logger.AddSystemLog(structs.LogMessage{ + Id: 2, + Type: utils.LogTypeInfo, + Messages: []structs.LogData{ + { + Type: "userId", + Value: conn.Locals("userId").(string), + }, + { + Type: "roleId", + Value: roleId, + }, + { + Type: "changes", + Value: logger.FormatMapInterface(updates), + }, + }, + }) } -func AdminAreaMoveRoleToSortingOrder(body map[string]interface{}) { +func AdminAreaMoveRoleToSortingOrder(conn *websocket.Conn, body map[string]interface{}) { roleId := body["RoleId"].(string) direction := int(body["Direction"].(float64)) @@ -533,8 +581,6 @@ func AdminAreaMoveRoleToSortingOrder(body map[string]interface{}) { if direction == 1 { newSortingOrder = currentSortingOrder + 1 - log.Debug().Msgf("up %v %v", newSortingOrder, currentSortingOrder) - if newSortingOrder > database.GetRoleSortingOrder()-1 { return } @@ -572,9 +618,28 @@ func AdminAreaMoveRoleToSortingOrder(body map[string]interface{}) { Cmd: utils.SentCmdAdminAreaUpdateRoleSortingOrder, Body: body, }) + + logger.AddSystemLog(structs.LogMessage{ + Id: 3, + Type: utils.LogTypeInfo, + Messages: []structs.LogData{ + { + Type: "userId", + Value: conn.Locals("userId").(string), + }, + { + Type: "roleId", + Value: role.Id, + }, + { + Type: "sortingOrder", + Value: strconv.Itoa(newSortingOrder), + }, + }, + }) } -func AdminAreaDeleteRole(body map[string]interface{}) { +func AdminAreaDeleteRole(conn *websocket.Conn, body map[string]interface{}) { roleId := body["RoleId"].(string) var role structs.Role @@ -593,9 +658,24 @@ func AdminAreaDeleteRole(body map[string]interface{}) { Cmd: utils.SentCmdAdminAreaRoleDeleted, Body: body, }) + + logger.AddSystemLog(structs.LogMessage{ + Id: 4, + Type: utils.LogTypeInfo, + Messages: []structs.LogData{ + { + Type: "userId", + Value: conn.Locals("userId").(string), + }, + { + Type: "roleId", + Value: role.Id, + }, + }, + }) } -func UpdateUserRole(userId string, roleId string) { +func UpdateUserRole(conn *websocket.Conn, userId string, roleId string) { database.DB.Model(&structs.User{}).Where("id = ?", userId).Updates(structs.User{ RoleId: roleId, }) @@ -623,6 +703,25 @@ func UpdateUserRole(userId string, roleId string) { RoleId: roleId, }, }) + + logger.AddSystemLog(structs.LogMessage{ + Id: 5, + Type: utils.LogTypeInfo, + Messages: []structs.LogData{ + { + Type: "userId", + Value: conn.Locals("userId").(string), + }, + { + Type: "roleId", + Value: roleId, + }, + { + Type: "targetUserId", + Value: userId, + }, + }, + }) } func HasPermission(userId string, permission string) bool { @@ -647,7 +746,7 @@ func SendErrorMessageNoPermissions(sessionId string) { }) } -func AllUsersCreateNewUser(sessionId string, body map[string]interface{}) { +func AllUsersCreateNewUser(conn *websocket.Conn, body map[string]interface{}) { if body["Username"] == nil || body["Email"] == nil || body["Password"] == nil || @@ -666,6 +765,8 @@ func AllUsersCreateNewUser(sessionId string, body map[string]interface{}) { return } + sessionId := conn.Locals("sessionId").(string) + if !isUsernameAvailable(username) { SendMessageOnlyToSessionId(sessionId, structs.SendSocketMessage{ Cmd: utils.SentCmdAllUsersNewUserCreated, @@ -731,9 +832,28 @@ func AllUsersCreateNewUser(sessionId string, body map[string]interface{}) { Deactivated: false, }, }) + + logger.AddSystemLog(structs.LogMessage{ + Id: 6, + Type: utils.LogTypeInfo, + Messages: []structs.LogData{ + { + Type: "userId", + Value: conn.Locals("userId").(string), + }, + { + Type: "targetUserId", + Value: newUser.Id, + }, + { + Type: "roleId", + Value: roleId, + }, + }, + }) } -func AllUsersDeleteUser(userId string) { +func AllUsersDeleteUser(conn *websocket.Conn, userId string) { var user structs.User database.DB.Select("avatar").Where("id = ?", userId).Find(&user) @@ -767,9 +887,24 @@ func AllUsersDeleteUser(userId string) { ScannerId: scannerInUsage.Id, }, }) + + logger.AddSystemLog(structs.LogMessage{ + Id: 7, + Type: utils.LogTypeInfo, + Messages: []structs.LogData{ + { + Type: "userId", + Value: conn.Locals("userId").(string), + }, + { + Type: "targetUserId", + Value: userId, + }, + }, + }) } -func AllUsersUserDeactivation(userId string, deactivate bool) { +func AllUsersUserDeactivation(conn *websocket.Conn, userId string, deactivate bool) { database.DB.Model(&structs.User{}).Select("deactivated").Where("id = ?", userId).Updates(structs.User{ Deactivated: deactivate, }) @@ -788,6 +923,25 @@ func AllUsersUserDeactivation(userId string, deactivate bool) { Deactivated: deactivate, }, }) + + logger.AddSystemLog(structs.LogMessage{ + Id: 9, + Type: utils.LogTypeInfo, + Messages: []structs.LogData{ + { + Type: "userId", + Value: conn.Locals("userId").(string), + }, + { + Type: "targetUserId", + Value: userId, + }, + { + Type: "deactivated", + Value: strconv.FormatBool(deactivate), + }, + }, + }) } func ScannersUpdateScannerUsedByUserId(userId string, scannerId string) { @@ -803,4 +957,32 @@ func ScannersUpdateScannerUsedByUserId(userId string, scannerId string) { UsedByUserId: userId, }, }) + + if userId != "" { + logger.AddSystemLog(structs.LogMessage{ + Id: 10, + Type: utils.LogTypeInfo, + Messages: []structs.LogData{ + { + Type: "scannerId", + Value: scannerId, + }, + { + Type: "userId", + Value: userId, + }, + }, + }) + } else { + logger.AddSystemLog(structs.LogMessage{ + Id: 11, + Type: utils.LogTypeInfo, + Messages: []structs.LogData{ + { + Type: "scannerId", + Value: scannerId, + }, + }, + }) + } } diff --git a/socketserver/hub.go b/socketserver/hub.go index 9e7ded4..27f79aa 100644 --- a/socketserver/hub.go +++ b/socketserver/hub.go @@ -268,6 +268,21 @@ func RunHub() { Cmd: utils.SentCmdAdminAreaNewRoleCreated, Body: role, }) + + logger.AddSystemLog(structs.LogMessage{ + Id: 1, + Type: utils.LogTypeInfo, + Messages: []structs.LogData{ + { + Type: "userId", + Value: data.Conn.Locals("userId").(string), + }, + { + Type: "roleId", + Value: role.Id, + }, + }, + }) break case utils.ReceivedCmdAdminAreaUpdateRole: if !socketclients.HasPermission(data.Conn.Locals("userId").(string), utils.PermissionAdminAreaUpdateRole) { @@ -275,7 +290,7 @@ func RunHub() { break } - socketclients.AdminAreaUpdateRole(data.Conn.Locals("sessionId").(string), receivedMessage.Body) + socketclients.AdminAreaUpdateRole(data.Conn, receivedMessage.Body) break case utils.ReceivedCmdAdminAreaUpdateRoleSortingOrder: if !socketclients.HasPermission(data.Conn.Locals("userId").(string), utils.PermissionAdminAreaMoveRoleUpDown) { @@ -283,7 +298,7 @@ func RunHub() { break } - socketclients.AdminAreaMoveRoleToSortingOrder(receivedMessage.Body) + socketclients.AdminAreaMoveRoleToSortingOrder(data.Conn, receivedMessage.Body) break case utils.ReceivedCmdAdminAreaDeleteRole: if !socketclients.HasPermission(data.Conn.Locals("userId").(string), utils.PermissionAdminAreaDeleteRole) { @@ -291,7 +306,7 @@ func RunHub() { break } - socketclients.AdminAreaDeleteRole(receivedMessage.Body) + socketclients.AdminAreaDeleteRole(data.Conn, receivedMessage.Body) break case utils.ReceivedCmdAllUsersUpdateUserRole: if !socketclients.HasPermission(data.Conn.Locals("userId").(string), utils.PermissionAllUsersActionChangeRole) { @@ -299,7 +314,7 @@ func RunHub() { break } - socketclients.UpdateUserRole(receivedMessage.Body["UserId"].(string), receivedMessage.Body["RoleId"].(string)) + socketclients.UpdateUserRole(data.Conn, receivedMessage.Body["UserId"].(string), receivedMessage.Body["RoleId"].(string)) break case utils.ReceivedCmdAllUsersCreateNewUser: if !socketclients.HasPermission(data.Conn.Locals("userId").(string), utils.PermissionAllUsersCreateNewUser) { @@ -307,7 +322,7 @@ func RunHub() { break } - socketclients.AllUsersCreateNewUser(data.Conn.Locals("sessionId").(string), receivedMessage.Body) + socketclients.AllUsersCreateNewUser(data.Conn, receivedMessage.Body) break case utils.ReceivedCmdAllUsersDeleteUser: if !socketclients.HasPermission(data.Conn.Locals("userId").(string), utils.PermissionAllUsersActionDeleteUser) { @@ -315,7 +330,7 @@ func RunHub() { break } - socketclients.AllUsersDeleteUser(receivedMessage.Body["UserId"].(string)) + socketclients.AllUsersDeleteUser(data.Conn, receivedMessage.Body["UserId"].(string)) break case utils.ReceivedCmdAllUsersUserDeactivation: if !socketclients.HasPermission(data.Conn.Locals("userId").(string), utils.PermissionAllUsersActionUserDeactivation) { @@ -323,7 +338,7 @@ func RunHub() { break } - socketclients.AllUsersUserDeactivation(receivedMessage.Body["UserId"].(string), receivedMessage.Body["Deactivation"].(bool)) + socketclients.AllUsersUserDeactivation(data.Conn, receivedMessage.Body["UserId"].(string), receivedMessage.Body["Deactivation"].(bool)) break case utils.ReceivedCmdScannersUseScanners: if !socketclients.HasPermission(data.Conn.Locals("userId").(string), utils.PermissionScannerUseScanners) { @@ -360,6 +375,17 @@ func RunHub() { socketclients.UpdateUserSessionsForUser(userId, sessionId) socketclients.UpdateConnectedUsers(userId) + + logger.AddSystemLog(structs.LogMessage{ + Id: 18, + Type: utils.LogTypeInfo, + Messages: []structs.LogData{ + { + Type: "userId", + Value: userId, + }, + }, + }) } } } diff --git a/system_lang_log_messages.json b/system_lang_log_messages.json index 9bebed3..e8c054a 100644 --- a/system_lang_log_messages.json +++ b/system_lang_log_messages.json @@ -1,11 +1,209 @@ [ { "id": 0, + "languages": [ + { + "lang": "en", + "message": "%userId% has come online" + } + ] + }, + { + "id": 1, + "languages": [ + { + "lang": "en", + "message": "%userId% has created the role %roleId%" + } + ] + }, + { + "id": 2, + "languages": [ + { + "lang": "en", + "message": "%userId% has updated the role %roleId% with the following changes: %changes%" + } + ] + }, + { + "id": 3, + "languages": [ + { + "lang": "en", + "message": "%userId% has changed the sorting order of role %roleId% to %sortingOrder%" + } + ] + }, + { + "id": 4, + "languages": [ + { + "lang": "en", + "message": "%userId% has deleted the role %roleId%" + } + ] + }, + { + "id": 5, + "languages": [ + { + "lang": "en", + "message": "%userId% has assigned the role %roleId% to %targetUserId%" + } + ] + }, + { + "id": 6, + "languages": [ + { + "lang": "en", + "message": "%userId% has created the user %targetUserId% with the assigned role %roleId%" + } + ] + }, + { + "id": 7, + "languages": [ + { + "lang": "en", + "message": "%userId% has deleted the user %targetUserId%" + } + ] + }, + { + "id": 8, + "languages": [ + { + "lang": "en", + "message": "SERVER has started" + } + ] + }, + { + "id": 9, + "languages": [ + { + "lang": "en", + "message": "%userId% has changed the deactivation status of %targetUserId% to %deactivated%" + } + ] + }, + { + "id": 10, + "languages": [ + { + "lang": "en", + "message": "Scanner %scannerId% is now used by %userId%" + } + ] + }, + { + "id": 11, + "languages": [ + { + "lang": "en", + "message": "Scanner %scannerId% is not longer used" + } + ] + }, + { + "id": 12, + "languages": [ + { + "lang": "en", + "message": "%userId% has changed his password" + } + ] + }, + { + "id": 13, + "languages": [ + { + "lang": "en", + "message": "%userId% has updated his account with the following changes: %changes%" + } + ] + }, + { + "id": 14, + "languages": [ + { + "lang": "en", + "message": "Scanner %scannerId% %scannerName% was registered" + } + ] + }, + { + "id": 15, + "languages": [ + { + "lang": "en", + "message": "Scanner %scannerId% has scanned %scanResult%" + } + ] + }, + { + "id": 16, + "languages": [ + { + "lang": "en", + "message": "Scanner %scannerId% was deleted" + } + ] + }, + { + "id": 17, + "languages": [ + { + "lang": "en", + "message": "%userId% has viewed %logType% logs of the date %logDate%" + } + ] + }, + { + "id": 18, + "languages": [ + { + "lang": "en", + "message": "%userId% has gone offline" + } + ] + }, + { + "id": 19, "languages": [ { "lang": "en", "message": "%userId% has logged in" } ] + }, + { + "id": 20, + "languages": [ + { + "lang": "en", + "message": "%userId% has logged out" + } + ] + }, + { + "id": 21, + "languages": [ + { + "lang": "en", + "message": "%userId% has changed his avatar" + } + ] + }, + { + "id": 22, + "languages": [ + { + "lang": "en", + "message": "%userId% has logged out one of his account sessions" + } + ] } ] \ No newline at end of file