diff --git a/src/App.tsx b/src/App.tsx index fbdd9a7..563d27f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -9,7 +9,7 @@ import style from './App.module.css'; import SettingsPage from './pages/SettingsPage/SettingsPage'; import CategoryPage from './pages/CategoryPage/CategoryPage'; -import {APINode, callAPI, token} from './utils/Api'; +import {APINode, callAPI} from './utils/Api'; import {BrowserRouter as Router, NavLink, Route, Switch} from 'react-router-dom'; import Player from './pages/Player/Player'; @@ -20,6 +20,7 @@ import AuthenticationPage from './pages/AuthenticationPage/AuthenticationPage'; import TVShowPage from './pages/TVShowPage/TVShowPage'; import TVPlayer from './pages/TVShowPage/TVPlayer'; import {CookieTokenStore} from './utils/TokenStore/CookieTokenStore'; +import {token} from './utils/TokenHandler'; interface state { password: boolean | null; // null if uninitialized - true if pwd needed false if not needed @@ -33,7 +34,7 @@ class App extends React.Component<{}, state> { constructor(props: {}) { super(props); - token.setTokenStore(new CookieTokenStore()); + token.init(new CookieTokenStore()); let pwdneeded: boolean | null = null; diff --git a/src/pages/AuthenticationPage/AuthenticationPage.tsx b/src/pages/AuthenticationPage/AuthenticationPage.tsx index 65f3dd3..5a3a771 100644 --- a/src/pages/AuthenticationPage/AuthenticationPage.tsx +++ b/src/pages/AuthenticationPage/AuthenticationPage.tsx @@ -2,7 +2,7 @@ import React from 'react'; import {Button} from '../../elements/GPElements/Button'; import style from './AuthenticationPage.module.css'; import {addKeyHandler, removeKeyHandler} from '../../utils/ShortkeyHandler'; -import {token} from '../../utils/Api'; +import {token} from '../../utils/TokenHandler'; import {faTimes} from '@fortawesome/free-solid-svg-icons'; import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; diff --git a/src/setupTests.js b/src/setupTests.js index 0bedd82..0ffa066 100644 --- a/src/setupTests.js +++ b/src/setupTests.js @@ -6,9 +6,8 @@ import '@testing-library/jest-dom/extend-expect'; import {configure} from 'enzyme'; import Adapter from 'enzyme-adapter-react-16'; -import GlobalInfos from './utils/GlobalInfos'; import {CookieTokenStore} from "./utils/TokenStore/CookieTokenStore"; -import {token} from "./utils/Api"; +import {token} from "./utils/TokenHandler"; configure({adapter: new Adapter()}); @@ -46,7 +45,7 @@ global.callAPIMock = (resonse) => { global.beforeEach(() => { // empty fetch response implementation for each test global.fetch = prepareFetchApi({}); - token.setTokenStore(new CookieTokenStore()); + token.init(new CookieTokenStore()); // todo with callAPIMock }); diff --git a/src/utils/Api.ts b/src/utils/Api.ts index c126639..0b15abd 100644 --- a/src/utils/Api.ts +++ b/src/utils/Api.ts @@ -1,5 +1,5 @@ import GlobalInfos from './GlobalInfos'; -import {TokenStore} from './TokenStore/TokenStore'; +import {token} from './TokenHandler'; const APIPREFIX: string = '/api/'; @@ -12,130 +12,6 @@ interface ApiBaseRequest { [_: string]: string | number | boolean | object; } -export namespace token { - // store api token - empty if not set - let apiToken = ''; - - // a callback que to be called after api token refresh - let callQue: ((error: string) => void)[] = []; - // flag to check wheter a api refresh is currently pending - let refreshInProcess = false; - // store the expire seconds of token - let expireSeconds = -1; - - let tokenStore: TokenStore; - - export function setTokenStore(ts: TokenStore): void { - tokenStore = ts; - } - - /** - * refresh the api token or use that one in cookie if still valid - * @param callback to be called after successful refresh - * @param password - * @param force - */ - export function refreshAPIToken(callback: (error: string) => void, force?: boolean, password?: string): void { - callQue.push(callback); - - // check if already is a token refresh is in process - if (refreshInProcess) { - // if yes return - return; - } else { - // if not set flat - refreshInProcess = true; - } - - if (apiTokenValid() && !force) { - console.log('token still valid...'); - callFuncQue(''); - return; - } - - const formData = new FormData(); - formData.append('grant_type', 'client_credentials'); - formData.append('client_id', 'openmediacenter'); - formData.append('client_secret', password ? password : 'openmediacenter'); - formData.append('scope', 'all'); - - interface APIToken { - error?: string; - // eslint-disable-next-line camelcase - access_token: string; // no camel case allowed because of backendlib - // eslint-disable-next-line camelcase - expires_in: number; // no camel case allowed because of backendlib - scope: string; - // eslint-disable-next-line camelcase - token_type: string; // no camel case allowed because of backendlib - } - - fetch('/token', {method: 'POST', body: formData}).then((response) => - response.json().then((result: APIToken) => { - if (result.error) { - callFuncQue(result.error); - return; - } - // set api token - apiToken = result.access_token; - // set expire time - expireSeconds = new Date().getTime() / 1000 + result.expires_in; - // setTokenCookie(apiToken, expireSeconds); - tokenStore.setToken({accessToken: apiToken, expireTime: expireSeconds, tokenType: '', scope: ''}); - // call all handlers and release flag - callFuncQue(''); - }) - ); - } - - export function apiTokenValid(): boolean { - // check if a cookie with token is available - // const token = getTokenCookie(); - const tmptoken = tokenStore.loadToken(); - if (tmptoken !== null) { - // check if token is at least valid for the next minute - if (tmptoken.expireTime > new Date().getTime() / 1000 + 60) { - apiToken = tmptoken.accessToken; - expireSeconds = tmptoken.expireTime; - - return true; - } - } - return false; - } - - /** - * call all qued callbacks - */ - function callFuncQue(error: string): void { - // call all pending handlers - callQue.map((func) => { - return func(error); - }); - // reset pending que - callQue = []; - // release flag to be able to start new refresh - refreshInProcess = false; - } - - /** - * check if api token is valid -- if not request new one - * when finished call callback - * @param callback function to be called afterwards - */ - export function checkAPITokenValid(callback: (mytoken: string) => void): void { - // check if token is valid and set - if (apiToken === '' || expireSeconds <= new Date().getTime() / 1000) { - console.log('token not valid...'); - refreshAPIToken(() => { - callback(apiToken); - }); - } else { - callback(apiToken); - } - } -} - /** * A backend api call * @param apinode which api backend handler to call diff --git a/src/utils/TokenHandler.ts b/src/utils/TokenHandler.ts new file mode 100644 index 0000000..f830894 --- /dev/null +++ b/src/utils/TokenHandler.ts @@ -0,0 +1,131 @@ +import {TokenStore} from './TokenStore/TokenStore'; + +export namespace token { + // store api token - empty if not set + let apiToken = ''; + + // a callback que to be called after api token refresh + let callQue: ((error: string) => void)[] = []; + // flag to check wheter a api refresh is currently pending + let refreshInProcess = false; + // store the expire seconds of token + let expireSeconds = -1; + + let tokenStore: TokenStore; + let APiHost: string = '/'; + + export function init(ts: TokenStore, apiHost?: string): void { + tokenStore = ts; + if (apiHost) { + APiHost = apiHost; + } + } + + /** + * refresh the api token or use that one in cookie if still valid + * @param callback to be called after successful refresh + * @param password + * @param force + */ + export function refreshAPIToken(callback: (error: string) => void, force?: boolean, password?: string): void { + callQue.push(callback); + + // check if already is a token refresh is in process + if (refreshInProcess) { + // if yes return + return; + } else { + // if not set flat + refreshInProcess = true; + } + + if (apiTokenValid() && !force) { + console.log('token still valid...'); + callFuncQue(''); + return; + } + + const formData = new FormData(); + formData.append('grant_type', 'client_credentials'); + formData.append('client_id', 'openmediacenter'); + formData.append('client_secret', password ? password : 'openmediacenter'); + formData.append('scope', 'all'); + + interface APIToken { + error?: string; + // eslint-disable-next-line camelcase + access_token: string; // no camel case allowed because of backendlib + // eslint-disable-next-line camelcase + expires_in: number; // no camel case allowed because of backendlib + scope: string; + // eslint-disable-next-line camelcase + token_type: string; // no camel case allowed because of backendlib + } + + console.log(APiHost); + + fetch(APiHost + 'token', {method: 'POST', body: formData}).then((response) => + response.json().then((result: APIToken) => { + if (result.error) { + callFuncQue(result.error); + return; + } + // set api token + apiToken = result.access_token; + // set expire time + expireSeconds = new Date().getTime() / 1000 + result.expires_in; + // setTokenCookie(apiToken, expireSeconds); + tokenStore.setToken({accessToken: apiToken, expireTime: expireSeconds, tokenType: '', scope: ''}); + // call all handlers and release flag + callFuncQue(''); + }) + ); + } + + export function apiTokenValid(): boolean { + // check if a cookie with token is available + // const token = getTokenCookie(); + const tmptoken = tokenStore.loadToken(); + if (tmptoken !== null) { + // check if token is at least valid for the next minute + if (tmptoken.expireTime > new Date().getTime() / 1000 + 60) { + apiToken = tmptoken.accessToken; + expireSeconds = tmptoken.expireTime; + + return true; + } + } + return false; + } + + /** + * call all qued callbacks + */ + function callFuncQue(error: string): void { + // call all pending handlers + callQue.map((func) => { + return func(error); + }); + // reset pending que + callQue = []; + // release flag to be able to start new refresh + refreshInProcess = false; + } + + /** + * check if api token is valid -- if not request new one + * when finished call callback + * @param callback function to be called afterwards + */ + export function checkAPITokenValid(callback: (mytoken: string) => void): void { + // check if token is valid and set + if (apiToken === '' || expireSeconds <= new Date().getTime() / 1000) { + console.log('token not valid...'); + refreshAPIToken(() => { + callback(apiToken); + }); + } else { + callback(apiToken); + } + } +}