File restructure

Different components moved to packages which are imported. More proper and clean compared to previous implementation.
This commit is contained in:
Aune 2022-11-29 12:43:01 +01:00
parent 0e6506c381
commit 787b00aebc
20 changed files with 1138 additions and 418 deletions

View file

@ -15,4 +15,4 @@ RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build
EXPOSE ${port}
ENTRYPOINT /app/Wrapperr -port=${port}
ENTRYPOINT /app/wrapperr -port=${port}

View file

@ -63,7 +63,7 @@ There are two main ways. Docker information can be found further below.
<br>
### Download and start
There are multiple ways to install Wrapperr. The easiest is just to download the latest release from the [Release Page](https://github.com/aunefyren/wrapperr/releases) which matches your operating system, move all the content to a directory, and start the ```Wrapperr``` application located within the release. It should start right up, perhaps triggering some operating system or firewall warnings.
There are multiple ways to install Wrapperr. The easiest is just to download the latest release from the [Release Page](https://github.com/aunefyren/wrapperr/releases) which matches your operating system, move all the content to a directory, and start the ```wrapperr``` application located within the release. It should start right up, perhaps triggering some operating system or firewall warnings.
<br>
<br>
@ -73,9 +73,9 @@ If you want to build Wrapperr yourself, you can download whatever version/tag/br
```
$ go build
$ ./Wrapperr
$ ./wrapperr
```
Note, if building on another operating system, the executable could have a different name. Such as ```Wrapperr.exe``` on Windows.
Note, if building on another operating system, the executable could have a different name. Such as ```wrapperr.exe``` on Windows.
<br>
<br>

View file

@ -1,6 +1,7 @@
package main
package files
import (
"aunefyren/wrapperr/models"
"encoding/json"
"errors"
"io/ioutil"
@ -30,7 +31,7 @@ func GetAdminState() (bool, error) {
}
// Saves the given admin config struct as admin.json
func SaveAdminConfig(config AdminConfig) error {
func SaveAdminConfig(config models.AdminConfig) error {
file, err := json.MarshalIndent(config, "", " ")
if err != nil {
@ -46,7 +47,7 @@ func SaveAdminConfig(config AdminConfig) error {
}
// Read the config file and return the file as an object
func GetAdminConfig() (*AdminConfig, error) {
func GetAdminConfig() (*models.AdminConfig, error) {
// Create admin.json if it doesn't exist
if _, err := os.Stat(admin_config_path); errors.Is(err, os.ErrNotExist) {
@ -66,7 +67,7 @@ func GetAdminConfig() (*AdminConfig, error) {
}
defer file.Close()
decoder := json.NewDecoder(file)
admin_config := AdminConfig{}
admin_config := models.AdminConfig{}
err = decoder.Decode(&admin_config)
if err != nil {
log.Println("Admin config parsing threw error.")
@ -79,7 +80,7 @@ func GetAdminConfig() (*AdminConfig, error) {
// Creates empty admin.json
func CreateAdminConfigFile() error {
var admin_config AdminConfig
var admin_config models.AdminConfig
err := SaveAdminConfig(admin_config)
if err != nil {

View file

@ -1,6 +1,8 @@
package main
package files
import (
"aunefyren/wrapperr/models"
"aunefyren/wrapperr/utilities"
"encoding/json"
"errors"
"log"
@ -11,7 +13,7 @@ import (
var cache_path, _ = filepath.Abs("./config/cache.json")
// Saves the given config struct as cache.json
func SaveCache(cache *[]WrapperrDay) error {
func SaveCache(cache *[]models.WrapperrDay) error {
file, err := json.MarshalIndent(cache, "", " ")
if err != nil {
@ -28,7 +30,7 @@ func SaveCache(cache *[]WrapperrDay) error {
// Saves an empty cache, clearing any data present
func ClearCache() error {
cache := []WrapperrDay{}
cache := []models.WrapperrDay{}
err := SaveCache(&cache)
if err != nil {
@ -41,7 +43,7 @@ func ClearCache() error {
// Creates empty cache.json
func CreateCacheFile() error {
var cache []WrapperrDay
var cache []models.WrapperrDay
err := SaveCache(&cache)
if err != nil {
@ -52,7 +54,7 @@ func CreateCacheFile() error {
}
// Read the cache file and return the file as an object
func GetCache() ([]WrapperrDay, error) {
func GetCache() ([]models.WrapperrDay, error) {
// Create cache.json if it doesn't exist
if _, err := os.Stat(cache_path); errors.Is(err, os.ErrNotExist) {
@ -61,7 +63,7 @@ func GetCache() ([]WrapperrDay, error) {
err = CreateCacheFile()
if err != nil {
log.Println("Failed to create new cache file. Restarting Wrapperr.")
err = RestartSelf()
err = utilities.RestartSelf()
if err != nil {
return nil, err
}
@ -69,7 +71,7 @@ func GetCache() ([]WrapperrDay, error) {
}
// Load cache file for alterations, information
var cache []WrapperrDay
var cache []models.WrapperrDay
file, err := os.Open(cache_path)
if err != nil {
return nil, err

View file

@ -1,6 +1,7 @@
package main
package files
import (
"aunefyren/wrapperr/models"
"encoding/json"
"errors"
"log"
@ -96,7 +97,7 @@ func UpdatePrivateKey() (string, error) {
}
// Saves the given config struct as config.json
func SaveConfig(config *WrapperrConfig) error {
func SaveConfig(config *models.WrapperrConfig) error {
file, err := json.MarshalIndent(config, "", " ")
if err != nil {
@ -114,14 +115,14 @@ func SaveConfig(config *WrapperrConfig) error {
// Creates empty config.json
func CreateConfigFile() error {
var config WrapperrConfig
var config models.WrapperrConfig
// Define default boolean values since they are harder to seperate from deliberate boolean values
config.UseCache = true
config.PlexAuth = true
config.UseLogs = true
var tautulli_config = TautulliConfig{
var tautulli_config = models.TautulliConfig{
TautulliGrouping: true,
}
config.TautulliConfig = append(config.TautulliConfig, tautulli_config)
@ -150,7 +151,7 @@ func CreateConfigFile() error {
}
// Read the config file and return the file as an object
func GetConfig() (*WrapperrConfig, error) {
func GetConfig() (*models.WrapperrConfig, error) {
// Create config.json if it doesn't exist
if _, err := os.Stat(config_path); errors.Is(err, os.ErrNotExist) {
log.Println("Config file does not exist. Creating.")
@ -171,7 +172,7 @@ func GetConfig() (*WrapperrConfig, error) {
// Parse config file
decoder := json.NewDecoder(file)
config := WrapperrConfig{}
config := models.WrapperrConfig{}
err = decoder.Decode(&config)
if err != nil {
@ -186,7 +187,7 @@ func GetConfig() (*WrapperrConfig, error) {
defer file.Close()
decoder := json.NewDecoder(file)
config_legacy := WrapperrConfigLegacy{}
config_legacy := models.WrapperrConfigLegacy{}
err = decoder.Decode(&config_legacy)
convert_failed := false
@ -231,7 +232,7 @@ func GetConfig() (*WrapperrConfig, error) {
// Parse default config file
decoder = json.NewDecoder(file)
config = WrapperrConfig{}
config = models.WrapperrConfig{}
err = decoder.Decode(&config)
if err != nil {
log.Println("Get config file threw error trying to parse the template file.")
@ -251,7 +252,7 @@ func GetConfig() (*WrapperrConfig, error) {
// Parse default config file
decoder = json.NewDecoder(file)
config_default := WrapperrConfig{}
config_default := models.WrapperrConfig{}
err = decoder.Decode(&config_default)
if err != nil {
log.Println("Get config file threw error trying to parse the template file.")
@ -294,9 +295,9 @@ func GetConfig() (*WrapperrConfig, error) {
}
if config.TautulliConfig == nil {
config.TautulliConfig = []TautulliConfig{}
config.TautulliConfig = []models.TautulliConfig{}
NewTautulliConfig := TautulliConfig{
NewTautulliConfig := models.TautulliConfig{
TautulliLength: config_default.TautulliConfig[0].TautulliLength,
TautulliPort: config_default.TautulliConfig[0].TautulliPort,
}
@ -681,9 +682,9 @@ func BackUpConfig(ConfigPath string) (string, error) {
return new_save_loc, nil
}
func ConvertLegacyToCurrentConfig(config WrapperrConfig, config_legacy WrapperrConfigLegacy) (WrapperrConfig, error) {
func ConvertLegacyToCurrentConfig(config models.WrapperrConfig, config_legacy models.WrapperrConfigLegacy) (models.WrapperrConfig, error) {
var NewTautulli TautulliConfig
var NewTautulli models.TautulliConfig
NewTautulli.TautulliApiKey = config_legacy.TautulliConfig.TautulliApiKey
NewTautulli.TautulliIP = config_legacy.TautulliConfig.TautulliIP

View file

@ -1,6 +1,7 @@
package main
package files
import (
"aunefyren/wrapperr/models"
"encoding/json"
"errors"
"log"
@ -12,7 +13,7 @@ import (
var link_path, _ = filepath.Abs("./config/links")
// Save new link object to the correct path
func SaveLink(link_object *WrapperrShareLink) error {
func SaveLink(link_object *models.WrapperrShareLink) error {
// Check if the link folder exists
err := CheckLinkDir()
@ -63,14 +64,14 @@ func CheckLinkDir() error {
}
// Read
func GetLink(UserID string) (*WrapperrShareLink, error) {
func GetLink(UserID string) (*models.WrapperrShareLink, error) {
// Check if the link folder exists
err := CheckLinkDir()
if err != nil {
return nil, err
}
link_object := WrapperrShareLink{}
link_object := models.WrapperrShareLink{}
share_link_path, err := filepath.Abs(link_path + "/" + UserID + ".json")
if err != nil {

View file

@ -1,6 +1,7 @@
package main
package files
import (
"aunefyren/wrapperr/models"
"bufio"
"log"
"os"
@ -11,7 +12,11 @@ import (
var log_path, _ = filepath.Abs("./config/wrapperr.log")
var max_lines_returned = 200
func GetLogLines() ([]WrapperrLogLine, error) {
func GetMaxLogLinesReturned() int {
return max_lines_returned
}
func GetLogLines() ([]models.WrapperrLogLine, error) {
readFile, err := os.Open(log_path)
@ -30,14 +35,14 @@ func GetLogLines() ([]WrapperrLogLine, error) {
readFile.Close()
var logline_array []WrapperrLogLine
var logline_array []models.WrapperrLogLine
var re = regexp.MustCompile(`([0-9]{4,4}\/{1,1}[0-9]{2,2}\/[0-9]{2,2})\s([0-9]{2,2}:[0-9]{2,2}:[0-9]{2,2})\s([^\n]{1,})`)
for _, line := range fileLines {
match := re.FindStringSubmatch(line)
var logline WrapperrLogLine
var logline models.WrapperrLogLine
if len(match) != 4 {

2
go.mod
View file

@ -1,4 +1,4 @@
module Wrapperr
module aunefyren/wrapperr
go 1.19

49
main.go
View file

@ -1,6 +1,9 @@
package main
import (
"aunefyren/wrapperr/files"
"aunefyren/wrapperr/routes"
"aunefyren/wrapperr/utilities"
"flag"
"fmt"
"log"
@ -16,7 +19,7 @@ import (
func main() {
PrintASCII()
utilities.PrintASCII()
// Create and define file for logging
file, err := os.OpenFile("config/wrapperr.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
@ -30,7 +33,7 @@ func main() {
os.Exit(1)
}
config, err := GetConfig()
config, err := files.GetConfig()
if err != nil {
log.Println("Failed to load configuration file. Error: ")
log.Println(err)
@ -54,7 +57,7 @@ func main() {
log.Println("Removing value...")
config.Timezone = ""
err = SaveConfig(config)
err = files.SaveConfig(config)
if err != nil {
log.Println("Failed to set new time zone in the config. Error: ")
log.Println(err)
@ -87,32 +90,32 @@ func main() {
router := mux.NewRouter().StrictSlash(true)
// Admin auth routes
router.HandleFunc("/api/validate/admin", ApiValidateAdmin)
router.HandleFunc("/api/get/config", ApiGetConfig)
router.HandleFunc("/api/get/log", ApiGetLog)
router.HandleFunc("/api/set/config", ApiSetConfig)
router.HandleFunc("/api/update/admin", ApiUpdateAdmin)
router.HandleFunc("/api/validate/admin", routes.ApiValidateAdmin)
router.HandleFunc("/api/get/config", routes.ApiGetConfig)
router.HandleFunc("/api/get/log", routes.ApiGetLog)
router.HandleFunc("/api/set/config", routes.ApiSetConfig)
router.HandleFunc("/api/update/admin", routes.ApiUpdateAdmin)
// No-auth routes
router.HandleFunc("/api/get/config-state", ApiWrapperrConfigured)
router.HandleFunc("/api/login/admin", ApiLogInAdmin)
router.HandleFunc("/api/get/wrapperr-version", ApiGetWrapperrVersion)
router.HandleFunc("/api/get/admin-state", ApiGetAdminState)
router.HandleFunc("/api/get/functions", ApiGetFunctions)
router.HandleFunc("/api/create/admin", ApiCreateAdmin)
router.HandleFunc("/api/get/tautulli-connection", ApiGetTautulliConncection)
router.HandleFunc("/api/get/share-link", ApiGetShareLink)
router.HandleFunc("/api/get/config-state", routes.ApiWrapperrConfigured)
router.HandleFunc("/api/login/admin", routes.ApiLogInAdmin)
router.HandleFunc("/api/get/wrapperr-version", routes.ApiGetWrapperrVersion)
router.HandleFunc("/api/get/admin-state", routes.ApiGetAdminState)
router.HandleFunc("/api/get/functions", routes.ApiGetFunctions)
router.HandleFunc("/api/create/admin", routes.ApiCreateAdmin)
router.HandleFunc("/api/get/tautulli-connection", routes.ApiGetTautulliConncection)
router.HandleFunc("/api/get/share-link", routes.ApiGetShareLink)
// User auth routes
router.HandleFunc("/api/get/login-url", ApiGetLoginURL)
router.HandleFunc("/api/login/plex-auth", ApiLoginPlexAuth)
router.HandleFunc("/api/validate/plex-auth", ApiValidatePlexAuth)
router.HandleFunc("/api/create/share-link", ApiCreateShareLink)
router.HandleFunc("/api/get/user-share-link", ApiGetUserShareLink)
router.HandleFunc("/api/delete/user-share-link", ApiDeleteUserShareLink)
router.HandleFunc("/api/get/login-url", routes.ApiGetLoginURL)
router.HandleFunc("/api/login/plex-auth", routes.ApiLoginPlexAuth)
router.HandleFunc("/api/validate/plex-auth", routes.ApiValidatePlexAuth)
router.HandleFunc("/api/create/share-link", routes.ApiCreateShareLink)
router.HandleFunc("/api/get/user-share-link", routes.ApiGetUserShareLink)
router.HandleFunc("/api/delete/user-share-link", routes.ApiDeleteUserShareLink)
// Get stats route
router.HandleFunc("/api/get/statistics", ApiWrapperGetStatistics)
router.HandleFunc("/api/get/statistics", routes.ApiWrapperGetStatistics)
// Static routes
router.PathPrefix("/").Handler(http.FileServer(http.Dir("./web/")))

20
models/methods.go Normal file
View file

@ -0,0 +1,20 @@
package models
import (
"errors"
"time"
)
// Different types of error returned by the VerifyToken function
var (
ErrInvalidToken = errors.New("token is invalid")
ErrExpiredToken = errors.New("token has expired")
)
// Valid checks if the token payload is valid or not
func (payload *Payload) Valid() error {
if time.Now().After(payload.ExpiredAt) {
return ErrExpiredToken
}
return nil
}

679
models/models.go Normal file
View file

@ -0,0 +1,679 @@
package models
import (
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/google/uuid"
)
type Default_Reply struct {
Message string `json:"message"`
Error bool `json:"error"`
}
/*
type CustomPayload struct {
jwt.Payload
Username string `json:"username,omitempty"`
PlexID int `json:"plexid,omitempty"`
Admin bool `json:"admin,omitempty"`
}
*/
type Payload struct {
jwt.Claims
ID uuid.UUID `json:"id"`
Username string `json:"username"`
Admin bool `json:"admin"`
AuthToken string `json:"authtoken"`
IssuedAt time.Time `json:"issued_at"`
ExpiredAt time.Time `json:"expired_at"`
}
type JWTMaker struct {
secretKey string
}
type AdminConfig struct {
AdminUsername string `json:"admin_username"`
AdminPassword string `json:"admin_password"`
}
type WrapperrConfig struct {
TautulliConfig []TautulliConfig `json:"tautulli_config"`
WrapperrCustomize WrapperrCustomize `json:"wrapperr_customize"`
WrapperrVersion string `json:"wrapperr_version"`
Timezone string `json:"timezone"`
ApplicationName string `json:"application_name"`
ApplicationURL string `json:"application_url"`
UseCache bool `json:"use_cache"`
UseLogs bool `json:"use_logs"`
ClientKey string `json:"client_key"`
WrapperrRoot string `json:"wrapperr_root"`
PrivateKey string `json:"private_key"`
CreateShareLinks bool `json:"create_share_links"`
WrappedStart int `json:"wrapped_start"`
WrappedEnd int `json:"wrapped_end"`
WrapperrPort int `json:"wrapperr_port"`
PlexAuth bool `json:"plex_auth"`
WinterTheme bool `json:"winter_theme"`
}
type WrapperrConfigLegacy struct {
TautulliConfig TautulliConfigLegacy `json:"tautulli_config"`
WrapperrCustomize WrapperrCustomize `json:"wrapperr_customize"`
WrapperrVersion string `json:"wrapperr_version"`
Timezone string `json:"timezone"`
ApplicationName string `json:"application_name"`
ApplicationURL string `json:"application_url"`
UseCache bool `json:"use_cache"`
UseLogs bool `json:"use_logs"`
ClientKey string `json:"client_key"`
WrapperrRoot string `json:"wrapperr_root"`
PrivateKey string `json:"private_key"`
CreateShareLinks bool `json:"create_share_links"`
WrappedStart int `json:"wrapped_start"`
WrappedEnd int `json:"wrapped_end"`
WrapperrPort int `json:"wrapperr_port"`
PlexAuth bool `json:"plex_auth"`
WinterTheme bool `json:"winter_theme"`
}
type TautulliConfig struct {
TautulliApiKey string `json:"tautulli_apikey"`
TautulliIP string `json:"tautulli_ip"`
TautulliPort int `json:"tautulli_port"`
TautulliLength int `json:"tautulli_length"`
TautulliRoot string `json:"tautulli_root"`
TautulliLibraries string `json:"tautulli_libraries"`
TautulliGrouping bool `json:"tautulli_grouping"`
TautulliHttps bool `json:"tautulli_https"`
TautulliName string `json:"tautulli_name"`
TautulliID int `json:"tautulli_id"`
}
type TautulliConfigLegacy struct {
TautulliApiKey string `json:"tautulli_apikey"`
TautulliIP string `json:"tautulli_ip"`
TautulliPort int `json:"tautulli_port"`
TautulliLength int `json:"tautulli_length"`
TautulliRoot string `json:"tautulli_root"`
TautulliLibraries string `json:"tautulli_libraries"`
TautulliGrouping bool `json:"tautulli_grouping"`
TautulliHttps bool `json:"tautulli_https"`
}
type WrapperrCustomize struct {
WrapperrFrontPageTitle string `json:"wrapperr_front_page_title"`
WrapperrFrontPageSubtitle string `json:"wrapperr_front_page_subtitle"`
StatsIntroTitle string `json:"stats_intro_title"`
StatsIntroSubtitle string `json:"stats_intro_subtitle"`
StatsOutroTitle string `json:"stats_outro_title"`
StatsOutroSubtitle string `json:"stats_outro_subtitle"`
StatsOrderByPlays bool `json:"stats_order_by_plays"`
StatsOrderByDuration bool `json:"stats_order_by_duration"`
StatsTopListLength int `json:"stats_top_list_length"`
GetUserMovieStats bool `json:"get_user_movie_stats"`
GetUserMovieStatsTitle string `json:"get_user_movie_stats_title"`
GetUserMovieStatsSubtitle string `json:"get_user_movie_stats_subtitle"`
GetUserMovieStatsSubsubtitle string `json:"get_user_movie_stats_subsubtitle"`
GetUserMovieStatsSubtitleOne string `json:"get_user_movie_stats_subtitle_one"`
GetUserMovieStatsSubsubtitleOne string `json:"get_user_movie_stats_subsubtitle_one"`
GetUserMovieStatsSubtitleNone string `json:"get_user_movie_stats_subtitle_none"`
GetUserMovieStatsSubsubtitleNone string `json:"get_user_movie_stats_subsubtitle_none"`
GetUserMovieStatsTopMovie string `json:"get_user_movie_stats_top_movie"`
GetUserMovieStatsTopMoviePlural string `json:"get_user_movie_stats_top_movie_plural"`
GetUserMovieStatsMovieCompletionTitle string `json:"get_user_movie_stats_movie_completion_title"`
GetUserMovieStatsMovieCompletionTitlePlural string `json:"get_user_movie_stats_movie_completion_title_plural"`
GetUserMovieStatsMovieCompletionSubtitle string `json:"get_user_movie_stats_movie_completion_subtitle"`
GetUserMovieStatsPauseTitle string `json:"get_user_movie_stats_pause_title"`
GetUserMovieStatsPauseSubtitle string `json:"get_user_movie_stats_pause_subtitle"`
GetUserMovieStatsPauseTitleOne string `json:"get_user_movie_stats_pause_title_one"`
GetUserMovieStatsPauseSubtitleOne string `json:"get_user_movie_stats_pause_subtitle_one"`
GetUserMovieStatsPauseTitleNone string `json:"get_user_movie_stats_pause_title_none"`
GetUserMovieStatsPauseSubtitleNone string `json:"get_user_movie_stats_pause_subtitle_none"`
GetUserMovieStatsOldestTitle string `json:"get_user_movie_stats_oldest_title"`
GetUserMovieStatsOldestSubtitle string `json:"get_user_movie_stats_oldest_subtitle"`
GetUserMovieStatsOldestSubtitlePre1950 string `json:"get_user_movie_stats_oldest_subtitle_pre_1950"`
GetUserMovieStatsOldestSubtitlePre1975 string `json:"get_user_movie_stats_oldest_subtitle_pre_1975"`
GetUserMovieStatsOldestSubtitlePre2000 string `json:"get_user_movie_stats_oldest_subtitle_pre_2000"`
GetUserMovieStatsSpentTitle string `json:"get_user_movie_stats_spent_title"`
GetUserShowStats bool `json:"get_user_show_stats"`
GetUserShowBuddy bool `json:"get_user_show_stats_buddy"`
GetUserShowStatsTitle string `json:"get_user_show_stats_title"`
GetUserShowStatsSubtitle string `json:"get_user_show_stats_subtitle"`
GetUserShowStatsSubsubtitle string `json:"get_user_show_stats_subsubtitle"`
GetUserShowStatsSubtitleOne string `json:"get_user_show_stats_subtitle_one"`
GetUserShowStatsSubsubtitleOne string `json:"get_user_show_stats_subsubtitle_one"`
GetUserShowStatsSubtitleNone string `json:"get_user_show_stats_subtitle_none"`
GetUserShowStatsSubsubtitleNone string `json:"get_user_show_stats_subsubtitle_none"`
GetUserShowStatsTopShow string `json:"get_user_show_stats_top_show"`
GetUserShowStatsTopShowPlural string `json:"get_user_show_stats_top_show_plural"`
GetUserShowStatsSpentTitle string `json:"get_user_show_stats_spent_title"`
GetUserShowStatsMostPlayedTitle string `json:"get_user_show_stats_most_played_title"`
GetUserShowStatsMostPlayedSubtitle string `json:"get_user_show_stats_most_played_subtitle"`
GetUserShowStatsBuddyTitle string `json:"get_user_show_stats_buddy_title"`
GetUserShowStatsBuddySubtitle string `json:"get_user_show_stats_buddy_subtitle"`
GetUserShowStatsBuddyTitleNone string `json:"get_user_show_stats_buddy_title_none"`
GetUserShowStatsBuddySubtitleNone string `json:"get_user_show_stats_buddy_subtitle_none"`
GetUserMusicStats bool `json:"get_user_music_stats"`
GetUserMusicStatsTitle string `json:"get_user_music_stats_title"`
GetUserMusicStatsSubtitle string `json:"get_user_music_stats_subtitle"`
GetUserMusicStatsSubsubtitle string `json:"get_user_music_stats_subsubtitle"`
GetUserMusicStatsSubtitleOne string `json:"get_user_music_stats_subtitle_one"`
GetUserMusicStatsSubsubtitleOne string `json:"get_user_music_stats_subsubtitle_one"`
GetUserMusicStatsSubtitleNone string `json:"get_user_music_stats_subtitle_none"`
GetUserMusicStatsSubsubtitleNone string `json:"get_user_music_stats_subsubtitle_none"`
GetUserMusicStatsTopTrack string `json:"get_user_music_stats_top_track"`
GetUserMusicStatsTopTrackPlural string `json:"get_user_music_stats_top_track_plural"`
GetUserMusicStatsTopAlbumPlural string `json:"get_user_music_stats_top_album_plural"`
GetUserMusicStatsTopArtistPlural string `json:"get_user_music_stats_top_artist_plural"`
GetUserMusicStatsSpentTitle string `json:"get_user_music_stats_spent_title"`
GetUserMusicStatsSpentSubtitle string `json:"get_user_music_stats_spent_subtitle"`
GetUserMusicStatsOldestAlbumTitle string `json:"get_user_music_stats_oldest_album_title"`
GetUserMusicStatsOldestAlbumSubtitle string `json:"get_user_music_stats_oldest_album_subtitle"`
GetYearStatsTitle string `json:"get_year_stats_title"`
GetYearStatsSubtitle string `json:"get_year_stats_subtitle"`
GetYearStatsSubsubtitle string `json:"get_year_stats_subsubtitle"`
GetYearStatsMovies bool `json:"get_year_stats_movies"`
GetYearStatsMoviesTitle string `json:"get_year_stats_movies_title"`
GetYearStatsShows bool `json:"get_year_stats_shows"`
GetYearStatsShowsTitle string `json:"get_year_stats_shows_title"`
GetYearStatsMusic bool `json:"get_year_stats_music"`
GetYearStatsMusicTitle string `json:"get_year_stats_music_title"`
GetYearStatsLeaderboard bool `json:"get_year_stats_leaderboard"`
GetYearStatsLeaderboardNumbers bool `json:"get_year_stats_leaderboard_numbers"`
GetYearStatsLeaderboardTitle string `json:"get_year_stats_leaderboard_title"`
GetYearStatsMoviesDurationTitle string `json:"get_year_stats_movies_duration_title"`
GetYearStatsShowsDurationTitle string `json:"get_year_stats_shows_duration_title"`
GetYearStatsMusicDurationTitle string `json:"get_year_stats_music_duration_title"`
GetYearStatsDurationSumTitle string `json:"get_year_stats_duration_sum_title"`
WrapperrAnd string `json:"wrapperr_and"`
WrapperrPlay string `json:"wrapperr_play"`
WrapperrPlayPlural string `json:"wrapperr_play_plural"`
WrapperrDay string `json:"wrapperr_day"`
WrapperrDayPlural string `json:"wrapperr_day_plural"`
WrapperrHour string `json:"wrapperr_hour"`
WrapperrHourPlural string `json:"wrapperr_hour_plural"`
WrapperrMinute string `json:"wrapperr_minute"`
WrapperrMinutePlural string `json:"wrapperr_minute_plural"`
WrapperrSecond string `json:"wrapperr_second"`
WrapperrSecondPlural string `json:"wrapperr_second_plural"`
WrapperrSortPlays string `json:"wrapperr_sort_plays"`
WrapperrSortDuration string `json:"wrapperr_sort_duration"`
}
type WrapperrVersion struct {
WrapperrVersion string `json:"wrapperr_version"`
ApplicationName string `json:"application_name"`
PlexAuth bool `json:"plex_auth"`
WrapperrFrontPageTitle string `json:"wrapperr_front_page_title"`
WrapperrFrontPageSubtitle string `json:"wrapperr_front_page_subtitle"`
ClientKey string `json:"client_key"`
WrapperrConfigured bool `json:"wrapperr_configured"`
WinterTheme bool `json:"winter_theme"`
Message string `json:"message"`
Error bool `json:"error"`
}
type BooleanReply struct {
Message string `json:"message"`
Error bool `json:"error"`
Data bool `json:"data"`
}
type StringReply struct {
Message string `json:"message"`
Error bool `json:"error"`
Data string `json:"data"`
}
type ConfigReply struct {
Message string `json:"message"`
Error bool `json:"error"`
Data WrapperrConfig `json:"data"`
Username string `json:"username"`
}
type WrapperrFunctions struct {
WrapperrCustomize WrapperrCustomize `json:"wrapperr_customize"`
WrapperrVersion string `json:"wrapperr_version"`
PlexAuth bool `json:"plex_auth"`
CreateShareLinks bool `json:"create_share_links"`
}
type SetWrapperrConfig struct {
ClearCache bool `json:"clear_cache"`
DataType string `json:"data_type"`
TautulliConfig []TautulliConfig `json:"tautulli_config"`
WrapperrCustomize WrapperrCustomize `json:"wrapperr_customize"`
WrapperrData struct {
UseCache bool `json:"use_cache"`
UseLogs bool `json:"use_logs"`
PlexAuth bool `json:"plex_auth"`
WrapperrRoot string `json:"wrapperr_root"`
CreateShareLinks bool `json:"create_share_links"`
Timezone string `json:"timezone"`
ApplicationName string `json:"application_name"`
ApplicationURL string `json:"application_url"`
WrappedStart int `json:"wrapped_start"`
WrappedEnd int `json:"wrapped_end"`
WinterTheme bool `json:"winter_theme"`
} `json:"wrapperr_data"`
}
type GetLoginURL struct {
HomeURL string `json:"home_url"`
}
type GetLoginURLReply struct {
ID int `json:"id"`
Code string `json:"code"`
URL string `json:"url"`
Message string `json:"message"`
Error bool `json:"error"`
}
type PlexGetPinReply struct {
ID int `json:"id"`
Code string `json:"code"`
Product string `json:"product"`
Trusted bool `json:"trusted"`
ClientIdentifier string `json:"clientIdentifier"`
Location struct {
Code string `json:"code"`
EuropeanUnionMember bool `json:"european_union_member"`
ContinentCode string `json:"continent_code"`
Country string `json:"country"`
City string `json:"city"`
TimeZone string `json:"time_zone"`
PostalCode string `json:"postal_code"`
InPrivacyRestrictedCountry bool `json:"in_privacy_restricted_country"`
Subdivisions string `json:"subdivisions"`
Coordinates string `json:"coordinates"`
} `json:"location"`
ExpiresIn int `json:"expiresIn"`
CreatedAt time.Time `json:"createdAt"`
ExpiresAt time.Time `json:"expiresAt"`
AuthToken string `json:"authToken"`
NewRegistration interface{} `json:"newRegistration"`
}
type LoginPlexAuth struct {
ID int `json:"id"`
Code string `json:"code"`
}
type PlexGetUserReply struct {
ID int `json:"id"`
UUID string `json:"uuid"`
Username string `json:"username"`
Title string `json:"title"`
Email string `json:"email"`
FriendlyName string `json:"friendlyName"`
Locale interface{} `json:"locale"`
Confirmed bool `json:"confirmed"`
EmailOnlyAuth bool `json:"emailOnlyAuth"`
HasPassword bool `json:"hasPassword"`
Protected bool `json:"protected"`
Thumb string `json:"thumb"`
AuthToken string `json:"authToken"`
MailingListStatus string `json:"mailingListStatus"`
MailingListActive bool `json:"mailingListActive"`
ScrobbleTypes string `json:"scrobbleTypes"`
Country string `json:"country"`
Pin string `json:"pin"`
Subscription struct {
Active bool `json:"active"`
SubscribedAt time.Time `json:"subscribedAt"`
Status string `json:"status"`
PaymentService string `json:"paymentService"`
Plan string `json:"plan"`
Features []string `json:"features"`
} `json:"subscription"`
SubscriptionDescription string `json:"subscriptionDescription"`
Restricted bool `json:"restricted"`
Anonymous interface{} `json:"anonymous"`
Home bool `json:"home"`
Guest bool `json:"guest"`
HomeSize int `json:"homeSize"`
HomeAdmin bool `json:"homeAdmin"`
MaxHomeSize int `json:"maxHomeSize"`
CertificateVersion int `json:"certificateVersion"`
RememberExpiresAt int `json:"rememberExpiresAt"`
Profile struct {
AutoSelectAudio bool `json:"autoSelectAudio"`
DefaultAudioLanguage string `json:"defaultAudioLanguage"`
DefaultSubtitleLanguage string `json:"defaultSubtitleLanguage"`
AutoSelectSubtitle int `json:"autoSelectSubtitle"`
DefaultSubtitleAccessibility int `json:"defaultSubtitleAccessibility"`
DefaultSubtitleForced int `json:"defaultSubtitleForced"`
PlexPassVisibility string `json:"plexPassVisibility"`
AccountAgeVisibility string `json:"accountAgeVisibility"`
} `json:"profile"`
Entitlements []string `json:"entitlements"`
Roles []string `json:"roles"`
Services []struct {
Identifier string `json:"identifier"`
Endpoint string `json:"endpoint"`
Token string `json:"token"`
Secret interface{} `json:"secret"`
Status string `json:"status"`
} `json:"services"`
AdsConsent interface{} `json:"adsConsent"`
AdsConsentSetAt interface{} `json:"adsConsentSetAt"`
AdsConsentReminderAt interface{} `json:"adsConsentReminderAt"`
ExperimentalFeatures bool `json:"experimentalFeatures"`
TwoFactorEnabled bool `json:"twoFactorEnabled"`
BackupCodesCreated bool `json:"backupCodesCreated"`
}
type TautulliStatusReply struct {
Response struct {
Result string `json:"result"`
Message string `json:"message"`
Data struct {
} `json:"data"`
} `json:"response"`
}
type SearchWrapperrRequest struct {
CachingMode bool `json:"caching"`
CachingLimit int `json:"cache_limit"`
PlexIdentity string `json:"plex_identity"`
}
type TautulliGetUsersReply struct {
Response struct {
Result string `json:"result"`
Message interface{} `json:"message"`
Data []struct {
RowID int `json:"row_id"`
UserID int `json:"user_id"`
Username string `json:"username"`
FriendlyName string `json:"friendly_name"`
Thumb interface{} `json:"thumb"`
Email string `json:"email"`
IsActive int `json:"is_active"`
IsAdmin int `json:"is_admin"`
IsHomeUser interface{} `json:"is_home_user"`
IsAllowSync interface{} `json:"is_allow_sync"`
IsRestricted interface{} `json:"is_restricted"`
DoNotify int `json:"do_notify"`
KeepHistory int `json:"keep_history"`
AllowGuest int `json:"allow_guest"`
ServerToken interface{} `json:"server_token"`
SharedLibraries interface{} `json:"shared_libraries"`
FilterAll interface{} `json:"filter_all"`
FilterMovies interface{} `json:"filter_movies"`
FilterTv interface{} `json:"filter_tv"`
FilterMusic interface{} `json:"filter_music"`
FilterPhotos interface{} `json:"filter_photos"`
} `json:"data"`
} `json:"response"`
}
type WrapperrDay struct {
Date string `json:"date"`
Data []TautulliEntry `json:"data"`
DataComplete bool `json:"data_complete"`
TautulliServers []string `json:"tautulli_servers"`
}
type TautulliEntry struct {
Date int `json:"date"`
Duration int `json:"duration"`
RowID int `json:"row_id"`
FriendlyName string `json:"friendly_name"`
FullTitle string `json:"full_title"`
GrandparentRatingKey int `json:"grandparent_rating_key"`
GrandparentTitle string `json:"grandparent_title"`
OriginalTitle string `json:"original_title"`
MediaType string `json:"media_type"`
ParentRatingKey int `json:"parent_rating_key"`
ParentTitle string `json:"parent_title"`
PausedCounter int `json:"paused_counter"`
PercentComplete int `json:"percent_complete"`
RatingKey int `json:"rating_key"`
Title string `json:"title"`
User string `json:"user"`
UserID int `json:"user_id"`
Year int `json:"year"`
Plays int `json:"plays"`
}
type WrapperrYearUserEntry struct {
FriendlyName string `json:"friendly_name"`
GrandparentTitle string `json:"grandparent_title"`
OriginalTitle string `json:"original_title"`
ParentTitle string `json:"parent_title"`
PausedCounter int `json:"paused_counter"`
Title string `json:"title"`
User string `json:"user"`
UserID int `json:"user_id"`
Year int `json:"year"`
Plays int `json:"plays"`
DurationMovies int `json:"duration_movies"`
DurationShows int `json:"duration_shows"`
DurationArtists int `json:"duration_artists"`
Duration int `json:"duration"`
}
type TautulliGetHistoryReply struct {
Response struct {
Result string `json:"result"`
Message interface{} `json:"message"`
Data struct {
RecordsFiltered int `json:"recordsFiltered"`
RecordsTotal int `json:"recordsTotal"`
Data []TautulliHistoryItem `json:"data"`
Draw int `json:"draw"`
FilterDuration string `json:"filter_duration"`
TotalDuration string `json:"total_duration"`
} `json:"data"`
} `json:"response"`
}
type TautulliHistoryItem struct {
ReferenceID int `json:"reference_id"`
RowID int `json:"row_id"`
ID int `json:"id"`
Date int `json:"date"`
Started int `json:"started"`
Stopped int `json:"stopped"`
Duration int `json:"duration"`
PausedCounter int `json:"paused_counter"`
UserID int `json:"user_id"`
User string `json:"user"`
FriendlyName string `json:"friendly_name"`
Platform string `json:"platform"`
Product string `json:"product"`
Player string `json:"player"`
IPAddress string `json:"ip_address"`
Live int `json:"live"`
MachineID string `json:"machine_id"`
Location string `json:"location"`
Secure interface{} `json:"secure"`
Relayed interface{} `json:"relayed"`
MediaType string `json:"media_type"`
RatingKey int `json:"rating_key"`
ParentRatingKey int `json:"parent_rating_key"`
GrandparentRatingKey int `json:"grandparent_rating_key"`
FullTitle string `json:"full_title"`
Title string `json:"title"`
ParentTitle string `json:"parent_title"`
GrandparentTitle string `json:"grandparent_title"`
OriginalTitle string `json:"original_title"`
Year int `json:"year"`
MediaIndex string `json:"media_index"`
ParentMediaIndex string `json:"parent_media_index"`
Thumb string `json:"thumb"`
OriginallyAvailableAt string `json:"originally_available_at"`
GUID string `json:"guid"`
TranscodeDecision string `json:"transcode_decision"`
PercentComplete int `json:"percent_complete"`
WatchedStatus int `json:"watched_status"`
GroupCount int `json:"group_count"`
GroupIds string `json:"group_ids"`
State interface{} `json:"state"`
SessionKey interface{} `json:"session_key"`
}
type WrapperrStatisticsReply struct {
Error bool `json:"error"`
Date string `json:"date"`
Message string `json:"message"`
User struct {
Name string `json:"name"`
ID int `json:"id"`
UserMovies struct {
Data struct {
MoviesDuration []TautulliEntry `json:"movies_duration"`
MoviesPlays []TautulliEntry `json:"movies_plays"`
UserMovieMostPaused struct {
Title string `json:"title"`
Year int `json:"year"`
Plays int `json:"plays"`
Duration int `json:"duration"`
PausedCounter int `json:"paused_counter"`
} `json:"user_movie_most_paused"`
UserMovieFinishingPercent float64 `json:"user_movie_finishing_percent"`
UserMovieOldest struct {
Title string `json:"title"`
Year int `json:"year"`
Plays int `json:"plays"`
Duration int `json:"duration"`
PausedCounter int `json:"paused_counter"`
Error bool `json:"error"`
} `json:"user_movie_oldest"`
MovieDuration int `json:"movie_duration"`
MoviePlays int `json:"movie_plays"`
} `json:"data"`
Message string `json:"message"`
Error bool `json:"error"`
} `json:"user_movies"`
UserShows struct {
Data struct {
ShowsDuration []TautulliEntry `json:"shows_duration"`
ShowsPlays []TautulliEntry `json:"shows_plays"`
EpisodeDurationLongest struct {
Title string `json:"title"`
ParentTitle string `json:"parent_title"`
GrandparentTitle string `json:"grandparent_title"`
Duration int `json:"duration"`
Plays int `json:"plays"`
Error bool `json:"error"`
} `json:"episode_duration_longest"`
ShowDuration int `json:"show_duration"`
ShowPlays int `json:"show_plays"`
ShowBuddy WrapperrShowBuddy `json:"show_buddy"`
} `json:"data"`
Message string `json:"message"`
Error bool `json:"error"`
} `json:"user_shows"`
UserMusic struct {
Data struct {
TracksDuration []TautulliEntry `json:"tracks_duration"`
TracksPlays []TautulliEntry `json:"tracks_plays"`
AlbumsDuration []TautulliEntry `json:"albums_duration"`
AlbumsPlays []TautulliEntry `json:"albums_plays"`
UserAlbumOldest struct {
ParentTitle string `json:"parent_title"`
GrandparentTitle string `json:"grandparent_title"`
Year int `json:"year"`
Plays int `json:"plays"`
Duration int `json:"duration"`
Error bool `json:"error"`
} `json:"user_album_oldest"`
ArtistsDuration []TautulliEntry `json:"artists_duration"`
ArtistsPlays []TautulliEntry `json:"artists_plays"`
TrackDuration int `json:"track_duration"`
TrackPlays int `json:"track_plays"`
} `json:"data"`
Message string `json:"message"`
Error bool `json:"error"`
} `json:"user_music"`
} `json:"user"`
YearStats struct {
YearMovies struct {
Data struct {
MoviesDuration []TautulliEntry `json:"movies_duration"`
MoviesPlays []TautulliEntry `json:"movies_plays"`
MovieDuration int `json:"movie_duration"`
MoviePlays int `json:"movie_plays"`
} `json:"data"`
Message string `json:"message"`
Error bool `json:"error"`
} `json:"year_movies"`
YearShows struct {
Data struct {
ShowsDuration []TautulliEntry `json:"shows_duration"`
ShowsPlays []TautulliEntry `json:"shows_plays"`
ShowDuration int `json:"show_duration"`
ShowPlays int `json:"show_plays"`
} `json:"data"`
Message string `json:"message"`
Error bool `json:"error"`
} `json:"year_shows"`
YearMusic struct {
Data struct {
ArtistsDuration []TautulliEntry `json:"artists_duration"`
ArtistsPlays []TautulliEntry `json:"artists_plays"`
MusicDuration int `json:"music_duration"`
MusicPlays int `json:"music_plays"`
} `json:"data"`
Message string `json:"message"`
Error bool `json:"error"`
} `json:"year_music"`
YearUsers struct {
Data struct {
UsersDuration []WrapperrYearUserEntry `json:"users_duration"`
UsersPlays []WrapperrYearUserEntry `json:"users_plays"`
} `json:"data"`
Message string `json:"message"`
Error bool `json:"error"`
} `json:"year_users"`
} `json:"year_stats"`
}
type WrapperrShareLinkCreateRequest struct {
Data WrapperrStatisticsReply `json:"data"`
Functions WrapperrCustomize `json:"functions"`
}
type WrapperrShareLinkGetRequest struct {
Hash string `json:"hash"`
}
type WrapperrShareLink struct {
Date string `json:"date"`
UserID int `json:"user_id"`
WrapperrVersion string `json:"wrapperr_version"`
Hash string `json:"hash"`
Content WrapperrShareLinkCreateRequest `json:"content"`
Message string `json:"message"`
Error bool `json:"error"`
Expired bool `json:"expired"`
}
type WrapperrShowBuddy struct {
Message string `json:"message"`
Error bool `json:"error"`
BuddyName string `json:"buddy_name"`
BuddyDuration int `json:"buddy_duration"`
BuddyFound bool `json:"buddy_found"`
}
type WrapperrLogLine struct {
Date string `json:"date"`
Time string `json:"time"`
Message string `json:"message"`
}
type WrapperrLogLineReply struct {
Message string `json:"message"`
Error bool `json:"error"`
Data []WrapperrLogLine `json:"data"`
Limit int `json:"limit"`
}

View file

@ -1,6 +1,8 @@
package main
package modules
import (
"aunefyren/wrapperr/files"
"aunefyren/wrapperr/models"
"errors"
"log"
"net/http"
@ -11,20 +13,20 @@ import (
)
// AuthorizeToken validates JWT tokens using the private key.
func AuthorizeToken(writer http.ResponseWriter, request *http.Request) (*Payload, error) {
func AuthorizeToken(writer http.ResponseWriter, request *http.Request) (*models.Payload, error) {
PrivateKey, err := GetPrivateKey()
PrivateKey, err := files.GetPrivateKey()
if err != nil {
log.Println("Failed to load JWT Token settings. Error: ")
log.Println(err)
return &Payload{}, errors.New("Failed to load JWT Token settings.")
return &models.Payload{}, errors.New("Failed to load JWT Token settings.")
}
// Check if Authorization header is available
header := request.Header.Get("Authorization")
if header == "" || !strings.Contains(header, " ") || !strings.Contains(strings.ToLower(header), "bearer") {
log.Println("No valid Authorization token found in header during API request.")
return &Payload{}, errors.New("No valid Authorization token found in header.")
return &models.Payload{}, errors.New("No valid Authorization token found in header.")
}
headerParts := strings.Split(header, " ")
@ -32,7 +34,7 @@ func AuthorizeToken(writer http.ResponseWriter, request *http.Request) (*Payload
if len(headerParts) < 2 {
log.Println("Failed to parse header. Error: ")
log.Println(err)
return &Payload{}, errors.New("Failed to parse header.")
return &models.Payload{}, errors.New("Failed to parse header.")
}
jwtToken := headerParts[1]
@ -41,34 +43,34 @@ func AuthorizeToken(writer http.ResponseWriter, request *http.Request) (*Payload
if err != nil {
log.Println("Session token not accepted. Error: ")
log.Println(err)
return &Payload{}, errors.New("Session token not accepted. Please relog.")
return &models.Payload{}, errors.New("Session token not accepted. Please relog.")
}
return payload, nil
}
// VerifyToken checks if the token is valid or not
func VerifyToken(PrivateKey string, token string) (*Payload, error) {
func VerifyToken(PrivateKey string, token string) (*models.Payload, error) {
keyFunc := func(token *jwt.Token) (interface{}, error) {
_, ok := token.Method.(*jwt.SigningMethodHMAC)
if !ok {
return nil, ErrInvalidToken
return nil, models.ErrInvalidToken
}
return []byte(PrivateKey), nil
}
jwtToken, err := jwt.ParseWithClaims(token, &Payload{}, keyFunc)
jwtToken, err := jwt.ParseWithClaims(token, &models.Payload{}, keyFunc)
if err != nil {
verr, ok := err.(*jwt.ValidationError)
if ok && errors.Is(verr.Inner, ErrExpiredToken) {
return nil, ErrExpiredToken
if ok && errors.Is(verr.Inner, jwt.ErrTokenExpired) {
return nil, models.ErrExpiredToken
}
return nil, ErrInvalidToken
return nil, models.ErrInvalidToken
}
payload, ok := jwtToken.Claims.(*Payload)
payload, ok := jwtToken.Claims.(*models.Payload)
if !ok {
return nil, ErrInvalidToken
return nil, models.ErrInvalidToken
}
return payload, nil
@ -77,7 +79,7 @@ func VerifyToken(PrivateKey string, token string) (*Payload, error) {
// CreateToken creates a new JWT token used to validate a users session. Valid for three days by default.
func CreateToken(username string, admin bool, authtoken string) (string, error) {
PrivateKey, err := GetPrivateKey()
PrivateKey, err := files.GetPrivateKey()
if err != nil {
log.Println("Failed to load JWT Token settings. Error: ")
log.Println(err)
@ -97,7 +99,7 @@ func CreateToken(username string, admin bool, authtoken string) (string, error)
}
// CreateToken creates a new token for a specific username and duration
func CreateTokenTwo(PrivateKey string, username string, admin bool, authtoken string, duration time.Duration) (string, *Payload, error) {
func CreateTokenTwo(PrivateKey string, username string, admin bool, authtoken string, duration time.Duration) (string, *models.Payload, error) {
payload, err := NewPayload(username, admin, authtoken, duration)
if err != nil {
return "", payload, err

View file

@ -1,26 +1,20 @@
package main
package modules
import (
"errors"
"aunefyren/wrapperr/models"
"time"
"github.com/google/uuid"
)
// Different types of error returned by the VerifyToken function
var (
ErrInvalidToken = errors.New("token is invalid")
ErrExpiredToken = errors.New("token has expired")
)
// NewPayload creates a new token payload with a specific username and duration
func NewPayload(username string, admin bool, authtoken string, duration time.Duration) (*Payload, error) {
func NewPayload(username string, admin bool, authtoken string, duration time.Duration) (*models.Payload, error) {
tokenID, err := uuid.NewRandom()
if err != nil {
return nil, err
}
payload := &Payload{
payload := &models.Payload{
ID: tokenID,
Username: username,
Admin: admin,
@ -30,11 +24,3 @@ func NewPayload(username string, admin bool, authtoken string, duration time.Dur
}
return payload, nil
}
// Valid checks if the token payload is valid or not
func (payload *Payload) Valid() error {
if time.Now().After(payload.ExpiredAt) {
return ErrExpiredToken
}
return nil
}

View file

@ -1,6 +1,7 @@
package main
package modules
import (
"aunefyren/wrapperr/models"
"encoding/json"
"io/ioutil"
"net/http"
@ -15,7 +16,7 @@ var strong bool = true
var x_plex_model string = "Plex OAuth"
var x_plex_language string = "en"
func GetPin(ClientKey string, WrapperrVersion string) (*PlexGetPinReply, error) {
func GetPin(ClientKey string, WrapperrVersion string) (*models.PlexGetPinReply, error) {
url_string := "https://plex.tv/api/v2/pins"
@ -44,7 +45,7 @@ func GetPin(ClientKey string, WrapperrVersion string) (*PlexGetPinReply, error)
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
var body_reply PlexGetPinReply
var body_reply models.PlexGetPinReply
json.Unmarshal(body, &body_reply)
if err != nil {
return nil, err
@ -62,7 +63,7 @@ func GetLoginURLString(client_id string, code string, home_url string) string {
}
func GetPlexAuthLogin(ID int, Code string, WrapperrVersion string, ClientKey string) (*PlexGetPinReply, error) {
func GetPlexAuthLogin(ID int, Code string, WrapperrVersion string, ClientKey string) (*models.PlexGetPinReply, error) {
url_string := "https://plex.tv/api/v2/pins/" + strconv.Itoa(ID)
@ -91,7 +92,7 @@ func GetPlexAuthLogin(ID int, Code string, WrapperrVersion string, ClientKey str
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
var body_reply PlexGetPinReply
var body_reply models.PlexGetPinReply
json.Unmarshal(body, &body_reply)
if err != nil {
return nil, err
@ -100,7 +101,7 @@ func GetPlexAuthLogin(ID int, Code string, WrapperrVersion string, ClientKey str
return &body_reply, nil
}
func PlexAuthValidateToken(PlexAuth string, ClientKey string, WrapperrVersion string) (*PlexGetUserReply, error) {
func PlexAuthValidateToken(PlexAuth string, ClientKey string, WrapperrVersion string) (*models.PlexGetUserReply, error) {
url_string := "https://plex.tv/api/v2/user"
@ -130,7 +131,7 @@ func PlexAuthValidateToken(PlexAuth string, ClientKey string, WrapperrVersion st
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
var body_reply PlexGetUserReply
var body_reply models.PlexGetUserReply
json.Unmarshal(body, &body_reply)
if err != nil {
return nil, err

View file

@ -1,6 +1,8 @@
package main
package modules
import (
"aunefyren/wrapperr/models"
"aunefyren/wrapperr/utilities"
"encoding/json"
"errors"
"io/ioutil"
@ -13,7 +15,7 @@ import (
func TautulliTestConnection(TautulliPort int, TautulliIP string, TautulliHttps bool, TautulliRoot string, TautulliApiKey string) (bool, error) {
url_string, err := BuildURL(TautulliPort, TautulliIP, TautulliHttps, TautulliRoot)
url_string, err := utilities.BuildURL(TautulliPort, TautulliIP, TautulliHttps, TautulliRoot)
if err != nil {
log.Println(err)
return false, errors.New("Failed to build Tautulli connection URL.")
@ -41,7 +43,7 @@ func TautulliTestConnection(TautulliPort int, TautulliIP string, TautulliHttps b
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
var body_reply TautulliStatusReply
var body_reply models.TautulliStatusReply
json.Unmarshal(body, &body_reply)
if err != nil {
log.Println(err)
@ -61,7 +63,7 @@ func TautulliTestConnection(TautulliPort int, TautulliIP string, TautulliHttps b
func TautulliGetUserId(TautulliPort int, TautulliIP string, TautulliHttps bool, TautulliRoot string, TautulliApiKey string, PlexUser string) (int, string, error) {
url_string, err := BuildURL(TautulliPort, TautulliIP, TautulliHttps, TautulliRoot)
url_string, err := utilities.BuildURL(TautulliPort, TautulliIP, TautulliHttps, TautulliRoot)
if err != nil {
log.Println(err)
return 0, "", errors.New("Failed to build Tautulli connection URL.")
@ -89,7 +91,7 @@ func TautulliGetUserId(TautulliPort int, TautulliIP string, TautulliHttps bool,
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
var body_reply TautulliGetUsersReply
var body_reply models.TautulliGetUsersReply
json.Unmarshal(body, &body_reply)
if err != nil {
log.Println(err)
@ -117,12 +119,12 @@ func TautulliGetUserId(TautulliPort int, TautulliIP string, TautulliHttps bool,
return 0, "", errors.New("Failed to find user.")
}
func TautulliDownloadStatistics(TautulliPort int, TautulliIP string, TautulliHttps bool, TautulliRoot string, TautulliApiKey string, TautulliLength int, Libraries string, Grouping string, StartDate string) ([]TautulliHistoryItem, error) {
func TautulliDownloadStatistics(TautulliPort int, TautulliIP string, TautulliHttps bool, TautulliRoot string, TautulliApiKey string, TautulliLength int, Libraries string, Grouping string, StartDate string) ([]models.TautulliHistoryItem, error) {
url_string, err := BuildURL(TautulliPort, TautulliIP, TautulliHttps, TautulliRoot)
url_string, err := utilities.BuildURL(TautulliPort, TautulliIP, TautulliHttps, TautulliRoot)
if err != nil {
log.Println(err)
return []TautulliHistoryItem{}, errors.New("Failed to build Tautulli connection URL.")
return []models.TautulliHistoryItem{}, errors.New("Failed to build Tautulli connection URL.")
}
url_string = url_string + "api/v2/" + "?apikey=" + TautulliApiKey + "&cmd=get_history&order_column=date&order_dir=desc&include_activity=0" + Libraries + "&grouping=" + Grouping + "&length=" + strconv.Itoa(TautulliLength) + "&start_date=" + StartDate
@ -133,7 +135,7 @@ func TautulliDownloadStatistics(TautulliPort int, TautulliIP string, TautulliHtt
req, err := http.NewRequest("GET", url_string, payload)
if err != nil {
log.Println(err)
return []TautulliHistoryItem{}, errors.New("Failed to reach Tautulli server.")
return []models.TautulliHistoryItem{}, errors.New("Failed to reach Tautulli server.")
}
req.Header.Add("Accept", "application/json")
@ -141,17 +143,17 @@ func TautulliDownloadStatistics(TautulliPort int, TautulliIP string, TautulliHtt
res, err := http.DefaultClient.Do(req)
if err != nil {
log.Println(err)
return []TautulliHistoryItem{}, errors.New("Failed to reach Tautulli server.")
return []models.TautulliHistoryItem{}, errors.New("Failed to reach Tautulli server.")
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
var body_reply TautulliGetHistoryReply
var body_reply models.TautulliGetHistoryReply
json.Unmarshal(body, &body_reply)
if err != nil {
log.Println(err)
return []TautulliHistoryItem{}, errors.New("Failed to parse Tautulli response.")
return []models.TautulliHistoryItem{}, errors.New("Failed to parse Tautulli response.")
}
return body_reply.Response.Data.Data, nil

View file

@ -1,6 +1,10 @@
package main
package routes
import (
"aunefyren/wrapperr/files"
"aunefyren/wrapperr/models"
"aunefyren/wrapperr/modules"
"aunefyren/wrapperr/utilities"
"encoding/json"
"errors"
"fmt"
@ -13,27 +17,27 @@ import (
// API route used to retrieve the Wrapperr configuration file.
func ApiGetConfig(w http.ResponseWriter, r *http.Request) {
payload, err := AuthorizeToken(w, r)
payload, err := modules.AuthorizeToken(w, r)
if err == nil && payload.Admin {
config, err := GetConfig()
config, err := files.GetConfig()
if err != nil {
respond_default_error(w, r, errors.New("Failed to retrieve Wrapperr configuration."), 500)
utilities.RespondDefaultError(w, r, errors.New("Failed to retrieve Wrapperr configuration."), 500)
} else {
config_reply := ConfigReply{
config_reply := models.ConfigReply{
Data: *config,
Message: "Retrieved Wrapperr config.",
Error: false,
Username: payload.Username,
}
ip_string := GetOriginIPString(w, r)
ip_string := utilities.GetOriginIPString(w, r)
log.Println("Retrieved Wrapperr configuration." + ip_string)
respondWithJSON(w, http.StatusOK, config_reply)
utilities.RespondWithJSON(w, http.StatusOK, config_reply)
return
}
@ -41,13 +45,13 @@ func ApiGetConfig(w http.ResponseWriter, r *http.Request) {
} else if !payload.Admin {
log.Println(errors.New("Only the admin can retrieve the config."))
respond_default_error(w, r, errors.New("Only the admin can retrieve the config."), 401)
utilities.RespondDefaultError(w, r, errors.New("Only the admin can retrieve the config."), 401)
return
} else {
log.Println(err)
respond_default_error(w, r, errors.New("Failed to authorize JWT token."), 500)
utilities.RespondDefaultError(w, r, errors.New("Failed to authorize JWT token."), 500)
return
}
@ -56,15 +60,15 @@ func ApiGetConfig(w http.ResponseWriter, r *http.Request) {
// API route used to update the Wrapperr configuration file.
func ApiSetConfig(w http.ResponseWriter, r *http.Request) {
payload, err := AuthorizeToken(w, r)
payload, err := modules.AuthorizeToken(w, r)
if err == nil && payload.Admin {
config, err := GetConfig()
config, err := files.GetConfig()
if err != nil {
log.Println(err)
respond_default_error(w, r, errors.New("Failed to retrieve Wrapperr configuration."), 500)
utilities.RespondDefaultError(w, r, errors.New("Failed to retrieve Wrapperr configuration."), 500)
} else {
// Read payload from Post input
@ -72,35 +76,35 @@ func ApiSetConfig(w http.ResponseWriter, r *http.Request) {
if err != nil {
log.Println("Failed to parse config request. Error:")
log.Println(err)
respond_default_error(w, r, errors.New("Failed to parse config request."), 401)
utilities.RespondDefaultError(w, r, errors.New("Failed to parse config request."), 401)
return
}
var config_payload SetWrapperrConfig
var config_payload models.SetWrapperrConfig
json.Unmarshal(reqBody, &config_payload)
// Confirm username length
if config_payload.DataType == "" {
log.Println("Cannot set new config. Invalid data type recieved.")
respond_default_error(w, r, errors.New("Data type specified is invalid."), 400)
utilities.RespondDefaultError(w, r, errors.New("Data type specified is invalid."), 400)
return
}
if config_payload.DataType == "tautulli_config" {
config.TautulliConfig = config_payload.TautulliConfig
err = SaveConfig(config)
err = files.SaveConfig(config)
if err != nil {
respond_default_error(w, r, errors.New("Failed to save new Wrapperr configuration."), 500)
utilities.RespondDefaultError(w, r, errors.New("Failed to save new Wrapperr configuration."), 500)
}
} else if config_payload.DataType == "wrapperr_customize" {
config.WrapperrCustomize = config_payload.WrapperrCustomize
err = SaveConfig(config)
err = files.SaveConfig(config)
if err != nil {
respond_default_error(w, r, errors.New("Failed to save new Wrapperr configuration."), 500)
utilities.RespondDefaultError(w, r, errors.New("Failed to save new Wrapperr configuration."), 500)
return
}
@ -110,7 +114,7 @@ func ApiSetConfig(w http.ResponseWriter, r *http.Request) {
if err != nil {
log.Println("Failed to set the new time zone. Error: ")
log.Println(err)
respond_default_error(w, r, errors.New("Given time zone is invalid."), 401)
utilities.RespondDefaultError(w, r, errors.New("Given time zone is invalid."), 401)
return
}
@ -126,10 +130,10 @@ func ApiSetConfig(w http.ResponseWriter, r *http.Request) {
config.WrappedStart = config_payload.WrapperrData.WrappedStart
config.WinterTheme = config_payload.WrapperrData.WinterTheme
err = SaveConfig(config)
err = files.SaveConfig(config)
if err != nil {
log.Println(err)
respond_default_error(w, r, errors.New("Failed to save new Wrapperr configuration."), 500)
utilities.RespondDefaultError(w, r, errors.New("Failed to save new Wrapperr configuration."), 500)
return
}
@ -137,7 +141,7 @@ func ApiSetConfig(w http.ResponseWriter, r *http.Request) {
} else {
log.Println("Cannot set new config. Invalid data type recieved. Type: " + config_payload.DataType)
respond_default_error(w, r, errors.New("Failed to save new Wrapperr confguration."), 400)
utilities.RespondDefaultError(w, r, errors.New("Failed to save new Wrapperr confguration."), 400)
return
}
@ -145,7 +149,7 @@ func ApiSetConfig(w http.ResponseWriter, r *http.Request) {
log.Println("Clear cache setting set to true. Clearing cache.")
err = ClearCache()
err = files.ClearCache()
if err != nil {
log.Println("Failed to clear cache:")
log.Println(err)
@ -153,7 +157,7 @@ func ApiSetConfig(w http.ResponseWriter, r *http.Request) {
}
log.Println("New Wrapperr configuration saved for type: " + config_payload.DataType + ".")
respond_default_okay(w, r, "Saved new Wrapperr config.")
utilities.RespondDefaultOkay(w, r, "Saved new Wrapperr config.")
return
}
@ -161,13 +165,13 @@ func ApiSetConfig(w http.ResponseWriter, r *http.Request) {
} else if !payload.Admin {
log.Println("User not authenticated as admin.")
respond_default_error(w, r, errors.New("User not authenticated as admin."), 401)
utilities.RespondDefaultError(w, r, errors.New("User not authenticated as admin."), 401)
return
} else {
log.Println(err)
respond_default_error(w, r, errors.New("Failed to save config."), 500)
utilities.RespondDefaultError(w, r, errors.New("Failed to save config."), 500)
return
}
@ -176,84 +180,84 @@ func ApiSetConfig(w http.ResponseWriter, r *http.Request) {
// API route used to update admin accounts details (username, password).
func ApiUpdateAdmin(w http.ResponseWriter, r *http.Request) {
admin, err := GetAdminState()
admin, err := files.GetAdminState()
if err != nil {
log.Println("Failed to load admin state. Error: ")
log.Println(err)
respond_default_error(w, r, errors.New("Failed to load admin state."), 500)
utilities.RespondDefaultError(w, r, errors.New("Failed to load admin state."), 500)
return
}
if !admin {
log.Print("Admin update failed. No admin is configured.")
respond_default_error(w, r, errors.New("No admin is configured."), 400)
utilities.RespondDefaultError(w, r, errors.New("No admin is configured."), 400)
return
} else {
payload, err := AuthorizeToken(w, r)
payload, err := modules.AuthorizeToken(w, r)
if err == nil && payload.Admin {
// Read payload from Post input
reqBody, _ := ioutil.ReadAll(r.Body)
var admin_payload AdminConfig
var admin_payload models.AdminConfig
json.Unmarshal(reqBody, &admin_payload)
// Confirm username length
if len(admin_payload.AdminUsername) < 4 {
log.Println("Admin update 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)
utilities.RespondDefaultError(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 update 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)
utilities.RespondDefaultError(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)
hash, err := utilities.HashAndSalt(admin_payload.AdminPassword)
if err != nil {
log.Println(err)
respond_default_error(w, r, errors.New("Failed to hash your password."), 500)
utilities.RespondDefaultError(w, r, errors.New("Failed to hash your password."), 500)
return
}
admin_payload.AdminPassword = hash
// Save new admin config
err = SaveAdminConfig(admin_payload)
err = files.SaveAdminConfig(admin_payload)
if err != nil {
log.Println(err)
respond_default_error(w, r, errors.New("Failed to update admin."), 500)
utilities.RespondDefaultError(w, r, errors.New("Failed to update admin."), 500)
return
}
// Update the private key to delete old logins
_, err = UpdatePrivateKey()
_, err = files.UpdatePrivateKey()
if err != nil {
log.Println(err)
respond_default_error(w, r, errors.New("Admin account updated, but failed to rotate private key. Old logins still function."), 500)
utilities.RespondDefaultError(w, r, errors.New("Admin account updated, but failed to rotate private key. Old logins still function."), 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.")
utilities.RespondDefaultOkay(w, r, "Admin created.")
return
} else if !payload.Admin {
log.Println("User not authenticated as admin.")
respond_default_error(w, r, errors.New("User not authenticated as admin."), 401)
utilities.RespondDefaultError(w, r, errors.New("User not authenticated as admin."), 401)
return
} else {
log.Println(err)
respond_default_error(w, r, errors.New("Failed to update admin."), 500)
utilities.RespondDefaultError(w, r, errors.New("Failed to update admin."), 500)
return
}
@ -264,24 +268,24 @@ func ApiUpdateAdmin(w http.ResponseWriter, r *http.Request) {
// API route which validates an admin JWT token
func ApiValidateAdmin(w http.ResponseWriter, r *http.Request) {
payload, err := AuthorizeToken(w, r)
payload, err := modules.AuthorizeToken(w, r)
if err == nil && payload.Admin {
log.Println("Admin login session JWT validated.")
respond_default_okay(w, r, "The admin login session is valid.")
utilities.RespondDefaultOkay(w, r, "The admin login session is valid.")
return
} else if !payload.Admin {
log.Println("User not authenticated as admin.")
respond_default_error(w, r, errors.New("User not authenticated as admin."), 401)
utilities.RespondDefaultError(w, r, errors.New("User not authenticated as admin."), 401)
return
} else {
log.Println(err)
respond_default_error(w, r, errors.New("Failed to validate admin."), 500)
utilities.RespondDefaultError(w, r, errors.New("Failed to validate admin."), 500)
return
}
@ -290,41 +294,41 @@ func ApiValidateAdmin(w http.ResponseWriter, r *http.Request) {
// API route which retrieves lines from the log file
func ApiGetLog(w http.ResponseWriter, r *http.Request) {
payload, err := AuthorizeToken(w, r)
payload, err := modules.AuthorizeToken(w, r)
if err != nil {
log.Println(err)
respond_default_error(w, r, errors.New("Failed to validate admin."), 500)
utilities.RespondDefaultError(w, r, errors.New("Failed to validate admin."), 500)
return
} else if !payload.Admin {
log.Println("User not authenticated as admin.")
respond_default_error(w, r, errors.New("User not authenticated as admin."), 401)
utilities.RespondDefaultError(w, r, errors.New("User not authenticated as admin."), 401)
return
}
log_lines, err := GetLogLines()
log_lines, err := files.GetLogLines()
if err != nil {
log.Println("Error trying to retrieve log lines. Error: ")
log.Println(err)
respond_default_error(w, r, errors.New("Failed to retrieve log file."), 500)
utilities.RespondDefaultError(w, r, errors.New("Failed to retrieve log file."), 500)
return
}
log_lines_return := WrapperrLogLineReply{
log_lines_return := models.WrapperrLogLineReply{
Message: "Log lines retrieved",
Error: false,
Data: log_lines,
Limit: max_lines_returned,
Limit: files.GetMaxLogLinesReturned(),
}
log.Println("Log lines retrieved for admin.")
respondWithJSON(w, http.StatusOK, log_lines_return)
utilities.RespondWithJSON(w, http.StatusOK, log_lines_return)
return
}

View file

@ -1,6 +1,10 @@
package main
package routes
import (
"aunefyren/wrapperr/files"
"aunefyren/wrapperr/models"
"aunefyren/wrapperr/modules"
"aunefyren/wrapperr/utilities"
"encoding/json"
"errors"
"fmt"
@ -14,26 +18,26 @@ import (
// 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()
configured_bool, err := files.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)
utilities.RespondDefaultError(w, r, errors.New("Failed to retrieve configuration state."), 500)
return
}
config, err := GetConfig()
config, err := files.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)
utilities.RespondDefaultError(w, r, errors.New("Failed to retrieve configuration state."), 500)
return
}
version_reply := WrapperrVersion{
version_reply := models.WrapperrVersion{
WrapperrVersion: config.WrapperrVersion,
ApplicationName: config.ApplicationName,
PlexAuth: config.PlexAuth,
@ -46,11 +50,11 @@ func ApiGetWrapperrVersion(w http.ResponseWriter, r *http.Request) {
Error: false,
}
ip_string := GetOriginIPString(w, r)
ip_string := utilities.GetOriginIPString(w, r)
log.Println("Retrieved Wrapperr version." + ip_string)
respondWithJSON(w, http.StatusOK, version_reply)
utilities.RespondWithJSON(w, http.StatusOK, version_reply)
return
}
@ -58,24 +62,24 @@ func ApiGetWrapperrVersion(w http.ResponseWriter, r *http.Request) {
// API route which returns if whether or not a Wrapperr admin is configured.
func ApiGetAdminState(w http.ResponseWriter, r *http.Request) {
admin, err := GetAdminState()
admin, err := files.GetAdminState()
if err != nil {
log.Println(err)
log.Println("Failed to load admin state.")
return
}
boolean_reply := BooleanReply{
boolean_reply := models.BooleanReply{
Message: "Retrieved Wrapperr version.",
Error: false,
Data: admin,
}
ip_string := GetOriginIPString(w, r)
ip_string := utilities.GetOriginIPString(w, r)
log.Println("Retrieved Wrapperr admin state." + ip_string)
respondWithJSON(w, http.StatusOK, boolean_reply)
utilities.RespondWithJSON(w, http.StatusOK, boolean_reply)
return
}
@ -83,7 +87,7 @@ func ApiGetAdminState(w http.ResponseWriter, r *http.Request) {
// API route which retrieves the Wrapperr settings needed for the front-end.
func ApiGetFunctions(w http.ResponseWriter, r *http.Request) {
config, err := GetConfig()
config, err := files.GetConfig()
if err != nil {
log.Println(err)
log.Println("Failed to load configuration file.")
@ -91,18 +95,18 @@ func ApiGetFunctions(w http.ResponseWriter, r *http.Request) {
return
}
function_reply := WrapperrFunctions{
function_reply := models.WrapperrFunctions{
WrapperrVersion: config.WrapperrVersion,
PlexAuth: config.PlexAuth,
WrapperrCustomize: config.WrapperrCustomize,
CreateShareLinks: config.CreateShareLinks,
}
ip_string := GetOriginIPString(w, r)
ip_string := utilities.GetOriginIPString(w, r)
log.Println("Retrieved Wrapperr functions." + ip_string)
respondWithJSON(w, http.StatusOK, function_reply)
utilities.RespondWithJSON(w, http.StatusOK, function_reply)
return
}
@ -110,7 +114,7 @@ func ApiGetFunctions(w http.ResponseWriter, r *http.Request) {
// API route used to create the admin account and claim the Wrapperr server
func ApiCreateAdmin(w http.ResponseWriter, r *http.Request) {
admin, err := GetAdminState()
admin, err := files.GetAdminState()
if err != nil {
log.Println(err)
log.Println("Failed to load admin state.")
@ -120,52 +124,52 @@ func ApiCreateAdmin(w http.ResponseWriter, r *http.Request) {
if admin {
log.Println("Admin creation failed. Admin already configured.")
respond_default_error(w, r, errors.New("Admin already configured."), 401)
utilities.RespondDefaultError(w, r, errors.New("Admin already configured."), 401)
return
} else {
// Read payload from Post input
reqBody, _ := ioutil.ReadAll(r.Body)
var admin_payload AdminConfig
var admin_payload models.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)
utilities.RespondDefaultError(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)
utilities.RespondDefaultError(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)
hash, err := utilities.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)
utilities.RespondDefaultError(w, r, errors.New("Failed to hash your password."), 500)
return
}
admin_payload.AdminPassword = hash
// Save new admin config
err = SaveAdminConfig(admin_payload)
err = files.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)
utilities.RespondDefaultError(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.")
utilities.RespondDefaultOkay(w, r, "Admin created.")
return
}
@ -174,27 +178,27 @@ func ApiCreateAdmin(w http.ResponseWriter, r *http.Request) {
// API route which returns if whether or not Wrapperr is configured.
func ApiWrapperrConfigured(w http.ResponseWriter, r *http.Request) {
bool, err := GetConfigState()
bool, err := files.GetConfigState()
if err != nil {
log.Panicln(err)
respond_default_error(w, r, errors.New("Failed to retrieve confguration state."), 500)
utilities.RespondDefaultError(w, r, errors.New("Failed to retrieve confguration state."), 500)
return
} else {
boolean_reply := BooleanReply{
boolean_reply := models.BooleanReply{
Message: "Retrieved Wrapperr configuration state.",
Error: false,
Data: bool,
}
ip_string := GetOriginIPString(w, r)
ip_string := utilities.GetOriginIPString(w, r)
log.Println("Retrieved Wrapperr configuration state." + ip_string)
respondWithJSON(w, http.StatusOK, boolean_reply)
utilities.RespondWithJSON(w, http.StatusOK, boolean_reply)
return
}
@ -204,7 +208,7 @@ func ApiWrapperrConfigured(w http.ResponseWriter, r *http.Request) {
// 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()
admin, err := files.GetAdminState()
if err != nil {
log.Println(err)
log.Println("Failed to load admin state.")
@ -214,11 +218,11 @@ func ApiLogInAdmin(w http.ResponseWriter, r *http.Request) {
if !admin {
log.Println("Admin login failed. Admin is not configured.")
respond_default_error(w, r, errors.New("No admin configured."), 400)
utilities.RespondDefaultError(w, r, errors.New("No admin configured."), 400)
return
} else {
admin_config, err := GetAdminConfig()
admin_config, err := files.GetAdminConfig()
if err != nil {
log.Println(err)
log.Println("Failed to load admin config.")
@ -228,55 +232,55 @@ func ApiLogInAdmin(w http.ResponseWriter, r *http.Request) {
// Read payload from Post input
reqBody, _ := ioutil.ReadAll(r.Body)
var admin_payload AdminConfig
var admin_payload models.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)
utilities.RespondDefaultError(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)
utilities.RespondDefaultError(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)
password_validity := utilities.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)
ip_string := utilities.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)
utilities.RespondDefaultError(w, r, errors.New("Login failed. Username or password is incorrect."), 401)
return
}
token, err := CreateToken(admin_config.AdminUsername, true, "")
token, err := modules.CreateToken(admin_config.AdminUsername, true, "")
if err != nil {
log.Println(err)
respond_default_error(w, r, errors.New("Failed to create JWT token."), 500)
utilities.RespondDefaultError(w, r, errors.New("Failed to create JWT token."), 500)
return
}
string_reply := StringReply{
string_reply := models.StringReply{
Message: "Login cookie created",
Error: false,
Data: token,
}
ip_string := GetOriginIPString(w, r)
ip_string := utilities.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)
utilities.RespondWithJSON(w, http.StatusOK, string_reply)
return
}
@ -288,33 +292,33 @@ func ApiGetTautulliConncection(w http.ResponseWriter, r *http.Request) {
// Read payload from Post input
reqBody, _ := ioutil.ReadAll(r.Body)
var tautulli_connection TautulliConfig
var tautulli_connection models.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)
utilities.RespondDefaultError(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)
tautulli_state, err := modules.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)
utilities.RespondDefaultError(w, r, errors.New("Failed to reach Tautulli server."), 500)
return
}
boolean_reply := BooleanReply{
boolean_reply := models.BooleanReply{
Message: "Tested Tautulli connection.",
Error: false,
Data: tautulli_state,
}
ip_string := GetOriginIPString(w, r)
ip_string := utilities.GetOriginIPString(w, r)
log.Println("Tested Tautulli connection." + ip_string)
respondWithJSON(w, http.StatusOK, boolean_reply)
utilities.RespondWithJSON(w, http.StatusOK, boolean_reply)
return
}
@ -322,39 +326,39 @@ func ApiGetTautulliConncection(w http.ResponseWriter, r *http.Request) {
// Get shareable link
func ApiGetShareLink(w http.ResponseWriter, r *http.Request) {
config_bool, err := GetConfigState()
config_bool, err := files.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)
utilities.RespondDefaultError(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)
utilities.RespondDefaultError(w, r, errors.New("Wrapperr is not configured."), 400)
return
}
config, err := GetConfig()
config, err := files.GetConfig()
if err != nil {
log.Println(err)
respond_default_error(w, r, errors.New("Failed to load Wrapperr configuration."), 500)
utilities.RespondDefaultError(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)
utilities.RespondDefaultError(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)
utilities.RespondDefaultError(w, r, errors.New("Shareable links are not enabled in the Wrapperr configuration."), 400)
return
}
@ -363,11 +367,11 @@ func ApiGetShareLink(w http.ResponseWriter, r *http.Request) {
if err != nil {
log.Println(err)
respond_default_error(w, r, errors.New("Failed to parse payload for request."), 500)
utilities.RespondDefaultError(w, r, errors.New("Failed to parse payload for request."), 500)
return
}
var link_payload WrapperrShareLinkGetRequest
var link_payload models.WrapperrShareLinkGetRequest
json.Unmarshal(reqBody, &link_payload)
hash_array := strings.Split(link_payload.Hash, "-")
@ -375,7 +379,7 @@ func ApiGetShareLink(w http.ResponseWriter, r *http.Request) {
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)
utilities.RespondDefaultError(w, r, errors.New("Failed to parse payload hash for Wrapperr link."), 500)
return
}
@ -390,11 +394,11 @@ func ApiGetShareLink(w http.ResponseWriter, r *http.Request) {
hash = hash + hash_array[j]
}
share_link_object, err := GetLink(user_id)
share_link_object, err := files.GetLink(user_id)
if err != nil {
log.Println(err)
respond_default_error(w, r, err, 500)
utilities.RespondDefaultError(w, r, err, 500)
return
}
@ -404,7 +408,7 @@ func ApiGetShareLink(w http.ResponseWriter, r *http.Request) {
if err != nil {
log.Println(err)
respond_default_error(w, r, errors.New("Failed to retrieve saved Wrapperr link."), 500)
utilities.RespondDefaultError(w, r, errors.New("Failed to retrieve saved Wrapperr link."), 500)
return
}
@ -416,11 +420,11 @@ func ApiGetShareLink(w http.ResponseWriter, r *http.Request) {
share_link_object.Message = "Shared link retrieved."
share_link_object.Error = false
ip_string := GetOriginIPString(w, r)
ip_string := utilities.GetOriginIPString(w, r)
log.Println("Retrieved Wrapperr share link made by User ID: " + user_id + "." + ip_string)
respondWithJSON(w, http.StatusOK, share_link_object)
utilities.RespondWithJSON(w, http.StatusOK, share_link_object)
return
} else {
@ -430,7 +434,7 @@ func ApiGetShareLink(w http.ResponseWriter, r *http.Request) {
if linkTime.Before(currentTime) {
share_link_object.Expired = true
err = SaveLink(share_link_object)
err = files.SaveLink(share_link_object)
if err != nil {
log.Println(err)
}
@ -439,7 +443,7 @@ func ApiGetShareLink(w http.ResponseWriter, r *http.Request) {
}
log.Println("Failed to retrieve Wrapperr share link with hash: " + link_payload.Hash + ".")
respond_default_error(w, r, return_error, 401)
utilities.RespondDefaultError(w, r, return_error, 401)
return
}

View file

@ -1,6 +1,10 @@
package main
package routes
import (
"aunefyren/wrapperr/files"
"aunefyren/wrapperr/models"
"aunefyren/wrapperr/modules"
"aunefyren/wrapperr/utilities"
"encoding/json"
"errors"
"io/ioutil"
@ -15,24 +19,24 @@ import (
func ApiWrapperGetStatistics(w http.ResponseWriter, r *http.Request) {
ip_string := GetOriginIPString(w, r)
ip_string := utilities.GetOriginIPString(w, r)
log.Println("New /get/statistics request." + ip_string)
bool_state, err := GetConfigState()
bool_state, err := files.GetConfigState()
if err != nil {
log.Println(err)
respond_default_error(w, r, errors.New("Failed to retrieve confguration state."), 500)
utilities.RespondDefaultError(w, r, errors.New("Failed to retrieve confguration state."), 500)
return
} else if !bool_state {
log.Println("Wrapperr get statistics failed. Configuration state function retrieved false response.")
respond_default_error(w, r, errors.New("Can't retrieve statistics because Wrapperr is not configured."), 400)
utilities.RespondDefaultError(w, r, errors.New("Can't retrieve statistics because Wrapperr is not configured."), 400)
return
}
config, err := GetConfig()
config, err := files.GetConfig()
if err != nil {
log.Println(err)
respond_default_error(w, r, errors.New("Failed to load Wrapperr configuration."), 500)
utilities.RespondDefaultError(w, r, errors.New("Failed to load Wrapperr configuration."), 500)
return
}
@ -41,14 +45,14 @@ func ApiWrapperGetStatistics(w http.ResponseWriter, r *http.Request) {
// Check every Tautulli server
for i := 0; i < len(config.TautulliConfig); i++ {
log.Println("Checking Tautulli server '" + config.TautulliConfig[i].TautulliName + "'." + ip_string)
tautulli_state, err := TautulliTestConnection(config.TautulliConfig[i].TautulliPort, config.TautulliConfig[i].TautulliIP, config.TautulliConfig[i].TautulliHttps, config.TautulliConfig[i].TautulliRoot, config.TautulliConfig[i].TautulliApiKey)
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(err)
respond_default_error(w, r, errors.New("Failed to reach Tautulli server '"+config.TautulliConfig[i].TautulliName+"'."), 500)
utilities.RespondDefaultError(w, r, errors.New("Failed to reach Tautulli server '"+config.TautulliConfig[i].TautulliName+"'."), 500)
return
} else if !tautulli_state {
log.Println("Failed to ping Tautulli server '" + config.TautulliConfig[i].TautulliName + "' before retrieving statistics.")
respond_default_error(w, r, errors.New("Failed to reach Tautulli server '"+config.TautulliConfig[i].TautulliName+"'."), 400)
utilities.RespondDefaultError(w, r, errors.New("Failed to reach Tautulli server '"+config.TautulliConfig[i].TautulliName+"'."), 400)
return
}
}
@ -62,13 +66,13 @@ func ApiWrapperGetStatistics(w http.ResponseWriter, r *http.Request) {
var admin bool = false
// Try to authorize bearer token from header
payload, err := AuthorizeToken(w, r)
payload, err := modules.AuthorizeToken(w, r)
// 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(err)
respond_default_error(w, r, errors.New("Failed to authorize request."), 401)
utilities.RespondDefaultError(w, r, errors.New("Failed to authorize request."), 401)
return
} else if config.PlexAuth {
auth_passed = true
@ -82,10 +86,10 @@ func ApiWrapperGetStatistics(w http.ResponseWriter, r *http.Request) {
// If the user is not an admin, and PlexAuth is enabled, validate and retrieve details from Plex Token in payload
if !admin && config.PlexAuth {
plex_object, err := PlexAuthValidateToken(payload.AuthToken, config.ClientKey, config.WrapperrVersion)
plex_object, err := modules.PlexAuthValidateToken(payload.AuthToken, config.ClientKey, config.WrapperrVersion)
if err != nil {
log.Println(err)
respond_default_error(w, r, errors.New("Could not validate Plex Auth login."), 500)
utilities.RespondDefaultError(w, r, errors.New("Could not validate Plex Auth login."), 500)
return
}
@ -97,13 +101,13 @@ func ApiWrapperGetStatistics(w http.ResponseWriter, r *http.Request) {
// Read payload from Post input
reqBody, _ := ioutil.ReadAll(r.Body)
var wrapperr_request SearchWrapperrRequest
var wrapperr_request models.SearchWrapperrRequest
json.Unmarshal(reqBody, &wrapperr_request)
// If auth is not passed, caching mode is false, and no PlexIdentity was recieved, mark it as a bad request
if wrapperr_request.PlexIdentity == "" && !auth_passed && !wrapperr_request.CachingMode {
log.Println("Cannot retrieve statistics because search parameter is invalid.")
respond_default_error(w, r, errors.New("Invalid search parameter."), 400)
utilities.RespondDefaultError(w, r, errors.New("Invalid search parameter."), 400)
return
}
@ -113,7 +117,7 @@ func ApiWrapperGetStatistics(w http.ResponseWriter, r *http.Request) {
UserNameFound := false
for i := 0; i < len(config.TautulliConfig); i++ {
new_id, new_username, err := TautulliGetUserId(config.TautulliConfig[i].TautulliPort, config.TautulliConfig[i].TautulliIP, config.TautulliConfig[i].TautulliHttps, config.TautulliConfig[i].TautulliRoot, config.TautulliConfig[i].TautulliApiKey, wrapperr_request.PlexIdentity)
new_id, new_username, err := modules.TautulliGetUserId(config.TautulliConfig[i].TautulliPort, config.TautulliConfig[i].TautulliIP, config.TautulliConfig[i].TautulliHttps, config.TautulliConfig[i].TautulliRoot, config.TautulliConfig[i].TautulliApiKey, wrapperr_request.PlexIdentity)
if err == nil {
UserNameFound = true
@ -124,7 +128,7 @@ func ApiWrapperGetStatistics(w http.ResponseWriter, r *http.Request) {
if !UserNameFound {
log.Println(err)
respond_default_error(w, r, errors.New("Could not find a matching user."), 500)
utilities.RespondDefaultError(w, r, errors.New("Could not find a matching user."), 500)
return
}
}
@ -132,14 +136,14 @@ func ApiWrapperGetStatistics(w http.ResponseWriter, r *http.Request) {
// If caching mode is false and user is admin, return bad request error
if !wrapperr_request.CachingMode && admin {
log.Println("Caching mode deactivated, but admin login session retrieved.")
respond_default_error(w, r, errors.New("You can not retrieve stats as admin."), 400)
utilities.RespondDefaultError(w, r, errors.New("You can not retrieve stats as admin."), 400)
return
}
// If caching mode is true and user is not admin, return bad request error
if wrapperr_request.CachingMode && !admin {
log.Println("Caching mode recieved, but user was not verified as admin.")
respond_default_error(w, r, errors.New("Only the admin can perform caching."), 401)
utilities.RespondDefaultError(w, r, errors.New("Only the admin can perform caching."), 401)
return
}
@ -151,7 +155,7 @@ func ApiWrapperGetStatistics(w http.ResponseWriter, r *http.Request) {
if !config.UseCache {
log.Println("Admin attempted to use cache mode, but the cache feature is disabled in the config.")
respond_default_error(w, r, errors.New("Caching mode enabled, but the cache feature is disabled in the settings."), 500)
utilities.RespondDefaultError(w, r, errors.New("Caching mode enabled, but the cache feature is disabled in the settings."), 500)
return
}
}
@ -159,19 +163,19 @@ func ApiWrapperGetStatistics(w http.ResponseWriter, r *http.Request) {
// If no username and no user_id has been declared at this point, something is wrong. Return error.
if user_name == "" && user_id == 0 {
log.Println("At this point the user should have been verified, but username and ID is empty.")
respond_default_error(w, r, errors.New("User validation error."), 500)
utilities.RespondDefaultError(w, r, errors.New("User validation error."), 500)
return
}
log.Println("4. User details confirmed for " + user_name + " (" + strconv.Itoa(user_id) + ")." + ip_string)
// Create empty array object for each day in Wrapped period. If cache is enabled, call GetCache() and replace the empty object.
wrapperr_data := []WrapperrDay{}
wrapperr_data := []models.WrapperrDay{}
if config.UseCache {
wrapperr_data, err = GetCache()
wrapperr_data, err = files.GetCache()
if err != nil {
log.Println(err)
respond_default_error(w, r, errors.New("Failed to load cache file."), 500)
utilities.RespondDefaultError(w, r, errors.New("Failed to load cache file."), 500)
return
}
}
@ -188,10 +192,10 @@ func ApiWrapperGetStatistics(w http.ResponseWriter, r *http.Request) {
// If cache is enabled, send the object to SaveCache() for later use.
if config.UseCache {
err = SaveCache(&wrapperr_data)
err = files.SaveCache(&wrapperr_data)
if err != nil {
log.Println(err)
respond_default_error(w, r, errors.New("Failed to save new cache file for "+user_name+" ("+strconv.Itoa(user_id)+")."), 500)
utilities.RespondDefaultError(w, r, errors.New("Failed to save new cache file for "+user_name+" ("+strconv.Itoa(user_id)+")."), 500)
return
}
}
@ -201,22 +205,22 @@ func ApiWrapperGetStatistics(w http.ResponseWriter, r *http.Request) {
// If caching mode is in use, stop the proccess here and return the result to the user
if wrapperr_request.CachingMode {
boolean_reply := BooleanReply{
boolean_reply := models.BooleanReply{
Message: "Completed caching request.",
Error: false,
Data: wrapperr_data_complete,
}
ip_string := GetOriginIPString(w, r)
ip_string := utilities.GetOriginIPString(w, r)
log.Println("Caching request completed for " + user_name + " (" + strconv.Itoa(user_id) + ")." + ip_string)
respondWithJSON(w, http.StatusOK, boolean_reply)
utilities.RespondWithJSON(w, http.StatusOK, boolean_reply)
return
}
// Create reply object
var wrapperr_reply WrapperrStatisticsReply
var wrapperr_reply models.WrapperrStatisticsReply
wrapperr_reply.User.ID = user_id
wrapperr_reply.User.Name = user_name
wrapperr_reply.Date = time.Now().Format("2006-01-02")
@ -225,15 +229,15 @@ func ApiWrapperGetStatistics(w http.ResponseWriter, r *http.Request) {
// Loop through Wrapperr data and format reply
wrapperr_reply, err = WrapperrLoopData(user_id, config, wrapperr_data, wrapperr_reply)
ip_string = GetOriginIPString(w, r)
ip_string = utilities.GetOriginIPString(w, r)
log.Println("8. Wrapperr request completed for " + user_name + " (" + strconv.Itoa(user_id) + ")." + ip_string)
respondWithJSON(w, http.StatusOK, wrapperr_reply)
utilities.RespondWithJSON(w, http.StatusOK, wrapperr_reply)
return
}
func WrapperrDownloadDays(ID int, wrapperr_data []WrapperrDay, loop_interval int, config *WrapperrConfig) ([]WrapperrDay, bool, error) {
func WrapperrDownloadDays(ID int, wrapperr_data []models.WrapperrDay, loop_interval int, config *models.WrapperrConfig) ([]models.WrapperrDay, bool, error) {
// Define variables
var complete_date_loop bool = true
@ -274,7 +278,7 @@ func WrapperrDownloadDays(ID int, wrapperr_data []WrapperrDay, loop_interval int
}
// Clean array to populate with results
wrapperr_day := WrapperrDay{
wrapperr_day := models.WrapperrDay{
Date: current_loop_date,
Data: nil,
DataComplete: true,
@ -344,14 +348,14 @@ func WrapperrDownloadDays(ID int, wrapperr_data []WrapperrDay, loop_interval int
grouping = "0"
}
tautulli_data, err := TautulliDownloadStatistics(config.TautulliConfig[q].TautulliPort, config.TautulliConfig[q].TautulliIP, config.TautulliConfig[q].TautulliHttps, config.TautulliConfig[q].TautulliRoot, config.TautulliConfig[q].TautulliApiKey, config.TautulliConfig[q].TautulliLength, library_str, grouping, current_loop_date)
tautulli_data, err := modules.TautulliDownloadStatistics(config.TautulliConfig[q].TautulliPort, config.TautulliConfig[q].TautulliIP, config.TautulliConfig[q].TautulliHttps, config.TautulliConfig[q].TautulliRoot, config.TautulliConfig[q].TautulliApiKey, config.TautulliConfig[q].TautulliLength, library_str, grouping, current_loop_date)
if err != nil {
log.Println(err)
}
for j := 0; j < len(tautulli_data); j++ {
if tautulli_data[j].MediaType == "movie" || tautulli_data[j].MediaType == "episode" || tautulli_data[j].MediaType == "track" {
tautulli_entry := TautulliEntry{
tautulli_entry := models.TautulliEntry{
Date: tautulli_data[j].Date,
RowID: tautulli_data[j].RowID,
Duration: tautulli_data[j].Duration,
@ -412,22 +416,22 @@ func WrapperrDownloadDays(ID int, wrapperr_data []WrapperrDay, loop_interval int
return wrapperr_data, complete_date_loop, nil
}
func WrapperrLoopData(user_id int, config *WrapperrConfig, wrapperr_data []WrapperrDay, wrapperr_reply WrapperrStatisticsReply) (WrapperrStatisticsReply, error) {
func WrapperrLoopData(user_id int, config *models.WrapperrConfig, wrapperr_data []models.WrapperrDay, wrapperr_reply models.WrapperrStatisticsReply) (models.WrapperrStatisticsReply, error) {
end_loop_date := time.Unix(int64(config.WrappedEnd), 0)
start_loop_date := time.Unix(int64(config.WrappedStart), 0)
top_list_limit := config.WrapperrCustomize.StatsTopListLength
var wrapperr_user_movie []TautulliEntry
var wrapperr_user_episode []TautulliEntry
var wrapperr_user_show []TautulliEntry
var wrapperr_user_track []TautulliEntry
var wrapperr_user_album []TautulliEntry
var wrapperr_user_artist []TautulliEntry
var wrapperr_year_user []WrapperrYearUserEntry
var wrapperr_year_movie []TautulliEntry
var wrapperr_year_show []TautulliEntry
var wrapperr_year_artist []TautulliEntry
var wrapperr_user_movie []models.TautulliEntry
var wrapperr_user_episode []models.TautulliEntry
var wrapperr_user_show []models.TautulliEntry
var wrapperr_user_track []models.TautulliEntry
var wrapperr_user_album []models.TautulliEntry
var wrapperr_user_artist []models.TautulliEntry
var wrapperr_year_user []models.WrapperrYearUserEntry
var wrapperr_year_movie []models.TautulliEntry
var wrapperr_year_show []models.TautulliEntry
var wrapperr_year_artist []models.TautulliEntry
for i := 0; i < len(wrapperr_data); i++ {
@ -620,7 +624,7 @@ func WrapperrLoopData(user_id int, config *WrapperrConfig, wrapperr_data []Wrapp
// If user was not found, add it to array
if !user_found {
var user_entry = WrapperrYearUserEntry{
var user_entry = models.WrapperrYearUserEntry{
Plays: 1,
DurationMovies: wrapperr_data[i].Data[j].Duration,
PausedCounter: wrapperr_data[i].Data[j].PausedCounter,
@ -670,7 +674,7 @@ func WrapperrLoopData(user_id int, config *WrapperrConfig, wrapperr_data []Wrapp
// If user was not found, add it to array
if !user_found {
var user_entry = WrapperrYearUserEntry{
var user_entry = models.WrapperrYearUserEntry{
Plays: 1,
DurationShows: wrapperr_data[i].Data[j].Duration,
PausedCounter: wrapperr_data[i].Data[j].PausedCounter,
@ -720,7 +724,7 @@ func WrapperrLoopData(user_id int, config *WrapperrConfig, wrapperr_data []Wrapp
// If user was not found, add it to array
if !user_found {
var user_entry = WrapperrYearUserEntry{
var user_entry = models.WrapperrYearUserEntry{
Plays: 1,
DurationArtists: wrapperr_data[i].Data[j].Duration,
PausedCounter: wrapperr_data[i].Data[j].PausedCounter,
@ -791,8 +795,8 @@ func WrapperrLoopData(user_id int, config *WrapperrConfig, wrapperr_data []Wrapp
wrapperr_reply.User.UserMovies.Message = "All movies processed."
} else {
wrapperr_reply.User.UserMovies.Data.MoviesDuration = []TautulliEntry{}
wrapperr_reply.User.UserMovies.Data.MoviesPlays = []TautulliEntry{}
wrapperr_reply.User.UserMovies.Data.MoviesDuration = []models.TautulliEntry{}
wrapperr_reply.User.UserMovies.Data.MoviesPlays = []models.TautulliEntry{}
wrapperr_reply.User.UserMovies.Message = "No movies processed."
}
@ -843,8 +847,8 @@ func WrapperrLoopData(user_id int, config *WrapperrConfig, wrapperr_data []Wrapp
wrapperr_reply.User.UserShows.Message = "All shows processed."
} else {
wrapperr_reply.User.UserShows.Data.ShowsDuration = []TautulliEntry{}
wrapperr_reply.User.UserShows.Data.ShowsPlays = []TautulliEntry{}
wrapperr_reply.User.UserShows.Data.ShowsDuration = []models.TautulliEntry{}
wrapperr_reply.User.UserShows.Data.ShowsPlays = []models.TautulliEntry{}
wrapperr_reply.User.UserShows.Message = "No shows processed."
}
@ -944,8 +948,8 @@ func WrapperrLoopData(user_id int, config *WrapperrConfig, wrapperr_data []Wrapp
wrapperr_reply.User.UserMusic.Message = "All tracks processed."
} else {
wrapperr_reply.User.UserMusic.Data.TracksDuration = []TautulliEntry{}
wrapperr_reply.User.UserMusic.Data.TracksPlays = []TautulliEntry{}
wrapperr_reply.User.UserMusic.Data.TracksDuration = []models.TautulliEntry{}
wrapperr_reply.User.UserMusic.Data.TracksPlays = []models.TautulliEntry{}
wrapperr_reply.User.UserMusic.Message = "No tracks processed."
}
@ -988,8 +992,8 @@ func WrapperrLoopData(user_id int, config *WrapperrConfig, wrapperr_data []Wrapp
wrapperr_reply.YearStats.YearMovies.Message = "All movies processed."
} else {
wrapperr_reply.YearStats.YearMovies.Data.MoviesDuration = []TautulliEntry{}
wrapperr_reply.YearStats.YearMovies.Data.MoviesPlays = []TautulliEntry{}
wrapperr_reply.YearStats.YearMovies.Data.MoviesDuration = []models.TautulliEntry{}
wrapperr_reply.YearStats.YearMovies.Data.MoviesPlays = []models.TautulliEntry{}
wrapperr_reply.YearStats.YearMovies.Message = "No movies processed."
}
@ -1032,8 +1036,8 @@ func WrapperrLoopData(user_id int, config *WrapperrConfig, wrapperr_data []Wrapp
wrapperr_reply.YearStats.YearShows.Message = "All shows processed."
} else {
wrapperr_reply.YearStats.YearShows.Data.ShowsDuration = []TautulliEntry{}
wrapperr_reply.YearStats.YearShows.Data.ShowsPlays = []TautulliEntry{}
wrapperr_reply.YearStats.YearShows.Data.ShowsDuration = []models.TautulliEntry{}
wrapperr_reply.YearStats.YearShows.Data.ShowsPlays = []models.TautulliEntry{}
wrapperr_reply.YearStats.YearShows.Message = "No shows processed."
}
@ -1076,8 +1080,8 @@ func WrapperrLoopData(user_id int, config *WrapperrConfig, wrapperr_data []Wrapp
wrapperr_reply.YearStats.YearMusic.Message = "All tracks processed."
} else {
wrapperr_reply.YearStats.YearMusic.Data.ArtistsDuration = []TautulliEntry{}
wrapperr_reply.YearStats.YearMusic.Data.ArtistsPlays = []TautulliEntry{}
wrapperr_reply.YearStats.YearMusic.Data.ArtistsDuration = []models.TautulliEntry{}
wrapperr_reply.YearStats.YearMusic.Data.ArtistsPlays = []models.TautulliEntry{}
wrapperr_reply.YearStats.YearMusic.Message = "No tracks processed."
}
@ -1086,7 +1090,7 @@ func WrapperrLoopData(user_id int, config *WrapperrConfig, wrapperr_data []Wrapp
if (config.WrapperrCustomize.GetYearStatsMusic || config.WrapperrCustomize.GetYearStatsMovies || config.WrapperrCustomize.GetYearStatsShows) && config.WrapperrCustomize.GetYearStatsLeaderboard && len(wrapperr_year_user) > 0 {
// Create new array with duration sum, then sort year users array by duration
var wrapperr_year_user_summed []WrapperrYearUserEntry
var wrapperr_year_user_summed []models.WrapperrYearUserEntry
for d := 0; d < len(wrapperr_year_user); d++ {
wrapperr_year_user[d].Duration = wrapperr_year_user[d].DurationMovies + wrapperr_year_user[d].DurationShows + wrapperr_year_user[d].DurationArtists
wrapperr_year_user_summed = append(wrapperr_year_user_summed, wrapperr_year_user[d])
@ -1135,8 +1139,8 @@ func WrapperrLoopData(user_id int, config *WrapperrConfig, wrapperr_data []Wrapp
}
} else {
wrapperr_reply.YearStats.YearUsers.Data.UsersDuration = []WrapperrYearUserEntry{}
wrapperr_reply.YearStats.YearUsers.Data.UsersPlays = []WrapperrYearUserEntry{}
wrapperr_reply.YearStats.YearUsers.Data.UsersDuration = []models.WrapperrYearUserEntry{}
wrapperr_reply.YearStats.YearUsers.Data.UsersPlays = []models.WrapperrYearUserEntry{}
wrapperr_reply.YearStats.YearMovies.Message = "No users processed."
}
@ -1151,7 +1155,7 @@ func WrapperrLoopData(user_id int, config *WrapperrConfig, wrapperr_data []Wrapp
err, buddy_name, buddy_found, buddy_duration := GetUserShowBuddy(config, wrapperr_reply.User.UserShows.Data.ShowsDuration[0], user_id, wrapperr_data)
var show_buddy WrapperrShowBuddy
var show_buddy models.WrapperrShowBuddy
if err != nil {
log.Println("Show buddy threw error: ")
@ -1181,9 +1185,9 @@ func WrapperrLoopData(user_id int, config *WrapperrConfig, wrapperr_data []Wrapp
}
func GetUserShowBuddy(config *WrapperrConfig, top_show TautulliEntry, user_id int, wrapperr_data []WrapperrDay) (error, string, bool, int) {
func GetUserShowBuddy(config *models.WrapperrConfig, top_show models.TautulliEntry, user_id int, wrapperr_data []models.WrapperrDay) (error, string, bool, int) {
var top_show_users []WrapperrYearUserEntry
var top_show_users []models.WrapperrYearUserEntry
var top_show_buddy_name = "Something went wrong."
var top_show_buddy_duration = 0
var top_show_buddy_found = false
@ -1221,7 +1225,7 @@ func GetUserShowBuddy(config *WrapperrConfig, top_show TautulliEntry, user_id in
// If user was not found, add it to array
if !user_found {
var user_entry = WrapperrYearUserEntry{
var user_entry = models.WrapperrYearUserEntry{
Plays: 1,
Duration: wrapperr_data[i].Data[j].Duration,
FriendlyName: wrapperr_data[i].Data[j].FriendlyName,

View file

@ -1,6 +1,10 @@
package main
package routes
import (
"aunefyren/wrapperr/files"
"aunefyren/wrapperr/models"
"aunefyren/wrapperr/modules"
"aunefyren/wrapperr/utilities"
"encoding/json"
"errors"
"fmt"
@ -15,63 +19,63 @@ import (
func ApiGetLoginURL(w http.ResponseWriter, r *http.Request) {
config_bool, err := GetConfigState()
config_bool, err := files.GetConfigState()
if err != nil {
log.Println(err)
respond_default_error(w, r, errors.New("Failed to retrieve confguration state."), 500)
utilities.RespondDefaultError(w, r, errors.New("Failed to retrieve confguration 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)
utilities.RespondDefaultError(w, r, errors.New("Wrapperr is not configured."), 400)
return
}
config, err := GetConfig()
config, err := files.GetConfig()
if err != nil {
log.Println(err)
respond_default_error(w, r, errors.New("Failed to load Wrapperr confguration."), 500)
utilities.RespondDefaultError(w, r, errors.New("Failed to load Wrapperr confguration."), 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)
utilities.RespondDefaultError(w, r, errors.New("Plex Auth is not enabled in the Wrapperr configuration."), 400)
return
}
// Read payload from Post input
reqBody, _ := ioutil.ReadAll(r.Body)
var homeurl_payload GetLoginURL
var homeurl_payload models.GetLoginURL
json.Unmarshal(reqBody, &homeurl_payload)
// Confirm username length
if homeurl_payload.HomeURL == "" {
log.Println("Cannot retrieve Plex Auth login URL. Invalid HomeURL recieved.")
respond_default_error(w, r, errors.New("HomeURL specified is invalid."), 400)
utilities.RespondDefaultError(w, r, errors.New("HomeURL specified is invalid."), 400)
return
}
plex_pin, err := GetPin(config.ClientKey, config.WrapperrVersion)
plex_pin, err := modules.GetPin(config.ClientKey, config.WrapperrVersion)
if err != nil {
log.Println(err)
respond_default_error(w, r, errors.New("Failed to retrieve Plex Auth pin."), 500)
utilities.RespondDefaultError(w, r, errors.New("Failed to retrieve Plex Auth pin."), 500)
return
}
if plex_pin.ID == 0 || plex_pin.Code == "" {
log.Println("Plex Auth response invalid. No ID and/or Code.")
respond_default_error(w, r, errors.New("Plex Auth response invalid."), 500)
utilities.RespondDefaultError(w, r, errors.New("Plex Auth response invalid."), 500)
return
}
login_url := GetLoginURLString(config.ClientKey, plex_pin.Code, homeurl_payload.HomeURL)
login_url := modules.GetLoginURLString(config.ClientKey, plex_pin.Code, homeurl_payload.HomeURL)
url_reply := GetLoginURLReply{
url_reply := models.GetLoginURLReply{
Message: "Plex Auth login URL retrieved.",
Error: false,
URL: login_url,
@ -79,92 +83,92 @@ func ApiGetLoginURL(w http.ResponseWriter, r *http.Request) {
ID: plex_pin.ID,
}
ip_string := GetOriginIPString(w, r)
ip_string := utilities.GetOriginIPString(w, r)
log.Println("Created and retrieved Plex Auth login URL." + ip_string)
fmt.Println("Created and retrieved Plex Auth login URL." + ip_string)
respondWithJSON(w, http.StatusOK, url_reply)
utilities.RespondWithJSON(w, http.StatusOK, url_reply)
return
}
func ApiLoginPlexAuth(w http.ResponseWriter, r *http.Request) {
config_bool, err := GetConfigState()
config_bool, err := files.GetConfigState()
if err != nil {
log.Println(err)
respond_default_error(w, r, errors.New("Failed to retrieve configuration state."), 500)
utilities.RespondDefaultError(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)
utilities.RespondDefaultError(w, r, errors.New("Wrapperr is not configured."), 400)
return
}
config, err := GetConfig()
config, err := files.GetConfig()
if err != nil {
log.Println(err)
respond_default_error(w, r, errors.New("Failed to load Wrapperr configuration."), 500)
utilities.RespondDefaultError(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)
utilities.RespondDefaultError(w, r, errors.New("Plex Auth is not enabled in the Wrapperr configuration."), 400)
return
}
// Read payload from Post input
reqBody, _ := ioutil.ReadAll(r.Body)
var payload LoginPlexAuth
var payload models.LoginPlexAuth
json.Unmarshal(reqBody, &payload)
// Confirm username length
if payload.ID == 0 || payload.Code == "" {
log.Println("Cannot retrieve Plex Auth login state. Invalid ID or Code recieved.")
respond_default_error(w, r, errors.New("Login ID and/or Code is invalid."), 400)
utilities.RespondDefaultError(w, r, errors.New("Login ID and/or Code is invalid."), 400)
return
}
plex_auth, err := GetPlexAuthLogin(payload.ID, payload.Code, config.WrapperrVersion, config.ClientKey)
plex_auth, err := modules.GetPlexAuthLogin(payload.ID, payload.Code, config.WrapperrVersion, config.ClientKey)
if err != nil {
log.Println(err)
respond_default_error(w, r, errors.New("Failed to retrieve Plex Auth pin."), 500)
utilities.RespondDefaultError(w, r, errors.New("Failed to retrieve Plex Auth pin."), 500)
return
}
if plex_auth.AuthToken == "" {
log.Println("Plex Auth response invalid. No Authtoken recieved.")
respond_default_error(w, r, errors.New("Plex Auth response invalid."), 400)
utilities.RespondDefaultError(w, r, errors.New("Plex Auth response invalid."), 400)
return
}
token, err := CreateToken("Plex Auth", false, plex_auth.AuthToken)
token, err := modules.CreateToken("Plex Auth", false, plex_auth.AuthToken)
if err != nil {
log.Println(err)
respond_default_error(w, r, errors.New("Faield to create JWT token."), 500)
utilities.RespondDefaultError(w, r, errors.New("Faield to create JWT token."), 500)
return
}
string_reply := StringReply{
string_reply := models.StringReply{
Message: "Login cookie created",
Error: false,
Data: token,
}
ip_string := GetOriginIPString(w, r)
ip_string := utilities.GetOriginIPString(w, r)
log.Println("Created and retrieved Plex Auth login JWT Token." + ip_string)
fmt.Println("Created and retrieved Plex Auth login JWT Token." + ip_string)
respondWithJSON(w, http.StatusOK, string_reply)
utilities.RespondWithJSON(w, http.StatusOK, string_reply)
return
}
@ -172,44 +176,44 @@ func ApiLoginPlexAuth(w http.ResponseWriter, r *http.Request) {
// API route which validates an admin JWT token
func ApiValidatePlexAuth(w http.ResponseWriter, r *http.Request) {
payload, err := AuthorizeToken(w, r)
payload, err := modules.AuthorizeToken(w, r)
if err != nil {
log.Println("Failed to parse login token. Error: ")
log.Println(err)
respond_default_error(w, r, errors.New("Failed to parse login token."), 500)
utilities.RespondDefaultError(w, r, errors.New("Failed to parse login token."), 500)
return
} else if payload.Admin {
log.Println("Recieved JWT token is for admin use.")
respond_default_error(w, r, errors.New("Recieved JWT token is for admin use."), 401)
utilities.RespondDefaultError(w, r, errors.New("Recieved JWT token is for admin use."), 401)
return
}
config, err := GetConfig()
config, err := files.GetConfig()
if err != nil {
log.Println("Failed to load Wrapperr configuration. Error: ")
log.Println(err)
respond_default_error(w, r, errors.New("Failed to load Wrapperr configuration."), 500)
utilities.RespondDefaultError(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)
utilities.RespondDefaultError(w, r, errors.New("Plex Auth is not enabled in the Wrapperr configuration."), 400)
return
}
_, err = PlexAuthValidateToken(payload.AuthToken, config.ClientKey, config.WrapperrVersion)
_, err = modules.PlexAuthValidateToken(payload.AuthToken, config.ClientKey, config.WrapperrVersion)
if err != nil {
log.Println("Could not validate Plex Auth login. Error: ")
log.Println(err)
respond_default_error(w, r, errors.New("Could not validate Plex Auth login."), 500)
utilities.RespondDefaultError(w, r, errors.New("Could not validate Plex Auth login."), 500)
return
}
log.Println("Plex Auth JWT Token validated using Plex API.")
respond_default_okay(w, r, "Plex Auth validated.")
utilities.RespondDefaultOkay(w, r, "Plex Auth validated.")
return
}
@ -217,43 +221,43 @@ func ApiValidatePlexAuth(w http.ResponseWriter, r *http.Request) {
// Create shareable link using Plex Auth
func ApiCreateShareLink(w http.ResponseWriter, r *http.Request) {
config_bool, err := GetConfigState()
config_bool, err := files.GetConfigState()
if err != nil {
log.Println(err)
respond_default_error(w, r, errors.New("Failed to retrieve configuration state."), 500)
utilities.RespondDefaultError(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)
utilities.RespondDefaultError(w, r, errors.New("Wrapperr is not configured."), 400)
return
}
config, err := GetConfig()
config, err := files.GetConfig()
if err != nil {
log.Println(err)
respond_default_error(w, r, errors.New("Failed to load Wrapperr configuration."), 500)
utilities.RespondDefaultError(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)
utilities.RespondDefaultError(w, r, errors.New("Plex Auth is not enabled in the Wrapperr configuration."), 400)
return
}
if !config.CreateShareLinks {
log.Panicln("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)
utilities.RespondDefaultError(w, r, errors.New("Shareable links are not enabled in the Wrapperr configuration."), 400)
return
}
// Try to authorize bearer token from header
payload, err := AuthorizeToken(w, r)
payload, err := modules.AuthorizeToken(w, r)
var user_name string
var user_id int
@ -261,13 +265,13 @@ func ApiCreateShareLink(w http.ResponseWriter, r *http.Request) {
if err != nil || payload.Admin {
log.Println(err)
log.Println(payload.Admin)
respond_default_error(w, r, errors.New("Failed to authorize request."), 401)
utilities.RespondDefaultError(w, r, errors.New("Failed to authorize request."), 401)
return
} else {
plex_object, err := PlexAuthValidateToken(payload.AuthToken, config.ClientKey, config.WrapperrVersion)
plex_object, err := modules.PlexAuthValidateToken(payload.AuthToken, config.ClientKey, config.WrapperrVersion)
if err != nil {
log.Println(err)
respond_default_error(w, r, errors.New("Could not validate Plex Auth login."), 500)
utilities.RespondDefaultError(w, r, errors.New("Could not validate Plex Auth login."), 500)
return
}
@ -279,16 +283,16 @@ func ApiCreateShareLink(w http.ResponseWriter, r *http.Request) {
reqBody, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Println(err)
respond_default_error(w, r, errors.New("Failed to parse link payload."), 500)
utilities.RespondDefaultError(w, r, errors.New("Failed to parse link payload."), 500)
return
}
var link_payload WrapperrShareLinkCreateRequest
var link_payload models.WrapperrShareLinkCreateRequest
json.Unmarshal(reqBody, &link_payload)
currentTime := time.Now()
hash_value := uuid.New().String()
link_object := WrapperrShareLink{
link_object := models.WrapperrShareLink{
Content: link_payload,
UserID: user_id,
Hash: hash_value,
@ -297,24 +301,24 @@ func ApiCreateShareLink(w http.ResponseWriter, r *http.Request) {
Expired: false,
}
err = SaveLink(&link_object)
err = files.SaveLink(&link_object)
if err != nil {
log.Println(err)
respond_default_error(w, r, errors.New("Failed to save new link."), 500)
utilities.RespondDefaultError(w, r, errors.New("Failed to save new link."), 500)
return
}
string_reply := StringReply{
string_reply := models.StringReply{
Message: "Saved Wrapperr link.",
Error: false,
Data: strconv.Itoa(user_id) + "-" + hash_value,
}
ip_string := GetOriginIPString(w, r)
ip_string := utilities.GetOriginIPString(w, r)
log.Println("Saved new Wrapperr share link for " + user_name + " (" + strconv.Itoa(user_id) + ")." + ip_string)
respondWithJSON(w, http.StatusOK, string_reply)
utilities.RespondWithJSON(w, http.StatusOK, string_reply)
return
}
@ -322,43 +326,43 @@ func ApiCreateShareLink(w http.ResponseWriter, r *http.Request) {
// Get users shareable link
func ApiGetUserShareLink(w http.ResponseWriter, r *http.Request) {
config_bool, err := GetConfigState()
config_bool, err := files.GetConfigState()
if err != nil {
log.Println(err)
respond_default_error(w, r, errors.New("Failed to retrieve configuration state."), 500)
utilities.RespondDefaultError(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)
utilities.RespondDefaultError(w, r, errors.New("Wrapperr is not configured."), 400)
return
}
config, err := GetConfig()
config, err := files.GetConfig()
if err != nil {
log.Println(err)
respond_default_error(w, r, errors.New("Failed to load Wrapperr configuration."), 500)
utilities.RespondDefaultError(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)
utilities.RespondDefaultError(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)
utilities.RespondDefaultError(w, r, errors.New("Shareable links are not enabled in the Wrapperr configuration."), 400)
return
}
// Try to authorize bearer token from header
payload, err := AuthorizeToken(w, r)
payload, err := modules.AuthorizeToken(w, r)
var user_name string
var user_id int
@ -366,17 +370,17 @@ func ApiGetUserShareLink(w http.ResponseWriter, r *http.Request) {
if err != nil {
log.Println(err)
log.Println(payload.Admin)
respond_default_error(w, r, errors.New("Failed to authorize request."), 500)
utilities.RespondDefaultError(w, r, errors.New("Failed to authorize request."), 500)
return
} else if payload.Admin {
log.Println("Admin tried to retrieve share links.")
respond_default_error(w, r, errors.New("Admin cannot retrieve share links."), 401)
utilities.RespondDefaultError(w, r, errors.New("Admin cannot retrieve share links."), 401)
return
} else {
plex_object, err := PlexAuthValidateToken(payload.AuthToken, config.ClientKey, config.WrapperrVersion)
plex_object, err := modules.PlexAuthValidateToken(payload.AuthToken, config.ClientKey, config.WrapperrVersion)
if err != nil {
log.Println(err)
respond_default_error(w, r, errors.New("Could not validate Plex Auth login."), 500)
utilities.RespondDefaultError(w, r, errors.New("Could not validate Plex Auth login."), 500)
return
}
@ -384,20 +388,20 @@ func ApiGetUserShareLink(w http.ResponseWriter, r *http.Request) {
user_id = plex_object.ID
}
share_link_object, err := GetLink(strconv.Itoa(user_id))
share_link_object, err := files.GetLink(strconv.Itoa(user_id))
if err != nil {
string_reply := StringReply{
string_reply := models.StringReply{
Message: "No Wrapperr links found for user.",
Error: true,
Data: "",
}
ip_string := GetOriginIPString(w, r)
ip_string := utilities.GetOriginIPString(w, r)
log.Println("No Wrapperr links found for " + user_name + " (" + strconv.Itoa(user_id) + ")." + ip_string)
respondWithJSON(w, http.StatusOK, string_reply)
utilities.RespondWithJSON(w, http.StatusOK, string_reply)
return
}
@ -407,7 +411,7 @@ func ApiGetUserShareLink(w http.ResponseWriter, r *http.Request) {
if err != nil {
log.Println(err)
respond_default_error(w, r, errors.New("Failed to retrieve saved Wrapperr link."), 500)
utilities.RespondDefaultError(w, r, errors.New("Failed to retrieve saved Wrapperr link."), 500)
return
}
@ -416,32 +420,32 @@ func ApiGetUserShareLink(w http.ResponseWriter, r *http.Request) {
if !linkTime.Before(currentTime) {
string_reply := StringReply{
string_reply := models.StringReply{
Message: "Retrieved Wrapperr link created by user.",
Error: false,
Data: strconv.Itoa(user_id) + "-" + share_link_object.Hash,
}
ip_string := GetOriginIPString(w, r)
ip_string := utilities.GetOriginIPString(w, r)
log.Println("Retrieved Wrapperr link created by " + user_name + " (" + strconv.Itoa(user_id) + ")." + ip_string)
respondWithJSON(w, http.StatusOK, string_reply)
utilities.RespondWithJSON(w, http.StatusOK, string_reply)
return
} else {
string_reply := StringReply{
string_reply := models.StringReply{
Message: "No Wrapperr links found for user.",
Error: true,
Data: "",
}
ip_string := GetOriginIPString(w, r)
ip_string := utilities.GetOriginIPString(w, r)
log.Println("No Wrapperr links found for " + user_name + " (" + strconv.Itoa(user_id) + ")." + ip_string)
respondWithJSON(w, http.StatusOK, string_reply)
utilities.RespondWithJSON(w, http.StatusOK, string_reply)
return
}
@ -450,43 +454,43 @@ func ApiGetUserShareLink(w http.ResponseWriter, r *http.Request) {
// Delete users shareable link
func ApiDeleteUserShareLink(w http.ResponseWriter, r *http.Request) {
config_bool, err := GetConfigState()
config_bool, err := files.GetConfigState()
if err != nil {
log.Println(err)
respond_default_error(w, r, errors.New("Failed to retrieve configuration state."), 500)
utilities.RespondDefaultError(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)
utilities.RespondDefaultError(w, r, errors.New("Wrapperr is not configured."), 400)
return
}
config, err := GetConfig()
config, err := files.GetConfig()
if err != nil {
log.Println(err)
respond_default_error(w, r, errors.New("Failed to load Wrapperr configuration."), 500)
utilities.RespondDefaultError(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)
utilities.RespondDefaultError(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)
utilities.RespondDefaultError(w, r, errors.New("Shareable links are not enabled in the Wrapperr configuration."), 400)
return
}
// Try to authorize bearer token from header
payload, err := AuthorizeToken(w, r)
payload, err := modules.AuthorizeToken(w, r)
var user_name string
var user_id int
@ -494,13 +498,13 @@ func ApiDeleteUserShareLink(w http.ResponseWriter, r *http.Request) {
if err != nil || payload.Admin {
log.Println(err)
log.Println(payload.Admin)
respond_default_error(w, r, errors.New("Failed to authorize request."), 401)
utilities.RespondDefaultError(w, r, errors.New("Failed to authorize request."), 401)
return
} else {
plex_object, err := PlexAuthValidateToken(payload.AuthToken, config.ClientKey, config.WrapperrVersion)
plex_object, err := modules.PlexAuthValidateToken(payload.AuthToken, config.ClientKey, config.WrapperrVersion)
if err != nil {
log.Println(err)
respond_default_error(w, r, errors.New("Could not validate Plex Auth login."), 500)
utilities.RespondDefaultError(w, r, errors.New("Could not validate Plex Auth login."), 500)
return
}
@ -508,29 +512,29 @@ func ApiDeleteUserShareLink(w http.ResponseWriter, r *http.Request) {
user_id = plex_object.ID
}
share_link_object, err := GetLink(strconv.Itoa(user_id))
share_link_object, err := files.GetLink(strconv.Itoa(user_id))
if err != nil {
log.Println(err)
respond_default_error(w, r, errors.New("Failed to retrieve any saved Wrapperr link."), 500)
utilities.RespondDefaultError(w, r, errors.New("Failed to retrieve any saved Wrapperr link."), 500)
return
}
share_link_object.Date = "1970-01-01"
err = SaveLink(share_link_object)
err = files.SaveLink(share_link_object)
if err != nil {
log.Println(err)
respond_default_error(w, r, errors.New("Failed to overwrite saved Wrapperr link."), 500)
utilities.RespondDefaultError(w, r, errors.New("Failed to overwrite saved Wrapperr link."), 500)
return
}
log.Println("Deleted Wrapperr link for user " + user_name + " (" + strconv.Itoa(user_id) + ").")
respond_default_okay(w, r, "Deleted Wrapperr link.")
utilities.RespondDefaultOkay(w, r, "Deleted Wrapperr link.")
return
}

View file

@ -1,6 +1,7 @@
package main
package utilities
import (
"aunefyren/wrapperr/models"
"encoding/base64"
"encoding/json"
"fmt"
@ -38,7 +39,7 @@ func checkScopes(requiredScopes []string, providedScopes string) bool {
return true
}
func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
func RespondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
response, err := json.Marshal(payload)
if err != nil {
log.Println(err)
@ -50,37 +51,37 @@ func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
return
}
func respond_default_error(writer http.ResponseWriter, request *http.Request, error_reply error, http_code int) {
func RespondDefaultError(writer http.ResponseWriter, request *http.Request, error_reply error, http_code int) {
ip_string := GetOriginIPString(writer, request)
log.Println("Returned error: '" + error_reply.Error() + "'" + ip_string)
reply := Default_Reply{
reply := models.Default_Reply{
Message: error_reply.Error(),
Error: true,
}
respondWithJSON(writer, http_code, reply)
RespondWithJSON(writer, http_code, reply)
return
}
func respond_default_okay(writer http.ResponseWriter, request *http.Request, reply_string string) {
func RespondDefaultOkay(writer http.ResponseWriter, request *http.Request, reply_string string) {
ip_string := GetOriginIPString(writer, request)
log.Println("Returned reply: '" + reply_string + "'" + ip_string)
reply := Default_Reply{
reply := models.Default_Reply{
Message: reply_string,
Error: false,
}
respondWithJSON(writer, http.StatusOK, reply)
RespondWithJSON(writer, http.StatusOK, reply)
return
}
func hashAndSalt(pwd_string string) (string, error) {
func HashAndSalt(pwd_string string) (string, error) {
pwd := []byte(pwd_string)
@ -98,7 +99,7 @@ func hashAndSalt(pwd_string string) (string, error) {
return string(hash), nil
}
func comparePasswords(hashedPwd string, pwd string) bool {
func ComparePasswords(hashedPwd string, pwd string) bool {
// Since we'll be getting the hashed password from the DB it
// will be a string so we'll need to convert it to a byte slice
plainPwd := []byte(pwd)