diff --git a/src/app/app.component.html b/src/app/app.component.html
index 88f2a69..fa18240 100644
--- a/src/app/app.component.html
+++ b/src/app/app.component.html
@@ -41,7 +41,6 @@
-
Menu
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index add8854..9598146 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -1,4 +1,4 @@
-import {Component, OnInit, ViewChild, HostListener, Host} from '@angular/core';
+import {Component, OnInit, ViewChild, HostListener} from '@angular/core';
import {
AlertController,
@@ -144,21 +144,6 @@ export class AppComponent implements OnInit {
}).then(popover => popover.present());
}
- onFilterChange($event) {
- const query = $event.detail.value.toLowerCase();
- this.menuTree.treeModel.clearFilter();
- if ( query ) {
- this.menuTree.treeModel.filterNodes(node => {
- if ( node.data.virtual ) {
- // "Virtual" tree nodes should always be shown
- return true;
- }
-
- return node.data.name.toLowerCase().indexOf(query) >= 0;
- });
- }
- }
-
@HostListener('document:keyup.control./', ['$event'])
async handleKeyboardEvent() {
if ( this.hasSearchOpen ) {
diff --git a/src/app/service/api.service.ts b/src/app/service/api.service.ts
index bcf0464..84989ad 100644
--- a/src/app/service/api.service.ts
+++ b/src/app/service/api.service.ts
@@ -285,12 +285,27 @@ export class ApiService {
});
}
- public _build_url(endpoint) {
+ public _build_url(endpoint, base = this.baseEndpoint) {
if ( !endpoint.startsWith('/') ) {
endpoint = `/${endpoint}`;
}
- return `${this.baseEndpoint.endsWith('/') ? this.baseEndpoint.slice(0, -1) : this.baseEndpoint}${endpoint}`;
+ return `${base.endsWith('/') ? base.slice(0, -1) : base}${endpoint}`;
+ }
+
+ public async getToken(): Promise {
+ return new Promise(async (res, rej) => {
+ if ( this.isOffline ) {
+ return rej(new ResourceNotAvailableOfflineError());
+ }
+
+ this.get('token').subscribe({
+ next: response => {
+ return res(response.data);
+ },
+ error: rej,
+ });
+ });
}
public async syncOfflineData() {
diff --git a/src/app/service/db-api.service.ts b/src/app/service/db-api.service.ts
new file mode 100644
index 0000000..2579a90
--- /dev/null
+++ b/src/app/service/db-api.service.ts
@@ -0,0 +1,139 @@
+import {Injectable} from '@angular/core';
+import {ApiService} from './api.service';
+import {Observable} from 'rxjs';
+import {HttpClient, HttpHeaders} from '@angular/common/http';
+import ApiResponse from '../structures/ApiResponse';
+import {environment} from '../../environments/environment';
+import {debug} from '../utility';
+import * as Structures from '../structures/db-api';
+
+@Injectable({
+ providedIn: 'root',
+})
+export class DbApiService {
+ protected token?: string;
+ protected tokenTime?: number;
+ protected databaseBase: string = environment.databaseBase;
+
+ constructor(
+ protected readonly api: ApiService,
+ protected readonly http: HttpClient,
+ ) {
+ Structures._setDbApi(this);
+ }
+
+ public async getDatabase(databaseId: string): Promise {
+ return new Promise(async (res, rej) => {
+
+ this._authRequest(`/${databaseId}`).subscribe({
+ next: async response => {
+ if ( response.status !== 200 ) {
+ debug('Invalid get database response.', response);
+ return rej(new Error('Invalid database ID.'));
+ }
+
+ return res(new Structures.Database(response.data));
+ },
+ error: rej,
+ });
+ });
+ }
+
+ public async getDatabases(): Promise {
+ return new Promise(async (res, rej) => {
+
+ this._authRequest('/').subscribe({
+ next: async response => {
+ return res(response.data.map(x => new Structures.Database(x)));
+ },
+ error: rej,
+ });
+ });
+ }
+
+ public async getColumns(databaseId: string): Promise {
+ return new Promise(async (res, rej) => {
+
+ this._authRequest(`${databaseId}/columns`).subscribe({
+ next: async response => {
+ return res(response.data.map(x => new Structures.DatabaseColumn(x)));
+ },
+ error: rej,
+ });
+ });
+ }
+
+ public async getColumnOrder(databaseId: string): Promise {
+ return new Promise(async (res, rej) => {
+ this._authRequest(`${databaseId}/columns/order`).subscribe({
+ next: async response => {
+ return res(response.data);
+ },
+ error: rej,
+ });
+ });
+ }
+
+ public async getData(databaseId: string): Promise {
+ return new Promise(async (res, rej) => {
+ this._authRequest(`${databaseId}/data`).subscribe({
+ next: async response => {
+ return res(response.data.map(x => new Structures.DatabaseRecord(x)));
+ },
+ error: rej,
+ });
+ });
+ }
+
+ public async getDataRecord(databaseId: string, recordId: string): Promise {
+ return new Promise(async (res, rej) => {
+ this._authRequest(`${databaseId}/record/${recordId}`).subscribe({
+ next: async response => {
+ return res(new Structures.DatabaseRecord(response.data));
+ },
+ error: rej,
+ });
+ });
+ }
+
+ protected _authRequest(endpoint, params = {}, method: 'get' | 'post' = 'get') {
+ return new Observable(sub => {
+ this.getToken().then(token => {
+ const headers = new HttpHeaders({
+ authorization: `bearer ${token}`
+ });
+
+ debug('Auth request headers', headers);
+
+ if ( method === 'get' ) {
+ const data = {
+ params,
+ headers,
+ };
+
+ this.http.get(this.api._build_url(endpoint, this.databaseBase), data).subscribe({
+ next: (response: any) => {
+ return sub.next(new ApiResponse(response));
+ },
+ error: e => {
+ return sub.error(e);
+ },
+ });
+ }
+ });
+ });
+ }
+
+ public async getToken(): Promise {
+ const now = (new Date()).getTime();
+
+ // 1 hour
+ if ( this.token && this.tokenTime && (now - this.tokenTime) < (60 * 60 * 1000) ) {
+ return this.token;
+ }
+
+ this.token = await this.api.getToken();
+ this.tokenTime = now;
+ return this.token;
+ }
+}
diff --git a/src/app/structures/db-api.ts b/src/app/structures/db-api.ts
new file mode 100644
index 0000000..8fcd37d
--- /dev/null
+++ b/src/app/structures/db-api.ts
@@ -0,0 +1,102 @@
+import {DbApiService} from '../service/db-api.service';
+
+let privDbApi!: DbApiService;
+export function _setDbApi(dbApi: DbApiService) {
+ privDbApi = dbApi;
+}
+
+export function _getDbApi(): DbApiService {
+ return privDbApi;
+}
+
+export class Database {
+ public name!: string;
+ public uuid!: string;
+ public pageId!: string;
+ public columnIds: string[] = [];
+ public versionNum?: number;
+
+ static async all() {
+ return _getDbApi().getDatabases();
+ }
+
+ static async one(databaseId: string) {
+ return _getDbApi().getDatabase(databaseId);
+ }
+
+ constructor(record?: any) {
+ if ( record ) {
+ this.loadFrom(record);
+ }
+ }
+
+ loadFrom(record: any) {
+ this.name = record.name;
+ this.uuid = record.uuid;
+ this.pageId = record.page_id;
+ this.columnIds = record.column_ids;
+
+ if ( record.version_num ) {
+ this.versionNum = record.version_num;
+ }
+ }
+
+ async columns() {
+ const order = await this.columnOrder();
+ const columns = await _getDbApi().getColumns(this.uuid);
+
+ return order.map(x => columns.find(col => col.uuid === x));
+ }
+
+ async columnOrder() {
+ return _getDbApi().getColumnOrder(this.uuid);
+ }
+
+ async data() {
+ return _getDbApi().getData(this.uuid);
+ }
+
+ async record(id: string) {
+ return _getDbApi().getDataRecord(this.uuid, id);
+ }
+}
+
+export class DatabaseColumn {
+ public name!: string;
+ public databaseId!: string;
+ public uuid!: string;
+ public type!: string;
+ public metadata: any;
+
+ constructor(record?: any) {
+ if ( record ) {
+ this.loadFrom(record);
+ }
+ }
+
+ loadFrom(record: any) {
+ this.name = record.name;
+ this.databaseId = record.database_id;
+ this.uuid = record.uuid;
+ this.type = record.type;
+ this.metadata = record.metadata;
+ }
+}
+
+export class DatabaseRecord {
+ public data: any = {};
+ public uuid!: string;
+ public databaseId!: string;
+
+ constructor(record?: any) {
+ if ( record ) {
+ this.loadFrom(record);
+ }
+ }
+
+ loadFrom(record: any) {
+ this.data = record.data;
+ this.uuid = record.uuid;
+ this.databaseId = record.database_id;
+ }
+}
diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts
index 345f10a..0c527d9 100644
--- a/src/environments/environment.prod.ts
+++ b/src/environments/environment.prod.ts
@@ -1,6 +1,7 @@
export const environment = {
production: true,
backendBase: '/api/v1',
+ databaseBase: '/db_api/v1',
statUrl: '/stat?ngsw-bypass',
versionUrl: '/i/version.html?ngsw-bypass',
outputDebug: false,
diff --git a/src/environments/environment.ts b/src/environments/environment.ts
index 4e6eb22..91b9c3a 100644
--- a/src/environments/environment.ts
+++ b/src/environments/environment.ts
@@ -5,6 +5,7 @@
export const environment = {
production: false,
backendBase: '/link_api/api/v1',
+ databaseBase: '/link_api/db_api/v1',
statUrl: '/link_api/stat?ngsw-bypass',
versionUrl: '/link_api/assets/version.html?ngsw-bypass',
outputDebug: true,