mirror of
https://github.com/aunefyren/wrapperr
synced 2024-12-13 21:02:28 +00:00
448 lines
12 KiB
Go
448 lines
12 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"io/ioutil"
|
||
|
"log"
|
||
|
"net/http"
|
||
|
"strings"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
// API route which retrieves the Wrapperr version and some minor details (application name, Plex-Auth...).
|
||
|
func ApiGetWrapperrVersion(w http.ResponseWriter, r *http.Request) {
|
||
|
|
||
|
configured_bool, err := GetConfigState()
|
||
|
|
||
|
if err != nil {
|
||
|
|
||
|
log.Println("Failed to retrieve configuration state. Error: ")
|
||
|
log.Println(err)
|
||
|
respond_default_error(w, r, errors.New("Failed to retrieve configuration state."), 500)
|
||
|
return
|
||
|
|
||
|
}
|
||
|
|
||
|
config, err := GetConfig()
|
||
|
if err != nil {
|
||
|
log.Println("Failed to load configuration file. Error: ")
|
||
|
log.Println(err)
|
||
|
respond_default_error(w, r, errors.New("Failed to retrieve configuration state."), 500)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
version_reply := WrapperrVersion{
|
||
|
WrapperrVersion: config.WrapperrVersion,
|
||
|
ApplicationName: config.ApplicationName,
|
||
|
PlexAuth: config.PlexAuth,
|
||
|
WrapperrFrontPageTitle: config.WrapperrCustomize.WrapperrFrontPageTitle,
|
||
|
WrapperrFrontPageSubtitle: config.WrapperrCustomize.WrapperrFrontPageSubtitle,
|
||
|
ClientKey: config.ClientKey,
|
||
|
WrapperrConfigured: configured_bool,
|
||
|
WinterTheme: config.WinterTheme,
|
||
|
Message: "Retrieved Wrapperr version.",
|
||
|
Error: false,
|
||
|
}
|
||
|
|
||
|
ip_string := GetOriginIPString(w, r)
|
||
|
|
||
|
log.Println("Retrieved Wrapperr version." + ip_string)
|
||
|
|
||
|
respondWithJSON(w, http.StatusOK, version_reply)
|
||
|
return
|
||
|
|
||
|
}
|
||
|
|
||
|
// API route which returns if whether or not a Wrapperr admin is configured.
|
||
|
func ApiGetAdminState(w http.ResponseWriter, r *http.Request) {
|
||
|
|
||
|
admin, err := GetAdminState()
|
||
|
if err != nil {
|
||
|
log.Println(err)
|
||
|
log.Println("Failed to load admin state.")
|
||
|
return
|
||
|
}
|
||
|
|
||
|
boolean_reply := BooleanReply{
|
||
|
Message: "Retrieved Wrapperr version.",
|
||
|
Error: false,
|
||
|
Data: admin,
|
||
|
}
|
||
|
|
||
|
ip_string := GetOriginIPString(w, r)
|
||
|
|
||
|
log.Println("Retrieved Wrapperr admin state." + ip_string)
|
||
|
|
||
|
respondWithJSON(w, http.StatusOK, boolean_reply)
|
||
|
return
|
||
|
|
||
|
}
|
||
|
|
||
|
// API route which retrieves the Wrapperr settings needed for the front-end.
|
||
|
func ApiGetFunctions(w http.ResponseWriter, r *http.Request) {
|
||
|
|
||
|
config, err := GetConfig()
|
||
|
if err != nil {
|
||
|
log.Println(err)
|
||
|
log.Println("Failed to load configuration file.")
|
||
|
fmt.Println("Failed to load configuration file.")
|
||
|
return
|
||
|
}
|
||
|
|
||
|
function_reply := WrapperrFunctions{
|
||
|
WrapperrVersion: config.WrapperrVersion,
|
||
|
PlexAuth: config.PlexAuth,
|
||
|
WrapperrCustomize: config.WrapperrCustomize,
|
||
|
CreateShareLinks: config.CreateShareLinks,
|
||
|
}
|
||
|
|
||
|
ip_string := GetOriginIPString(w, r)
|
||
|
|
||
|
log.Println("Retrieved Wrapperr functions." + ip_string)
|
||
|
|
||
|
respondWithJSON(w, http.StatusOK, function_reply)
|
||
|
return
|
||
|
|
||
|
}
|
||
|
|
||
|
// API route used to create the admin account and claim the Wrapperr server
|
||
|
func ApiCreateAdmin(w http.ResponseWriter, r *http.Request) {
|
||
|
|
||
|
admin, err := GetAdminState()
|
||
|
if err != nil {
|
||
|
log.Println(err)
|
||
|
log.Println("Failed to load admin state.")
|
||
|
fmt.Println("Failed to load admin state.")
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if admin {
|
||
|
log.Println("Admin creation failed. Admin already configured.")
|
||
|
respond_default_error(w, r, errors.New("Admin already configured."), 401)
|
||
|
return
|
||
|
} else {
|
||
|
|
||
|
// Read payload from Post input
|
||
|
reqBody, _ := ioutil.ReadAll(r.Body)
|
||
|
var admin_payload AdminConfig
|
||
|
json.Unmarshal(reqBody, &admin_payload)
|
||
|
|
||
|
// Confirm username length
|
||
|
if len(admin_payload.AdminUsername) < 4 {
|
||
|
log.Println("Admin creation failed. Admin username requires four or more characters.")
|
||
|
respond_default_error(w, r, errors.New("Admin username is too short. Four characters or more required."), 500)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Confirm password length
|
||
|
if len(admin_payload.AdminPassword) < 8 {
|
||
|
log.Println("Admin creation failed. Admin password requires eight or more characters.")
|
||
|
respond_default_error(w, r, errors.New("Admin password is too short. Eight characters or more required."), 500)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Hash new password
|
||
|
hash, err := hashAndSalt(admin_payload.AdminPassword)
|
||
|
if err != nil {
|
||
|
errors.New("Admin creation failed. Could not hash new password. Error: ")
|
||
|
log.Println(err)
|
||
|
respond_default_error(w, r, errors.New("Failed to hash your password."), 500)
|
||
|
return
|
||
|
}
|
||
|
admin_payload.AdminPassword = hash
|
||
|
|
||
|
// Save new admin config
|
||
|
err = SaveAdminConfig(admin_payload)
|
||
|
if err != nil {
|
||
|
errors.New("Admin creation failed. Could not save configuration. Error: ")
|
||
|
log.Println(err)
|
||
|
respond_default_error(w, r, errors.New("Failed to save new admin."), 500)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
log.Println("New admin account created. Server is now claimed.")
|
||
|
fmt.Println("New admin account created. Server is now claimed.")
|
||
|
|
||
|
respond_default_okay(w, r, "Admin created.")
|
||
|
return
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// API route which returns if whether or not Wrapperr is configured.
|
||
|
func ApiWrapperrConfigured(w http.ResponseWriter, r *http.Request) {
|
||
|
|
||
|
bool, err := GetConfigState()
|
||
|
|
||
|
if err != nil {
|
||
|
|
||
|
log.Panicln(err)
|
||
|
respond_default_error(w, r, errors.New("Failed to retrieve confguration state."), 500)
|
||
|
return
|
||
|
|
||
|
} else {
|
||
|
|
||
|
boolean_reply := BooleanReply{
|
||
|
Message: "Retrieved Wrapperr configuration state.",
|
||
|
Error: false,
|
||
|
Data: bool,
|
||
|
}
|
||
|
|
||
|
ip_string := GetOriginIPString(w, r)
|
||
|
|
||
|
log.Println("Retrieved Wrapperr configuration state." + ip_string)
|
||
|
|
||
|
respondWithJSON(w, http.StatusOK, boolean_reply)
|
||
|
return
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
// API route which trades admin login credentials for an admin JWT session token. Valid for three days.
|
||
|
func ApiLogInAdmin(w http.ResponseWriter, r *http.Request) {
|
||
|
|
||
|
admin, err := GetAdminState()
|
||
|
if err != nil {
|
||
|
log.Println(err)
|
||
|
log.Println("Failed to load admin state.")
|
||
|
fmt.Println("Failed to load admin state.")
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if !admin {
|
||
|
log.Println("Admin login failed. Admin is not configured.")
|
||
|
respond_default_error(w, r, errors.New("No admin configured."), 400)
|
||
|
return
|
||
|
} else {
|
||
|
|
||
|
admin_config, err := GetAdminConfig()
|
||
|
if err != nil {
|
||
|
log.Println(err)
|
||
|
log.Println("Failed to load admin config.")
|
||
|
fmt.Println("Failed to load admin config.")
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Read payload from Post input
|
||
|
reqBody, _ := ioutil.ReadAll(r.Body)
|
||
|
var admin_payload AdminConfig
|
||
|
json.Unmarshal(reqBody, &admin_payload)
|
||
|
|
||
|
// Confirm username length
|
||
|
if len(admin_payload.AdminUsername) < 4 {
|
||
|
log.Println("Admin creation failed. Admin username requires four or more characters.")
|
||
|
respond_default_error(w, r, errors.New("Admin username is too short. Four characters or more required."), 500)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Confirm password length
|
||
|
if len(admin_payload.AdminPassword) < 8 {
|
||
|
log.Println("Admin creation failed. Admin password requires eight or more characters.")
|
||
|
respond_default_error(w, r, errors.New("Admin password is too short. Eight characters or more required."), 500)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Hash new password
|
||
|
password_validity := comparePasswords(admin_config.AdminPassword, admin_payload.AdminPassword)
|
||
|
|
||
|
// Validate admin username and password
|
||
|
if !password_validity || admin_config.AdminUsername != admin_payload.AdminUsername {
|
||
|
ip_string := GetOriginIPString(w, r)
|
||
|
log.Println("Admin login failed. Incorrect admin username or password." + ip_string)
|
||
|
fmt.Println("Admin login failed. Incorrect admin username or password." + ip_string)
|
||
|
respond_default_error(w, r, errors.New("Login failed. Username or password is incorrect."), 401)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
token, err := CreateToken(admin_config.AdminUsername, true, "")
|
||
|
if err != nil {
|
||
|
log.Println(err)
|
||
|
respond_default_error(w, r, errors.New("Failed to create JWT token."), 500)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
string_reply := StringReply{
|
||
|
Message: "Login cookie created",
|
||
|
Error: false,
|
||
|
Data: token,
|
||
|
}
|
||
|
|
||
|
ip_string := GetOriginIPString(w, r)
|
||
|
|
||
|
log.Println("Created and retrieved admin login JWT Token." + ip_string)
|
||
|
|
||
|
fmt.Println("Created and retrieved admin login JWT Token." + ip_string)
|
||
|
|
||
|
respondWithJSON(w, http.StatusOK, string_reply)
|
||
|
return
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
// APi route which trades admin login credentials for an admin JWT session token. Valid for three days.
|
||
|
func ApiGetTautulliConncection(w http.ResponseWriter, r *http.Request) {
|
||
|
|
||
|
// Read payload from Post input
|
||
|
reqBody, _ := ioutil.ReadAll(r.Body)
|
||
|
var tautulli_connection TautulliConfig
|
||
|
json.Unmarshal(reqBody, &tautulli_connection)
|
||
|
|
||
|
if tautulli_connection.TautulliApiKey == "" || tautulli_connection.TautulliIP == "" || tautulli_connection.TautulliPort == 0 {
|
||
|
log.Println("Cannot test Tautulli connection. Invalid Tautulli connection details recieved.")
|
||
|
respond_default_error(w, r, errors.New("Tautulli connection details specified are invalid."), 400)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
tautulli_state, err := TautulliTestConnection(tautulli_connection.TautulliPort, tautulli_connection.TautulliIP, tautulli_connection.TautulliHttps, tautulli_connection.TautulliRoot, tautulli_connection.TautulliApiKey)
|
||
|
if err != nil {
|
||
|
log.Println(err)
|
||
|
respond_default_error(w, r, errors.New("Failed to reach Tautulli server."), 500)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
boolean_reply := BooleanReply{
|
||
|
Message: "Tested Tautulli connection.",
|
||
|
Error: false,
|
||
|
Data: tautulli_state,
|
||
|
}
|
||
|
|
||
|
ip_string := GetOriginIPString(w, r)
|
||
|
|
||
|
log.Println("Tested Tautulli connection." + ip_string)
|
||
|
|
||
|
respondWithJSON(w, http.StatusOK, boolean_reply)
|
||
|
return
|
||
|
|
||
|
}
|
||
|
|
||
|
// Get shareable link
|
||
|
func ApiGetShareLink(w http.ResponseWriter, r *http.Request) {
|
||
|
|
||
|
config_bool, err := GetConfigState()
|
||
|
|
||
|
if err != nil {
|
||
|
|
||
|
log.Println("Failed to retrieve configuration state. Error: ")
|
||
|
log.Println(err)
|
||
|
respond_default_error(w, r, errors.New("Failed to retrieve configuration state"), 500)
|
||
|
return
|
||
|
|
||
|
} else if !config_bool {
|
||
|
|
||
|
log.Println("Wrapperr is not configured.")
|
||
|
respond_default_error(w, r, errors.New("Wrapperr is not configured."), 400)
|
||
|
return
|
||
|
|
||
|
}
|
||
|
|
||
|
config, err := GetConfig()
|
||
|
if err != nil {
|
||
|
log.Println(err)
|
||
|
respond_default_error(w, r, errors.New("Failed to load Wrapperr configuration."), 500)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if !config.PlexAuth {
|
||
|
log.Println("Plex Auth is not enabled in the Wrapperr configuration.")
|
||
|
respond_default_error(w, r, errors.New("Plex Auth is not enabled in the Wrapperr configuration."), 400)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if !config.CreateShareLinks {
|
||
|
log.Println("Shareable links are not enabled in the Wrapperr configuration.")
|
||
|
respond_default_error(w, r, errors.New("Shareable links are not enabled in the Wrapperr configuration."), 400)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Read payload from Post input
|
||
|
reqBody, err := ioutil.ReadAll(r.Body)
|
||
|
if err != nil {
|
||
|
|
||
|
log.Println(err)
|
||
|
respond_default_error(w, r, errors.New("Failed to parse payload for request."), 500)
|
||
|
return
|
||
|
|
||
|
}
|
||
|
var link_payload WrapperrShareLinkGetRequest
|
||
|
json.Unmarshal(reqBody, &link_payload)
|
||
|
|
||
|
hash_array := strings.Split(link_payload.Hash, "-")
|
||
|
|
||
|
if len(hash_array) < 2 {
|
||
|
|
||
|
log.Println("Failed to split hash string while looking for user ID.")
|
||
|
respond_default_error(w, r, errors.New("Failed to parse payload hash for Wrapperr link."), 500)
|
||
|
return
|
||
|
|
||
|
}
|
||
|
|
||
|
user_id := hash_array[0]
|
||
|
hash := ""
|
||
|
|
||
|
for j := 1; j < len(hash_array); j++ {
|
||
|
if j != 1 {
|
||
|
hash = hash + "-"
|
||
|
}
|
||
|
hash = hash + hash_array[j]
|
||
|
}
|
||
|
|
||
|
share_link_object, err := GetLink(user_id)
|
||
|
if err != nil {
|
||
|
|
||
|
log.Println(err)
|
||
|
respond_default_error(w, r, err, 500)
|
||
|
return
|
||
|
|
||
|
}
|
||
|
|
||
|
currentTime := time.Now()
|
||
|
linkTime, err := time.Parse("2006-01-02", share_link_object.Date)
|
||
|
if err != nil {
|
||
|
|
||
|
log.Println(err)
|
||
|
respond_default_error(w, r, errors.New("Failed to retrieve saved Wrapperr link."), 500)
|
||
|
return
|
||
|
|
||
|
}
|
||
|
|
||
|
linkTime = linkTime.Add(7 * 24 * time.Hour)
|
||
|
|
||
|
if !linkTime.Before(currentTime) && share_link_object.Hash == hash {
|
||
|
|
||
|
share_link_object.Message = "Shared link retrieved."
|
||
|
share_link_object.Error = false
|
||
|
|
||
|
ip_string := GetOriginIPString(w, r)
|
||
|
|
||
|
log.Println("Retrieved Wrapperr share link made by User ID: " + user_id + "." + ip_string)
|
||
|
|
||
|
respondWithJSON(w, http.StatusOK, share_link_object)
|
||
|
return
|
||
|
|
||
|
} else {
|
||
|
|
||
|
return_error := errors.New("Invalid share link.")
|
||
|
|
||
|
if linkTime.Before(currentTime) {
|
||
|
|
||
|
share_link_object.Expired = true
|
||
|
err = SaveLink(share_link_object)
|
||
|
if err != nil {
|
||
|
log.Println(err)
|
||
|
}
|
||
|
return_error = errors.New("This Wrapped link has expired.")
|
||
|
|
||
|
}
|
||
|
|
||
|
log.Println("Failed to retrieve Wrapperr share link with hash: " + link_payload.Hash + ".")
|
||
|
respond_default_error(w, r, return_error, 401)
|
||
|
return
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|