Merge branch 'passwordpage' into 'master'
PasswordPage if enabled in settings See merge request lukas/openmediacenter!40
This commit is contained in:
		@@ -16,6 +16,7 @@ const (
 | 
				
			|||||||
	TagNode      = iota
 | 
						TagNode      = iota
 | 
				
			||||||
	SettingsNode = iota
 | 
						SettingsNode = iota
 | 
				
			||||||
	ActorNode    = iota
 | 
						ActorNode    = iota
 | 
				
			||||||
 | 
						InitNode     = iota
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type actionStruct struct {
 | 
					type actionStruct struct {
 | 
				
			||||||
@@ -42,6 +43,9 @@ func ServerInit(port uint16) {
 | 
				
			|||||||
	http.Handle(APIPREFIX+"/settings", oauth.ValidateToken(settingsHandler))
 | 
						http.Handle(APIPREFIX+"/settings", oauth.ValidateToken(settingsHandler))
 | 
				
			||||||
	http.Handle(APIPREFIX+"/actor", oauth.ValidateToken(actorHandler))
 | 
						http.Handle(APIPREFIX+"/actor", oauth.ValidateToken(actorHandler))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// initialization api calls to check if password is neccessaray
 | 
				
			||||||
 | 
						http.Handle(APIPREFIX+"/init", http.HandlerFunc(initHandler))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// initialize oauth service and add corresponding auth routes
 | 
						// initialize oauth service and add corresponding auth routes
 | 
				
			||||||
	oauth.InitOAuth()
 | 
						oauth.InitOAuth()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -85,6 +89,10 @@ func settingsHandler(rw http.ResponseWriter, req *http.Request) {
 | 
				
			|||||||
	handlefunc(rw, req, SettingsNode)
 | 
						handlefunc(rw, req, SettingsNode)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func initHandler(rw http.ResponseWriter, req *http.Request) {
 | 
				
			||||||
 | 
						handlefunc(rw, req, InitNode)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func handlefunc(rw http.ResponseWriter, req *http.Request, node int) {
 | 
					func handlefunc(rw http.ResponseWriter, req *http.Request, node int) {
 | 
				
			||||||
	// only allow post requests
 | 
						// only allow post requests
 | 
				
			||||||
	if req.Method != "POST" {
 | 
						if req.Method != "POST" {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										33
									
								
								apiGo/api/Init.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								apiGo/api/Init.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					package api
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"openmediacenter/apiGo/database/settings"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func AddInitHandlers() {
 | 
				
			||||||
 | 
						passwordNeeded()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func passwordNeeded() {
 | 
				
			||||||
 | 
						AddHandler("loadInitialData", InitNode, nil, func() []byte {
 | 
				
			||||||
 | 
							sett := settings.LoadSettings()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							type InitialDataTypeResponse struct {
 | 
				
			||||||
 | 
								DarkMode        bool
 | 
				
			||||||
 | 
								Pasword         bool
 | 
				
			||||||
 | 
								MediacenterName string
 | 
				
			||||||
 | 
								VideoPath       string
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							res := InitialDataTypeResponse{
 | 
				
			||||||
 | 
								DarkMode:        sett.DarkMode,
 | 
				
			||||||
 | 
								Pasword:         sett.Pasword != "-1",
 | 
				
			||||||
 | 
								MediacenterName: sett.Mediacenter_name,
 | 
				
			||||||
 | 
								VideoPath:       sett.VideoPath,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							str, _ := json.Marshal(res)
 | 
				
			||||||
 | 
							return str
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,8 +1,6 @@
 | 
				
			|||||||
package api
 | 
					package api
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"encoding/json"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"openmediacenter/apiGo/api/types"
 | 
						"openmediacenter/apiGo/api/types"
 | 
				
			||||||
	"openmediacenter/apiGo/database"
 | 
						"openmediacenter/apiGo/database"
 | 
				
			||||||
	"openmediacenter/apiGo/videoparser"
 | 
						"openmediacenter/apiGo/videoparser"
 | 
				
			||||||
@@ -15,41 +13,6 @@ func AddSettingsHandlers() {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getSettingsFromDB() {
 | 
					func getSettingsFromDB() {
 | 
				
			||||||
	AddHandler("loadInitialData", SettingsNode, nil, func() []byte {
 | 
					 | 
				
			||||||
		query := "SELECT DarkMode, password, mediacenter_name, video_path from settings"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		type InitialDataType struct {
 | 
					 | 
				
			||||||
			DarkMode         int
 | 
					 | 
				
			||||||
			Pasword          int
 | 
					 | 
				
			||||||
			Mediacenter_name string
 | 
					 | 
				
			||||||
			VideoPath        string
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		result := InitialDataType{}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		err := database.QueryRow(query).Scan(&result.DarkMode, &result.Pasword, &result.Mediacenter_name, &result.VideoPath)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			fmt.Println("error while parsing db data: " + err.Error())
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		type InitialDataTypeResponse struct {
 | 
					 | 
				
			||||||
			DarkMode         bool
 | 
					 | 
				
			||||||
			Pasword          bool
 | 
					 | 
				
			||||||
			Mediacenter_name string
 | 
					 | 
				
			||||||
			VideoPath        string
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		res := InitialDataTypeResponse{
 | 
					 | 
				
			||||||
			DarkMode:         result.DarkMode != 0,
 | 
					 | 
				
			||||||
			Pasword:          result.Pasword != -1,
 | 
					 | 
				
			||||||
			Mediacenter_name: result.Mediacenter_name,
 | 
					 | 
				
			||||||
			VideoPath:        result.VideoPath,
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		str, _ := json.Marshal(res)
 | 
					 | 
				
			||||||
		return str
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	AddHandler("loadGeneralSettings", SettingsNode, nil, func() []byte {
 | 
						AddHandler("loadGeneralSettings", SettingsNode, nil, func() []byte {
 | 
				
			||||||
		result := database.GetSettings()
 | 
							result := database.GetSettings()
 | 
				
			||||||
		return jsonify(result)
 | 
							return jsonify(result)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										56
									
								
								apiGo/api/oauth/CustomClientStore.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								apiGo/api/oauth/CustomClientStore.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,56 @@
 | 
				
			|||||||
 | 
					package oauth
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"gopkg.in/oauth2.v3"
 | 
				
			||||||
 | 
						"openmediacenter/apiGo/database/settings"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type CustomClientStore struct {
 | 
				
			||||||
 | 
						oauth2.ClientStore
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type CustomClientInfo struct {
 | 
				
			||||||
 | 
						oauth2.ClientInfo
 | 
				
			||||||
 | 
						ID     string
 | 
				
			||||||
 | 
						Secret string
 | 
				
			||||||
 | 
						Domain string
 | 
				
			||||||
 | 
						UserID string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewCustomStore() oauth2.ClientStore {
 | 
				
			||||||
 | 
						s := new(CustomClientStore)
 | 
				
			||||||
 | 
						return s
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a *CustomClientStore) GetByID(id string) (oauth2.ClientInfo, error) {
 | 
				
			||||||
 | 
						password := settings.GetPassword()
 | 
				
			||||||
 | 
						if password == nil {
 | 
				
			||||||
 | 
							defaultpassword := "openmediacenter"
 | 
				
			||||||
 | 
							password = &defaultpassword
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						clientinfo := CustomClientInfo{
 | 
				
			||||||
 | 
							ID:     "openmediacenter",
 | 
				
			||||||
 | 
							Secret: *password,
 | 
				
			||||||
 | 
							Domain: "http://localhost:8081",
 | 
				
			||||||
 | 
							UserID: "openmediacenter",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &clientinfo, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a *CustomClientInfo) GetID() string {
 | 
				
			||||||
 | 
						return a.ID
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a *CustomClientInfo) GetSecret() string {
 | 
				
			||||||
 | 
						return a.Secret
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a *CustomClientInfo) GetDomain() string {
 | 
				
			||||||
 | 
						return a.Domain
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a *CustomClientInfo) GetUserID() string {
 | 
				
			||||||
 | 
						return a.UserID
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -3,7 +3,7 @@ package oauth
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"gopkg.in/oauth2.v3/errors"
 | 
						"gopkg.in/oauth2.v3/errors"
 | 
				
			||||||
	"gopkg.in/oauth2.v3/manage"
 | 
						"gopkg.in/oauth2.v3/manage"
 | 
				
			||||||
	"gopkg.in/oauth2.v3/models"
 | 
						//"gopkg.in/oauth2.v3/models"
 | 
				
			||||||
	"gopkg.in/oauth2.v3/server"
 | 
						"gopkg.in/oauth2.v3/server"
 | 
				
			||||||
	"gopkg.in/oauth2.v3/store"
 | 
						"gopkg.in/oauth2.v3/store"
 | 
				
			||||||
	"log"
 | 
						"log"
 | 
				
			||||||
@@ -17,15 +17,19 @@ func InitOAuth() {
 | 
				
			|||||||
	// token store
 | 
						// token store
 | 
				
			||||||
	manager.MustTokenStorage(store.NewMemoryTokenStore())
 | 
						manager.MustTokenStorage(store.NewMemoryTokenStore())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	clientStore := store.NewClientStore()
 | 
						//clientStore := store.NewClientStore()
 | 
				
			||||||
	// todo we need to check here if a password is enabled in db -- when yes set it here!
 | 
						//// todo we need to check here if a password is enabled in db -- when yes set it here!
 | 
				
			||||||
	clientStore.Set("openmediacenter", &models.Client{
 | 
						//clientStore.Set("openmediacenter", &models.Client{
 | 
				
			||||||
		ID:     "openmediacenter",
 | 
						//	ID:     "openmediacenter",
 | 
				
			||||||
		Secret: "openmediacenter",
 | 
						//	Secret: "openmediacenter",
 | 
				
			||||||
		Domain: "http://localhost:8081",
 | 
						//	Domain: "http://localhost:8081",
 | 
				
			||||||
	})
 | 
						//})
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						//manager.MapClientStorage(clientStore)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						strtest := NewCustomStore()
 | 
				
			||||||
 | 
						manager.MapClientStorage(strtest)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	manager.MapClientStorage(clientStore)
 | 
					 | 
				
			||||||
	srv = server.NewServer(server.NewConfig(), manager)
 | 
						srv = server.NewServer(server.NewConfig(), manager)
 | 
				
			||||||
	srv.SetClientInfoHandler(server.ClientFormHandler)
 | 
						srv.SetClientInfoHandler(server.ClientFormHandler)
 | 
				
			||||||
	manager.SetRefreshTokenCfg(manage.DefaultRefreshTokenCfg)
 | 
						manager.SetRefreshTokenCfg(manage.DefaultRefreshTokenCfg)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										49
									
								
								apiGo/database/settings/DBSettings.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								apiGo/database/settings/DBSettings.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
				
			|||||||
 | 
					package settings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"openmediacenter/apiGo/database"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GetPassword() *string {
 | 
				
			||||||
 | 
						pwd := LoadSettings().Pasword
 | 
				
			||||||
 | 
						if pwd == "-1" {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							return &pwd
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SettingsType struct {
 | 
				
			||||||
 | 
						DarkMode         bool
 | 
				
			||||||
 | 
						Pasword          string
 | 
				
			||||||
 | 
						Mediacenter_name string
 | 
				
			||||||
 | 
						VideoPath        string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func LoadSettings() *SettingsType {
 | 
				
			||||||
 | 
						query := "SELECT DarkMode, password, mediacenter_name, video_path from settings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						type RawSettingsType struct {
 | 
				
			||||||
 | 
							DarkMode         int
 | 
				
			||||||
 | 
							Pasword          string
 | 
				
			||||||
 | 
							Mediacenter_name string
 | 
				
			||||||
 | 
							VideoPath        string
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						result := RawSettingsType{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := database.QueryRow(query).Scan(&result.DarkMode, &result.Pasword, &result.Mediacenter_name, &result.VideoPath)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							fmt.Println("error while parsing db data: " + err.Error())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						res := SettingsType{
 | 
				
			||||||
 | 
							DarkMode:         result.DarkMode != 0,
 | 
				
			||||||
 | 
							Pasword:          result.Pasword,
 | 
				
			||||||
 | 
							Mediacenter_name: result.Mediacenter_name,
 | 
				
			||||||
 | 
							VideoPath:        result.VideoPath,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &res
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -3,7 +3,6 @@ module openmediacenter/apiGo
 | 
				
			|||||||
go 1.16
 | 
					go 1.16
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require (
 | 
					require (
 | 
				
			||||||
	github.com/go-session/session v3.1.2+incompatible
 | 
					 | 
				
			||||||
	github.com/go-sql-driver/mysql v1.5.0
 | 
						github.com/go-sql-driver/mysql v1.5.0
 | 
				
			||||||
	gopkg.in/oauth2.v3 v3.12.0
 | 
						gopkg.in/oauth2.v3 v3.12.0
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,7 +11,6 @@ github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga
 | 
				
			|||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 | 
					github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 | 
				
			||||||
github.com/gavv/httpexpect v2.0.0+incompatible h1:1X9kcRshkSKEjNJJxX9Y9mQ5BRfbxU5kORdjhlA1yX8=
 | 
					github.com/gavv/httpexpect v2.0.0+incompatible h1:1X9kcRshkSKEjNJJxX9Y9mQ5BRfbxU5kORdjhlA1yX8=
 | 
				
			||||||
github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
 | 
					github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
 | 
				
			||||||
github.com/go-session/session v3.1.2+incompatible h1:yStchEObKg4nk2F7JGE7KoFIrA/1Y078peagMWcrncg=
 | 
					 | 
				
			||||||
github.com/go-session/session v3.1.2+incompatible/go.mod h1:8B3iivBQjrz/JtC68Np2T1yBBLxTan3mn/3OM0CyRt0=
 | 
					github.com/go-session/session v3.1.2+incompatible/go.mod h1:8B3iivBQjrz/JtC68Np2T1yBBLxTan3mn/3OM0CyRt0=
 | 
				
			||||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
 | 
					github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
 | 
				
			||||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
 | 
					github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,6 +26,7 @@ func main() {
 | 
				
			|||||||
	api.AddSettingsHandlers()
 | 
						api.AddSettingsHandlers()
 | 
				
			||||||
	api.AddTagHandlers()
 | 
						api.AddTagHandlers()
 | 
				
			||||||
	api.AddActorsHandlers()
 | 
						api.AddActorsHandlers()
 | 
				
			||||||
 | 
						api.AddInitHandlers()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	api.ServerInit(8081)
 | 
						api.ServerInit(8081)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
import React from 'react';
 | 
					import React from 'react';
 | 
				
			||||||
import App from './App';
 | 
					import App from './App';
 | 
				
			||||||
import {shallow} from 'enzyme';
 | 
					import {shallow} from 'enzyme';
 | 
				
			||||||
 | 
					import GlobalInfos from "./utils/GlobalInfos";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('<App/>', function () {
 | 
					describe('<App/>', function () {
 | 
				
			||||||
    it('renders without crashing ', function () {
 | 
					    it('renders without crashing ', function () {
 | 
				
			||||||
@@ -10,34 +11,37 @@ describe('<App/>', function () {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    it('renders title', () => {
 | 
					    it('renders title', () => {
 | 
				
			||||||
        const wrapper = shallow(<App/>);
 | 
					        const wrapper = shallow(<App/>);
 | 
				
			||||||
 | 
					        wrapper.setState({password: false});
 | 
				
			||||||
        expect(wrapper.find('.navbrand').text()).toBe('OpenMediaCenter');
 | 
					        expect(wrapper.find('.navbrand').text()).toBe('OpenMediaCenter');
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it('are navlinks correct', function () {
 | 
					    it('are navlinks correct', function () {
 | 
				
			||||||
        const wrapper = shallow(<App/>);
 | 
					        const wrapper = shallow(<App/>);
 | 
				
			||||||
 | 
					        wrapper.setState({password: false});
 | 
				
			||||||
        expect(wrapper.find('.navitem')).toHaveLength(4);
 | 
					        expect(wrapper.find('.navitem')).toHaveLength(4);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it('test initial fetch from api', done => {
 | 
					    it('test initial fetch from api', done => {
 | 
				
			||||||
        global.fetch = global.prepareFetchApi({
 | 
					        callAPIMock({
 | 
				
			||||||
            generalSettingsLoaded: true,
 | 
					            MediacenterName: 'testname'
 | 
				
			||||||
            passwordsupport: true,
 | 
					        })
 | 
				
			||||||
            mediacentername: 'testname'
 | 
					
 | 
				
			||||||
        });
 | 
					        GlobalInfos.enableDarkTheme = jest.fn((r) => {})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const wrapper = shallow(<App/>);
 | 
					        const wrapper = shallow(<App/>);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
        const func = jest.fn();
 | 
					 | 
				
			||||||
        wrapper.instance().setState = func;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        expect(global.fetch).toBeCalledTimes(1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        process.nextTick(() => {
 | 
					        process.nextTick(() => {
 | 
				
			||||||
            expect(func).toBeCalledTimes(1);
 | 
					            expect(document.title).toBe('testname');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            global.fetch.mockClear();
 | 
					            global.fetch.mockClear();
 | 
				
			||||||
            done();
 | 
					            done();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('test render of password page', function () {
 | 
				
			||||||
 | 
					        const wrapper = shallow(<App/>);
 | 
				
			||||||
 | 
					        wrapper.setState({password: true});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        expect(wrapper.find('AuthenticationPage')).toHaveLength(1);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										71
									
								
								src/App.tsx
									
									
									
									
									
								
							
							
						
						
									
										71
									
								
								src/App.tsx
									
									
									
									
									
								
							@@ -9,7 +9,7 @@ import style from './App.module.css';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import SettingsPage from './pages/SettingsPage/SettingsPage';
 | 
					import SettingsPage from './pages/SettingsPage/SettingsPage';
 | 
				
			||||||
import CategoryPage from './pages/CategoryPage/CategoryPage';
 | 
					import CategoryPage from './pages/CategoryPage/CategoryPage';
 | 
				
			||||||
import {APINode, callAPI} from './utils/Api';
 | 
					import {APINode, apiTokenValid, callApiUnsafe, refreshAPIToken} from './utils/Api';
 | 
				
			||||||
import {NoBackendConnectionPopup} from './elements/Popups/NoBackendConnectionPopup/NoBackendConnectionPopup';
 | 
					import {NoBackendConnectionPopup} from './elements/Popups/NoBackendConnectionPopup/NoBackendConnectionPopup';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import {BrowserRouter as Router, NavLink, Route, Switch} from 'react-router-dom';
 | 
					import {BrowserRouter as Router, NavLink, Route, Switch} from 'react-router-dom';
 | 
				
			||||||
@@ -17,10 +17,10 @@ import Player from './pages/Player/Player';
 | 
				
			|||||||
import ActorOverviewPage from './pages/ActorOverviewPage/ActorOverviewPage';
 | 
					import ActorOverviewPage from './pages/ActorOverviewPage/ActorOverviewPage';
 | 
				
			||||||
import ActorPage from './pages/ActorPage/ActorPage';
 | 
					import ActorPage from './pages/ActorPage/ActorPage';
 | 
				
			||||||
import {SettingsTypes} from './types/ApiTypes';
 | 
					import {SettingsTypes} from './types/ApiTypes';
 | 
				
			||||||
 | 
					import AuthenticationPage from "./pages/AuthenticationPage/AuthenticationPage";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface state {
 | 
					interface state {
 | 
				
			||||||
    generalSettingsLoaded: boolean;
 | 
					    password: boolean | null; // null if uninitialized - true if pwd needed false if not needed
 | 
				
			||||||
    passwordsupport: boolean;
 | 
					 | 
				
			||||||
    mediacentername: string;
 | 
					    mediacentername: string;
 | 
				
			||||||
    onapierror: boolean;
 | 
					    onapierror: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -31,30 +31,48 @@ interface state {
 | 
				
			|||||||
class App extends React.Component<{}, state> {
 | 
					class App extends React.Component<{}, state> {
 | 
				
			||||||
    constructor(props: {}) {
 | 
					    constructor(props: {}) {
 | 
				
			||||||
        super(props);
 | 
					        super(props);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let pwdneeded: boolean | null = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (apiTokenValid()) {
 | 
				
			||||||
 | 
					            pwdneeded = false;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            refreshAPIToken((err) => {
 | 
				
			||||||
 | 
					                if (err === 'invalid_client') {
 | 
				
			||||||
 | 
					                    this.setState({password: true})
 | 
				
			||||||
 | 
					                } else if (err === '') {
 | 
				
			||||||
 | 
					                    this.setState({password: false})
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    console.log("unimplemented token error: " + err)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.state = {
 | 
					        this.state = {
 | 
				
			||||||
            generalSettingsLoaded: false,
 | 
					 | 
				
			||||||
            passwordsupport: false,
 | 
					 | 
				
			||||||
            mediacentername: 'OpenMediaCenter',
 | 
					            mediacentername: 'OpenMediaCenter',
 | 
				
			||||||
            onapierror: false
 | 
					            onapierror: false,
 | 
				
			||||||
 | 
					            password: pwdneeded
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        GlobalInfos.onThemeChange(() => {
 | 
				
			||||||
 | 
					            this.forceUpdate();
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    initialAPICall(): void {
 | 
					    initialAPICall(): void {
 | 
				
			||||||
        // this is the first api call so if it fails we know there is no connection to backend
 | 
					        // this is the first api call so if it fails we know there is no connection to backend
 | 
				
			||||||
        callAPI(APINode.Settings, {action: 'loadInitialData'}, (result: SettingsTypes.initialApiCallData) => {
 | 
					        callApiUnsafe(APINode.Init, {action: 'loadInitialData'}, (result: SettingsTypes.initialApiCallData) => {
 | 
				
			||||||
            // set theme
 | 
					            // set theme
 | 
				
			||||||
            GlobalInfos.enableDarkTheme(result.DarkMode);
 | 
					            GlobalInfos.enableDarkTheme(result.DarkMode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            GlobalInfos.setVideoPath(result.VideoPath);
 | 
					            GlobalInfos.setVideoPath(result.VideoPath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            this.setState({
 | 
					            this.setState({
 | 
				
			||||||
                generalSettingsLoaded: true,
 | 
					                mediacentername: result.MediacenterName,
 | 
				
			||||||
                passwordsupport: result.Password,
 | 
					 | 
				
			||||||
                mediacentername: result.Mediacenter_name,
 | 
					 | 
				
			||||||
                onapierror: false
 | 
					                onapierror: false
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
            // set tab title to received mediacenter name
 | 
					            // set tab title to received mediacenter name
 | 
				
			||||||
            document.title = result.Mediacenter_name;
 | 
					            document.title = result.MediacenterName;
 | 
				
			||||||
        }, error => {
 | 
					        }, error => {
 | 
				
			||||||
            this.setState({onapierror: true});
 | 
					            this.setState({onapierror: true});
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
@@ -70,23 +88,44 @@ class App extends React.Component<{}, state> {
 | 
				
			|||||||
        // add the main theme to the page body
 | 
					        // add the main theme to the page body
 | 
				
			||||||
        document.body.className = themeStyle.backgroundcolor;
 | 
					        document.body.className = themeStyle.backgroundcolor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (this.state.password === true) {
 | 
				
			||||||
 | 
					            return (
 | 
				
			||||||
 | 
					                <AuthenticationPage submit={(password): void => {
 | 
				
			||||||
 | 
					                    refreshAPIToken((error) => {
 | 
				
			||||||
 | 
					                        if (error !== '') {
 | 
				
			||||||
 | 
					                            console.log("wrong password!!!");
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            this.setState({password: false});
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }, password);
 | 
				
			||||||
 | 
					                }}/>
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        } else if (this.state.password === false) {
 | 
				
			||||||
            return (
 | 
					            return (
 | 
				
			||||||
                <Router>
 | 
					                <Router>
 | 
				
			||||||
                    <div className={style.app}>
 | 
					                    <div className={style.app}>
 | 
				
			||||||
                    <div className={[style.navcontainer, themeStyle.backgroundcolor, themeStyle.textcolor, themeStyle.hrcolor].join(' ')}>
 | 
					                        <div
 | 
				
			||||||
 | 
					                            className={[style.navcontainer, themeStyle.backgroundcolor, themeStyle.textcolor, themeStyle.hrcolor].join(' ')}>
 | 
				
			||||||
                            <div className={style.navbrand}>{this.state.mediacentername}</div>
 | 
					                            <div className={style.navbrand}>{this.state.mediacentername}</div>
 | 
				
			||||||
                        <NavLink className={[style.navitem, themeStyle.navitem].join(' ')} to={'/'} activeStyle={{opacity: '0.85'}}>Home</NavLink>
 | 
					                            <NavLink className={[style.navitem, themeStyle.navitem].join(' ')} to={'/'}
 | 
				
			||||||
                        <NavLink className={[style.navitem, themeStyle.navitem].join(' ')} to={'/random'} activeStyle={{opacity: '0.85'}}>Random
 | 
					                                     activeStyle={{opacity: '0.85'}}>Home</NavLink>
 | 
				
			||||||
 | 
					                            <NavLink className={[style.navitem, themeStyle.navitem].join(' ')} to={'/random'}
 | 
				
			||||||
 | 
					                                     activeStyle={{opacity: '0.85'}}>Random
 | 
				
			||||||
                                Video</NavLink>
 | 
					                                Video</NavLink>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        <NavLink className={[style.navitem, themeStyle.navitem].join(' ')} to={'/categories'} activeStyle={{opacity: '0.85'}}>Categories</NavLink>
 | 
					                            <NavLink className={[style.navitem, themeStyle.navitem].join(' ')} to={'/categories'}
 | 
				
			||||||
                        <NavLink className={[style.navitem, themeStyle.navitem].join(' ')} to={'/settings'} activeStyle={{opacity: '0.85'}}>Settings</NavLink>
 | 
					                                     activeStyle={{opacity: '0.85'}}>Categories</NavLink>
 | 
				
			||||||
 | 
					                            <NavLink className={[style.navitem, themeStyle.navitem].join(' ')} to={'/settings'}
 | 
				
			||||||
 | 
					                                     activeStyle={{opacity: '0.85'}}>Settings</NavLink>
 | 
				
			||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                        {this.routing()}
 | 
					                        {this.routing()}
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                    {this.state.onapierror ? this.ApiError() : null}
 | 
					                    {this.state.onapierror ? this.ApiError() : null}
 | 
				
			||||||
                </Router>
 | 
					                </Router>
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            return (<>still loading...</>);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    routing(): JSX.Element {
 | 
					    routing(): JSX.Element {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										52
									
								
								src/pages/AuthenticationPage/AuthenticationPage.module.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/pages/AuthenticationPage/AuthenticationPage.module.css
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,52 @@
 | 
				
			|||||||
 | 
					.main {
 | 
				
			||||||
 | 
					    background-color: #00b3ff;
 | 
				
			||||||
 | 
					    margin-left: calc(50% - 125px);
 | 
				
			||||||
 | 
					    margin-top: 5%;
 | 
				
			||||||
 | 
					    padding-bottom: 15px;
 | 
				
			||||||
 | 
					    width: 250px;
 | 
				
			||||||
 | 
					    text-align: center;
 | 
				
			||||||
 | 
					    border-radius: 10px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.loginText {
 | 
				
			||||||
 | 
					    font-size: xx-large;
 | 
				
			||||||
 | 
					    text-align: center;
 | 
				
			||||||
 | 
					    margin-bottom: 15px;
 | 
				
			||||||
 | 
					    font-weight: bolder;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.openmediacenterlabel {
 | 
				
			||||||
 | 
					    margin-top: 5%;
 | 
				
			||||||
 | 
					    text-align: center;
 | 
				
			||||||
 | 
					    font-size: xxx-large;
 | 
				
			||||||
 | 
					    font-weight: bold;
 | 
				
			||||||
 | 
					    text-transform: capitalize;
 | 
				
			||||||
 | 
					    color: white;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.input {
 | 
				
			||||||
 | 
					    margin-left: 10px;
 | 
				
			||||||
 | 
					    margin-right: 10px;
 | 
				
			||||||
 | 
					    width: calc(100% - 20px);
 | 
				
			||||||
 | 
					    background: transparent;
 | 
				
			||||||
 | 
					    border-width: 0 0 1px 0;
 | 
				
			||||||
 | 
					    color: #505050;
 | 
				
			||||||
 | 
					    border-color: #505050;
 | 
				
			||||||
 | 
					    text-align: center;
 | 
				
			||||||
 | 
					    margin-bottom: 25px;
 | 
				
			||||||
 | 
					    font-size: larger;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					::placeholder {
 | 
				
			||||||
 | 
					    color: #505050;
 | 
				
			||||||
 | 
					    opacity: 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*:focus {
 | 
				
			||||||
 | 
					    outline: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.input:focus {
 | 
				
			||||||
 | 
					    color: black;
 | 
				
			||||||
 | 
					    border-color: black;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										21
									
								
								src/pages/AuthenticationPage/AuthenticationPage.test.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/pages/AuthenticationPage/AuthenticationPage.test.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import AuthenticationPage from './AuthenticationPage';
 | 
				
			||||||
 | 
					import {shallow} from 'enzyme';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('<AuthenticationPage/>', function () {
 | 
				
			||||||
 | 
					    it('renders without crashing ', function () {
 | 
				
			||||||
 | 
					        const wrapper = shallow(<AuthenticationPage submit={() => {}}/>);
 | 
				
			||||||
 | 
					        wrapper.unmount();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('test button click', function () {
 | 
				
			||||||
 | 
					        let pass;
 | 
				
			||||||
 | 
					        const func = jest.fn((pwd) => {pass = pwd});
 | 
				
			||||||
 | 
					        const wrapper = shallow(<AuthenticationPage submit={func}/>);
 | 
				
			||||||
 | 
					        wrapper.setState({pwdText: 'testpwd'});
 | 
				
			||||||
 | 
					        wrapper.find('Button').simulate('click');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        expect(func).toHaveBeenCalledTimes(1);
 | 
				
			||||||
 | 
					        expect(pass).toBe('testpwd');
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										46
									
								
								src/pages/AuthenticationPage/AuthenticationPage.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/pages/AuthenticationPage/AuthenticationPage.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					import React from "react";
 | 
				
			||||||
 | 
					import {Button} from "../../elements/GPElements/Button";
 | 
				
			||||||
 | 
					import style from './AuthenticationPage.module.css'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface state {
 | 
				
			||||||
 | 
					    pwdText: string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface props {
 | 
				
			||||||
 | 
					    submit: (password: string) => void
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AuthenticationPage extends React.Component<props, state> {
 | 
				
			||||||
 | 
					    constructor(props: props) {
 | 
				
			||||||
 | 
					        super(props);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.state = {
 | 
				
			||||||
 | 
					            pwdText: ''
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    render(): JSX.Element {
 | 
				
			||||||
 | 
					        return (
 | 
				
			||||||
 | 
					            <>
 | 
				
			||||||
 | 
					                <div className={style.openmediacenterlabel}>OpenMediaCenter</div>
 | 
				
			||||||
 | 
					                <div className={style.main}>
 | 
				
			||||||
 | 
					                    <div className={style.loginText}>Login</div>
 | 
				
			||||||
 | 
					                    <div>
 | 
				
			||||||
 | 
					                        <input className={style.input}
 | 
				
			||||||
 | 
					                               placeholder='Password'
 | 
				
			||||||
 | 
					                               type='password'
 | 
				
			||||||
 | 
					                               onChange={(ch): void => this.setState({pwdText: ch.target.value})}
 | 
				
			||||||
 | 
					                               value={this.state.pwdText}/>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    <div>
 | 
				
			||||||
 | 
					                        <Button title='Submit' onClick={(): void => {
 | 
				
			||||||
 | 
					                            this.props.submit(this.state.pwdText);
 | 
				
			||||||
 | 
					                        }}/>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					            </>
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default AuthenticationPage;
 | 
				
			||||||
@@ -37,6 +37,7 @@ global.prepareFailingFetchApi = () => {
 | 
				
			|||||||
global.callAPIMock = (resonse) => {
 | 
					global.callAPIMock = (resonse) => {
 | 
				
			||||||
    const helpers = require('./utils/Api');
 | 
					    const helpers = require('./utils/Api');
 | 
				
			||||||
    helpers.callAPI = jest.fn().mockImplementation((_, __, func1) => {func1(resonse);});
 | 
					    helpers.callAPI = jest.fn().mockImplementation((_, __, func1) => {func1(resonse);});
 | 
				
			||||||
 | 
					    helpers.callApiUnsafe = jest.fn().mockImplementation((_, __, func1) => {func1(resonse);});
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// code to run before each test
 | 
					// code to run before each test
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,7 +33,7 @@ export namespace SettingsTypes {
 | 
				
			|||||||
    export interface initialApiCallData {
 | 
					    export interface initialApiCallData {
 | 
				
			||||||
        DarkMode: boolean;
 | 
					        DarkMode: boolean;
 | 
				
			||||||
        Password: boolean;
 | 
					        Password: boolean;
 | 
				
			||||||
        Mediacenter_name: string;
 | 
					        MediacenterName: string;
 | 
				
			||||||
        VideoPath: string;
 | 
					        VideoPath: string;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -47,13 +47,14 @@ interface ApiBaseRequest {
 | 
				
			|||||||
let apiToken = ''
 | 
					let apiToken = ''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// a callback que to be called after api token refresh
 | 
					// a callback que to be called after api token refresh
 | 
				
			||||||
let callQue: (() => void)[] = []
 | 
					let callQue: ((error: string) => void)[] = []
 | 
				
			||||||
// flag to check wheter a api refresh is currently pending
 | 
					// flag to check wheter a api refresh is currently pending
 | 
				
			||||||
let refreshInProcess = false;
 | 
					let refreshInProcess = false;
 | 
				
			||||||
// store the expire seconds of token
 | 
					// store the expire seconds of token
 | 
				
			||||||
let expireSeconds = -1;
 | 
					let expireSeconds = -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface APIToken {
 | 
					interface APIToken {
 | 
				
			||||||
 | 
					    error?: string;
 | 
				
			||||||
    access_token: string;
 | 
					    access_token: string;
 | 
				
			||||||
    expires_in: number;
 | 
					    expires_in: number;
 | 
				
			||||||
    scope: string;
 | 
					    scope: string;
 | 
				
			||||||
@@ -63,8 +64,9 @@ interface APIToken {
 | 
				
			|||||||
/**
 | 
					/**
 | 
				
			||||||
 * refresh the api token or use that one in cookie if still valid
 | 
					 * refresh the api token or use that one in cookie if still valid
 | 
				
			||||||
 * @param callback to be called after successful refresh
 | 
					 * @param callback to be called after successful refresh
 | 
				
			||||||
 | 
					 * @param password
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export function refreshAPIToken(callback: () => void): void {
 | 
					export function refreshAPIToken(callback: (error: string) => void, password?: string): void {
 | 
				
			||||||
    callQue.push(callback);
 | 
					    callQue.push(callback);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // check if already is a token refresh is in process
 | 
					    // check if already is a token refresh is in process
 | 
				
			||||||
@@ -76,30 +78,26 @@ export function refreshAPIToken(callback: () => void): void {
 | 
				
			|||||||
        refreshInProcess = true;
 | 
					        refreshInProcess = true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // check if a cookie with token is available
 | 
					    if (apiTokenValid()) {
 | 
				
			||||||
    const token = getTokenCookie();
 | 
					 | 
				
			||||||
    if (token !== null) {
 | 
					 | 
				
			||||||
        // check if token is at least valid for the next minute
 | 
					 | 
				
			||||||
        if (token.expire > (new Date().getTime() / 1000) + 60) {
 | 
					 | 
				
			||||||
            apiToken = token.token;
 | 
					 | 
				
			||||||
            expireSeconds = token.expire;
 | 
					 | 
				
			||||||
            callback();
 | 
					 | 
				
			||||||
        console.log("token still valid...")
 | 
					        console.log("token still valid...")
 | 
				
			||||||
            callFuncQue();
 | 
					        callFuncQue('');
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const formData = new FormData();
 | 
					    const formData = new FormData();
 | 
				
			||||||
    formData.append("grant_type", "client_credentials");
 | 
					    formData.append("grant_type", "client_credentials");
 | 
				
			||||||
    formData.append("client_id", "openmediacenter");
 | 
					    formData.append("client_id", "openmediacenter");
 | 
				
			||||||
    formData.append("client_secret", 'openmediacenter');
 | 
					    formData.append("client_secret", password ? password : 'openmediacenter');
 | 
				
			||||||
    formData.append("scope", 'all');
 | 
					    formData.append("scope", 'all');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fetch(getBackendDomain() + '/token', {method: 'POST', body: formData})
 | 
					    fetch(getBackendDomain() + '/token', {method: 'POST', body: formData})
 | 
				
			||||||
        .then((response) => response.json()
 | 
					        .then((response) => response.json()
 | 
				
			||||||
            .then((result: APIToken) => {
 | 
					            .then((result: APIToken) => {
 | 
				
			||||||
 | 
					                if (result.error) {
 | 
				
			||||||
 | 
					                    callFuncQue(result.error);
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
                console.log(result)
 | 
					                console.log(result)
 | 
				
			||||||
                // set api token
 | 
					                // set api token
 | 
				
			||||||
                apiToken = result.access_token;
 | 
					                apiToken = result.access_token;
 | 
				
			||||||
@@ -107,17 +105,32 @@ export function refreshAPIToken(callback: () => void): void {
 | 
				
			|||||||
                expireSeconds = (new Date().getTime() / 1000) + result.expires_in;
 | 
					                expireSeconds = (new Date().getTime() / 1000) + result.expires_in;
 | 
				
			||||||
                setTokenCookie(apiToken, expireSeconds);
 | 
					                setTokenCookie(apiToken, expireSeconds);
 | 
				
			||||||
                // call all handlers and release flag
 | 
					                // call all handlers and release flag
 | 
				
			||||||
                callFuncQue();
 | 
					                callFuncQue('');
 | 
				
			||||||
            }));
 | 
					            }));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function apiTokenValid(): boolean {
 | 
				
			||||||
 | 
					    // check if a cookie with token is available
 | 
				
			||||||
 | 
					    const token = getTokenCookie();
 | 
				
			||||||
 | 
					    if (token !== null) {
 | 
				
			||||||
 | 
					        // check if token is at least valid for the next minute
 | 
				
			||||||
 | 
					        if (token.expire > (new Date().getTime() / 1000) + 60) {
 | 
				
			||||||
 | 
					            apiToken = token.token;
 | 
				
			||||||
 | 
					            expireSeconds = token.expire;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * call all qued callbacks
 | 
					 * call all qued callbacks
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
function callFuncQue(): void {
 | 
					function callFuncQue(error: string): void {
 | 
				
			||||||
    // call all pending handlers
 | 
					    // call all pending handlers
 | 
				
			||||||
    callQue.map(func => {
 | 
					    callQue.map(func => {
 | 
				
			||||||
        return func();
 | 
					        return func(error);
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
    // reset pending que
 | 
					    // reset pending que
 | 
				
			||||||
    callQue = []
 | 
					    callQue = []
 | 
				
			||||||
@@ -221,6 +234,25 @@ export function callAPI<T>(apinode: APINode,
 | 
				
			|||||||
    })
 | 
					    })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * make a public unsafe api call (without token) -- use as rare as possible only for initialization (eg. check if pwd is neccessary)
 | 
				
			||||||
 | 
					 * @param apinode
 | 
				
			||||||
 | 
					 * @param fd
 | 
				
			||||||
 | 
					 * @param callback
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export function callApiUnsafe<T>(apinode: APINode, fd: ApiBaseRequest, callback: (_: T) => void, errorcallback?: (_: string) => void): void {
 | 
				
			||||||
 | 
					    fetch(getAPIDomain() + apinode, {method: 'POST', body: JSON.stringify(fd),}).then((response) => {
 | 
				
			||||||
 | 
					        if (response.status !== 200) {
 | 
				
			||||||
 | 
					            console.log('Error: ' + response.statusText);
 | 
				
			||||||
 | 
					            // todo place error popup here
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            response.json().then((result: T) => {
 | 
				
			||||||
 | 
					                callback(result);
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }).catch(reason => errorcallback ? errorcallback(reason) : {})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * A backend api call
 | 
					 * A backend api call
 | 
				
			||||||
 * @param apinode which api backend handler to call
 | 
					 * @param apinode which api backend handler to call
 | 
				
			||||||
@@ -249,5 +281,6 @@ export enum APINode {
 | 
				
			|||||||
    Settings = 'settings',
 | 
					    Settings = 'settings',
 | 
				
			||||||
    Tags = 'tags',
 | 
					    Tags = 'tags',
 | 
				
			||||||
    Actor = 'actor',
 | 
					    Actor = 'actor',
 | 
				
			||||||
    Video = 'video'
 | 
					    Video = 'video',
 | 
				
			||||||
 | 
					    Init = 'init'
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,6 +23,9 @@ class StaticInfos {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    enableDarkTheme(enable = true): void {
 | 
					    enableDarkTheme(enable = true): void {
 | 
				
			||||||
        this.darktheme = enable;
 | 
					        this.darktheme = enable;
 | 
				
			||||||
 | 
					        this.handlers.map(func => {
 | 
				
			||||||
 | 
					            return func();
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
@@ -33,6 +36,11 @@ class StaticInfos {
 | 
				
			|||||||
        return this.isDarkTheme() ? darktheme : lighttheme;
 | 
					        return this.isDarkTheme() ? darktheme : lighttheme;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    handlers: (() => void)[] = [];
 | 
				
			||||||
 | 
					    onThemeChange(func: () => void): void {
 | 
				
			||||||
 | 
					        this.handlers.push(func);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * set the current videopath
 | 
					     * set the current videopath
 | 
				
			||||||
     * @param vidpath videopath with beginning and ending slash
 | 
					     * @param vidpath videopath with beginning and ending slash
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user