@pi-r/gcp 0.6.7 → 0.7.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/README.md +3 -6
- package/client/index.js +159 -90
- package/download/index.js +37 -29
- package/package.json +6 -6
- package/types/index.d.ts +36 -0
- package/upload/index.js +173 -31
package/README.md
CHANGED
package/client/index.js
CHANGED
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
exports.getProjectId = exports.createBucketRequest = exports.setPredefinedAcl = exports.setPublicRead = exports.executeBatchQuery = exports.executeQuery = exports.deleteObjectsV2 = exports.deleteObjects = exports.setBucketWebsite = exports.setBucketPolicy = exports.createBucketV2 = exports.createBucket = exports.createDatabaseClient = exports.createStorageClient = exports.validateDatabase = exports.validateStorage = exports.isFirebaseApp = void 0;
|
|
2
|
+
exports.CLOUD_DOWNLOAD_CHUNK = exports.CLOUD_UPLOAD_CHUNK = exports.CLOUD_UPLOAD_STREAM = exports.getProjectId = exports.createBucketRequest = exports.setPredefinedAcl = exports.setPublicRead = exports.executeBatchQuery = exports.executeQuery = exports.deleteObjectsV2 = exports.deleteObjects = exports.setBucketWebsite = exports.setBucketTagging = exports.setBucketPolicy = exports.createBucketV2 = exports.createBucket = exports.createDatabaseClient = exports.createStorageClient = exports.validateDatabase = exports.validateStorage = exports.isFirebaseApp = void 0;
|
|
3
3
|
const path = require("path");
|
|
4
4
|
const storage = require("@google-cloud/storage");
|
|
5
5
|
const firestore_1 = require("@google-cloud/firestore");
|
|
6
6
|
const util_1 = require("@e-mc/cloud/util");
|
|
7
7
|
const types_1 = require("@e-mc/types");
|
|
8
|
-
const Module = require("@e-mc/module");
|
|
9
8
|
const Cloud = require("@e-mc/cloud");
|
|
10
9
|
const FIREBASE_DB = {};
|
|
11
10
|
const FIREBASE_STORAGE = {};
|
|
12
11
|
const ADMIN_DB = {};
|
|
13
12
|
const ADMIN_STORAGE = {};
|
|
14
13
|
function getApp(credential, isDB) {
|
|
15
|
-
var _a, _b;
|
|
16
14
|
const { projectId, databaseURL, storageBucket, apiKey, authDomain } = credential;
|
|
17
15
|
if (apiKey && authDomain) {
|
|
18
16
|
const { getApps, initializeApp } = require('@firebase/app');
|
|
@@ -26,7 +24,7 @@ function getApp(credential, isDB) {
|
|
|
26
24
|
}
|
|
27
25
|
if (!app) {
|
|
28
26
|
const firebaseKey = (projectId || '') + '_' + apiKey + '_' + authDomain + (isDB ? '_' + databaseURL : '');
|
|
29
|
-
app = isDB ? FIREBASE_DB[firebaseKey]
|
|
27
|
+
app = isDB ? FIREBASE_DB[firebaseKey] ||= initializeApp({ ...credential, databaseURL }, firebaseKey) : FIREBASE_STORAGE[firebaseKey] ||= initializeApp({ ...credential, storageBucket }, firebaseKey);
|
|
30
28
|
}
|
|
31
29
|
if (isDB) {
|
|
32
30
|
const { getDatabase, goOnline } = require('@firebase/database');
|
|
@@ -47,7 +45,7 @@ function getApp(credential, isDB) {
|
|
|
47
45
|
}
|
|
48
46
|
}
|
|
49
47
|
if (!app) {
|
|
50
|
-
app = isDB ? ADMIN_DB[
|
|
48
|
+
app = isDB ? ADMIN_DB[databaseURL || "[DEFAULT]"] ||= initializeApp({ credential: applicationDefault(), databaseURL }, databaseURL) : ADMIN_STORAGE[storageBucket || "[DEFAULT]"] ||= initializeApp({ credential: applicationDefault(), storageBucket }, storageBucket);
|
|
51
49
|
}
|
|
52
50
|
credential.admin = true;
|
|
53
51
|
if (isDB) {
|
|
@@ -59,7 +57,7 @@ function getApp(credential, isDB) {
|
|
|
59
57
|
}
|
|
60
58
|
}
|
|
61
59
|
async function hasBucket(client) {
|
|
62
|
-
const [exists] = await client.exists().catch(err => isErrorConflict(err) ? [true] : [false]);
|
|
60
|
+
const [exists] = await client.exists().catch((err) => isErrorConflict(err) ? [true] : [false]);
|
|
63
61
|
return exists;
|
|
64
62
|
}
|
|
65
63
|
async function updatePredefinedAcl(acl, values, metadata = {}) {
|
|
@@ -69,7 +67,7 @@ async function updatePredefinedAcl(acl, values, metadata = {}) {
|
|
|
69
67
|
let defaultObjectAcl;
|
|
70
68
|
({ projectNumber, defaultObjectAcl } = metadata);
|
|
71
69
|
if (projectNumber === undefined && (0, types_1.isArray)(defaultObjectAcl)) {
|
|
72
|
-
const target = defaultObjectAcl.find(item =>
|
|
70
|
+
const target = defaultObjectAcl.find(item => item.projectTeam?.projectNumber);
|
|
73
71
|
if (target !== undefined) {
|
|
74
72
|
projectNumber = target.projectTeam.projectNumber;
|
|
75
73
|
}
|
|
@@ -83,7 +81,7 @@ async function updatePredefinedAcl(acl, values, metadata = {}) {
|
|
|
83
81
|
}
|
|
84
82
|
if ((0, types_1.isString)(value.role)) {
|
|
85
83
|
if (projectNumber !== undefined) {
|
|
86
|
-
value.userProject
|
|
84
|
+
value.userProject ||= projectNumber.toString();
|
|
87
85
|
}
|
|
88
86
|
return target.add(value);
|
|
89
87
|
}
|
|
@@ -99,25 +97,25 @@ async function updatePredefinedAcl(acl, values, metadata = {}) {
|
|
|
99
97
|
}
|
|
100
98
|
return errors;
|
|
101
99
|
})
|
|
102
|
-
.catch(err => [err]);
|
|
100
|
+
.catch((err) => [err]);
|
|
103
101
|
}
|
|
104
102
|
async function setUniformBucketLevel(bucket, enabled) {
|
|
105
103
|
return bucket.setMetadata({ iamConfiguration: { uniformBucketLevelAccess: { enabled } } });
|
|
106
104
|
}
|
|
105
|
+
const asFieldPath = (params) => Array.isArray(params) && params.every(item => typeof item === 'string') ? new firestore_1.FieldPath(...params) : params;
|
|
107
106
|
const getFirebasePackageName = (credential) => credential.apiKey && credential.authDomain ? 'firebase' : 'firebase-admin';
|
|
108
|
-
const hasKeyFile = (credential) => (0, types_1.isString)(credential.keyFilename || credential.keyFile || process.env.GOOGLE_APPLICATION_CREDENTIALS) || (0, types_1.isObject)(credential.credentials);
|
|
109
107
|
const isErrorConflict = (err) => (err instanceof Error) && err.code === 409;
|
|
108
|
+
const hasGoogleAuth = (credential) => (0, types_1.isString)(credential.keyFilename || credential.keyFile || process.env.GOOGLE_APPLICATION_CREDENTIALS) || (0, types_1.isObject)(credential.credentials) || !!credential.authClient;
|
|
110
109
|
function isFirebaseApp(credential, store, admin) {
|
|
111
|
-
return !!(credential.apiKey && credential.authDomain && (store || credential.databaseURL) || credential.product ===
|
|
110
|
+
return !!(credential.apiKey && credential.authDomain && (store || credential.databaseURL) || credential.product === "firebase" && process.env.GOOGLE_APPLICATION_CREDENTIALS) && (!admin || !credential.admin);
|
|
112
111
|
}
|
|
113
112
|
exports.isFirebaseApp = isFirebaseApp;
|
|
114
113
|
function validateStorage(credential) {
|
|
115
|
-
return
|
|
114
|
+
return hasGoogleAuth(credential) || isFirebaseApp(credential, true);
|
|
116
115
|
}
|
|
117
116
|
exports.validateStorage = validateStorage;
|
|
118
117
|
function validateDatabase(credential, data) {
|
|
119
|
-
|
|
120
|
-
return (hasKeyFile(credential) || !!credential.authClient) && !!(data.product || data.database || data.table || data.query || data.id || data.keys || data.kind || ((_a = data.options) === null || _a === void 0 ? void 0 : _a.query)) || isFirebaseApp(credential);
|
|
118
|
+
return hasGoogleAuth(credential) && !!(data.product || data.database || data.table || data.query || data.id || data.keys || data.kind || data.options?.query) || isFirebaseApp(credential);
|
|
121
119
|
}
|
|
122
120
|
exports.validateDatabase = validateDatabase;
|
|
123
121
|
function createStorageClient(credential, bucket) {
|
|
@@ -142,29 +140,26 @@ exports.createStorageClient = createStorageClient;
|
|
|
142
140
|
function createDatabaseClient(credential, data) {
|
|
143
141
|
let packageName = '';
|
|
144
142
|
try {
|
|
145
|
-
|
|
146
|
-
if (data) {
|
|
147
|
-
({ product, name, table, query, database } = data);
|
|
148
|
-
}
|
|
149
|
-
if (product === 'firebase' || isFirebaseApp(credential)) {
|
|
143
|
+
if (data?.product === "firebase" || isFirebaseApp(credential)) {
|
|
150
144
|
packageName = getFirebasePackageName(credential);
|
|
151
145
|
return getApp(credential, true);
|
|
152
146
|
}
|
|
153
147
|
getProjectId(credential, true);
|
|
154
148
|
if (data) {
|
|
155
|
-
|
|
149
|
+
const { product, name, table, query, database } = data;
|
|
150
|
+
if (product === "spanner" || !product && name && database && (query || table)) {
|
|
156
151
|
const { Spanner } = require(packageName = '@google-cloud/spanner');
|
|
157
152
|
return new Spanner(credential);
|
|
158
153
|
}
|
|
159
|
-
if (product ===
|
|
154
|
+
if (product === "bigquery" || !product && (!name && table && database || (0, types_1.isString)(query))) {
|
|
160
155
|
const { BigQuery } = require(packageName = '@google-cloud/bigquery');
|
|
161
156
|
return new BigQuery(credential);
|
|
162
157
|
}
|
|
163
|
-
if (product ===
|
|
158
|
+
if (product === "bigtable" || !product && name && table && !database) {
|
|
164
159
|
const { Bigtable } = require(packageName = '@google-cloud/bigtable');
|
|
165
160
|
return new Bigtable(credential);
|
|
166
161
|
}
|
|
167
|
-
if (product ===
|
|
162
|
+
if (product === "datastore" || !product && (data.keys || data.kind || query && !table && !data.id)) {
|
|
168
163
|
const { Datastore } = require(packageName = '@google-cloud/datastore');
|
|
169
164
|
return new Datastore(credential);
|
|
170
165
|
}
|
|
@@ -210,7 +205,6 @@ async function createBucketV2(credential, bucket, acl, options, service = 'gcp')
|
|
|
210
205
|
}
|
|
211
206
|
exports.createBucketV2 = createBucketV2;
|
|
212
207
|
async function setBucketPolicy(credential, bucket, options, service = 'gcp') {
|
|
213
|
-
var _a;
|
|
214
208
|
if (isFirebaseApp(credential, true, true)) {
|
|
215
209
|
return false;
|
|
216
210
|
}
|
|
@@ -224,32 +218,32 @@ async function setBucketPolicy(credential, bucket, options, service = 'gcp') {
|
|
|
224
218
|
if (!Array.isArray(options.acl)) {
|
|
225
219
|
options.acl = undefined;
|
|
226
220
|
switch (acl) {
|
|
227
|
-
case
|
|
228
|
-
case
|
|
229
|
-
await client.makePrivate(options).catch(err => !isErrorConflict(err) && errors.push(err));
|
|
230
|
-
await setPredefinedAcl.call(this, client.acl, acl, client, '',
|
|
221
|
+
case "private":
|
|
222
|
+
case "projectPrivate":
|
|
223
|
+
await client.makePrivate(options).catch((err) => !isErrorConflict(err) && errors.push(err));
|
|
224
|
+
await setPredefinedAcl.call(this, client.acl, acl, client, '', options.bucket?.metadata).catch((err) => errors.push(err));
|
|
231
225
|
break;
|
|
232
|
-
case
|
|
233
|
-
await client.makePublic(options).catch(err => !isErrorConflict(err) && errors.push(err));
|
|
226
|
+
case "authenticatedRead":
|
|
227
|
+
await client.makePublic(options).catch((err) => !isErrorConflict(err) && errors.push(err));
|
|
234
228
|
options = { ...options };
|
|
235
229
|
options.acl = [{ entity: 'allUsers' }, { entity: 'allAuthenticatedUsers', role: 'READER' }];
|
|
236
230
|
break;
|
|
237
|
-
case
|
|
238
|
-
await client.makePublic(options).catch(err => !isErrorConflict(err) && errors.push(err));
|
|
231
|
+
case "publicReadWrite":
|
|
232
|
+
await client.makePublic(options).catch((err) => !isErrorConflict(err) && errors.push(err));
|
|
239
233
|
options = { ...options };
|
|
240
234
|
options.acl = [{ entity: 'allUsers', role: 'WRITER' }];
|
|
241
235
|
break;
|
|
242
|
-
case
|
|
243
|
-
await client.makePublic(options).catch(err => !isErrorConflict(err) && errors.push(err));
|
|
236
|
+
case "publicRead":
|
|
237
|
+
await client.makePublic(options).catch((err) => !isErrorConflict(err) && errors.push(err));
|
|
244
238
|
break;
|
|
245
239
|
case 'bucketAccessUniform':
|
|
246
|
-
await setUniformBucketLevel(client, true).catch(err => errors.push(err));
|
|
240
|
+
await setUniformBucketLevel(client, true).catch((err) => errors.push(err));
|
|
247
241
|
break;
|
|
248
242
|
case 'bucketAccessACL':
|
|
249
|
-
await setUniformBucketLevel(client, false).catch(err => errors.push(err));
|
|
243
|
+
await setUniformBucketLevel(client, false).catch((err) => errors.push(err));
|
|
250
244
|
break;
|
|
251
245
|
default:
|
|
252
|
-
errors.push((0, types_1.errorValue)('Invalid ACL',
|
|
246
|
+
errors.push((0, types_1.errorValue)('Invalid ACL', Cloud.asString(acl) || 'empty'));
|
|
253
247
|
break;
|
|
254
248
|
}
|
|
255
249
|
}
|
|
@@ -264,6 +258,33 @@ async function setBucketPolicy(credential, bucket, options, service = 'gcp') {
|
|
|
264
258
|
return true;
|
|
265
259
|
}
|
|
266
260
|
exports.setBucketPolicy = setBucketPolicy;
|
|
261
|
+
async function setBucketTagging(credential, bucket, labels, service = 'gcp') {
|
|
262
|
+
if (isFirebaseApp(credential, true, true) || !(0, types_1.isPlainObject)(labels)) {
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
const client = createStorageClient.call(this, credential, bucket).bucket(bucket);
|
|
266
|
+
const deleting = Object.keys(labels).length === 0;
|
|
267
|
+
if (deleting) {
|
|
268
|
+
const [metadata] = await client.getMetadata();
|
|
269
|
+
if (!(0, types_1.isPlainObject)(metadata.labels)) {
|
|
270
|
+
return true;
|
|
271
|
+
}
|
|
272
|
+
labels = {};
|
|
273
|
+
for (const key in metadata.labels) {
|
|
274
|
+
labels[key] = null;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return client.setMetadata({ labels })
|
|
278
|
+
.then(([metadata]) => {
|
|
279
|
+
this.formatMessage(64, service, [deleting ? "Tags deleted" : "Tags created", bucket], ((0, types_1.isPlainObject)(metadata.labels) ? Object.keys(metadata.labels).length : 0) + ' labels (current)', { ...Cloud.LOG_CLOUD_COMMAND });
|
|
280
|
+
return true;
|
|
281
|
+
})
|
|
282
|
+
.catch((err) => {
|
|
283
|
+
this.formatFail(64, service, ["Unable to update bucket tagging", bucket], err, { ...Cloud.LOG_CLOUD_FAIL, fatal: false });
|
|
284
|
+
return false;
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
exports.setBucketTagging = setBucketTagging;
|
|
267
288
|
async function setBucketWebsite(credential, bucket, options, service = 'gcp') {
|
|
268
289
|
if (isFirebaseApp(credential, true, true)) {
|
|
269
290
|
return false;
|
|
@@ -278,11 +299,11 @@ async function setBucketWebsite(credential, bucket, options, service = 'gcp') {
|
|
|
278
299
|
}
|
|
279
300
|
return createStorageClient.call(this, credential, bucket).bucket(bucket).setMetadata({ website })
|
|
280
301
|
.then(() => {
|
|
281
|
-
this.formatMessage(64, service, ["Bucket configured", bucket], website, { ...Cloud.LOG_CLOUD_COMMAND });
|
|
302
|
+
this.formatMessage(64, service, ["Bucket website configured", bucket], website, { ...Cloud.LOG_CLOUD_COMMAND });
|
|
282
303
|
return true;
|
|
283
304
|
})
|
|
284
|
-
.catch(err => {
|
|
285
|
-
this.formatFail(64, service, ["Unable to
|
|
305
|
+
.catch((err) => {
|
|
306
|
+
this.formatFail(64, service, ["Unable to set bucket website", bucket], err, { ...Cloud.LOG_CLOUD_FAIL, fatal: false });
|
|
286
307
|
return false;
|
|
287
308
|
});
|
|
288
309
|
}
|
|
@@ -306,7 +327,7 @@ async function deleteObjectsV2(credential, bucket, recursive = true, service = '
|
|
|
306
327
|
}
|
|
307
328
|
if (recursive && prefixes.length) {
|
|
308
329
|
for (const { name } of prefixes) {
|
|
309
|
-
await recurse(
|
|
330
|
+
await recurse(Cloud.joinPath(prefix, name));
|
|
310
331
|
}
|
|
311
332
|
}
|
|
312
333
|
}
|
|
@@ -329,7 +350,7 @@ async function deleteObjectsV2(credential, bucket, recursive = true, service = '
|
|
|
329
350
|
const result = await client.getFiles();
|
|
330
351
|
for (const { name } of result[0]) {
|
|
331
352
|
if (!name.includes('/')) {
|
|
332
|
-
tasks.push(client.file(name).delete({ ignoreNotFound: true }).catch(err => this.formatFail(64, service, ["Delete failed", name], err, { ...Cloud.LOG_CLOUD_FAIL, fatal: false })));
|
|
353
|
+
tasks.push(client.file(name).delete({ ignoreNotFound: true }).catch((err) => this.formatFail(64, service, ["Delete failed", name], err, { ...Cloud.LOG_CLOUD_FAIL, fatal: false })));
|
|
333
354
|
}
|
|
334
355
|
}
|
|
335
356
|
}
|
|
@@ -356,7 +377,7 @@ async function deleteObjectsV2(credential, bucket, recursive = true, service = '
|
|
|
356
377
|
this.formatMessage(64, service, ["Bucket emptied" + ` (${recursive ? 'recursive' : files})`, bucket], recursive ? files : '', { ...Cloud.LOG_CLOUD_COMMAND });
|
|
357
378
|
}
|
|
358
379
|
})
|
|
359
|
-
.catch(err => {
|
|
380
|
+
.catch((err) => {
|
|
360
381
|
errors.push(err);
|
|
361
382
|
});
|
|
362
383
|
}
|
|
@@ -368,10 +389,9 @@ async function executeQuery(credential, data, sessionKey) {
|
|
|
368
389
|
}
|
|
369
390
|
exports.executeQuery = executeQuery;
|
|
370
391
|
async function executeBatchQuery(credential, batch, sessionKey) {
|
|
371
|
-
var _a;
|
|
372
392
|
const length = batch.length;
|
|
373
393
|
const result = new Array(length);
|
|
374
|
-
const clients =
|
|
394
|
+
const clients = {};
|
|
375
395
|
const caching = length > 0 && this.hasCache(batch[0].service, sessionKey);
|
|
376
396
|
const cacheValue = { value: this.valueOfKey(credential, 'cache'), sessionKey };
|
|
377
397
|
const authItems = [];
|
|
@@ -386,7 +406,14 @@ async function executeBatchQuery(credential, batch, sessionKey) {
|
|
|
386
406
|
};
|
|
387
407
|
const closeClient = () => {
|
|
388
408
|
const fs = clients[3];
|
|
389
|
-
|
|
409
|
+
if (fs) {
|
|
410
|
+
fs.terminate();
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
const sp = clients[5];
|
|
414
|
+
if (sp) {
|
|
415
|
+
sp.close();
|
|
416
|
+
}
|
|
390
417
|
};
|
|
391
418
|
const getFamily = (data, row, family) => {
|
|
392
419
|
const familyData = data[family];
|
|
@@ -419,14 +446,14 @@ async function executeBatchQuery(credential, batch, sessionKey) {
|
|
|
419
446
|
};
|
|
420
447
|
cacheValue.exclusiveOf = Array.isArray(ignoreCache) ? ignoreCache : undefined;
|
|
421
448
|
let rows, queryString = '';
|
|
422
|
-
if (product ===
|
|
449
|
+
if (product === "firebase" || isFirebaseApp(credential)) {
|
|
423
450
|
if (!(0, types_1.isString)(query)) {
|
|
424
451
|
closeClient();
|
|
425
452
|
throw (0, util_1.formatError)(item, "Missing database query");
|
|
426
453
|
}
|
|
427
454
|
const orderBy = item.orderBy;
|
|
428
455
|
if (useCache) {
|
|
429
|
-
queryString = query +
|
|
456
|
+
queryString = query + Cloud.asString(orderBy, true);
|
|
430
457
|
if (!update && (rows = getCache(queryString))) {
|
|
431
458
|
result[i] = rows;
|
|
432
459
|
continue;
|
|
@@ -475,7 +502,7 @@ async function executeBatchQuery(credential, batch, sessionKey) {
|
|
|
475
502
|
rows = [group && (0, types_1.isObject)(val) && (0, types_1.isObject)(val[group[0]]) ? val[group[0]] : val];
|
|
476
503
|
}
|
|
477
504
|
}
|
|
478
|
-
else if (product ===
|
|
505
|
+
else if (product === "spanner" || !product && name && database && (query || table)) {
|
|
479
506
|
const columns = (0, types_1.isPlainObject)(query) && query.columns || item.columns;
|
|
480
507
|
if (!(query || table && columns)) {
|
|
481
508
|
closeClient();
|
|
@@ -487,7 +514,7 @@ async function executeBatchQuery(credential, batch, sessionKey) {
|
|
|
487
514
|
}
|
|
488
515
|
const { updateType, options = {} } = item;
|
|
489
516
|
if (useCache) {
|
|
490
|
-
queryString = name + '_' + database + '_' +
|
|
517
|
+
queryString = name + '_' + database + (table ? '_' + table + (columns ? Cloud.asString(columns, true) : '') : '') + '_' + Cloud.asString(query, true) + (limit > 0 ? limit : '');
|
|
491
518
|
if (!update && (rows = getCache(queryString))) {
|
|
492
519
|
result[i] = rows;
|
|
493
520
|
continue;
|
|
@@ -518,8 +545,12 @@ async function executeBatchQuery(credential, batch, sessionKey) {
|
|
|
518
545
|
}
|
|
519
546
|
else {
|
|
520
547
|
transaction.runUpdate(update)
|
|
521
|
-
.then(async () =>
|
|
522
|
-
.
|
|
548
|
+
.then(async () => {
|
|
549
|
+
transaction.commit()
|
|
550
|
+
.then(() => resolve())
|
|
551
|
+
.catch((error) => reject(error));
|
|
552
|
+
})
|
|
553
|
+
.catch((error) => reject(error));
|
|
523
554
|
}
|
|
524
555
|
});
|
|
525
556
|
});
|
|
@@ -527,12 +558,12 @@ async function executeBatchQuery(credential, batch, sessionKey) {
|
|
|
527
558
|
const request = (0, types_1.isPlainObject)(query) ? query : { sql: query };
|
|
528
559
|
request.json = true;
|
|
529
560
|
if (limit > 0) {
|
|
530
|
-
(request.gaxOptions
|
|
561
|
+
(request.gaxOptions ||= {}).maxResults = limit;
|
|
531
562
|
}
|
|
532
563
|
[rows] = await db.run(request);
|
|
533
564
|
}
|
|
534
565
|
}
|
|
535
|
-
else if (product ===
|
|
566
|
+
else if (product === "bigtable" || !product && name && table && !database) {
|
|
536
567
|
if (!name || !table) {
|
|
537
568
|
closeClient();
|
|
538
569
|
throw (0, util_1.formatError)(item, !name ? "Missing database name" : "Missing database table");
|
|
@@ -542,10 +573,10 @@ async function executeBatchQuery(credential, batch, sessionKey) {
|
|
|
542
573
|
options.filter = query;
|
|
543
574
|
}
|
|
544
575
|
if (!id && limit > 0) {
|
|
545
|
-
(options.gaxOptions
|
|
576
|
+
(options.gaxOptions ||= {}).maxResults = limit;
|
|
546
577
|
}
|
|
547
578
|
if (useCache) {
|
|
548
|
-
queryString = name + '_' + table +
|
|
579
|
+
queryString = name + '_' + table + ((0, types_1.isString)(id) ? '_' + id + ((0, types_1.isArray)(columns) ? '_' + Cloud.asString(columns, true) : '') : '') + Cloud.asString(options, true);
|
|
549
580
|
if (!(update && id) && (rows = getCache(queryString))) {
|
|
550
581
|
result[i] = rows;
|
|
551
582
|
continue;
|
|
@@ -576,7 +607,7 @@ async function executeBatchQuery(credential, batch, sessionKey) {
|
|
|
576
607
|
});
|
|
577
608
|
}
|
|
578
609
|
}
|
|
579
|
-
else if (product ===
|
|
610
|
+
else if (product === "bigquery" || !product && (!name && database && table || (0, types_1.isString)(query) || (0, types_1.isString)(item.options?.query))) {
|
|
580
611
|
const { params, options = {} } = item;
|
|
581
612
|
if ((0, types_1.isString)(query)) {
|
|
582
613
|
options.query = query;
|
|
@@ -588,7 +619,7 @@ async function executeBatchQuery(credential, batch, sessionKey) {
|
|
|
588
619
|
options.maxResults = limit;
|
|
589
620
|
}
|
|
590
621
|
if (useCache) {
|
|
591
|
-
queryString = query + (database ? '_' + database + (table ? '_' + table : '') : '') +
|
|
622
|
+
queryString = query + (database ? '_' + database + (table ? '_' + table : '') : '') + Cloud.asString(options, true);
|
|
592
623
|
if (!update && (rows = getCache(queryString))) {
|
|
593
624
|
result[i] = rows;
|
|
594
625
|
continue;
|
|
@@ -615,14 +646,14 @@ async function executeBatchQuery(credential, batch, sessionKey) {
|
|
|
615
646
|
}
|
|
616
647
|
[rows] = await job.getQueryResults();
|
|
617
648
|
}
|
|
618
|
-
else if (product ===
|
|
619
|
-
const { keys, kind } = item;
|
|
649
|
+
else if (product === "datastore" || !product && (item.keys || item.kind || query && !table && !id)) {
|
|
650
|
+
const { keys, kind, options } = item;
|
|
620
651
|
if (!keys && !kind && !Array.isArray(query)) {
|
|
621
652
|
closeClient();
|
|
622
653
|
throw (0, util_1.formatError)(item, "Missing database query");
|
|
623
654
|
}
|
|
624
655
|
if (useCache) {
|
|
625
|
-
queryString = (keys ? Cloud.asString(keys, true) : (kind ? (name ?
|
|
656
|
+
queryString = (keys ? Cloud.asString(keys, true) : (kind ? (name ? '_' + name : '') + '_' + Cloud.asString(kind, true) : '') + '_' + Cloud.asString(query, true)) + (limit > 0 ? '_' + limit : 0) + (options ? Cloud.asString(options, true) : '');
|
|
626
657
|
if (!update && (rows = getCache(queryString))) {
|
|
627
658
|
result[i] = rows;
|
|
628
659
|
continue;
|
|
@@ -634,7 +665,7 @@ async function executeBatchQuery(credential, batch, sessionKey) {
|
|
|
634
665
|
if ((0, types_1.isPlainObject)(update)) {
|
|
635
666
|
await db.save({ key: items, data: update });
|
|
636
667
|
}
|
|
637
|
-
rows = await db.get(items,
|
|
668
|
+
rows = await db.get(items, options);
|
|
638
669
|
if (limit > 0 && rows.length > limit) {
|
|
639
670
|
rows = rows.slice(0, limit);
|
|
640
671
|
}
|
|
@@ -668,29 +699,30 @@ async function executeBatchQuery(credential, batch, sessionKey) {
|
|
|
668
699
|
}
|
|
669
700
|
if ((0, types_1.isPlainObject)(update)) {
|
|
670
701
|
await new Promise((resolve, reject) => {
|
|
671
|
-
db.runQuery(task.select('__key__'),
|
|
702
|
+
db.runQuery(task.select('__key__'), options).then(entities => {
|
|
672
703
|
if ((0, types_1.isArray)(entities)) {
|
|
673
704
|
const items = entities.map((entity) => db.key(entity[db.KEY]));
|
|
674
|
-
db.save({ key: items, data: update }).then(() => resolve()).catch(err => reject(err));
|
|
705
|
+
db.save({ key: items, data: update }).then(() => resolve()).catch((err) => reject(err));
|
|
675
706
|
}
|
|
676
707
|
else {
|
|
677
708
|
resolve();
|
|
678
709
|
}
|
|
679
710
|
})
|
|
680
|
-
.catch(err => reject(err));
|
|
711
|
+
.catch((err) => reject(err));
|
|
681
712
|
});
|
|
682
713
|
}
|
|
683
|
-
[rows] = await db.runQuery(task,
|
|
714
|
+
[rows] = await db.runQuery(task, options);
|
|
684
715
|
}
|
|
685
716
|
}
|
|
686
|
-
else if (product ===
|
|
717
|
+
else if (product === "firestore" || !product && table) {
|
|
687
718
|
if (!table) {
|
|
688
719
|
closeClient();
|
|
689
720
|
throw (0, util_1.formatError)(item, "Missing database table");
|
|
690
721
|
}
|
|
691
722
|
if (id) {
|
|
723
|
+
const options = Array.isArray(id) ? item.options : null;
|
|
692
724
|
if (useCache) {
|
|
693
|
-
queryString = table + '_' + id;
|
|
725
|
+
queryString = table + '_' + id + (options ? Cloud.asString(options, true) : '');
|
|
694
726
|
if ((!update || Array.isArray(id)) && (rows = getCache(queryString))) {
|
|
695
727
|
result[i] = rows;
|
|
696
728
|
continue;
|
|
@@ -699,15 +731,42 @@ async function executeBatchQuery(credential, batch, sessionKey) {
|
|
|
699
731
|
const db = createClient(item, 3);
|
|
700
732
|
if (Array.isArray(id)) {
|
|
701
733
|
const items = id.map(ref => db.doc(table + '/' + ref));
|
|
702
|
-
if (
|
|
703
|
-
items.push(
|
|
734
|
+
if (options) {
|
|
735
|
+
items.push(options);
|
|
704
736
|
}
|
|
705
737
|
rows = (await db.getAll(...items)).filter(value => value.exists).map(value => value.data());
|
|
706
738
|
}
|
|
707
739
|
else {
|
|
708
740
|
const doc = db.collection(table).doc(id);
|
|
709
741
|
if ((0, types_1.isPlainObject)(update)) {
|
|
710
|
-
|
|
742
|
+
const pattern = /^__(delete|serverTimestamp|increment)(?:<(.+)>)?__$/;
|
|
743
|
+
for (const attr in update) {
|
|
744
|
+
const match = pattern.exec(attr);
|
|
745
|
+
if (match) {
|
|
746
|
+
const method = match[1];
|
|
747
|
+
switch (method) {
|
|
748
|
+
case 'delete':
|
|
749
|
+
case 'serverTimestamp':
|
|
750
|
+
update[attr] = firestore_1.FieldValue[method]();
|
|
751
|
+
break;
|
|
752
|
+
case 'increment':
|
|
753
|
+
update[attr] = firestore_1.FieldValue.increment(match[2] ? +match[2] : 1);
|
|
754
|
+
break;
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
switch (item.updateType) {
|
|
759
|
+
case 1:
|
|
760
|
+
await doc.create(update);
|
|
761
|
+
break;
|
|
762
|
+
case 2:
|
|
763
|
+
case 3:
|
|
764
|
+
await doc.set(update, { merge: item.updateType === 3 });
|
|
765
|
+
break;
|
|
766
|
+
default:
|
|
767
|
+
await doc.update(update);
|
|
768
|
+
break;
|
|
769
|
+
}
|
|
711
770
|
}
|
|
712
771
|
const data = (await doc.get()).data();
|
|
713
772
|
if (data) {
|
|
@@ -717,7 +776,7 @@ async function executeBatchQuery(credential, batch, sessionKey) {
|
|
|
717
776
|
}
|
|
718
777
|
else if (Array.isArray(query)) {
|
|
719
778
|
const orderBy = item.orderBy;
|
|
720
|
-
if (useCache && (rows = getCache(queryString = table + '_' +
|
|
779
|
+
if (useCache && (rows = getCache(queryString = table + '_' + Cloud.asString(query, true) + '_' + Cloud.asString(orderBy, true) + (limit > 0 ? '_' + limit : '')))) {
|
|
721
780
|
result[i] = rows;
|
|
722
781
|
continue;
|
|
723
782
|
}
|
|
@@ -729,33 +788,41 @@ async function executeBatchQuery(credential, batch, sessionKey) {
|
|
|
729
788
|
switch (method) {
|
|
730
789
|
case 'whereAnd':
|
|
731
790
|
case 'whereOr':
|
|
732
|
-
if (params.every(p =>
|
|
733
|
-
db = db.where(firestore_1.Filter[method === 'whereAnd' ? 'and' : 'or'](...params.map((p) => firestore_1.Filter.where(p[0], p[1], p[2]))));
|
|
734
|
-
}
|
|
735
|
-
else {
|
|
791
|
+
if (!params.every(p => (0, types_1.isArray)(p))) {
|
|
736
792
|
throw (0, types_1.errorMessage)(method, 'Conditionals are not in array format', Cloud.asString(params));
|
|
737
793
|
}
|
|
794
|
+
db = db.where(firestore_1.Filter[method === 'whereAnd' ? 'and' : 'or'](...params.map((p) => firestore_1.Filter.where(p[0], p[1], p[2]))));
|
|
795
|
+
break;
|
|
796
|
+
case 'where':
|
|
797
|
+
case 'orderBy':
|
|
798
|
+
params[0] = asFieldPath(params[0]);
|
|
799
|
+
db = db[method].apply(db, params);
|
|
800
|
+
case 'select':
|
|
801
|
+
params.forEach((field, index) => {
|
|
802
|
+
params[index] = asFieldPath(field);
|
|
803
|
+
});
|
|
804
|
+
db = db.select.apply(db, params);
|
|
738
805
|
break;
|
|
739
806
|
case 'endAt':
|
|
740
807
|
case 'endBefore':
|
|
741
808
|
case 'limit':
|
|
742
809
|
case 'limitToLast':
|
|
743
810
|
case 'offset':
|
|
744
|
-
case 'orderBy':
|
|
745
|
-
case 'select':
|
|
746
811
|
case 'startAfter':
|
|
747
812
|
case 'startAt':
|
|
748
|
-
case 'where':
|
|
749
813
|
case 'withConverter':
|
|
750
814
|
db = db[method].apply(db, params);
|
|
751
815
|
break;
|
|
752
816
|
}
|
|
753
817
|
}
|
|
754
818
|
}
|
|
755
|
-
if (
|
|
819
|
+
if (Array.isArray(orderBy)) {
|
|
756
820
|
for (const order of orderBy) {
|
|
757
821
|
if ((0, types_1.isArray)(order)) {
|
|
758
|
-
db = db.orderBy(order[0], order[1]);
|
|
822
|
+
db = db.orderBy(asFieldPath(order[0]), order[1]);
|
|
823
|
+
}
|
|
824
|
+
else if ((0, types_1.isString)(order)) {
|
|
825
|
+
db = db.orderBy(order);
|
|
759
826
|
}
|
|
760
827
|
}
|
|
761
828
|
}
|
|
@@ -786,7 +853,7 @@ function setPublicRead(acl, pathname, requested, service = 'gcp') {
|
|
|
786
853
|
this.formatMessage(64, service, "Grant public-read", pathname, { ...Cloud.LOG_CLOUD_COMMAND });
|
|
787
854
|
}
|
|
788
855
|
})
|
|
789
|
-
.catch(err => {
|
|
856
|
+
.catch((err) => {
|
|
790
857
|
if (requested) {
|
|
791
858
|
this.formatMessage(64, service, ["Unable to grant public-read", pathname], err, { ...Cloud.LOG_CLOUD_WARN });
|
|
792
859
|
}
|
|
@@ -794,7 +861,6 @@ function setPublicRead(acl, pathname, requested, service = 'gcp') {
|
|
|
794
861
|
}
|
|
795
862
|
exports.setPublicRead = setPublicRead;
|
|
796
863
|
async function setPredefinedAcl(target, value, bucket, pathname = '', metadata = {}, requested = true, service = 'gcp') {
|
|
797
|
-
var _a;
|
|
798
864
|
let revoke;
|
|
799
865
|
if (metadata === true) {
|
|
800
866
|
revoke = true;
|
|
@@ -809,7 +875,7 @@ async function setPredefinedAcl(target, value, bucket, pathname = '', metadata =
|
|
|
809
875
|
items = [{ entity: 'allUsers' }, { entity: 'allAuthenticatedUsers', role: revoke ? undefined : 'READER' }];
|
|
810
876
|
break;
|
|
811
877
|
default: {
|
|
812
|
-
const owner =
|
|
878
|
+
const owner = metadata.owner?.entity;
|
|
813
879
|
let acl = metadata.acl;
|
|
814
880
|
if ((0, types_1.isArray)(acl)) {
|
|
815
881
|
items = [];
|
|
@@ -819,7 +885,7 @@ async function setPredefinedAcl(target, value, bucket, pathname = '', metadata =
|
|
|
819
885
|
items = [{ entity: 'allUsers' }, { entity: 'allAuthenticatedUsers' }];
|
|
820
886
|
}
|
|
821
887
|
switch (value) {
|
|
822
|
-
case
|
|
888
|
+
case "private": {
|
|
823
889
|
const owners = new Set(acl.filter(item => item.role === 'OWNER' && item.entity).map(item => item.entity));
|
|
824
890
|
if (owner) {
|
|
825
891
|
owners.add(owner);
|
|
@@ -834,7 +900,7 @@ async function setPredefinedAcl(target, value, bucket, pathname = '', metadata =
|
|
|
834
900
|
}
|
|
835
901
|
break;
|
|
836
902
|
}
|
|
837
|
-
case
|
|
903
|
+
case "projectPrivate":
|
|
838
904
|
for (const user of acl) {
|
|
839
905
|
const entity = user.entity;
|
|
840
906
|
if (entity) {
|
|
@@ -843,7 +909,7 @@ async function setPredefinedAcl(target, value, bucket, pathname = '', metadata =
|
|
|
843
909
|
}
|
|
844
910
|
}
|
|
845
911
|
break;
|
|
846
|
-
case
|
|
912
|
+
case "bucketOwnerRead":
|
|
847
913
|
if (bucket && owner) {
|
|
848
914
|
for (const user of acl) {
|
|
849
915
|
const entity = user.entity;
|
|
@@ -852,7 +918,7 @@ async function setPredefinedAcl(target, value, bucket, pathname = '', metadata =
|
|
|
852
918
|
}
|
|
853
919
|
}
|
|
854
920
|
}
|
|
855
|
-
case
|
|
921
|
+
case "bucketOwnerFullControl":
|
|
856
922
|
if (bucket && owner) {
|
|
857
923
|
items.push({ entity: owner, role: 'OWNER' });
|
|
858
924
|
}
|
|
@@ -867,7 +933,7 @@ async function setPredefinedAcl(target, value, bucket, pathname = '', metadata =
|
|
|
867
933
|
this.formatMessage(64, service, [`Unable to ${revoke ? 'revoke' : 'grant'} ` + value, bucket && pathname ? bucket.name + '/' + pathname : bucket ? bucket.name : pathname], errors[0], { ...Cloud.LOG_CLOUD_WARN });
|
|
868
934
|
}
|
|
869
935
|
else if (requested && (bucket || pathname)) {
|
|
870
|
-
this.formatMessage(64, service, [(revoke ? 'Revoke' : 'Grant') + ' ' + value, bucket
|
|
936
|
+
this.formatMessage(64, service, [(revoke ? 'Revoke' : 'Grant') + ' ' + value, bucket?.name], pathname, { ...Cloud.LOG_CLOUD_COMMAND });
|
|
871
937
|
}
|
|
872
938
|
});
|
|
873
939
|
}
|
|
@@ -919,13 +985,16 @@ function getProjectId(credential, cache) {
|
|
|
919
985
|
const id = require(pathname).project_id;
|
|
920
986
|
if ((0, types_1.isString)(id)) {
|
|
921
987
|
projectId = id;
|
|
922
|
-
credential.projectId = projectId;
|
|
923
988
|
}
|
|
924
989
|
}
|
|
925
990
|
catch {
|
|
926
991
|
}
|
|
992
|
+
credential.projectId = projectId || (projectId = process.env.GCLOUD_PROJECT);
|
|
927
993
|
}
|
|
928
994
|
}
|
|
929
995
|
return projectId;
|
|
930
996
|
}
|
|
931
997
|
exports.getProjectId = getProjectId;
|
|
998
|
+
exports.CLOUD_UPLOAD_STREAM = true;
|
|
999
|
+
exports.CLOUD_UPLOAD_CHUNK = true;
|
|
1000
|
+
exports.CLOUD_DOWNLOAD_CHUNK = true;
|
package/download/index.js
CHANGED
|
@@ -3,20 +3,18 @@ const path = require("path");
|
|
|
3
3
|
const fs = require("fs");
|
|
4
4
|
const https = require("https");
|
|
5
5
|
const types_1 = require("@e-mc/types");
|
|
6
|
-
const Module = require("@e-mc/module");
|
|
7
6
|
const Cloud = require("@e-mc/cloud");
|
|
8
7
|
const client_1 = require("@pi-r/gcp");
|
|
9
8
|
module.exports = function download(credential, service = 'gcp') {
|
|
10
9
|
const storage = client_1.createStorageClient.call(this, credential);
|
|
11
10
|
return (data, callback) => {
|
|
12
11
|
const { bucket: Bucket, download: target } = data;
|
|
13
|
-
const Key = target.filename;
|
|
14
|
-
const firebase = (0, client_1.isFirebaseApp)(credential, true, true);
|
|
12
|
+
const Key = target.keyname || target.filename;
|
|
15
13
|
let tempDir;
|
|
16
|
-
const downloadResponse = (err, url) => {
|
|
14
|
+
const downloadResponse = (err, url = null) => {
|
|
17
15
|
callback(err, url);
|
|
18
16
|
if (tempDir) {
|
|
19
|
-
queueMicrotask(() =>
|
|
17
|
+
queueMicrotask(() => Cloud.removeDir(tempDir));
|
|
20
18
|
}
|
|
21
19
|
};
|
|
22
20
|
if (!Bucket || !Key) {
|
|
@@ -24,13 +22,13 @@ module.exports = function download(credential, service = 'gcp') {
|
|
|
24
22
|
return;
|
|
25
23
|
}
|
|
26
24
|
if (!(tempDir = this.getTempDir({ uuidDir: true }))) {
|
|
27
|
-
downloadResponse(
|
|
25
|
+
downloadResponse((0, types_1.errorValue)("Unable to create temp directory", service));
|
|
28
26
|
return;
|
|
29
27
|
}
|
|
30
|
-
const location =
|
|
31
|
-
const destination = path.join(tempDir, path.basename(Key));
|
|
28
|
+
const location = Cloud.joinPath(Bucket, Key);
|
|
29
|
+
const destination = path.join(tempDir, path.basename(target.filename || Key));
|
|
32
30
|
const deleteMessage = () => this.formatMessage(64, service, "Delete success", location, { ...Cloud.LOG_CLOUD_DELETE });
|
|
33
|
-
if (
|
|
31
|
+
if ((0, client_1.isFirebaseApp)(credential, true, true)) {
|
|
34
32
|
const { ref, getDownloadURL, deleteObject } = require('@firebase/storage');
|
|
35
33
|
const objectRef = ref(storage, Key);
|
|
36
34
|
getDownloadURL(objectRef).then(url => {
|
|
@@ -44,7 +42,7 @@ module.exports = function download(credential, service = 'gcp') {
|
|
|
44
42
|
if (target.deleteObject) {
|
|
45
43
|
deleteObject(objectRef)
|
|
46
44
|
.then(() => deleteMessage())
|
|
47
|
-
.catch(err => this.formatFail(64, service, ["Delete failed", location], err, { ...Cloud.LOG_CLOUD_FAIL, fatal: !!target.active }));
|
|
45
|
+
.catch((err) => this.formatFail(64, service, ["Delete failed", location], err, { ...Cloud.LOG_CLOUD_FAIL, fatal: !!target.active }));
|
|
48
46
|
}
|
|
49
47
|
})
|
|
50
48
|
.on('error', err => downloadResponse(err)));
|
|
@@ -54,26 +52,36 @@ module.exports = function download(credential, service = 'gcp') {
|
|
|
54
52
|
}
|
|
55
53
|
});
|
|
56
54
|
});
|
|
55
|
+
return;
|
|
57
56
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
else {
|
|
71
|
-
this.formatFail(64, service, ["Delete failed", location], err, { ...Cloud.LOG_CLOUD_FAIL, fatal: !!target.active });
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
})
|
|
76
|
-
.catch(err => downloadResponse(err));
|
|
57
|
+
const { flags = 0 } = target;
|
|
58
|
+
const bucket = storage.bucket(Bucket);
|
|
59
|
+
const file = bucket.file(Key, { generation: target.versionId });
|
|
60
|
+
const chunkSize = flags & 4 ? (0, types_1.alignSize)(target.chunkSize, 1024) : 0;
|
|
61
|
+
let manager;
|
|
62
|
+
if (chunkSize > 0) {
|
|
63
|
+
const { TransferManager } = require('@google-cloud/storage');
|
|
64
|
+
let concurrencyLimit = target.chunkLimit;
|
|
65
|
+
if (!concurrencyLimit && target.options) {
|
|
66
|
+
({ concurrencyLimit } = target.options);
|
|
67
|
+
}
|
|
68
|
+
manager = new TransferManager(bucket).downloadFileInChunks(file, { destination, chunkSizeBytes: chunkSize, concurrencyLimit });
|
|
77
69
|
}
|
|
70
|
+
(manager || file.download({ destination }))
|
|
71
|
+
.then(() => {
|
|
72
|
+
downloadResponse(null, destination);
|
|
73
|
+
const deleteObject = target.deleteObject;
|
|
74
|
+
if (deleteObject) {
|
|
75
|
+
file.delete((0, types_1.isObject)(deleteObject) ? deleteObject : { ignoreNotFound: true }, err => {
|
|
76
|
+
if (!err) {
|
|
77
|
+
deleteMessage();
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
this.formatFail(64, service, ["Delete failed", location], err, { ...Cloud.LOG_CLOUD_FAIL, fatal: !!target.active });
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
})
|
|
85
|
+
.catch((err) => downloadResponse(err));
|
|
78
86
|
};
|
|
79
87
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-r/gcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "Google (GCP) cloud functions for E-mc.",
|
|
5
5
|
"main": "client/index.js",
|
|
6
6
|
"publishConfig": {
|
|
@@ -20,10 +20,10 @@
|
|
|
20
20
|
"license": "MIT",
|
|
21
21
|
"homepage": "https://github.com/anpham6/pi-r#readme",
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@e-mc/cloud": "^0.
|
|
24
|
-
"@e-mc/module": "^0.
|
|
25
|
-
"@e-mc/types": "^0.
|
|
26
|
-
"@google-cloud/firestore": "^7.
|
|
27
|
-
"@google-cloud/storage": "^7.
|
|
23
|
+
"@e-mc/cloud": "^0.9.2",
|
|
24
|
+
"@e-mc/module": "^0.9.2",
|
|
25
|
+
"@e-mc/types": "^0.9.2",
|
|
26
|
+
"@google-cloud/firestore": "^7.7.0",
|
|
27
|
+
"@google-cloud/storage": "^7.11.1"
|
|
28
28
|
}
|
|
29
29
|
}
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { CloudDatabase, CloudStorage } from '@e-mc/types/lib/cloud';
|
|
2
|
+
|
|
3
|
+
import type { StorageOptions } from '@google-cloud/storage';
|
|
4
|
+
import type { PathType } from '@google-cloud/datastore';
|
|
5
|
+
import type { entity } from '@google-cloud/datastore/build/src/entity';
|
|
6
|
+
import type { FirebaseOptions as IFirebaseOptions } from '@firebase/app';
|
|
7
|
+
|
|
8
|
+
// @ts-ignore
|
|
9
|
+
import type { GoogleAuthOptions } from 'google-auth-library';
|
|
10
|
+
|
|
11
|
+
export type GCPStorage = CloudStorage<GCPStorageCredential, "gcp | gcloud">;
|
|
12
|
+
|
|
13
|
+
export interface GCPStorageCredential extends StorageOptions, IFirebaseOptions {}
|
|
14
|
+
|
|
15
|
+
export interface GCPDatabaseQuery<T = PlainObject> extends Omit<CloudDatabase<unknown, T, unknown, unknown[] | StandardMap, GCPDatabaseCredential>, "id"> {
|
|
16
|
+
source: "cloud";
|
|
17
|
+
service: "gcp" | "gcloud";
|
|
18
|
+
product?: "firestore" | "firebase" | "bigquery" | "bigtable" | "datastore" | "spanner";
|
|
19
|
+
database?: string;
|
|
20
|
+
id?: ArrayOf<string>;
|
|
21
|
+
updateType?: 0 | 1 | 2 | 3;
|
|
22
|
+
columns?: string[];
|
|
23
|
+
keys?: ArrayOf<DatastoreKey>;
|
|
24
|
+
kind?: ArrayOf<string>;
|
|
25
|
+
orderBy?: unknown[][];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type GCPDatabaseCredential = GoogleAuthOptions;
|
|
29
|
+
|
|
30
|
+
export interface FirebaseOptions extends IFirebaseOptions {
|
|
31
|
+
product?: "firebase";
|
|
32
|
+
admin?: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type DatastoreKey = string | PathType[] | entity.KeyOptions;
|
|
36
|
+
export type BucketAcl = "authenticatedRead" | "private" | "projectPrivate" | "publicRead" | "publicReadWrite" | "bucketAccessUniform" | "bucketAccessACL";
|
package/upload/index.js
CHANGED
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
const path = require("path");
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const stream = require("stream");
|
|
3
5
|
const util_1 = require("@e-mc/cloud/util");
|
|
4
6
|
const types_1 = require("@e-mc/types");
|
|
5
|
-
const Module = require("@e-mc/module");
|
|
6
7
|
const Cloud = require("@e-mc/cloud");
|
|
7
8
|
const client_1 = require("@pi-r/gcp");
|
|
8
9
|
const BUCKET_SESSION = new Set();
|
|
9
10
|
const BUCKET_RESPONSE = {};
|
|
10
|
-
const getBucketKey = (credential, bucket, acl = '') =>
|
|
11
|
+
const getBucketKey = (credential, bucket, acl = '') => Cloud.asString(credential, true) + bucket + '_' + acl;
|
|
11
12
|
module.exports = function upload(credential, service = 'gcp') {
|
|
12
13
|
const storage = client_1.createStorageClient.call(this, credential);
|
|
13
14
|
return async (data, callback) => {
|
|
14
|
-
var _a;
|
|
15
|
-
var _b;
|
|
16
15
|
const firebase = (0, client_1.isFirebaseApp)(credential, true, true);
|
|
17
16
|
const { bucket, localUri } = data;
|
|
18
|
-
const { pathname = '', fileGroup, contentType, metadata, endpoint, active, publicRead, acl, admin = {}, overwrite, options } = data.upload;
|
|
19
|
-
let filename = data.upload.filename || path.basename(localUri), bucketClient, bucketKey, listFiles;
|
|
17
|
+
const { pathname = '', flags = 0, fileGroup, contentType, metadata, endpoint, active, publicRead, acl, admin = {}, overwrite, options } = data.upload;
|
|
18
|
+
let filename = data.upload.filename || path.basename(localUri), chunkSizeBytes = 0, bucketClient, bucketKey, listFiles;
|
|
20
19
|
const uploadResponse = (err, url) => {
|
|
21
20
|
if (!firebase) {
|
|
22
21
|
BUCKET_SESSION.delete(bucket);
|
|
@@ -43,8 +42,8 @@ module.exports = function upload(credential, service = 'gcp') {
|
|
|
43
42
|
else {
|
|
44
43
|
const configBucket = admin.configBucket;
|
|
45
44
|
if (!BUCKET_SESSION.has(bucket)) {
|
|
46
|
-
const bucketAcl = admin.publicRead ?
|
|
47
|
-
const response = BUCKET_RESPONSE[
|
|
45
|
+
const bucketAcl = admin.publicRead ? "publicRead" : admin.acl;
|
|
46
|
+
const response = BUCKET_RESPONSE[bucketKey = getBucketKey(credential, bucket, bucketAcl)] ||= client_1.createBucketV2.call(this, credential, bucket, bucketAcl, configBucket?.create);
|
|
48
47
|
if (!await response) {
|
|
49
48
|
uploadResponse(null);
|
|
50
49
|
return;
|
|
@@ -52,14 +51,49 @@ module.exports = function upload(credential, service = 'gcp') {
|
|
|
52
51
|
BUCKET_SESSION.add(bucket);
|
|
53
52
|
}
|
|
54
53
|
bucketClient = storage.bucket(bucket);
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
54
|
+
if (configBucket) {
|
|
55
|
+
let { cors, lifecycle, retentionPolicy } = configBucket;
|
|
56
|
+
const commandMessage = (feature, message) => this.formatMessage(64, service, ["Bucket configured" + ` (${feature})`, bucket], message, { ...Cloud.LOG_CLOUD_COMMAND });
|
|
57
|
+
if (typeof retentionPolicy === 'number') {
|
|
58
|
+
bucketClient.setRetentionPeriod(retentionPolicy)
|
|
59
|
+
.then(([response]) => commandMessage('Retention Policy', response.retentionPolicy?.effectiveTime))
|
|
60
|
+
.catch((err) => addLog(err));
|
|
61
|
+
}
|
|
62
|
+
if ((0, types_1.isObject)(cors)) {
|
|
63
|
+
bucketClient.setCorsConfiguration(Array.isArray(cors) ? cors : [cors])
|
|
64
|
+
.then(([response]) => {
|
|
65
|
+
const length = response.cors?.length;
|
|
66
|
+
commandMessage('CORS', length ? length + ' rules' : null);
|
|
67
|
+
})
|
|
68
|
+
.catch((err) => addLog(err));
|
|
69
|
+
}
|
|
70
|
+
if ((0, types_1.isObject)(lifecycle)) {
|
|
71
|
+
let append;
|
|
72
|
+
if (Array.isArray(lifecycle)) {
|
|
73
|
+
if (lifecycle.length === 0) {
|
|
74
|
+
append = false;
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
const arg = lifecycle[lifecycle.length - 1];
|
|
78
|
+
if (typeof arg === 'boolean') {
|
|
79
|
+
append = arg;
|
|
80
|
+
lifecycle = lifecycle.slice(0, lifecycle.length - 1);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
bucketClient.addLifecycleRule(lifecycle, { append })
|
|
85
|
+
.then(([response]) => {
|
|
86
|
+
const length = response.lifecycle?.rule?.length;
|
|
87
|
+
commandMessage('Lifecycle', length ? length + ' rules' : null);
|
|
88
|
+
})
|
|
89
|
+
.catch((err) => addLog(err));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (flags & 4) {
|
|
93
|
+
const chunkSize = (0, types_1.alignSize)(data.upload.chunkSize, 1024);
|
|
94
|
+
if (chunkSize > 0) {
|
|
95
|
+
chunkSizeBytes = Math.max(chunkSize, 8388608);
|
|
96
|
+
}
|
|
63
97
|
}
|
|
64
98
|
}
|
|
65
99
|
if (!overwrite && (!listFiles || listFiles.length)) {
|
|
@@ -73,7 +107,7 @@ module.exports = function upload(credential, service = 'gcp') {
|
|
|
73
107
|
break;
|
|
74
108
|
}
|
|
75
109
|
}
|
|
76
|
-
const name = pathname ?
|
|
110
|
+
const name = pathname ? Cloud.joinPath(pathname, filename) : filename;
|
|
77
111
|
if (firebase) {
|
|
78
112
|
exists = listFiles.includes(name);
|
|
79
113
|
}
|
|
@@ -85,16 +119,94 @@ module.exports = function upload(credential, service = 'gcp') {
|
|
|
85
119
|
this.formatMessage(64, service, ["File renamed", current], filename, { ...Cloud.LOG_CLOUD_WARN });
|
|
86
120
|
}
|
|
87
121
|
}
|
|
88
|
-
const getURL = (value) => endpoint ?
|
|
122
|
+
const getURL = (value) => endpoint ? Cloud.joinPath(endpoint, value) : Cloud.joinPath("https://storage.googleapis.com", bucket, value);
|
|
89
123
|
const Key = [filename];
|
|
90
124
|
const Body = [data.buffer];
|
|
125
|
+
const Path = [];
|
|
126
|
+
const Stream = [];
|
|
91
127
|
const ContentType = [contentType];
|
|
92
|
-
if (
|
|
93
|
-
const
|
|
128
|
+
if (chunkSizeBytes > 0) {
|
|
129
|
+
const main = Body[0];
|
|
130
|
+
if (main.length > chunkSizeBytes) {
|
|
131
|
+
try {
|
|
132
|
+
fs.writeFileSync(localUri, main);
|
|
133
|
+
Path[0] = localUri;
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
if (flags & 2) {
|
|
137
|
+
Stream[0] = stream.Readable.from(main, { highWaterMark: chunkSizeBytes });
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
else if (main.length === 0) {
|
|
142
|
+
try {
|
|
143
|
+
if (fs.statSync(localUri).size > chunkSizeBytes) {
|
|
144
|
+
Path[0] = localUri;
|
|
145
|
+
}
|
|
146
|
+
else if (flags & 2) {
|
|
147
|
+
Stream[0] = fs.createReadStream(localUri, { highWaterMark: chunkSizeBytes, signal: this.signal });
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
Body[0] = fs.readFileSync(localUri);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
uploadResponse(err);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (fileGroup) {
|
|
159
|
+
const [key, body, type] = (0, util_1.createKeyAndBody)(filename, fileGroup, chunkSizeBytes, addLog, flags);
|
|
160
|
+
Key.push(...key);
|
|
161
|
+
ContentType.push(...type);
|
|
162
|
+
for (let i = 0; i < body.length; ++i) {
|
|
163
|
+
const item = body[i];
|
|
164
|
+
if (typeof item === 'string') {
|
|
165
|
+
Path[i + 1] = item;
|
|
166
|
+
}
|
|
167
|
+
else if (Buffer.isBuffer(item)) {
|
|
168
|
+
Body[i + 1] = item;
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
Stream[i + 1] = item;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
else if ((flags & 2) && !firebase) {
|
|
177
|
+
try {
|
|
178
|
+
Stream.push(data.buffer.length ? stream.Readable.from(data.buffer) : fs.createReadStream(localUri, { signal: this.signal }));
|
|
179
|
+
if (fileGroup) {
|
|
180
|
+
const [key, body, type] = (0, util_1.createKeyAndBody)(filename, fileGroup, 0, addLog, 2);
|
|
181
|
+
Key.push(...key);
|
|
182
|
+
Stream.push(...body);
|
|
183
|
+
ContentType.push(...type);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
catch (err) {
|
|
187
|
+
uploadResponse(err);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
else if (fileGroup) {
|
|
192
|
+
const [key, body, type] = (0, util_1.createKeyAndBody)(filename, fileGroup, 0, addLog);
|
|
94
193
|
Key.push(...key);
|
|
95
194
|
Body.push(...body);
|
|
96
195
|
ContentType.push(...type);
|
|
97
196
|
}
|
|
197
|
+
let headers, concurrencyLimit, maxQueueSize;
|
|
198
|
+
if (options) {
|
|
199
|
+
({ headers, concurrencyLimit, maxQueueSize } = options);
|
|
200
|
+
if (headers !== undefined) {
|
|
201
|
+
delete options.headers;
|
|
202
|
+
}
|
|
203
|
+
if (concurrencyLimit !== undefined) {
|
|
204
|
+
delete options.concurrencyLimit;
|
|
205
|
+
}
|
|
206
|
+
if (maxQueueSize !== undefined) {
|
|
207
|
+
delete options.maxQueueSize;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
98
210
|
for (let i = 0; i < Key.length; ++i) {
|
|
99
211
|
const first = i === 0;
|
|
100
212
|
if (this.aborted) {
|
|
@@ -103,12 +215,21 @@ module.exports = function upload(credential, service = 'gcp') {
|
|
|
103
215
|
}
|
|
104
216
|
return;
|
|
105
217
|
}
|
|
106
|
-
const destination = pathname ?
|
|
218
|
+
const destination = pathname ? Cloud.joinPath(pathname, Key[i]) : Key[i];
|
|
107
219
|
if (firebase) {
|
|
108
220
|
const { ref, uploadBytes } = require('@firebase/storage');
|
|
109
221
|
const params = { ...options };
|
|
110
222
|
if (first) {
|
|
111
|
-
|
|
223
|
+
if (Body[0].length === 0) {
|
|
224
|
+
try {
|
|
225
|
+
Body[0] = fs.readFileSync(localUri);
|
|
226
|
+
}
|
|
227
|
+
catch (err) {
|
|
228
|
+
uploadResponse(err);
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
params.contentType ||= ContentType[i];
|
|
112
233
|
if (metadata) {
|
|
113
234
|
params.customMetadata = metadata;
|
|
114
235
|
}
|
|
@@ -124,7 +245,7 @@ module.exports = function upload(credential, service = 'gcp') {
|
|
|
124
245
|
uploadResponse(null, url);
|
|
125
246
|
}
|
|
126
247
|
})
|
|
127
|
-
.catch(err => {
|
|
248
|
+
.catch((err) => {
|
|
128
249
|
if (first) {
|
|
129
250
|
uploadResponse(err);
|
|
130
251
|
}
|
|
@@ -138,14 +259,14 @@ module.exports = function upload(credential, service = 'gcp') {
|
|
|
138
259
|
const readable = publicRead || active && publicRead !== false && publicRead !== 0 && !acl;
|
|
139
260
|
let ACL, revoke, predefined;
|
|
140
261
|
if (first) {
|
|
141
|
-
params.contentType
|
|
262
|
+
params.contentType ||= ContentType[i];
|
|
142
263
|
if (readable) {
|
|
143
|
-
ACL =
|
|
264
|
+
ACL = "publicRead";
|
|
144
265
|
}
|
|
145
266
|
else {
|
|
146
267
|
predefined = true;
|
|
147
268
|
}
|
|
148
|
-
if (
|
|
269
|
+
if ((0, types_1.isObject)(metadata)) {
|
|
149
270
|
params.metadata = metadata;
|
|
150
271
|
}
|
|
151
272
|
}
|
|
@@ -153,7 +274,7 @@ module.exports = function upload(credential, service = 'gcp') {
|
|
|
153
274
|
params.contentType = ContentType[i];
|
|
154
275
|
if (!params.predefinedAcl) {
|
|
155
276
|
if (readable) {
|
|
156
|
-
ACL =
|
|
277
|
+
ACL = "publicRead";
|
|
157
278
|
}
|
|
158
279
|
else {
|
|
159
280
|
predefined = true;
|
|
@@ -162,26 +283,47 @@ module.exports = function upload(credential, service = 'gcp') {
|
|
|
162
283
|
}
|
|
163
284
|
if (predefined) {
|
|
164
285
|
if (publicRead === 0) {
|
|
165
|
-
ACL =
|
|
286
|
+
ACL = "publicRead";
|
|
166
287
|
revoke = true;
|
|
167
288
|
}
|
|
168
289
|
if (acl && (!revoke || acl !== ACL)) {
|
|
169
290
|
params.predefinedAcl = acl;
|
|
170
291
|
}
|
|
171
292
|
}
|
|
172
|
-
(_a = params.resumable) !== null && _a !== void 0 ? _a : (params.resumable = false);
|
|
173
293
|
const file = bucketClient.file(destination);
|
|
174
|
-
|
|
294
|
+
let uploadClient;
|
|
295
|
+
if (Path[i]) {
|
|
296
|
+
const { TransferManager } = require('@google-cloud/storage');
|
|
297
|
+
uploadClient = new TransferManager(bucketClient).uploadFileInChunks(Path[i], { uploadName: destination, chunkSizeBytes, headers, concurrencyLimit: data.upload.chunkLimit || concurrencyLimit, maxQueueSize });
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
params.resumable ??= false;
|
|
301
|
+
if (Stream[i]) {
|
|
302
|
+
if (chunkSizeBytes > 0) {
|
|
303
|
+
params.chunkSize = chunkSizeBytes;
|
|
304
|
+
}
|
|
305
|
+
uploadClient = new Promise((resolve, reject) => {
|
|
306
|
+
Stream[i].pipe(file.createWriteStream(params))
|
|
307
|
+
.on('finish', resolve)
|
|
308
|
+
.on('error', reject);
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
(uploadClient || file.save(Body[i], params))
|
|
313
|
+
.then(async () => {
|
|
175
314
|
const url = getURL(file.name);
|
|
176
315
|
this.formatMessage(64, service, "Upload success", url, { ...Cloud.LOG_CLOUD_UPLOAD });
|
|
177
316
|
if (first) {
|
|
178
317
|
uploadResponse(null, url);
|
|
179
318
|
}
|
|
319
|
+
if (Path[i]) {
|
|
320
|
+
file.setMetadata(params).catch((err) => addLog(err));
|
|
321
|
+
}
|
|
180
322
|
if (ACL) {
|
|
181
323
|
client_1.setPredefinedAcl.call(this, file.acl, ACL, file.bucket, file.name, revoke || (await file.getMetadata())[0], true);
|
|
182
324
|
}
|
|
183
325
|
})
|
|
184
|
-
.catch(err => {
|
|
326
|
+
.catch((err) => {
|
|
185
327
|
if (first) {
|
|
186
328
|
uploadResponse(err);
|
|
187
329
|
}
|