backend-manager 2.5.45 → 2.5.47

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": "2.5.45",
3
+ "version": "2.5.47",
4
4
  "description": "Quick tools for developing Firebase functions",
5
5
  "main": "src/manager/index.js",
6
6
  "bin": {
@@ -51,13 +51,13 @@ Module.prototype.main = function () {
51
51
 
52
52
  const response = responses[0];
53
53
  assistant.log('Saved backup successfully:', response.metadata.outputUriPrefix, {environment: 'development'})
54
+
54
55
  return resolve(response['name']);
55
56
  })
56
57
  .catch(async (e) => {
57
58
  await self._setMetaStats(e);
58
59
  return reject(assistant.errorManager(e, {code: 500, sentry: false, send: false, log: true}).error)
59
60
  });
60
-
61
61
  }
62
62
  });
63
63
 
@@ -23,29 +23,28 @@ Module.prototype.main = function () {
23
23
  .get()
24
24
  .then(async (doc) => {
25
25
  let data = doc.data() || {};
26
- let error = null;
27
-
28
- await self.updateStats(data)
29
- .catch(e => {
30
- error = e;
31
- })
32
26
 
27
+ // Only update if requested
28
+ if (payload.data.payload.update) {
29
+ await self.updateStats(data)
30
+ .catch(e => data = e)
31
+ }
33
32
 
34
- if (error) {
35
- return reject(assistant.errorManager(error, {code: 500, sentry: false, send: false, log: false}).error)
33
+ if (data instanceof Error) {
34
+ return reject(assistant.errorManager(data, {code: 500, sentry: false, send: false, log: false}).error)
36
35
  }
37
36
 
37
+ // Retrieve the stats again after updating
38
38
  await stats
39
39
  .get()
40
40
  .then(doc => {
41
41
  data = doc.data() || {};
42
42
  })
43
- .catch((e) => {
44
- error = e;
45
- })
43
+ .catch(e => data = e)
44
+
46
45
 
47
- if (error) {
48
- return reject(assistant.errorManager(error, {code: 500, sentry: false, send: false, log: false}).error)
46
+ if (data instanceof Error) {
47
+ return reject(assistant.errorManager(data, {code: 500, sentry: false, send: false, log: false}).error)
49
48
  }
50
49
 
51
50
  return resolve({data: data})
@@ -70,6 +69,7 @@ Module.prototype.fixStats = function (data) {
70
69
  });
71
70
  }
72
71
 
72
+ // TODO: ADD https://firebase.google.com/docs/firestore/query-data/aggregation-queries#pricing
73
73
  Module.prototype.updateStats = function (existingData) {
74
74
  const self = this;
75
75
 
@@ -0,0 +1,121 @@
1
+ const {get, merge} = require('lodash');
2
+
3
+ function Module() {
4
+
5
+ }
6
+
7
+ Module.prototype.main = function () {
8
+ const self = this;
9
+ const Manager = self.Manager;
10
+ const Api = self.Api;
11
+ const assistant = self.assistant;
12
+ const payload = self.payload;
13
+
14
+ return new Promise(async function(resolve, reject) {
15
+
16
+ // If the user is not an admin, reject
17
+ if (!payload.user.roles.admin && assistant.meta.environment === 'production') {
18
+ return reject(assistant.errorManager(`Admin required.`, {code: 401, sentry: false, send: false, log: false}).error)
19
+ }
20
+
21
+ // Get lastPageToken from meta/stats
22
+ self.libraries.admin.firestore().doc(`meta/stats`)
23
+ .get()
24
+ .then(async (doc) => {
25
+ const data = doc.data() || {};
26
+ const lastPageToken = get(data, 'syncUsers.lastPageToken', undefined);
27
+ let processedUsers = 0;
28
+
29
+ // Initial pageToken
30
+ assistant.log(`Running syn-users based on lastPageToken: ${lastPageToken}`);
31
+
32
+ // List firebase auth users
33
+ await Manager.Utilities().iterateUsers(function (batch, index) {
34
+ return new Promise(async function(resolve, reject) {
35
+
36
+ // Process user function
37
+ async function _process(user, i) {
38
+ const account = user.toJSON();
39
+ const uid = account.uid;
40
+ const email = account.email;
41
+ const created = new Date(account.metadata.creationTime);
42
+ const activity = new Date(account.metadata.lastSignInTime);
43
+ const isAnonymous = account.providerData.length === 0;
44
+
45
+ // Skip anonymous users
46
+ if (isAnonymous) {
47
+ return
48
+ }
49
+
50
+ // Add the user to the database only if it doesn't exist
51
+ await self.libraries.admin.firestore().doc(`users/${account.uid}`)
52
+ .get()
53
+ .then(async (doc) => {
54
+ const data = doc.data() || {};
55
+
56
+ const newUser = Manager.User({
57
+ auth: {
58
+ uid: uid,
59
+ email: email,
60
+ },
61
+ activity: {
62
+ created: {
63
+ timestamp: created.toISOString(),
64
+ timestampUNIX: Math.floor(created.getTime() / 1000),
65
+ },
66
+ lastActivity: {
67
+ timestamp: activity.toISOString(),
68
+ timestampUNIX: Math.floor(activity.getTime() / 1000),
69
+ },
70
+ }
71
+ });
72
+
73
+ const finalData = merge(newUser.properties, data);
74
+
75
+ await self.libraries.admin.firestore().doc(`users/${account.uid}`)
76
+ .set(finalData, {merge: true})
77
+ .then(r => {
78
+ assistant.log(`Synched user: ${account.uid}`);
79
+ processedUsers++;
80
+ })
81
+ })
82
+ }
83
+
84
+ // Process each user
85
+ for (var i = 0; i < batch.users.length; i++) {
86
+ await _process(batch.users[i], i);
87
+ }
88
+
89
+ // Save to database only if there is a page token
90
+ if (batch.pageToken) {
91
+ await self.libraries.admin.firestore().doc(`meta/stats`)
92
+ .update({
93
+ syncUsers: {
94
+ lastPageToken: batch.pageToken
95
+ }
96
+ })
97
+ .then(r => {
98
+ assistant.log(`Saved lastPageToken: ${batch.pageToken}`);
99
+ })
100
+ .catch(e => {
101
+ assistant.error('Failed to update lastPageToken', e);
102
+ })
103
+ }
104
+
105
+ return resolve();
106
+ })
107
+ }, {batchSize: 2, log: true, pageToken: lastPageToken})
108
+
109
+ assistant.log(`Processed ${processedUsers} users.`);
110
+
111
+ // Complete
112
+ return resolve();
113
+ })
114
+ .catch(e => {
115
+ return reject(assistant.errorManager(e, {code: 500, sentry: false, send: false, log: false}).error)
116
+ })
117
+ });
118
+
119
+ };
120
+
121
+ module.exports = Module;
@@ -22,21 +22,44 @@ Module.prototype.main = function() {
22
22
  const context = self.context;
23
23
 
24
24
  return new Promise(async function(resolve, reject) {
25
- fetch(`${Manager.project.functionsUrl}/bm_api`, {
26
- method: 'post',
27
- response: 'json',
28
- body: {
29
- backendManagerKey: Manager.config.backend_manager.key,
30
- command: 'admin:backup',
31
- }
32
- })
33
- .then(response => {
34
- assistant.log(`Successfully executed backup:`, response, {environment: 'production'})
35
- return resolve(response);
25
+ // Wait for all the promises to resolve
26
+ Promise.all([
27
+ // Backup the database
28
+ fetch(`${Manager.project.functionsUrl}/bm_api`, {
29
+ method: 'post',
30
+ response: 'json',
31
+ body: {
32
+ backendManagerKey: Manager.config.backend_manager.key,
33
+ command: 'admin:backup',
34
+ }
35
+ })
36
+ .then(response => {
37
+ assistant.log(`Successfully executed backup:`, response, {environment: 'production'})
38
+ }),
39
+
40
+ // Sync Firestore users to the database
41
+ fetch(`${Manager.project.functionsUrl}/bm_api`, {
42
+ method: 'post',
43
+ response: 'json',
44
+ body: {
45
+ backendManagerKey: Manager.config.backend_manager.key,
46
+ command: 'admin:sync-users',
47
+ }
48
+ })
49
+ .then(response => {
50
+ assistant.log(`Successfully executed sync-users:`, response, {environment: 'production'})
51
+ }),
52
+
53
+ // More daily processes
54
+ // ...
55
+ ])
56
+ .then(() => {
57
+ assistant.log(`Successfully executed all daily processes:`, {environment: 'production'})
58
+ return resolve();
36
59
  })
37
60
  .catch(e => {
38
- assistant.errorManager(`Error executing backup: ${e}`, {sentry: true, send: false, log: true})
39
- return reject();
61
+ assistant.errorManager(`Error executing all proceses: ${e}`, {sentry: true, send: false, log: true})
62
+ return reject(e);
40
63
  })
41
64
  });
42
65
  }
@@ -90,12 +90,14 @@ Utilities.prototype.iterateUsers = function (callback, options) {
90
90
  let batch = -1;
91
91
 
92
92
  options = options || {};
93
+ options.batchSize = options.batchSize || 1000;
93
94
  options.log = options.log;
95
+ options.pageToken = options.pageToken;
94
96
 
95
97
  function listAllUsers(nextPageToken) {
96
98
  // List batch of users, 1000 at a time.
97
99
  admin.auth()
98
- .listUsers(1000, nextPageToken)
100
+ .listUsers(options.batchSize, nextPageToken)
99
101
  .then(async (listUsersResult) => {
100
102
 
101
103
  batch++;
@@ -105,7 +107,9 @@ Utilities.prototype.iterateUsers = function (callback, options) {
105
107
  }
106
108
 
107
109
  callback({
108
- snap: listUsersResult, users: listUsersResult.users,
110
+ snap: listUsersResult,
111
+ users: listUsersResult.users,
112
+ pageToken: listUsersResult.pageToken,
109
113
  }, batch)
110
114
  .then(r => {
111
115
  if (listUsersResult.pageToken) {
@@ -126,7 +130,7 @@ Utilities.prototype.iterateUsers = function (callback, options) {
126
130
  });
127
131
  }
128
132
 
129
- listAllUsers();
133
+ listAllUsers(options.pageToken);
130
134
  });
131
135
  };
132
136