@yomotech/view-toolkit 0.0.53 → 0.0.54
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.
|
@@ -866,43 +866,28 @@ class Couchdb {
|
|
|
866
866
|
constructor(utilityService) {
|
|
867
867
|
this.utilityService = utilityService;
|
|
868
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
|
-
}
|
|
881
869
|
async getTenantDatabases(tenantKey) {
|
|
882
870
|
const normalizedTenantKey = tenantKey?.trim();
|
|
883
871
|
if (!normalizedTenantKey) {
|
|
884
872
|
throw new Error('Missing tenantKey');
|
|
885
873
|
}
|
|
886
|
-
const
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
headers: {
|
|
890
|
-
Accept: 'application/json',
|
|
891
|
-
'X-Tenant-Key': normalizedTenantKey,
|
|
892
|
-
},
|
|
874
|
+
const result = await this.sendGetRequest('/tenant-dbs', {
|
|
875
|
+
Accept: 'application/json',
|
|
876
|
+
'X-Tenant-Key': normalizedTenantKey,
|
|
893
877
|
});
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
878
|
+
if (!result.ok) {
|
|
879
|
+
throw new Error(`GET /tenant-dbs -> ${result.status} ` +
|
|
880
|
+
`${this.stringifyErrorDetails(result.data)}`);
|
|
881
|
+
}
|
|
882
|
+
if (Array.isArray(result.data)) {
|
|
883
|
+
return result.data;
|
|
884
|
+
}
|
|
885
|
+
if (result.data &&
|
|
886
|
+
Array.isArray(result.data.databases)) {
|
|
887
|
+
return result.data.databases;
|
|
888
|
+
}
|
|
889
|
+
throw new Error(`Invalid /tenant-dbs response: ` +
|
|
890
|
+
`${this.stringifyErrorDetails(result.data)}`);
|
|
906
891
|
}
|
|
907
892
|
async getFileContentFromCouchDb(couchDbName, filePath) {
|
|
908
893
|
try {
|
|
@@ -916,51 +901,56 @@ class Couchdb {
|
|
|
916
901
|
console.error('Missing file path');
|
|
917
902
|
return null;
|
|
918
903
|
}
|
|
919
|
-
const
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
console.error(`Failed to fetch file "${docId}" from "${dbName}": ` +
|
|
927
|
-
`${response.status}`, doc);
|
|
904
|
+
const path = this.getDocumentPath(dbName, docId);
|
|
905
|
+
const result = await this.sendGetRequest(path, await this.getReadHeaders());
|
|
906
|
+
const doc = result.data;
|
|
907
|
+
if (!result.ok) {
|
|
908
|
+
console.error(`Failed to fetch file "${docId}" ` +
|
|
909
|
+
`from "${dbName}": ` +
|
|
910
|
+
`${result.status}`, doc);
|
|
928
911
|
return null;
|
|
929
912
|
}
|
|
930
913
|
if (!doc?.data) {
|
|
931
|
-
console.error(`Document "${docId}"
|
|
914
|
+
console.error(`Document "${docId}" ` +
|
|
915
|
+
`does not contain file data`);
|
|
932
916
|
return null;
|
|
933
917
|
}
|
|
918
|
+
const fileName = doc.name ||
|
|
919
|
+
doc.path ||
|
|
920
|
+
docId;
|
|
934
921
|
if (doc.type === 'media' ||
|
|
935
922
|
doc.type === 'base64file' ||
|
|
936
|
-
this.isBinaryMediaFile(
|
|
937
|
-
const fileName = doc.name || doc.path || docId;
|
|
923
|
+
this.isBinaryMediaFile(fileName, doc.contentType)) {
|
|
938
924
|
const mimeType = this.getMimeType(fileName, doc.contentType);
|
|
939
925
|
return {
|
|
940
|
-
type: doc.type ||
|
|
926
|
+
type: doc.type ||
|
|
927
|
+
'base64file',
|
|
941
928
|
mimeType,
|
|
942
929
|
base64: doc.data,
|
|
943
|
-
src: `data:${mimeType};base64
|
|
930
|
+
src: `data:${mimeType};base64,` +
|
|
931
|
+
`${doc.data}`,
|
|
944
932
|
name: doc.name,
|
|
945
933
|
path: doc.path,
|
|
946
934
|
metadata: doc.metadata || {},
|
|
947
935
|
};
|
|
948
936
|
}
|
|
949
937
|
const decodedContent = this.fromBase64(doc.data);
|
|
950
|
-
if (this.isJsonFile(
|
|
938
|
+
if (this.isJsonFile(fileName, doc.contentType)) {
|
|
951
939
|
try {
|
|
952
940
|
return JSON.parse(decodedContent);
|
|
953
941
|
}
|
|
954
942
|
catch (jsonError) {
|
|
955
|
-
console.error(`Failed to parse JSON
|
|
943
|
+
console.error(`Failed to parse JSON ` +
|
|
944
|
+
`for file "${docId}"`, jsonError);
|
|
956
945
|
return decodedContent;
|
|
957
946
|
}
|
|
958
947
|
}
|
|
959
948
|
return decodedContent;
|
|
960
949
|
}
|
|
961
950
|
catch (error) {
|
|
962
|
-
console.error(`Unexpected error while fetching
|
|
963
|
-
`
|
|
951
|
+
console.error(`Unexpected error while fetching ` +
|
|
952
|
+
`file "${filePath}" from ` +
|
|
953
|
+
`"${couchDbName}"`, error);
|
|
964
954
|
return null;
|
|
965
955
|
}
|
|
966
956
|
}
|
|
@@ -973,35 +963,119 @@ class Couchdb {
|
|
|
973
963
|
if (!docId) {
|
|
974
964
|
throw new Error('Missing file path');
|
|
975
965
|
}
|
|
976
|
-
const
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
`${response.status} ${this.stringifyErrorDetails(doc)}`);
|
|
966
|
+
const path = this.getDocumentPath(dbName, docId);
|
|
967
|
+
const result = await this.sendGetRequest(path, await this.getReadHeaders());
|
|
968
|
+
const doc = result.data;
|
|
969
|
+
if (!result.ok) {
|
|
970
|
+
throw new Error(`Failed to fetch image ` +
|
|
971
|
+
`"${docId}" from "${dbName}": ` +
|
|
972
|
+
`${result.status} ` +
|
|
973
|
+
`${this.stringifyErrorDetails(doc)}`);
|
|
985
974
|
}
|
|
986
975
|
if (!doc?.data) {
|
|
987
|
-
throw new Error(`Document "${docId}"
|
|
976
|
+
throw new Error(`Document "${docId}" ` +
|
|
977
|
+
`does not contain image data`);
|
|
978
|
+
}
|
|
979
|
+
const mimeType = this.getMimeType(doc.name ||
|
|
980
|
+
doc.path ||
|
|
981
|
+
docId, doc.contentType);
|
|
982
|
+
return (`data:${mimeType};base64,` +
|
|
983
|
+
`${doc.data}`);
|
|
984
|
+
}
|
|
985
|
+
async sendGetRequest(path, headers) {
|
|
986
|
+
const normalizedPath = path.startsWith('/')
|
|
987
|
+
? path
|
|
988
|
+
: `/${path}`;
|
|
989
|
+
if (Capacitor.isNativePlatform()) {
|
|
990
|
+
return this.sendNativeGetRequest(normalizedPath, headers);
|
|
988
991
|
}
|
|
989
|
-
|
|
990
|
-
return `data:${mimeType};base64,${doc.data}`;
|
|
992
|
+
return this.sendWebGetRequest(normalizedPath, headers);
|
|
991
993
|
}
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
994
|
+
async sendNativeGetRequest(path, headers) {
|
|
995
|
+
const url = `${this.remoteCouchDbBaseUrl}${path}`;
|
|
996
|
+
console.log('[CouchDB][Native] GET:', url);
|
|
997
|
+
try {
|
|
998
|
+
const response = await CapacitorHttp.get({
|
|
999
|
+
url,
|
|
1000
|
+
headers,
|
|
1001
|
+
connectTimeout: 15000,
|
|
1002
|
+
readTimeout: 30000,
|
|
1003
|
+
});
|
|
1004
|
+
const data = this.normalizeNativeResponseData(response.data);
|
|
1005
|
+
return {
|
|
1006
|
+
ok: response.status >= 200 &&
|
|
1007
|
+
response.status < 300,
|
|
1008
|
+
status: response.status,
|
|
1009
|
+
data: data,
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
1012
|
+
catch (error) {
|
|
1013
|
+
console.error('[CouchDB][Native] Request failed:', error);
|
|
1014
|
+
return {
|
|
1015
|
+
ok: false,
|
|
1016
|
+
status: this.extractNativeErrorStatus(error),
|
|
1017
|
+
data: this.extractNativeErrorData(error),
|
|
1018
|
+
};
|
|
1019
|
+
}
|
|
995
1020
|
}
|
|
996
|
-
|
|
997
|
-
const
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1021
|
+
async sendWebGetRequest(path, headers) {
|
|
1022
|
+
const url = `${this.webProxyBaseUrl}${path}`;
|
|
1023
|
+
console.log('[CouchDB][Web] GET:', url);
|
|
1024
|
+
try {
|
|
1025
|
+
const response = await fetch(url, {
|
|
1026
|
+
method: 'GET',
|
|
1027
|
+
cache: 'no-store',
|
|
1028
|
+
headers,
|
|
1029
|
+
});
|
|
1030
|
+
const data = await this.parseJsonOrText(response);
|
|
1031
|
+
return {
|
|
1032
|
+
ok: response.ok,
|
|
1033
|
+
status: response.status,
|
|
1034
|
+
data: data,
|
|
1035
|
+
};
|
|
1036
|
+
}
|
|
1037
|
+
catch (error) {
|
|
1038
|
+
console.error('[CouchDB][Web] Request failed:', error);
|
|
1039
|
+
return {
|
|
1040
|
+
ok: false,
|
|
1041
|
+
status: 0,
|
|
1042
|
+
data: error,
|
|
1043
|
+
};
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
normalizeNativeResponseData(data) {
|
|
1047
|
+
if (typeof data !== 'string') {
|
|
1048
|
+
return data;
|
|
1049
|
+
}
|
|
1050
|
+
if (!data.trim()) {
|
|
1051
|
+
return null;
|
|
1002
1052
|
}
|
|
1003
|
-
|
|
1004
|
-
|
|
1053
|
+
try {
|
|
1054
|
+
return JSON.parse(data);
|
|
1055
|
+
}
|
|
1056
|
+
catch {
|
|
1057
|
+
return data;
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
extractNativeErrorStatus(error) {
|
|
1061
|
+
const status = error?.status ??
|
|
1062
|
+
error?.response?.status ??
|
|
1063
|
+
error?.data?.status;
|
|
1064
|
+
return Number.isFinite(Number(status))
|
|
1065
|
+
? Number(status)
|
|
1066
|
+
: 0;
|
|
1067
|
+
}
|
|
1068
|
+
extractNativeErrorData(error) {
|
|
1069
|
+
return (error?.data ??
|
|
1070
|
+
error?.response?.data ??
|
|
1071
|
+
error?.message ??
|
|
1072
|
+
error);
|
|
1073
|
+
}
|
|
1074
|
+
getDocumentPath(dbName, docId) {
|
|
1075
|
+
const encodedDbName = encodeURIComponent(dbName.trim().toLowerCase());
|
|
1076
|
+
const encodedDocId = encodeURIComponent(docId.trim());
|
|
1077
|
+
return (`/${encodedDbName}/` +
|
|
1078
|
+
`${encodedDocId}`);
|
|
1005
1079
|
}
|
|
1006
1080
|
async getReadHeaders() {
|
|
1007
1081
|
return {
|
|
@@ -1012,7 +1086,8 @@ class Couchdb {
|
|
|
1012
1086
|
async buildTenantHeaders() {
|
|
1013
1087
|
const tenantKey = await this.utilityService.getStorage('tenantKey');
|
|
1014
1088
|
const tenantName = await this.utilityService.getStorage('tenantName');
|
|
1015
|
-
const selectedTenant = await this.utilityService
|
|
1089
|
+
const selectedTenant = await this.utilityService
|
|
1090
|
+
.getLocalStorage('selectedTenant');
|
|
1016
1091
|
if (typeof tenantKey === 'string' &&
|
|
1017
1092
|
tenantKey.trim()) {
|
|
1018
1093
|
return {
|
|
@@ -1028,7 +1103,8 @@ class Couchdb {
|
|
|
1028
1103
|
const tenant = normalizedTenantName ||
|
|
1029
1104
|
normalizedSelectedTenant;
|
|
1030
1105
|
if (!tenant) {
|
|
1031
|
-
throw new Error('Missing tenantKey, tenantName,
|
|
1106
|
+
throw new Error('Missing tenantKey, tenantName, ' +
|
|
1107
|
+
'or selectedTenant');
|
|
1032
1108
|
}
|
|
1033
1109
|
return {
|
|
1034
1110
|
'X-Tenant': tenant,
|
|
@@ -1069,12 +1145,20 @@ class Couchdb {
|
|
|
1069
1145
|
}
|
|
1070
1146
|
isJsonFile(fileNameOrPath, contentType) {
|
|
1071
1147
|
const lower = (fileNameOrPath || '').toLowerCase();
|
|
1148
|
+
const normalizedContentType = (contentType || '')
|
|
1149
|
+
.toLowerCase()
|
|
1150
|
+
.split(';')[0]
|
|
1151
|
+
.trim();
|
|
1072
1152
|
return (lower.endsWith('.json') ||
|
|
1073
|
-
|
|
1153
|
+
normalizedContentType ===
|
|
1154
|
+
'application/json');
|
|
1074
1155
|
}
|
|
1075
1156
|
isBinaryMediaFile(fileNameOrPath, contentType) {
|
|
1076
1157
|
const lower = (fileNameOrPath || '').toLowerCase();
|
|
1077
|
-
const type = (contentType || '')
|
|
1158
|
+
const type = (contentType || '')
|
|
1159
|
+
.toLowerCase()
|
|
1160
|
+
.split(';')[0]
|
|
1161
|
+
.trim();
|
|
1078
1162
|
return (type.startsWith('image/') ||
|
|
1079
1163
|
type.startsWith('video/') ||
|
|
1080
1164
|
type.startsWith('audio/') ||
|
|
@@ -1094,8 +1178,11 @@ class Couchdb {
|
|
|
1094
1178
|
lower.endsWith('.pdf'));
|
|
1095
1179
|
}
|
|
1096
1180
|
getMimeType(fileNameOrPath, contentType) {
|
|
1097
|
-
if (contentType
|
|
1098
|
-
|
|
1181
|
+
if (contentType &&
|
|
1182
|
+
contentType.trim()) {
|
|
1183
|
+
return contentType
|
|
1184
|
+
.split(';')[0]
|
|
1185
|
+
.trim();
|
|
1099
1186
|
}
|
|
1100
1187
|
const lower = (fileNameOrPath || '').toLowerCase();
|
|
1101
1188
|
if (lower.endsWith('.png')) {
|