firebase-tools 11.7.0 → 11.9.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/auth.js +1 -1
- package/lib/deploy/functions/build.js +39 -25
- package/lib/deploy/functions/cache/applyHash.js +29 -0
- package/lib/deploy/functions/cache/hash.js +30 -0
- package/lib/deploy/functions/cel.js +249 -0
- package/lib/deploy/functions/functionsDeployHelper.js +12 -1
- package/lib/deploy/functions/params.js +259 -102
- package/lib/deploy/functions/prepare.js +34 -4
- package/lib/deploy/functions/prepareFunctionsUpload.js +13 -5
- package/lib/deploy/functions/release/fabricator.js +17 -1
- package/lib/deploy/functions/release/planner.js +17 -0
- package/lib/deploy/functions/runtimes/node/parseTriggers.js +9 -0
- package/lib/deploy/functions/services/index.js +11 -0
- package/lib/deploy/functions/services/remoteConfig.js +14 -0
- package/lib/emulator/extensionsEmulator.js +1 -0
- package/lib/emulator/functionsEmulator.js +18 -59
- package/lib/emulator/functionsRuntimeWorker.js +38 -7
- package/lib/emulator/storage/apis/firebase.js +139 -125
- package/lib/emulator/storage/apis/gcloud.js +102 -42
- package/lib/emulator/storage/files.js +25 -15
- package/lib/emulator/storage/metadata.js +86 -56
- package/lib/emulator/storage/rules/runtime.js +10 -2
- package/lib/emulator/storage/upload.js +45 -9
- package/lib/extensions/extensionsHelper.js +1 -1
- package/lib/functions/constants.js +14 -0
- package/lib/functions/events/v2.js +2 -1
- package/lib/functions/secrets.js +8 -1
- package/lib/gcp/cloudfunctions.js +15 -18
- package/lib/gcp/cloudfunctionsv2.js +15 -18
- package/lib/gcp/cloudscheduler.js +2 -1
- package/lib/gcp/secretManager.js +15 -1
- package/lib/gcp/storage.js +15 -1
- package/lib/previews.js +1 -1
- package/lib/track.js +1 -1
- package/npm-shrinkwrap.json +54 -30
- package/package.json +6 -5
- package/templates/init/storage/storage.rules +1 -1
|
@@ -199,6 +199,15 @@ function addResourcesToBuild(projectId, runtime, annotation, want) {
|
|
|
199
199
|
return str;
|
|
200
200
|
});
|
|
201
201
|
proto.convertIfPresent(endpoint, annotation, "timeoutSeconds", "timeout", proto.secondsFromDuration);
|
|
202
|
+
if (annotation.secrets) {
|
|
203
|
+
endpoint.secretEnvironmentVariables = annotation.secrets.map((secret) => {
|
|
204
|
+
return {
|
|
205
|
+
secret,
|
|
206
|
+
projectId,
|
|
207
|
+
key: secret,
|
|
208
|
+
};
|
|
209
|
+
});
|
|
210
|
+
}
|
|
202
211
|
want.endpoints[endpointId] = endpoint;
|
|
203
212
|
}
|
|
204
213
|
exports.addResourcesToBuild = addResourcesToBuild;
|
|
@@ -6,6 +6,7 @@ const auth_1 = require("./auth");
|
|
|
6
6
|
const storage_1 = require("./storage");
|
|
7
7
|
const firebaseAlerts_1 = require("./firebaseAlerts");
|
|
8
8
|
const database_1 = require("./database");
|
|
9
|
+
const remoteConfig_1 = require("./remoteConfig");
|
|
9
10
|
const noop = () => Promise.resolve();
|
|
10
11
|
exports.noop = noop;
|
|
11
12
|
const noopProjectBindings = () => Promise.resolve([]);
|
|
@@ -55,6 +56,15 @@ const databaseService = {
|
|
|
55
56
|
registerTrigger: exports.noop,
|
|
56
57
|
unregisterTrigger: exports.noop,
|
|
57
58
|
};
|
|
59
|
+
const remoteConfigService = {
|
|
60
|
+
name: "remoteconfig",
|
|
61
|
+
api: "firebaseremoteconfig.googleapis.com",
|
|
62
|
+
requiredProjectBindings: exports.noopProjectBindings,
|
|
63
|
+
ensureTriggerRegion: remoteConfig_1.ensureRemoteConfigTriggerRegion,
|
|
64
|
+
validateTrigger: exports.noop,
|
|
65
|
+
registerTrigger: exports.noop,
|
|
66
|
+
unregisterTrigger: exports.noop,
|
|
67
|
+
};
|
|
58
68
|
const EVENT_SERVICE_MAPPING = {
|
|
59
69
|
"google.cloud.pubsub.topic.v1.messagePublished": pubSubService,
|
|
60
70
|
"google.cloud.storage.object.v1.finalized": storageService,
|
|
@@ -68,6 +78,7 @@ const EVENT_SERVICE_MAPPING = {
|
|
|
68
78
|
"google.firebase.database.ref.v1.created": databaseService,
|
|
69
79
|
"google.firebase.database.ref.v1.updated": databaseService,
|
|
70
80
|
"google.firebase.database.ref.v1.deleted": databaseService,
|
|
81
|
+
"google.firebase.remoteconfig.remoteConfig.v1.updated": remoteConfigService,
|
|
71
82
|
};
|
|
72
83
|
function serviceForEndpoint(endpoint) {
|
|
73
84
|
if (backend.isEventTriggered(endpoint)) {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ensureRemoteConfigTriggerRegion = void 0;
|
|
4
|
+
const error_1 = require("../../../error");
|
|
5
|
+
function ensureRemoteConfigTriggerRegion(endpoint) {
|
|
6
|
+
if (!endpoint.eventTrigger.region) {
|
|
7
|
+
endpoint.eventTrigger.region = "global";
|
|
8
|
+
}
|
|
9
|
+
if (endpoint.eventTrigger.region !== "global") {
|
|
10
|
+
throw new error_1.FirebaseError("A remote config trigger must specify 'global' trigger location");
|
|
11
|
+
}
|
|
12
|
+
return Promise.resolve();
|
|
13
|
+
}
|
|
14
|
+
exports.ensureRemoteConfigTriggerRegion = ensureRemoteConfigTriggerRegion;
|
|
@@ -54,7 +54,11 @@ class FunctionsEmulator {
|
|
|
54
54
|
const mode = this.args.debugPort
|
|
55
55
|
? types_1.FunctionsExecutionMode.SEQUENTIAL
|
|
56
56
|
: types_1.FunctionsExecutionMode.AUTO;
|
|
57
|
-
this.
|
|
57
|
+
this.workerPools = {};
|
|
58
|
+
for (const backend of this.args.emulatableBackends) {
|
|
59
|
+
const pool = new functionsRuntimeWorker_1.RuntimeWorkerPool(mode);
|
|
60
|
+
this.workerPools[backend.codebase] = pool;
|
|
61
|
+
}
|
|
58
62
|
this.workQueue = new workQueue_1.WorkQueue(mode);
|
|
59
63
|
}
|
|
60
64
|
static getHttpFunctionUrl(host, port, projectId, name, region) {
|
|
@@ -241,7 +245,9 @@ class FunctionsEmulator {
|
|
|
241
245
|
this.logger.logLabeled("WARN", "functions", "Functions emulator work queue did not empty before stopping");
|
|
242
246
|
}
|
|
243
247
|
this.workQueue.stop();
|
|
244
|
-
this.
|
|
248
|
+
for (const pool of Object.values(this.workerPools)) {
|
|
249
|
+
pool.exit();
|
|
250
|
+
}
|
|
245
251
|
if (this.destroyServer) {
|
|
246
252
|
await this.destroyServer();
|
|
247
253
|
}
|
|
@@ -273,7 +279,8 @@ class FunctionsEmulator {
|
|
|
273
279
|
projectAlias: this.args.projectAlias,
|
|
274
280
|
};
|
|
275
281
|
const discoveredBuild = await runtimeDelegate.discoverBuild(runtimeConfig, environment);
|
|
276
|
-
const
|
|
282
|
+
const resolution = await (0, build_1.resolveBackend)(discoveredBuild, userEnvOpt, environment);
|
|
283
|
+
const discoveredBackend = resolution.backend;
|
|
277
284
|
const endpoints = backend.allEndpoints(discoveredBackend);
|
|
278
285
|
(0, functionsEmulatorShared_1.prepareEndpoints)(endpoints);
|
|
279
286
|
for (const e of endpoints) {
|
|
@@ -297,7 +304,7 @@ class FunctionsEmulator {
|
|
|
297
304
|
this.logger.logLabeled("ERROR", "functions", `Failed to load function definition from source: ${e}`);
|
|
298
305
|
return;
|
|
299
306
|
}
|
|
300
|
-
this.
|
|
307
|
+
this.workerPools[emulatableBackend.codebase].refresh();
|
|
301
308
|
this.blockingFunctionsConfig = {};
|
|
302
309
|
const toSetup = triggerDefinitions.filter((definition) => {
|
|
303
310
|
if (force) {
|
|
@@ -380,7 +387,7 @@ class FunctionsEmulator {
|
|
|
380
387
|
}
|
|
381
388
|
}
|
|
382
389
|
if (this.args.debugPort) {
|
|
383
|
-
this.startRuntime(emulatableBackend, { nodeBinary: emulatableBackend.nodeBinary });
|
|
390
|
+
await this.startRuntime(emulatableBackend, { nodeBinary: emulatableBackend.nodeBinary });
|
|
384
391
|
}
|
|
385
392
|
}
|
|
386
393
|
addEventarcTrigger(projectId, key, eventTrigger) {
|
|
@@ -802,10 +809,11 @@ class FunctionsEmulator {
|
|
|
802
809
|
return secretEnvs;
|
|
803
810
|
}
|
|
804
811
|
async invokeRuntime(backend, trigger, frb, opts) {
|
|
805
|
-
|
|
812
|
+
const pool = this.workerPools[backend.codebase];
|
|
813
|
+
if (!pool.readyForWork(trigger.id)) {
|
|
806
814
|
await this.startRuntime(backend, opts, trigger);
|
|
807
815
|
}
|
|
808
|
-
return
|
|
816
|
+
return pool.submitWork(trigger.id, frb, opts);
|
|
809
817
|
}
|
|
810
818
|
async startRuntime(backend, opts, trigger) {
|
|
811
819
|
var _a;
|
|
@@ -838,52 +846,18 @@ class FunctionsEmulator {
|
|
|
838
846
|
env: Object.assign(Object.assign(Object.assign(Object.assign({ node: opts.nodeBinary }, process.env), runtimeEnv), secretEnvs), { PORT: socketPath }),
|
|
839
847
|
stdio: ["pipe", "pipe", "pipe", "ipc"],
|
|
840
848
|
});
|
|
841
|
-
if (!childProcess.stderr) {
|
|
842
|
-
throw new error_1.FirebaseError(`childProcess.stderr is undefined.`);
|
|
843
|
-
}
|
|
844
|
-
if (!childProcess.stdout) {
|
|
845
|
-
throw new error_1.FirebaseError(`childProcess.stdout is undefined.`);
|
|
846
|
-
}
|
|
847
|
-
const buffers = {
|
|
848
|
-
stderr: { pipe: childProcess.stderr, value: "" },
|
|
849
|
-
stdout: { pipe: childProcess.stdout, value: "" },
|
|
850
|
-
};
|
|
851
|
-
const ipcBuffer = { value: "" };
|
|
852
|
-
childProcess.on("message", (message) => {
|
|
853
|
-
this.onData(childProcess, emitter, ipcBuffer, message);
|
|
854
|
-
});
|
|
855
|
-
for (const id in buffers) {
|
|
856
|
-
if (buffers.hasOwnProperty(id)) {
|
|
857
|
-
const buffer = buffers[id];
|
|
858
|
-
buffer.pipe.on("data", (buf) => {
|
|
859
|
-
this.onData(childProcess, emitter, buffer, buf);
|
|
860
|
-
});
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
849
|
const runtime = {
|
|
864
|
-
|
|
865
|
-
exit: new Promise((resolve) => {
|
|
866
|
-
childProcess.on("exit", resolve);
|
|
867
|
-
}),
|
|
850
|
+
process: childProcess,
|
|
868
851
|
events: emitter,
|
|
869
852
|
cwd: backend.functionsDir,
|
|
870
853
|
socketPath,
|
|
871
|
-
shutdown: () => {
|
|
872
|
-
childProcess.kill();
|
|
873
|
-
},
|
|
874
|
-
kill: (signal) => {
|
|
875
|
-
childProcess.kill(signal);
|
|
876
|
-
emitter.emit("log", new types_1.EmulatorLog("SYSTEM", "runtime-status", "killed"));
|
|
877
|
-
},
|
|
878
|
-
send: (args) => {
|
|
879
|
-
return childProcess.send(JSON.stringify(args));
|
|
880
|
-
},
|
|
881
854
|
};
|
|
882
855
|
const extensionLogInfo = {
|
|
883
856
|
instanceId: backend.extensionInstanceId,
|
|
884
857
|
ref: (_a = backend.extensionVersion) === null || _a === void 0 ? void 0 : _a.ref,
|
|
885
858
|
};
|
|
886
|
-
|
|
859
|
+
const pool = this.workerPools[backend.codebase];
|
|
860
|
+
pool.addWorker(trigger === null || trigger === void 0 ? void 0 : trigger.id, runtime, extensionLogInfo);
|
|
887
861
|
return;
|
|
888
862
|
}
|
|
889
863
|
async disableBackgroundTriggers() {
|
|
@@ -1057,20 +1031,5 @@ class FunctionsEmulator {
|
|
|
1057
1031
|
});
|
|
1058
1032
|
await worker.waitForDone();
|
|
1059
1033
|
}
|
|
1060
|
-
onData(runtime, emitter, buffer, buf) {
|
|
1061
|
-
buffer.value += buf.toString();
|
|
1062
|
-
const lines = buffer.value.split("\n");
|
|
1063
|
-
if (lines.length > 1) {
|
|
1064
|
-
lines.slice(0, -1).forEach((line) => {
|
|
1065
|
-
const log = types_1.EmulatorLog.fromJSON(line);
|
|
1066
|
-
emitter.emit("log", log);
|
|
1067
|
-
if (log.level === "FATAL") {
|
|
1068
|
-
emitter.emit("log", new types_1.EmulatorLog("SYSTEM", "runtime-status", "killed"));
|
|
1069
|
-
runtime.kill();
|
|
1070
|
-
}
|
|
1071
|
-
});
|
|
1072
|
-
}
|
|
1073
|
-
buffer.value = lines[lines.length - 1];
|
|
1074
|
-
}
|
|
1075
1034
|
}
|
|
1076
1035
|
exports.FunctionsEmulator = FunctionsEmulator;
|
|
@@ -30,21 +30,52 @@ class RuntimeWorker {
|
|
|
30
30
|
}
|
|
31
31
|
else if (this.state === RuntimeWorkerState.FINISHING) {
|
|
32
32
|
this.log(`IDLE --> FINISHING`);
|
|
33
|
-
this.runtime.
|
|
33
|
+
this.runtime.process.kill();
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
});
|
|
38
|
-
this.runtime.
|
|
38
|
+
const childProc = this.runtime.process;
|
|
39
|
+
let msgBuffer = "";
|
|
40
|
+
childProc.on("message", (msg) => {
|
|
41
|
+
msgBuffer = this.processStream(msg, msgBuffer);
|
|
42
|
+
});
|
|
43
|
+
let stdBuffer = "";
|
|
44
|
+
if (childProc.stdout) {
|
|
45
|
+
childProc.stdout.on("data", (data) => {
|
|
46
|
+
stdBuffer = this.processStream(data, stdBuffer);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
if (childProc.stderr) {
|
|
50
|
+
childProc.stderr.on("data", (data) => {
|
|
51
|
+
stdBuffer = this.processStream(data, stdBuffer);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
childProc.on("exit", () => {
|
|
39
55
|
this.log("exited");
|
|
40
56
|
this.state = RuntimeWorkerState.FINISHED;
|
|
41
57
|
});
|
|
42
58
|
}
|
|
59
|
+
processStream(s, buf) {
|
|
60
|
+
buf += s.toString();
|
|
61
|
+
const lines = buf.split("\n");
|
|
62
|
+
if (lines.length > 1) {
|
|
63
|
+
lines.slice(0, -1).forEach((line) => {
|
|
64
|
+
const log = types_1.EmulatorLog.fromJSON(line);
|
|
65
|
+
this.runtime.events.emit("log", log);
|
|
66
|
+
if (log.level === "FATAL") {
|
|
67
|
+
this.runtime.events.emit("log", new types_1.EmulatorLog("SYSTEM", "runtime-status", "killed"));
|
|
68
|
+
this.runtime.process.kill();
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return lines[lines.length - 1];
|
|
73
|
+
}
|
|
43
74
|
execute(frb, opts) {
|
|
44
75
|
const execFrb = Object.assign({}, frb);
|
|
45
76
|
const args = { frb: execFrb, opts };
|
|
46
77
|
this.state = RuntimeWorkerState.BUSY;
|
|
47
|
-
this.runtime.send(args);
|
|
78
|
+
this.runtime.process.send(JSON.stringify(args));
|
|
48
79
|
}
|
|
49
80
|
get state() {
|
|
50
81
|
return this._state;
|
|
@@ -102,7 +133,7 @@ class RuntimeWorker {
|
|
|
102
133
|
const timeout = new Promise((resolve, reject) => {
|
|
103
134
|
setTimeout(() => {
|
|
104
135
|
reject(new error_1.FirebaseError("Failed to load function."));
|
|
105
|
-
},
|
|
136
|
+
}, 30000);
|
|
106
137
|
});
|
|
107
138
|
while (true) {
|
|
108
139
|
try {
|
|
@@ -142,7 +173,7 @@ class RuntimeWorkerPool {
|
|
|
142
173
|
if (w.state === RuntimeWorkerState.IDLE) {
|
|
143
174
|
this.log(`Shutting down IDLE worker (${w.key})`);
|
|
144
175
|
w.state = RuntimeWorkerState.FINISHING;
|
|
145
|
-
w.runtime.
|
|
176
|
+
w.runtime.process.kill();
|
|
146
177
|
}
|
|
147
178
|
else if (w.state === RuntimeWorkerState.BUSY) {
|
|
148
179
|
this.log(`Marking BUSY worker to finish (${w.key})`);
|
|
@@ -155,10 +186,10 @@ class RuntimeWorkerPool {
|
|
|
155
186
|
for (const arr of this.workers.values()) {
|
|
156
187
|
arr.forEach((w) => {
|
|
157
188
|
if (w.state === RuntimeWorkerState.IDLE) {
|
|
158
|
-
w.runtime.
|
|
189
|
+
w.runtime.process.kill();
|
|
159
190
|
}
|
|
160
191
|
else {
|
|
161
|
-
w.runtime.kill();
|
|
192
|
+
w.runtime.process.kill();
|
|
162
193
|
}
|
|
163
194
|
});
|
|
164
195
|
}
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.createFirebaseEndpoints = void 0;
|
|
4
4
|
const emulatorLogger_1 = require("../../emulatorLogger");
|
|
5
5
|
const types_1 = require("../../types");
|
|
6
|
+
const uuid = require("uuid");
|
|
6
7
|
const zlib_1 = require("zlib");
|
|
7
8
|
const metadata_1 = require("../metadata");
|
|
8
9
|
const express_1 = require("express");
|
|
@@ -93,8 +94,8 @@ function createFirebaseEndpoints(emulator) {
|
|
|
93
94
|
}
|
|
94
95
|
throw err;
|
|
95
96
|
}
|
|
96
|
-
if (
|
|
97
|
-
metadata.addDownloadToken();
|
|
97
|
+
if (metadata.downloadTokens.length === 0) {
|
|
98
|
+
metadata.addDownloadToken(true);
|
|
98
99
|
}
|
|
99
100
|
if (req.query.alt === "media") {
|
|
100
101
|
const isGZipped = metadata.contentEncoding === "gzip";
|
|
@@ -102,7 +103,8 @@ function createFirebaseEndpoints(emulator) {
|
|
|
102
103
|
data = (0, zlib_1.gunzipSync)(data);
|
|
103
104
|
}
|
|
104
105
|
res.setHeader("Accept-Ranges", "bytes");
|
|
105
|
-
res.setHeader("Content-Type", metadata.contentType);
|
|
106
|
+
res.setHeader("Content-Type", metadata.contentType || "application/octet-stream");
|
|
107
|
+
res.setHeader("Content-Disposition", metadata.contentDisposition || "inline");
|
|
106
108
|
setObjectHeaders(res, metadata, { "Content-Encoding": isGZipped ? "identity" : undefined });
|
|
107
109
|
const byteRange = req.range(data.byteLength, { combine: true });
|
|
108
110
|
if (Array.isArray(byteRange) && byteRange.type === "bytes" && byteRange.length > 0) {
|
|
@@ -165,45 +167,26 @@ function createFirebaseEndpoints(emulator) {
|
|
|
165
167
|
});
|
|
166
168
|
});
|
|
167
169
|
const handleUpload = async (req, res) => {
|
|
168
|
-
var _a;
|
|
170
|
+
var _a, _b;
|
|
169
171
|
const bucketId = req.params.bucketId;
|
|
170
172
|
const objectId = req.params.objectId
|
|
171
173
|
? decodeURIComponent(req.params.objectId)
|
|
172
174
|
: ((_a = req.query.name) === null || _a === void 0 ? void 0 : _a.toString()) || null;
|
|
173
|
-
const uploadType = req.header("x-goog-upload-protocol");
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
const contentTypeHeader = req.header("content-type");
|
|
180
|
-
if (!contentTypeHeader) {
|
|
181
|
-
return res.sendStatus(400);
|
|
182
|
-
}
|
|
183
|
-
let metadataRaw;
|
|
184
|
-
let dataRaw;
|
|
185
|
-
try {
|
|
186
|
-
({ metadataRaw, dataRaw } = (0, multipart_1.parseObjectUploadMultipartRequest)(contentTypeHeader, await (0, request_1.reqBodyToBuffer)(req)));
|
|
175
|
+
const uploadType = (_b = req.header("x-goog-upload-protocol")) === null || _b === void 0 ? void 0 : _b.toString();
|
|
176
|
+
async function finalizeOneShotUpload(upload) {
|
|
177
|
+
var _a, _b, _c;
|
|
178
|
+
if (!((_b = (_a = upload.metadata) === null || _a === void 0 ? void 0 : _a.metadata) === null || _b === void 0 ? void 0 : _b.firebaseStorageDownloadTokens)) {
|
|
179
|
+
const customMetadata = Object.assign(Object.assign({}, (((_c = upload.metadata) === null || _c === void 0 ? void 0 : _c.metadata) || {})), { firebaseStorageDownloadTokens: uuid.v4() });
|
|
180
|
+
upload.metadata = Object.assign(Object.assign({}, (upload.metadata || {})), { metadata: customMetadata });
|
|
187
181
|
}
|
|
188
|
-
catch (err) {
|
|
189
|
-
if (err instanceof Error) {
|
|
190
|
-
return res.status(400).send(err.message);
|
|
191
|
-
}
|
|
192
|
-
throw err;
|
|
193
|
-
}
|
|
194
|
-
const upload = uploadService.multipartUpload({
|
|
195
|
-
bucketId,
|
|
196
|
-
objectId,
|
|
197
|
-
metadataRaw,
|
|
198
|
-
dataRaw: dataRaw,
|
|
199
|
-
authorization: req.header("authorization"),
|
|
200
|
-
});
|
|
201
182
|
let metadata;
|
|
202
183
|
try {
|
|
203
184
|
metadata = await storageLayer.uploadObject(upload);
|
|
204
185
|
}
|
|
205
186
|
catch (err) {
|
|
206
187
|
if (err instanceof errors_1.ForbiddenError) {
|
|
188
|
+
res.header("x-goog-upload-status", "final");
|
|
189
|
+
uploadService.setResponseCode(upload.id, 403);
|
|
207
190
|
return res.status(403).json({
|
|
208
191
|
error: {
|
|
209
192
|
code: 403,
|
|
@@ -213,124 +196,153 @@ function createFirebaseEndpoints(emulator) {
|
|
|
213
196
|
}
|
|
214
197
|
throw err;
|
|
215
198
|
}
|
|
216
|
-
metadata.
|
|
199
|
+
if (!metadata.contentDisposition) {
|
|
200
|
+
metadata.contentDisposition = "inline";
|
|
201
|
+
}
|
|
217
202
|
return res.status(200).json(new metadata_1.OutgoingFirebaseMetadata(metadata));
|
|
218
203
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
if (uploadCommand === "start") {
|
|
225
|
-
if (!objectId) {
|
|
204
|
+
if (uploadType === "resumable") {
|
|
205
|
+
const uploadCommand = req.header("x-goog-upload-command");
|
|
206
|
+
if (!uploadCommand) {
|
|
226
207
|
res.sendStatus(400);
|
|
227
208
|
return;
|
|
228
209
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
authorization: req.header("authorization"),
|
|
234
|
-
});
|
|
235
|
-
res.header("x-goog-upload-chunk-granularity", "10000");
|
|
236
|
-
res.header("x-goog-upload-control-url", "");
|
|
237
|
-
res.header("x-goog-upload-status", "active");
|
|
238
|
-
res.header("x-gupload-uploadid", upload.id);
|
|
239
|
-
const uploadUrl = registry_1.EmulatorRegistry.url(types_1.Emulators.STORAGE, req);
|
|
240
|
-
uploadUrl.pathname = `/v0/b/${bucketId}/o`;
|
|
241
|
-
uploadUrl.searchParams.set("name", objectId);
|
|
242
|
-
uploadUrl.searchParams.set("upload_id", upload.id);
|
|
243
|
-
uploadUrl.searchParams.set("upload_protocol", "resumable");
|
|
244
|
-
res.header("x-goog-upload-url", uploadUrl.toString());
|
|
245
|
-
return res.sendStatus(200);
|
|
246
|
-
}
|
|
247
|
-
if (!req.query.upload_id) {
|
|
248
|
-
return res.sendStatus(400);
|
|
249
|
-
}
|
|
250
|
-
const uploadId = req.query.upload_id.toString();
|
|
251
|
-
if (uploadCommand === "query") {
|
|
252
|
-
let upload;
|
|
253
|
-
try {
|
|
254
|
-
upload = uploadService.getResumableUpload(uploadId);
|
|
255
|
-
}
|
|
256
|
-
catch (err) {
|
|
257
|
-
if (err instanceof errors_1.NotFoundError) {
|
|
258
|
-
return res.sendStatus(404);
|
|
210
|
+
if (uploadCommand === "start") {
|
|
211
|
+
if (!objectId) {
|
|
212
|
+
res.sendStatus(400);
|
|
213
|
+
return;
|
|
259
214
|
}
|
|
260
|
-
|
|
215
|
+
const upload = uploadService.startResumableUpload({
|
|
216
|
+
bucketId,
|
|
217
|
+
objectId,
|
|
218
|
+
metadataRaw: JSON.stringify(req.body),
|
|
219
|
+
authorization: req.header("authorization"),
|
|
220
|
+
});
|
|
221
|
+
res.header("x-goog-upload-chunk-granularity", "10000");
|
|
222
|
+
res.header("x-goog-upload-control-url", "");
|
|
223
|
+
res.header("x-goog-upload-status", "active");
|
|
224
|
+
res.header("x-gupload-uploadid", upload.id);
|
|
225
|
+
const uploadUrl = registry_1.EmulatorRegistry.url(types_1.Emulators.STORAGE, req);
|
|
226
|
+
uploadUrl.pathname = `/v0/b/${bucketId}/o`;
|
|
227
|
+
uploadUrl.searchParams.set("name", objectId);
|
|
228
|
+
uploadUrl.searchParams.set("upload_id", upload.id);
|
|
229
|
+
uploadUrl.searchParams.set("upload_protocol", "resumable");
|
|
230
|
+
res.header("x-goog-upload-url", uploadUrl.toString());
|
|
231
|
+
return res.sendStatus(200);
|
|
261
232
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
}
|
|
265
|
-
if (uploadCommand === "cancel") {
|
|
266
|
-
try {
|
|
267
|
-
uploadService.cancelResumableUpload(uploadId);
|
|
233
|
+
if (!req.query.upload_id) {
|
|
234
|
+
return res.sendStatus(400);
|
|
268
235
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
236
|
+
const uploadId = req.query.upload_id.toString();
|
|
237
|
+
if (uploadCommand === "query") {
|
|
238
|
+
let upload;
|
|
239
|
+
try {
|
|
240
|
+
upload = uploadService.getResumableUpload(uploadId);
|
|
272
241
|
}
|
|
273
|
-
|
|
274
|
-
|
|
242
|
+
catch (err) {
|
|
243
|
+
if (err instanceof errors_1.NotFoundError) {
|
|
244
|
+
return res.sendStatus(404);
|
|
245
|
+
}
|
|
246
|
+
throw err;
|
|
275
247
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
return res.sendStatus(200);
|
|
279
|
-
}
|
|
280
|
-
if (uploadCommand.includes("upload")) {
|
|
281
|
-
let upload;
|
|
282
|
-
try {
|
|
283
|
-
upload = uploadService.continueResumableUpload(uploadId, await (0, request_1.reqBodyToBuffer)(req));
|
|
248
|
+
res.header("X-Goog-Upload-Size-Received", upload.size.toString());
|
|
249
|
+
return res.sendStatus(200);
|
|
284
250
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
251
|
+
if (uploadCommand === "cancel") {
|
|
252
|
+
try {
|
|
253
|
+
uploadService.cancelResumableUpload(uploadId);
|
|
288
254
|
}
|
|
289
|
-
|
|
290
|
-
|
|
255
|
+
catch (err) {
|
|
256
|
+
if (err instanceof errors_1.NotFoundError) {
|
|
257
|
+
return res.sendStatus(404);
|
|
258
|
+
}
|
|
259
|
+
else if (err instanceof upload_1.NotCancellableError) {
|
|
260
|
+
return res.sendStatus(400);
|
|
261
|
+
}
|
|
262
|
+
throw err;
|
|
291
263
|
}
|
|
292
|
-
throw err;
|
|
293
|
-
}
|
|
294
|
-
if (!uploadCommand.includes("finalize")) {
|
|
295
|
-
res.header("x-goog-upload-status", "active");
|
|
296
|
-
res.header("x-gupload-uploadid", upload.id);
|
|
297
264
|
return res.sendStatus(200);
|
|
298
265
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
266
|
+
if (uploadCommand.includes("upload")) {
|
|
267
|
+
let upload;
|
|
268
|
+
try {
|
|
269
|
+
upload = uploadService.continueResumableUpload(uploadId, await (0, request_1.reqBodyToBuffer)(req));
|
|
270
|
+
}
|
|
271
|
+
catch (err) {
|
|
272
|
+
if (err instanceof errors_1.NotFoundError) {
|
|
273
|
+
return res.sendStatus(404);
|
|
274
|
+
}
|
|
275
|
+
else if (err instanceof upload_1.UploadNotActiveError) {
|
|
276
|
+
return res.sendStatus(400);
|
|
277
|
+
}
|
|
278
|
+
throw err;
|
|
279
|
+
}
|
|
280
|
+
if (!uploadCommand.includes("finalize")) {
|
|
281
|
+
res.header("x-goog-upload-status", "active");
|
|
282
|
+
res.header("x-gupload-uploadid", upload.id);
|
|
283
|
+
return res.sendStatus(200);
|
|
284
|
+
}
|
|
304
285
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
286
|
+
if (uploadCommand.includes("finalize")) {
|
|
287
|
+
let upload;
|
|
288
|
+
try {
|
|
289
|
+
upload = uploadService.finalizeResumableUpload(uploadId);
|
|
308
290
|
}
|
|
309
|
-
|
|
310
|
-
|
|
291
|
+
catch (err) {
|
|
292
|
+
if (err instanceof errors_1.NotFoundError) {
|
|
293
|
+
uploadService.setResponseCode(uploadId, 404);
|
|
294
|
+
return res.sendStatus(404);
|
|
295
|
+
}
|
|
296
|
+
else if (err instanceof upload_1.UploadNotActiveError) {
|
|
297
|
+
uploadService.setResponseCode(uploadId, 400);
|
|
298
|
+
return res.sendStatus(400);
|
|
299
|
+
}
|
|
300
|
+
else if (err instanceof upload_1.UploadPreviouslyFinalizedError) {
|
|
301
|
+
res.header("x-goog-upload-status", "final");
|
|
302
|
+
return res.sendStatus(uploadService.getPreviousResponseCode(uploadId));
|
|
303
|
+
}
|
|
304
|
+
throw err;
|
|
311
305
|
}
|
|
312
|
-
|
|
306
|
+
res.header("x-goog-upload-status", "final");
|
|
307
|
+
return await finalizeOneShotUpload(upload);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
if (!objectId) {
|
|
311
|
+
res.sendStatus(400);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
if (uploadType === "multipart") {
|
|
315
|
+
const contentTypeHeader = req.header("content-type");
|
|
316
|
+
if (!contentTypeHeader) {
|
|
317
|
+
return res.sendStatus(400);
|
|
313
318
|
}
|
|
314
|
-
let
|
|
319
|
+
let metadataRaw;
|
|
320
|
+
let dataRaw;
|
|
315
321
|
try {
|
|
316
|
-
|
|
322
|
+
({ metadataRaw, dataRaw } = (0, multipart_1.parseObjectUploadMultipartRequest)(contentTypeHeader, await (0, request_1.reqBodyToBuffer)(req)));
|
|
317
323
|
}
|
|
318
324
|
catch (err) {
|
|
319
|
-
if (err instanceof
|
|
320
|
-
return res.status(
|
|
321
|
-
error: {
|
|
322
|
-
code: 403,
|
|
323
|
-
message: `Permission denied. No WRITE permission.`,
|
|
324
|
-
},
|
|
325
|
-
});
|
|
325
|
+
if (err instanceof Error) {
|
|
326
|
+
return res.status(400).send(err.message);
|
|
326
327
|
}
|
|
327
328
|
throw err;
|
|
328
329
|
}
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
330
|
+
const upload = uploadService.multipartUpload({
|
|
331
|
+
bucketId,
|
|
332
|
+
objectId,
|
|
333
|
+
metadataRaw,
|
|
334
|
+
dataRaw: dataRaw,
|
|
335
|
+
authorization: req.header("authorization"),
|
|
336
|
+
});
|
|
337
|
+
return await finalizeOneShotUpload(upload);
|
|
332
338
|
}
|
|
333
|
-
|
|
339
|
+
const upload = uploadService.mediaUpload({
|
|
340
|
+
bucketId: req.params.bucketId,
|
|
341
|
+
objectId: objectId,
|
|
342
|
+
dataRaw: await (0, request_1.reqBodyToBuffer)(req),
|
|
343
|
+
authorization: req.header("authorization"),
|
|
344
|
+
});
|
|
345
|
+
return await finalizeOneShotUpload(upload);
|
|
334
346
|
};
|
|
335
347
|
const handleTokenRequest = (req, res) => {
|
|
336
348
|
var _a, _b;
|
|
@@ -469,11 +481,13 @@ function createFirebaseEndpoints(emulator) {
|
|
|
469
481
|
}
|
|
470
482
|
exports.createFirebaseEndpoints = createFirebaseEndpoints;
|
|
471
483
|
function setObjectHeaders(res, metadata, headerOverride = { "Content-Encoding": undefined }) {
|
|
472
|
-
|
|
484
|
+
if (metadata.contentDisposition) {
|
|
485
|
+
res.setHeader("Content-Disposition", metadata.contentDisposition);
|
|
486
|
+
}
|
|
473
487
|
if (headerOverride["Content-Encoding"]) {
|
|
474
488
|
res.setHeader("Content-Encoding", headerOverride["Content-Encoding"]);
|
|
475
489
|
}
|
|
476
|
-
else {
|
|
490
|
+
else if (metadata.contentEncoding) {
|
|
477
491
|
res.setHeader("Content-Encoding", metadata.contentEncoding);
|
|
478
492
|
}
|
|
479
493
|
if (metadata.cacheControl) {
|