backend-manager 5.9.14 → 5.9.15

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "backend-manager",
3
- "version": "5.9.14",
3
+ "version": "5.9.15",
4
4
  "description": "Quick tools for developing Firebase functions",
5
5
  "main": "src/manager/index.js",
6
6
  "files": [
@@ -3,15 +3,23 @@ const chalk = require('chalk').default;
3
3
  const powertools = require('node-powertools');
4
4
  const attachLogFile = require('../utils/attach-log-file');
5
5
  const { execSync } = require('child_process');
6
+ const { homedir } = require('os');
6
7
  const path = require('path');
7
8
  const jetpack = require('fs-jetpack');
8
9
 
9
10
  const DEFAULT_REGION = 'us-central1';
10
- const PUBLIC_HTTP_FUNCTIONS = [
11
- 'bm_api',
12
- 'bm_authBeforeCreate',
13
- 'bm_authBeforeSignIn',
14
- ];
11
+
12
+ function gcloudExec(cmd, options = {}) {
13
+ const env = { ...process.env };
14
+ env.PATH = `${path.join(homedir(), 'google-cloud-sdk', 'bin')}:${env.PATH}`;
15
+
16
+ return execSync(cmd, {
17
+ encoding: 'utf8',
18
+ timeout: options.timeout || 30000,
19
+ stdio: ['pipe', 'pipe', 'pipe'],
20
+ env,
21
+ });
22
+ }
15
23
 
16
24
  class DeployCommand extends BaseCommand {
17
25
  async execute() {
@@ -49,11 +57,14 @@ class DeployCommand extends BaseCommand {
49
57
  }
50
58
 
51
59
  /**
52
- * Ensure HTTP-triggered functions have allUsers as cloudfunctions.invoker.
60
+ * Ensure all HTTP-triggered functions have allUsers as cloudfunctions.invoker.
53
61
  *
54
62
  * Firebase CLI used to set this automatically but stopped around the Node 10
55
63
  * runtime transition. Without it, HTTP requests get a 403 at the IAM level
56
64
  * before BEM's application-level auth (backendManagerKey) can run.
65
+ *
66
+ * Dynamically discovers all deployed functions and fixes any HTTP-triggered
67
+ * function missing the allUsers invoker binding.
57
68
  */
58
69
  async ensurePublicInvoker() {
59
70
  const projectId = this.getProjectId();
@@ -62,10 +73,24 @@ class DeployCommand extends BaseCommand {
62
73
  return;
63
74
  }
64
75
 
65
- const gcloud = this.findGcloud();
76
+ // Discover all deployed HTTP-triggered functions
77
+ let httpFunctions;
78
+
79
+ try {
80
+ const output = gcloudExec(
81
+ `gcloud functions list --project ${projectId} --regions ${DEFAULT_REGION} --format="json(name,httpsTrigger)"`,
82
+ );
83
+
84
+ const allFunctions = JSON.parse(output);
85
+
86
+ httpFunctions = allFunctions
87
+ .filter((fn) => fn.httpsTrigger)
88
+ .map((fn) => fn.name.split('/').pop());
89
+ } catch {
90
+ return;
91
+ }
66
92
 
67
- if (!gcloud) {
68
- this.log(chalk.gray('\n Skipping public invoker check (gcloud not found)\n'));
93
+ if (!httpFunctions.length) {
69
94
  return;
70
95
  }
71
96
 
@@ -74,12 +99,11 @@ class DeployCommand extends BaseCommand {
74
99
  let fixed = 0;
75
100
  let ok = 0;
76
101
 
77
- for (const fnName of PUBLIC_HTTP_FUNCTIONS) {
102
+ for (const fnName of httpFunctions) {
78
103
  try {
79
- // Check current IAM policy
80
- const policyOutput = execSync(
81
- `${gcloud} functions get-iam-policy ${fnName} --project ${projectId} --region ${DEFAULT_REGION} --format=json`,
82
- { encoding: 'utf8', timeout: 15000, stdio: ['pipe', 'pipe', 'pipe'] },
104
+ const policyOutput = gcloudExec(
105
+ `gcloud functions get-iam-policy ${fnName} --project ${projectId} --region ${DEFAULT_REGION} --format=json`,
106
+ { timeout: 15000 },
83
107
  );
84
108
 
85
109
  const policy = JSON.parse(policyOutput);
@@ -93,16 +117,14 @@ class DeployCommand extends BaseCommand {
93
117
  continue;
94
118
  }
95
119
 
96
- // Add allUsers as invoker
97
- execSync(
98
- `${gcloud} functions add-iam-policy-binding ${fnName} --project ${projectId} --region ${DEFAULT_REGION} --member="allUsers" --role="roles/cloudfunctions.invoker"`,
99
- { encoding: 'utf8', timeout: 30000, stdio: ['pipe', 'pipe', 'pipe'] },
120
+ gcloudExec(
121
+ `gcloud functions add-iam-policy-binding ${fnName} --project ${projectId} --region ${DEFAULT_REGION} --member="allUsers" --role="roles/cloudfunctions.invoker"`,
100
122
  );
101
123
 
102
124
  this.log(` ${chalk.green('✓')} Set public invoker on ${chalk.cyan(fnName)}`);
103
125
  fixed++;
104
126
  } catch {
105
- // Function might not exist (not all projects use all functions) — skip silently
127
+ // Skip silently function may be in a transient state
106
128
  }
107
129
  }
108
130
 
@@ -121,23 +143,6 @@ class DeployCommand extends BaseCommand {
121
143
  return null;
122
144
  }
123
145
  }
124
-
125
- findGcloud() {
126
- const homeDir = require('os').homedir();
127
- const sdkPath = path.join(homeDir, 'google-cloud-sdk', 'bin', 'gcloud');
128
-
129
- if (jetpack.exists(sdkPath)) {
130
- return sdkPath;
131
- }
132
-
133
- // Try PATH
134
- try {
135
- execSync('which gcloud', { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] });
136
- return 'gcloud';
137
- } catch {
138
- return null;
139
- }
140
- }
141
146
  }
142
147
 
143
148
  module.exports = DeployCommand;