@yomotech/view-toolkit 0.0.52 → 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,40 +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
|
-
|
|
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;
|
|
898
884
|
}
|
|
899
|
-
if (
|
|
900
|
-
|
|
885
|
+
if (result.data &&
|
|
886
|
+
Array.isArray(result.data.databases)) {
|
|
887
|
+
return result.data.databases;
|
|
901
888
|
}
|
|
902
|
-
|
|
889
|
+
throw new Error(`Invalid /tenant-dbs response: ` +
|
|
890
|
+
`${this.stringifyErrorDetails(result.data)}`);
|
|
903
891
|
}
|
|
904
892
|
async getFileContentFromCouchDb(couchDbName, filePath) {
|
|
905
893
|
try {
|
|
@@ -913,51 +901,56 @@ class Couchdb {
|
|
|
913
901
|
console.error('Missing file path');
|
|
914
902
|
return null;
|
|
915
903
|
}
|
|
916
|
-
const
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
console.error(`Failed to fetch file "${docId}" from "${dbName}": ` +
|
|
924
|
-
`${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);
|
|
925
911
|
return null;
|
|
926
912
|
}
|
|
927
913
|
if (!doc?.data) {
|
|
928
|
-
console.error(`Document "${docId}"
|
|
914
|
+
console.error(`Document "${docId}" ` +
|
|
915
|
+
`does not contain file data`);
|
|
929
916
|
return null;
|
|
930
917
|
}
|
|
918
|
+
const fileName = doc.name ||
|
|
919
|
+
doc.path ||
|
|
920
|
+
docId;
|
|
931
921
|
if (doc.type === 'media' ||
|
|
932
922
|
doc.type === 'base64file' ||
|
|
933
|
-
this.isBinaryMediaFile(
|
|
934
|
-
const fileName = doc.name || doc.path || docId;
|
|
923
|
+
this.isBinaryMediaFile(fileName, doc.contentType)) {
|
|
935
924
|
const mimeType = this.getMimeType(fileName, doc.contentType);
|
|
936
925
|
return {
|
|
937
|
-
type: doc.type ||
|
|
926
|
+
type: doc.type ||
|
|
927
|
+
'base64file',
|
|
938
928
|
mimeType,
|
|
939
929
|
base64: doc.data,
|
|
940
|
-
src: `data:${mimeType};base64
|
|
930
|
+
src: `data:${mimeType};base64,` +
|
|
931
|
+
`${doc.data}`,
|
|
941
932
|
name: doc.name,
|
|
942
933
|
path: doc.path,
|
|
943
934
|
metadata: doc.metadata || {},
|
|
944
935
|
};
|
|
945
936
|
}
|
|
946
937
|
const decodedContent = this.fromBase64(doc.data);
|
|
947
|
-
if (this.isJsonFile(
|
|
938
|
+
if (this.isJsonFile(fileName, doc.contentType)) {
|
|
948
939
|
try {
|
|
949
940
|
return JSON.parse(decodedContent);
|
|
950
941
|
}
|
|
951
942
|
catch (jsonError) {
|
|
952
|
-
console.error(`Failed to parse JSON
|
|
943
|
+
console.error(`Failed to parse JSON ` +
|
|
944
|
+
`for file "${docId}"`, jsonError);
|
|
953
945
|
return decodedContent;
|
|
954
946
|
}
|
|
955
947
|
}
|
|
956
948
|
return decodedContent;
|
|
957
949
|
}
|
|
958
950
|
catch (error) {
|
|
959
|
-
console.error(`Unexpected error while fetching
|
|
960
|
-
`
|
|
951
|
+
console.error(`Unexpected error while fetching ` +
|
|
952
|
+
`file "${filePath}" from ` +
|
|
953
|
+
`"${couchDbName}"`, error);
|
|
961
954
|
return null;
|
|
962
955
|
}
|
|
963
956
|
}
|
|
@@ -970,35 +963,119 @@ class Couchdb {
|
|
|
970
963
|
if (!docId) {
|
|
971
964
|
throw new Error('Missing file path');
|
|
972
965
|
}
|
|
973
|
-
const
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
`${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)}`);
|
|
982
974
|
}
|
|
983
975
|
if (!doc?.data) {
|
|
984
|
-
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);
|
|
985
991
|
}
|
|
986
|
-
|
|
987
|
-
return `data:${mimeType};base64,${doc.data}`;
|
|
992
|
+
return this.sendWebGetRequest(normalizedPath, headers);
|
|
988
993
|
}
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
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
|
+
}
|
|
992
1020
|
}
|
|
993
|
-
|
|
994
|
-
const
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
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;
|
|
999
1049
|
}
|
|
1000
|
-
|
|
1001
|
-
|
|
1050
|
+
if (!data.trim()) {
|
|
1051
|
+
return null;
|
|
1052
|
+
}
|
|
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}`);
|
|
1002
1079
|
}
|
|
1003
1080
|
async getReadHeaders() {
|
|
1004
1081
|
return {
|
|
@@ -1009,7 +1086,8 @@ class Couchdb {
|
|
|
1009
1086
|
async buildTenantHeaders() {
|
|
1010
1087
|
const tenantKey = await this.utilityService.getStorage('tenantKey');
|
|
1011
1088
|
const tenantName = await this.utilityService.getStorage('tenantName');
|
|
1012
|
-
const selectedTenant = await this.utilityService
|
|
1089
|
+
const selectedTenant = await this.utilityService
|
|
1090
|
+
.getLocalStorage('selectedTenant');
|
|
1013
1091
|
if (typeof tenantKey === 'string' &&
|
|
1014
1092
|
tenantKey.trim()) {
|
|
1015
1093
|
return {
|
|
@@ -1025,7 +1103,8 @@ class Couchdb {
|
|
|
1025
1103
|
const tenant = normalizedTenantName ||
|
|
1026
1104
|
normalizedSelectedTenant;
|
|
1027
1105
|
if (!tenant) {
|
|
1028
|
-
throw new Error('Missing tenantKey, tenantName,
|
|
1106
|
+
throw new Error('Missing tenantKey, tenantName, ' +
|
|
1107
|
+
'or selectedTenant');
|
|
1029
1108
|
}
|
|
1030
1109
|
return {
|
|
1031
1110
|
'X-Tenant': tenant,
|
|
@@ -1066,12 +1145,20 @@ class Couchdb {
|
|
|
1066
1145
|
}
|
|
1067
1146
|
isJsonFile(fileNameOrPath, contentType) {
|
|
1068
1147
|
const lower = (fileNameOrPath || '').toLowerCase();
|
|
1148
|
+
const normalizedContentType = (contentType || '')
|
|
1149
|
+
.toLowerCase()
|
|
1150
|
+
.split(';')[0]
|
|
1151
|
+
.trim();
|
|
1069
1152
|
return (lower.endsWith('.json') ||
|
|
1070
|
-
|
|
1153
|
+
normalizedContentType ===
|
|
1154
|
+
'application/json');
|
|
1071
1155
|
}
|
|
1072
1156
|
isBinaryMediaFile(fileNameOrPath, contentType) {
|
|
1073
1157
|
const lower = (fileNameOrPath || '').toLowerCase();
|
|
1074
|
-
const type = (contentType || '')
|
|
1158
|
+
const type = (contentType || '')
|
|
1159
|
+
.toLowerCase()
|
|
1160
|
+
.split(';')[0]
|
|
1161
|
+
.trim();
|
|
1075
1162
|
return (type.startsWith('image/') ||
|
|
1076
1163
|
type.startsWith('video/') ||
|
|
1077
1164
|
type.startsWith('audio/') ||
|
|
@@ -1091,8 +1178,11 @@ class Couchdb {
|
|
|
1091
1178
|
lower.endsWith('.pdf'));
|
|
1092
1179
|
}
|
|
1093
1180
|
getMimeType(fileNameOrPath, contentType) {
|
|
1094
|
-
if (contentType
|
|
1095
|
-
|
|
1181
|
+
if (contentType &&
|
|
1182
|
+
contentType.trim()) {
|
|
1183
|
+
return contentType
|
|
1184
|
+
.split(';')[0]
|
|
1185
|
+
.trim();
|
|
1096
1186
|
}
|
|
1097
1187
|
const lower = (fileNameOrPath || '').toLowerCase();
|
|
1098
1188
|
if (lower.endsWith('.png')) {
|