firebase-tools 10.1.2 → 10.2.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/api.js +1 -0
- package/lib/apiv2.js +92 -48
- package/lib/archiveDirectory.js +63 -73
- package/lib/auth.js +62 -25
- package/lib/commands/ext-configure.js +1 -0
- package/lib/commands/ext-dev-usage.js +3 -8
- package/lib/commands/ext-install.js +1 -0
- package/lib/commands/ext-uninstall.js +1 -0
- package/lib/commands/ext-update.js +1 -0
- package/lib/commands/functions-secrets-access.js +17 -0
- package/lib/commands/functions-secrets-destroy.js +40 -0
- package/lib/commands/functions-secrets-get.js +21 -0
- package/lib/commands/functions-secrets-prune.js +50 -0
- package/lib/commands/functions-secrets-set.js +46 -0
- package/lib/commands/index.js +7 -3
- package/lib/commands/login.js +1 -1
- package/lib/database/metadata.js +16 -24
- package/lib/deploy/functions/backend.js +11 -1
- package/lib/deploy/functions/ensure.js +112 -0
- package/lib/deploy/functions/ensureCloudBuildEnabled.js +0 -49
- package/lib/deploy/functions/prepare.js +13 -19
- package/lib/deploy/functions/release/fabricator.js +4 -1
- package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +1 -0
- package/lib/deploy/functions/runtimes/node/parseTriggers.js +12 -0
- package/lib/deploy/functions/validate.js +83 -1
- package/lib/deploy/hosting/convertConfig.js +45 -24
- package/lib/deploy/hosting/prepare.js +1 -1
- package/lib/emulator/controller.js +3 -1
- package/lib/emulator/emulatorLogger.js +7 -0
- package/lib/emulator/functionsEmulator.js +113 -79
- package/lib/emulator/functionsEmulatorRuntime.js +100 -83
- package/lib/emulator/functionsEmulatorShared.js +51 -1
- package/lib/emulator/functionsEmulatorShell.js +1 -2
- package/lib/emulator/functionsRuntimeWorker.js +1 -1
- package/lib/emulator/storage/apis/gcloud.js +2 -2
- package/lib/emulator/storage/files.js +8 -3
- package/lib/extensions/askUserForParam.js +1 -1
- package/lib/extensions/diagnose.js +56 -0
- package/lib/extensions/extensionsApi.js +0 -1
- package/lib/extensions/extensionsHelper.js +10 -17
- package/lib/extensions/resolveSource.js +1 -53
- package/lib/extensions/secretsUtils.js +1 -1
- package/lib/extensions/updateHelper.js +0 -14
- package/lib/extensions/utils.js +4 -2
- package/lib/functions/env.js +5 -7
- package/lib/functions/secrets.js +112 -0
- package/lib/gcp/cloudbilling.js +8 -19
- package/lib/gcp/cloudfunctions.js +24 -48
- package/lib/gcp/cloudlogging.js +8 -11
- package/lib/gcp/cloudmonitoring.js +8 -5
- package/lib/gcp/cloudscheduler.js +7 -18
- package/lib/gcp/firedata.js +5 -4
- package/lib/gcp/firestore.js +5 -5
- package/lib/gcp/iam.js +18 -33
- package/lib/gcp/resourceManager.js +8 -13
- package/lib/gcp/runtimeconfig.js +31 -53
- package/lib/gcp/secretManager.js +137 -77
- package/lib/gcp/storage.js +25 -29
- package/lib/previews.js +1 -1
- package/lib/serve/functions.js +2 -2
- package/lib/utils.js +6 -1
- package/npm-shrinkwrap.json +962 -987
- package/package.json +5 -3
- package/schema/firebase-config.json +387 -12
- package/templates/init/hosting/index.html +1 -1
package/lib/api.js
CHANGED
|
@@ -67,6 +67,7 @@ var _appendQueryData = function (path, data) {
|
|
|
67
67
|
return path;
|
|
68
68
|
};
|
|
69
69
|
var api = {
|
|
70
|
+
authProxyOrigin: utils.envOverride("FIREBASE_AUTHPROXY_URL", "https://auth.firebase.tools"),
|
|
70
71
|
clientId: utils.envOverride("FIREBASE_CLIENT_ID", "563584335869-fgrhgmd47bqnekij5i8b5pr03ho849e6.apps.googleusercontent.com"),
|
|
71
72
|
clientSecret: utils.envOverride("FIREBASE_CLIENT_SECRET", "j9iVZfS8kkCEFUPaAeJV0sAi"),
|
|
72
73
|
cloudbillingOrigin: utils.envOverride("FIREBASE_CLOUDBILLING_URL", "https://cloudbilling.googleapis.com"),
|
package/lib/apiv2.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Client = exports.setAccessToken = exports.setRefreshToken = void 0;
|
|
4
|
-
const stream_1 = require("stream");
|
|
5
4
|
const url_1 = require("url");
|
|
5
|
+
const stream_1 = require("stream");
|
|
6
6
|
const ProxyAgent = require("proxy-agent");
|
|
7
|
+
const retry = require("retry");
|
|
7
8
|
const abort_controller_1 = require("abort-controller");
|
|
8
9
|
const node_fetch_1 = require("node-fetch");
|
|
9
10
|
const util_1 = require("util");
|
|
@@ -151,6 +152,7 @@ class Client {
|
|
|
151
152
|
return `${this.opts.urlPrefix}${versionPath}${options.path}`;
|
|
152
153
|
}
|
|
153
154
|
async doRequest(options) {
|
|
155
|
+
var _a;
|
|
154
156
|
if (!options.path.startsWith("/")) {
|
|
155
157
|
options.path = "/" + options.path;
|
|
156
158
|
}
|
|
@@ -199,53 +201,95 @@ class Client {
|
|
|
199
201
|
else if (options.body !== undefined) {
|
|
200
202
|
fetchOptions.body = JSON.stringify(options.body);
|
|
201
203
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
207
|
-
catch (thrown) {
|
|
208
|
-
const err = thrown instanceof Error ? thrown : new Error(thrown);
|
|
209
|
-
const isAbortError = err.name.includes("AbortError");
|
|
210
|
-
if (isAbortError) {
|
|
211
|
-
throw new error_1.FirebaseError(`Timeout reached making request to ${fetchURL}`, { original: err });
|
|
212
|
-
}
|
|
213
|
-
throw new error_1.FirebaseError(`Failed to make request to ${fetchURL}`, { original: err });
|
|
214
|
-
}
|
|
215
|
-
finally {
|
|
216
|
-
if (reqTimeout) {
|
|
217
|
-
clearTimeout(reqTimeout);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
let body;
|
|
221
|
-
if (options.responseType === "json") {
|
|
222
|
-
const text = await res.text();
|
|
223
|
-
if (!text.length) {
|
|
224
|
-
body = undefined;
|
|
225
|
-
}
|
|
226
|
-
else {
|
|
227
|
-
body = JSON.parse(text);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
else if (options.responseType === "stream") {
|
|
231
|
-
body = res.body;
|
|
232
|
-
}
|
|
233
|
-
else {
|
|
234
|
-
throw new error_1.FirebaseError(`Unable to interpret response. Please set responseType.`, {
|
|
235
|
-
exit: 2,
|
|
236
|
-
});
|
|
237
|
-
}
|
|
238
|
-
this.logResponse(res, body, options);
|
|
239
|
-
if (res.status >= 400) {
|
|
240
|
-
if (!options.resolveOnHTTPError) {
|
|
241
|
-
throw responseToError({ statusCode: res.status }, body);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
return {
|
|
245
|
-
status: res.status,
|
|
246
|
-
response: res,
|
|
247
|
-
body,
|
|
204
|
+
const operationOptions = {
|
|
205
|
+
retries: ((_a = options.retryCodes) === null || _a === void 0 ? void 0 : _a.length) ? 1 : 2,
|
|
206
|
+
minTimeout: 1 * 1000,
|
|
207
|
+
maxTimeout: 5 * 1000,
|
|
248
208
|
};
|
|
209
|
+
if (typeof options.retries === "number") {
|
|
210
|
+
operationOptions.retries = options.retries;
|
|
211
|
+
}
|
|
212
|
+
if (typeof options.retryMinTimeout === "number") {
|
|
213
|
+
operationOptions.minTimeout = options.retryMinTimeout;
|
|
214
|
+
}
|
|
215
|
+
if (typeof options.retryMaxTimeout === "number") {
|
|
216
|
+
operationOptions.maxTimeout = options.retryMaxTimeout;
|
|
217
|
+
}
|
|
218
|
+
const operation = retry.operation(operationOptions);
|
|
219
|
+
return await new Promise((resolve, reject) => {
|
|
220
|
+
operation.attempt(async (currentAttempt) => {
|
|
221
|
+
var _a;
|
|
222
|
+
let res;
|
|
223
|
+
let body;
|
|
224
|
+
try {
|
|
225
|
+
if (currentAttempt > 1) {
|
|
226
|
+
logger_1.logger.debug(`*** [apiv2] Attempting the request again. Attempt number ${currentAttempt}`);
|
|
227
|
+
}
|
|
228
|
+
this.logRequest(options);
|
|
229
|
+
try {
|
|
230
|
+
res = await (0, node_fetch_1.default)(fetchURL, fetchOptions);
|
|
231
|
+
}
|
|
232
|
+
catch (thrown) {
|
|
233
|
+
const err = thrown instanceof Error ? thrown : new Error(thrown);
|
|
234
|
+
const isAbortError = err.name.includes("AbortError");
|
|
235
|
+
if (isAbortError) {
|
|
236
|
+
throw new error_1.FirebaseError(`Timeout reached making request to ${fetchURL}`, {
|
|
237
|
+
original: err,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
throw new error_1.FirebaseError(`Failed to make request to ${fetchURL}`, { original: err });
|
|
241
|
+
}
|
|
242
|
+
finally {
|
|
243
|
+
if (reqTimeout) {
|
|
244
|
+
clearTimeout(reqTimeout);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
if (options.responseType === "json") {
|
|
248
|
+
const text = await res.text();
|
|
249
|
+
if (!text.length) {
|
|
250
|
+
body = undefined;
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
try {
|
|
254
|
+
body = JSON.parse(text);
|
|
255
|
+
}
|
|
256
|
+
catch (err) {
|
|
257
|
+
this.logResponse(res, text, options);
|
|
258
|
+
throw new error_1.FirebaseError(`Unable to parse JSON: ${err}`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
else if (options.responseType === "stream") {
|
|
263
|
+
body = res.body;
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
throw new error_1.FirebaseError(`Unable to interpret response. Please set responseType.`, {
|
|
267
|
+
exit: 2,
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
catch (err) {
|
|
272
|
+
return err instanceof error_1.FirebaseError ? reject(err) : reject(new error_1.FirebaseError(`${err}`));
|
|
273
|
+
}
|
|
274
|
+
this.logResponse(res, body, options);
|
|
275
|
+
if (res.status >= 400) {
|
|
276
|
+
if ((_a = options.retryCodes) === null || _a === void 0 ? void 0 : _a.includes(res.status)) {
|
|
277
|
+
const err = responseToError({ statusCode: res.status }, body) || undefined;
|
|
278
|
+
if (operation.retry(err)) {
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
if (!options.resolveOnHTTPError) {
|
|
283
|
+
return reject(responseToError({ statusCode: res.status }, body));
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
resolve({
|
|
287
|
+
status: res.status,
|
|
288
|
+
response: res,
|
|
289
|
+
body,
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
});
|
|
249
293
|
}
|
|
250
294
|
logRequest(options) {
|
|
251
295
|
var _a, _b;
|
|
@@ -282,7 +326,7 @@ class Client {
|
|
|
282
326
|
}
|
|
283
327
|
exports.Client = Client;
|
|
284
328
|
function isLocalInsecureRequest(urlPrefix) {
|
|
285
|
-
const u =
|
|
329
|
+
const u = new url_1.URL(urlPrefix);
|
|
286
330
|
return u.protocol === "http:";
|
|
287
331
|
}
|
|
288
332
|
function bodyToString(body) {
|
package/lib/archiveDirectory.js
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.archiveDirectory = void 0;
|
|
3
4
|
const archiver = require("archiver");
|
|
4
5
|
const filesize = require("filesize");
|
|
5
6
|
const fs = require("fs");
|
|
6
7
|
const path = require("path");
|
|
7
8
|
const tar = require("tar");
|
|
8
9
|
const tmp = require("tmp");
|
|
9
|
-
const
|
|
10
|
-
const
|
|
10
|
+
const error_1 = require("./error");
|
|
11
|
+
const listFiles_1 = require("./listFiles");
|
|
12
|
+
const logger_1 = require("./logger");
|
|
11
13
|
const fsAsync = require("./fsAsync");
|
|
12
|
-
|
|
13
|
-
const utils = require("./utils");
|
|
14
|
-
const archiveDirectory = (sourceDirectory, options) => {
|
|
15
|
-
options = options || {};
|
|
14
|
+
async function archiveDirectory(sourceDirectory, options = {}) {
|
|
16
15
|
let postfix = ".tar.gz";
|
|
17
16
|
if (options.type === "zip") {
|
|
18
17
|
postfix = ".zip";
|
|
@@ -26,105 +25,96 @@ const archiveDirectory = (sourceDirectory, options) => {
|
|
|
26
25
|
}
|
|
27
26
|
let makeArchive;
|
|
28
27
|
if (options.type === "zip") {
|
|
29
|
-
makeArchive =
|
|
28
|
+
makeArchive = zipDirectory(sourceDirectory, tempFile, options);
|
|
30
29
|
}
|
|
31
30
|
else {
|
|
32
|
-
makeArchive =
|
|
31
|
+
makeArchive = tarDirectory(sourceDirectory, tempFile, options);
|
|
33
32
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
logger.debug(`Archived ${filesize(archive.size)} in ${sourceDirectory}.`);
|
|
33
|
+
try {
|
|
34
|
+
const archive = await makeArchive;
|
|
35
|
+
logger_1.logger.debug(`Archived ${filesize(archive.size)} in ${sourceDirectory}.`);
|
|
37
36
|
return archive;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (err instanceof FirebaseError) {
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
if (err instanceof error_1.FirebaseError) {
|
|
41
40
|
throw err;
|
|
42
41
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const
|
|
49
|
-
const allFiles = listFiles(sourceDirectory, options.ignore);
|
|
42
|
+
throw new error_1.FirebaseError("Failed to create archive.", { original: err });
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
exports.archiveDirectory = archiveDirectory;
|
|
46
|
+
async function tarDirectory(sourceDirectory, tempFile, options) {
|
|
47
|
+
const allFiles = (0, listFiles_1.listFiles)(sourceDirectory, options.ignore);
|
|
50
48
|
try {
|
|
51
49
|
fs.statSync(sourceDirectory);
|
|
52
50
|
}
|
|
53
51
|
catch (err) {
|
|
54
52
|
if (err.code === "ENOENT") {
|
|
55
|
-
|
|
53
|
+
throw new error_1.FirebaseError(`Could not read directory "${sourceDirectory}"`);
|
|
56
54
|
}
|
|
57
55
|
throw err;
|
|
58
56
|
}
|
|
59
57
|
if (!allFiles.length) {
|
|
60
|
-
|
|
58
|
+
throw new error_1.FirebaseError(`Cannot create a tar archive with 0 files from directory "${sourceDirectory}"`);
|
|
61
59
|
}
|
|
62
|
-
|
|
63
|
-
.create({
|
|
60
|
+
await tar.create({
|
|
64
61
|
gzip: true,
|
|
65
62
|
file: tempFile.name,
|
|
66
63
|
cwd: sourceDirectory,
|
|
67
64
|
follow: true,
|
|
68
65
|
noDirRecurse: true,
|
|
69
66
|
portable: true,
|
|
70
|
-
}, allFiles)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
};
|
|
82
|
-
const _zipDirectory = (sourceDirectory, tempFile, options) => {
|
|
67
|
+
}, allFiles);
|
|
68
|
+
const stats = fs.statSync(tempFile.name);
|
|
69
|
+
return {
|
|
70
|
+
file: tempFile.name,
|
|
71
|
+
stream: fs.createReadStream(tempFile.name),
|
|
72
|
+
manifest: allFiles,
|
|
73
|
+
size: stats.size,
|
|
74
|
+
source: sourceDirectory,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
async function zipDirectory(sourceDirectory, tempFile, options) {
|
|
83
78
|
const archiveFileStream = fs.createWriteStream(tempFile.name, {
|
|
84
79
|
flags: "w",
|
|
85
80
|
encoding: "binary",
|
|
86
81
|
});
|
|
87
82
|
const archive = archiver("zip");
|
|
88
|
-
const archiveDone =
|
|
83
|
+
const archiveDone = pipeAsync(archive, archiveFileStream);
|
|
89
84
|
const allFiles = [];
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
.
|
|
85
|
+
let files;
|
|
86
|
+
try {
|
|
87
|
+
files = await fsAsync.readdirRecursive({ path: sourceDirectory, ignore: options.ignore });
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
93
90
|
if (err.code === "ENOENT") {
|
|
94
|
-
|
|
91
|
+
throw new error_1.FirebaseError(`Could not read directory "${sourceDirectory}"`, { original: err });
|
|
95
92
|
}
|
|
96
93
|
throw err;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
mode: file.mode,
|
|
105
|
-
});
|
|
94
|
+
}
|
|
95
|
+
for (const file of files) {
|
|
96
|
+
const name = path.relative(sourceDirectory, file.name);
|
|
97
|
+
allFiles.push(name);
|
|
98
|
+
archive.file(file.name, {
|
|
99
|
+
name,
|
|
100
|
+
mode: file.mode,
|
|
106
101
|
});
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const _pipeAsync = function (from, to) {
|
|
122
|
-
return new Promise(function (resolve, reject) {
|
|
102
|
+
}
|
|
103
|
+
void archive.finalize();
|
|
104
|
+
await archiveDone;
|
|
105
|
+
const stats = fs.statSync(tempFile.name);
|
|
106
|
+
return {
|
|
107
|
+
file: tempFile.name,
|
|
108
|
+
stream: fs.createReadStream(tempFile.name),
|
|
109
|
+
manifest: allFiles,
|
|
110
|
+
size: stats.size,
|
|
111
|
+
source: sourceDirectory,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
async function pipeAsync(from, to) {
|
|
115
|
+
return new Promise((resolve, reject) => {
|
|
123
116
|
to.on("finish", resolve);
|
|
124
117
|
to.on("error", reject);
|
|
125
118
|
from.pipe(to);
|
|
126
119
|
});
|
|
127
|
-
}
|
|
128
|
-
module.exports = {
|
|
129
|
-
archiveDirectory,
|
|
130
|
-
};
|
|
120
|
+
}
|
package/lib/auth.js
CHANGED
|
@@ -19,6 +19,10 @@ const logger_1 = require("./logger");
|
|
|
19
19
|
const prompt_1 = require("./prompt");
|
|
20
20
|
const scopes = require("./scopes");
|
|
21
21
|
const defaultCredentials_1 = require("./defaultCredentials");
|
|
22
|
+
const uuid_1 = require("uuid");
|
|
23
|
+
const crypto_1 = require("crypto");
|
|
24
|
+
const cli_color_1 = require("cli-color");
|
|
25
|
+
const track_1 = require("./track");
|
|
22
26
|
portfinder.basePort = 9005;
|
|
23
27
|
function getGlobalDefaultAccount() {
|
|
24
28
|
const user = configstore_1.configstore.get("user");
|
|
@@ -182,19 +186,23 @@ function getLoginUrl(callbackUrl, userHint) {
|
|
|
182
186
|
login_hint: userHint,
|
|
183
187
|
}));
|
|
184
188
|
}
|
|
185
|
-
async function getTokensFromAuthorizationCode(code, callbackUrl) {
|
|
189
|
+
async function getTokensFromAuthorizationCode(code, callbackUrl, verifier) {
|
|
186
190
|
var _a, _b;
|
|
187
191
|
let res;
|
|
192
|
+
const params = {
|
|
193
|
+
code: code,
|
|
194
|
+
client_id: api.clientId,
|
|
195
|
+
client_secret: api.clientSecret,
|
|
196
|
+
redirect_uri: callbackUrl,
|
|
197
|
+
grant_type: "authorization_code",
|
|
198
|
+
};
|
|
199
|
+
if (verifier) {
|
|
200
|
+
params["code_verifier"] = verifier;
|
|
201
|
+
}
|
|
188
202
|
try {
|
|
189
203
|
res = await api.request("POST", "/o/oauth2/token", {
|
|
190
204
|
origin: api.authOrigin,
|
|
191
|
-
form:
|
|
192
|
-
code: code,
|
|
193
|
-
client_id: api.clientId,
|
|
194
|
-
client_secret: api.clientSecret,
|
|
195
|
-
redirect_uri: callbackUrl,
|
|
196
|
-
grant_type: "authorization_code",
|
|
197
|
-
},
|
|
205
|
+
form: params,
|
|
198
206
|
});
|
|
199
207
|
}
|
|
200
208
|
catch (err) {
|
|
@@ -248,31 +256,58 @@ async function respondWithFile(req, res, statusCode, filename) {
|
|
|
248
256
|
res.end(response);
|
|
249
257
|
req.socket.destroy();
|
|
250
258
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
259
|
+
function urlsafeBase64(base64string) {
|
|
260
|
+
return base64string.replace(/\+/g, "-").replace(/=+$/, "").replace(/\//g, "_");
|
|
261
|
+
}
|
|
262
|
+
async function loginRemotely(userHint) {
|
|
263
|
+
var _a;
|
|
264
|
+
const authProxyClient = new apiv2.Client({
|
|
265
|
+
urlPrefix: api.authProxyOrigin,
|
|
266
|
+
auth: false,
|
|
267
|
+
});
|
|
268
|
+
const sessionId = (0, uuid_1.v4)();
|
|
269
|
+
const codeVerifier = (0, crypto_1.randomBytes)(32).toString("hex");
|
|
270
|
+
const codeChallenge = urlsafeBase64((0, crypto_1.createHash)("sha256").update(codeVerifier).digest("base64"));
|
|
271
|
+
const attestToken = (_a = (await authProxyClient.post("/attest", {
|
|
272
|
+
session_id: sessionId,
|
|
273
|
+
})).body) === null || _a === void 0 ? void 0 : _a.token;
|
|
274
|
+
const loginUrl = `${api.authProxyOrigin}/login?code_challenge=${codeChallenge}&session=${sessionId}&attest=${attestToken}`;
|
|
275
|
+
logger_1.logger.info();
|
|
276
|
+
logger_1.logger.info("To sign in to the Firebase CLI:");
|
|
254
277
|
logger_1.logger.info();
|
|
255
|
-
logger_1.logger.info("
|
|
256
|
-
logger_1.logger.info(
|
|
278
|
+
logger_1.logger.info("1. Take note of your session ID:");
|
|
279
|
+
logger_1.logger.info();
|
|
280
|
+
logger_1.logger.info(` ${(0, cli_color_1.bold)(sessionId.substring(0, 5).toUpperCase())}`);
|
|
281
|
+
logger_1.logger.info();
|
|
282
|
+
logger_1.logger.info("2. Visit the URL below on any device and follow the instructions to get your code:");
|
|
283
|
+
logger_1.logger.info();
|
|
284
|
+
logger_1.logger.info(` ${loginUrl}`);
|
|
285
|
+
logger_1.logger.info();
|
|
286
|
+
logger_1.logger.info("3. Paste or enter the authorization code below once you have it:");
|
|
257
287
|
logger_1.logger.info();
|
|
258
|
-
open(authUrl);
|
|
259
288
|
const code = await (0, prompt_1.promptOnce)({
|
|
260
289
|
type: "input",
|
|
261
|
-
|
|
262
|
-
message: "Paste authorization code here:",
|
|
290
|
+
message: "Enter authorization code:",
|
|
263
291
|
});
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
292
|
+
try {
|
|
293
|
+
const tokens = await getTokensFromAuthorizationCode(code, `${api.authProxyOrigin}/complete`, codeVerifier);
|
|
294
|
+
(0, track_1.track)("login", "google_remote");
|
|
295
|
+
return {
|
|
296
|
+
user: jwt.decode(tokens.id_token),
|
|
297
|
+
tokens: tokens,
|
|
298
|
+
scopes: SCOPES,
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
catch (e) {
|
|
302
|
+
throw new error_1.FirebaseError("Unable to authenticate using the provided code. Please try again.");
|
|
303
|
+
}
|
|
270
304
|
}
|
|
271
305
|
async function loginWithLocalhostGoogle(port, userHint) {
|
|
272
306
|
const callbackUrl = getCallbackUrl(port);
|
|
273
307
|
const authUrl = getLoginUrl(callbackUrl, userHint);
|
|
274
308
|
const successTemplate = "../templates/loginSuccess.html";
|
|
275
309
|
const tokens = await loginWithLocalhost(port, callbackUrl, authUrl, successTemplate, getTokensFromAuthorizationCode);
|
|
310
|
+
(0, track_1.track)("login", "google_localhost");
|
|
276
311
|
return {
|
|
277
312
|
user: jwt.decode(tokens.id_token),
|
|
278
313
|
tokens: tokens,
|
|
@@ -283,7 +318,9 @@ async function loginWithLocalhostGitHub(port) {
|
|
|
283
318
|
const callbackUrl = getCallbackUrl(port);
|
|
284
319
|
const authUrl = getGithubLoginUrl(callbackUrl);
|
|
285
320
|
const successTemplate = "../templates/loginSuccessGithub.html";
|
|
286
|
-
|
|
321
|
+
const tokens = await loginWithLocalhost(port, callbackUrl, authUrl, successTemplate, getGithubTokensFromAuthorizationCode);
|
|
322
|
+
(0, track_1.track)("login", "google_localhost");
|
|
323
|
+
return tokens;
|
|
287
324
|
}
|
|
288
325
|
async function loginWithLocalhost(port, callbackUrl, authUrl, successTemplate, getTokens) {
|
|
289
326
|
return new Promise((resolve, reject) => {
|
|
@@ -331,10 +368,10 @@ async function loginGoogle(localhost, userHint) {
|
|
|
331
368
|
return await loginWithLocalhostGoogle(port, userHint);
|
|
332
369
|
}
|
|
333
370
|
catch (_a) {
|
|
334
|
-
return await
|
|
371
|
+
return await loginRemotely(userHint);
|
|
335
372
|
}
|
|
336
373
|
}
|
|
337
|
-
return await
|
|
374
|
+
return await loginRemotely(userHint);
|
|
338
375
|
}
|
|
339
376
|
exports.loginGoogle = loginGoogle;
|
|
340
377
|
async function loginGithub() {
|
|
@@ -27,6 +27,7 @@ exports.default = new command_1.Command("ext:configure <extensionInstanceId>")
|
|
|
27
27
|
"firebaseextensions.instances.get",
|
|
28
28
|
])
|
|
29
29
|
.before(checkMinRequiredVersion_1.checkMinRequiredVersion, "extMinVersion")
|
|
30
|
+
.before(extensionsHelper_1.diagnoseAndFixProject)
|
|
30
31
|
.action(async (instanceId, options) => {
|
|
31
32
|
const spinner = ora(`Configuring ${clc.bold(instanceId)}. This usually takes 3 to 5 minutes...`);
|
|
32
33
|
try {
|
|
@@ -58,14 +58,14 @@ module.exports = new command_1.Command("ext:dev:usage <publisherId>")
|
|
|
58
58
|
}
|
|
59
59
|
const profile = await (0, extensionsApi_1.getPublisherProfile)("-", publisherId);
|
|
60
60
|
const projectNumber = (0, extensionsHelper_1.getPublisherProjectFromName)(profile.name);
|
|
61
|
-
const
|
|
62
|
-
|
|
61
|
+
const past45d = new Date();
|
|
62
|
+
past45d.setDate(past45d.getDate() - 45);
|
|
63
63
|
const query = {
|
|
64
64
|
filter: `metric.type="firebaseextensions.googleapis.com/extension/version/active_instances" ` +
|
|
65
65
|
`resource.type="firebaseextensions.googleapis.com/ExtensionVersion" ` +
|
|
66
66
|
`resource.labels.extension="${extensionName}"`,
|
|
67
67
|
"interval.endTime": new Date().toJSON(),
|
|
68
|
-
"interval.startTime":
|
|
68
|
+
"interval.startTime": past45d.toJSON(),
|
|
69
69
|
view: cloudmonitoring_1.TimeSeriesView.FULL,
|
|
70
70
|
"aggregation.alignmentPeriod": (60 * 60 * 24).toString() + "s",
|
|
71
71
|
"aggregation.perSeriesAligner": cloudmonitoring_1.Aligner.ALIGN_MAX,
|
|
@@ -95,15 +95,10 @@ module.exports = new command_1.Command("ext:dev:usage <publisherId>")
|
|
|
95
95
|
});
|
|
96
96
|
utils.logLabeledBullet(extensionsHelper_1.logPrefix, `showing usage stats for ${clc.bold(extensionName)}:`);
|
|
97
97
|
logger_1.logger.info(table.toString());
|
|
98
|
-
const link = await buildCloudMonitoringLink({
|
|
99
|
-
projectNumber: projectNumber,
|
|
100
|
-
extensionName,
|
|
101
|
-
});
|
|
102
98
|
utils.logLabeledBullet(extensionsHelper_1.logPrefix, `How to read this table:`);
|
|
103
99
|
logger_1.logger.info(`* Due to privacy considerations, numbers are reported as ranges.`);
|
|
104
100
|
logger_1.logger.info(`* In the absence of significant changes, we will render a '-' symbol.`);
|
|
105
101
|
logger_1.logger.info(`* You will need more than 10 installs over a period of more than 28 days to render sufficient data.`);
|
|
106
|
-
logger_1.logger.info(`For more detail, visit: ${link}`);
|
|
107
102
|
});
|
|
108
103
|
async function buildCloudMonitoringLink(args) {
|
|
109
104
|
const pageState = {
|
|
@@ -201,6 +201,7 @@ exports.default = new command_1.Command("ext:install [extensionName]")
|
|
|
201
201
|
.before(requirePermissions_1.requirePermissions, ["firebaseextensions.instances.create"])
|
|
202
202
|
.before(extensionsHelper_1.ensureExtensionsApiEnabled)
|
|
203
203
|
.before(checkMinRequiredVersion_1.checkMinRequiredVersion, "extMinVersion")
|
|
204
|
+
.before(extensionsHelper_1.diagnoseAndFixProject)
|
|
204
205
|
.action(async (extensionName, options) => {
|
|
205
206
|
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
206
207
|
const paramsEnvPath = options.params;
|
|
@@ -33,6 +33,7 @@ exports.default = new command_1.Command("ext:uninstall <extensionInstanceId>")
|
|
|
33
33
|
.before(requirePermissions_1.requirePermissions, ["firebaseextensions.instances.delete"])
|
|
34
34
|
.before(extensionsHelper_1.ensureExtensionsApiEnabled)
|
|
35
35
|
.before(checkMinRequiredVersion_1.checkMinRequiredVersion, "extMinVersion")
|
|
36
|
+
.before(extensionsHelper_1.diagnoseAndFixProject)
|
|
36
37
|
.action(async (instanceId, options) => {
|
|
37
38
|
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
38
39
|
let instance;
|
|
@@ -44,6 +44,7 @@ exports.default = new command_1.Command("ext:update <extensionInstanceId> [updat
|
|
|
44
44
|
])
|
|
45
45
|
.before(extensionsHelper_1.ensureExtensionsApiEnabled)
|
|
46
46
|
.before(checkMinRequiredVersion_1.checkMinRequiredVersion, "extMinVersion")
|
|
47
|
+
.before(extensionsHelper_1.diagnoseAndFixProject)
|
|
47
48
|
.withForce()
|
|
48
49
|
.option("--params <paramsFile>", "name of params variables file with .env format.")
|
|
49
50
|
.action(async (instanceId, updateSource, options) => {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const command_1 = require("../command");
|
|
4
|
+
const logger_1 = require("../logger");
|
|
5
|
+
const projectUtils_1 = require("../projectUtils");
|
|
6
|
+
const secretManager_1 = require("../gcp/secretManager");
|
|
7
|
+
exports.default = new command_1.Command("functions:secrets:access <KEY>[@version]")
|
|
8
|
+
.description("Access secret value given secret and its version. Defaults to accessing the latest version.")
|
|
9
|
+
.action(async (key, options) => {
|
|
10
|
+
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
11
|
+
let [name, version] = key.split("@");
|
|
12
|
+
if (!version) {
|
|
13
|
+
version = "latest";
|
|
14
|
+
}
|
|
15
|
+
const value = await (0, secretManager_1.accessSecretVersion)(projectId, name, version);
|
|
16
|
+
logger_1.logger.info(value);
|
|
17
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const command_1 = require("../command");
|
|
4
|
+
const logger_1 = require("../logger");
|
|
5
|
+
const projectUtils_1 = require("../projectUtils");
|
|
6
|
+
const secretManager_1 = require("../gcp/secretManager");
|
|
7
|
+
const prompt_1 = require("../prompt");
|
|
8
|
+
const secrets = require("../functions/secrets");
|
|
9
|
+
exports.default = new command_1.Command("functions:secrets:destroy <KEY>[@version]")
|
|
10
|
+
.description("Destroy a secret. Defaults to destroying the latest version.")
|
|
11
|
+
.withForce("Destroys a secret without confirmation.")
|
|
12
|
+
.action(async (key, options) => {
|
|
13
|
+
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
14
|
+
let [name, version] = key.split("@");
|
|
15
|
+
if (!version) {
|
|
16
|
+
version = "latest";
|
|
17
|
+
}
|
|
18
|
+
const sv = await (0, secretManager_1.getSecretVersion)(projectId, name, version);
|
|
19
|
+
if (!options.force) {
|
|
20
|
+
const confirm = await (0, prompt_1.promptOnce)({
|
|
21
|
+
name: "destroy",
|
|
22
|
+
type: "confirm",
|
|
23
|
+
default: true,
|
|
24
|
+
message: `Are you sure you want to destroy ${sv.secret.name}@${sv.versionId}`,
|
|
25
|
+
}, options);
|
|
26
|
+
if (!confirm) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
await (0, secretManager_1.destroySecretVersion)(projectId, name, version);
|
|
31
|
+
logger_1.logger.info(`Destroyed secret version ${name}@${sv.versionId}`);
|
|
32
|
+
const secret = await (0, secretManager_1.getSecret)(projectId, name);
|
|
33
|
+
if (secrets.isFirebaseManaged(secret)) {
|
|
34
|
+
const versions = await (0, secretManager_1.listSecretVersions)(projectId, name);
|
|
35
|
+
if (versions.filter((v) => v.state === "ENABLED").length === 0) {
|
|
36
|
+
logger_1.logger.info(`No active secret versions left. Destroying secret ${name}`);
|
|
37
|
+
await (0, secretManager_1.deleteSecret)(projectId, name);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const Table = require("cli-table");
|
|
4
|
+
const command_1 = require("../command");
|
|
5
|
+
const logger_1 = require("../logger");
|
|
6
|
+
const projectUtils_1 = require("../projectUtils");
|
|
7
|
+
const secretManager_1 = require("../gcp/secretManager");
|
|
8
|
+
exports.default = new command_1.Command("functions:secrets:get <KEY>")
|
|
9
|
+
.description("Get metadata for secret and its versions")
|
|
10
|
+
.action(async (key, options) => {
|
|
11
|
+
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
12
|
+
const versions = await (0, secretManager_1.listSecretVersions)(projectId, key);
|
|
13
|
+
const table = new Table({
|
|
14
|
+
head: ["Version", "State"],
|
|
15
|
+
style: { head: ["yellow"] },
|
|
16
|
+
});
|
|
17
|
+
for (const version of versions) {
|
|
18
|
+
table.push([version.versionId, version.state]);
|
|
19
|
+
}
|
|
20
|
+
logger_1.logger.info(table.toString());
|
|
21
|
+
});
|