backend-manager 3.2.108 → 3.2.110

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/CHANGELOG.md CHANGED
@@ -15,18 +15,21 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
15
15
  - `Security` in case of vulnerabilities.
16
16
 
17
17
  ---
18
- ## [3.2.32] - 2023-01-30
19
- ### Added
18
+ ## [3.2.109] - 2024-05-08
19
+ ### Changed
20
+ - Replaced all `methods` references with `routes`. This should be changed in your code as well.
21
+
22
+ ## [3.2.32] - 2024-01-30
23
+ ### Changed
20
24
  - Modified `.assistant().errorify()` to have defaults of `log`, `sentry`, and `send` to `false` if not specified to prevent accidental logging and premature sending of errors.
21
25
 
22
- ## [3.2.30] - 2023-01-30
23
- ### Added
26
+ ## [3.2.30] - 2024-01-30
27
+ ### Changed
24
28
  - Modified `.assistant()` token/key check to use `options.apiKey || data.apiKey`
25
29
 
26
- ## [3.2.0] - 2023-01-19
30
+ ## [3.2.0] - 2024-01-19
27
31
  ### Added
28
- - Added `.settings()` API. Put your settings in `./schema/*.js` and access them with `assistant.settings.*`.
29
-
32
+ - Added `.settings()` API. Put your settings in `./schemas/*.js` and access them with `assistant.settings.*`.
30
33
 
31
34
  ## [3.1.0] - 2023-12-19
32
35
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "backend-manager",
3
- "version": "3.2.108",
3
+ "version": "3.2.110",
4
4
  "description": "Quick tools for developing Firebase functions",
5
5
  "main": "src/manager/index.js",
6
6
  "bin": {
@@ -39,13 +39,16 @@
39
39
  "@octokit/rest": "^19.0.13",
40
40
  "@sendgrid/mail": "^7.7.0",
41
41
  "@sentry/node": "^6.19.7",
42
+ "body-parser": "^1.20.2",
42
43
  "busboy": "^1.6.0",
43
44
  "chalk": "^4.1.2",
44
45
  "cors": "^2.8.5",
45
46
  "dotenv": "^16.4.5",
47
+ "express": "^4.19.2",
46
48
  "firebase-admin": "^11.11.1",
47
49
  "firebase-functions": "^4.9.0",
48
50
  "fs-jetpack": "^5.1.0",
51
+ "glob": "^10.3.12",
49
52
  "hcaptcha": "^0.1.1",
50
53
  "inquirer": "^8.2.5",
51
54
  "json5": "^2.2.3",
package/src/cli/cli.js CHANGED
@@ -82,9 +82,13 @@ Main.prototype.process = async function (args) {
82
82
  console.clear();
83
83
  process.stdout.write("\u001b[3J\u001b[2J\u001b[1J");
84
84
  }
85
+
86
+ // Log CWD
85
87
  if (self.options.cwd) {
86
88
  console.log('cwd: ', self.firebaseProjectPath);
87
89
  }
90
+
91
+ // Run setup
88
92
  if (self.options.setup) {
89
93
  // console.log(`Running Setup`);
90
94
  // console.log(`node:`, process.versions.node);
@@ -95,14 +99,21 @@ Main.prototype.process = async function (args) {
95
99
  await cmd_configGet(self).catch(e => log(chalk.red(`Failed to run config:get`)));
96
100
  await self.setup();
97
101
  }
102
+
103
+ // Install local BEM
98
104
  if ((self.options.i || self.options.install) && (self.options.local || self.options.dev || self.options.development)) {
99
105
  await uninstallPkg('backend-manager');
100
- return await installPkg('file:../../../ITW-Creative-Works/backend-manager');
106
+ // return await installPkg('file:../../../ITW-Creative-Works/backend-manager');
107
+ return await installPkg('file:/Users/ian/Developer/Repositories/ITW-Creative-Works/backend-manager');
101
108
  }
109
+
110
+ // Install live BEM
102
111
  if ((self.options.i || self.options.install) && (self.options.live || self.options.prod || self.options.production)) {
103
112
  await uninstallPkg('backend-manager');
104
113
  return await installPkg('backend-manager');
105
114
  }
115
+
116
+ // Serve firebase
106
117
  if (self.options.serve) {
107
118
  if (!self.options.quick && !self.options.q) {
108
119
  }
@@ -121,30 +132,36 @@ Main.prototype.process = async function (args) {
121
132
  });
122
133
  }
123
134
 
135
+ // Get indexes
124
136
  if (self.options['firestore:indexes:get'] || self.options['firestore:indexes'] || self.options['indexes:get']) {
125
137
  return await cmd_indexesGet(self, undefined, true);
126
138
  }
127
139
 
140
+ // Get config
128
141
  if (self.options['functions:config:get'] || self.options['config:get']) {
129
142
  return await cmd_configGet(self);
130
143
  }
131
144
 
145
+ // Set config
132
146
  if (self.options['functions:config:set'] || self.options['config:set']) {
133
147
  await cmd_configSet(self);
134
148
  return await cmd_configGet(self);
135
149
  }
136
150
 
151
+ // Unset config
137
152
  if (self.options['functions:config:unset'] || self.options['config:unset'] || self.options['config:delete'] || self.options['config:remove']) {
138
153
  await cmd_configUnset(self);
139
154
  return await cmd_configGet(self);
140
155
  }
141
156
 
157
+ // Get rules
142
158
  if (self.options['rules:default'] || self.options['rules:getdefault']) {
143
159
  self.getRulesFile();
144
160
  console.log(self.default.firestoreRulesWhole.match(bem_allRulesDefaultRegex)[0].replace(' ///', '///'));
145
161
  return;
146
162
  }
147
163
 
164
+ // Deploy
148
165
  if (self.options.deploy) {
149
166
  await self.setup();
150
167
 
@@ -166,8 +183,9 @@ Main.prototype.process = async function (args) {
166
183
  console.error(chalk.red(`${cleanOutput(data)}`));
167
184
  // ls = null;
168
185
  });
169
-
170
186
  }
187
+
188
+ // Test
171
189
  if (self.options['test']) {
172
190
  await self.setup();
173
191
  // firebase emulators:exec --only firestore 'npm test'
@@ -182,6 +200,7 @@ Main.prototype.process = async function (args) {
182
200
  });
183
201
  }
184
202
 
203
+ // Clean
185
204
  if (self.options['clean:npm']) {
186
205
  // await self.setup();
187
206
  // firebase emulators:exec --only firestore 'npm test'
@@ -100,8 +100,6 @@ Module.prototype.main = function () {
100
100
  // Insert ads
101
101
  const articleWithAds = insertAds(formattedContent, 3, 1500);
102
102
 
103
-
104
-
105
103
  // Log
106
104
  assistant.log(`main(): articleWithAds`, articleWithAds);
107
105
 
@@ -28,7 +28,7 @@ Module.prototype.main = function () {
28
28
  try {
29
29
  hook = (new (require(pathify(`${Manager.cwd}/${payload.data.payload.path}.js`)))());
30
30
  } catch (e) {
31
- hook = (new (require(pathify(`${Manager.cwd}/methods/hooks/${payload.data.payload.path}.js`)))());
31
+ hook = (new (require(pathify(`${Manager.cwd}/routes/hooks/${payload.data.payload.path}.js`)))());
32
32
  }
33
33
 
34
34
  // Run the hook
@@ -326,7 +326,7 @@ function resolveBasePath(basePath, command) {
326
326
  };
327
327
 
328
328
  function resolveApiPath(command) {
329
- const projectBasePath = path.join(process.cwd(), 'methods/api');
329
+ const projectBasePath = path.join(process.cwd(), 'routes/api');
330
330
  const localBasePath = './api/';
331
331
 
332
332
  const projectPath = resolveBasePath(projectBasePath, command);
@@ -140,17 +140,19 @@ BackendAssistant.prototype.init = function (ref, options) {
140
140
  self.request.path = (self.ref.req.path || '');
141
141
  self.request.user = self.resolveAccount({authenticated: false});
142
142
 
143
+ // Set body and query
143
144
  if (options.accept === 'json') {
144
145
  self.request.body = tryParse(self.ref.req.body || '{}');
145
146
  self.request.query = tryParse(self.ref.req.query || '{}');
146
147
  }
147
148
 
148
- self.request.headers = (self.ref.req.headers || {});
149
- self.request.data = Object.assign(
150
- {},
151
- _.cloneDeep(self.request.body || {}),
152
- _.cloneDeep(self.request.query || {})
153
- );
149
+ // Set headers
150
+ self.request.headers = self.ref.req.headers || {};
151
+
152
+ // Merge data
153
+ self.request.data = _.merge({}, self.request.body, self.request.query);
154
+
155
+ // Set multipart data
154
156
  self.request.multipartData = {
155
157
  fields: {},
156
158
  files: {},
@@ -385,6 +387,11 @@ BackendAssistant.prototype.errorify = function (e, options) {
385
387
  sendable = `${sendable} (${newError.tag})`;
386
388
  }
387
389
 
390
+ // Log
391
+ if (options.log) {
392
+ self.log(`Sending response (${options.code}):`, JSON.stringify(sendable));
393
+ }
394
+
388
395
  // Send response
389
396
  res
390
397
  .status(options.code)
@@ -463,7 +470,11 @@ BackendAssistant.prototype.respond = function(response, options) {
463
470
  _log(`Sending response`);
464
471
 
465
472
  // If it is an object, send as json
466
- if (response && typeof response === 'object') {
473
+ if (
474
+ response
475
+ && typeof response === 'object'
476
+ && typeof res.json === 'function'
477
+ ) {
467
478
  return res.json(response);
468
479
  } else {
469
480
  return res.send(response);
@@ -39,15 +39,21 @@ Middleware.prototype.run = function (libPath, options) {
39
39
  options.includeNonSchemaSettings = typeof options.includeNonSchemaSettings === 'undefined' ? false : options.includeNonSchemaSettings;
40
40
  options.schema = typeof options.schema === 'undefined' ? undefined : options.schema;
41
41
 
42
+ // Set base path
43
+ options.routesDir = typeof options.routesDir === 'undefined' ? `${process.cwd()}/routes` : options.routesDir;
44
+ options.schemasDir = typeof options.schemasDir === 'undefined' ? `${process.cwd()}/schemas` : options.schemasDir;
45
+
42
46
  // Log
43
47
  assistant.log(`Middleware.process(): Request (${geolocation.ip} @ ${geolocation.country}, ${geolocation.region}, ${geolocation.city})`, JSON.stringify(data));
44
48
 
45
- const basePath = path.resolve(process.cwd(), `methods/${libPath.replace('.js', '')}`);
46
- let library;
49
+ // Set paths
50
+ const routesDir = path.resolve(options.routesDir, libPath.replace('.js', ''));
51
+ const schemasDir = path.resolve(options.schemasDir);
47
52
 
48
53
  // Load library
54
+ let library;
49
55
  try {
50
- libPath = path.resolve(basePath, `index.js`);
56
+ libPath = path.resolve(routesDir, `index.js`);
51
57
  library = new (require(libPath))();
52
58
  } catch (e) {
53
59
  return assistant.respond(new Error(`Unable to load library @ (${libPath}): ${e.message}`), {code: 500, sentry: true});
@@ -85,7 +91,8 @@ Middleware.prototype.run = function (libPath, options) {
85
91
 
86
92
  // Resolve settings
87
93
  try {
88
- assistant.settings = Manager.Settings().resolve(assistant, options.schema, data);
94
+ // assistant.settings = Manager.Settings().resolve(assistant, options.schema, data);
95
+ assistant.settings = Manager.Settings().resolve(assistant, undefined, data, {dir: schemasDir, schema: options.schema});
89
96
  } catch (e) {
90
97
  return assistant.respond(new Error(`Unable to resolve schema ${options.schema}: ${e.message}`), {code: 500, sentry: true});
91
98
  }
@@ -16,25 +16,29 @@ function Settings(m) {
16
16
  self.settings = null;
17
17
  }
18
18
 
19
- Settings.prototype.resolve = function (assistant, schema, settings) {
19
+ Settings.prototype.resolve = function (assistant, schema, settings, options) {
20
20
  const self = this;
21
21
  const Manager = self.Manager;
22
22
 
23
23
  // Set settings
24
- schema = schema;
25
- settings = typeof settings === 'undefined' ? {} : settings;
24
+ schema = schema || undefined;
25
+ settings = settings || {};
26
26
 
27
- // Throw error if there is no schema
28
- if (typeof schema === 'undefined') {
29
- throw assistant.errorify(`Schema is undefined`, {code: 500});
30
- }
31
-
32
- // Reset settings
33
- self.settings = null;
34
-
35
- // Load schema
36
- if (typeof schema === 'string') {
37
- const schemaPath = path.resolve(process.cwd(), `schemas/${schema.replace('.js', '')}.js`);
27
+ // Set options
28
+ options = options || {};
29
+ options.dir = typeof options.dir === 'undefined' ? `${process.cwd()}/schemas` : options.dir;
30
+ options.schema = typeof options.schema === 'undefined' ? undefined : options.schema;
31
+
32
+ // Load schema if not provided and schema is defined in options
33
+ // console.log('----schema:', schema);
34
+ // console.log('----settings:', settings);
35
+ // console.log('----options.dir:', options.dir);
36
+ // console.log('----options.schema:', options.schema);
37
+ if (
38
+ typeof schema === 'undefined'
39
+ && typeof options.schema !== 'undefined'
40
+ ) {
41
+ const schemaPath = path.resolve(options.dir, `${options.schema.replace('.js', '')}.js`);
38
42
 
39
43
  schema = loadSchema(assistant, schemaPath, settings);
40
44
  }
@@ -120,10 +120,12 @@ Usage.prototype.init = function (assistant, options) {
120
120
  // Get app data
121
121
  self.app = self.storage.get(`${self.paths.app}.data`, {}).value();
122
122
 
123
+ // Check for app data
123
124
  if (!self.app) {
124
125
  return reject(new Error('Usage.init(): No app data found'));
125
126
  }
126
127
 
128
+ // Log
127
129
  self.log(`Usage.init(): Got app data`, self.app);
128
130
  self.log(`Usage.init(): Got user`, self.user);
129
131
 
@@ -135,26 +137,43 @@ Usage.prototype.init = function (assistant, options) {
135
137
  });
136
138
  };
137
139
 
138
- Usage.prototype.validate = function (path, options) {
140
+ Usage.prototype.validate = function (name, options) {
139
141
  const self = this;
140
142
 
141
143
  return new Promise(async function(resolve, reject) {
142
144
  const Manager = self.Manager;
143
145
  const assistant = self.assistant;
144
146
 
147
+ // Set options
145
148
  options = options || {};
146
149
  options.useCaptchaResponse = typeof options.useCaptchaResponse === 'undefined' ? true : options.useCaptchaResponse;
150
+ options.log = typeof options.log === 'undefined' ? true : options.log;
151
+ options.throw = typeof options.throw === 'undefined' ? false : options.throw;
147
152
 
148
153
  // Check for required options
149
- const period = self.getUsage(path);
150
- const allowed = self.getLimit(path);
154
+ const period = self.getUsage(name);
155
+ const allowed = self.getLimit(name);
151
156
 
152
- // Log
153
- self.log(`Usage.validate(): Checking ${period}/${allowed} for ${path}...`);
157
+ // Log (independent of options.log because this is important)
158
+ if (options.log) {
159
+ assistant.log(`Usage.validate(): Checking ${period}/${allowed} for ${name}...`);
160
+ }
161
+
162
+ // Reject function
163
+ function _reject() {
164
+ reject(
165
+ assistant.errorify(`You have exceeded your ${name} usage limit of ${period}/${allowed}.`, {code: 429})
166
+ );
167
+ }
168
+
169
+ // Dev mode throw
170
+ if (options.throw) {
171
+ return _reject();
172
+ }
154
173
 
155
174
  // If they are under the limit, resolve
156
175
  if (period < allowed) {
157
- self.log(`Usage.validate(): Valid for ${path}`);
176
+ self.log(`Usage.validate(): Valid for ${name}`);
158
177
 
159
178
  return resolve(true);
160
179
  }
@@ -177,25 +196,28 @@ Usage.prototype.validate = function (path, options) {
177
196
  }
178
197
 
179
198
  // Otherwise, they are over the limit, reject
180
- return reject(
181
- assistant.errorify(`You have exceeded your ${path} usage limit of ${period}/${allowed}.`, {code: 429})
182
- );
199
+ return _reject();
183
200
  });
184
201
  };
185
202
 
186
- Usage.prototype.increment = function (path, value, options) {
203
+ Usage.prototype.increment = function (name, value, options) {
187
204
  const self = this;
188
205
  const Manager = self.Manager;
189
206
  const assistant = self.assistant;
190
207
 
208
+ // Set name
209
+ name = name || 'requests';
210
+
211
+ // Set value
191
212
  value = typeof value === 'undefined' ? 1 : value;
192
213
 
214
+ // Set options
193
215
  options = options || {};
194
216
  options.id = options.id || null;
195
217
 
196
218
  // Update total and period
197
219
  ['total', 'period', 'last'].forEach((key) => {
198
- const resolved = `usage.${path}.${key}`;
220
+ const resolved = `usage.${name}.${key}`;
199
221
  const existing = _.get(self.user, resolved, 0);
200
222
 
201
223
  if (key === 'last') {
@@ -214,51 +236,58 @@ Usage.prototype.increment = function (path, value, options) {
214
236
  });
215
237
 
216
238
  // Log the updated user
217
- self.log(`Usage.init(): Incremented ${path} for user`, self.user);
239
+ self.log(`Usage.init(): Incremented ${name} for user`, self.user);
218
240
 
219
241
  return self;
220
242
  };
221
243
 
222
- Usage.prototype.set = function (path, value) {
244
+ Usage.prototype.set = function (name, value) {
223
245
  const self = this;
224
246
  const Manager = self.Manager;
225
247
  const assistant = self.assistant;
226
248
 
227
- // Update total and period
228
- const resolved = `usage.${path}.period`;
249
+ // Set name
250
+ name = name || 'requests';
229
251
 
252
+ // Set value
230
253
  value = typeof value === 'undefined' ? 0 : value;
231
254
 
255
+ // Update total and period
256
+ const resolved = `usage.${name}.period`;
257
+
232
258
  // Set the value
233
259
  _.set(self.user, resolved, value);
234
260
 
235
261
  // Log the updated user
236
- self.log(`Usage.init(): Set ${path} for user`, self.user);
262
+ self.log(`Usage.init(): Set ${name} for user`, self.user);
237
263
 
238
264
  return self;
239
265
  };
240
266
 
241
- Usage.prototype.getUsage = function (path) {
267
+ Usage.prototype.getUsage = function (name) {
242
268
  const self = this;
243
269
  const Manager = self.Manager;
244
270
  const assistant = self.assistant;
245
271
 
246
- if (path) {
247
- return _.get(self.user, `usage.${path}.period`, 0);
272
+ // Get usage
273
+ if (name) {
274
+ return _.get(self.user, `usage.${name}.period`, 0);
248
275
  } else {
249
276
  return self.user.usage;
250
277
  }
251
278
  };
252
279
 
253
- Usage.prototype.getLimit = function (path) {
280
+ Usage.prototype.getLimit = function (name) {
254
281
  const self = this;
255
282
  const Manager = self.Manager;
256
283
  const assistant = self.assistant;
257
284
 
285
+ // Get key
258
286
  const key = `products.${self.options.app}-${self.user.plan.id}.limits`;
259
287
 
260
- if (path) {
261
- return _.get(self.app, `${key}.${path}`, 0);
288
+ // Get limit
289
+ if (name) {
290
+ return _.get(self.app, `${key}.${name}`, 0);
262
291
  } else {
263
292
  return _.get(self.app, key, {});
264
293
  }
@@ -3,6 +3,9 @@ const path = require('path');
3
3
  const { get, merge } = require('lodash');
4
4
  const jetpack = require('fs-jetpack');
5
5
  const JSON5 = require('json5');
6
+ const EventEmitter = require('events');
7
+ // const EventEmitter = require('events').EventEmitter;
8
+ const util = require('util');
6
9
 
7
10
  // const { debug, log, error, warn } = require('firebase-functions/lib/logger');
8
11
  // let User;
@@ -13,6 +16,7 @@ const wrappers = './functions/wrappers';
13
16
 
14
17
  function Manager(exporter, options) {
15
18
  const self = this;
19
+
16
20
  // Constants
17
21
  self.SERVER_UUID = '11111111-1111-1111-1111-111111111111';
18
22
 
@@ -24,11 +28,15 @@ function Manager(exporter, options) {
24
28
  storage: {},
25
29
  };
26
30
 
27
- // self.isDevelopment = false;
31
+ // Setup EventEmitter
32
+ EventEmitter.call(self);
28
33
 
29
34
  return self;
30
35
  }
31
36
 
37
+ // Inherit from EventEmitter
38
+ util.inherits(Manager, EventEmitter);
39
+
32
40
  Manager.prototype.init = function (exporter, options) {
33
41
  const self = this;
34
42
 
@@ -36,6 +44,9 @@ Manager.prototype.init = function (exporter, options) {
36
44
  options = options || {};
37
45
  options.initialize = typeof options.initialize === 'undefined' ? true : options.initialize;
38
46
  options.log = typeof options.log === 'undefined' ? false : options.log;
47
+ options.projectType = typeof options.projectType === 'undefined' ? 'firebase' : options.projectType; // firebase, custom
48
+ options.routes = typeof options.routes === 'undefined' ? '/routes' : options.routes;
49
+ options.schemas = typeof options.schemas === 'undefined' ? '/schemas' : options.schemas;
39
50
  options.setupFunctions = typeof options.setupFunctions === 'undefined' ? true : options.setupFunctions;
40
51
  options.setupFunctionsLegacy = typeof options.setupFunctionsLegacy === 'undefined' ? false : options.setupFunctionsLegacy;
41
52
  options.setupFunctionsIdentity = typeof options.setupFunctionsIdentity === 'undefined' ? true : options.setupFunctionsIdentity;
@@ -71,6 +82,7 @@ Manager.prototype.init = function (exporter, options) {
71
82
  // Set properties
72
83
  self.cwd = process.cwd();
73
84
 
85
+ // Set options
74
86
  self.options = options;
75
87
  self.project = options.firebaseConfig || JSON.parse(process.env.FIREBASE_CONFIG || '{}');
76
88
  self.project.resourceZone = options.resourceZone;
@@ -206,222 +218,14 @@ Manager.prototype.init = function (exporter, options) {
206
218
  // admin.firestore().settings({/* your settings... */ timestampsInSnapshots: true})
207
219
  }
208
220
 
209
- // Main functions
210
- if (options.setupFunctions) {
211
- exporter.bm_api =
212
- self.libraries.functions
213
- .runWith({memory: '256MB', timeoutSeconds: 60})
214
- // TODO: Replace this with new API
215
- .https.onRequest(async (req, res) => self._process((new (require(`${core}/actions/api.js`))()).init(self, { req: req, res: res, })));
216
-
217
- if (options.setupFunctionsLegacy) {
218
- exporter.bm_signUpHandler =
219
- self.libraries.functions
220
- .runWith({memory: '256MB', timeoutSeconds: 60})
221
- .https.onRequest(async (req, res) => {
222
- const Module = require(`${core}/actions/sign-up-handler.js`);
223
- Module.init(self, { req: req, res: res, });
224
-
225
- return self._preProcess(Module)
226
- .then(r => Module.main())
227
- .catch(e => {
228
- self.assistant.error(e);
229
- return res.status(500).send(e.message);
230
- });
231
- });
232
-
233
- // Admin
234
- exporter.bm_createPost =
235
- self.libraries.functions
236
- .runWith({memory: '256MB', timeoutSeconds: 60})
237
- .https.onRequest(async (req, res) => {
238
- const Module = require(`${core}/admin/create-post.js`);
239
- Module.init(self, { req: req, res: res, });
240
-
241
- return self._preProcess(Module)
242
- .then(r => Module.main())
243
- .catch(e => {
244
- self.assistant.error(e);
245
- return res.status(500).send(e.message);
246
- });
247
- });
248
-
249
- exporter.bm_firestoreWrite =
250
- self.libraries.functions
251
- .runWith({memory: '256MB', timeoutSeconds: 60})
252
- .https.onRequest(async (req, res) => {
253
- const Module = require(`${core}/admin/firestore-write.js`);
254
- Module.init(self, { req: req, res: res, });
255
-
256
- return self._preProcess(Module)
257
- .then(r => Module.main())
258
- .catch(e => {
259
- self.assistant.error(e);
260
- return res.status(500).send(e.message);
261
- });
262
- });
263
-
264
- exporter.bm_getStats =
265
- self.libraries.functions
266
- .runWith({memory: '256MB', timeoutSeconds: 420})
267
- .https.onRequest(async (req, res) => {
268
- const Module = require(`${core}/admin/get-stats.js`);
269
- Module.init(self, { req: req, res: res, });
270
-
271
- return self._preProcess(Module)
272
- .then(r => Module.main())
273
- .catch(e => {
274
- self.assistant.error(e);
275
- return res.status(500).send(e.message);
276
- });
277
- });
278
-
279
- exporter.bm_sendNotification =
280
- self.libraries.functions
281
- .runWith({memory: '1GB', timeoutSeconds: 420})
282
- .https.onRequest(async (req, res) => {
283
- const Module = require(`${core}/admin/send-notification.js`);
284
- Module.init(self, { req: req, res: res, });
285
-
286
- return self._preProcess(Module)
287
- .then(r => Module.main())
288
- .catch(e => {
289
- self.assistant.error(e);
290
- return res.status(500).send(e.message);
291
- });
292
- });
293
-
294
- exporter.bm_query =
295
- self.libraries.functions
296
- .runWith({memory: '256MB', timeoutSeconds: 60})
297
- .https.onRequest(async (req, res) => {
298
- const Module = require(`${core}/admin/query.js`);
299
- Module.init(self, { req: req, res: res, });
300
-
301
- return self._preProcess(Module)
302
- .then(r => Module.main())
303
- .catch(e => {
304
- self.assistant.error(e);
305
- return res.status(500).send(e.message);
306
- });
307
- });
308
-
309
- exporter.bm_createPostHandler =
310
- self.libraries.functions
311
- .runWith({memory: '256MB', timeoutSeconds: 60})
312
- .https.onRequest(async (req, res) => {
313
- const Module = require(`${core}/actions/create-post-handler.js`);
314
- Module.init(self, { req: req, res: res, });
315
-
316
- return self._preProcess(Module)
317
- .then(r => Module.main())
318
- .catch(e => {
319
- self.assistant.error(e);
320
- return res.status(500).send(e.message);
321
- });
322
- });
323
-
324
- exporter.bm_generateUuid =
325
- self.libraries.functions
326
- .runWith({memory: '256MB', timeoutSeconds: 60})
327
- .https.onRequest(async (req, res) => {
328
- const Module = require(`${core}/actions/generate-uuid.js`);
329
- Module.init(self, { req: req, res: res, });
330
-
331
- return self._preProcess(Module)
332
- .then(r => Module.main())
333
- .catch(e => {
334
- self.assistant.error(e);
335
- return res.status(500).send(e.message);
336
- });
337
- });
338
-
339
- // Test
340
- exporter.bm_test_authenticate =
341
- self.libraries.functions
342
- .runWith({memory: '256MB', timeoutSeconds: 60})
343
- .https.onRequest(async (req, res) => {
344
- const Module = require(`${test}/authenticate.js`);
345
- Module.init(self, { req: req, res: res, });
346
-
347
- return self._preProcess(Module)
348
- .then(r => Module.main())
349
- .catch(e => {
350
- self.assistant.error(e);
351
- return res.status(500).send(e.message);
352
- });
353
- });
354
-
355
- exporter.bm_test_createTestAccounts =
356
- self.libraries.functions
357
- .runWith({memory: '256MB', timeoutSeconds: 60})
358
- .https.onRequest(async (req, res) => {
359
- const Module = require(`${test}/create-test-accounts.js`);
360
- Module.init(self, { req: req, res: res, });
361
-
362
- return self._preProcess(Module)
363
- .then(r => Module.main())
364
- .catch(e => {
365
- self.assistant.error(e);
366
- return res.status(500).send(e.message);
367
- });
368
- });
369
-
370
- exporter.bm_test_webhook =
371
- self.libraries.functions
372
- .runWith({memory: '256MB', timeoutSeconds: 60})
373
- .https.onRequest(async (req, res) => {
374
- const Module = require(`${test}/webhook.js`);
375
- Module.init(self, { req: req, res: res, });
376
-
377
- return self._preProcess(Module)
378
- .then(r => Module.main())
379
- .catch(e => {
380
- self.assistant.error(e);
381
- return res.status(500).send(e.message);
382
- });
383
- });
384
- }
385
-
386
- // Events
387
- if (options.setupFunctionsIdentity) {
388
- exporter.bm_authBeforeCreate =
389
- self.libraries.functions
390
- .runWith({memory: '256MB', timeoutSeconds: 60})
391
- .auth.user()
392
- .beforeCreate(async (user, context) => self._process((new (require(`${core}/events/auth/before-create.js`))()).init(self, { user: user, context: context})));
393
-
394
- exporter.bm_authBeforeSignIn =
395
- self.libraries.functions
396
- .runWith({memory: '256MB', timeoutSeconds: 60})
397
- .auth.user()
398
- .beforeSignIn(async (user, context) => self._process((new (require(`${core}/events/auth/before-signin.js`))()).init(self, { user: user, context: context})));
399
- }
400
-
401
- exporter.bm_authOnCreate =
402
- self.libraries.functions
403
- .runWith({memory: '256MB', timeoutSeconds: 60})
404
- .auth.user()
405
- .onCreate(async (user, context) => self._process((new (require(`${core}/events/auth/on-create.js`))()).init(self, { user: user, context: context})));
406
-
407
- exporter.bm_authOnDelete =
408
- self.libraries.functions
409
- .runWith({memory: '256MB', timeoutSeconds: 60})
410
- .auth.user()
411
- .onDelete(async (user, context) => self._process((new (require(`${core}/events/auth/on-delete.js`))()).init(self, { user: user, context: context})));
412
-
413
- exporter.bm_subOnWrite =
414
- self.libraries.functions
415
- .runWith({memory: '256MB', timeoutSeconds: 60})
416
- .firestore.document('notifications/subscriptions/all/{token}')
417
- .onWrite(async (change, context) => self._process((new (require(`${core}/events/firestore/on-subscription.js`))()).init(self, { change: change, context: context, })));
221
+ // Setup main functions
222
+ if (options.projectType === 'firebase' && options.setupFunctions) {
223
+ self.setupFunctions(exporter, options);
224
+ }
418
225
 
419
- // Cron
420
- exporter.bm_cronDaily =
421
- self.libraries.functions
422
- .runWith({ memory: '256MB', timeoutSeconds: 120 })
423
- .pubsub.schedule('every 24 hours')
424
- .onRun(async (context) => self._process((new (require(`${core}/cron/daily.js`))()).init(self, { context: context, })));
226
+ // Setup custom server
227
+ if (options.projectType === 'custom') {
228
+ self.setupCustomServer(exporter, options);
425
229
  }
426
230
 
427
231
  // Set dotenv
@@ -799,6 +603,380 @@ Manager.prototype.debug = function () {
799
603
  }
800
604
  }
801
605
 
606
+ // Setup functions
607
+ Manager.prototype.setupFunctions = function (exporter, options) {
608
+ const self = this;
609
+
610
+ // Log
611
+ if (options.log) {
612
+ self.assistant.log('Setting up Firebase functions...');
613
+ }
614
+
615
+ // Setup functions
616
+ exporter.bm_api =
617
+ self.libraries.functions
618
+ .runWith({memory: '256MB', timeoutSeconds: 60})
619
+ // TODO: Replace this with new API
620
+ .https.onRequest(async (req, res) => self._process((new (require(`${core}/actions/api.js`))()).init(self, { req: req, res: res, })));
621
+
622
+ // Setup legacy functions
623
+ if (options.setupFunctionsLegacy) {
624
+ exporter.bm_signUpHandler =
625
+ self.libraries.functions
626
+ .runWith({memory: '256MB', timeoutSeconds: 60})
627
+ .https.onRequest(async (req, res) => {
628
+ const Module = require(`${core}/actions/sign-up-handler.js`);
629
+ Module.init(self, { req: req, res: res, });
630
+
631
+ return self._preProcess(Module)
632
+ .then(r => Module.main())
633
+ .catch(e => {
634
+ self.assistant.error(e);
635
+ return res.status(500).send(e.message);
636
+ });
637
+ });
638
+
639
+ // Admin
640
+ exporter.bm_createPost =
641
+ self.libraries.functions
642
+ .runWith({memory: '256MB', timeoutSeconds: 60})
643
+ .https.onRequest(async (req, res) => {
644
+ const Module = require(`${core}/admin/create-post.js`);
645
+ Module.init(self, { req: req, res: res, });
646
+
647
+ return self._preProcess(Module)
648
+ .then(r => Module.main())
649
+ .catch(e => {
650
+ self.assistant.error(e);
651
+ return res.status(500).send(e.message);
652
+ });
653
+ });
654
+
655
+ exporter.bm_firestoreWrite =
656
+ self.libraries.functions
657
+ .runWith({memory: '256MB', timeoutSeconds: 60})
658
+ .https.onRequest(async (req, res) => {
659
+ const Module = require(`${core}/admin/firestore-write.js`);
660
+ Module.init(self, { req: req, res: res, });
661
+
662
+ return self._preProcess(Module)
663
+ .then(r => Module.main())
664
+ .catch(e => {
665
+ self.assistant.error(e);
666
+ return res.status(500).send(e.message);
667
+ });
668
+ });
669
+
670
+ exporter.bm_getStats =
671
+ self.libraries.functions
672
+ .runWith({memory: '256MB', timeoutSeconds: 420})
673
+ .https.onRequest(async (req, res) => {
674
+ const Module = require(`${core}/admin/get-stats.js`);
675
+ Module.init(self, { req: req, res: res, });
676
+
677
+ return self._preProcess(Module)
678
+ .then(r => Module.main())
679
+ .catch(e => {
680
+ self.assistant.error(e);
681
+ return res.status(500).send(e.message);
682
+ });
683
+ });
684
+
685
+ exporter.bm_sendNotification =
686
+ self.libraries.functions
687
+ .runWith({memory: '1GB', timeoutSeconds: 420})
688
+ .https.onRequest(async (req, res) => {
689
+ const Module = require(`${core}/admin/send-notification.js`);
690
+ Module.init(self, { req: req, res: res, });
691
+
692
+ return self._preProcess(Module)
693
+ .then(r => Module.main())
694
+ .catch(e => {
695
+ self.assistant.error(e);
696
+ return res.status(500).send(e.message);
697
+ });
698
+ });
699
+
700
+ exporter.bm_query =
701
+ self.libraries.functions
702
+ .runWith({memory: '256MB', timeoutSeconds: 60})
703
+ .https.onRequest(async (req, res) => {
704
+ const Module = require(`${core}/admin/query.js`);
705
+ Module.init(self, { req: req, res: res, });
706
+
707
+ return self._preProcess(Module)
708
+ .then(r => Module.main())
709
+ .catch(e => {
710
+ self.assistant.error(e);
711
+ return res.status(500).send(e.message);
712
+ });
713
+ });
714
+
715
+ exporter.bm_createPostHandler =
716
+ self.libraries.functions
717
+ .runWith({memory: '256MB', timeoutSeconds: 60})
718
+ .https.onRequest(async (req, res) => {
719
+ const Module = require(`${core}/actions/create-post-handler.js`);
720
+ Module.init(self, { req: req, res: res, });
721
+
722
+ return self._preProcess(Module)
723
+ .then(r => Module.main())
724
+ .catch(e => {
725
+ self.assistant.error(e);
726
+ return res.status(500).send(e.message);
727
+ });
728
+ });
729
+
730
+ exporter.bm_generateUuid =
731
+ self.libraries.functions
732
+ .runWith({memory: '256MB', timeoutSeconds: 60})
733
+ .https.onRequest(async (req, res) => {
734
+ const Module = require(`${core}/actions/generate-uuid.js`);
735
+ Module.init(self, { req: req, res: res, });
736
+
737
+ return self._preProcess(Module)
738
+ .then(r => Module.main())
739
+ .catch(e => {
740
+ self.assistant.error(e);
741
+ return res.status(500).send(e.message);
742
+ });
743
+ });
744
+
745
+ // Test
746
+ exporter.bm_test_authenticate =
747
+ self.libraries.functions
748
+ .runWith({memory: '256MB', timeoutSeconds: 60})
749
+ .https.onRequest(async (req, res) => {
750
+ const Module = require(`${test}/authenticate.js`);
751
+ Module.init(self, { req: req, res: res, });
752
+
753
+ return self._preProcess(Module)
754
+ .then(r => Module.main())
755
+ .catch(e => {
756
+ self.assistant.error(e);
757
+ return res.status(500).send(e.message);
758
+ });
759
+ });
760
+
761
+ exporter.bm_test_createTestAccounts =
762
+ self.libraries.functions
763
+ .runWith({memory: '256MB', timeoutSeconds: 60})
764
+ .https.onRequest(async (req, res) => {
765
+ const Module = require(`${test}/create-test-accounts.js`);
766
+ Module.init(self, { req: req, res: res, });
767
+
768
+ return self._preProcess(Module)
769
+ .then(r => Module.main())
770
+ .catch(e => {
771
+ self.assistant.error(e);
772
+ return res.status(500).send(e.message);
773
+ });
774
+ });
775
+
776
+ exporter.bm_test_webhook =
777
+ self.libraries.functions
778
+ .runWith({memory: '256MB', timeoutSeconds: 60})
779
+ .https.onRequest(async (req, res) => {
780
+ const Module = require(`${test}/webhook.js`);
781
+ Module.init(self, { req: req, res: res, });
782
+
783
+ return self._preProcess(Module)
784
+ .then(r => Module.main())
785
+ .catch(e => {
786
+ self.assistant.error(e);
787
+ return res.status(500).send(e.message);
788
+ });
789
+ });
790
+ }
791
+
792
+ // Setup identity functions
793
+ if (options.setupFunctionsIdentity) {
794
+ exporter.bm_authBeforeCreate =
795
+ self.libraries.functions
796
+ .runWith({memory: '256MB', timeoutSeconds: 60})
797
+ .auth.user()
798
+ .beforeCreate(async (user, context) => self._process((new (require(`${core}/events/auth/before-create.js`))()).init(self, { user: user, context: context})));
799
+
800
+ exporter.bm_authBeforeSignIn =
801
+ self.libraries.functions
802
+ .runWith({memory: '256MB', timeoutSeconds: 60})
803
+ .auth.user()
804
+ .beforeSignIn(async (user, context) => self._process((new (require(`${core}/events/auth/before-signin.js`))()).init(self, { user: user, context: context})));
805
+ }
806
+
807
+ // Setup events
808
+ exporter.bm_authOnCreate =
809
+ self.libraries.functions
810
+ .runWith({memory: '256MB', timeoutSeconds: 60})
811
+ .auth.user()
812
+ .onCreate(async (user, context) => self._process((new (require(`${core}/events/auth/on-create.js`))()).init(self, { user: user, context: context})));
813
+
814
+ exporter.bm_authOnDelete =
815
+ self.libraries.functions
816
+ .runWith({memory: '256MB', timeoutSeconds: 60})
817
+ .auth.user()
818
+ .onDelete(async (user, context) => self._process((new (require(`${core}/events/auth/on-delete.js`))()).init(self, { user: user, context: context})));
819
+
820
+ exporter.bm_subOnWrite =
821
+ self.libraries.functions
822
+ .runWith({memory: '256MB', timeoutSeconds: 60})
823
+ .firestore.document('notifications/subscriptions/all/{token}')
824
+ .onWrite(async (change, context) => self._process((new (require(`${core}/events/firestore/on-subscription.js`))()).init(self, { change: change, context: context, })));
825
+
826
+ // Setup cron jobs
827
+ exporter.bm_cronDaily =
828
+ self.libraries.functions
829
+ .runWith({ memory: '256MB', timeoutSeconds: 120 })
830
+ .pubsub.schedule('every 24 hours')
831
+ .onRun(async (context) => self._process((new (require(`${core}/cron/daily.js`))()).init(self, { context: context, })));
832
+ };
833
+
834
+ // Setup Custom Server
835
+ Manager.prototype.setupCustomServer = function (_library, options) {
836
+ const self = this;
837
+
838
+ // Require
839
+ const glob = require('glob').globSync;
840
+
841
+ // Log
842
+ if (options.log) {
843
+ self.assistant.log('Setting up custom server...');
844
+ }
845
+
846
+ // Setup fastify
847
+ // const app = library({
848
+ // logger: true,
849
+ // // querystringParser: str => querystring.parse(str.toLowerCase())
850
+ // });
851
+
852
+ //
853
+ const app = require('express')({
854
+ logger: true,
855
+ // querystringParser: str => querystring.parse(str.toLowerCase())
856
+ });
857
+
858
+ // Setup body parser
859
+ app.use(require('body-parser').json());
860
+
861
+ // Designate paths
862
+ const managerRoutesPath = path.normalize(`${__dirname}/routes`);
863
+ const managerSchemasPath = path.normalize(`${__dirname}/schemas`);
864
+ const customRoutesPath = path.normalize(`${self.cwd}${options.routes}`);
865
+ const customSchemasPath = path.normalize(`${self.cwd}${options.schemas}`);
866
+
867
+ // Create routes
868
+ const routes = [];
869
+
870
+ // Push function
871
+ function _push(dir, isManager) {
872
+ // Get all files
873
+ glob('**/index.js', { cwd: dir })
874
+ .forEach((file) => {
875
+ // Build the item
876
+ const item = {
877
+ name: file.replace('/index.js', ''),
878
+ namespace: file,
879
+ path: path.resolve(dir, file),
880
+ dir: dir,
881
+ isManager: isManager,
882
+ }
883
+
884
+ // If it exists in routes, replace it
885
+ const existing = routes.findIndex(r => r.name === item.name);
886
+ if (existing > -1) {
887
+ routes[existing] = item;
888
+ return;
889
+ }
890
+
891
+ // Otherwise, push it
892
+ routes.push(item);
893
+ });
894
+ }
895
+
896
+ // Push routes
897
+ // _push(`${__dirname}/routes`)
898
+ _push(managerRoutesPath, true)
899
+ _push(customRoutesPath, false)
900
+
901
+ // Log routes
902
+ // if (options.log) {
903
+ // self.assistant.log('Routes:', routes);
904
+ // }
905
+
906
+ // Install process
907
+ routes.forEach((file) => {
908
+ // console.log('---file', file);
909
+ // Require the file
910
+ const cors = self.libraries.cors;
911
+
912
+ // Log
913
+ if (options.log) {
914
+ self.assistant.log(`Initializing route: ${file.name} @ ${file.path}`);
915
+ }
916
+
917
+ // Register the route
918
+ app.all(`/${file.name}`, async (req, res) => {
919
+ return cors(req, res, async () => {
920
+ // self.Middleware(req, res).run(file.name, {schema: file.name})
921
+ self.Middleware(req, res).run(file.name, {
922
+ schema: file.name,
923
+ routesDir: file.isManager ? managerRoutesPath : customRoutesPath,
924
+ schemasDir: file.isManager ? managerSchemasPath : customSchemasPath,
925
+ })
926
+ });
927
+ })
928
+
929
+ // app.all(`/${name}`, async (req, res) => {
930
+ // return cors(req, res, async () => {
931
+ // // Fix req/res
932
+ // req.body = req.body || {};
933
+ // req.query = Object.assign({}, req.query || {});
934
+
935
+ // // Manager.Middleware(req, res).run('tools/screenshot', {schema: 'screenshot'})
936
+ // const handler = new (require(file.path))();
937
+ // const assistant = self.Assistant({req: req, res: res}, {functionName: name, functionType: 'http'});
938
+ // // const apiUser = await ApiManager.getUser(assistant);
939
+
940
+ // // Set handler properties
941
+ // handler.Manager = self;
942
+ // handler.assistant = assistant;
943
+ // handler.apiUser = null;
944
+
945
+ // // Log
946
+ // if (options.log) {
947
+ // self.assistant.log(`[Request] ${name} @ ${filepath}`, req.body, req.query);
948
+ // }
949
+
950
+ // // Execute the route
951
+ // try {
952
+ // await handler.process(req, res);
953
+ // } catch (e) {
954
+ // assistant.respond(e, {code: e.code});
955
+ // }
956
+ // });
957
+ // })
958
+ });
959
+
960
+ // Run the server!
961
+ const server = app.listen({ port: process.env.PORT || 3000, host: '0.0.0.0' }, () => {
962
+ const address = server.address();
963
+
964
+ // Check if there's an error
965
+ if (server.address() === null) {
966
+ self.assistant.error(e);
967
+ process.exit(1);
968
+ }
969
+
970
+ // Log
971
+ if (options.log) {
972
+ self.assistant.log(`Server listening on ${address.address}:${address.port}`);
973
+ }
974
+
975
+ // Emit event
976
+ self.emit('online', new Event('online'), server, app);
977
+ });
978
+ }
979
+
802
980
  function resolveProjectPackage() {
803
981
  try {
804
982
  return require(path.resolve(process.cwd(), 'functions', 'package.json'));
@@ -0,0 +1,52 @@
1
+ function Route() {
2
+ const self = this;
3
+
4
+ return self;
5
+ }
6
+
7
+ Route.prototype.process = async function (assistant) {
8
+ const self = this;
9
+
10
+ // Set shortcuts
11
+ const Manager = assistant.Manager;
12
+ const usage = assistant.usage;
13
+ const user = assistant.usage.user;
14
+ const analytics = assistant.analytics;
15
+ const settings = assistant.settings;
16
+
17
+ // Load preloaded libraries
18
+ const jetpack = require('fs-jetpack');
19
+
20
+ // Send analytics event
21
+ analytics.event({
22
+ name: 'restart',
23
+ params: {},
24
+ });
25
+
26
+ // Check for user authentication
27
+ // if (!user.roles.admin) {
28
+ // return assistant.respond(`Admin required`, {code: 401});;
29
+ // }
30
+
31
+ // Log
32
+ assistant.log('Restarting...');
33
+
34
+ // Remove node_modules
35
+ jetpack.remove('node_modules');
36
+
37
+ // Perform delayed refresh to allow a successful response
38
+ setTimeout(function () {
39
+ require('child_process').exec('refresh', (error, stdout, stderr) => {
40
+ // Quit the process if there is an error
41
+ if (error || stderr) {
42
+ console.log(`error: ${error ? error.message : stderr}`);
43
+ return process.exit(1);
44
+ }
45
+ });
46
+ }, 1000);
47
+
48
+ // Return success
49
+ assistant.respond({success: true});
50
+ };
51
+
52
+ module.exports = Route;
@@ -0,0 +1,43 @@
1
+ function Route() {
2
+ const self = this;
3
+
4
+ return self;
5
+ }
6
+
7
+ Route.prototype.main = async function (assistant) {
8
+ const self = this;
9
+
10
+ // Set shortcuts
11
+ const Manager = assistant.Manager;
12
+ const usage = assistant.usage;
13
+ const user = assistant.usage.user;
14
+ const analytics = assistant.analytics;
15
+ const settings = assistant.settings;
16
+
17
+ // Load preloaded libraries
18
+ // ..
19
+
20
+ // Send analytics event
21
+ analytics.event({
22
+ name: 'test',
23
+ params: {},
24
+ });
25
+
26
+ // Check for user authentication
27
+ // if (!user.roles.admin) {
28
+ // return assistant.respond(`Admin required`, {code: 401});;
29
+ // }
30
+
31
+ // Log
32
+ assistant.log('Running test');
33
+ assistant.log('assistant.request.body', assistant.request.body);
34
+ assistant.log('assistant.request.query', assistant.request.query);
35
+ assistant.log('assistant.request.headers', assistant.request.headers);
36
+ assistant.log('assistant.request.data', assistant.request.data);
37
+ assistant.log('assistant.settings', assistant.settings);
38
+
39
+ // Return success
40
+ assistant.respond({timestamp: new Date().toISOString(), id: assistant.id});
41
+ };
42
+
43
+ module.exports = Route;
@@ -0,0 +1,13 @@
1
+ module.exports = function (assistant) {
2
+ return {
3
+ // DEFAULTS
4
+ ['defaults']: {
5
+ // Common
6
+ delay: {
7
+ types: ['number'],
8
+ default: 1000,
9
+ value: undefined,
10
+ },
11
+ },
12
+ };
13
+ }
@@ -0,0 +1,13 @@
1
+ module.exports = function (assistant) {
2
+ return {
3
+ // DEFAULTS
4
+ ['defaults']: {
5
+ // Common
6
+ delay: {
7
+ types: ['number'],
8
+ default: 1000,
9
+ value: undefined,
10
+ },
11
+ },
12
+ };
13
+ }