@onlineapps/conn-base-storage 1.0.1 → 1.0.2
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.
- package/package.json +1 -1
- package/src/index.js +137 -0
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -12,8 +12,78 @@
|
|
|
12
12
|
const Minio = require('minio');
|
|
13
13
|
const crypto = require('crypto');
|
|
14
14
|
const winston = require('winston');
|
|
15
|
+
const dns = require('dns').promises;
|
|
15
16
|
const SharedUrlAdapter = require('./sharedUrlAdapter');
|
|
16
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Check if string is already an IP address
|
|
20
|
+
* @param {string} str - String to check
|
|
21
|
+
* @returns {boolean} True if string is an IP address
|
|
22
|
+
*/
|
|
23
|
+
function isIP(str) {
|
|
24
|
+
// IPv4 pattern
|
|
25
|
+
const ipv4 = /^(\d{1,3}\.){3}\d{1,3}$/;
|
|
26
|
+
// IPv6 pattern (simplified)
|
|
27
|
+
const ipv6 = /^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/;
|
|
28
|
+
return ipv4.test(str) || ipv6.test(str);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Resolve hostname to IP address
|
|
33
|
+
* MinIO client sometimes has issues with hostnames, this ensures we use IP
|
|
34
|
+
* @param {string} host - Hostname or IP
|
|
35
|
+
* @returns {Promise<string>} Resolved IP address
|
|
36
|
+
*/
|
|
37
|
+
async function resolveHostToIP(host) {
|
|
38
|
+
// If already IP, return as-is
|
|
39
|
+
if (isIP(host)) {
|
|
40
|
+
return host;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// If localhost, return as-is
|
|
44
|
+
if (host === 'localhost' || host === '127.0.0.1') {
|
|
45
|
+
return host;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const { address } = await dns.lookup(host);
|
|
50
|
+
return address;
|
|
51
|
+
} catch (err) {
|
|
52
|
+
// If DNS lookup fails, return original hostname
|
|
53
|
+
console.warn(`[StorageConnector] DNS lookup failed for ${host}, using original: ${err.message}`);
|
|
54
|
+
return host;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Parse storage reference to bucket and path
|
|
60
|
+
* Supports formats:
|
|
61
|
+
* - minio://bucket/path
|
|
62
|
+
* - internal://storage/bucket/path
|
|
63
|
+
*
|
|
64
|
+
* @param {string} ref - Storage reference
|
|
65
|
+
* @returns {{ bucket: string, path: string, fingerprint?: string }}
|
|
66
|
+
*/
|
|
67
|
+
function parseStorageRef(ref) {
|
|
68
|
+
if (!ref || typeof ref !== 'string') {
|
|
69
|
+
throw new Error('Invalid storage reference: must be a string');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// minio://bucket/path
|
|
73
|
+
let match = ref.match(/^minio:\/\/([^/]+)\/(.+)$/);
|
|
74
|
+
if (match) {
|
|
75
|
+
return { bucket: match[1], path: match[2] };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// internal://storage/bucket/path
|
|
79
|
+
match = ref.match(/^internal:\/\/storage\/([^/]+)\/(.+)$/);
|
|
80
|
+
if (match) {
|
|
81
|
+
return { bucket: match[1], path: match[2] };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
throw new Error(`Invalid storage reference format: ${ref}`);
|
|
85
|
+
}
|
|
86
|
+
|
|
17
87
|
/**
|
|
18
88
|
* Storage connector for MinIO with automatic fingerprinting
|
|
19
89
|
*
|
|
@@ -141,6 +211,30 @@ class StorageConnector {
|
|
|
141
211
|
*/
|
|
142
212
|
async initialize() {
|
|
143
213
|
try {
|
|
214
|
+
// Resolve hostname to IP (MinIO client can have issues with Docker hostnames)
|
|
215
|
+
const originalEndpoint = this.config.endPoint;
|
|
216
|
+
const resolvedEndpoint = await resolveHostToIP(originalEndpoint);
|
|
217
|
+
|
|
218
|
+
if (resolvedEndpoint !== originalEndpoint) {
|
|
219
|
+
this.logger.info('StorageConnector: Resolved hostname to IP', {
|
|
220
|
+
original: originalEndpoint,
|
|
221
|
+
resolved: resolvedEndpoint
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// Recreate MinIO client with resolved IP
|
|
225
|
+
const minioConfig = {
|
|
226
|
+
endPoint: resolvedEndpoint,
|
|
227
|
+
port: this.config.port,
|
|
228
|
+
useSSL: this.config.useSSL || false,
|
|
229
|
+
accessKey: this.config.accessKey,
|
|
230
|
+
secretKey: this.config.secretKey
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
this.client = new Minio.Client(minioConfig);
|
|
234
|
+
this.minioClient = this.client;
|
|
235
|
+
this.config.endPoint = resolvedEndpoint;
|
|
236
|
+
}
|
|
237
|
+
|
|
144
238
|
// Check if default bucket exists
|
|
145
239
|
const bucketExists = await this.client.bucketExists(this.defaultBucket);
|
|
146
240
|
|
|
@@ -827,6 +921,49 @@ class StorageConnector {
|
|
|
827
921
|
* @property {string} [uploadedAt] - Upload timestamp
|
|
828
922
|
*/
|
|
829
923
|
|
|
924
|
+
/**
|
|
925
|
+
* Download file from storage reference
|
|
926
|
+
*
|
|
927
|
+
* @param {string} ref - Storage reference (minio://bucket/path)
|
|
928
|
+
* @returns {Promise<Buffer>} File content
|
|
929
|
+
*
|
|
930
|
+
* @example
|
|
931
|
+
* const buffer = await storage.downloadFromRef('minio://workflow/inputs/wf-123/doc.pdf');
|
|
932
|
+
*/
|
|
933
|
+
StorageConnector.prototype.downloadFromRef = async function(ref) {
|
|
934
|
+
const { bucket, path } = parseStorageRef(ref);
|
|
935
|
+
|
|
936
|
+
// Download from MinIO
|
|
937
|
+
const stream = await this.client.getObject(bucket, path);
|
|
938
|
+
|
|
939
|
+
// Collect stream data
|
|
940
|
+
const chunks = [];
|
|
941
|
+
for await (const chunk of stream) {
|
|
942
|
+
chunks.push(chunk);
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
return Buffer.concat(chunks);
|
|
946
|
+
};
|
|
947
|
+
|
|
948
|
+
/**
|
|
949
|
+
* Check if storage reference is valid format
|
|
950
|
+
* @param {string} ref - Reference to check
|
|
951
|
+
* @returns {boolean}
|
|
952
|
+
*/
|
|
953
|
+
StorageConnector.isValidRef = function(ref) {
|
|
954
|
+
try {
|
|
955
|
+
parseStorageRef(ref);
|
|
956
|
+
return true;
|
|
957
|
+
} catch {
|
|
958
|
+
return false;
|
|
959
|
+
}
|
|
960
|
+
};
|
|
961
|
+
|
|
962
|
+
/**
|
|
963
|
+
* Parse storage reference (static helper)
|
|
964
|
+
*/
|
|
965
|
+
StorageConnector.parseRef = parseStorageRef;
|
|
966
|
+
|
|
830
967
|
// Export main class
|
|
831
968
|
module.exports = StorageConnector;
|
|
832
969
|
|