firebase-tools 10.2.2 → 10.4.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/commands/deploy.js +1 -1
- package/lib/commands/experimental-functions-shell.js +1 -1
- package/lib/commands/ext-configure.js +68 -7
- package/lib/commands/ext-export.js +10 -9
- package/lib/commands/ext-install.js +73 -9
- package/lib/commands/ext-uninstall.js +9 -0
- package/lib/commands/ext-update.js +58 -3
- package/lib/commands/functions-config-export.js +2 -2
- package/lib/commands/functions-shell.js +1 -1
- package/lib/commands/hosting-channel-create.js +2 -2
- package/lib/commands/hosting-channel-delete.js +2 -2
- package/lib/commands/hosting-channel-deploy.js +2 -2
- package/lib/commands/hosting-channel-list.js +2 -2
- package/lib/commands/hosting-channel-open.js +2 -2
- package/lib/commands/hosting-sites-delete.js +2 -2
- package/lib/commands/serve.js +1 -1
- package/lib/commands/target-apply.js +2 -2
- package/lib/commands/target-clear.js +2 -2
- package/lib/commands/target-remove.js +2 -2
- package/lib/commands/target.js +2 -2
- package/lib/config.js +9 -3
- package/lib/deploy/extensions/planner.js +15 -9
- package/lib/deploy/functions/backend.js +10 -1
- package/lib/deploy/functions/checkIam.js +4 -4
- package/lib/deploy/functions/prepare.js +2 -1
- package/lib/deploy/functions/release/fabricator.js +4 -4
- package/lib/deploy/functions/release/planner.js +34 -20
- package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +6 -1
- package/lib/deploy/functions/runtimes/node/index.js +27 -0
- package/lib/deploy/functions/runtimes/node/parseTriggers.js +36 -13
- 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/emulator/auth/apiSpec.js +37 -0
- package/lib/emulator/commandUtils.js +2 -2
- package/lib/emulator/constants.js +1 -0
- package/lib/emulator/controller.js +9 -7
- package/lib/emulator/extensions/validation.js +37 -2
- package/lib/emulator/extensionsEmulator.js +47 -9
- package/lib/emulator/functionsEmulator.js +17 -12
- package/lib/emulator/functionsEmulatorShared.js +34 -11
- package/lib/emulator/storage/apis/firebase.js +316 -341
- package/lib/emulator/storage/apis/gcloud.js +238 -113
- package/lib/emulator/storage/crc.js +5 -1
- package/lib/emulator/storage/errors.js +9 -0
- package/lib/emulator/storage/files.js +161 -304
- package/lib/emulator/storage/index.js +25 -74
- package/lib/emulator/storage/metadata.js +63 -49
- package/lib/emulator/storage/multipart.js +62 -0
- package/lib/emulator/storage/persistence.js +78 -0
- package/lib/emulator/storage/rules/config.js +34 -0
- package/lib/emulator/storage/rules/manager.js +98 -0
- package/lib/emulator/storage/rules/runtime.js +4 -0
- 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/extensions/askUserForParam.js +77 -28
- package/lib/extensions/emulator/optionsHelper.js +35 -3
- package/lib/extensions/extensionsHelper.js +19 -10
- package/lib/extensions/manifest.js +142 -14
- package/lib/extensions/paramHelper.js +32 -9
- package/lib/fsutils.js +14 -1
- package/lib/functions/env.js +4 -6
- package/lib/functions/events/v2.js +11 -0
- package/lib/gcp/cloudfunctions.js +20 -7
- package/lib/gcp/cloudfunctionsv2.js +30 -12
- package/lib/gcp/resourceManager.js +4 -4
- package/lib/requireConfig.js +11 -9
- package/lib/serve/functions.js +2 -1
- package/lib/utils.js +14 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/lib/deploy/extensions/params.js +0 -42
- package/lib/deploy/functions/eventTypes.js +0 -10
- package/lib/prepareUpload.js +0 -44
|
@@ -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,98 +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
44
|
if (req.query.alt === "media") {
|
|
30
|
-
return sendFileBytes(
|
|
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
|
-
|
|
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);
|
|
123
|
+
}
|
|
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));
|
|
90
144
|
});
|
|
91
|
-
gcloudStorageAPI.post("/b/:bucketId/o/:objectId/acl", (req, res) => {
|
|
145
|
+
gcloudStorageAPI.post("/b/:bucketId/o/:objectId/acl", async (req, res) => {
|
|
92
146
|
var _a, _b;
|
|
93
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.");
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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;
|
|
98
163
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
164
|
+
const { metadata } = getObjectResponse;
|
|
165
|
+
metadata.update({});
|
|
166
|
+
return res.json({
|
|
102
167
|
kind: "storage#objectAccessControl",
|
|
103
|
-
object:
|
|
104
|
-
id: `${req.params.bucketId}/${
|
|
105
|
-
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/${
|
|
106
|
-
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,
|
|
107
172
|
entity: req.body.entity,
|
|
108
173
|
role: req.body.role,
|
|
109
174
|
etag: "someEtag",
|
|
110
|
-
generation:
|
|
111
|
-
})
|
|
112
|
-
.status(200);
|
|
175
|
+
generation: metadata.generation.toString(),
|
|
176
|
+
});
|
|
113
177
|
});
|
|
114
|
-
gcloudStorageAPI.post("/upload/storage/v1/b/:bucketId/o", (req, res) => {
|
|
178
|
+
gcloudStorageAPI.post("/upload/storage/v1/b/:bucketId/o", async (req, res) => {
|
|
115
179
|
if (!req.query.name) {
|
|
116
180
|
res.sendStatus(400);
|
|
117
181
|
return;
|
|
@@ -120,69 +184,112 @@ function createCloudEndpoints(emulator) {
|
|
|
120
184
|
if (name.startsWith("/")) {
|
|
121
185
|
name = name.slice(1);
|
|
122
186
|
}
|
|
123
|
-
const
|
|
124
|
-
if (!
|
|
125
|
-
res.sendStatus(400);
|
|
126
|
-
return;
|
|
187
|
+
const contentTypeHeader = req.header("content-type") || req.header("x-upload-content-type");
|
|
188
|
+
if (!contentTypeHeader) {
|
|
189
|
+
return res.sendStatus(400);
|
|
127
190
|
}
|
|
128
191
|
if (req.query.uploadType === "resumable") {
|
|
129
|
-
const upload = storageLayer.startUpload(req.params.bucketId, name, contentType, req.body);
|
|
130
192
|
const emulatorInfo = registry_1.EmulatorRegistry.getInfo(types_1.Emulators.STORAGE);
|
|
131
193
|
if (emulatorInfo === undefined) {
|
|
132
|
-
res.sendStatus(500);
|
|
133
|
-
return;
|
|
194
|
+
return res.sendStatus(500);
|
|
134
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
|
+
});
|
|
135
202
|
const { host, port } = emulatorInfo;
|
|
136
|
-
const uploadUrl = `http://${host}:${port}/upload/storage/v1/b/${
|
|
137
|
-
res.header("location", uploadUrl).
|
|
138
|
-
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);
|
|
139
205
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
206
|
+
let metadataRaw;
|
|
207
|
+
let dataRaw;
|
|
208
|
+
try {
|
|
209
|
+
({ metadataRaw, dataRaw } = (0, multipart_1.parseObjectUploadMultipartRequest)(contentTypeHeader, await reqBodyToBuffer(req)));
|
|
143
210
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
res.sendStatus(400);
|
|
152
|
-
return;
|
|
211
|
+
catch (err) {
|
|
212
|
+
return res.status(400).json({
|
|
213
|
+
error: {
|
|
214
|
+
code: 400,
|
|
215
|
+
message: err,
|
|
216
|
+
},
|
|
217
|
+
});
|
|
153
218
|
}
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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);
|
|
162
229
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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;
|
|
235
|
+
}
|
|
236
|
+
return res.status(200).json(new metadata_1.CloudStorageObjectMetadata(metadata));
|
|
237
|
+
});
|
|
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);
|
|
169
245
|
}
|
|
170
|
-
|
|
171
|
-
|
|
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);
|
|
172
256
|
});
|
|
173
|
-
gcloudStorageAPI.
|
|
257
|
+
gcloudStorageAPI.post("/b/:bucketId/o/:objectId/:method(rewriteTo|copyTo)/b/:destBucketId/o/:destObjectId", (req, res, next) => {
|
|
174
258
|
const md = storageLayer.getMetadata(req.params.bucketId, req.params.objectId);
|
|
175
259
|
if (!md) {
|
|
176
|
-
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);
|
|
177
268
|
return;
|
|
178
269
|
}
|
|
179
|
-
|
|
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
|
+
}
|
|
180
287
|
});
|
|
181
288
|
gcloudStorageAPI.all("/**", (req, res) => {
|
|
182
289
|
if (process.env.STORAGE_EMULATOR_DEBUG) {
|
|
183
290
|
console.table(req.headers);
|
|
184
291
|
console.log(req.method, req.url);
|
|
185
|
-
res.json("endpoint not implemented");
|
|
292
|
+
res.status(501).json("endpoint not implemented");
|
|
186
293
|
}
|
|
187
294
|
else {
|
|
188
295
|
res.sendStatus(501);
|
|
@@ -191,12 +298,7 @@ function createCloudEndpoints(emulator) {
|
|
|
191
298
|
return gcloudStorageAPI;
|
|
192
299
|
}
|
|
193
300
|
exports.createCloudEndpoints = createCloudEndpoints;
|
|
194
|
-
function sendFileBytes(md,
|
|
195
|
-
let data = storageLayer.getBytes(req.params.bucketId, req.params.objectId);
|
|
196
|
-
if (!data) {
|
|
197
|
-
res.sendStatus(404);
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
301
|
+
function sendFileBytes(md, data, req, res) {
|
|
200
302
|
const isGZipped = md.contentEncoding === "gzip";
|
|
201
303
|
if (isGZipped) {
|
|
202
304
|
data = (0, zlib_1.gunzipSync)(data);
|
|
@@ -204,19 +306,42 @@ function sendFileBytes(md, storageLayer, req, res) {
|
|
|
204
306
|
res.setHeader("Accept-Ranges", "bytes");
|
|
205
307
|
res.setHeader("Content-Type", md.contentType);
|
|
206
308
|
res.setHeader("Content-Disposition", md.contentDisposition);
|
|
207
|
-
res.setHeader("Content-Encoding",
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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));
|
|
217
321
|
}
|
|
218
322
|
else {
|
|
219
323
|
res.end(data);
|
|
220
324
|
}
|
|
221
|
-
|
|
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
|
+
}
|
|
222
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;
|