firebase-tools 10.2.0 → 10.3.0
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/apiv2.js +3 -0
- 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/auth-import.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 +58 -4
- package/lib/commands/ext-dev-init.js +49 -49
- package/lib/commands/ext-export.js +7 -2
- package/lib/commands/ext-install.js +163 -104
- package/lib/commands/ext-uninstall.js +17 -8
- package/lib/commands/ext-update.js +64 -11
- package/lib/commands/functions-config-clone.js +1 -1
- 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 +52 -15
- 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/client.js +9 -0
- package/lib/deploy/hosting/convertConfig.js +6 -0
- package/lib/deploy/hosting/deploy.js +2 -2
- package/lib/deploy/hosting/hashcache.js +21 -19
- package/lib/deploy/hosting/index.js +5 -5
- package/lib/deploy/hosting/prepare.js +25 -25
- package/lib/deploy/hosting/release.js +21 -24
- 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/commandUtils.js +5 -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 +30 -13
- 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 +106 -44
- package/lib/emulator/functionsEmulatorRuntime.js +44 -36
- package/lib/emulator/functionsEmulatorShared.js +17 -10
- 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 +1 -1
- 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/listExtensions.js +2 -0
- package/lib/extensions/manifest.js +144 -0
- package/lib/extensions/metricsUtils.js +4 -4
- package/lib/extensions/paramHelper.js +9 -8
- 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 +42 -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/gcp/storage.js +1 -0
- package/lib/hosting/api.js +1 -1
- package/lib/hosting/functionsProxy.js +15 -5
- 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/responseToError.js +16 -7
- package/lib/serve/functions.js +2 -1
- package/lib/serve/hosting.js +1 -1
- package/lib/utils.js +15 -2
- package/npm-shrinkwrap.json +904 -412
- package/package.json +3 -3
- package/schema/firebase-config.json +32 -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,9 +7,13 @@ const types_1 = require("../../types");
|
|
|
7
7
|
const metadata_1 = require("../metadata");
|
|
8
8
|
const registry_1 = require("../../registry");
|
|
9
9
|
const emulatorLogger_1 = require("../../emulatorLogger");
|
|
10
|
+
const crc_1 = require("../crc");
|
|
11
|
+
const multipart_1 = require("../multipart");
|
|
12
|
+
const upload_1 = require("../upload");
|
|
13
|
+
const errors_1 = require("../errors");
|
|
10
14
|
function createCloudEndpoints(emulator) {
|
|
11
15
|
const gcloudStorageAPI = (0, express_1.Router)();
|
|
12
|
-
const { storageLayer } = emulator;
|
|
16
|
+
const { storageLayer, uploadService } = emulator;
|
|
13
17
|
gcloudStorageAPI.use(/.*\/b\/(.+?)\/.*/, (req, res, next) => {
|
|
14
18
|
storageLayer.createBucket(req.params[0]);
|
|
15
19
|
next();
|
|
@@ -20,103 +24,158 @@ function createCloudEndpoints(emulator) {
|
|
|
20
24
|
items: await storageLayer.listBuckets(),
|
|
21
25
|
});
|
|
22
26
|
});
|
|
23
|
-
gcloudStorageAPI.get(["/b/:bucketId/o/:objectId", "/download/storage/v1/b/:bucketId/o/:objectId"], (req, res) => {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
gcloudStorageAPI.get(["/b/:bucketId/o/:objectId", "/download/storage/v1/b/:bucketId/o/:objectId"], async (req, res) => {
|
|
28
|
+
let getObjectResponse;
|
|
29
|
+
try {
|
|
30
|
+
getObjectResponse = await storageLayer.handleGetObject({
|
|
31
|
+
bucketId: req.params.bucketId,
|
|
32
|
+
decodedObjectId: req.params.objectId,
|
|
33
|
+
}, true);
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
if (err instanceof errors_1.NotFoundError) {
|
|
37
|
+
return sendObjectNotFound(req, res);
|
|
38
|
+
}
|
|
39
|
+
if (err instanceof errors_1.ForbiddenError) {
|
|
40
|
+
throw new Error("Request failed unexpectedly due to Firebase Rules.");
|
|
41
|
+
}
|
|
42
|
+
throw err;
|
|
28
43
|
}
|
|
29
|
-
if (req.query.alt
|
|
30
|
-
return sendFileBytes(
|
|
44
|
+
if (req.query.alt === "media") {
|
|
45
|
+
return sendFileBytes(getObjectResponse.metadata, getObjectResponse.data, req, res);
|
|
31
46
|
}
|
|
32
|
-
|
|
33
|
-
res.json(outgoingMd).status(200).send();
|
|
34
|
-
return;
|
|
47
|
+
return res.json(new metadata_1.CloudStorageObjectMetadata(getObjectResponse.metadata));
|
|
35
48
|
});
|
|
36
|
-
gcloudStorageAPI.patch("/b/:bucketId/o/:objectId", (req, res) => {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
49
|
+
gcloudStorageAPI.patch("/b/:bucketId/o/:objectId", async (req, res) => {
|
|
50
|
+
let updatedMetadata;
|
|
51
|
+
try {
|
|
52
|
+
updatedMetadata = await storageLayer.handleUpdateObjectMetadata({
|
|
53
|
+
bucketId: req.params.bucketId,
|
|
54
|
+
decodedObjectId: req.params.objectId,
|
|
55
|
+
metadata: req.body,
|
|
56
|
+
}, true);
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
if (err instanceof errors_1.NotFoundError) {
|
|
60
|
+
return sendObjectNotFound(req, res);
|
|
61
|
+
}
|
|
62
|
+
if (err instanceof errors_1.ForbiddenError) {
|
|
63
|
+
throw new Error("Request failed unexpectedly due to Firebase Rules.");
|
|
64
|
+
}
|
|
65
|
+
throw err;
|
|
41
66
|
}
|
|
42
|
-
|
|
43
|
-
const outgoingMetadata = new metadata_1.CloudStorageObjectMetadata(md);
|
|
44
|
-
res.json(outgoingMetadata).status(200).send();
|
|
45
|
-
return;
|
|
67
|
+
return res.json(new metadata_1.CloudStorageObjectMetadata(updatedMetadata));
|
|
46
68
|
});
|
|
47
69
|
gcloudStorageAPI.get("/b/:bucketId/o", (req, res) => {
|
|
48
70
|
let maxRes = undefined;
|
|
49
71
|
if (req.query.maxResults) {
|
|
50
72
|
maxRes = +req.query.maxResults.toString();
|
|
51
73
|
}
|
|
52
|
-
const delimiter = req.query.delimiter ? req.query.delimiter.toString() : "
|
|
74
|
+
const delimiter = req.query.delimiter ? req.query.delimiter.toString() : "";
|
|
53
75
|
const pageToken = req.query.pageToken ? req.query.pageToken.toString() : undefined;
|
|
54
76
|
const prefix = req.query.prefix ? req.query.prefix.toString() : "";
|
|
55
77
|
const listResult = storageLayer.listItems(req.params.bucketId, prefix, delimiter, pageToken, maxRes);
|
|
56
|
-
res.json(listResult);
|
|
78
|
+
res.json(Object.assign(Object.assign({}, listResult), { kind: "#storage/objects" }));
|
|
57
79
|
});
|
|
58
|
-
gcloudStorageAPI.delete("/b/:bucketId/o/:objectId", (req, res) => {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
80
|
+
gcloudStorageAPI.delete("/b/:bucketId/o/:objectId", async (req, res) => {
|
|
81
|
+
try {
|
|
82
|
+
await storageLayer.handleDeleteObject({
|
|
83
|
+
bucketId: req.params.bucketId,
|
|
84
|
+
decodedObjectId: req.params.objectId,
|
|
85
|
+
}, true);
|
|
63
86
|
}
|
|
64
|
-
|
|
65
|
-
|
|
87
|
+
catch (err) {
|
|
88
|
+
if (err instanceof errors_1.NotFoundError) {
|
|
89
|
+
return sendObjectNotFound(req, res);
|
|
90
|
+
}
|
|
91
|
+
if (err instanceof errors_1.ForbiddenError) {
|
|
92
|
+
throw new Error("Request failed unexpectedly due to Firebase Rules.");
|
|
93
|
+
}
|
|
94
|
+
throw err;
|
|
95
|
+
}
|
|
96
|
+
return res.sendStatus(204);
|
|
66
97
|
});
|
|
67
|
-
|
|
68
|
-
if (
|
|
69
|
-
|
|
70
|
-
return;
|
|
98
|
+
const reqBodyToBuffer = async (req) => {
|
|
99
|
+
if (req.body instanceof Buffer) {
|
|
100
|
+
return Buffer.from(req.body);
|
|
71
101
|
}
|
|
72
|
-
const uploadId = req.query.upload_id.toString();
|
|
73
102
|
const bufs = [];
|
|
74
103
|
req.on("data", (data) => {
|
|
75
104
|
bufs.push(data);
|
|
76
105
|
});
|
|
77
106
|
await new Promise((resolve) => {
|
|
78
107
|
req.on("end", () => {
|
|
79
|
-
req.body = Buffer.concat(bufs);
|
|
80
108
|
resolve();
|
|
81
109
|
});
|
|
82
110
|
});
|
|
83
|
-
|
|
84
|
-
|
|
111
|
+
return Buffer.concat(bufs);
|
|
112
|
+
};
|
|
113
|
+
gcloudStorageAPI.put("/upload/storage/v1/b/:bucketId/o", async (req, res) => {
|
|
114
|
+
if (!req.query.upload_id) {
|
|
85
115
|
res.sendStatus(400);
|
|
86
116
|
return;
|
|
87
117
|
}
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
118
|
+
const uploadId = req.query.upload_id.toString();
|
|
119
|
+
let upload;
|
|
120
|
+
try {
|
|
121
|
+
uploadService.continueResumableUpload(uploadId, await reqBodyToBuffer(req));
|
|
122
|
+
upload = uploadService.finalizeResumableUpload(uploadId);
|
|
92
123
|
}
|
|
93
|
-
|
|
94
|
-
|
|
124
|
+
catch (err) {
|
|
125
|
+
if (err instanceof errors_1.NotFoundError) {
|
|
126
|
+
return res.sendStatus(404);
|
|
127
|
+
}
|
|
128
|
+
else if (err instanceof upload_1.UploadNotActiveError) {
|
|
129
|
+
return res.sendStatus(400);
|
|
130
|
+
}
|
|
131
|
+
throw err;
|
|
132
|
+
}
|
|
133
|
+
let metadata;
|
|
134
|
+
try {
|
|
135
|
+
metadata = await storageLayer.handleUploadObject(upload, true);
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
if (err instanceof errors_1.ForbiddenError) {
|
|
139
|
+
throw new Error("Request failed unexpectedly due to Firebase Rules.");
|
|
140
|
+
}
|
|
141
|
+
throw err;
|
|
142
|
+
}
|
|
143
|
+
return res.json(new metadata_1.CloudStorageObjectMetadata(metadata));
|
|
95
144
|
});
|
|
96
|
-
gcloudStorageAPI.post("/b/:bucketId/o/:objectId/acl", (req, res) => {
|
|
145
|
+
gcloudStorageAPI.post("/b/:bucketId/o/:objectId/acl", async (req, res) => {
|
|
97
146
|
var _a, _b;
|
|
98
147
|
emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.STORAGE).log("WARN_ONCE", "Cloud Storage ACLs are not supported in the Storage Emulator. All related methods will succeed, but have no effect.");
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
148
|
+
let getObjectResponse;
|
|
149
|
+
try {
|
|
150
|
+
getObjectResponse = await storageLayer.handleGetObject({
|
|
151
|
+
bucketId: req.params.bucketId,
|
|
152
|
+
decodedObjectId: req.params.objectId,
|
|
153
|
+
}, true);
|
|
154
|
+
}
|
|
155
|
+
catch (err) {
|
|
156
|
+
if (err instanceof errors_1.NotFoundError) {
|
|
157
|
+
return sendObjectNotFound(req, res);
|
|
158
|
+
}
|
|
159
|
+
if (err instanceof errors_1.ForbiddenError) {
|
|
160
|
+
throw new Error("Request failed unexpectedly due to Firebase Rules.");
|
|
161
|
+
}
|
|
162
|
+
throw err;
|
|
103
163
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
164
|
+
const { metadata } = getObjectResponse;
|
|
165
|
+
metadata.update({});
|
|
166
|
+
return res.json({
|
|
107
167
|
kind: "storage#objectAccessControl",
|
|
108
|
-
object:
|
|
109
|
-
id: `${req.params.bucketId}/${
|
|
110
|
-
selfLink: `http://${(_a = registry_1.EmulatorRegistry.getInfo(types_1.Emulators.STORAGE)) === null || _a === void 0 ? void 0 : _a.host}:${(_b = registry_1.EmulatorRegistry.getInfo(types_1.Emulators.STORAGE)) === null || _b === void 0 ? void 0 : _b.port}/storage/v1/b/${
|
|
111
|
-
bucket:
|
|
168
|
+
object: metadata.name,
|
|
169
|
+
id: `${req.params.bucketId}/${metadata.name}/${metadata.generation}/allUsers`,
|
|
170
|
+
selfLink: `http://${(_a = registry_1.EmulatorRegistry.getInfo(types_1.Emulators.STORAGE)) === null || _a === void 0 ? void 0 : _a.host}:${(_b = registry_1.EmulatorRegistry.getInfo(types_1.Emulators.STORAGE)) === null || _b === void 0 ? void 0 : _b.port}/storage/v1/b/${metadata.bucket}/o/${encodeURIComponent(metadata.name)}/acl/allUsers`,
|
|
171
|
+
bucket: metadata.bucket,
|
|
112
172
|
entity: req.body.entity,
|
|
113
173
|
role: req.body.role,
|
|
114
174
|
etag: "someEtag",
|
|
115
|
-
generation:
|
|
116
|
-
})
|
|
117
|
-
.status(200);
|
|
175
|
+
generation: metadata.generation.toString(),
|
|
176
|
+
});
|
|
118
177
|
});
|
|
119
|
-
gcloudStorageAPI.post("/upload/storage/v1/b/:bucketId/o", (req, res) => {
|
|
178
|
+
gcloudStorageAPI.post("/upload/storage/v1/b/:bucketId/o", async (req, res) => {
|
|
120
179
|
if (!req.query.name) {
|
|
121
180
|
res.sendStatus(400);
|
|
122
181
|
return;
|
|
@@ -125,69 +184,112 @@ function createCloudEndpoints(emulator) {
|
|
|
125
184
|
if (name.startsWith("/")) {
|
|
126
185
|
name = name.slice(1);
|
|
127
186
|
}
|
|
128
|
-
const
|
|
129
|
-
if (!
|
|
130
|
-
res.sendStatus(400);
|
|
131
|
-
return;
|
|
187
|
+
const contentTypeHeader = req.header("content-type") || req.header("x-upload-content-type");
|
|
188
|
+
if (!contentTypeHeader) {
|
|
189
|
+
return res.sendStatus(400);
|
|
132
190
|
}
|
|
133
|
-
if (req.query.uploadType
|
|
134
|
-
const upload = storageLayer.startUpload(req.params.bucketId, name, contentType, req.body);
|
|
191
|
+
if (req.query.uploadType === "resumable") {
|
|
135
192
|
const emulatorInfo = registry_1.EmulatorRegistry.getInfo(types_1.Emulators.STORAGE);
|
|
136
|
-
if (emulatorInfo
|
|
137
|
-
res.sendStatus(500);
|
|
138
|
-
return;
|
|
193
|
+
if (emulatorInfo === undefined) {
|
|
194
|
+
return res.sendStatus(500);
|
|
139
195
|
}
|
|
196
|
+
const upload = uploadService.startResumableUpload({
|
|
197
|
+
bucketId: req.params.bucketId,
|
|
198
|
+
objectId: name,
|
|
199
|
+
metadataRaw: JSON.stringify(req.body),
|
|
200
|
+
authorization: req.header("authorization"),
|
|
201
|
+
});
|
|
140
202
|
const { host, port } = emulatorInfo;
|
|
141
|
-
const uploadUrl = `http://${host}:${port}/upload/storage/v1/b/${
|
|
142
|
-
res.header("location", uploadUrl).
|
|
143
|
-
return;
|
|
203
|
+
const uploadUrl = `http://${host}:${port}/upload/storage/v1/b/${req.params.bucketId}/o?name=${name}&uploadType=resumable&upload_id=${upload.id}`;
|
|
204
|
+
return res.header("location", uploadUrl).sendStatus(200);
|
|
144
205
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
206
|
+
let metadataRaw;
|
|
207
|
+
let dataRaw;
|
|
208
|
+
try {
|
|
209
|
+
({ metadataRaw, dataRaw } = (0, multipart_1.parseObjectUploadMultipartRequest)(contentTypeHeader, await reqBodyToBuffer(req)));
|
|
148
210
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
res.sendStatus(400);
|
|
157
|
-
return;
|
|
211
|
+
catch (err) {
|
|
212
|
+
return res.status(400).json({
|
|
213
|
+
error: {
|
|
214
|
+
code: 400,
|
|
215
|
+
message: err,
|
|
216
|
+
},
|
|
217
|
+
});
|
|
158
218
|
}
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
219
|
+
const upload = uploadService.multipartUpload({
|
|
220
|
+
bucketId: req.params.bucketId,
|
|
221
|
+
objectId: name,
|
|
222
|
+
metadataRaw: metadataRaw,
|
|
223
|
+
dataRaw: dataRaw,
|
|
224
|
+
authorization: req.header("authorization"),
|
|
225
|
+
});
|
|
226
|
+
let metadata;
|
|
227
|
+
try {
|
|
228
|
+
metadata = await storageLayer.handleUploadObject(upload, true);
|
|
167
229
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
return;
|
|
230
|
+
catch (err) {
|
|
231
|
+
if (err instanceof errors_1.ForbiddenError) {
|
|
232
|
+
throw new Error("Request failed unexpectedly due to Firebase Rules.");
|
|
233
|
+
}
|
|
234
|
+
throw err;
|
|
174
235
|
}
|
|
175
|
-
res.status(200).json(new metadata_1.CloudStorageObjectMetadata(metadata))
|
|
176
|
-
return;
|
|
236
|
+
return res.status(200).json(new metadata_1.CloudStorageObjectMetadata(metadata));
|
|
177
237
|
});
|
|
178
|
-
gcloudStorageAPI.get("/:bucketId/:objectId(**)", (req, res) => {
|
|
238
|
+
gcloudStorageAPI.get("/:bucketId/:objectId(**)", async (req, res) => {
|
|
239
|
+
let getObjectResponse;
|
|
240
|
+
try {
|
|
241
|
+
getObjectResponse = await storageLayer.handleGetObject({
|
|
242
|
+
bucketId: req.params.bucketId,
|
|
243
|
+
decodedObjectId: req.params.objectId,
|
|
244
|
+
}, true);
|
|
245
|
+
}
|
|
246
|
+
catch (err) {
|
|
247
|
+
if (err instanceof errors_1.NotFoundError) {
|
|
248
|
+
return sendObjectNotFound(req, res);
|
|
249
|
+
}
|
|
250
|
+
if (err instanceof errors_1.ForbiddenError) {
|
|
251
|
+
throw new Error("Request failed unexpectedly due to Firebase Rules.");
|
|
252
|
+
}
|
|
253
|
+
throw err;
|
|
254
|
+
}
|
|
255
|
+
return sendFileBytes(getObjectResponse.metadata, getObjectResponse.data, req, res);
|
|
256
|
+
});
|
|
257
|
+
gcloudStorageAPI.post("/b/:bucketId/o/:objectId/:method(rewriteTo|copyTo)/b/:destBucketId/o/:destObjectId", (req, res, next) => {
|
|
179
258
|
const md = storageLayer.getMetadata(req.params.bucketId, req.params.objectId);
|
|
180
259
|
if (!md) {
|
|
181
|
-
res
|
|
260
|
+
return sendObjectNotFound(req, res);
|
|
261
|
+
}
|
|
262
|
+
if (req.params.method === "rewriteTo" && req.query.rewriteToken) {
|
|
263
|
+
return next();
|
|
264
|
+
}
|
|
265
|
+
const metadata = storageLayer.copyFile(md, req.params.destBucketId, req.params.destObjectId, req.body);
|
|
266
|
+
if (!metadata) {
|
|
267
|
+
res.sendStatus(400);
|
|
182
268
|
return;
|
|
183
269
|
}
|
|
184
|
-
|
|
270
|
+
const resource = new metadata_1.CloudStorageObjectMetadata(metadata);
|
|
271
|
+
res.status(200);
|
|
272
|
+
if (req.params.method === "copyTo") {
|
|
273
|
+
return res.json(resource);
|
|
274
|
+
}
|
|
275
|
+
else if (req.params.method === "rewriteTo") {
|
|
276
|
+
return res.json({
|
|
277
|
+
kind: "storage#rewriteResponse",
|
|
278
|
+
totalBytesRewritten: String(metadata.size),
|
|
279
|
+
objectSize: String(metadata.size),
|
|
280
|
+
done: true,
|
|
281
|
+
resource,
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
return next();
|
|
286
|
+
}
|
|
185
287
|
});
|
|
186
288
|
gcloudStorageAPI.all("/**", (req, res) => {
|
|
187
289
|
if (process.env.STORAGE_EMULATOR_DEBUG) {
|
|
188
290
|
console.table(req.headers);
|
|
189
291
|
console.log(req.method, req.url);
|
|
190
|
-
res.json("endpoint not implemented");
|
|
292
|
+
res.status(501).json("endpoint not implemented");
|
|
191
293
|
}
|
|
192
294
|
else {
|
|
193
295
|
res.sendStatus(501);
|
|
@@ -196,32 +298,50 @@ function createCloudEndpoints(emulator) {
|
|
|
196
298
|
return gcloudStorageAPI;
|
|
197
299
|
}
|
|
198
300
|
exports.createCloudEndpoints = createCloudEndpoints;
|
|
199
|
-
function sendFileBytes(md,
|
|
200
|
-
|
|
201
|
-
if (!data) {
|
|
202
|
-
res.sendStatus(404);
|
|
203
|
-
return;
|
|
204
|
-
}
|
|
205
|
-
const isGZipped = md.contentEncoding == "gzip";
|
|
301
|
+
function sendFileBytes(md, data, req, res) {
|
|
302
|
+
const isGZipped = md.contentEncoding === "gzip";
|
|
206
303
|
if (isGZipped) {
|
|
207
304
|
data = (0, zlib_1.gunzipSync)(data);
|
|
208
305
|
}
|
|
209
306
|
res.setHeader("Accept-Ranges", "bytes");
|
|
210
307
|
res.setHeader("Content-Type", md.contentType);
|
|
211
308
|
res.setHeader("Content-Disposition", md.contentDisposition);
|
|
212
|
-
res.setHeader("Content-Encoding",
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
309
|
+
res.setHeader("Content-Encoding", md.contentEncoding);
|
|
310
|
+
res.setHeader("ETag", md.etag);
|
|
311
|
+
res.setHeader("Cache-Control", md.cacheControl);
|
|
312
|
+
res.setHeader("x-goog-generation", `${md.generation}`);
|
|
313
|
+
res.setHeader("x-goog-metadatageneration", `${md.metageneration}`);
|
|
314
|
+
res.setHeader("x-goog-storage-class", md.storageClass);
|
|
315
|
+
res.setHeader("x-goog-hash", `crc32c=${(0, crc_1.crc32cToString)(md.crc32c)},md5=${md.md5Hash}`);
|
|
316
|
+
const byteRange = req.range(data.byteLength, { combine: true });
|
|
317
|
+
if (Array.isArray(byteRange) && byteRange.type === "bytes" && byteRange.length > 0) {
|
|
318
|
+
const range = byteRange[0];
|
|
319
|
+
res.setHeader("Content-Range", `${byteRange.type} ${range.start}-${range.end}/${data.byteLength}`);
|
|
320
|
+
res.status(206).end(data.slice(range.start, range.end + 1));
|
|
222
321
|
}
|
|
223
322
|
else {
|
|
224
323
|
res.end(data);
|
|
225
324
|
}
|
|
226
|
-
|
|
325
|
+
}
|
|
326
|
+
function sendObjectNotFound(req, res) {
|
|
327
|
+
res.status(404);
|
|
328
|
+
const message = `No such object: ${req.params.bucketId}/${req.params.objectId}`;
|
|
329
|
+
if (req.method === "GET" && req.query.alt === "media") {
|
|
330
|
+
res.send(message);
|
|
331
|
+
}
|
|
332
|
+
else {
|
|
333
|
+
res.json({
|
|
334
|
+
error: {
|
|
335
|
+
code: 404,
|
|
336
|
+
message,
|
|
337
|
+
errors: [
|
|
338
|
+
{
|
|
339
|
+
message,
|
|
340
|
+
domain: "global",
|
|
341
|
+
reason: "notFound",
|
|
342
|
+
},
|
|
343
|
+
],
|
|
344
|
+
},
|
|
345
|
+
});
|
|
346
|
+
}
|
|
227
347
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.crc32c = void 0;
|
|
3
|
+
exports.crc32cToString = exports.crc32c = void 0;
|
|
4
4
|
function makeCRCTable(poly) {
|
|
5
5
|
let c;
|
|
6
6
|
const crcTable = [];
|
|
@@ -24,3 +24,7 @@ function crc32c(bytes) {
|
|
|
24
24
|
return (crc ^ -1) >>> 0;
|
|
25
25
|
}
|
|
26
26
|
exports.crc32c = crc32c;
|
|
27
|
+
function crc32cToString(crc32cValue) {
|
|
28
|
+
return "----" + Buffer.from([crc32cValue]).toString("base64");
|
|
29
|
+
}
|
|
30
|
+
exports.crc32cToString = crc32cToString;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ForbiddenError = exports.NotFoundError = void 0;
|
|
4
|
+
class NotFoundError extends Error {
|
|
5
|
+
}
|
|
6
|
+
exports.NotFoundError = NotFoundError;
|
|
7
|
+
class ForbiddenError extends Error {
|
|
8
|
+
}
|
|
9
|
+
exports.ForbiddenError = ForbiddenError;
|