package routes import ( "aunefyren/wrapperr/files" "aunefyren/wrapperr/middlewares" "aunefyren/wrapperr/models" "aunefyren/wrapperr/modules" "aunefyren/wrapperr/utilities" "log" "net/http" "strconv" "strings" "time" "github.com/gin-gonic/gin" "github.com/google/uuid" ) // Create shareable link using Plex Auth func ApiCreateShareLink(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 } 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 } if !config.CreateShareLinks { log.Panicln("Shareable links are not enabled in the Wrapperr configuration.") context.JSON(http.StatusBadRequest, gin.H{"error": "Shareable links are not enabled in the 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 } var user_name string var user_id int // Try to authorize bearer token from header if config.PlexAuth { 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": err.Error()}) context.Abort() return } if err != nil || payload.Admin { log.Println("Either error or admin sessions.") context.JSON(http.StatusUnauthorized, gin.H{"error": "Failed to authorize request."}) context.Abort() return } else { plex_object, err := modules.PlexAuthValidateToken(payload.AuthToken, config.ClientKey, config.WrapperrVersion) if err != nil { log.Println("Could not validate Plex Auth login. Error: " + err.Error()) context.JSON(http.StatusUnauthorized, gin.H{"error": "Could not validate Plex Auth login."}) context.Abort() return } user_name = plex_object.Username user_id = plex_object.ID } } // Read payload from Post input var link_payload models.WrapperrShareLinkCreateRequest if err := context.ShouldBindJSON(&link_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 } currentTime := time.Now() hash_value := uuid.New().String() if !config.PlexAuth { user_name = link_payload.Data.User.Name user_id = 0 } link_object := models.WrapperrShareLink{ Content: link_payload, UserID: user_id, Hash: hash_value, Date: currentTime.Format("2006-01-02"), WrapperrVersion: config.WrapperrVersion, Expired: false, } err = files.SaveLink(&link_object, config.PlexAuth) if err != nil { log.Println("Failed to save new link. Error: " + err.Error()) context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save new link."}) context.Abort() return } ipString := utilities.GetOriginIPString(context) log.Println("Saved new Wrapperr share link for " + user_name + " (" + strconv.Itoa(user_id) + ")." + ipString) context.JSON(http.StatusCreated, gin.H{"message": "Saved Wrapperr link.", "error": false, "data": strconv.Itoa(user_id) + "-" + hash_value}) return } func ApiWrapperGetStatistics(context *gin.Context) { log.Println("New wrap 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 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 = "" var userId int = 0 var userEmail string = "" var userFriendlyName = "" var userActive = false // Try to authorize bearer token from header authorizationHeader := context.GetHeader("Authorization") payload, _, err := middlewares.AuthGetPayloadFromAuthorization(authorizationHeader, config, adminConfig) // If it failed and PlexAuth is enabled, respond with and error // If it didn't fail, and PlexAuth is enabled, declare auth as passed if err != nil && config.PlexAuth { log.Println("Failed to authorize authorization header. Error: " + err.Error()) context.JSON(http.StatusUnauthorized, gin.H{"error": "Failed to authorize request."}) context.Abort() return } else if payload.Admin && config.PlexAuth { context.JSON(http.StatusBadRequest, gin.H{"error": "Admin login cannot retrieve statistics."}) context.Abort() return } // If the user is not an admin, and PlexAuth is enabled, validate and retrieve details from Plex Token in payload if config.PlexAuth { plex_object, err := modules.PlexAuthValidateToken(payload.AuthToken, config.ClientKey, config.WrapperrVersion) if err != nil { log.Println("Could not validate Plex Auth login. Error: " + err.Error()) context.JSON(http.StatusUnauthorized, gin.H{"error": "Could not validate Plex Auth login."}) context.Abort() return } // Set user details from Plex login userName = plex_object.Username userId = plex_object.ID userEmail = plex_object.Email // 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 userFriendlyName = wrapperrUser.FriendlyName userActive = wrapperrUser.Active } // Read payload from Post input var wrapperr_request models.SearchWrapperrRequest if err := context.ShouldBindJSON(&wrapperr_request); 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 auth is not passed, caching mode is false, and no PlexIdentity was received, mark it as a bad request if wrapperr_request.PlexIdentity == "" && !config.PlexAuth { log.Println("Cannot retrieve statistics because search parameter is invalid.") context.JSON(http.StatusBadRequest, gin.H{"error": "Invalid search parameter."}) context.Abort() return } // If no auth has been passed, caching mode is false, and user is not admin, search for the Plex details using Tautulli and PlexIdentity if !config.PlexAuth { UserNameFound := false for i := 0; i < len(config.TautulliConfig); i++ { 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 userId = new_id break } } if !UserNameFound { log.Println("Failed to find user in Tautulli.") context.JSON(http.StatusInternalServerError, gin.H{"error": "Could not find a matching user."}) 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, email and no user_id has been declared at this point, and Plex Auth is active, something is wrong. Return error. if (userName == "" || userEmail == "" || userId == 0) && config.PlexAuth { log.Println("At this point the user should have been verified, but username, email or ID is empty.") context.JSON(http.StatusInternalServerError, gin.H{"error": "User validation error."}) context.Abort() return } else if userName == "" || userEmail == "" { log.Println("At this point the user should have been verified, but username or ID is empty.") context.JSON(http.StatusInternalServerError, gin.H{"error": "User validation error."}) context.Abort() return } if !userActive { context.JSON(http.StatusUnauthorized, gin.H{"error": "User is not active."}) context.Abort() return } wrapperrReply, _, err := modules.GetWrapperStatistics(userName, userFriendlyName, userId, userEmail, config, adminConfig, false, 0) 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 } userhistoryEntry := models.WrapperrHistoryEntry{ Date: time.Now(), IP: context.ClientIP(), } historyEntries := []models.WrapperrHistoryEntry{} historyEntries = append(historyEntries, userhistoryEntry) userEntry := models.WrapperrUser{ User: userName, UserID: userId, FriendlyName: userFriendlyName, Email: userEmail, Wrappings: historyEntries, } err = modules.UsersSaveUserEntry(userEntry) if err != nil { log.Println("Failed to save user history entry. Error: " + err.Error()) context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save user history entry."}) context.Abort() return } context.JSON(http.StatusOK, wrapperrReply) return }