mirror of
https://github.com/aunefyren/wrapperr
synced 2025-01-10 00:38:41 +00:00
80720039e8
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
454 lines
16 KiB
Go
454 lines
16 KiB
Go
package routes
|
|
|
|
import (
|
|
"aunefyren/wrapperr/files"
|
|
"aunefyren/wrapperr/middlewares"
|
|
"aunefyren/wrapperr/models"
|
|
"aunefyren/wrapperr/modules"
|
|
"aunefyren/wrapperr/utilities"
|
|
"log"
|
|
"net/http"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// API route used to retrieve the Wrapperr configuration file.
|
|
func ApiGetConfig(context *gin.Context) {
|
|
config, err := files.GetConfig()
|
|
if err != nil {
|
|
log.Println("Failed to load Wrapperr configuration. Error: " + err.Error())
|
|
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load Wrapperr configuration."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
|
|
adminConfig, err := files.GetAdminConfig()
|
|
if err != nil {
|
|
log.Println("Failed to load Wrapperr admin configuration. Error: " + err.Error())
|
|
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load Wrapperr admin configuration."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
|
|
authorizationHeader := context.GetHeader("Authorization")
|
|
payload, httpStatus, err := middlewares.AuthGetPayloadFromAuthorization(authorizationHeader, config, adminConfig)
|
|
if err != nil {
|
|
log.Println("Failed to get payload from Authorization token. Error: " + err.Error())
|
|
context.JSON(httpStatus, gin.H{"error": "Failed to get payload from Authorization token."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
|
|
ipString := utilities.GetOriginIPString(context)
|
|
log.Println("Retrieved Wrapperr configuration." + ipString)
|
|
|
|
context.JSON(http.StatusOK, gin.H{"data": config, "message": "Retrieved Wrapperr config.", "username": payload.Username})
|
|
return
|
|
}
|
|
|
|
// API route used to update the Wrapperr configuration file.
|
|
func ApiSetConfig(context *gin.Context) {
|
|
config, err := files.GetConfig()
|
|
if err != nil {
|
|
log.Println("Failed to load Wrapperr configuration. Error: " + err.Error())
|
|
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load Wrapperr configuration."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
|
|
originalRoot := config.WrapperrRoot
|
|
|
|
// Read payload from Post input
|
|
var config_payload models.SetWrapperrConfig
|
|
if err := context.ShouldBindJSON(&config_payload); 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
|
|
}
|
|
|
|
// Confirm username length
|
|
if config_payload.DataType == "" {
|
|
log.Println("Cannot set new config. Invalid data type received.")
|
|
context.JSON(http.StatusBadRequest, gin.H{"error": "Data type specified is invalid."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
|
|
if config_payload.DataType == "tautulli_config" {
|
|
config.TautulliConfig = config_payload.TautulliConfig
|
|
|
|
err = files.SaveConfig(config)
|
|
if err != nil {
|
|
log.Println("Failed to save new Wrapperr configuration. Error: " + err.Error())
|
|
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save new Wrapperr configuration."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
} else if config_payload.DataType == "wrapperr_customize" {
|
|
config.WrapperrCustomize = config_payload.WrapperrCustomize
|
|
|
|
err = files.SaveConfig(config)
|
|
if err != nil {
|
|
log.Println("Failed to save new Wrapperr configuration. Error: " + err.Error())
|
|
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save new Wrapperr configuration."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
} else if config_payload.DataType == "wrapperr_data" {
|
|
new_time, err := time.LoadLocation(config_payload.WrapperrData.Timezone)
|
|
if err != nil {
|
|
log.Println("Failed to set the new time zone. Error: " + err.Error())
|
|
context.JSON(http.StatusBadRequest, gin.H{"error": "Given time zone is invalid."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
|
|
config.UseCache = config_payload.WrapperrData.UseCache
|
|
config.UseLogs = config_payload.WrapperrData.UseLogs
|
|
config.PlexAuth = config_payload.WrapperrData.PlexAuth
|
|
config.BasicAuth = config_payload.WrapperrData.BasicAuth
|
|
config.WrapperrRoot = config_payload.WrapperrData.WrapperrRoot
|
|
config.CreateShareLinks = config_payload.WrapperrData.CreateShareLinks
|
|
config.Timezone = config_payload.WrapperrData.Timezone
|
|
config.ApplicationName = config_payload.WrapperrData.ApplicationName
|
|
config.ApplicationURL = config_payload.WrapperrData.ApplicationURL
|
|
config.WrappedEnd = config_payload.WrapperrData.WrappedEnd
|
|
config.WrappedStart = config_payload.WrapperrData.WrappedStart
|
|
config.WinterTheme = config_payload.WrapperrData.WinterTheme
|
|
|
|
err = files.SaveConfig(config)
|
|
if err != nil {
|
|
log.Println("Failed to save new Wrapperr configuration. Error: " + err.Error())
|
|
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save new Wrapperr configuration."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
|
|
time.Local = new_time
|
|
} else {
|
|
log.Println("Cannot set new config. Invalid data type received. Type: " + config_payload.DataType)
|
|
context.JSON(http.StatusBadRequest, gin.H{"error": "Failed to save new Wrapperr configuration."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
|
|
if config_payload.ClearCache {
|
|
log.Println("Clear cache setting set to true. Clearing cache.")
|
|
|
|
err = files.ClearCache()
|
|
if err != nil {
|
|
log.Println("Failed to clear cache. Error: " + err.Error())
|
|
}
|
|
}
|
|
|
|
log.Println("New Wrapperr configuration saved for type: " + config_payload.DataType + ".")
|
|
context.JSON(http.StatusOK, gin.H{"message": "Saved new Wrapperr config."})
|
|
|
|
if config.WrapperrRoot != originalRoot {
|
|
log.Println("Root changed, attempting Wrapperr restart.")
|
|
err = utilities.RestartSelf()
|
|
if err != nil {
|
|
log.Println("Failed to restart. Error: " + err.Error())
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// API route used to update admin accounts details (username, password).
|
|
func ApiUpdateAdmin(context *gin.Context) {
|
|
adminConfig, err := files.GetAdminConfig()
|
|
if err != nil {
|
|
log.Println("Failed to load Wrapperr admin configuration. Error: " + err.Error())
|
|
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load Wrapperr admin configuration."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
|
|
// Read payload from Post input
|
|
var admin_payload models.AdminConfigUpdateRequest
|
|
if err := context.ShouldBindJSON(&admin_payload); 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
|
|
}
|
|
|
|
// Test old password
|
|
passwordValidity := utilities.ComparePasswords(adminConfig.AdminPassword, admin_payload.AdminPasswordOriginal)
|
|
if !passwordValidity {
|
|
ipString := utilities.GetOriginIPString(context)
|
|
log.Println("Admin login update failed. Incorrect password." + ipString)
|
|
context.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid credentials."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
|
|
// Confirm username length
|
|
if len(admin_payload.AdminUsername) < 4 {
|
|
context.JSON(http.StatusBadRequest, gin.H{"error": "Admin username is too short. Four characters or more required."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
|
|
// Confirm password length
|
|
if len(admin_payload.AdminPassword) < 8 {
|
|
context.JSON(http.StatusBadRequest, gin.H{"error": "Admin password is too short. Eight characters or more required."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
|
|
// Hash new password
|
|
hash, err := utilities.HashAndSalt(admin_payload.AdminPassword)
|
|
if err != nil {
|
|
log.Println("Failed to hash your password. Error: " + err.Error())
|
|
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to hash your password."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
|
|
adminConfig.AdminPassword = hash
|
|
|
|
// Save new admin config
|
|
err = files.SaveAdminConfig(adminConfig)
|
|
if err != nil {
|
|
log.Println("Failed to update admin. Error: " + err.Error())
|
|
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update admin."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
|
|
// Update the private key to delete old logins
|
|
_, err = files.UpdatePrivateKey()
|
|
if err != nil {
|
|
log.Println("Failed to update private key. Error: " + err.Error())
|
|
log.Println("Admin account updated, but failed to rotate private key. Old logins still function.")
|
|
context.JSON(http.StatusInternalServerError, gin.H{"error": "Admin account updated, but failed to rotate private key. Old logins still function."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
|
|
log.Println("New admin account created. Server is now claimed.")
|
|
|
|
context.JSON(http.StatusOK, gin.H{"message": "Admin created."})
|
|
return
|
|
}
|
|
|
|
// API route which validates an admin JWT token
|
|
func ApiValidateAdmin(context *gin.Context) {
|
|
log.Println("Admin login session JWT validated.")
|
|
context.JSON(http.StatusOK, gin.H{"message": "Admin session is valid."})
|
|
}
|
|
|
|
// API route which retrieves lines from the log file
|
|
func ApiGetLog(context *gin.Context) {
|
|
logLines, err := files.GetLogLines()
|
|
if err != nil {
|
|
log.Println("Error trying to retrieve log lines. Error: " + err.Error())
|
|
context.JSON(http.StatusInternalServerError, gin.H{"error": "Error trying to retrieve log lines."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
|
|
log.Println("Log lines retrieved for admin.")
|
|
context.JSON(http.StatusOK, gin.H{"message": "Log lines retrieved", "data": logLines, "limit": files.GetMaxLogLinesReturned()})
|
|
return
|
|
}
|
|
|
|
// API route to get all timezones
|
|
func ApiGetTimezones(context *gin.Context) {
|
|
timezones, err := files.GetTimezones()
|
|
if err != nil {
|
|
log.Println("Error trying to get timezones. Error: " + err.Error())
|
|
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get timezones."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
|
|
context.JSON(http.StatusOK, gin.H{"message": "Timezones received.", "error": false, "data": timezones.Timezones})
|
|
return
|
|
}
|
|
|
|
func ApiWrapperCacheStatistics(context *gin.Context) {
|
|
log.Println("New caching request.")
|
|
|
|
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
|
|
}
|
|
|
|
config, err := files.GetConfig()
|
|
if err != nil {
|
|
log.Println("Failed to load Wrapperr configuration. Error: " + err.Error())
|
|
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load Wrapperr configuration."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
|
|
adminConfig, err := files.GetAdminConfig()
|
|
if err != nil {
|
|
log.Println("Failed to load Wrapperr admin configuration. Error: " + err.Error())
|
|
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load Wrapperr admin configuration."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
|
|
// 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
|
|
}
|
|
}
|
|
|
|
var userName string = "Caching mode"
|
|
var userId int = 0
|
|
var userEmail string = "N/A"
|
|
var userFriendlyName = "Caching mode"
|
|
|
|
// Read payload from Post input
|
|
var cachingRequest models.CacheWrapperrRequest
|
|
if err := context.ShouldBindJSON(&cachingRequest); 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
|
|
}
|
|
|
|
if cachingRequest.CacheLimit < 1 {
|
|
context.JSON(http.StatusBadRequest, gin.H{"error": "Chache limit too low."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
|
|
_, cachingComplete, err := modules.GetWrapperStatistics(userName, userFriendlyName, userId, userEmail, config, adminConfig, true, cachingRequest.CacheLimit)
|
|
if err != nil {
|
|
log.Println("Failed to get statistics. Error: " + err.Error())
|
|
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get statistics."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
|
|
context.JSON(http.StatusOK, gin.H{"message": "Completed caching request.", "error": false, "data": *cachingComplete})
|
|
return
|
|
}
|
|
|
|
func ApiGetUsers(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
|
|
}
|
|
|
|
users, err := files.GetUsers()
|
|
if err != nil {
|
|
log.Println("Failed to get users. Error: " + err.Error())
|
|
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get users."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
|
|
context.JSON(http.StatusOK, gin.H{"message": "Users recieved.", "data": users})
|
|
return
|
|
}
|
|
|
|
func ApiGetUser(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
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
context.JSON(http.StatusOK, gin.H{"message": "Users recieved.", "data": user})
|
|
return
|
|
}
|
|
|
|
func ApiSyncTautulliUsers(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
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
newusers, err := files.GetUsers()
|
|
if err != nil {
|
|
log.Println("Failed to new users. Error: " + err.Error())
|
|
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to new users."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
|
|
context.JSON(http.StatusOK, gin.H{"message": "Users synced.", "data": newusers})
|
|
return
|
|
}
|