|
|
|
import {Injectable} from '@angular/core';
|
|
|
|
import {environment} from '../../environments/environment';
|
|
|
|
import {HttpClient} from '@angular/common/http';
|
|
|
|
import {BehaviorSubject, Observable} from 'rxjs';
|
|
|
|
import ApiResponse from '../structures/ApiResponse';
|
|
|
|
import {MenuItem} from './db/MenuItem';
|
|
|
|
import {DatabaseService} from './db/database.service';
|
|
|
|
import {ConnectionService} from 'ng-connection-service';
|
|
|
|
|
|
|
|
@Injectable({
|
|
|
|
providedIn: 'root'
|
|
|
|
})
|
|
|
|
export class ApiService {
|
|
|
|
protected baseEndpoint: string = environment.backendBase;
|
|
|
|
protected statUrl: string = environment.statUrl;
|
|
|
|
protected versionUrl: string = environment.versionUrl;
|
|
|
|
protected offline = false;
|
|
|
|
public readonly offline$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
|
|
|
|
|
|
|
get isOffline() {
|
|
|
|
return this.offline;
|
|
|
|
}
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
protected http: HttpClient,
|
|
|
|
protected db: DatabaseService,
|
|
|
|
protected connection: ConnectionService,
|
|
|
|
) {
|
|
|
|
connection.monitor().subscribe(isConnected => {
|
|
|
|
if ( !isConnected ) {
|
|
|
|
this.makeOffline();
|
|
|
|
} else {
|
|
|
|
this.makeOnline(); // TODO add checks for server.
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public makeOffline() {
|
|
|
|
this.offline = true;
|
|
|
|
this.offline$.next(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
public makeOnline() {
|
|
|
|
this.offline = false;
|
|
|
|
this.offline$.next(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
public get(endpoint, params = {}): Observable<ApiResponse> {
|
|
|
|
return this.request(endpoint, params, 'get');
|
|
|
|
}
|
|
|
|
|
|
|
|
public post(endpoint, body = {}): Observable<ApiResponse> {
|
|
|
|
return this.request(endpoint, body, 'post');
|
|
|
|
}
|
|
|
|
|
|
|
|
public request(endpoint, params = {}, method: 'get'|'post' = 'get'): Observable<ApiResponse> {
|
|
|
|
return this._request(this._build_url(endpoint), params, method);
|
|
|
|
}
|
|
|
|
|
|
|
|
public stat(): Observable<ApiResponse> {
|
|
|
|
return new Observable<ApiResponse>(sub => {
|
|
|
|
(async () => {
|
|
|
|
const statKV = await this.db.getKeyValue('host_stat');
|
|
|
|
|
|
|
|
// If offline, look up the last stored stat for information
|
|
|
|
if ( this.isOffline ) {
|
|
|
|
if ( typeof statKV !== 'object' ) {
|
|
|
|
throw new Error('No locally stored host stat found.');
|
|
|
|
}
|
|
|
|
|
|
|
|
sub.next(new ApiResponse(statKV.data));
|
|
|
|
sub.complete();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, fetch the stat and cache it locally
|
|
|
|
this._request(this.statUrl).subscribe(apiResponse => {
|
|
|
|
statKV.data = {status: apiResponse.status, message: apiResponse.message, data: apiResponse.data};
|
|
|
|
statKV.save().then(() => {
|
|
|
|
sub.next(statKV.data);
|
|
|
|
sub.complete();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
})();
|
|
|
|
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public version(): Promise<string> {
|
|
|
|
return new Promise(async (res, rej) => {
|
|
|
|
const versionKV = await this.db.getKeyValue('app_version');
|
|
|
|
|
|
|
|
// If offline, look up the local app version.
|
|
|
|
if ( this.isOffline ) {
|
|
|
|
if ( versionKV ) {
|
|
|
|
return res(versionKV.data);
|
|
|
|
} else {
|
|
|
|
return rej(new Error('No local app version found.'));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, look up the app version and store it locally
|
|
|
|
this._request(this.versionUrl).subscribe({
|
|
|
|
next: async result => {
|
|
|
|
const version = result.data.text.trim();
|
|
|
|
|
|
|
|
versionKV.data = version;
|
|
|
|
await versionKV.save();
|
|
|
|
|
|
|
|
res(version);
|
|
|
|
},
|
|
|
|
error: rej,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
protected _request(endpoint, params = {}, method: 'get'|'post' = 'get'): Observable<ApiResponse> {
|
|
|
|
return new Observable<ApiResponse>(sub => {
|
|
|
|
let data: any = {}
|
|
|
|
if ( method === 'get' ) {
|
|
|
|
data.params = params;
|
|
|
|
} else {
|
|
|
|
data = params;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.http[method](endpoint, data).subscribe({
|
|
|
|
next: (response: any) => {
|
|
|
|
sub.next(new ApiResponse(response));
|
|
|
|
},
|
|
|
|
error: (err) => {
|
|
|
|
const response = {
|
|
|
|
status: err.status,
|
|
|
|
message: err.message,
|
|
|
|
data: err.error,
|
|
|
|
};
|
|
|
|
|
|
|
|
sub.next(new ApiResponse(response));
|
|
|
|
},
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public _build_url(endpoint) {
|
|
|
|
if ( !endpoint.startsWith('/') ) {
|
|
|
|
endpoint = `/${endpoint}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
return `${this.baseEndpoint.endsWith('/') ? this.baseEndpoint.slice(0, -1) : this.baseEndpoint}${endpoint}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
public getMenuItems(): Promise<any[]> {
|
|
|
|
return new Promise(async (res, rej) => {
|
|
|
|
await this.db.createSchemata();
|
|
|
|
|
|
|
|
// If offline, fetch the menu from the database
|
|
|
|
if ( this.isOffline ) {
|
|
|
|
const items = await this.db.menuItems.toArray();
|
|
|
|
const nodes = MenuItem.inflateTree(items as MenuItem[]);
|
|
|
|
return res(nodes);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Download the latest menu items
|
|
|
|
const tree: any[] = await new Promise(res2 => {
|
|
|
|
this.get('/menu/items').subscribe({
|
|
|
|
next: async result => {
|
|
|
|
const nodes = result.data as any[];
|
|
|
|
const items = MenuItem.deflateTree(nodes);
|
|
|
|
|
|
|
|
// Update the locally stored nodes
|
|
|
|
await this.db.menuItems.clear();
|
|
|
|
await Promise.all(items.map(item => item.save()));
|
|
|
|
|
|
|
|
res2(nodes);
|
|
|
|
},
|
|
|
|
error: rej,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
res(tree);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public getSessionData(): Promise<any> {
|
|
|
|
return new Promise(async (res, rej) => {
|
|
|
|
const sessionKV = await this.db.getKeyValue('session_data');
|
|
|
|
|
|
|
|
// If offline, just return the locally cached session data
|
|
|
|
if ( this.isOffline ) {
|
|
|
|
if ( typeof sessionKV.data !== 'object' ) {
|
|
|
|
return rej(new Error('No locally cached session data found.'));
|
|
|
|
}
|
|
|
|
|
|
|
|
return res(sessionKV.data);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, fetch the session data from the server and cache it locally
|
|
|
|
this.get('/session').subscribe(async result => {
|
|
|
|
sessionKV.data = result.data;
|
|
|
|
await sessionKV.save();
|
|
|
|
res(sessionKV.data);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public saveSessionData(data: any): Promise<void> {
|
|
|
|
return new Promise(async (res, rej) => {
|
|
|
|
// Update the local session data
|
|
|
|
const sessionKV = await this.db.getKeyValue('session_data');
|
|
|
|
sessionKV.data = data;
|
|
|
|
await sessionKV.save();
|
|
|
|
|
|
|
|
// If we're not offline, then update the data on the server
|
|
|
|
if ( !this.isOffline ) {
|
|
|
|
await new Promise(res2 => {
|
|
|
|
this.post('/session', data || {}).subscribe({
|
|
|
|
next: res2,
|
|
|
|
error: rej,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
res();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|