mirror of
https://github.com/aunefyren/wrapperr
synced 2024-11-12 22:47:06 +00:00
492 lines
16 KiB
Go
492 lines
16 KiB
Go
package routes
|
|
|
|
import (
|
|
"aunefyren/wrapperr/files"
|
|
"aunefyren/wrapperr/models"
|
|
"aunefyren/wrapperr/modules"
|
|
"aunefyren/wrapperr/utilities"
|
|
"errors"
|
|
"log"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// API route which retrieves the Wrapperr version and some minor details (application name, Plex-Auth...).
|
|
func ApiGetWrapperrVersion(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
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
versionReply := models.WrapperrVersion{
|
|
WrapperrVersion: config.WrapperrVersion,
|
|
ApplicationName: config.ApplicationName,
|
|
PlexAuth: config.PlexAuth,
|
|
WrapperrFrontPageTitle: config.WrapperrCustomize.WrapperrFrontPageTitle,
|
|
WrapperrFrontPageSubtitle: config.WrapperrCustomize.WrapperrFrontPageSubtitle,
|
|
ClientKey: config.ClientKey,
|
|
WrapperrConfigured: configBool,
|
|
WinterTheme: config.WinterTheme,
|
|
Message: "Retrieved Wrapperr version.",
|
|
Error: false,
|
|
WrapperrRoot: config.WrapperrRoot,
|
|
BasicAuth: config.BasicAuth,
|
|
}
|
|
|
|
ipString := utilities.GetOriginIPString(context)
|
|
log.Println("Retrieved Wrapperr version." + ipString)
|
|
|
|
context.JSON(http.StatusOK, versionReply)
|
|
return
|
|
}
|
|
|
|
// API route which returns if whether or not a Wrapperr admin is configured.
|
|
func ApiGetAdminState(context *gin.Context) {
|
|
admin, err := files.GetAdminState()
|
|
if err != nil {
|
|
log.Println("Failed to load admin state. Error: " + err.Error())
|
|
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load admin state."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
|
|
ipString := utilities.GetOriginIPString(context)
|
|
log.Println("Retrieved Wrapperr admin state." + ipString)
|
|
|
|
context.JSON(http.StatusOK, gin.H{"message": "Retrieved Wrapperr version.", "error": false, "data": admin})
|
|
return
|
|
}
|
|
|
|
// API route which retrieves the Wrapperr settings needed for the front-end.
|
|
func ApiGetFunctions(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
|
|
}
|
|
|
|
functionReply := models.WrapperrFunctions{
|
|
WrapperrVersion: config.WrapperrVersion,
|
|
PlexAuth: config.PlexAuth,
|
|
WrapperrCustomize: config.WrapperrCustomize,
|
|
CreateShareLinks: config.CreateShareLinks,
|
|
}
|
|
|
|
ipString := utilities.GetOriginIPString(context)
|
|
log.Println("Retrieved Wrapperr functions." + ipString)
|
|
|
|
context.JSON(http.StatusOK, gin.H{"message": "Functions retrieved.", "data": functionReply})
|
|
return
|
|
}
|
|
|
|
// API route used to create the admin account and claim the Wrapperr server
|
|
func ApiCreateAdmin(context *gin.Context) {
|
|
admin, err := files.GetAdminState()
|
|
if err != nil {
|
|
log.Println("Failed to load admin state. Error: " + err.Error())
|
|
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load admin state."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
|
|
if admin {
|
|
log.Println("Admin creation failed. Admin already configured.")
|
|
context.JSON(http.StatusBadRequest, gin.H{"error": "Admin already configured."})
|
|
context.Abort()
|
|
return
|
|
} else {
|
|
// Read payload from Post input
|
|
var admin_payload models.AdminConfig
|
|
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
|
|
}
|
|
|
|
// 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 {
|
|
errors.New("Admin creation failed. Could not hash new password. Error: " + err.Error())
|
|
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to hash your password."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
admin_payload.AdminPassword = hash
|
|
|
|
// Save new admin config
|
|
err = files.SaveAdminConfig(admin_payload)
|
|
if err != nil {
|
|
errors.New("Admin creation failed. Could not save configuration. Error: " + err.Error())
|
|
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save new admin."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
|
|
log.Println("New admin account created. Server is now claimed.")
|
|
|
|
context.JSON(http.StatusCreated, gin.H{"message": "Admin created."})
|
|
return
|
|
}
|
|
}
|
|
|
|
// API route which returns if whether or not Wrapperr is configured.
|
|
func ApiWrapperrConfigured(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 {
|
|
ipString := utilities.GetOriginIPString(context)
|
|
log.Println("Retrieved Wrapperr configuration state." + ipString)
|
|
|
|
context.JSON(http.StatusOK, gin.H{"message": "Retrieved Wrapperr configuration state.", "error": false, "data": configBool})
|
|
return
|
|
}
|
|
}
|
|
|
|
// API route which trades admin login credentials for an admin JWT session token. Valid for three days.
|
|
func ApiLogInAdmin(context *gin.Context) {
|
|
admin, err := files.GetAdminState()
|
|
if err != nil {
|
|
log.Println("Failed to load admin state. Error: " + err.Error())
|
|
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load admin state."})
|
|
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 !admin {
|
|
log.Println("Admin login failed. Admin is not configured.")
|
|
context.JSON(http.StatusBadRequest, gin.H{"error": "Admin login failed. Admin is not configured."})
|
|
context.Abort()
|
|
return
|
|
} else {
|
|
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 username string
|
|
var password string
|
|
|
|
if !config.BasicAuth {
|
|
// Read payload from Post input
|
|
var admin_payload models.AdminConfig
|
|
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
|
|
}
|
|
|
|
username = admin_payload.AdminUsername
|
|
password = admin_payload.AdminPassword
|
|
|
|
// Hash new password
|
|
passwordValidity := utilities.ComparePasswords(adminConfig.AdminPassword, password)
|
|
|
|
// Validate admin username and password
|
|
if !passwordValidity || adminConfig.AdminUsername != username {
|
|
ipString := utilities.GetOriginIPString(context)
|
|
log.Println("Admin login failed. Incorrect admin username or password." + ipString)
|
|
context.JSON(http.StatusUnauthorized, gin.H{"error": "Login failed. Username or password is incorrect."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
} else {
|
|
err = utilities.ValidateBasicAuth(context, adminConfig)
|
|
if err != nil {
|
|
log.Println("Validate Basic auth. Error: " + err.Error())
|
|
context.JSON(http.StatusBadRequest, gin.H{"error": "Failed to validate HTTP Basic. You might need to restart your browser."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
}
|
|
|
|
token, err := modules.CreateToken(adminConfig.AdminUsername, true, "")
|
|
if err != nil {
|
|
log.Println("Failed to create JWT token. Error: " + err.Error())
|
|
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create JWT token."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
|
|
ipString := utilities.GetOriginIPString(context)
|
|
log.Println("Created and retrieved admin login JWT Token." + ipString)
|
|
|
|
context.JSON(http.StatusOK, gin.H{"message": "Login cookie created.", "error": false, "data": token})
|
|
return
|
|
}
|
|
}
|
|
|
|
// APi route which trades admin login credentials for an admin JWT session token. Valid for three days.
|
|
func ApiGetTautulliConncection(context *gin.Context) {
|
|
// Read payload from Post input
|
|
var tautulli_connection models.TautulliConfig
|
|
if err := context.ShouldBindJSON(&tautulli_connection); 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 tautulli_connection.TautulliApiKey == "" || tautulli_connection.TautulliIP == "" || tautulli_connection.TautulliPort == 0 {
|
|
log.Println("Cannot test Tautulli connection. Invalid Tautulli connection details received.")
|
|
context.JSON(http.StatusBadRequest, gin.H{"error": "Tautulli connection details specified are invalid."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
|
|
tautulli_state, err := modules.TautulliTestConnection(tautulli_connection.TautulliPort, tautulli_connection.TautulliIP, tautulli_connection.TautulliHttps, tautulli_connection.TautulliRoot, tautulli_connection.TautulliApiKey)
|
|
if err != nil {
|
|
context.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
context.Abort()
|
|
return
|
|
}
|
|
|
|
booleanReply := models.BooleanReply{
|
|
Message: "Tested Tautulli connection.",
|
|
Error: false,
|
|
Data: tautulli_state,
|
|
}
|
|
|
|
ipString := utilities.GetOriginIPString(context)
|
|
log.Println("Tested Tautulli connection." + ipString)
|
|
|
|
context.JSON(http.StatusOK, booleanReply)
|
|
return
|
|
}
|
|
|
|
// Get shareable link
|
|
func ApiGetShareLink(context *gin.Context) {
|
|
config_bool, 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 !config_bool {
|
|
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.Println("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
|
|
}
|
|
|
|
// Read payload from Post input
|
|
var link_payload models.WrapperrShareLinkGetRequest
|
|
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
|
|
}
|
|
|
|
var fileName string
|
|
var hash string
|
|
|
|
hash_array := strings.Split(link_payload.Hash, "-")
|
|
|
|
if len(hash_array) < 2 {
|
|
log.Println("Failed to split hash string while looking for user ID.")
|
|
context.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid shared link."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
|
|
if config.PlexAuth {
|
|
for j := 1; j < len(hash_array); j++ {
|
|
if j != 1 {
|
|
hash = hash + "-"
|
|
}
|
|
hash = hash + hash_array[j]
|
|
}
|
|
|
|
fileName = hash_array[0]
|
|
} else {
|
|
for j := 1; j < len(hash_array); j++ {
|
|
if j != 1 {
|
|
hash = hash + "-"
|
|
}
|
|
hash = hash + hash_array[j]
|
|
}
|
|
|
|
fileName = hash
|
|
}
|
|
|
|
share_link_object, err := files.GetLink(fileName)
|
|
if err != nil {
|
|
log.Println("Failed to get link file. Error: " + err.Error())
|
|
context.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid shared link."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
|
|
currentTime := time.Now()
|
|
linkTime, err := time.Parse("2006-01-02", share_link_object.Date)
|
|
if err != nil {
|
|
log.Println("Failed to parse link date. Error: " + err.Error())
|
|
context.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid shared link."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
|
|
linkTime = linkTime.Add(7 * 24 * time.Hour)
|
|
|
|
if !linkTime.Before(currentTime) && share_link_object.Hash == hash && !share_link_object.Expired {
|
|
share_link_object.Message = "Shared link retrieved."
|
|
share_link_object.Error = false
|
|
|
|
ipString := utilities.GetOriginIPString(context)
|
|
log.Println("Retrieved Wrapperr share link made by User ID/with hash: " + fileName + "." + ipString)
|
|
|
|
context.JSON(http.StatusCreated, share_link_object)
|
|
return
|
|
} else {
|
|
returnError := errors.New("Invalid shared link.")
|
|
|
|
if linkTime.Before(currentTime) {
|
|
|
|
share_link_object.Expired = true
|
|
err = files.SaveLink(share_link_object, config.PlexAuth)
|
|
if err != nil {
|
|
log.Println("Failed to save share link. Error: " + err.Error())
|
|
}
|
|
returnError = errors.New("This shared link has expired.")
|
|
}
|
|
|
|
log.Println("Failed to retrieve Wrapperr share link with hash: " + link_payload.Hash + ".")
|
|
|
|
context.JSON(http.StatusBadRequest, returnError)
|
|
return
|
|
}
|
|
}
|
|
|
|
func ApiLoginPlexAuth(context *gin.Context) {
|
|
config_bool, 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 !config_bool {
|
|
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
|
|
}
|
|
|
|
// Read payload from Post input
|
|
var payload models.LoginPlexAuth
|
|
if err := context.ShouldBindJSON(&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 payload.ID == 0 || payload.Code == "" {
|
|
log.Println("Cannot retrieve Plex Auth login state. Invalid ID or Code received.")
|
|
context.JSON(http.StatusUnauthorized, gin.H{"error": "Login ID and/or Code is invalid."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
|
|
plex_auth, err := modules.GetPlexAuthLogin(payload.ID, payload.Code, config.WrapperrVersion, config.ClientKey)
|
|
if err != nil {
|
|
log.Println("Failed to retrieve Plex Auth pin. Error: " + err.Error())
|
|
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to retrieve Plex Auth pin."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
|
|
if plex_auth.AuthToken == "" {
|
|
log.Println("Plex Auth response invalid. No Authtoken received.")
|
|
context.JSON(http.StatusUnauthorized, gin.H{"error": "Plex Auth response invalid."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
|
|
token, err := modules.CreateToken("Plex Auth", false, plex_auth.AuthToken)
|
|
if err != nil {
|
|
log.Println("Failed to create JWT token. Error: " + err.Error())
|
|
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create JWT token."})
|
|
context.Abort()
|
|
return
|
|
}
|
|
|
|
string_reply := models.StringReply{
|
|
Message: "Login cookie created",
|
|
Error: false,
|
|
Data: token,
|
|
}
|
|
|
|
ipString := utilities.GetOriginIPString(context)
|
|
log.Println("Created and retrieved Plex Auth login JWT Token." + ipString)
|
|
|
|
context.JSON(http.StatusCreated, string_reply)
|
|
return
|
|
}
|