Ignore users

Admin panel css tweaks
This commit is contained in:
aunefyren 2023-12-01 13:24:11 +01:00
parent eaab43f8e9
commit 74db0ebd7b
9 changed files with 190 additions and 9 deletions

View file

@ -171,6 +171,7 @@ func initRouter(config models.WrapperrConfig) *gin.Engine {
admin.POST("/get/cache-statistics", routes.ApiWrapperCacheStatistics) admin.POST("/get/cache-statistics", routes.ApiWrapperCacheStatistics)
admin.POST("/get/users", routes.ApiGetUsers) admin.POST("/get/users", routes.ApiGetUsers)
admin.POST("/get/users/:userId", routes.ApiGetUser) admin.POST("/get/users/:userId", routes.ApiGetUser)
admin.POST("/get/users/:userId/ignore", routes.ApiIgnoreUser)
admin.POST("/sync/users", routes.ApiSyncTautulliUsers) admin.POST("/sync/users", routes.ApiSyncTautulliUsers)
} }
} }

View file

@ -10,3 +10,7 @@ type AdminConfigUpdateRequest struct {
AdminPassword string `json:"admin_password"` AdminPassword string `json:"admin_password"`
AdminPasswordOriginal string `json:"admin_password_original"` AdminPasswordOriginal string `json:"admin_password_original"`
} }
type AdminIgnoreUser struct {
Ignore bool `json:"user_ignore"`
}

View file

@ -10,6 +10,7 @@ type WrapperrUser struct {
Active bool `json:"user_active"` Active bool `json:"user_active"`
TautulliServers []string `json:"user_tautulli_servers"` TautulliServers []string `json:"user_tautulli_servers"`
Wrappings []WrapperrHistoryEntry `json:"wrappings"` Wrappings []WrapperrHistoryEntry `json:"wrappings"`
Ignore bool `json:"user_ignore"`
} }
type WrapperrHistoryEntry struct { type WrapperrHistoryEntry struct {

View file

@ -84,6 +84,12 @@ func WrapperrDownloadDays(ID int, wrapperr_data []models.WrapperrDay, loop_inter
var found_date bool = false var found_date bool = false
var found_date_index int = 0 var found_date_index int = 0
ignoredUserIDSArray, err := UsersGetIgnoredUserIDs()
if err != nil {
log.Println("Failed to get ignored users. Error: " + err.Error())
return []models.WrapperrDay{}, false, errors.New("Failed to get ignored users.")
}
// If loop_interval is less than one, do not utilize the loop interval and set use_loop_interval to false // If loop_interval is less than one, do not utilize the loop interval and set use_loop_interval to false
if loop_interval < 1 { if loop_interval < 1 {
use_loop_interval = false use_loop_interval = false
@ -216,6 +222,18 @@ func WrapperrDownloadDays(ID int, wrapperr_data []models.WrapperrDay, loop_inter
for j := 0; j < len(tautulli_data); j++ { for j := 0; j < len(tautulli_data); j++ {
if tautulli_data[j].MediaType == "movie" || tautulli_data[j].MediaType == "episode" || tautulli_data[j].MediaType == "track" { if tautulli_data[j].MediaType == "movie" || tautulli_data[j].MediaType == "episode" || tautulli_data[j].MediaType == "track" {
// If the data is generated by an ignored user, skip the data
ignoreData := false
for u := 0; u < len(ignoredUserIDSArray); u++ {
if ignoredUserIDSArray[u] == tautulli_data[j].UserID {
ignoreData = true
}
}
if ignoreData {
continue
}
// Translate data to own struct
tautulli_entry := models.TautulliEntry{ tautulli_entry := models.TautulliEntry{
Date: tautulli_data[j].Date, Date: tautulli_data[j].Date,
RowID: tautulli_data[j].RowID, RowID: tautulli_data[j].RowID,

View file

@ -267,6 +267,12 @@ func TautulliSyncUsersToWrapperr() (err error) {
// Create Wrapperr user objects array to fill // Create Wrapperr user objects array to fill
newUserArray := []models.WrapperrUser{} newUserArray := []models.WrapperrUser{}
ignoredUsersIDArray, err := UsersGetIgnoredUserIDs()
if err != nil {
log.Println("Failed to get ignored users. Error: " + err.Error())
return errors.New("Failed to get ignored users.")
}
// Create new Wrapperr user objects from Tautulli reply // Create new Wrapperr user objects from Tautulli reply
for _, userGroup := range users { for _, userGroup := range users {
var userMatch bool = false var userMatch bool = false
@ -293,6 +299,14 @@ func TautulliSyncUsersToWrapperr() (err error) {
return errors.New("Failed to convert integer to bool.") return errors.New("Failed to convert integer to bool.")
} }
var userAlreadyIgnored = false
for _, ignoredID := range ignoredUsersIDArray {
if ignoredID == userGroup.TautulliUser.UserID {
userAlreadyIgnored = true
break
}
}
// Create new object // Create new object
wrapperrUser := models.WrapperrUser{ wrapperrUser := models.WrapperrUser{
FriendlyName: userGroup.TautulliUser.FriendlyName, FriendlyName: userGroup.TautulliUser.FriendlyName,
@ -302,6 +316,7 @@ func TautulliSyncUsersToWrapperr() (err error) {
TautulliServers: []string{userGroup.TautulliServer}, TautulliServers: []string{userGroup.TautulliServer},
Active: Active, Active: Active,
Wrappings: []models.WrapperrHistoryEntry{}, Wrappings: []models.WrapperrHistoryEntry{},
Ignore: userAlreadyIgnored,
} }
// Push to array // Push to array
@ -327,7 +342,7 @@ func TautulliSyncUsersToWrapperr() (err error) {
return errors.New("Failed to save new user.") return errors.New("Failed to save new user.")
} }
} else { } else {
err = UsersUpdateUser(user.UserID, user.FriendlyName, user.User, user.Email, user.Active, user.TautulliServers) err = UsersUpdateUser(user.UserID, user.FriendlyName, user.User, user.Email, user.Active, user.TautulliServers, user.Ignore)
if err != nil { if err != nil {
log.Println("Failed to update user. Error: " + err.Error()) log.Println("Failed to update user. Error: " + err.Error())
return errors.New("Failed to update user.") return errors.New("Failed to update user.")

View file

@ -59,7 +59,7 @@ func UsersGetUser(userID int) (user models.WrapperrUser, err error) {
return user, errors.New("User not found.") return user, errors.New("User not found.")
} }
func UsersUpdateUser(userID int, FriendlyName string, userName string, Email string, Active bool, TautulliServers []string) (err error) { func UsersUpdateUser(userID int, FriendlyName string, userName string, Email string, Active bool, TautulliServers []string, Ignore bool) (err error) {
err = nil err = nil
users, err := files.GetUsers() users, err := files.GetUsers()
@ -86,6 +86,7 @@ func UsersUpdateUser(userID int, FriendlyName string, userName string, Email str
users[userIndex].FriendlyName = FriendlyName users[userIndex].FriendlyName = FriendlyName
users[userIndex].User = userName users[userIndex].User = userName
users[userIndex].TautulliServers = TautulliServers users[userIndex].TautulliServers = TautulliServers
users[userIndex].Ignore = Ignore
err = files.SaveUsers(users) err = files.SaveUsers(users)
if err != nil { if err != nil {
@ -95,3 +96,22 @@ func UsersUpdateUser(userID int, FriendlyName string, userName string, Email str
return return
} }
func UsersGetIgnoredUserIDs() (users []int, err error) {
err = nil
users = []int{}
foundUsers, err := files.GetUsers()
if err != nil {
log.Println("Failed to get users. Error: " + err.Error())
return users, errors.New("Failed to get users.")
}
for _, foundUser := range foundUsers {
if foundUser.Ignore {
users = append(users, foundUser.UserID)
}
}
return users, err
}

View file

@ -452,3 +452,65 @@ func ApiSyncTautulliUsers(context *gin.Context) {
context.JSON(http.StatusOK, gin.H{"message": "Users synced.", "data": newusers}) context.JSON(http.StatusOK, gin.H{"message": "Users synced.", "data": newusers})
return return
} }
func ApiIgnoreUser(context *gin.Context) {
configBool, err := files.GetConfigState()
if err != nil {
log.Println("Failed to retrieve configuration state. Error: " + err.Error())
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to retrieve configuration state."})
context.Abort()
return
} else if !configBool {
context.JSON(http.StatusBadRequest, gin.H{"error": "Wrapperr is not configured."})
context.Abort()
return
}
var userId = context.Param("userId")
userIDInt, err := strconv.Atoi(userId)
if err != nil {
log.Println("Failed to parse user ID. Error: " + err.Error())
context.JSON(http.StatusBadRequest, gin.H{"error": "Failed to parse user ID."})
context.Abort()
return
}
// Read payload from Post input
var ignorePayload models.AdminIgnoreUser
if err := context.ShouldBindJSON(&ignorePayload); err != nil {
log.Println("Failed to parse request. Error: " + err.Error())
context.JSON(http.StatusBadRequest, gin.H{"error": "Failed to parse request."})
context.Abort()
return
}
user, err := modules.UsersGetUser(userIDInt)
if err != nil {
log.Println("Failed to get user. Error: " + err.Error())
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get user."})
context.Abort()
return
}
user.Ignore = ignorePayload.Ignore
err = modules.UsersUpdateUser(user.UserID, user.FriendlyName, user.User, user.Email, user.Active, user.TautulliServers, user.Ignore)
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
}
err = files.ClearCache()
if err != nil {
log.Println("Failed to clear cache. Error: " + err.Error())
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to clear cache."})
context.Abort()
return
}
context.JSON(http.StatusOK, gin.H{"message": "User toggeled."})
return
}

View file

@ -329,6 +329,7 @@ a {
background-color: var(--charcoal); background-color: var(--charcoal);
color: var(--white); color: var(--white);
border-radius: 0.25em; border-radius: 0.25em;
overflow: scroll;
} }
.user-object { .user-object {
@ -344,11 +345,22 @@ a {
align-items: center; align-items: center;
} }
.user-userid, .user-active { .user-userid, .user-active, .user-ignore, .user-logbutton {
margin: 0.25em 0.5em; margin: 0.25em 0.5em;
text-align: center; text-align: center;
width: 5em; width: 5em;
overflow: hidden; overflow: hidden;
height: 2em;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
align-content: center;
justify-content: center;
align-items: center;
}
.user-logbutton {
height: 3em;
} }
.user-active-true { .user-active-true {
@ -440,10 +452,6 @@ a {
text-decoration: underline; text-decoration: underline;
} }
.user-logbutton {
width: 4em;
}
/* The Modal (background) */ /* The Modal (background) */
.modal { .modal {
display: none; /* Hidden by default */ display: none; /* Hidden by default */

View file

@ -74,6 +74,8 @@ function placeUsers(usersArray) {
<div class="user-header">Email</div> <div class="user-header">Email</div>
<div class="user-header-short">Active</div> <div class="user-header-short">Active</div>
<div class="user-header">Tautulli servers</div> <div class="user-header">Tautulli servers</div>
<div class="user-header-short">Ignore user</div>
<div class="user-header-short">History</div>
</div> </div>
`; `;
@ -85,6 +87,11 @@ function placeUsers(usersArray) {
<button class="form-control btn" name="historyButton" id="historyButton" onclick="getHistory(${user.user_id});" style="width: 2em; height: 2em; padding: 0.25em;"><img src="${root}/assets/document.svg" class="btn_solo"></img><p2 id="historyButtonImage"></p2></button> <button class="form-control btn" name="historyButton" id="historyButton" onclick="getHistory(${user.user_id});" style="width: 2em; height: 2em; padding: 0.25em;"><img src="${root}/assets/document.svg" class="btn_solo"></img><p2 id="historyButtonImage"></p2></button>
</div> </div>
`; `;
} else {
historyDiv = `
<div class="user-logbutton">
</div>
`;
} }
var active_state_class = "" var active_state_class = ""
@ -99,6 +106,11 @@ function placeUsers(usersArray) {
tautulliServersString += `<div class="tautulli-server-badge" title="${server}">${server}</div>` tautulliServersString += `<div class="tautulli-server-badge" title="${server}">${server}</div>`
}); });
var ignoredUserString = ""
if(user.user_ignore) {
ignoredUserString = "checked"
}
var html = ` var html = `
<div class="user-object"> <div class="user-object">
<div class="user-details"> <div class="user-details">
@ -112,9 +124,11 @@ function placeUsers(usersArray) {
</div> </div>
</div> </div>
<div class="user-tautulli-servers">${tautulliServersString}</div> <div class="user-tautulli-servers">${tautulliServersString}</div>
<div class="user-ignore">
<input style="margin: auto;" class="clickable" type="checkbox" id="ignore_user_check_${user.user_id}" name="ignore_user_check_${user.user_id}" onclick="toggleUserIgnore('${user.user_id}', ${user.user_ignore})" value="" ${ignoredUserString}>
</div>
${historyDiv}
</div> </div>
${historyDiv}
</div> </div>
`; `;
@ -203,4 +217,42 @@ function syncTautulli() {
xhttp.setRequestHeader("Authorization", cookie); xhttp.setRequestHeader("Authorization", cookie);
xhttp.send(); xhttp.send();
return; return;
}
function toggleUserIgnore(userId, currentIgnoreValue) {
if(!confirm("Are you sure? The cache will be deleted for it to take effect.")) {
return;
}
var ignoreValue = !currentIgnoreValue
data_form = {
"user_ignore": ignoreValue
};
var data_obj = JSON.stringify(data_form);
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4) {
try {
var result= JSON.parse(this.responseText);
} catch(error) {
alert('Failed to parse API response.');
console.log('Failed to parse API response. Error: ' + this.responseText);
return;
}
if(result.error) {
document.getElementById("ignore_user_check_" + userId).checked = currentIgnoreValue;
alert(result.error);
}
}
};
xhttp.withCredentials = true;
xhttp.open("post", api_url + "get/users/" + userId + "/ignore");
xhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xhttp.setRequestHeader("Authorization", cookie);
xhttp.send(data_obj);
return;
} }