@e-mc/cloud 0.8.10 → 0.9.1
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/LICENSE +7 -3
- package/README.md +125 -18
- package/index.js +170 -114
- package/package.json +5 -5
- package/types/index.d.ts +20 -16
- package/util.d.ts +3 -7
- package/util.js +76 -24
package/LICENSE
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
Copyright 2024 An Pham
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
|
8
|
+
|
|
9
|
+
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
|
10
|
+
|
|
11
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @e-mc/cloud
|
|
2
2
|
|
|
3
|
-
* NodeJS
|
|
4
|
-
*
|
|
3
|
+
* NodeJS 16
|
|
4
|
+
* ES2020
|
|
5
5
|
|
|
6
6
|
## General Usage
|
|
7
7
|
|
|
@@ -9,28 +9,29 @@
|
|
|
9
9
|
|
|
10
10
|
## Interface
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
* [View Source](https://www.unpkg.com/@e-mc/types@0.9.1/lib/index.d.ts)
|
|
13
13
|
|
|
14
14
|
```typescript
|
|
15
15
|
import type { IHost, IScopeOrigin } from "./index";
|
|
16
16
|
import type { ExternalAsset } from "./asset";
|
|
17
|
-
import type { BucketWebsiteOptions, CloudDatabase, CloudFeatures, CloudFunctions, CloudService, CloudStorage, CloudStorageDownload, CloudStorageUpload } from "./cloud";
|
|
17
|
+
import type { BucketWebsiteOptions, CloudDatabase, CloudFeatures, CloudFunctions, CloudService, CloudStorage, CloudStorageDownload, CloudStorageUpload, UploadAssetOptions } from "./cloud";
|
|
18
18
|
import type { ClientDbConstructor, IClientDb } from "./core";
|
|
19
19
|
import type { BatchQueryResult, QueryResult } from "./db";
|
|
20
20
|
import type { LogMessageOptions } from "./logger";
|
|
21
|
-
import type { CloudModule, CloudServiceOptions, CloudSettings, DbCoerceSettings } from "./settings";
|
|
21
|
+
import type { CloudAuthSettings, CloudModule, CloudServiceOptions, CloudSettings, DbCoerceSettings } from "./settings";
|
|
22
22
|
|
|
23
|
-
interface ICloud extends IClientDb<IHost, CloudModule, CloudDatabase, CloudServiceOptions, DbCoerceSettings> {
|
|
23
|
+
interface ICloud extends IClientDb<IHost, CloudModule, CloudDatabase, CloudServiceOptions, DbCoerceSettings & CloudAuthSettings> {
|
|
24
24
|
module: CloudModule;
|
|
25
25
|
readonly uploaded: string[];
|
|
26
26
|
readonly downloaded: string[];
|
|
27
27
|
createBucket(service: string, credential: unknown, bucket: string, acl?: unknown, options?: unknown): Promise<boolean>;
|
|
28
28
|
createBucket(service: string, credential: unknown, bucket: string, publicRead?: boolean): Promise<boolean>;
|
|
29
29
|
setBucketPolicy(service: string, credential: unknown, bucket: string, options: unknown): Promise<boolean>;
|
|
30
|
+
setBucketTagging(service: string, credential: unknown, bucket: string, options: unknown): Promise<boolean>;
|
|
30
31
|
setBucketWebsite(service: string, credential: unknown, bucket: string, options: BucketWebsiteOptions): Promise<boolean>;
|
|
31
32
|
deleteObjects(service: string, credential: unknown, bucket: string, recursive?: boolean): Promise<void>;
|
|
32
|
-
uploadObject(service: string, credential: unknown, bucket: string, upload: CloudStorageUpload, localUri: string, beforeResolve?: (value: string) => Promise<void> | void): Promise<string>;
|
|
33
|
-
downloadObject(service: string, credential: unknown, bucket: string, download: CloudStorageDownload, beforeResolve?: (value: Buffer | string | null) => Promise<string | undefined> | void): Promise<Buffer | string>;
|
|
33
|
+
uploadObject(service: string, credential: unknown, bucket: string, upload: CloudStorageUpload, localUri: string, beforeResolve?: ((value: string) => Promise<void> | void)): Promise<string>;
|
|
34
|
+
downloadObject(service: string, credential: unknown, bucket: string, download: CloudStorageDownload, beforeResolve?: ((value: Buffer | string | null) => Promise<string | undefined> | void)): Promise<Buffer | string>;
|
|
34
35
|
getStorage(action: CloudFunctions, data: CloudStorage[] | undefined): CloudStorage | undefined;
|
|
35
36
|
hasStorage(action: CloudFunctions, storage: CloudStorage): CloudStorageUpload | false;
|
|
36
37
|
getDatabaseRows(item: CloudDatabase, ignoreErrors: boolean, sessionKey?: string): Promise<QueryResult>;
|
|
@@ -40,8 +41,8 @@ interface ICloud extends IClientDb<IHost, CloudModule, CloudDatabase, CloudServi
|
|
|
40
41
|
hasCredential(feature: CloudFeatures, data: CloudService, credential?: unknown): boolean;
|
|
41
42
|
getCredential(item: CloudService, unused?: boolean): Record<string | number | symbol, unknown>;
|
|
42
43
|
getSettings(service: string): Record<string, unknown> | undefined;
|
|
43
|
-
settingsOf(service: string, name: "cache"): unknown;
|
|
44
44
|
settingsOf(service: string, name: "coerce", component: keyof DbCoerceSettings): unknown;
|
|
45
|
+
settingsOf(service: string, name: "auth", component: keyof CloudAuthSettings): unknown;
|
|
45
46
|
getUploadHandler(service: string, credential: unknown): (...args: unknown[]) => void;
|
|
46
47
|
getDownloadHandler(service: string, credential: unknown): (...args: unknown[]) => void;
|
|
47
48
|
resolveService(service: string, folder?: string): string;
|
|
@@ -57,23 +58,129 @@ interface CloudConstructor extends ClientDbConstructor<IHost> {
|
|
|
57
58
|
LOG_CLOUD_DELETE: LogMessageOptions;
|
|
58
59
|
LOG_CLOUD_DELAYED: LogMessageOptions;
|
|
59
60
|
finalize(this: IHost, instance: ICloud): Promise<unknown>;
|
|
60
|
-
uploadAsset(state: IScopeOrigin<IFileManager, ICloud
|
|
61
|
-
uploadAsset(state: IScopeOrigin<IFileManager, ICloud
|
|
61
|
+
uploadAsset(state: IScopeOrigin<IFileManager, ICloud>, file: ExternalAsset, options: UploadAssetOptions): Promise<unknown>[];
|
|
62
|
+
uploadAsset(state: IScopeOrigin<IFileManager, ICloud>, file: ExternalAsset, ignoreProcess: boolean): Promise<unknown>[];
|
|
63
|
+
uploadAsset(state: IScopeOrigin<IFileManager, ICloud>, file: ExternalAsset, contentType?: string, ignoreProcess?: boolean): Promise<unknown>[];
|
|
62
64
|
sanitizeAssets(assets: ExternalAsset[]): ExternalAsset[];
|
|
63
65
|
readonly prototype: ICloud;
|
|
64
66
|
new(module?: CloudModule, database?: CloudDatabase[], ...args: unknown[]): ICloud;
|
|
65
67
|
}
|
|
68
|
+
|
|
69
|
+
interface ICloudServiceClient {
|
|
70
|
+
CLOUD_SERVICE_NAME: string;
|
|
71
|
+
CLOUD_UPLOAD_DISK?: boolean;
|
|
72
|
+
CLOUD_UPLOAD_STREAM?: boolean;
|
|
73
|
+
CLOUD_UPLOAD_CHUNK?: boolean;
|
|
74
|
+
CLOUD_DOWNLOAD_CHUNK?: boolean;
|
|
75
|
+
validateStorage?(credential: unknown, data?: CloudService): boolean;
|
|
76
|
+
validateDatabase?(credential: unknown, data?: CloudService): boolean;
|
|
77
|
+
createStorageClient?(this: IModule, credential: unknown, service?: string): unknown;
|
|
78
|
+
createDatabaseClient?(this: IModule, credential: unknown, data?: CloudService): unknown;
|
|
79
|
+
createBucket?(this: IModule, credential: unknown, bucket: string, publicRead?: boolean, service?: string, sdk?: string): Promise<boolean>;
|
|
80
|
+
createBucketV2?(this: IModule, credential: unknown, bucket: string, acl?: unknown, options?: unknown, service?: string, sdk?: string): Promise<boolean>;
|
|
81
|
+
setBucketPolicy?(this: IModule, credential: unknown, bucket: string, options: unknown, service?: string, sdk?: string): Promise<boolean>;
|
|
82
|
+
setBucketTagging?(this: IModule, credential: unknown, bucket: string, options: unknown, service?: string, sdk?: string): Promise<boolean>;
|
|
83
|
+
setBucketWebsite?(this: IModule, credential: unknown, bucket: string, options: BucketWebsiteOptions, service?: string, sdk?: string): Promise<boolean>;
|
|
84
|
+
deleteObjects?(this: IModule, credential: unknown, bucket: string, service?: string, sdk?: string, recursive?: boolean): Promise<void>;
|
|
85
|
+
deleteObjectsV2?(this: IModule, credential: unknown, bucket: string, recursive?: boolean, service?: string, sdk?: string): Promise<void>;
|
|
86
|
+
executeQuery?(this: ICloud, credential: unknown, data: CloudDatabase, sessionKey?: string): Promise<QueryResult>;
|
|
87
|
+
executeBatchQuery?(this: ICloud, credential: unknown, batch: CloudDatabase[], sessionKey?: string): Promise<BatchQueryResult>;
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Settings
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
import type { PermittedDirectories } from "./core";
|
|
95
|
+
import type { CloudServiceOptions, DbSourceOptions, PurgeComponent } from "./settings";
|
|
96
|
+
|
|
97
|
+
interface CloudModule {
|
|
98
|
+
// handler: "@e-mc/cloud";
|
|
99
|
+
extensions?: string[];
|
|
100
|
+
atlas?: CloudStoredCredentials;
|
|
101
|
+
aws?: CloudStoredCredentials;
|
|
102
|
+
"aws-v3"?: CloudStoredCredentials;
|
|
103
|
+
azure?: CloudStoredCredentials; // az
|
|
104
|
+
gcp?: CloudStoredCredentials; // gcloud
|
|
105
|
+
ibm?: CloudStoredCredentials;
|
|
106
|
+
oci?: CloudStoredCredentials;
|
|
107
|
+
minio?: CloudStoredCredentials;
|
|
108
|
+
settings?: {
|
|
109
|
+
broadcast_id?: string | string[];
|
|
110
|
+
users?: Record<string, Record<string, unknown>>;
|
|
111
|
+
cache_dir?: string;
|
|
112
|
+
session_expires?: number;
|
|
113
|
+
user_key?: Record<string, DbSourceOptions>;
|
|
114
|
+
imports?: StringMap;
|
|
115
|
+
purge?: PurgeComponent;
|
|
116
|
+
atlas?: CloudServiceOptions;
|
|
117
|
+
aws?: CloudServiceOptions;
|
|
118
|
+
"aws-v3"?: CloudServiceOptions;
|
|
119
|
+
azure?: CloudServiceOptions;
|
|
120
|
+
gcp?: CloudServiceOptions;
|
|
121
|
+
ibm?: CloudServiceOptions;
|
|
122
|
+
oci?: CloudServiceOptions;
|
|
123
|
+
minio?: CloudServiceOptions;
|
|
124
|
+
};
|
|
125
|
+
permission?: PermittedDirectories;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
type CloudStoredCredentials = Record<string, Record<string, unknown>>;
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Example usage
|
|
132
|
+
|
|
133
|
+
```javascript
|
|
134
|
+
const Cloud = require("@e-mc/cloud"); // Using @pi-r/aws
|
|
135
|
+
|
|
136
|
+
const instance = new Cloud({
|
|
137
|
+
aws: {
|
|
138
|
+
main: {
|
|
139
|
+
accessKeyId: "**********",
|
|
140
|
+
secretAccessKey: "**********"
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
"aws-v3": {
|
|
144
|
+
main: {
|
|
145
|
+
credentials: {
|
|
146
|
+
accessKeyId: "**********",
|
|
147
|
+
secretAccessKey: "**********",
|
|
148
|
+
region: "ap-northeast-1"
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
// instance.host = new Host();
|
|
154
|
+
instance.init();
|
|
155
|
+
|
|
156
|
+
const options = {
|
|
157
|
+
contentType: "application/tar",
|
|
158
|
+
acl: "authenticated-read",
|
|
159
|
+
chunkSize: "8mb",
|
|
160
|
+
overwrite: false, // Default
|
|
161
|
+
tags: { key_1: "value", key_2: "value" }
|
|
162
|
+
};
|
|
163
|
+
Promise.all([
|
|
164
|
+
// nodejs-001/archive.tar
|
|
165
|
+
instance.uploadObject("aws", "main", "nodejs-001", options, "/tmp/archive.tar"),
|
|
166
|
+
// nodejs-001/2024/01-01.tar
|
|
167
|
+
instance.uploadObject("aws", "main", "nodejs-001", { ...options, publicRead: true, pathname: "2024", filename: "01-01.tar" }, "/tmp/archive.tar"),
|
|
168
|
+
// nodejs-001/archive_1.tar
|
|
169
|
+
instance.uploadObject("aws", { accessKeyId: "*****", secretAccessKey: "*****" }, "nodejs-001", { overwrite: false }, "/tmp/archive.tar")
|
|
170
|
+
]);
|
|
171
|
+
|
|
172
|
+
const rows = await instance.getDatabaseRows({ service: "aws-v3", credential: "main", table: "demo", key: { id: 1 } });
|
|
66
173
|
```
|
|
67
174
|
|
|
68
175
|
## References
|
|
69
176
|
|
|
70
|
-
- https://www.unpkg.com/@e-mc/types@0.
|
|
71
|
-
- https://www.unpkg.com/@e-mc/types@0.
|
|
72
|
-
- https://www.unpkg.com/@e-mc/types@0.
|
|
73
|
-
- https://www.unpkg.com/@e-mc/types@0.
|
|
74
|
-
- https://www.unpkg.com/@e-mc/types@0.
|
|
75
|
-
- https://www.unpkg.com/@e-mc/types@0.
|
|
177
|
+
- https://www.unpkg.com/@e-mc/types@0.9.1/lib/asset.d.ts
|
|
178
|
+
- https://www.unpkg.com/@e-mc/types@0.9.1/lib/cloud.d.ts
|
|
179
|
+
- https://www.unpkg.com/@e-mc/types@0.9.1/lib/core.d.ts
|
|
180
|
+
- https://www.unpkg.com/@e-mc/types@0.9.1/lib/db.d.ts
|
|
181
|
+
- https://www.unpkg.com/@e-mc/types@0.9.1/lib/logger.d.ts
|
|
182
|
+
- https://www.unpkg.com/@e-mc/types@0.9.1/lib/settings.d.ts
|
|
76
183
|
|
|
77
184
|
## LICENSE
|
|
78
185
|
|
|
79
|
-
|
|
186
|
+
BSD 3-Clause
|
package/index.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
2
|
const path = require("path");
|
|
4
3
|
const fs = require("fs");
|
|
5
4
|
const types_1 = require("@e-mc/types");
|
|
@@ -50,10 +49,8 @@ class Cloud extends core_1.ClientDb {
|
|
|
50
49
|
this.downloaded = [];
|
|
51
50
|
}
|
|
52
51
|
static async finalize(instance) {
|
|
53
|
-
var _a, _b, _c, _d;
|
|
54
|
-
var _e, _f;
|
|
55
52
|
if (instance.aborted) {
|
|
56
|
-
return
|
|
53
|
+
return (0, types_1.createAbortError)(true);
|
|
57
54
|
}
|
|
58
55
|
Cloud.sanitizeAssets(this.assets);
|
|
59
56
|
const localStorage = new Map();
|
|
@@ -61,6 +58,7 @@ class Cloud extends core_1.ClientDb {
|
|
|
61
58
|
const state = { host: this, instance, bucketGroup, localStorage };
|
|
62
59
|
const bucketDelete = {};
|
|
63
60
|
const bucketPolicy = {};
|
|
61
|
+
const bucketTagging = {};
|
|
64
62
|
const rawFiles = [];
|
|
65
63
|
const startTime = process.hrtime();
|
|
66
64
|
let tasks = [], downloadMap;
|
|
@@ -68,7 +66,7 @@ class Cloud extends core_1.ClientDb {
|
|
|
68
66
|
instance.writeTimeElapsed(instance.moduleName, "Transactions were committed", startTime, { type: 64, ...Cloud.LOG_STYLE_SUCCESS });
|
|
69
67
|
}
|
|
70
68
|
for (const { instance: document } of this.Document) {
|
|
71
|
-
|
|
69
|
+
document.cloudInit?.(state);
|
|
72
70
|
}
|
|
73
71
|
for (const item of this.assets) {
|
|
74
72
|
const cloudStorage = item.cloudStorage;
|
|
@@ -77,11 +75,11 @@ class Cloud extends core_1.ClientDb {
|
|
|
77
75
|
cloudStorage.forEach(storage => instance.formatMessage(64, storage.service, ["Upload failed", storage.bucket], (0, types_1.errorValue)("File not found", item.uri || item.filename || "Unknown"), { ...Cloud.LOG_CLOUD_WARN }));
|
|
78
76
|
continue;
|
|
79
77
|
}
|
|
80
|
-
|
|
78
|
+
resume: {
|
|
81
79
|
if (item.localUri || item.torrentFiles) {
|
|
82
80
|
for (const { instance: document } of this.Document) {
|
|
83
|
-
if (
|
|
84
|
-
break
|
|
81
|
+
if (document.cloudObject?.(state, item)) {
|
|
82
|
+
break resume;
|
|
85
83
|
}
|
|
86
84
|
}
|
|
87
85
|
if (item.compress) {
|
|
@@ -93,20 +91,29 @@ class Cloud extends core_1.ClientDb {
|
|
|
93
91
|
for (const storage of cloudStorage) {
|
|
94
92
|
const { admin, bucket } = storage;
|
|
95
93
|
if (admin && bucket && instance.hasCredential('storage', storage)) {
|
|
96
|
-
const
|
|
94
|
+
const name = storage.service;
|
|
95
|
+
const credential = instance.getCredential(storage, true);
|
|
96
|
+
const configBucket = admin.configBucket;
|
|
97
97
|
if (admin.emptyBucket) {
|
|
98
|
-
const service = bucketDelete[
|
|
98
|
+
const service = bucketDelete[name] || (bucketDelete[name] = {});
|
|
99
99
|
const items = service[bucket];
|
|
100
100
|
if (!items) {
|
|
101
|
-
service[bucket] = [
|
|
101
|
+
service[bucket] = [{ ...credential }, admin.recursive];
|
|
102
102
|
}
|
|
103
103
|
else if (admin.recursive === false) {
|
|
104
104
|
items[1] = false;
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
|
-
if (
|
|
108
|
-
const
|
|
109
|
-
|
|
107
|
+
if (configBucket) {
|
|
108
|
+
const { policy, tags } = configBucket;
|
|
109
|
+
if (policy) {
|
|
110
|
+
const service = bucketPolicy[name] || (bucketPolicy[name] = {});
|
|
111
|
+
service[bucket] = [{ ...credential }, policy];
|
|
112
|
+
}
|
|
113
|
+
if (tags) {
|
|
114
|
+
const service = bucketTagging[name] || (bucketTagging[name] = {});
|
|
115
|
+
service[bucket] = [{ ...credential }, tags];
|
|
116
|
+
}
|
|
110
117
|
}
|
|
111
118
|
}
|
|
112
119
|
}
|
|
@@ -115,7 +122,7 @@ class Cloud extends core_1.ClientDb {
|
|
|
115
122
|
if (tasks.length) {
|
|
116
123
|
await instance.allSettled(tasks, ['Compress files', instance.moduleName]);
|
|
117
124
|
if (instance.aborted) {
|
|
118
|
-
return
|
|
125
|
+
return (0, types_1.createAbortError)(true);
|
|
119
126
|
}
|
|
120
127
|
tasks = [];
|
|
121
128
|
}
|
|
@@ -123,43 +130,53 @@ class Cloud extends core_1.ClientDb {
|
|
|
123
130
|
const map = bucketDelete[service];
|
|
124
131
|
for (const bucket in map) {
|
|
125
132
|
const [credential, recursive] = map[bucket];
|
|
126
|
-
tasks.push(instance.deleteObjects(service, credential, bucket, recursive).catch(err => instance.writeFail(["Unable to empty bucket", service + ': ' + bucket], err, { type: 64, startTime })));
|
|
133
|
+
tasks.push(instance.deleteObjects(service, credential, bucket, recursive).catch((err) => instance.writeFail(["Unable to empty bucket", service + ': ' + bucket], err, { type: 64, startTime })));
|
|
127
134
|
}
|
|
128
135
|
}
|
|
129
136
|
if (tasks.length) {
|
|
130
137
|
await instance.allSettled(tasks, ['Empty bucket', instance.moduleName]);
|
|
131
138
|
if (instance.aborted) {
|
|
132
|
-
return
|
|
139
|
+
return (0, types_1.createAbortError)(true);
|
|
133
140
|
}
|
|
134
141
|
tasks = [];
|
|
135
142
|
}
|
|
136
|
-
|
|
143
|
+
if (rawFiles.length) {
|
|
144
|
+
const options = { preferBuffer: process.env.EMC_CLOUD_UPLOAD_BUFFER === 'true' };
|
|
145
|
+
rawFiles.forEach(item => tasks.push(...Cloud.uploadAsset(state, item, options)));
|
|
146
|
+
}
|
|
137
147
|
if (tasks.length) {
|
|
138
148
|
await instance.allSettled(tasks, ['Upload raw assets', instance.moduleName]);
|
|
139
149
|
if (instance.aborted) {
|
|
140
|
-
return
|
|
150
|
+
return (0, types_1.createAbortError)(true);
|
|
141
151
|
}
|
|
142
152
|
tasks = [];
|
|
143
153
|
}
|
|
144
154
|
for (const service in bucketPolicy) {
|
|
145
155
|
const map = bucketPolicy[service];
|
|
146
156
|
for (const bucket in map) {
|
|
147
|
-
const
|
|
148
|
-
tasks.push(instance.setBucketPolicy(
|
|
157
|
+
const [credential, options] = map[bucket];
|
|
158
|
+
tasks.push(instance.setBucketPolicy(service, credential, bucket, options).catch((err) => instance.writeFail(["Unable to update bucket policy", service + ': ' + bucket], err, { type: 64, startTime })));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
for (const service in bucketTagging) {
|
|
162
|
+
const map = bucketTagging[service];
|
|
163
|
+
for (const bucket in map) {
|
|
164
|
+
const [credential, options] = map[bucket];
|
|
165
|
+
tasks.push(instance.setBucketTagging(service, credential, bucket, options).catch((err) => instance.writeFail(["Unable to update bucket tagging", service + ': ' + bucket], err, { type: 64, startTime })));
|
|
149
166
|
}
|
|
150
167
|
}
|
|
151
168
|
if (tasks.length) {
|
|
152
169
|
await instance.allSettled(tasks, ['Configure bucket', instance.moduleName]);
|
|
153
170
|
if (instance.aborted) {
|
|
154
|
-
return
|
|
171
|
+
return (0, types_1.createAbortError)(true);
|
|
155
172
|
}
|
|
156
173
|
tasks = [];
|
|
157
174
|
}
|
|
158
175
|
for (const { instance: document } of this.Document) {
|
|
159
176
|
if (document.cloudFinalize) {
|
|
160
|
-
await document.cloudFinalize(state).catch(err => document.writeFail(["Handled rejection", document.moduleName], err, { type: 64, startTime }));
|
|
177
|
+
await document.cloudFinalize(state).catch((err) => document.writeFail(["Handled rejection", document.moduleName], err, { type: 64, startTime }));
|
|
161
178
|
if (document.aborted) {
|
|
162
|
-
return
|
|
179
|
+
return (0, types_1.createAbortError)(true);
|
|
163
180
|
}
|
|
164
181
|
}
|
|
165
182
|
}
|
|
@@ -188,7 +205,7 @@ class Cloud extends core_1.ClientDb {
|
|
|
188
205
|
}
|
|
189
206
|
}
|
|
190
207
|
else {
|
|
191
|
-
downloadUri = pathname ? path.join(this.baseDirectory, pathname.replace(/^([A-Z]:)?[\\/]+/i, ''), filename) : path.join(
|
|
208
|
+
downloadUri = pathname ? path.join(this.baseDirectory, pathname.replace(/^([A-Z]:)?[\\/]+/i, ''), filename) : path.join(data.admin?.preservePath && localUri ? path.dirname(localUri) : this.baseDirectory, filename);
|
|
192
209
|
}
|
|
193
210
|
const destDir = path.dirname(downloadUri);
|
|
194
211
|
if (Cloud.isPath(downloadUri)) {
|
|
@@ -209,7 +226,8 @@ class Cloud extends core_1.ClientDb {
|
|
|
209
226
|
active = false;
|
|
210
227
|
}
|
|
211
228
|
}
|
|
212
|
-
const
|
|
229
|
+
const service = data.service;
|
|
230
|
+
const location = service + '_' + data.bucket + '_' + (download.keyname || filename);
|
|
213
231
|
let pending = (downloadMap || (downloadMap = {}))[location];
|
|
214
232
|
if (pending) {
|
|
215
233
|
pending.add(downloadUri);
|
|
@@ -217,45 +235,47 @@ class Cloud extends core_1.ClientDb {
|
|
|
217
235
|
}
|
|
218
236
|
pending = new Set([downloadUri]);
|
|
219
237
|
download.admin = data.admin;
|
|
220
|
-
|
|
238
|
+
download.flags = SERVICE_CLIENT.get(service).CLOUD_DOWNLOAD_CHUNK && (0, types_1.alignSize)(download.chunkSize) > 0 ? 4 : 0;
|
|
239
|
+
const task = instance.downloadObject(service, instance.getCredential(data), data.bucket, download, async (value) => {
|
|
240
|
+
if (instance.aborted || !value) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
const items = Array.from(pending);
|
|
221
244
|
let result;
|
|
222
|
-
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
if (!copy && i === length - 1) {
|
|
229
|
-
fs.renameSync(value, destUri);
|
|
230
|
-
}
|
|
231
|
-
else {
|
|
232
|
-
fs.copyFileSync(value, destUri);
|
|
233
|
-
}
|
|
234
|
-
size = this.addDownload(destUri);
|
|
245
|
+
for (let i = 0, length = items.length, size, copy; i < length; ++i) {
|
|
246
|
+
const destUri = items[i];
|
|
247
|
+
try {
|
|
248
|
+
if (typeof value === 'string') {
|
|
249
|
+
if (!copy && i === length - 1) {
|
|
250
|
+
fs.renameSync(value, destUri);
|
|
235
251
|
}
|
|
236
252
|
else {
|
|
237
|
-
fs.
|
|
238
|
-
this.addDownload(size = value.length);
|
|
253
|
+
fs.copyFileSync(value, destUri);
|
|
239
254
|
}
|
|
240
|
-
this.
|
|
241
|
-
this.formatMessage(64, data.service, ["Download success", (0, types_1.formatSize)(size)], destUri, { ...Cloud.LOG_CLOUD_DOWNLOAD });
|
|
242
|
-
result || (result = destUri);
|
|
255
|
+
size = this.addDownload(destUri);
|
|
243
256
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
257
|
+
else {
|
|
258
|
+
fs.writeFileSync(destUri, value);
|
|
259
|
+
this.addDownload(size = value.length);
|
|
260
|
+
}
|
|
261
|
+
this.add(destUri);
|
|
262
|
+
this.formatMessage(64, service, ["Download success", (0, types_1.formatSize)(size)], destUri, { ...Cloud.LOG_CLOUD_DOWNLOAD });
|
|
263
|
+
result || (result = destUri);
|
|
264
|
+
}
|
|
265
|
+
catch (err) {
|
|
266
|
+
if (!copy && core_1.ClientDb.isErrorCode(err, 'EXDEV')) {
|
|
267
|
+
copy = true;
|
|
268
|
+
--i;
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
instance.writeFail(["Unable to write file", path.basename(destUri)], err, { type: 32, fatal: !!active, startTime });
|
|
252
272
|
}
|
|
253
273
|
}
|
|
254
274
|
}
|
|
255
275
|
return result;
|
|
256
276
|
})
|
|
257
|
-
.catch(err => instance.writeFail(["Download failed", path.basename(downloadUri)], err, { type: 64, startTime }));
|
|
258
|
-
if (active || waitStatus || this.incremental ===
|
|
277
|
+
.catch((err) => instance.writeFail(["Download failed", path.basename(downloadUri)], err, { type: 64, startTime }));
|
|
278
|
+
if (active || waitStatus || this.incremental === "staging") {
|
|
259
279
|
tasks.push(task);
|
|
260
280
|
}
|
|
261
281
|
downloadMap[location] = pending;
|
|
@@ -266,11 +286,16 @@ class Cloud extends core_1.ClientDb {
|
|
|
266
286
|
return instance.allSettled(tasks, ['Download objects', instance.moduleName]);
|
|
267
287
|
}
|
|
268
288
|
}
|
|
269
|
-
static uploadAsset(state, file, contentType
|
|
270
|
-
|
|
289
|
+
static uploadAsset(state, file, contentType, ignoreProcess) {
|
|
290
|
+
let preferBuffer;
|
|
291
|
+
if ((0, types_1.isObject)(contentType)) {
|
|
292
|
+
({ contentType, ignoreProcess, preferBuffer } = contentType);
|
|
293
|
+
}
|
|
294
|
+
else if (typeof contentType === 'boolean') {
|
|
271
295
|
ignoreProcess = contentType;
|
|
272
|
-
contentType =
|
|
296
|
+
contentType = undefined;
|
|
273
297
|
}
|
|
298
|
+
contentType || (contentType = file.mimeType);
|
|
274
299
|
const { host, instance } = state;
|
|
275
300
|
const cloudStorage = file.cloudStorage;
|
|
276
301
|
if (instance.aborted || !Array.isArray(cloudStorage)) {
|
|
@@ -281,8 +306,18 @@ class Cloud extends core_1.ClientDb {
|
|
|
281
306
|
if (!instance.hasStorage('upload', storage)) {
|
|
282
307
|
continue;
|
|
283
308
|
}
|
|
309
|
+
const client = SERVICE_CLIENT.get(storage.service);
|
|
284
310
|
const upload = storage.upload;
|
|
285
311
|
const active = storage === instance.getStorage('upload', cloudStorage);
|
|
312
|
+
let flags = client.CLOUD_UPLOAD_DISK ? 1 : 0;
|
|
313
|
+
if (!preferBuffer) {
|
|
314
|
+
if (client.CLOUD_UPLOAD_STREAM && upload.minStreamSize !== -1) {
|
|
315
|
+
flags |= 2;
|
|
316
|
+
}
|
|
317
|
+
if (client.CLOUD_UPLOAD_CHUNK && (0, types_1.alignSize)(upload.chunkSize) > 0) {
|
|
318
|
+
flags |= 4;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
286
321
|
if (active && upload.localStorage === false) {
|
|
287
322
|
state.localStorage.set(file, upload);
|
|
288
323
|
}
|
|
@@ -297,14 +332,10 @@ class Cloud extends core_1.ClientDb {
|
|
|
297
332
|
};
|
|
298
333
|
const task = new Promise(resolve => {
|
|
299
334
|
const { service, bucket = state.bucketGroup, admin } = storage;
|
|
300
|
-
let minStreamSize = upload.minStreamSize;
|
|
301
|
-
if ((0, types_1.isString)(minStreamSize)) {
|
|
302
|
-
minStreamSize = (0, types_1.formatSize)(minStreamSize);
|
|
303
|
-
}
|
|
304
335
|
const credential = instance.getCredential(storage, true);
|
|
336
|
+
const minStreamSize = (0, types_1.alignSize)(upload.minStreamSize);
|
|
305
337
|
const uploading = [];
|
|
306
338
|
getFiles(file, upload).forEach(async (group, index) => {
|
|
307
|
-
var _a;
|
|
308
339
|
let fileGroup;
|
|
309
340
|
if (index === 0) {
|
|
310
341
|
if (!group[0]) {
|
|
@@ -312,7 +343,7 @@ class Cloud extends core_1.ClientDb {
|
|
|
312
343
|
return;
|
|
313
344
|
}
|
|
314
345
|
if (group.length > 1) {
|
|
315
|
-
if (
|
|
346
|
+
if (flags > 0) {
|
|
316
347
|
fileGroup = group.slice(1).filter(value => this.isPath(value, true)).map(value => [value, path.extname(value), value]);
|
|
317
348
|
}
|
|
318
349
|
else {
|
|
@@ -321,7 +352,7 @@ class Cloud extends core_1.ClientDb {
|
|
|
321
352
|
const value = group[i];
|
|
322
353
|
if (this.isPath(value, true)) {
|
|
323
354
|
try {
|
|
324
|
-
fileGroup.push([
|
|
355
|
+
fileGroup.push([!isNaN(minStreamSize) ? await this.streamFile(value, { minStreamSize, cache: false, signal: instance.signal }) : fs.readFileSync(value), path.extname(value), value]);
|
|
325
356
|
}
|
|
326
357
|
catch (err) {
|
|
327
358
|
instance.writeFail(["Unable to read file", path.basename(value)], err, { type: 32, fatal: false });
|
|
@@ -336,7 +367,7 @@ class Cloud extends core_1.ClientDb {
|
|
|
336
367
|
const localUri = group[i];
|
|
337
368
|
const exists = index === 0 || this.isPath(localUri, true);
|
|
338
369
|
if (!exists || !instance.canRead(localUri, { ownPermissionOnly: true })) {
|
|
339
|
-
instance.writeFail(["Unable to read file", path.basename(localUri)], (0, types_1.errorValue)(exists ? "Not permitted to read file"
|
|
370
|
+
instance.writeFail(["Unable to read file", path.basename(localUri)], (0, types_1.errorValue)(!exists ? "File not found" : "Not permitted to read file", localUri), { type: 64, fatal: i === 0 && index === 0 });
|
|
340
371
|
continue;
|
|
341
372
|
}
|
|
342
373
|
let buffer, filename;
|
|
@@ -350,17 +381,32 @@ class Cloud extends core_1.ClientDb {
|
|
|
350
381
|
else if (upload.overwrite) {
|
|
351
382
|
filename = path.basename(localUri);
|
|
352
383
|
}
|
|
353
|
-
|
|
384
|
+
if (file.sourceUTF8) {
|
|
385
|
+
buffer = Buffer.from(file.sourceUTF8, file.encoding);
|
|
386
|
+
}
|
|
387
|
+
else if (!(buffer = file.buffer) && (flags & 2) === 0 && ((flags & 4) === 0 || !isNaN(minStreamSize))) {
|
|
388
|
+
if (!isNaN(minStreamSize)) {
|
|
389
|
+
buffer = await host.getBuffer(file, minStreamSize).catch(() => {
|
|
390
|
+
if (flags & 1) {
|
|
391
|
+
return Buffer.alloc(0);
|
|
392
|
+
}
|
|
393
|
+
return null;
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
else {
|
|
397
|
+
buffer = host.getBuffer(file);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
354
400
|
}
|
|
355
401
|
else {
|
|
356
402
|
contentType = this.lookupMime(path.basename(localUri)) || file.mimeType;
|
|
357
403
|
}
|
|
358
|
-
const options = { ...upload, buffer, filename, fileGroup, admin };
|
|
404
|
+
const options = { ...upload, buffer, filename, fileGroup, admin, flags };
|
|
359
405
|
if (index > 0 || !options.contentType) {
|
|
360
406
|
options.contentType = contentType;
|
|
361
407
|
}
|
|
362
408
|
uploading.push(instance.uploadObject(service, { ...credential }, bucket, options, localUri, callback)
|
|
363
|
-
.catch(err => instance.writeFail(["Upload failed", path.basename(localUri)], err, { type: 64, fatal: i === 0 && index === 0 })));
|
|
409
|
+
.catch((err) => instance.writeFail(["Upload failed", path.basename(localUri)], err, { type: 64, fatal: i === 0 && index === 0 })));
|
|
364
410
|
}
|
|
365
411
|
});
|
|
366
412
|
instance.allSettled(uploading, [`Upload file "${contentType || "Unknown"}"`, storage.service + ': ' + path.basename(file.localUri)]).then(() => resolve());
|
|
@@ -372,7 +418,6 @@ class Cloud extends core_1.ClientDb {
|
|
|
372
418
|
return tasks;
|
|
373
419
|
}
|
|
374
420
|
static sanitizeAssets(assets) {
|
|
375
|
-
var _a;
|
|
376
421
|
const storage = [];
|
|
377
422
|
for (const item of assets) {
|
|
378
423
|
const cloudStorage = item.cloudStorage;
|
|
@@ -383,7 +428,7 @@ class Cloud extends core_1.ClientDb {
|
|
|
383
428
|
if (upload.filename) {
|
|
384
429
|
setUploadFilename(upload, this.toPosix(upload.filename));
|
|
385
430
|
}
|
|
386
|
-
const pathname = upload.pathname ||
|
|
431
|
+
const pathname = upload.pathname || data.admin?.preservePath && item.pathname;
|
|
387
432
|
if (pathname) {
|
|
388
433
|
upload.pathname = this.toPosix(pathname).replace(/^\/+/, '') + '/';
|
|
389
434
|
}
|
|
@@ -451,21 +496,20 @@ class Cloud extends core_1.ClientDb {
|
|
|
451
496
|
return super.setQueryResult(service, credential, queryString, result, sessionKey);
|
|
452
497
|
}
|
|
453
498
|
async createBucket(service, credential, bucket, publicRead, options) {
|
|
454
|
-
var _a, _b;
|
|
455
499
|
if (this.aborted) {
|
|
456
|
-
return
|
|
500
|
+
return (0, types_1.createAbortError)(true);
|
|
457
501
|
}
|
|
458
502
|
try {
|
|
459
503
|
const client = this.getClient(service);
|
|
460
504
|
try {
|
|
461
505
|
if (publicRead === undefined || typeof publicRead === 'boolean') {
|
|
462
|
-
const handler =
|
|
506
|
+
const handler = client.createBucket?.bind(this);
|
|
463
507
|
if (handler) {
|
|
464
508
|
return handler.call(this, credential, bucket, publicRead);
|
|
465
509
|
}
|
|
466
510
|
}
|
|
467
511
|
else {
|
|
468
|
-
const handler =
|
|
512
|
+
const handler = client.createBucketV2?.bind(this);
|
|
469
513
|
if (handler) {
|
|
470
514
|
return handler.call(this, credential, bucket, publicRead, options);
|
|
471
515
|
}
|
|
@@ -482,12 +526,11 @@ class Cloud extends core_1.ClientDb {
|
|
|
482
526
|
}
|
|
483
527
|
}
|
|
484
528
|
async setBucketPolicy(service, credential, bucket, options) {
|
|
485
|
-
var _a;
|
|
486
529
|
if (this.aborted) {
|
|
487
|
-
return
|
|
530
|
+
return (0, types_1.createAbortError)(true);
|
|
488
531
|
}
|
|
489
532
|
try {
|
|
490
|
-
const handler =
|
|
533
|
+
const handler = this.getClient(service).setBucketPolicy?.bind(this);
|
|
491
534
|
if (handler) {
|
|
492
535
|
try {
|
|
493
536
|
return handler.call(this, credential, bucket, options);
|
|
@@ -503,13 +546,33 @@ class Cloud extends core_1.ClientDb {
|
|
|
503
546
|
return Promise.reject(err);
|
|
504
547
|
}
|
|
505
548
|
}
|
|
549
|
+
async setBucketTagging(service, credential, bucket, options) {
|
|
550
|
+
if (this.aborted) {
|
|
551
|
+
return (0, types_1.createAbortError)(true);
|
|
552
|
+
}
|
|
553
|
+
try {
|
|
554
|
+
const handler = this.getClient(service).setBucketTagging?.bind(this);
|
|
555
|
+
if (handler) {
|
|
556
|
+
try {
|
|
557
|
+
return handler.call(this, credential, bucket, options);
|
|
558
|
+
}
|
|
559
|
+
catch (err) {
|
|
560
|
+
this.formatMessage(64, service, ["Unable to update bucket tagging", bucket], err, { ...Cloud.LOG_CLOUD_WARN });
|
|
561
|
+
return Promise.reject(err);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
return Promise.reject((0, types_1.errorMessage)(service, "Bucket tagging not supported"));
|
|
565
|
+
}
|
|
566
|
+
catch (err) {
|
|
567
|
+
return Promise.reject(err);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
506
570
|
async setBucketWebsite(service, credential, bucket, options) {
|
|
507
|
-
var _a;
|
|
508
571
|
if (this.aborted) {
|
|
509
|
-
return
|
|
572
|
+
return (0, types_1.createAbortError)(true);
|
|
510
573
|
}
|
|
511
574
|
try {
|
|
512
|
-
const handler =
|
|
575
|
+
const handler = this.getClient(service).setBucketWebsite?.bind(this);
|
|
513
576
|
if (handler) {
|
|
514
577
|
try {
|
|
515
578
|
return handler.call(this, credential, bucket, options);
|
|
@@ -526,20 +589,19 @@ class Cloud extends core_1.ClientDb {
|
|
|
526
589
|
}
|
|
527
590
|
}
|
|
528
591
|
async deleteObjects(service, credential, bucket, recursive = true) {
|
|
529
|
-
var _a, _b;
|
|
530
592
|
if (this.aborted) {
|
|
531
|
-
return
|
|
593
|
+
return (0, types_1.createAbortError)(true);
|
|
532
594
|
}
|
|
533
595
|
try {
|
|
534
596
|
const errorResponse = (err) => this.formatMessage(64, service, ["Unable to empty bucket", bucket], err, { ...Cloud.LOG_CLOUD_WARN });
|
|
535
597
|
const client = this.getClient(service);
|
|
536
|
-
const handlerV2 =
|
|
598
|
+
const handlerV2 = client.deleteObjectsV2?.bind(this);
|
|
537
599
|
if (handlerV2) {
|
|
538
|
-
return handlerV2.call(this, credential, bucket, recursive, service).catch(err => errorResponse(err));
|
|
600
|
+
return handlerV2.call(this, credential, bucket, recursive, service).catch((err) => errorResponse(err));
|
|
539
601
|
}
|
|
540
|
-
const handlerV1 =
|
|
602
|
+
const handlerV1 = client.deleteObjects?.bind(this);
|
|
541
603
|
if (handlerV1) {
|
|
542
|
-
return handlerV1.call(this, credential, bucket, service, undefined, recursive).catch(err => errorResponse(err));
|
|
604
|
+
return handlerV1.call(this, credential, bucket, service, undefined, recursive).catch((err) => errorResponse(err));
|
|
543
605
|
}
|
|
544
606
|
return Promise.reject((0, types_1.errorMessage)(service, "Delete objects not supported"));
|
|
545
607
|
}
|
|
@@ -549,7 +611,7 @@ class Cloud extends core_1.ClientDb {
|
|
|
549
611
|
}
|
|
550
612
|
async uploadObject(service, credential, bucket, upload, localUri, beforeResolve) {
|
|
551
613
|
if (this.aborted) {
|
|
552
|
-
return
|
|
614
|
+
return (0, types_1.createAbortError)(true);
|
|
553
615
|
}
|
|
554
616
|
let handler;
|
|
555
617
|
try {
|
|
@@ -561,7 +623,8 @@ class Cloud extends core_1.ClientDb {
|
|
|
561
623
|
}
|
|
562
624
|
return new Promise((resolve, reject) => {
|
|
563
625
|
try {
|
|
564
|
-
|
|
626
|
+
const flags = upload.flags || 0;
|
|
627
|
+
handler({ bucket, upload, buffer: upload.buffer || ((flags & 1) === 0 && (flags & 2) === 0 && (flags & 4) === 0 ? fs.readFileSync(localUri) : Buffer.alloc(0)), localUri }, async (err, value) => {
|
|
565
628
|
if (err) {
|
|
566
629
|
reject(errorObject(err, service, "Upload failed"));
|
|
567
630
|
}
|
|
@@ -569,7 +632,7 @@ class Cloud extends core_1.ClientDb {
|
|
|
569
632
|
if (beforeResolve) {
|
|
570
633
|
await beforeResolve(value);
|
|
571
634
|
}
|
|
572
|
-
this.addLog(types_1.STATUS_TYPE.INFO, service + ' ->
|
|
635
|
+
this.addLog(types_1.STATUS_TYPE.INFO, service + ' -> uploadObject', bucket, value);
|
|
573
636
|
resolve(value);
|
|
574
637
|
}
|
|
575
638
|
else {
|
|
@@ -584,7 +647,7 @@ class Cloud extends core_1.ClientDb {
|
|
|
584
647
|
}
|
|
585
648
|
async downloadObject(service, credential, bucket, download, beforeResolve) {
|
|
586
649
|
if (this.aborted) {
|
|
587
|
-
return
|
|
650
|
+
return (0, types_1.createAbortError)(true);
|
|
588
651
|
}
|
|
589
652
|
if ((service === 'gcp' || service === 'gcloud') && (0, types_1.isPlainObject)(credential)) {
|
|
590
653
|
credential.storageBucket || (credential.storageBucket = bucket);
|
|
@@ -611,7 +674,7 @@ class Cloud extends core_1.ClientDb {
|
|
|
611
674
|
}
|
|
612
675
|
}
|
|
613
676
|
if (typeof value === 'string' && path.isAbsolute(value)) {
|
|
614
|
-
this.addLog(types_1.STATUS_TYPE.INFO, service + ' ->
|
|
677
|
+
this.addLog(types_1.STATUS_TYPE.INFO, service + ' -> downloadObject', bucket, value);
|
|
615
678
|
this.downloaded.push(value);
|
|
616
679
|
}
|
|
617
680
|
resolve(value);
|
|
@@ -628,7 +691,7 @@ class Cloud extends core_1.ClientDb {
|
|
|
628
691
|
}
|
|
629
692
|
async getDatabaseRows(item, ignoreErrors, sessionKey) {
|
|
630
693
|
if (this.aborted) {
|
|
631
|
-
return
|
|
694
|
+
return (0, types_1.createAbortError)(true);
|
|
632
695
|
}
|
|
633
696
|
if (typeof ignoreErrors === 'string') {
|
|
634
697
|
sessionKey = ignoreErrors;
|
|
@@ -637,9 +700,9 @@ class Cloud extends core_1.ClientDb {
|
|
|
637
700
|
const service = item.service;
|
|
638
701
|
let client;
|
|
639
702
|
if (this.hasCredential('database', item) && (client = this.getClient(item.service))) {
|
|
640
|
-
if (client
|
|
703
|
+
if (client?.executeQuery) {
|
|
641
704
|
const credential = this.getCredential(item);
|
|
642
|
-
if (item.options && this.hasCoerce(service, 'options',
|
|
705
|
+
if (item.options && this.hasCoerce(service, 'options', credential)) {
|
|
643
706
|
(0, types_1.coerceObject)(item.options);
|
|
644
707
|
}
|
|
645
708
|
if (ignoreErrors) {
|
|
@@ -659,7 +722,7 @@ class Cloud extends core_1.ClientDb {
|
|
|
659
722
|
}
|
|
660
723
|
async getDatabaseBatchRows(batch, ignoreErrors, sessionKey) {
|
|
661
724
|
if (this.aborted) {
|
|
662
|
-
return
|
|
725
|
+
return (0, types_1.createAbortError)(true);
|
|
663
726
|
}
|
|
664
727
|
if (typeof ignoreErrors === 'string') {
|
|
665
728
|
sessionKey = ignoreErrors;
|
|
@@ -669,9 +732,9 @@ class Cloud extends core_1.ClientDb {
|
|
|
669
732
|
const service = data.service;
|
|
670
733
|
let client;
|
|
671
734
|
if (this.hasCredential('database', data) && (client = this.getClient(service))) {
|
|
672
|
-
if (client
|
|
735
|
+
if (client?.executeBatchQuery) {
|
|
673
736
|
const credential = this.getCredential(data);
|
|
674
|
-
if (this.hasCoerce(service, 'options',
|
|
737
|
+
if (this.hasCoerce(service, 'options', credential)) {
|
|
675
738
|
batch.forEach(item => item.options && (0, types_1.coerceObject)(item.options));
|
|
676
739
|
}
|
|
677
740
|
if (ignoreErrors) {
|
|
@@ -741,9 +804,9 @@ class Cloud extends core_1.ClientDb {
|
|
|
741
804
|
const client = this.getClient(data.service);
|
|
742
805
|
switch (feature) {
|
|
743
806
|
case 'storage':
|
|
744
|
-
return typeof client.validateStorage === 'function' && client.validateStorage(credential, data);
|
|
807
|
+
return typeof client.validateStorage === 'function' && client.validateStorage(credential, data) || this.settingsOf(data.service, 'auth', 'storage') === false;
|
|
745
808
|
case 'database':
|
|
746
|
-
return typeof client.validateDatabase === 'function' && client.validateDatabase(credential, data);
|
|
809
|
+
return typeof client.validateDatabase === 'function' && client.validateDatabase(credential, data) || this.settingsOf(data.service, 'auth', 'database') === false;
|
|
747
810
|
}
|
|
748
811
|
}
|
|
749
812
|
catch (err) {
|
|
@@ -758,28 +821,25 @@ class Cloud extends core_1.ClientDb {
|
|
|
758
821
|
return (SERVICE_DOWNLOAD[service] || (SERVICE_DOWNLOAD[service] = require(this.resolveService(service, 'download')))).call(this, credential, service);
|
|
759
822
|
}
|
|
760
823
|
resolveService(service, folder) {
|
|
761
|
-
var _a;
|
|
762
824
|
let result;
|
|
763
825
|
if (!service.startsWith('@')) {
|
|
764
|
-
result =
|
|
826
|
+
result = this.settings.imports?.[service] || util_1.IMPORTS[service];
|
|
765
827
|
}
|
|
766
|
-
else
|
|
767
|
-
folder = 'client';
|
|
828
|
+
else {
|
|
829
|
+
folder || (folder = 'client');
|
|
768
830
|
}
|
|
769
831
|
return (result || service) + (folder ? '/' + folder : '');
|
|
770
832
|
}
|
|
771
833
|
settingsOf(service, name, component) {
|
|
772
|
-
|
|
773
|
-
const result = (_a = this.settings[service]) === null || _a === void 0 ? void 0 : _a[name];
|
|
834
|
+
const result = this.settings[service]?.[name];
|
|
774
835
|
return component ? (0, types_1.isObject)(result) ? result[component] : undefined : result;
|
|
775
836
|
}
|
|
776
837
|
async commit() {
|
|
777
838
|
if (this.aborted) {
|
|
778
|
-
return
|
|
839
|
+
return (0, types_1.createAbortError)(true);
|
|
779
840
|
}
|
|
780
841
|
const items = this.pending.filter(item => !item.document).map(async (data) => {
|
|
781
|
-
|
|
782
|
-
(_a = data.ignoreCache) !== null && _a !== void 0 ? _a : (data.ignoreCache = true);
|
|
842
|
+
data.ignoreCache ?? (data.ignoreCache = true);
|
|
783
843
|
return this.getDatabaseRows(data, true);
|
|
784
844
|
});
|
|
785
845
|
return items.length === 0 ? false : this.allSettled(items, ["Execute unassigned queries", this.moduleName]).then(result => result.length > 0).catch(() => false);
|
|
@@ -810,9 +870,5 @@ Cloud.LOG_CLOUD_UPLOAD = Object.freeze({ titleColor: 'green' });
|
|
|
810
870
|
Cloud.LOG_CLOUD_DOWNLOAD = Object.freeze({ titleColor: 'cyan' });
|
|
811
871
|
Cloud.LOG_CLOUD_DELETE = Object.freeze({ titleColor: 'grey' });
|
|
812
872
|
Cloud.LOG_CLOUD_DELAYED = Object.freeze({ titleColor: 'grey' });
|
|
813
|
-
exports.default = Cloud;
|
|
814
873
|
|
|
815
|
-
|
|
816
|
-
module.exports = exports.default;
|
|
817
|
-
module.exports.default = exports.default;
|
|
818
|
-
}
|
|
874
|
+
module.exports = Cloud;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@e-mc/cloud",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"description": "Cloud constructor for E-mc.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -17,12 +17,12 @@
|
|
|
17
17
|
"squared-functions"
|
|
18
18
|
],
|
|
19
19
|
"author": "An Pham <anpham6@gmail.com>",
|
|
20
|
-
"license": "
|
|
20
|
+
"license": "BSD 3-Clause",
|
|
21
21
|
"homepage": "https://github.com/anpham6/e-mc#readme",
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@e-mc/core": "0.
|
|
24
|
-
"@e-mc/db": "0.
|
|
25
|
-
"@e-mc/types": "0.
|
|
23
|
+
"@e-mc/core": "0.9.1",
|
|
24
|
+
"@e-mc/db": "0.9.1",
|
|
25
|
+
"@e-mc/types": "0.9.1",
|
|
26
26
|
"mime-types": "^2.1.35"
|
|
27
27
|
}
|
|
28
28
|
}
|
package/types/index.d.ts
CHANGED
|
@@ -2,21 +2,25 @@ import type { ICloud, IFileManager, IModule, IScopeOrigin } from '../../types/li
|
|
|
2
2
|
import type { BucketWebsiteOptions, CloudAsset, CloudDatabase, CloudService, CloudStorageUpload, DownloadData, UploadData } from '../../types/lib/cloud';
|
|
3
3
|
import type { BatchQueryResult, QueryResult } from '../../types/lib/db';
|
|
4
4
|
|
|
5
|
-
export interface ICloudServiceClient<T extends CloudDatabase = CloudDatabase> {
|
|
5
|
+
export interface ICloudServiceClient<T extends CloudDatabase = CloudDatabase, U = unknown, V = unknown> {
|
|
6
6
|
CLOUD_SERVICE_NAME: string;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
7
|
+
CLOUD_UPLOAD_DISK?: boolean;
|
|
8
|
+
CLOUD_UPLOAD_STREAM?: boolean;
|
|
9
|
+
CLOUD_UPLOAD_CHUNK?: boolean;
|
|
10
|
+
CLOUD_DOWNLOAD_CHUNK?: boolean;
|
|
11
|
+
validateStorage?(credential: U, data?: CloudService): boolean;
|
|
12
|
+
validateDatabase?(credential: V, data?: CloudService): boolean;
|
|
13
|
+
createStorageClient?<W>(this: IModule, credential: U, service?: string): W;
|
|
14
|
+
createDatabaseClient?<W>(this: IModule, credential: V, data?: CloudService): W;
|
|
15
|
+
createBucket?(this: IModule, credential: U, bucket: string, publicRead?: boolean, service?: string, sdk?: string): Promise<boolean>;
|
|
16
|
+
createBucketV2?(this: IModule, credential: U, bucket: string, acl?: unknown, options?: unknown, service?: string, sdk?: string): Promise<boolean>;
|
|
17
|
+
setBucketPolicy?(this: IModule, credential: U, bucket: string, options: unknown, service?: string, sdk?: string): Promise<boolean>;
|
|
18
|
+
setBucketTagging?(this: IModule, credential: U, bucket: string, options: unknown, service?: string, sdk?: string): Promise<boolean>;
|
|
19
|
+
setBucketWebsite?(this: IModule, credential: U, bucket: string, options: BucketWebsiteOptions, service?: string, sdk?: string): Promise<boolean>;
|
|
20
|
+
deleteObjects?(this: IModule, credential: U, bucket: string, service?: string, sdk?: string, recursive?: boolean): Promise<void>;
|
|
21
|
+
deleteObjectsV2?(this: IModule, credential: U, bucket: string, recursive?: boolean, service?: string, sdk?: string): Promise<void>;
|
|
22
|
+
executeQuery?(this: ICloud, credential: V, data: T, sessionKey?: string): Promise<QueryResult>;
|
|
23
|
+
executeBatchQuery?(this: ICloud, credential: V, batch: T[], sessionKey?: string): Promise<BatchQueryResult>;
|
|
20
24
|
}
|
|
21
25
|
|
|
22
26
|
export interface CloudScopeOrigin<T extends IFileManager<U>, U extends CloudAsset = CloudAsset, V extends ICloud = ICloud<T>> extends Required<IScopeOrigin<T, V>> {
|
|
@@ -24,8 +28,8 @@ export interface CloudScopeOrigin<T extends IFileManager<U>, U extends CloudAsse
|
|
|
24
28
|
localStorage: Map<U, CloudStorageUpload>;
|
|
25
29
|
}
|
|
26
30
|
|
|
27
|
-
export type ServiceHost<T> = (this: IModule, credential:
|
|
31
|
+
export type ServiceHost<T, U = unknown> = (this: IModule, credential: U, service?: string, sdk?: string) => T;
|
|
28
32
|
export type UploadCallback = (data: UploadData, callback: (err: unknown, value?: string) => void) => void;
|
|
29
|
-
export type DownloadCallback = (data: DownloadData, callback: (err: unknown, value?: Null<
|
|
33
|
+
export type DownloadCallback = (data: DownloadData, callback: (err: unknown, value?: Null<Bufferable>) => void) => void;
|
|
30
34
|
export type UploadHost = ServiceHost<UploadCallback>;
|
|
31
35
|
export type DownloadHost = ServiceHost<DownloadCallback>;
|
package/util.d.ts
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
import type { UploadContent } from '../types/lib/cloud';
|
|
2
|
+
import type { AuthValue } from '../types/lib/http';
|
|
2
3
|
|
|
3
4
|
import type { Readable } from 'stream';
|
|
4
5
|
|
|
5
|
-
interface AuthValue {
|
|
6
|
-
username?: string;
|
|
7
|
-
password?: string;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
6
|
declare namespace util {
|
|
11
7
|
const IMPORTS: Record<string, string | undefined>;
|
|
12
|
-
function readableAsBuffer(
|
|
13
|
-
function createKeyAndBody(filename: string, items: UploadContent[],
|
|
8
|
+
function readableAsBuffer(from: Readable): Promise<Buffer | null>;
|
|
9
|
+
function createKeyAndBody<T = Buffer>(filename: string, items: UploadContent[], chunkSize?: number | string | FunctionType<void>, errorCallback?: FunctionType<void> | number, flags?: number): [string[], T[], string[]];
|
|
14
10
|
function generateFilename(filename: string): (i: number) => [string, boolean];
|
|
15
11
|
function getBasicAuth(auth: AuthValue): string;
|
|
16
12
|
function getBasicAuth(username: unknown, password?: unknown): string;
|
package/util.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
2
|
exports.hasBasicAuth = exports.getBasicAuth = exports.formatError = exports.generateFilename = exports.createKeyAndBody = exports.readableAsBuffer = exports.IMPORTS = void 0;
|
|
4
3
|
const path = require("path");
|
|
5
4
|
const fs = require("fs");
|
|
5
|
+
const stream = require("stream");
|
|
6
6
|
const mime = require("mime-types");
|
|
7
7
|
const types_1 = require("@e-mc/types");
|
|
8
8
|
const util_1 = require("@e-mc/db/util");
|
|
@@ -20,11 +20,13 @@ exports.IMPORTS = {
|
|
|
20
20
|
"minio": "@pi-r/minio",
|
|
21
21
|
"oci": "@pi-r/oci"
|
|
22
22
|
};
|
|
23
|
-
async function readableAsBuffer(
|
|
23
|
+
async function readableAsBuffer(from) {
|
|
24
24
|
return new Promise((resolve, reject) => {
|
|
25
25
|
let result = null;
|
|
26
|
-
|
|
27
|
-
.
|
|
26
|
+
from.on('data', chunk => {
|
|
27
|
+
if (!Buffer.isBuffer(chunk)) {
|
|
28
|
+
chunk = Buffer.from(chunk);
|
|
29
|
+
}
|
|
28
30
|
result = result ? Buffer.concat([result, chunk]) : chunk;
|
|
29
31
|
})
|
|
30
32
|
.on('end', () => resolve(result))
|
|
@@ -33,38 +35,88 @@ async function readableAsBuffer(stream) {
|
|
|
33
35
|
});
|
|
34
36
|
}
|
|
35
37
|
exports.readableAsBuffer = readableAsBuffer;
|
|
36
|
-
function createKeyAndBody(filename, items,
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
function createKeyAndBody(filename, items, chunkSize = 0, errorCallback, flags = 0) {
|
|
39
|
+
let mimeType;
|
|
40
|
+
switch (typeof chunkSize) {
|
|
41
|
+
case 'function':
|
|
42
|
+
errorCallback = chunkSize;
|
|
43
|
+
chunkSize = 0;
|
|
44
|
+
break;
|
|
45
|
+
case 'string':
|
|
46
|
+
mimeType = chunkSize;
|
|
47
|
+
chunkSize = 0;
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
if (typeof errorCallback === 'number') {
|
|
51
|
+
flags = errorCallback;
|
|
52
|
+
errorCallback = undefined;
|
|
40
53
|
}
|
|
41
54
|
const key = [];
|
|
42
55
|
const body = [];
|
|
43
56
|
const type = [];
|
|
44
57
|
for (let [content, ext, localFile] of items) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (
|
|
49
|
-
|
|
58
|
+
try {
|
|
59
|
+
let target;
|
|
60
|
+
if (chunkSize > 0) {
|
|
61
|
+
if (Buffer.isBuffer(content)) {
|
|
62
|
+
if (flags & 2) {
|
|
63
|
+
target = stream.Readable.from(content);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
target = content;
|
|
67
|
+
if (localFile && content.length > chunkSize) {
|
|
68
|
+
try {
|
|
69
|
+
fs.writeFileSync(localFile, content);
|
|
70
|
+
target = localFile;
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
if (localFile) {
|
|
79
|
+
target = localFile;
|
|
80
|
+
}
|
|
81
|
+
else if (path.isAbsolute(content) && fs.existsSync(content)) {
|
|
82
|
+
target = content;
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
if (target && fs.statSync(target).size <= chunkSize) {
|
|
86
|
+
const buffer = flags & 2 ? fs.createReadStream(target) : fs.readFileSync(target);
|
|
87
|
+
localFile = target;
|
|
88
|
+
target = buffer;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
else if (typeof content === 'string') {
|
|
97
|
+
if (content === localFile || path.isAbsolute(content) && fs.existsSync(content)) {
|
|
98
|
+
target = flags & 2 ? fs.createReadStream(content) : fs.readFileSync(content);
|
|
50
99
|
localFile = content;
|
|
51
100
|
}
|
|
52
101
|
else {
|
|
53
|
-
|
|
102
|
+
target = Buffer.from(content);
|
|
103
|
+
if (flags & 2) {
|
|
104
|
+
target = stream.Readable.from(target);
|
|
105
|
+
}
|
|
54
106
|
}
|
|
55
107
|
}
|
|
56
|
-
|
|
57
|
-
|
|
108
|
+
else if (Buffer.isBuffer(content)) {
|
|
109
|
+
target = flags & 2 ? stream.Readable.from(content) : content;
|
|
110
|
+
}
|
|
111
|
+
if (target) {
|
|
112
|
+
const output = filename + ext;
|
|
113
|
+
key.push(ext === '.map' && localFile ? path.basename(localFile) : output);
|
|
114
|
+
body.push(target);
|
|
115
|
+
type.push(ext !== '.map' && mime.lookup(output) || mimeType || 'application/octet-stream');
|
|
58
116
|
}
|
|
59
117
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
if (buffer) {
|
|
64
|
-
const output = filename + ext;
|
|
65
|
-
key.push(ext === '.map' && localFile ? path.basename(localFile) : output);
|
|
66
|
-
body.push(buffer);
|
|
67
|
-
type.push(ext !== '.map' && mime.lookup(output) || mimeType || 'application/octet-stream');
|
|
118
|
+
catch (err) {
|
|
119
|
+
errorCallback?.(err);
|
|
68
120
|
}
|
|
69
121
|
}
|
|
70
122
|
return [key, body, type];
|