firebase-tools 10.2.1 → 10.3.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/lib/appdistribution/options-parser-util.js +1 -1
- package/lib/auth.js +3 -3
- package/lib/command.js +1 -1
- package/lib/commands/apps-android-sha-create.js +2 -2
- package/lib/commands/apps-sdkconfig.js +1 -1
- package/lib/commands/database-rules-list.js +2 -2
- package/lib/commands/emulators-start.js +1 -1
- package/lib/commands/ext-configure.js +67 -7
- package/lib/commands/ext-dev-init.js +49 -49
- package/lib/commands/ext-export.js +7 -2
- package/lib/commands/ext-install.js +173 -109
- package/lib/commands/ext-uninstall.js +17 -8
- package/lib/commands/ext-update.js +67 -12
- package/lib/commands/functions-config-export.js +1 -1
- package/lib/commands/hosting-clone.js +3 -3
- package/lib/commands/remoteconfig-get.js +1 -1
- package/lib/config.js +6 -3
- package/lib/deploy/extensions/deploymentSummary.js +3 -3
- package/lib/deploy/extensions/planner.js +7 -6
- package/lib/deploy/extensions/tasks.js +1 -1
- package/lib/deploy/functions/backend.js +21 -5
- package/lib/deploy/functions/checkIam.js +5 -5
- package/lib/deploy/functions/containerCleaner.js +3 -3
- package/lib/deploy/functions/ensure.js +3 -3
- package/lib/deploy/functions/functionsDeployHelper.js +2 -2
- package/lib/deploy/functions/prepare.js +5 -3
- package/lib/deploy/functions/pricing.js +1 -1
- package/lib/deploy/functions/prompts.js +2 -2
- package/lib/deploy/functions/release/fabricator.js +7 -7
- package/lib/deploy/functions/release/index.js +1 -1
- package/lib/deploy/functions/release/planner.js +43 -26
- package/lib/deploy/functions/release/reporter.js +3 -0
- package/lib/deploy/functions/runtimes/discovery/index.js +6 -6
- package/lib/deploy/functions/runtimes/discovery/parsing.js +1 -1
- package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +22 -12
- package/lib/deploy/functions/runtimes/golang/index.js +2 -2
- package/lib/deploy/functions/runtimes/node/index.js +53 -0
- package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +2 -2
- package/lib/deploy/functions/runtimes/node/parseTriggers.js +64 -20
- package/lib/deploy/functions/runtimes/node/versioning.js +2 -2
- package/lib/deploy/functions/services/firebaseAlerts.js +30 -0
- package/lib/deploy/functions/services/index.js +9 -1
- package/lib/deploy/functions/services/storage.js +10 -4
- package/lib/deploy/functions/triggerRegionHelper.js +1 -1
- package/lib/deploy/functions/validate.js +3 -3
- package/lib/deploy/hosting/deploy.js +2 -2
- package/lib/deploy/hosting/hashcache.js +21 -19
- package/lib/deploy/hosting/uploader.js +5 -5
- package/lib/deploy/remoteconfig/functions.js +2 -2
- package/lib/emulator/auth/cloudFunctions.js +1 -1
- package/lib/emulator/auth/operations.js +1 -1
- package/lib/emulator/constants.js +4 -0
- package/lib/emulator/controller.js +54 -24
- package/lib/emulator/download.js +18 -1
- package/lib/emulator/downloadableEmulators.js +1 -1
- package/lib/emulator/emulatorLogger.js +12 -1
- package/lib/emulator/extensions/validation.js +70 -0
- package/lib/emulator/extensionsEmulator.js +175 -0
- package/lib/emulator/functionsEmulator.js +95 -43
- package/lib/emulator/functionsEmulatorRuntime.js +44 -36
- package/lib/emulator/functionsEmulatorShared.js +30 -11
- package/lib/emulator/functionsEmulatorShell.js +1 -1
- package/lib/emulator/functionsEmulatorUtils.js +4 -4
- package/lib/emulator/functionsRuntimeWorker.js +2 -2
- package/lib/emulator/hub.js +4 -3
- package/lib/emulator/loggingEmulator.js +1 -1
- package/lib/emulator/pubsubEmulator.js +1 -1
- package/lib/emulator/registry.js +10 -2
- package/lib/emulator/storage/apis/firebase.js +314 -332
- package/lib/emulator/storage/apis/gcloud.js +241 -121
- package/lib/emulator/storage/crc.js +5 -1
- package/lib/emulator/storage/errors.js +9 -0
- package/lib/emulator/storage/files.js +159 -300
- package/lib/emulator/storage/index.js +27 -73
- package/lib/emulator/storage/metadata.js +65 -51
- package/lib/emulator/storage/multipart.js +62 -0
- package/lib/emulator/storage/persistence.js +78 -0
- package/lib/emulator/storage/rules/config.js +33 -0
- package/lib/emulator/storage/rules/manager.js +81 -0
- package/lib/emulator/storage/rules/runtime.js +8 -7
- package/lib/emulator/storage/rules/utils.js +48 -0
- package/lib/emulator/storage/server.js +2 -2
- package/lib/emulator/storage/upload.js +106 -0
- package/lib/emulator/types.js +3 -0
- package/lib/ensureApiEnabled.js +5 -1
- package/lib/error.js +1 -1
- package/lib/extensions/askUserForParam.js +31 -25
- package/lib/extensions/changelog.js +3 -1
- package/lib/extensions/checkProjectBilling.js +1 -1
- package/lib/extensions/displayExtensionInfo.js +1 -1
- package/lib/extensions/emulator/optionsHelper.js +56 -8
- package/lib/extensions/emulator/specHelper.js +10 -23
- package/lib/extensions/export.js +1 -51
- package/lib/extensions/extensionsApi.js +1 -1
- package/lib/extensions/extensionsHelper.js +32 -19
- package/lib/extensions/manifest.js +144 -0
- package/lib/extensions/metricsUtils.js +4 -4
- package/lib/extensions/paramHelper.js +34 -12
- package/lib/extensions/refs.js +1 -1
- package/lib/extensions/secretsUtils.js +3 -3
- package/lib/functional.js +1 -1
- package/lib/functions/env.js +6 -7
- package/lib/functions/events/v2.js +11 -0
- package/lib/gcp/cloudfunctions.js +43 -11
- package/lib/gcp/cloudfunctionsv2.js +48 -17
- package/lib/gcp/cloudtasks.js +1 -1
- package/lib/gcp/docker.js +2 -2
- package/lib/gcp/resourceManager.js +4 -4
- package/lib/gcp/run.js +2 -2
- package/lib/hosting/api.js +1 -1
- package/lib/hosting/proxy.js +2 -2
- package/lib/init/features/account.js +1 -1
- package/lib/management/database.js +1 -1
- package/lib/previews.js +1 -1
- package/lib/serve/functions.js +2 -1
- package/lib/utils.js +15 -2
- package/npm-shrinkwrap.json +786 -393
- package/package.json +1 -1
- package/schema/firebase-config.json +5 -0
- package/templates/init/functions/javascript/package.lint.json +3 -3
- package/templates/init/functions/javascript/package.nolint.json +2 -2
- package/templates/init/functions/typescript/package.lint.json +7 -7
- package/templates/init/functions/typescript/package.nolint.json +3 -3
- package/lib/deploy/extensions/params.js +0 -39
- package/lib/deploy/functions/eventTypes.js +0 -10
|
@@ -7,19 +7,18 @@ var __asyncValues = (this && this.__asyncValues) || function (o) {
|
|
|
7
7
|
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
|
8
8
|
};
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
-
exports.
|
|
10
|
+
exports.StorageLayer = exports.StoredFile = void 0;
|
|
11
11
|
const fs_1 = require("fs");
|
|
12
|
-
const os_1 = require("os");
|
|
13
|
-
const uuid_1 = require("uuid");
|
|
14
12
|
const list_1 = require("./list");
|
|
15
13
|
const metadata_1 = require("./metadata");
|
|
14
|
+
const errors_1 = require("./errors");
|
|
16
15
|
const path = require("path");
|
|
17
|
-
const fs = require("fs");
|
|
18
16
|
const fse = require("fs-extra");
|
|
19
|
-
const rimraf = require("rimraf");
|
|
20
17
|
const cloudFunctions_1 = require("./cloudFunctions");
|
|
21
18
|
const logger_1 = require("../../logger");
|
|
22
19
|
const adminSdkConfig_1 = require("../adminSdkConfig");
|
|
20
|
+
const types_1 = require("./rules/types");
|
|
21
|
+
const upload_1 = require("./upload");
|
|
23
22
|
class StoredFile {
|
|
24
23
|
constructor(metadata, path) {
|
|
25
24
|
this.metadata = metadata;
|
|
@@ -39,69 +38,17 @@ class StoredFile {
|
|
|
39
38
|
}
|
|
40
39
|
}
|
|
41
40
|
exports.StoredFile = StoredFile;
|
|
42
|
-
class ResumableUpload {
|
|
43
|
-
constructor(bucketId, objectId, uploadId, contentType, metadata) {
|
|
44
|
-
this._currentBytesUploaded = 0;
|
|
45
|
-
this._status = UploadStatus.ACTIVE;
|
|
46
|
-
this._bucketId = bucketId;
|
|
47
|
-
this._objectId = objectId;
|
|
48
|
-
this._uploadId = uploadId;
|
|
49
|
-
this._contentType = contentType;
|
|
50
|
-
this._metadata = metadata;
|
|
51
|
-
this._fileLocation = encodeURIComponent(`${uploadId}_b_${bucketId}_o_${objectId}`);
|
|
52
|
-
this._currentBytesUploaded = 0;
|
|
53
|
-
}
|
|
54
|
-
get uploadId() {
|
|
55
|
-
return this._uploadId;
|
|
56
|
-
}
|
|
57
|
-
get metadata() {
|
|
58
|
-
return this._metadata;
|
|
59
|
-
}
|
|
60
|
-
get bucketId() {
|
|
61
|
-
return this._bucketId;
|
|
62
|
-
}
|
|
63
|
-
get objectId() {
|
|
64
|
-
return this._objectId;
|
|
65
|
-
}
|
|
66
|
-
get contentType() {
|
|
67
|
-
return this._contentType;
|
|
68
|
-
}
|
|
69
|
-
set contentType(contentType) {
|
|
70
|
-
this._contentType = contentType;
|
|
71
|
-
}
|
|
72
|
-
get currentBytesUploaded() {
|
|
73
|
-
return this._currentBytesUploaded;
|
|
74
|
-
}
|
|
75
|
-
set currentBytesUploaded(value) {
|
|
76
|
-
this._currentBytesUploaded = value;
|
|
77
|
-
}
|
|
78
|
-
set status(status) {
|
|
79
|
-
this._status = status;
|
|
80
|
-
}
|
|
81
|
-
get status() {
|
|
82
|
-
return this._status;
|
|
83
|
-
}
|
|
84
|
-
get fileLocation() {
|
|
85
|
-
return this._fileLocation;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
exports.ResumableUpload = ResumableUpload;
|
|
89
|
-
var UploadStatus;
|
|
90
|
-
(function (UploadStatus) {
|
|
91
|
-
UploadStatus[UploadStatus["ACTIVE"] = 0] = "ACTIVE";
|
|
92
|
-
UploadStatus[UploadStatus["CANCELLED"] = 1] = "CANCELLED";
|
|
93
|
-
UploadStatus[UploadStatus["FINISHED"] = 2] = "FINISHED";
|
|
94
|
-
})(UploadStatus = exports.UploadStatus || (exports.UploadStatus = {}));
|
|
95
41
|
class StorageLayer {
|
|
96
|
-
constructor(_projectId) {
|
|
42
|
+
constructor(_projectId, _rulesValidator, _adminCredsValidator, _persistence) {
|
|
97
43
|
this._projectId = _projectId;
|
|
44
|
+
this._rulesValidator = _rulesValidator;
|
|
45
|
+
this._adminCredsValidator = _adminCredsValidator;
|
|
46
|
+
this._persistence = _persistence;
|
|
98
47
|
this.reset();
|
|
99
48
|
this._cloudFunctions = new cloudFunctions_1.StorageCloudFunctions(this._projectId);
|
|
100
49
|
}
|
|
101
50
|
reset() {
|
|
102
51
|
this._files = new Map();
|
|
103
|
-
this._persistence = new Persistence(`${(0, os_1.tmpdir)()}/firebase/storage/blobs`);
|
|
104
|
-
this._uploads = new Map();
|
|
105
52
|
this._buckets = new Map();
|
|
106
53
|
}
|
|
107
54
|
createBucket(id) {
|
|
@@ -110,7 +57,7 @@ class StorageLayer {
|
|
|
110
57
|
}
|
|
111
58
|
}
|
|
112
59
|
async listBuckets() {
|
|
113
|
-
if (this._buckets.size
|
|
60
|
+
if (this._buckets.size === 0) {
|
|
114
61
|
let adminSdkConfig = await (0, adminSdkConfig_1.getProjectAdminSdkConfigOrCached)(this._projectId);
|
|
115
62
|
if (!adminSdkConfig) {
|
|
116
63
|
adminSdkConfig = (0, adminSdkConfig_1.constructDefaultAdminSdkConfig)(this._projectId);
|
|
@@ -119,6 +66,22 @@ class StorageLayer {
|
|
|
119
66
|
}
|
|
120
67
|
return [...this._buckets.values()];
|
|
121
68
|
}
|
|
69
|
+
async handleGetObject(request, skipAuth = false) {
|
|
70
|
+
var _a;
|
|
71
|
+
const metadata = this.getMetadata(request.bucketId, request.decodedObjectId);
|
|
72
|
+
const hasValidDownloadToken = ((metadata === null || metadata === void 0 ? void 0 : metadata.downloadTokens) || []).includes((_a = request.downloadToken) !== null && _a !== void 0 ? _a : "");
|
|
73
|
+
let authorized = skipAuth || hasValidDownloadToken;
|
|
74
|
+
if (!authorized) {
|
|
75
|
+
authorized = await this._rulesValidator.validate(["b", request.bucketId, "o", request.decodedObjectId].join("/"), types_1.RulesetOperationMethod.GET, { before: metadata === null || metadata === void 0 ? void 0 : metadata.asRulesResource() }, request.authorization);
|
|
76
|
+
}
|
|
77
|
+
if (!authorized) {
|
|
78
|
+
throw new errors_1.ForbiddenError("Failed auth");
|
|
79
|
+
}
|
|
80
|
+
if (!metadata) {
|
|
81
|
+
throw new errors_1.NotFoundError("File not found");
|
|
82
|
+
}
|
|
83
|
+
return { metadata: metadata, data: this.getBytes(request.bucketId, request.decodedObjectId) };
|
|
84
|
+
}
|
|
122
85
|
getMetadata(bucket, object) {
|
|
123
86
|
const key = this.path(bucket, object);
|
|
124
87
|
const val = this._files.get(key);
|
|
@@ -136,34 +99,17 @@ class StorageLayer {
|
|
|
136
99
|
}
|
|
137
100
|
return undefined;
|
|
138
101
|
}
|
|
139
|
-
|
|
140
|
-
this.
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
this._uploads.set(uploadId, upload);
|
|
146
|
-
return upload;
|
|
147
|
-
}
|
|
148
|
-
queryUpload(uploadId) {
|
|
149
|
-
return this._uploads.get(uploadId);
|
|
150
|
-
}
|
|
151
|
-
cancelUpload(uploadId) {
|
|
152
|
-
const upload = this._uploads.get(uploadId);
|
|
153
|
-
if (!upload) {
|
|
154
|
-
return undefined;
|
|
102
|
+
async handleDeleteObject(request, skipAuth = false) {
|
|
103
|
+
const storedMetadata = this.getMetadata(request.bucketId, request.decodedObjectId);
|
|
104
|
+
const authorized = skipAuth ||
|
|
105
|
+
(await this._rulesValidator.validate(["b", request.bucketId, "o", request.decodedObjectId].join("/"), types_1.RulesetOperationMethod.DELETE, { before: storedMetadata === null || storedMetadata === void 0 ? void 0 : storedMetadata.asRulesResource() }, request.authorization));
|
|
106
|
+
if (!authorized) {
|
|
107
|
+
throw new errors_1.ForbiddenError();
|
|
155
108
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
const upload = this._uploads.get(uploadId);
|
|
161
|
-
if (!upload) {
|
|
162
|
-
return undefined;
|
|
163
|
-
}
|
|
164
|
-
this._persistence.appendBytes(upload.fileLocation, bytes, upload.currentBytesUploaded);
|
|
165
|
-
upload.currentBytesUploaded += bytes.byteLength;
|
|
166
|
-
return upload;
|
|
109
|
+
if (!storedMetadata) {
|
|
110
|
+
throw new errors_1.NotFoundError();
|
|
111
|
+
}
|
|
112
|
+
this.deleteFile(request.bucketId, request.decodedObjectId);
|
|
167
113
|
}
|
|
168
114
|
deleteFile(bucketId, objectId) {
|
|
169
115
|
const isFolder = objectId.toLowerCase().endsWith("%2f");
|
|
@@ -175,7 +121,7 @@ class StorageLayer {
|
|
|
175
121
|
filePath += "%2F";
|
|
176
122
|
}
|
|
177
123
|
const file = this._files.get(filePath);
|
|
178
|
-
if (file
|
|
124
|
+
if (file === undefined) {
|
|
179
125
|
return false;
|
|
180
126
|
}
|
|
181
127
|
else {
|
|
@@ -185,89 +131,130 @@ class StorageLayer {
|
|
|
185
131
|
return true;
|
|
186
132
|
}
|
|
187
133
|
}
|
|
188
|
-
async
|
|
189
|
-
|
|
134
|
+
async handleUpdateObjectMetadata(request, skipAuth = false) {
|
|
135
|
+
const storedMetadata = this.getMetadata(request.bucketId, request.decodedObjectId);
|
|
136
|
+
const authorized = skipAuth ||
|
|
137
|
+
(await this._rulesValidator.validate(["b", request.bucketId, "o", request.decodedObjectId].join("/"), types_1.RulesetOperationMethod.UPDATE, {
|
|
138
|
+
before: storedMetadata === null || storedMetadata === void 0 ? void 0 : storedMetadata.asRulesResource(),
|
|
139
|
+
after: storedMetadata === null || storedMetadata === void 0 ? void 0 : storedMetadata.asRulesResource(request.metadata),
|
|
140
|
+
}, request.authorization));
|
|
141
|
+
if (!authorized) {
|
|
142
|
+
throw new errors_1.ForbiddenError();
|
|
143
|
+
}
|
|
144
|
+
if (!storedMetadata) {
|
|
145
|
+
throw new errors_1.NotFoundError();
|
|
146
|
+
}
|
|
147
|
+
storedMetadata.update(request.metadata);
|
|
148
|
+
return storedMetadata;
|
|
190
149
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
return undefined;
|
|
150
|
+
async handleUploadObject(upload, skipAuth = false) {
|
|
151
|
+
if (upload.status !== upload_1.UploadStatus.FINISHED) {
|
|
152
|
+
throw new Error(`Unexpected upload status encountered: ${upload.status}.`);
|
|
195
153
|
}
|
|
196
|
-
upload.status = UploadStatus.FINISHED;
|
|
197
154
|
const filePath = this.path(upload.bucketId, upload.objectId);
|
|
198
|
-
const
|
|
199
|
-
const finalMetadata = new metadata_1.StoredFileMetadata({
|
|
155
|
+
const metadata = new metadata_1.StoredFileMetadata({
|
|
200
156
|
name: upload.objectId,
|
|
201
157
|
bucket: upload.bucketId,
|
|
202
|
-
contentType: "",
|
|
158
|
+
contentType: upload.metadata.contentType || "application/octet-stream",
|
|
159
|
+
contentDisposition: upload.metadata.contentDisposition,
|
|
203
160
|
contentEncoding: upload.metadata.contentEncoding,
|
|
161
|
+
contentLanguage: upload.metadata.contentLanguage,
|
|
162
|
+
cacheControl: upload.metadata.cacheControl,
|
|
204
163
|
customMetadata: upload.metadata.metadata,
|
|
205
|
-
}, this._cloudFunctions,
|
|
206
|
-
const
|
|
207
|
-
|
|
164
|
+
}, this._cloudFunctions, this._persistence.readBytes(upload.path, upload.size));
|
|
165
|
+
const authorized = skipAuth ||
|
|
166
|
+
(await this._rulesValidator.validate(["b", upload.bucketId, "o", upload.objectId].join("/"), types_1.RulesetOperationMethod.CREATE, { after: metadata === null || metadata === void 0 ? void 0 : metadata.asRulesResource() }, upload.authorization));
|
|
167
|
+
if (!authorized) {
|
|
168
|
+
this._persistence.deleteFile(upload.path);
|
|
169
|
+
throw new errors_1.ForbiddenError();
|
|
170
|
+
}
|
|
208
171
|
this._persistence.deleteFile(filePath, true);
|
|
209
|
-
this._persistence.renameFile(upload.
|
|
210
|
-
this.
|
|
211
|
-
|
|
172
|
+
this._persistence.renameFile(upload.path, filePath);
|
|
173
|
+
this._files.set(filePath, new StoredFile(metadata, this._persistence.getDiskPath(filePath)));
|
|
174
|
+
this._cloudFunctions.dispatch("finalize", new metadata_1.CloudStorageObjectMetadata(metadata));
|
|
175
|
+
return metadata;
|
|
212
176
|
}
|
|
213
|
-
|
|
214
|
-
const filePath = this.path(
|
|
177
|
+
copyFile(sourceFile, destinationBucket, destinationObject, incomingMetadata) {
|
|
178
|
+
const filePath = this.path(destinationBucket, destinationObject);
|
|
215
179
|
this._persistence.deleteFile(filePath, true);
|
|
180
|
+
const bytes = this.getBytes(sourceFile.bucket, sourceFile.name);
|
|
216
181
|
this._persistence.appendBytes(filePath, bytes);
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
182
|
+
const newMetadata = Object.assign(Object.assign(Object.assign({}, sourceFile), { metadata: sourceFile.customMetadata }), incomingMetadata);
|
|
183
|
+
if (sourceFile.downloadTokens.length &&
|
|
184
|
+
!((incomingMetadata === null || incomingMetadata === void 0 ? void 0 : incomingMetadata.metadata) && Object.keys(incomingMetadata === null || incomingMetadata === void 0 ? void 0 : incomingMetadata.metadata).length)) {
|
|
185
|
+
if (!newMetadata.metadata)
|
|
186
|
+
newMetadata.metadata = {};
|
|
187
|
+
newMetadata.metadata.firebaseStorageDownloadTokens = sourceFile.downloadTokens.join(",");
|
|
188
|
+
}
|
|
189
|
+
if (newMetadata.metadata) {
|
|
190
|
+
for (const [k, v] of Object.entries(newMetadata.metadata)) {
|
|
191
|
+
if (v === null)
|
|
192
|
+
newMetadata.metadata[k] = "";
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
const copiedFileMetadata = new metadata_1.StoredFileMetadata({
|
|
196
|
+
name: destinationObject,
|
|
197
|
+
bucket: destinationBucket,
|
|
198
|
+
contentType: newMetadata.contentType || "application/octet-stream",
|
|
199
|
+
contentDisposition: newMetadata.contentDisposition,
|
|
200
|
+
contentEncoding: newMetadata.contentEncoding,
|
|
201
|
+
contentLanguage: newMetadata.contentLanguage,
|
|
202
|
+
cacheControl: newMetadata.cacheControl,
|
|
203
|
+
customMetadata: newMetadata.metadata,
|
|
223
204
|
}, this._cloudFunctions, bytes, incomingMetadata);
|
|
224
|
-
const file = new StoredFile(
|
|
205
|
+
const file = new StoredFile(copiedFileMetadata, this._persistence.getDiskPath(filePath));
|
|
225
206
|
this._files.set(filePath, file);
|
|
226
207
|
this._cloudFunctions.dispatch("finalize", new metadata_1.CloudStorageObjectMetadata(file.metadata));
|
|
227
208
|
return file.metadata;
|
|
228
209
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
if (!
|
|
234
|
-
|
|
235
|
-
}
|
|
236
|
-
if (!prefix.endsWith(delimiter)) {
|
|
237
|
-
prefix += delimiter;
|
|
238
|
-
}
|
|
239
|
-
if (!prefix.startsWith(delimiter)) {
|
|
240
|
-
prefix = delimiter + prefix;
|
|
210
|
+
async handleListObjects(request, skipAuth = false) {
|
|
211
|
+
var _a, _b, _c;
|
|
212
|
+
const authorized = skipAuth ||
|
|
213
|
+
(await this._rulesValidator.validate(["b", request.bucketId, "o", request.prefix].join("/"), types_1.RulesetOperationMethod.LIST, {}, request.authorization));
|
|
214
|
+
if (!authorized) {
|
|
215
|
+
throw new errors_1.ForbiddenError();
|
|
241
216
|
}
|
|
217
|
+
const itemsResults = this.listItems(request.bucketId, request.prefix, request.delimiter, request.pageToken, request.maxResults);
|
|
218
|
+
return new list_1.ListResponse((_a = itemsResults.prefixes) !== null && _a !== void 0 ? _a : [], (_c = (_b = itemsResults.items) === null || _b === void 0 ? void 0 : _b.map((i) => new list_1.ListItem(i.name, i.bucket))) !== null && _c !== void 0 ? _c : [], itemsResults.nextPageToken);
|
|
219
|
+
}
|
|
220
|
+
listItems(bucket, prefix, delimiter, pageToken, maxResults) {
|
|
242
221
|
let items = [];
|
|
243
222
|
const prefixes = new Set();
|
|
244
223
|
for (const [, file] of this._files) {
|
|
245
|
-
if (file.metadata.bucket
|
|
224
|
+
if (file.metadata.bucket !== bucket) {
|
|
246
225
|
continue;
|
|
247
226
|
}
|
|
248
|
-
|
|
227
|
+
const name = file.metadata.name;
|
|
249
228
|
if (!name.startsWith(prefix)) {
|
|
250
229
|
continue;
|
|
251
230
|
}
|
|
252
|
-
|
|
253
|
-
if (
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
items.push(file.metadata.name);
|
|
231
|
+
let includeMetadata = true;
|
|
232
|
+
if (delimiter) {
|
|
233
|
+
const delimiterIdx = name.indexOf(delimiter);
|
|
234
|
+
const delimiterAfterPrefixIdx = name.indexOf(delimiter, prefix.length);
|
|
235
|
+
includeMetadata = delimiterIdx === -1 || delimiterAfterPrefixIdx === -1;
|
|
236
|
+
if (delimiterAfterPrefixIdx !== -1) {
|
|
237
|
+
prefixes.add(name.slice(0, delimiterAfterPrefixIdx + delimiter.length));
|
|
260
238
|
}
|
|
261
239
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
prefixes.add(prefixPath);
|
|
240
|
+
if (includeMetadata) {
|
|
241
|
+
items.push(file.metadata);
|
|
265
242
|
}
|
|
266
243
|
}
|
|
267
|
-
items.sort()
|
|
244
|
+
items.sort((a, b) => {
|
|
245
|
+
if (a.name === b.name) {
|
|
246
|
+
return 0;
|
|
247
|
+
}
|
|
248
|
+
else if (a.name < b.name) {
|
|
249
|
+
return -1;
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
return 1;
|
|
253
|
+
}
|
|
254
|
+
});
|
|
268
255
|
if (pageToken) {
|
|
269
|
-
const idx = items.findIndex((v) => v
|
|
270
|
-
if (idx
|
|
256
|
+
const idx = items.findIndex((v) => v.name === pageToken);
|
|
257
|
+
if (idx !== -1) {
|
|
271
258
|
items = items.slice(idx);
|
|
272
259
|
}
|
|
273
260
|
}
|
|
@@ -276,81 +263,39 @@ class StorageLayer {
|
|
|
276
263
|
}
|
|
277
264
|
let nextPageToken = undefined;
|
|
278
265
|
if (items.length > maxResults) {
|
|
279
|
-
nextPageToken = items[maxResults];
|
|
266
|
+
nextPageToken = items[maxResults].name;
|
|
280
267
|
items = items.slice(0, maxResults);
|
|
281
268
|
}
|
|
282
|
-
return new list_1.ListResponse([...prefixes].sort(), items.map((i) => new list_1.ListItem(i, bucket)), nextPageToken);
|
|
283
|
-
}
|
|
284
|
-
listItems(bucket, prefix, delimiter, pageToken, maxResults) {
|
|
285
|
-
if (!delimiter) {
|
|
286
|
-
delimiter = "/";
|
|
287
|
-
}
|
|
288
|
-
if (!prefix) {
|
|
289
|
-
prefix = "";
|
|
290
|
-
}
|
|
291
|
-
if (!prefix.endsWith(delimiter)) {
|
|
292
|
-
prefix += delimiter;
|
|
293
|
-
}
|
|
294
|
-
let items = [];
|
|
295
|
-
for (const [, file] of this._files) {
|
|
296
|
-
if (file.metadata.bucket != bucket) {
|
|
297
|
-
continue;
|
|
298
|
-
}
|
|
299
|
-
let name = file.metadata.name;
|
|
300
|
-
if (!name.startsWith(prefix)) {
|
|
301
|
-
continue;
|
|
302
|
-
}
|
|
303
|
-
name = name.substring(prefix.length);
|
|
304
|
-
if (name.startsWith(delimiter)) {
|
|
305
|
-
name = name.substring(prefix.length);
|
|
306
|
-
}
|
|
307
|
-
items.push(this.path(file.metadata.bucket, file.metadata.name));
|
|
308
|
-
}
|
|
309
|
-
items.sort();
|
|
310
|
-
if (pageToken) {
|
|
311
|
-
const idx = items.findIndex((v) => v == pageToken);
|
|
312
|
-
if (idx != -1) {
|
|
313
|
-
items = items.slice(idx);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
if (!maxResults) {
|
|
317
|
-
maxResults = 1000;
|
|
318
|
-
}
|
|
319
269
|
return {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
if (!storedFile) {
|
|
324
|
-
return console.warn(`No file ${item}`);
|
|
325
|
-
}
|
|
326
|
-
return new metadata_1.CloudStorageObjectMetadata(storedFile.metadata);
|
|
327
|
-
}),
|
|
270
|
+
nextPageToken,
|
|
271
|
+
prefixes: prefixes.size > 0 ? [...prefixes].sort() : undefined,
|
|
272
|
+
items: items.length > 0 ? items.map((item) => new metadata_1.CloudStorageObjectMetadata(item)) : undefined,
|
|
328
273
|
};
|
|
329
274
|
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
275
|
+
handleCreateDownloadToken(request) {
|
|
276
|
+
if (!this._adminCredsValidator.validate(request.authorization)) {
|
|
277
|
+
throw new errors_1.ForbiddenError();
|
|
278
|
+
}
|
|
279
|
+
const metadata = this.getMetadata(request.bucketId, request.decodedObjectId);
|
|
280
|
+
if (!metadata) {
|
|
281
|
+
throw new errors_1.NotFoundError();
|
|
335
282
|
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
return md;
|
|
283
|
+
metadata.addDownloadToken();
|
|
284
|
+
return metadata;
|
|
339
285
|
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
286
|
+
handleDeleteDownloadToken(request) {
|
|
287
|
+
if (!this._adminCredsValidator.validate(request.authorization)) {
|
|
288
|
+
throw new errors_1.ForbiddenError();
|
|
289
|
+
}
|
|
290
|
+
const metadata = this.getMetadata(request.bucketId, request.decodedObjectId);
|
|
291
|
+
if (!metadata) {
|
|
292
|
+
throw new errors_1.NotFoundError();
|
|
345
293
|
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
return md;
|
|
294
|
+
metadata.deleteDownloadToken(request.token);
|
|
295
|
+
return metadata;
|
|
349
296
|
}
|
|
350
297
|
path(bucket, object) {
|
|
351
|
-
|
|
352
|
-
const filename = path.basename(object) + (object.endsWith("/") ? "/" : "");
|
|
353
|
-
return path.join(bucket, directory, encodeURIComponent(filename));
|
|
298
|
+
return path.join(bucket, object);
|
|
354
299
|
}
|
|
355
300
|
get dirPath() {
|
|
356
301
|
return this._persistence.dirPath;
|
|
@@ -373,9 +318,7 @@ class StorageLayer {
|
|
|
373
318
|
try {
|
|
374
319
|
for (var _b = __asyncValues(this._files.entries()), _c; _c = await _b.next(), !_c.done;) {
|
|
375
320
|
const [p, file] = _c.value;
|
|
376
|
-
const metadataExportPath = path.join(metadataDirPath, p) + ".json";
|
|
377
|
-
const metadataExportDirPath = path.dirname(metadataExportPath);
|
|
378
|
-
await fse.ensureDir(metadataExportDirPath);
|
|
321
|
+
const metadataExportPath = path.join(metadataDirPath, encodeURIComponent(p)) + ".json";
|
|
379
322
|
await fse.writeFile(metadataExportPath, metadata_1.StoredFileMetadata.toJSON(file.metadata));
|
|
380
323
|
}
|
|
381
324
|
}
|
|
@@ -389,7 +332,7 @@ class StorageLayer {
|
|
|
389
332
|
}
|
|
390
333
|
import(storageExportPath) {
|
|
391
334
|
const bucketsFile = path.join(storageExportPath, "buckets.json");
|
|
392
|
-
const bucketsList = JSON.parse(
|
|
335
|
+
const bucketsList = JSON.parse((0, fs_1.readFileSync)(bucketsFile, "utf-8"));
|
|
393
336
|
for (const b of bucketsList.buckets) {
|
|
394
337
|
const bucketMetadata = new metadata_1.CloudStorageBucketMetadata(b.id);
|
|
395
338
|
this._buckets.set(b.id, bucketMetadata);
|
|
@@ -403,11 +346,11 @@ class StorageLayer {
|
|
|
403
346
|
logger_1.logger.debug(`Skipping unexpected storage metadata file: ${f}`);
|
|
404
347
|
continue;
|
|
405
348
|
}
|
|
406
|
-
const metadata = metadata_1.StoredFileMetadata.fromJSON(
|
|
349
|
+
const metadata = metadata_1.StoredFileMetadata.fromJSON((0, fs_1.readFileSync)(f, "utf-8"), this._cloudFunctions);
|
|
407
350
|
const metadataRelPath = path.relative(metadataDir, f);
|
|
408
351
|
const blobPath = metadataRelPath.substring(0, metadataRelPath.length - dotJson.length);
|
|
409
352
|
const blobAbsPath = path.join(blobsDir, blobPath);
|
|
410
|
-
if (!
|
|
353
|
+
if (!(0, fs_1.existsSync)(blobAbsPath)) {
|
|
411
354
|
logger_1.logger.warn(`Could not find file "${blobPath}" in storage export.`);
|
|
412
355
|
continue;
|
|
413
356
|
}
|
|
@@ -417,10 +360,10 @@ class StorageLayer {
|
|
|
417
360
|
fse.copySync(blobsDir, this.dirPath);
|
|
418
361
|
}
|
|
419
362
|
*walkDirSync(dir) {
|
|
420
|
-
const files =
|
|
363
|
+
const files = (0, fs_1.readdirSync)(dir);
|
|
421
364
|
for (const file of files) {
|
|
422
365
|
const p = path.join(dir, file);
|
|
423
|
-
if (
|
|
366
|
+
if ((0, fs_1.statSync)(p).isDirectory()) {
|
|
424
367
|
yield* this.walkDirSync(p);
|
|
425
368
|
}
|
|
426
369
|
else {
|
|
@@ -430,87 +373,3 @@ class StorageLayer {
|
|
|
430
373
|
}
|
|
431
374
|
}
|
|
432
375
|
exports.StorageLayer = StorageLayer;
|
|
433
|
-
class Persistence {
|
|
434
|
-
constructor(dirPath) {
|
|
435
|
-
this._dirPath = dirPath;
|
|
436
|
-
if (!(0, fs_1.existsSync)(dirPath)) {
|
|
437
|
-
(0, fs_1.mkdirSync)(dirPath, {
|
|
438
|
-
recursive: true,
|
|
439
|
-
});
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
get dirPath() {
|
|
443
|
-
return this._dirPath;
|
|
444
|
-
}
|
|
445
|
-
appendBytes(fileName, bytes, fileOffset) {
|
|
446
|
-
const filepath = this.getDiskPath(fileName);
|
|
447
|
-
const encodedSlashIndex = filepath.toLowerCase().lastIndexOf("%2f");
|
|
448
|
-
const dirPath = encodedSlashIndex >= 0 ? filepath.substring(0, encodedSlashIndex) : path.dirname(filepath);
|
|
449
|
-
if (!(0, fs_1.existsSync)(dirPath)) {
|
|
450
|
-
(0, fs_1.mkdirSync)(dirPath, {
|
|
451
|
-
recursive: true,
|
|
452
|
-
});
|
|
453
|
-
}
|
|
454
|
-
let fd;
|
|
455
|
-
try {
|
|
456
|
-
fs.appendFileSync(filepath, bytes);
|
|
457
|
-
return filepath;
|
|
458
|
-
}
|
|
459
|
-
finally {
|
|
460
|
-
if (fd) {
|
|
461
|
-
(0, fs_1.closeSync)(fd);
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
readBytes(fileName, size, fileOffset) {
|
|
466
|
-
const path = this.getDiskPath(fileName);
|
|
467
|
-
let fd;
|
|
468
|
-
try {
|
|
469
|
-
fd = (0, fs_1.openSync)(path, "r");
|
|
470
|
-
const buf = Buffer.alloc(size);
|
|
471
|
-
const offset = fileOffset && fileOffset > 0 ? fileOffset : 0;
|
|
472
|
-
(0, fs_1.readSync)(fd, buf, 0, size, offset);
|
|
473
|
-
return buf;
|
|
474
|
-
}
|
|
475
|
-
finally {
|
|
476
|
-
if (fd) {
|
|
477
|
-
(0, fs_1.closeSync)(fd);
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
deleteFile(fileName, failSilently = false) {
|
|
482
|
-
try {
|
|
483
|
-
(0, fs_1.unlinkSync)(this.getDiskPath(fileName));
|
|
484
|
-
}
|
|
485
|
-
catch (err) {
|
|
486
|
-
if (!failSilently) {
|
|
487
|
-
throw err;
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
deleteAll() {
|
|
492
|
-
return new Promise((resolve, reject) => {
|
|
493
|
-
rimraf(this._dirPath, (err) => {
|
|
494
|
-
if (err) {
|
|
495
|
-
reject(err);
|
|
496
|
-
}
|
|
497
|
-
else {
|
|
498
|
-
resolve();
|
|
499
|
-
}
|
|
500
|
-
});
|
|
501
|
-
});
|
|
502
|
-
}
|
|
503
|
-
renameFile(oldName, newName) {
|
|
504
|
-
const dirPath = this.getDiskPath(path.dirname(newName));
|
|
505
|
-
if (!(0, fs_1.existsSync)(dirPath)) {
|
|
506
|
-
(0, fs_1.mkdirSync)(dirPath, {
|
|
507
|
-
recursive: true,
|
|
508
|
-
});
|
|
509
|
-
}
|
|
510
|
-
(0, fs_1.renameSync)(this.getDiskPath(oldName), this.getDiskPath(newName));
|
|
511
|
-
}
|
|
512
|
-
getDiskPath(fileName) {
|
|
513
|
-
return path.join(this._dirPath, fileName);
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
exports.Persistence = Persistence;
|