mirror of
https://github.com/aunefyren/wrapperr
synced 2025-01-23 22:45:01 +00:00
Ignore users
Admin panel css tweaks
This commit is contained in:
parent
eaab43f8e9
commit
74db0ebd7b
9 changed files with 190 additions and 9 deletions
1
main.go
1
main.go
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"`
|
||||||
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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.")
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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,10 +124,12 @@ 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>
|
</div>
|
||||||
|
|
||||||
${historyDiv}
|
${historyDiv}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
userModule.innerHTML += html;
|
userModule.innerHTML += html;
|
||||||
|
@ -204,3 +218,41 @@ function syncTautulli() {
|
||||||
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;
|
||||||
|
}
|
Loading…
Reference in a new issue