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 +1 -1
- package/src/manager/functions/core/actions/api/admin/backup.js +1 -1
- package/src/manager/functions/core/actions/api/admin/get-stats.js +13 -13
- package/src/manager/functions/core/actions/api/admin/sync-users.js +121 -0
- package/src/manager/functions/core/cron/daily.js +36 -13
- package/src/manager/helpers/utilities.js +7 -3
package/package.json
CHANGED
|
@@ -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 (
|
|
35
|
-
return reject(assistant.errorManager(
|
|
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(
|
|
44
|
-
|
|
45
|
-
})
|
|
43
|
+
.catch(e => data = e)
|
|
44
|
+
|
|
46
45
|
|
|
47
|
-
if (
|
|
48
|
-
return reject(assistant.errorManager(
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
|
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(
|
|
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,
|
|
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
|
|