2020-12-17 20:53:22 +00:00
|
|
|
let customBackendURL: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* get the domain of the api backend
|
|
|
|
* @return string domain of backend http://x.x.x.x/bla
|
|
|
|
*/
|
|
|
|
export function getBackendDomain(): string {
|
|
|
|
let userAgent = navigator.userAgent.toLowerCase();
|
|
|
|
if (userAgent.indexOf(' electron/') > -1) {
|
|
|
|
// Electron-specific code - force a custom backendurl
|
|
|
|
return (customBackendURL);
|
|
|
|
} else {
|
|
|
|
// use custom only if defined
|
|
|
|
if (customBackendURL) {
|
|
|
|
return (customBackendURL);
|
|
|
|
} else {
|
|
|
|
return (window.location.origin);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* set a custom backend domain
|
|
|
|
* @param domain a url in format [http://x.x.x.x/somanode]
|
|
|
|
*/
|
2020-12-29 19:39:30 +00:00
|
|
|
export function setCustomBackendDomain(domain: string): void {
|
2020-12-17 20:53:22 +00:00
|
|
|
customBackendURL = domain;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* a helper function to get the api path
|
|
|
|
*/
|
|
|
|
function getAPIDomain(): string {
|
|
|
|
return getBackendDomain() + '/api/';
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* interface how an api request should look like
|
|
|
|
*/
|
|
|
|
interface ApiBaseRequest {
|
2020-12-29 19:39:30 +00:00
|
|
|
action: string | number,
|
2020-12-17 20:53:22 +00:00
|
|
|
|
2021-02-23 16:01:29 +00:00
|
|
|
[_: string]: string | number | boolean | object
|
2020-12-17 20:53:22 +00:00
|
|
|
}
|
|
|
|
|
2021-03-09 12:56:53 +00:00
|
|
|
// store api token - empty if not set
|
|
|
|
let apiToken = ''
|
|
|
|
|
|
|
|
// a callback que to be called after api token refresh
|
2021-03-14 12:49:24 +00:00
|
|
|
let callQue: ((error: string) => void)[] = []
|
2021-03-09 12:56:53 +00:00
|
|
|
// flag to check wheter a api refresh is currently pending
|
|
|
|
let refreshInProcess = false;
|
|
|
|
// store the expire seconds of token
|
|
|
|
let expireSeconds = -1;
|
|
|
|
|
|
|
|
interface APIToken {
|
2021-03-14 12:49:24 +00:00
|
|
|
error?: string;
|
2021-03-09 12:56:53 +00:00
|
|
|
access_token: string;
|
|
|
|
expires_in: number;
|
|
|
|
scope: string;
|
|
|
|
token_type: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* refresh the api token or use that one in cookie if still valid
|
|
|
|
* @param callback to be called after successful refresh
|
2021-03-14 12:49:24 +00:00
|
|
|
* @param password
|
2021-03-09 12:56:53 +00:00
|
|
|
*/
|
2021-03-14 12:49:24 +00:00
|
|
|
export function refreshAPIToken(callback: (error: string) => void, password?: string): void {
|
2021-03-09 12:56:53 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-03-14 12:49:24 +00:00
|
|
|
if (apiTokenValid()) {
|
|
|
|
console.log("token still valid...")
|
|
|
|
callFuncQue('');
|
|
|
|
return;
|
2021-03-09 12:56:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const formData = new FormData();
|
|
|
|
formData.append("grant_type", "client_credentials");
|
|
|
|
formData.append("client_id", "openmediacenter");
|
2021-03-14 12:49:24 +00:00
|
|
|
formData.append("client_secret", password ? password : 'openmediacenter');
|
2021-03-09 12:56:53 +00:00
|
|
|
formData.append("scope", 'all');
|
|
|
|
|
|
|
|
|
|
|
|
fetch(getBackendDomain() + '/token', {method: 'POST', body: formData})
|
|
|
|
.then((response) => response.json()
|
|
|
|
.then((result: APIToken) => {
|
2021-03-14 12:49:24 +00:00
|
|
|
if (result.error) {
|
|
|
|
callFuncQue(result.error);
|
|
|
|
return;
|
|
|
|
}
|
2021-03-09 12:56:53 +00:00
|
|
|
console.log(result)
|
|
|
|
// set api token
|
|
|
|
apiToken = result.access_token;
|
|
|
|
// set expire time
|
|
|
|
expireSeconds = (new Date().getTime() / 1000) + result.expires_in;
|
|
|
|
setTokenCookie(apiToken, expireSeconds);
|
|
|
|
// call all handlers and release flag
|
2021-03-14 12:49:24 +00:00
|
|
|
callFuncQue('');
|
2021-03-09 12:56:53 +00:00
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
2021-03-14 12:49:24 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-03-09 12:56:53 +00:00
|
|
|
/**
|
|
|
|
* call all qued callbacks
|
|
|
|
*/
|
2021-03-14 12:49:24 +00:00
|
|
|
function callFuncQue(error: string): void {
|
2021-03-09 12:56:53 +00:00
|
|
|
// call all pending handlers
|
|
|
|
callQue.map(func => {
|
2021-03-14 12:49:24 +00:00
|
|
|
return func(error);
|
2021-03-09 12:56:53 +00:00
|
|
|
})
|
|
|
|
// reset pending que
|
|
|
|
callQue = []
|
|
|
|
// release flag to be able to start new refresh
|
|
|
|
refreshInProcess = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* set the cookie for the currently gotten token
|
|
|
|
* @param token token string
|
|
|
|
* @param validSec second time when the token will be invalid
|
|
|
|
*/
|
|
|
|
function setTokenCookie(token: string, validSec: number): void {
|
|
|
|
let d = new Date();
|
|
|
|
d.setTime(validSec * 1000);
|
|
|
|
console.log("token set" + d.toUTCString())
|
|
|
|
let expires = "expires=" + d.toUTCString();
|
|
|
|
document.cookie = "token=" + token + ";" + expires + ";path=/";
|
|
|
|
document.cookie = "token_expire=" + validSec + ";" + expires + ";path=/";
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* get all required cookies for the token
|
|
|
|
*/
|
|
|
|
function getTokenCookie(): { token: string, expire: number } | null {
|
|
|
|
const token = decodeCookie('token');
|
|
|
|
const expireInString = decodeCookie('token_expire');
|
|
|
|
const expireIn = parseInt(expireInString, 10) | 0;
|
|
|
|
|
|
|
|
if (expireIn !== 0 && token !== '') {
|
|
|
|
return {token: token, expire: expireIn};
|
|
|
|
} else {
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* decode a simple cookie with key specified
|
|
|
|
* @param key cookie key
|
|
|
|
*/
|
|
|
|
function decodeCookie(key: string): string {
|
|
|
|
let name = key + "=";
|
|
|
|
let decodedCookie = decodeURIComponent(document.cookie);
|
|
|
|
let ca = decodedCookie.split(';');
|
|
|
|
for (let i = 0; i < ca.length; i++) {
|
|
|
|
let c = ca[i];
|
|
|
|
while (c.charAt(0) === ' ') {
|
|
|
|
c = c.substring(1);
|
|
|
|
}
|
|
|
|
if (c.indexOf(name) === 0) {
|
|
|
|
return c.substring(name.length, c.length);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* check if api token is valid -- if not request new one
|
|
|
|
* when finished call callback
|
|
|
|
* @param callback function to be called afterwards
|
|
|
|
*/
|
|
|
|
function checkAPITokenValid(callback: () => void): void {
|
|
|
|
// check if token is valid and set
|
|
|
|
if (apiToken === '' || expireSeconds <= new Date().getTime() / 1000) {
|
|
|
|
refreshAPIToken(() => {
|
|
|
|
callback()
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
callback()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-17 20:53:22 +00:00
|
|
|
/**
|
|
|
|
* A backend api call
|
|
|
|
* @param apinode which api backend handler to call
|
|
|
|
* @param fd the object to send to backend
|
|
|
|
* @param callback the callback with json reply from backend
|
|
|
|
* @param errorcallback a optional callback if an error occured
|
|
|
|
*/
|
2021-03-09 12:56:53 +00:00
|
|
|
export function callAPI<T>(apinode: APINode,
|
|
|
|
fd: ApiBaseRequest,
|
|
|
|
callback: (_: T) => void,
|
|
|
|
errorcallback: (_: string) => void = (_: string): void => {
|
|
|
|
}): void {
|
|
|
|
checkAPITokenValid(() => {
|
|
|
|
fetch(getAPIDomain() + apinode, {
|
|
|
|
method: 'POST', body: JSON.stringify(fd), headers: new Headers({
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
'Authorization': 'Bearer ' + apiToken,
|
|
|
|
}),
|
|
|
|
}).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(reason));
|
|
|
|
})
|
2020-12-17 20:53:22 +00:00
|
|
|
}
|
|
|
|
|
2021-03-14 12:49:24 +00:00
|
|
|
/**
|
|
|
|
* 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) : {})
|
|
|
|
}
|
|
|
|
|
2020-12-17 20:53:22 +00:00
|
|
|
/**
|
|
|
|
* A backend api call
|
|
|
|
* @param apinode which api backend handler to call
|
|
|
|
* @param fd the object to send to backend
|
|
|
|
* @param callback the callback with PLAIN text reply from backend
|
|
|
|
*/
|
2021-01-29 22:15:17 +00:00
|
|
|
export function callAPIPlain(apinode: APINode, fd: ApiBaseRequest, callback: (_: string) => void): void {
|
2021-03-09 12:56:53 +00:00
|
|
|
checkAPITokenValid(() => {
|
|
|
|
fetch(getAPIDomain() + apinode, {
|
|
|
|
method: 'POST', body: JSON.stringify(fd), headers: new Headers({
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
'Authorization': 'Bearer ' + apiToken,
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.then((response) => response.text()
|
|
|
|
.then((result) => {
|
|
|
|
callback(result);
|
|
|
|
}));
|
|
|
|
});
|
2020-12-17 20:53:22 +00:00
|
|
|
}
|
2021-01-29 22:15:17 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* API nodes definitions
|
|
|
|
*/
|
|
|
|
export enum APINode {
|
2021-02-23 16:01:29 +00:00
|
|
|
Settings = 'settings',
|
|
|
|
Tags = 'tags',
|
|
|
|
Actor = 'actor',
|
2021-03-14 12:49:24 +00:00
|
|
|
Video = 'video',
|
|
|
|
Init = 'init'
|
2021-01-29 22:15:17 +00:00
|
|
|
}
|