Users from Tautulli fix

Sync details to user from every Tautulli server during user aggregation
Prioritize active status to true if any Tautulli server says so
Sync users before showing statistics and validating active state
Show Tautulli servers on user page
This commit is contained in:
aunefyren 2023-11-16 19:34:22 +01:00
parent 2ec6d68c86
commit 80720039e8
5 changed files with 151 additions and 99 deletions

View file

@ -253,3 +253,87 @@ func TautulliGetUsersFromEveryServer() (tautulliUsers []models.TautulliUserGroup
return
}
func TautulliSyncUsersToWrapperr() (err error) {
err = nil
// Get all users from Tautulli
users, err := TautulliGetUsersFromEveryServer()
if err != nil {
log.Println("Failed to get users. Error: " + err.Error())
return errors.New("Failed to get users.")
}
// Create Wrapperr user objects array to fill
newUserArray := []models.WrapperrUser{}
// Create new Wrapperr user objects from Tautulli reply
for _, userGroup := range users {
var userMatch bool = false
var userIndex int = 0
for index, wrapperrUser := range newUserArray {
if userGroup.TautulliUser.UserID == wrapperrUser.UserID {
userMatch = true
userIndex = index
break
}
}
// User object is not in array, create and push to array
// If user object is in array, append new server and weigh active status
if !userMatch {
// Create bool from small int
var Active = false
if userGroup.TautulliUser.IsActive == 1 {
Active = true
} else if userGroup.TautulliUser.IsActive == 0 {
Active = false
} else {
log.Println("Failed to convert integer to bool. Error: " + err.Error())
return errors.New("Failed to convert integer to bool.")
}
// Create new object
wrapperrUser := models.WrapperrUser{
FriendlyName: userGroup.TautulliUser.FriendlyName,
User: userGroup.TautulliUser.Username,
UserID: userGroup.TautulliUser.UserID,
Email: userGroup.TautulliUser.Email,
TautulliServers: []string{userGroup.TautulliServer},
Active: Active,
Wrappings: []models.WrapperrHistoryEntry{},
}
// Push to array
newUserArray = append(newUserArray, wrapperrUser)
} else {
// If any server is "active", the user is active
if newUserArray[userIndex].Active == false && userGroup.TautulliUser.IsActive == 1 {
newUserArray[userIndex].Active = true
}
// Add new server
newUserArray[userIndex].TautulliServers = append(newUserArray[userIndex].TautulliServers, userGroup.TautulliServer)
}
}
// Update every new Wrapperr user with new object
for _, user := range newUserArray {
_, err = UsersGetUser(user.UserID)
if err != nil {
err = UsersSaveUserEntry(user)
if err != nil {
log.Println("Failed to save new user. Error: " + err.Error())
return errors.New("Failed to save new user.")
}
} else {
err = UsersUpdateUser(user.UserID, user.FriendlyName, user.User, user.Email, user.Active, user.TautulliServers)
if err != nil {
log.Println("Failed to update user. Error: " + err.Error())
return errors.New("Failed to update user.")
}
}
}
return
}

View file

@ -348,7 +348,7 @@ func ApiWrapperCacheStatistics(context *gin.Context) {
return
}
context.JSON(http.StatusBadRequest, gin.H{"message": "Completed caching request.", "error": false, "data": *cachingComplete})
context.JSON(http.StatusOK, gin.H{"message": "Completed caching request.", "error": false, "data": *cachingComplete})
return
}
@ -433,78 +433,14 @@ func ApiSyncTautulliUsers(context *gin.Context) {
return
}
users, err := modules.TautulliGetUsersFromEveryServer()
err = modules.TautulliSyncUsersToWrapperr()
if err != nil {
log.Println("Failed to get users. Error: " + err.Error())
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get users."})
log.Println("Failed to sync users from Tautulli. Error: " + err.Error())
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to sync users from Tautulli."})
context.Abort()
return
}
newUserArray := []models.WrapperrUser{}
for _, userGroup := range users {
var userMatch bool = false
var userIndex int = 0
for index, wrapperrUser := range newUserArray {
if userGroup.TautulliUser.UserID == wrapperrUser.UserID {
userMatch = true
userIndex = index
}
}
if !userMatch {
var Active = false
if userGroup.TautulliUser.IsActive == 1 {
Active = true
} else if userGroup.TautulliUser.IsActive == 0 {
Active = false
} else {
log.Println("Failed to convert integer to bool. Error: " + err.Error())
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to convert integer to bool."})
context.Abort()
return
}
wrapperrUser := models.WrapperrUser{
FriendlyName: userGroup.TautulliUser.FriendlyName,
User: userGroup.TautulliUser.Username,
UserID: userGroup.TautulliUser.UserID,
Email: userGroup.TautulliUser.Email,
TautulliServers: []string{userGroup.TautulliServer},
Active: Active,
Wrappings: []models.WrapperrHistoryEntry{},
}
newUserArray = append(newUserArray, wrapperrUser)
} else {
if !newUserArray[userIndex].Active && userGroup.TautulliUser.IsActive == 1 {
newUserArray[userIndex].Active = true
}
newUserArray[userIndex].TautulliServers = append(newUserArray[userIndex].TautulliServers, userGroup.TautulliServer)
}
}
for _, user := range newUserArray {
_, err := modules.UsersGetUser(user.UserID)
if err != nil {
err = modules.UsersSaveUserEntry(user)
if err != nil {
log.Println("Failed to save new user. Error: " + err.Error())
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save new user."})
context.Abort()
return
}
} else {
err = modules.UsersUpdateUser(user.UserID, user.FriendlyName, user.User, user.Email, user.Active, user.TautulliServers)
if err != nil {
log.Println("Failed to update user. Error: " + err.Error())
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update user."})
context.Abort()
return
}
}
}
newusers, err := files.GetUsers()
if err != nil {
log.Println("Failed to new users. Error: " + err.Error())

View file

@ -9,6 +9,7 @@ import (
"log"
"net/http"
"strconv"
"strings"
"time"
"github.com/gin-gonic/gin"
@ -158,20 +159,21 @@ func ApiWrapperGetStatistics(context *gin.Context) {
}
// Check connection to every Tautulli server
for i := 0; i < len(config.TautulliConfig); i++ {
log.Println("Checking Tautulli server '" + config.TautulliConfig[i].TautulliName + "'.")
tautulli_state, err := modules.TautulliTestConnection(config.TautulliConfig[i].TautulliPort, config.TautulliConfig[i].TautulliIP, config.TautulliConfig[i].TautulliHttps, config.TautulliConfig[i].TautulliRoot, config.TautulliConfig[i].TautulliApiKey)
if err != nil {
log.Println("Failed to reach Tautulli server '" + config.TautulliConfig[i].TautulliName + "'. Error: " + err.Error())
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to reach Tautulli server '" + config.TautulliConfig[i].TautulliName + "'."})
context.Abort()
return
} else if !tautulli_state {
log.Println("Failed to ping Tautulli server '" + config.TautulliConfig[i].TautulliName + "' before retrieving statistics.")
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to reach Tautulli server '" + config.TautulliConfig[i].TautulliName + "'."})
context.Abort()
return
}
err = modules.TautulliTestEveryServer()
if err != nil {
log.Println("Failed to test Tautulli server. Error: " + err.Error())
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to test Tautulli server."})
context.Abort()
return
}
// Sync users from Tautulli to Wrapperr
err = modules.TautulliSyncUsersToWrapperr()
if err != nil {
log.Println("Failed to sync users from Tautulli. Error: " + err.Error())
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to sync users from Tautulli."})
context.Abort()
return
}
var userName string = ""
@ -213,17 +215,17 @@ func ApiWrapperGetStatistics(context *gin.Context) {
userEmail = plex_object.Email
// Check for friendly name using Tautulli
for i := 0; i < len(config.TautulliConfig); i++ {
_, new_username, new_friendlyname, _, new_active, err := modules.TautulliGetUserId(config.TautulliConfig[i].TautulliPort, config.TautulliConfig[i].TautulliIP, config.TautulliConfig[i].TautulliHttps, config.TautulliConfig[i].TautulliRoot, config.TautulliConfig[i].TautulliApiKey, userName)
if err == nil {
userName = new_username
userFriendlyName = new_friendlyname
userActive = new_active
}
break
wrapperrUser, err := modules.UsersGetUser(userId)
if err != nil {
log.Println("Failed to find Wrapperr user. Error: " + err.Error())
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to find Wrapperr user."})
context.Abort()
return
}
userName = wrapperrUser.User
userFriendlyName = wrapperrUser.FriendlyName
userActive = wrapperrUser.Active
}
// Read payload from Post input
@ -248,15 +250,12 @@ func ApiWrapperGetStatistics(context *gin.Context) {
UserNameFound := false
for i := 0; i < len(config.TautulliConfig); i++ {
new_id, new_username, user_friendlyname, new_email, new_active, err := modules.TautulliGetUserId(config.TautulliConfig[i].TautulliPort, config.TautulliConfig[i].TautulliIP, config.TautulliConfig[i].TautulliHttps, config.TautulliConfig[i].TautulliRoot, config.TautulliConfig[i].TautulliApiKey, wrapperr_request.PlexIdentity)
new_id, _, _, _, _, err := modules.TautulliGetUserId(config.TautulliConfig[i].TautulliPort, config.TautulliConfig[i].TautulliIP, config.TautulliConfig[i].TautulliHttps, config.TautulliConfig[i].TautulliRoot, config.TautulliConfig[i].TautulliApiKey, strings.TrimSpace(wrapperr_request.PlexIdentity))
if err == nil {
UserNameFound = true
userName = new_username
userId = new_id
userEmail = new_email
userFriendlyName = user_friendlyname
userActive = new_active
break
}
}
@ -266,6 +265,20 @@ func ApiWrapperGetStatistics(context *gin.Context) {
context.Abort()
return
}
// Check for friendly name using Tautulli
wrapperrUser, err := modules.UsersGetUser(userId)
if err != nil {
log.Println("Failed to find Wrapperr user. Error: " + err.Error())
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to find Wrapperr user."})
context.Abort()
return
}
userName = wrapperrUser.User
userEmail = wrapperrUser.Email
userFriendlyName = wrapperrUser.FriendlyName
userActive = wrapperrUser.Active
}
// If no username and no user_id has been declared at this point, something is wrong. Return error.

View file

@ -355,7 +355,7 @@ a {
background-color: var(--green);
border-radius: 0.25em;
width: min-content;
padding: 0 0.5em;
padding: 0.15em 0.5em;
margin: auto;
}
@ -363,11 +363,23 @@ a {
background-color: var(--red);
border-radius: 0.25em;
width: min-content;
padding: 0 0.5em;
padding: 0.15em 0.5em;
margin: auto;
}
.user-username, .user-friendlyname, .user-email {
.tautulli-server-badge {
background-color: var(--yellow);
border-radius: 0.25em;
width: min-content;
padding: 0.15em 0.5em;
margin: auto;
color: var(--black);
white-space: nowrap;
margin: 0.5em auto;
overflow: hidden;
}
.user-username, .user-friendlyname, .user-email, .user-tautulli-servers {
margin: 0.25em 0.5em;
text-align: center;
width: 15em;

View file

@ -73,6 +73,7 @@ function placeUsers(usersArray) {
<div class="user-header">Friendly name</div>
<div class="user-header">Email</div>
<div class="user-header-short">Active</div>
<div class="user-header">Tautulli servers</div>
</div>
`;
@ -93,6 +94,11 @@ function placeUsers(usersArray) {
active_state_class = "user-active-false"
}
var tautulliServersString = ""
user.user_tautulli_servers.forEach(server => {
tautulliServersString += `<div class="tautulli-server-badge" title="${server}">${server}</div>`
});
var html = `
<div class="user-object">
<div class="user-details">
@ -105,6 +111,7 @@ function placeUsers(usersArray) {
${user.user_active}
</div>
</div>
<div class="user-tautulli-servers">${tautulliServersString}</div>
</div>
${historyDiv}