@yomotech/view-toolkit 0.0.51 → 0.0.53
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -6,7 +6,7 @@ import * as i3 from '@angular/forms';
|
|
|
6
6
|
import { FormsModule } from '@angular/forms';
|
|
7
7
|
import * as i1$1 from '@angular/common/http';
|
|
8
8
|
import { HttpHeaders } from '@angular/common/http';
|
|
9
|
-
import {
|
|
9
|
+
import { Capacitor, CapacitorHttp } from '@capacitor/core';
|
|
10
10
|
import * as i10 from 'ng-zorro-antd/icon';
|
|
11
11
|
import { NzIconModule } from 'ng-zorro-antd/icon';
|
|
12
12
|
import * as i12 from 'ng-zorro-antd/button';
|
|
@@ -861,78 +861,86 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImpor
|
|
|
861
861
|
|
|
862
862
|
class Couchdb {
|
|
863
863
|
utilityService;
|
|
864
|
-
|
|
864
|
+
remoteCouchDbBaseUrl = 'https://couchdb.yomotech.de';
|
|
865
|
+
webProxyBaseUrl = '/couchdb-api';
|
|
865
866
|
constructor(utilityService) {
|
|
866
867
|
this.utilityService = utilityService;
|
|
867
868
|
}
|
|
869
|
+
/**
|
|
870
|
+
* Angular web development:
|
|
871
|
+
* /couchdb-api -> Angular proxy -> https://couchdb.yomotech.de
|
|
872
|
+
*
|
|
873
|
+
* Capacitor Android/iOS:
|
|
874
|
+
* Direct native request to https://couchdb.yomotech.de
|
|
875
|
+
*/
|
|
876
|
+
get couchDbBaseUrl() {
|
|
877
|
+
return Capacitor.isNativePlatform()
|
|
878
|
+
? this.remoteCouchDbBaseUrl
|
|
879
|
+
: this.webProxyBaseUrl;
|
|
880
|
+
}
|
|
868
881
|
async getTenantDatabases(tenantKey) {
|
|
869
|
-
|
|
882
|
+
const normalizedTenantKey = tenantKey?.trim();
|
|
883
|
+
if (!normalizedTenantKey) {
|
|
870
884
|
throw new Error('Missing tenantKey');
|
|
871
885
|
}
|
|
872
|
-
const response = await fetch(
|
|
886
|
+
const response = await fetch(`${this.couchDbBaseUrl}/tenant-dbs`, {
|
|
873
887
|
method: 'GET',
|
|
888
|
+
cache: 'no-store',
|
|
874
889
|
headers: {
|
|
875
890
|
Accept: 'application/json',
|
|
876
|
-
'X-Tenant-Key':
|
|
891
|
+
'X-Tenant-Key': normalizedTenantKey,
|
|
877
892
|
},
|
|
878
893
|
});
|
|
879
894
|
const data = await this.parseJsonOrText(response);
|
|
880
895
|
if (!response.ok) {
|
|
881
|
-
throw new Error(`GET /tenant-dbs -> ${response.status}
|
|
882
|
-
|
|
896
|
+
throw new Error(`GET /tenant-dbs -> ${response.status} ` +
|
|
897
|
+
`${this.stringifyErrorDetails(data)}`);
|
|
898
|
+
}
|
|
899
|
+
console.log('data: ', data);
|
|
900
|
+
// if (!Array.isArray(data) || !Array.isArray(data.databases)) {
|
|
901
|
+
// throw new Error(
|
|
902
|
+
// `Invalid /tenant-dbs response: ${this.stringifyErrorDetails(data)}`
|
|
903
|
+
// );
|
|
904
|
+
// }
|
|
883
905
|
return data;
|
|
884
906
|
}
|
|
885
|
-
async parseJsonOrText(response) {
|
|
886
|
-
const rawText = await response.text();
|
|
887
|
-
if (!rawText)
|
|
888
|
-
return null;
|
|
889
|
-
try {
|
|
890
|
-
return JSON.parse(rawText);
|
|
891
|
-
}
|
|
892
|
-
catch {
|
|
893
|
-
return rawText;
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
stringifyErrorDetails(data) {
|
|
897
|
-
if (typeof data === 'string')
|
|
898
|
-
return data;
|
|
899
|
-
try {
|
|
900
|
-
return JSON.stringify(data);
|
|
901
|
-
}
|
|
902
|
-
catch {
|
|
903
|
-
return 'Unknown error';
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
907
|
async getFileContentFromCouchDb(couchDbName, filePath) {
|
|
907
908
|
try {
|
|
908
|
-
const dbName = couchDbName?.toLowerCase();
|
|
909
|
+
const dbName = couchDbName?.trim().toLowerCase();
|
|
909
910
|
const docId = filePath?.trim();
|
|
911
|
+
if (!dbName) {
|
|
912
|
+
console.error('Missing CouchDB database name');
|
|
913
|
+
return null;
|
|
914
|
+
}
|
|
910
915
|
if (!docId) {
|
|
911
916
|
console.error('Missing file path');
|
|
912
917
|
return null;
|
|
913
918
|
}
|
|
914
919
|
const response = await fetch(this.getDocumentUrl(dbName, docId), {
|
|
915
920
|
method: 'GET',
|
|
921
|
+
cache: 'no-store',
|
|
916
922
|
headers: await this.getReadHeaders(),
|
|
917
923
|
});
|
|
918
924
|
const doc = await this.parseJsonOrText(response);
|
|
919
925
|
if (!response.ok) {
|
|
920
|
-
console.error(`Failed to fetch file "${docId}" from "${dbName}":
|
|
926
|
+
console.error(`Failed to fetch file "${docId}" from "${dbName}": ` +
|
|
927
|
+
`${response.status}`, doc);
|
|
921
928
|
return null;
|
|
922
929
|
}
|
|
923
930
|
if (!doc?.data) {
|
|
924
931
|
console.error(`Document "${docId}" does not contain file data`);
|
|
925
932
|
return null;
|
|
926
933
|
}
|
|
927
|
-
// do not decode images as utf8 text
|
|
928
934
|
if (doc.type === 'media' ||
|
|
929
935
|
doc.type === 'base64file' ||
|
|
930
936
|
this.isBinaryMediaFile(doc.name || doc.path || docId, doc.contentType)) {
|
|
937
|
+
const fileName = doc.name || doc.path || docId;
|
|
938
|
+
const mimeType = this.getMimeType(fileName, doc.contentType);
|
|
931
939
|
return {
|
|
932
940
|
type: doc.type || 'base64file',
|
|
933
|
-
mimeType
|
|
941
|
+
mimeType,
|
|
934
942
|
base64: doc.data,
|
|
935
|
-
src: `data:${
|
|
943
|
+
src: `data:${mimeType};base64,${doc.data}`,
|
|
936
944
|
name: doc.name,
|
|
937
945
|
path: doc.path,
|
|
938
946
|
metadata: doc.metadata || {},
|
|
@@ -951,13 +959,122 @@ class Couchdb {
|
|
|
951
959
|
return decodedContent;
|
|
952
960
|
}
|
|
953
961
|
catch (error) {
|
|
954
|
-
console.error(`Unexpected error while fetching file "${filePath}"
|
|
962
|
+
console.error(`Unexpected error while fetching file "${filePath}" ` +
|
|
963
|
+
`from "${couchDbName}"`, error);
|
|
964
|
+
return null;
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
async getImageSrcFromCouchDb(couchDbName, filePath) {
|
|
968
|
+
const dbName = couchDbName?.trim().toLowerCase();
|
|
969
|
+
const docId = filePath?.trim();
|
|
970
|
+
if (!dbName) {
|
|
971
|
+
throw new Error('Missing CouchDB database name');
|
|
972
|
+
}
|
|
973
|
+
if (!docId) {
|
|
974
|
+
throw new Error('Missing file path');
|
|
975
|
+
}
|
|
976
|
+
const response = await fetch(this.getDocumentUrl(dbName, docId), {
|
|
977
|
+
method: 'GET',
|
|
978
|
+
cache: 'no-store',
|
|
979
|
+
headers: await this.getReadHeaders(),
|
|
980
|
+
});
|
|
981
|
+
const doc = await this.parseJsonOrText(response);
|
|
982
|
+
if (!response.ok) {
|
|
983
|
+
throw new Error(`Failed to fetch image "${docId}" from "${dbName}": ` +
|
|
984
|
+
`${response.status} ${this.stringifyErrorDetails(doc)}`);
|
|
985
|
+
}
|
|
986
|
+
if (!doc?.data) {
|
|
987
|
+
throw new Error(`Document "${docId}" does not contain image data`);
|
|
988
|
+
}
|
|
989
|
+
const mimeType = this.getMimeType(doc.name || doc.path || docId, doc.contentType);
|
|
990
|
+
return `data:${mimeType};base64,${doc.data}`;
|
|
991
|
+
}
|
|
992
|
+
getDocumentUrl(dbName, docId) {
|
|
993
|
+
return (`${this.getDbUrl(dbName)}/` +
|
|
994
|
+
`${encodeURIComponent(docId)}`);
|
|
995
|
+
}
|
|
996
|
+
getDbUrl(dbName) {
|
|
997
|
+
const normalizedDbName = dbName
|
|
998
|
+
?.trim()
|
|
999
|
+
.toLowerCase();
|
|
1000
|
+
if (!normalizedDbName) {
|
|
1001
|
+
throw new Error('Missing CouchDB database name');
|
|
1002
|
+
}
|
|
1003
|
+
return (`${this.couchDbBaseUrl}/` +
|
|
1004
|
+
`${encodeURIComponent(normalizedDbName)}`);
|
|
1005
|
+
}
|
|
1006
|
+
async getReadHeaders() {
|
|
1007
|
+
return {
|
|
1008
|
+
Accept: 'application/json',
|
|
1009
|
+
...(await this.buildTenantHeaders()),
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
1012
|
+
async buildTenantHeaders() {
|
|
1013
|
+
const tenantKey = await this.utilityService.getStorage('tenantKey');
|
|
1014
|
+
const tenantName = await this.utilityService.getStorage('tenantName');
|
|
1015
|
+
const selectedTenant = await this.utilityService.getLocalStorage('selectedTenant');
|
|
1016
|
+
if (typeof tenantKey === 'string' &&
|
|
1017
|
+
tenantKey.trim()) {
|
|
1018
|
+
return {
|
|
1019
|
+
'X-Tenant-Key': tenantKey.trim(),
|
|
1020
|
+
};
|
|
1021
|
+
}
|
|
1022
|
+
const normalizedTenantName = typeof tenantName === 'string'
|
|
1023
|
+
? tenantName.trim()
|
|
1024
|
+
: '';
|
|
1025
|
+
const normalizedSelectedTenant = typeof selectedTenant === 'string'
|
|
1026
|
+
? selectedTenant.trim()
|
|
1027
|
+
: '';
|
|
1028
|
+
const tenant = normalizedTenantName ||
|
|
1029
|
+
normalizedSelectedTenant;
|
|
1030
|
+
if (!tenant) {
|
|
1031
|
+
throw new Error('Missing tenantKey, tenantName, or selectedTenant');
|
|
1032
|
+
}
|
|
1033
|
+
return {
|
|
1034
|
+
'X-Tenant': tenant,
|
|
1035
|
+
};
|
|
1036
|
+
}
|
|
1037
|
+
async parseJsonOrText(response) {
|
|
1038
|
+
const rawText = await response.text();
|
|
1039
|
+
if (!rawText) {
|
|
955
1040
|
return null;
|
|
956
1041
|
}
|
|
1042
|
+
try {
|
|
1043
|
+
return JSON.parse(rawText);
|
|
1044
|
+
}
|
|
1045
|
+
catch {
|
|
1046
|
+
return rawText;
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
stringifyErrorDetails(data) {
|
|
1050
|
+
if (typeof data === 'string') {
|
|
1051
|
+
return data;
|
|
1052
|
+
}
|
|
1053
|
+
try {
|
|
1054
|
+
return JSON.stringify(data);
|
|
1055
|
+
}
|
|
1056
|
+
catch {
|
|
1057
|
+
return 'Unknown error';
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
fromBase64(base64) {
|
|
1061
|
+
try {
|
|
1062
|
+
const binaryString = atob(base64);
|
|
1063
|
+
const bytes = Uint8Array.from(binaryString, character => character.charCodeAt(0));
|
|
1064
|
+
return new TextDecoder('utf-8').decode(bytes);
|
|
1065
|
+
}
|
|
1066
|
+
catch {
|
|
1067
|
+
return atob(base64);
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
isJsonFile(fileNameOrPath, contentType) {
|
|
1071
|
+
const lower = (fileNameOrPath || '').toLowerCase();
|
|
1072
|
+
return (lower.endsWith('.json') ||
|
|
1073
|
+
contentType === 'application/json');
|
|
957
1074
|
}
|
|
958
1075
|
isBinaryMediaFile(fileNameOrPath, contentType) {
|
|
959
1076
|
const lower = (fileNameOrPath || '').toLowerCase();
|
|
960
|
-
const type = contentType || '';
|
|
1077
|
+
const type = (contentType || '').toLowerCase();
|
|
961
1078
|
return (type.startsWith('image/') ||
|
|
962
1079
|
type.startsWith('video/') ||
|
|
963
1080
|
type.startsWith('audio/') ||
|
|
@@ -977,111 +1094,48 @@ class Couchdb {
|
|
|
977
1094
|
lower.endsWith('.pdf'));
|
|
978
1095
|
}
|
|
979
1096
|
getMimeType(fileNameOrPath, contentType) {
|
|
980
|
-
if (contentType)
|
|
1097
|
+
if (contentType) {
|
|
981
1098
|
return contentType;
|
|
1099
|
+
}
|
|
982
1100
|
const lower = (fileNameOrPath || '').toLowerCase();
|
|
983
|
-
if (lower.endsWith('.png'))
|
|
1101
|
+
if (lower.endsWith('.png')) {
|
|
984
1102
|
return 'image/png';
|
|
985
|
-
|
|
1103
|
+
}
|
|
1104
|
+
if (lower.endsWith('.jpg') ||
|
|
1105
|
+
lower.endsWith('.jpeg')) {
|
|
986
1106
|
return 'image/jpeg';
|
|
987
|
-
|
|
1107
|
+
}
|
|
1108
|
+
if (lower.endsWith('.ico')) {
|
|
988
1109
|
return 'image/x-icon';
|
|
989
|
-
|
|
1110
|
+
}
|
|
1111
|
+
if (lower.endsWith('.svg')) {
|
|
990
1112
|
return 'image/svg+xml';
|
|
991
|
-
|
|
1113
|
+
}
|
|
1114
|
+
if (lower.endsWith('.webp')) {
|
|
992
1115
|
return 'image/webp';
|
|
993
|
-
|
|
1116
|
+
}
|
|
1117
|
+
if (lower.endsWith('.gif')) {
|
|
994
1118
|
return 'image/gif';
|
|
995
|
-
if (lower.endsWith('.mp4'))
|
|
996
|
-
return 'video/mp4';
|
|
997
|
-
if (lower.endsWith('.webm'))
|
|
998
|
-
return 'video/webm';
|
|
999
|
-
if (lower.endsWith('.mp3'))
|
|
1000
|
-
return 'audio/mpeg';
|
|
1001
|
-
if (lower.endsWith('.wav'))
|
|
1002
|
-
return 'audio/wav';
|
|
1003
|
-
if (lower.endsWith('.m4a'))
|
|
1004
|
-
return 'audio/mp4';
|
|
1005
|
-
if (lower.endsWith('.pdf'))
|
|
1006
|
-
return 'application/pdf';
|
|
1007
|
-
return 'application/octet-stream';
|
|
1008
|
-
}
|
|
1009
|
-
getImageMimeType(fileNameOrPath) {
|
|
1010
|
-
const lower = (fileNameOrPath || '').toLowerCase();
|
|
1011
|
-
if (lower.endsWith('.png'))
|
|
1012
|
-
return 'image/png';
|
|
1013
|
-
if (lower.endsWith('.jpg') || lower.endsWith('.jpeg'))
|
|
1014
|
-
return 'image/jpeg';
|
|
1015
|
-
if (lower.endsWith('.ico'))
|
|
1016
|
-
return 'image/x-icon';
|
|
1017
|
-
if (lower.endsWith('.svg'))
|
|
1018
|
-
return 'image/svg+xml';
|
|
1019
|
-
return 'image/png';
|
|
1020
|
-
}
|
|
1021
|
-
fromBase64(base64) {
|
|
1022
|
-
try {
|
|
1023
|
-
return decodeURIComponent(escape(atob(base64)));
|
|
1024
1119
|
}
|
|
1025
|
-
|
|
1026
|
-
return
|
|
1120
|
+
if (lower.endsWith('.mp4')) {
|
|
1121
|
+
return 'video/mp4';
|
|
1027
1122
|
}
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
const lower = (fileNameOrPath || '').toLowerCase();
|
|
1031
|
-
return lower.endsWith('.json') || contentType === 'application/json';
|
|
1032
|
-
}
|
|
1033
|
-
getDocumentUrl(dbName, docId) {
|
|
1034
|
-
return `${this.getDbUrl(dbName)}/${encodeURIComponent(docId)}`;
|
|
1035
|
-
}
|
|
1036
|
-
getDbUrl(dbName) {
|
|
1037
|
-
return `${this.couchDbBaseUrl}/${dbName}`;
|
|
1038
|
-
}
|
|
1039
|
-
async getReadHeaders() {
|
|
1040
|
-
return {
|
|
1041
|
-
Accept: 'application/json',
|
|
1042
|
-
...(await this.buildTenantHeaders()),
|
|
1043
|
-
};
|
|
1044
|
-
}
|
|
1045
|
-
async buildTenantHeaders() {
|
|
1046
|
-
const tenantKey = await this.utilityService.getStorage('tenantKey');
|
|
1047
|
-
const tenantName = await this.utilityService.getStorage('tenantName');
|
|
1048
|
-
const selectedTenant = await this.utilityService.getLocalStorage('selectedTenant');
|
|
1049
|
-
if (tenantKey) {
|
|
1050
|
-
if (typeof tenantKey !== 'string' || !tenantKey.trim()) {
|
|
1051
|
-
throw new Error('Missing tenantKey');
|
|
1052
|
-
}
|
|
1053
|
-
return {
|
|
1054
|
-
'X-Tenant-Key': tenantKey.trim(),
|
|
1055
|
-
};
|
|
1123
|
+
if (lower.endsWith('.webm')) {
|
|
1124
|
+
return 'video/webm';
|
|
1056
1125
|
}
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
throw new Error('Missing tenantName');
|
|
1060
|
-
}
|
|
1061
|
-
return {
|
|
1062
|
-
'X-Tenant': tenantName ? tenantName.trim() : selectedTenant.trim(),
|
|
1063
|
-
};
|
|
1126
|
+
if (lower.endsWith('.mp3')) {
|
|
1127
|
+
return 'audio/mpeg';
|
|
1064
1128
|
}
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
const dbName = couchDbName?.toLowerCase();
|
|
1068
|
-
const docId = filePath?.trim();
|
|
1069
|
-
if (!docId) {
|
|
1070
|
-
throw new Error('Missing file path');
|
|
1129
|
+
if (lower.endsWith('.wav')) {
|
|
1130
|
+
return 'audio/wav';
|
|
1071
1131
|
}
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
headers: await this.getReadHeaders(),
|
|
1075
|
-
});
|
|
1076
|
-
const doc = await this.parseJsonOrText(response);
|
|
1077
|
-
if (!response.ok) {
|
|
1078
|
-
throw new Error(`Failed to fetch image "${docId}" from "${dbName}": ${response.status} ${this.stringifyErrorDetails(doc)}`);
|
|
1132
|
+
if (lower.endsWith('.m4a')) {
|
|
1133
|
+
return 'audio/mp4';
|
|
1079
1134
|
}
|
|
1080
|
-
if (
|
|
1081
|
-
|
|
1135
|
+
if (lower.endsWith('.pdf')) {
|
|
1136
|
+
return 'application/pdf';
|
|
1082
1137
|
}
|
|
1083
|
-
|
|
1084
|
-
return `data:${mimeType};base64,${doc.data}`;
|
|
1138
|
+
return 'application/octet-stream';
|
|
1085
1139
|
}
|
|
1086
1140
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: Couchdb, deps: [{ token: UtilityService }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1087
1141
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: Couchdb, providedIn: 'root' });
|