backend-manager 3.2.169 → 3.2.171
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/LICENSE +1 -1
- package/README.md +1 -1
- package/dist/cli/cli.js +1534 -0
- package/dist/manager/functions/core/actions/api/admin/backup.js +338 -0
- package/dist/manager/functions/core/actions/api/admin/create-post.js +388 -0
- package/dist/manager/functions/core/actions/api/admin/cron.js +37 -0
- package/dist/manager/functions/core/actions/api/admin/database-read.js +35 -0
- package/dist/manager/functions/core/actions/api/admin/database-write.js +39 -0
- package/dist/manager/functions/core/actions/api/admin/edit-post.js +158 -0
- package/dist/manager/functions/core/actions/api/admin/firestore-query.js +165 -0
- package/dist/manager/functions/core/actions/api/admin/firestore-read.js +38 -0
- package/dist/manager/functions/core/actions/api/admin/firestore-write.js +54 -0
- package/dist/manager/functions/core/actions/api/admin/get-stats.js +269 -0
- package/dist/manager/functions/core/actions/api/admin/payment-processor.js +57 -0
- package/dist/manager/functions/core/actions/api/admin/run-hook.js +95 -0
- package/dist/manager/functions/core/actions/api/admin/send-notification.js +197 -0
- package/dist/manager/functions/core/actions/api/admin/sync-users.js +125 -0
- package/dist/manager/functions/core/actions/api/admin/templates/post.html +16 -0
- package/dist/manager/functions/core/actions/api/firebase/get-providers.js +102 -0
- package/dist/manager/functions/core/actions/api/general/emails/general:download-app-link.js +21 -0
- package/dist/manager/functions/core/actions/api/general/fetch-post.js +99 -0
- package/dist/manager/functions/core/actions/api/general/generate-uuid.js +41 -0
- package/dist/manager/functions/core/actions/api/general/send-email.js +112 -0
- package/dist/manager/functions/core/actions/api/handler/create-post.js +146 -0
- package/dist/manager/functions/core/actions/api/special/setup-electron-manager-client.js +103 -0
- package/dist/manager/functions/core/actions/api/template.js +33 -0
- package/dist/manager/functions/core/actions/api/test/authenticate.js +22 -0
- package/dist/manager/functions/core/actions/api/test/create-test-accounts.js +27 -0
- package/dist/manager/functions/core/actions/api/test/lab.js +55 -0
- package/dist/manager/functions/core/actions/api/test/redirect.js +26 -0
- package/dist/manager/functions/core/actions/api/test/webhook.js +30 -0
- package/dist/manager/functions/core/actions/api/user/create-custom-token.js +32 -0
- package/dist/manager/functions/core/actions/api/user/delete.js +68 -0
- package/dist/manager/functions/core/actions/api/user/get-active-sessions.js +45 -0
- package/dist/manager/functions/core/actions/api/user/get-subscription-info.js +49 -0
- package/dist/manager/functions/core/actions/api/user/oauth2/discord.js +114 -0
- package/dist/manager/functions/core/actions/api/user/oauth2/google.js +99 -0
- package/dist/manager/functions/core/actions/api/user/oauth2.js +476 -0
- package/dist/manager/functions/core/actions/api/user/regenerate-api-keys.js +54 -0
- package/dist/manager/functions/core/actions/api/user/resolve.js +32 -0
- package/dist/manager/functions/core/actions/api/user/sign-out-all-sessions.js +118 -0
- package/dist/manager/functions/core/actions/api/user/sign-up copy.js +544 -0
- package/dist/manager/functions/core/actions/api/user/sign-up.js +99 -0
- package/dist/manager/functions/core/actions/api/user/submit-feedback.js +96 -0
- package/dist/manager/functions/core/actions/api/user/validate-settings.js +86 -0
- package/dist/manager/functions/core/actions/api.js +354 -0
- package/dist/manager/functions/core/actions/create-post-handler.js +184 -0
- package/dist/manager/functions/core/actions/generate-uuid.js +62 -0
- package/dist/manager/functions/core/actions/sign-up-handler.js +205 -0
- package/dist/manager/functions/core/admin/create-post.js +206 -0
- package/dist/manager/functions/core/admin/firestore-write.js +72 -0
- package/dist/manager/functions/core/admin/get-stats.js +218 -0
- package/dist/manager/functions/core/admin/query.js +198 -0
- package/dist/manager/functions/core/admin/send-notification.js +206 -0
- package/dist/manager/functions/core/cron/daily/ghostii-auto-publisher.js +377 -0
- package/dist/manager/functions/core/cron/daily/reset-usage.js +197 -0
- package/dist/manager/functions/core/cron/daily.js +114 -0
- package/dist/manager/functions/core/events/auth/before-create.js +124 -0
- package/dist/manager/functions/core/events/auth/before-signin.js +62 -0
- package/dist/manager/functions/core/events/auth/on-create copy.js +121 -0
- package/dist/manager/functions/core/events/auth/on-create.js +564 -0
- package/dist/manager/functions/core/events/auth/on-delete.js +72 -0
- package/dist/manager/functions/core/events/firestore/on-subscription.js +107 -0
- package/dist/manager/functions/test/authenticate.js +38 -0
- package/dist/manager/functions/test/create-test-accounts.js +144 -0
- package/dist/manager/functions/test/webhook.js +37 -0
- package/dist/manager/functions/wrappers/mailchimp/addToList.js +25 -0
- package/dist/manager/helpers/analytics copy.js +217 -0
- package/dist/manager/helpers/analytics.js +467 -0
- package/dist/manager/helpers/api-manager.js +324 -0
- package/dist/manager/helpers/assistant.js +1043 -0
- package/dist/manager/helpers/metadata.js +32 -0
- package/dist/manager/helpers/middleware.js +154 -0
- package/dist/manager/helpers/roles.js +69 -0
- package/dist/manager/helpers/settings.js +158 -0
- package/dist/manager/helpers/subscription-resolver-new.js +828 -0
- package/dist/manager/helpers/subscription-resolver.js +842 -0
- package/dist/manager/helpers/usage.js +381 -0
- package/dist/manager/helpers/user.js +198 -0
- package/dist/manager/helpers/utilities.js +292 -0
- package/dist/manager/index.js +1076 -0
- package/dist/manager/libraries/openai.js +460 -0
- package/dist/manager/routes/restart/index.js +52 -0
- package/dist/manager/routes/test/index.js +43 -0
- package/dist/manager/schemas/restart.js +13 -0
- package/dist/manager/schemas/test.js +13 -0
- package/dist/require.js +3 -0
- package/package.json +19 -9
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
let Module = {
|
|
2
|
+
init: async function (Manager, data) {
|
|
3
|
+
this.Manager = Manager;
|
|
4
|
+
this.libraries = Manager.libraries;
|
|
5
|
+
this.assistant = Manager.Assistant({req: data.req, res: data.res})
|
|
6
|
+
this.req = data.req;
|
|
7
|
+
this.res = data.res;
|
|
8
|
+
|
|
9
|
+
return this;
|
|
10
|
+
},
|
|
11
|
+
main: async function() {
|
|
12
|
+
const self = this;
|
|
13
|
+
let libraries = self.libraries;
|
|
14
|
+
let assistant = self.assistant;
|
|
15
|
+
let req = self.req;
|
|
16
|
+
let res = self.res;
|
|
17
|
+
let options = self.assistant.request.data;
|
|
18
|
+
let admin = self.Manager.libraries.admin;
|
|
19
|
+
|
|
20
|
+
let response = {
|
|
21
|
+
status: 200,
|
|
22
|
+
data: {},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
return libraries.cors(req, res, async () => {
|
|
26
|
+
let user = await assistant.authenticate();
|
|
27
|
+
|
|
28
|
+
// Analytics
|
|
29
|
+
let analytics = self.Manager.Analytics({
|
|
30
|
+
assistant: assistant,
|
|
31
|
+
uuid: user.auth.uid,
|
|
32
|
+
})
|
|
33
|
+
.event({
|
|
34
|
+
category: 'admin',
|
|
35
|
+
action: 'firestore-write',
|
|
36
|
+
// label: '',
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Options
|
|
40
|
+
options.path = `${options.path || ''}`
|
|
41
|
+
options.document = options.document || {};
|
|
42
|
+
options.options = options.options || {};
|
|
43
|
+
options.options.merge = typeof options.options.merge === 'undefined' ? true : options.options.merge;
|
|
44
|
+
|
|
45
|
+
if (!user.roles.admin) {
|
|
46
|
+
response.status = 401;
|
|
47
|
+
response.error = new Error('Unauthenticated, admin required.');
|
|
48
|
+
} else if (!options.path) {
|
|
49
|
+
response.status = 401;
|
|
50
|
+
response.error = new Error('Path parameter required');
|
|
51
|
+
} else {
|
|
52
|
+
await admin.firestore().doc(options.path)
|
|
53
|
+
.set(options.document, options.options)
|
|
54
|
+
.then(r => {
|
|
55
|
+
assistant.log(`Wrote to ${options.path}:`, options.document, options.options)
|
|
56
|
+
})
|
|
57
|
+
.catch(e => {
|
|
58
|
+
response.status = 500;
|
|
59
|
+
response.error = e;
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (response.status === 200) {
|
|
64
|
+
return res.status(response.status).json(response.data);
|
|
65
|
+
} else {
|
|
66
|
+
assistant.error(response.error)
|
|
67
|
+
return res.status(response.status).send(response.error.message);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
},
|
|
71
|
+
}
|
|
72
|
+
module.exports = Module;
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
let Module = {
|
|
2
|
+
init: async function (Manager, data) {
|
|
3
|
+
this.Manager = Manager;
|
|
4
|
+
this.libraries = Manager.libraries;
|
|
5
|
+
this.assistant = Manager.Assistant({req: data.req, res: data.res})
|
|
6
|
+
this.req = data.req;
|
|
7
|
+
this.res = data.res;
|
|
8
|
+
|
|
9
|
+
return this;
|
|
10
|
+
},
|
|
11
|
+
main: async function() {
|
|
12
|
+
let self = this;
|
|
13
|
+
let libraries = self.libraries;
|
|
14
|
+
let assistant = self.assistant;
|
|
15
|
+
let req = self.req;
|
|
16
|
+
let res = self.res;
|
|
17
|
+
|
|
18
|
+
let response = {
|
|
19
|
+
status: 200,
|
|
20
|
+
data: {},
|
|
21
|
+
error: null,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
return libraries.cors(req, res, async () => {
|
|
25
|
+
// authenticate admin!
|
|
26
|
+
let user = await assistant.authenticate();
|
|
27
|
+
|
|
28
|
+
// Analytics
|
|
29
|
+
let analytics = self.Manager.Analytics({
|
|
30
|
+
assistant: assistant,
|
|
31
|
+
uuid: user.auth.uid,
|
|
32
|
+
})
|
|
33
|
+
.event({
|
|
34
|
+
category: 'admin',
|
|
35
|
+
action: 'get-stats',
|
|
36
|
+
// label: '',
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
if (!user.roles.admin) {
|
|
40
|
+
response.status = 401;
|
|
41
|
+
response.error = new Error('Unauthenticated, admin required.');
|
|
42
|
+
assistant.error(response.error)
|
|
43
|
+
} else {
|
|
44
|
+
let stats = libraries.admin.firestore().doc(`meta/stats`)
|
|
45
|
+
|
|
46
|
+
await stats
|
|
47
|
+
.get()
|
|
48
|
+
.then(async function (doc) {
|
|
49
|
+
response.data = doc.data() || {};
|
|
50
|
+
await self.fixStats(response.data)
|
|
51
|
+
.catch(e => {
|
|
52
|
+
response.status = 500;
|
|
53
|
+
response.error = new Error(`Failed fixing stats: ${e.message}`);
|
|
54
|
+
assistant.error(response.error)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
await self.updateStats()
|
|
58
|
+
.catch(e => {
|
|
59
|
+
response.status = 500;
|
|
60
|
+
response.error = new Error(`Failed updating stats: ${e.message}`);
|
|
61
|
+
assistant.error(response.error)
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
await stats
|
|
65
|
+
.get()
|
|
66
|
+
.then(r => {
|
|
67
|
+
response.data = r.data() || {};
|
|
68
|
+
})
|
|
69
|
+
.catch(function (e) {
|
|
70
|
+
response.status = 500;
|
|
71
|
+
response.error = e;
|
|
72
|
+
assistant.error(response.error)
|
|
73
|
+
})
|
|
74
|
+
})
|
|
75
|
+
.catch(function (e) {
|
|
76
|
+
response.status = 500;
|
|
77
|
+
response.error = e;
|
|
78
|
+
assistant.error(response.error)
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// response.data = data;
|
|
83
|
+
|
|
84
|
+
assistant.log('Stats', assistant.request.data, response);
|
|
85
|
+
|
|
86
|
+
if (response.status === 200) {
|
|
87
|
+
return res.status(response.status).json(response.data);
|
|
88
|
+
} else {
|
|
89
|
+
return res.status(response.status).send(response.error.message);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
},
|
|
93
|
+
fixStats: function (data) {
|
|
94
|
+
let self = this;
|
|
95
|
+
return new Promise(async function(resolve, reject) {
|
|
96
|
+
let stats = self.libraries.admin.firestore().doc(`meta/stats`);
|
|
97
|
+
|
|
98
|
+
if (!data || !data.users || !data.users.total || !data.subscriptions || !data.subscriptions.total) {
|
|
99
|
+
let usersTotal = 0;
|
|
100
|
+
let subscriptionsTotal = 0;
|
|
101
|
+
await self.getAllUsers()
|
|
102
|
+
.then(r => {
|
|
103
|
+
usersTotal = r.length
|
|
104
|
+
})
|
|
105
|
+
.catch(e => {
|
|
106
|
+
response.status = 500;
|
|
107
|
+
response.error = new Error(`Failed fixing stats: ${e.message}`);
|
|
108
|
+
self.assistant.error(response.error);
|
|
109
|
+
})
|
|
110
|
+
await self.getAllSubscriptions()
|
|
111
|
+
.then(r => {
|
|
112
|
+
subscriptionsTotal = r
|
|
113
|
+
})
|
|
114
|
+
.catch(e => {
|
|
115
|
+
response.status = 500;
|
|
116
|
+
response.error = new Error(`Failed fixing stats: ${e.message}`);
|
|
117
|
+
self.assistant.error(response.error);
|
|
118
|
+
})
|
|
119
|
+
await stats
|
|
120
|
+
.set({
|
|
121
|
+
users: {
|
|
122
|
+
total: usersTotal,
|
|
123
|
+
},
|
|
124
|
+
subscriptions: {
|
|
125
|
+
total: subscriptionsTotal,
|
|
126
|
+
},
|
|
127
|
+
}, { merge: true })
|
|
128
|
+
.catch(function (e) {
|
|
129
|
+
return reject(e);
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
return resolve(data);
|
|
135
|
+
});
|
|
136
|
+
},
|
|
137
|
+
updateStats: function () {
|
|
138
|
+
let self = this;
|
|
139
|
+
return new Promise(async function(resolve, reject) {
|
|
140
|
+
let stats = self.libraries.admin.firestore().doc(`meta/stats`);
|
|
141
|
+
let online = self.libraries.admin.database().ref(`gatherings/online`);
|
|
142
|
+
let onlineCount = 0;
|
|
143
|
+
|
|
144
|
+
await online
|
|
145
|
+
.once('value')
|
|
146
|
+
.then((snap) => {
|
|
147
|
+
let data = snap.val() || {};
|
|
148
|
+
let keys = Object.keys(data);
|
|
149
|
+
onlineCount = keys.length;
|
|
150
|
+
})
|
|
151
|
+
.catch(e => {
|
|
152
|
+
return reject(e);
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
await stats
|
|
156
|
+
.set({
|
|
157
|
+
users: {
|
|
158
|
+
online: onlineCount
|
|
159
|
+
}
|
|
160
|
+
}, { merge: true })
|
|
161
|
+
.catch(function (e) {
|
|
162
|
+
return reject(e);
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
return resolve();
|
|
166
|
+
});
|
|
167
|
+
},
|
|
168
|
+
getAllUsers: function () {
|
|
169
|
+
let self = this;
|
|
170
|
+
return new Promise(async function(resolve, reject) {
|
|
171
|
+
self.users = [];
|
|
172
|
+
await getUsersBatch(self)
|
|
173
|
+
.catch(e => {
|
|
174
|
+
return reject(e);
|
|
175
|
+
})
|
|
176
|
+
return resolve(self.users);
|
|
177
|
+
});
|
|
178
|
+
},
|
|
179
|
+
getAllSubscriptions: function () {
|
|
180
|
+
let self = this;
|
|
181
|
+
return new Promise(async function(resolve, reject) {
|
|
182
|
+
await self.libraries.admin.firestore().collection('notifications/subscriptions/all')
|
|
183
|
+
.get()
|
|
184
|
+
.then(function(querySnapshot) {
|
|
185
|
+
return resolve(querySnapshot.size)
|
|
186
|
+
})
|
|
187
|
+
.catch(function(e) {
|
|
188
|
+
return reject(e)
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
module.exports = Module;
|
|
195
|
+
|
|
196
|
+
function getUsersBatch(self, nextPageToken) {
|
|
197
|
+
return new Promise(async function(resolve, reject) {
|
|
198
|
+
self.libraries.admin.auth().listUsers(1000, nextPageToken)
|
|
199
|
+
.then(function(listUsersResult) {
|
|
200
|
+
self.users = self.users.concat(listUsersResult.users);
|
|
201
|
+
if (listUsersResult.pageToken) {
|
|
202
|
+
// List next batch of users.
|
|
203
|
+
getUsersBatch(self, listUsersResult.pageToken)
|
|
204
|
+
.then(() => {
|
|
205
|
+
return resolve(listUsersResult.users);
|
|
206
|
+
})
|
|
207
|
+
.catch((e) => {
|
|
208
|
+
return reject(e);
|
|
209
|
+
})
|
|
210
|
+
} else {
|
|
211
|
+
return resolve(listUsersResult.users);
|
|
212
|
+
}
|
|
213
|
+
})
|
|
214
|
+
.catch(function(e) {
|
|
215
|
+
return reject(e);
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
const _ = require('lodash');
|
|
2
|
+
const powertools = require('node-powertools');
|
|
3
|
+
|
|
4
|
+
let Module = {
|
|
5
|
+
init: async function (Manager, data) {
|
|
6
|
+
this.Manager = Manager;
|
|
7
|
+
this.libraries = Manager.libraries;
|
|
8
|
+
this.assistant = Manager.Assistant({req: data.req, res: data.res})
|
|
9
|
+
this.req = data.req;
|
|
10
|
+
this.res = data.res;
|
|
11
|
+
|
|
12
|
+
return this;
|
|
13
|
+
},
|
|
14
|
+
main: async function() {
|
|
15
|
+
let self = this;
|
|
16
|
+
let libraries = self.libraries;
|
|
17
|
+
let assistant = self.assistant;
|
|
18
|
+
let req = self.req;
|
|
19
|
+
let res = self.res;
|
|
20
|
+
|
|
21
|
+
let response = {
|
|
22
|
+
status: 200,
|
|
23
|
+
data: {},
|
|
24
|
+
error: null,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
return libraries.cors(req, res, async () => {
|
|
28
|
+
// authenticate admin!
|
|
29
|
+
let user = await assistant.authenticate();
|
|
30
|
+
|
|
31
|
+
// Analytics
|
|
32
|
+
let analytics = self.Manager.Analytics({
|
|
33
|
+
assistant: assistant,
|
|
34
|
+
uuid: user.auth.uid,
|
|
35
|
+
})
|
|
36
|
+
.event({
|
|
37
|
+
category: 'admin',
|
|
38
|
+
action: 'query',
|
|
39
|
+
// label: '',
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
if (!user.roles.admin) {
|
|
43
|
+
response.status = 401;
|
|
44
|
+
response.error = new Error('Unauthenticated, admin required.');
|
|
45
|
+
assistant.error(response.error)
|
|
46
|
+
} else {
|
|
47
|
+
self.docs = [];
|
|
48
|
+
// assistant.log('Queries', assistant.request.data.queries);
|
|
49
|
+
let queries = powertools.arrayify(assistant.request.data.queries || []);
|
|
50
|
+
|
|
51
|
+
let promises = [];
|
|
52
|
+
for (var i = 0; i < queries.length; i++) {
|
|
53
|
+
queries[i]
|
|
54
|
+
promises.push(self.runQuery(queries[i]))
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
await Promise.all(promises)
|
|
58
|
+
.then((r) => {
|
|
59
|
+
response.data = self.docs;
|
|
60
|
+
// assistant.log('Query result:', );
|
|
61
|
+
})
|
|
62
|
+
.catch((e) => {
|
|
63
|
+
response.error = e;
|
|
64
|
+
response.status = 400;
|
|
65
|
+
assistant.error(response.error)
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
assistant.log('Query', assistant.request.data, response);
|
|
70
|
+
|
|
71
|
+
if (response.status === 200) {
|
|
72
|
+
return res.status(response.status).json(response.data);
|
|
73
|
+
} else {
|
|
74
|
+
return res.status(response.status).send(response.error.message);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
},
|
|
78
|
+
runQuery: runQuery,
|
|
79
|
+
}
|
|
80
|
+
module.exports = Module;
|
|
81
|
+
|
|
82
|
+
// HELPERS //
|
|
83
|
+
async function runQuery(payload) {
|
|
84
|
+
let self = this;
|
|
85
|
+
|
|
86
|
+
payload = payload || {};
|
|
87
|
+
payload.where = powertools.arrayify(payload.where || []);
|
|
88
|
+
payload.filter = powertools.arrayify(payload.filter || []);
|
|
89
|
+
payload.orderBy = powertools.arrayify(payload.orderBy || []);
|
|
90
|
+
|
|
91
|
+
// self.assistant.log('Query', payload);
|
|
92
|
+
|
|
93
|
+
return new Promise(function(resolve, reject) {
|
|
94
|
+
let collection;
|
|
95
|
+
|
|
96
|
+
if (!payload.collection) {
|
|
97
|
+
return resolve([]);
|
|
98
|
+
// return reject(new Error('No collection specified.'));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
collection = self.libraries.admin.firestore().collection(payload.collection);
|
|
102
|
+
|
|
103
|
+
for (var i = 0; i < payload.where.length; i++) {
|
|
104
|
+
let cur = payload.where[i];
|
|
105
|
+
collection = collection.where(cur.field, cur.operator, cur.value);
|
|
106
|
+
}
|
|
107
|
+
for (var i = 0; i < payload.orderBy.length; i++) {
|
|
108
|
+
let cur = payload.orderBy[i];
|
|
109
|
+
collection = collection.orderBy(cur.field, cur.order)
|
|
110
|
+
}
|
|
111
|
+
if (payload.limit) {
|
|
112
|
+
collection = collection.limit(payload.limit)
|
|
113
|
+
}
|
|
114
|
+
if (payload.startAt) {
|
|
115
|
+
collection = collection.startAt(payload.startAt)
|
|
116
|
+
}
|
|
117
|
+
if (payload.startAfter) {
|
|
118
|
+
collection = collection.startAfter(payload.startAfter)
|
|
119
|
+
}
|
|
120
|
+
if (payload.endAt) {
|
|
121
|
+
collection = collection.endAt(payload.endAt)
|
|
122
|
+
}
|
|
123
|
+
if (payload.endBefore) {
|
|
124
|
+
collection = collection.endBefore(payload.endBefore)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
collection
|
|
128
|
+
.get()
|
|
129
|
+
.then(function (querySnapshot) {
|
|
130
|
+
querySnapshot.forEach(function (doc) {
|
|
131
|
+
|
|
132
|
+
let exists = self.docs.find(item => {
|
|
133
|
+
return item.path === doc.ref.path
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
if (!exists && checkFilter(doc.data(), payload.filter)) {
|
|
137
|
+
self.docs.push({
|
|
138
|
+
path: doc.ref.path,
|
|
139
|
+
data: doc.data(),
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
if (payload.filterIndex) {
|
|
146
|
+
let iS = payload.filterIndex[0];
|
|
147
|
+
let iF = payload.filterIndex[1];
|
|
148
|
+
iF = iF > self.docs.length ? self.docs.length - 1 : iF;
|
|
149
|
+
self.docs = self.docs.slice(iS, iF);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return resolve(self.docs);
|
|
153
|
+
})
|
|
154
|
+
.catch(function (error) {
|
|
155
|
+
self.assistant.error(error)
|
|
156
|
+
return reject(error);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function checkFilter(data, filter) {
|
|
163
|
+
|
|
164
|
+
// Loop through all filters
|
|
165
|
+
for (var i = 0, l = filter.length; i < l; i++) {
|
|
166
|
+
// Set up field and checks
|
|
167
|
+
let field = `${filter[i].field}`.split(' || ');
|
|
168
|
+
field = powertools.arrayify(field);
|
|
169
|
+
let matches = filter[i].matches || '';
|
|
170
|
+
let regex = powertools.regexify(matches);
|
|
171
|
+
|
|
172
|
+
// Pass/fail
|
|
173
|
+
let innerPassed = false;
|
|
174
|
+
|
|
175
|
+
// Loop through each filter's fields
|
|
176
|
+
for (var i2 = 0, l2 = field.length; i2 < l2; i2++) {
|
|
177
|
+
let fieldInner = field[i2];
|
|
178
|
+
let value = _.get(data, fieldInner, undefined);
|
|
179
|
+
|
|
180
|
+
if (typeof value === 'undefined') {
|
|
181
|
+
innerPassed = false;
|
|
182
|
+
continue;
|
|
183
|
+
} else if (typeof value === 'string') {
|
|
184
|
+
if (value.match(regex)) {
|
|
185
|
+
innerPassed = true;
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// If there was not a successful innerPassed then break (innerPassed works on OR logic)
|
|
192
|
+
if (!innerPassed) {
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
}
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
let Module = {
|
|
2
|
+
init: async function (Manager, data) {
|
|
3
|
+
this.Manager = Manager;
|
|
4
|
+
this.libraries = Manager.libraries;
|
|
5
|
+
this.assistant = Manager.Assistant({req: data.req, res: data.res})
|
|
6
|
+
this.req = data.req;
|
|
7
|
+
this.res = data.res
|
|
8
|
+
|
|
9
|
+
return this;
|
|
10
|
+
},
|
|
11
|
+
main: async function() {
|
|
12
|
+
let self = this;
|
|
13
|
+
let libraries = self.libraries;
|
|
14
|
+
let assistant = self.assistant;
|
|
15
|
+
let req = self.req;
|
|
16
|
+
let res = self.res;
|
|
17
|
+
|
|
18
|
+
let response = {
|
|
19
|
+
status: 200,
|
|
20
|
+
data: {},
|
|
21
|
+
error: null,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
return libraries.cors(req, res, async () => {
|
|
25
|
+
// authenticate admin!
|
|
26
|
+
let user = await assistant.authenticate();
|
|
27
|
+
|
|
28
|
+
// Analytics
|
|
29
|
+
let analytics = self.Manager.Analytics({
|
|
30
|
+
assistant: assistant,
|
|
31
|
+
uuid: user.auth.uid,
|
|
32
|
+
})
|
|
33
|
+
.event({
|
|
34
|
+
category: 'admin',
|
|
35
|
+
action: 'send-notification',
|
|
36
|
+
// label: '',
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
let payload = self.assistant.request.data.payload || {};
|
|
40
|
+
|
|
41
|
+
if (!payload.title || !payload.body) {
|
|
42
|
+
response.status = 400;
|
|
43
|
+
response.error = new Error('Not enough notification parameters supplied.');
|
|
44
|
+
assistant.error(response.error)
|
|
45
|
+
return res.status(response.status).send(response.error.message);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!user.roles.admin) {
|
|
49
|
+
response.status = 401;
|
|
50
|
+
response.error = new Error('Unauthenticated, admin required.');
|
|
51
|
+
assistant.error(response.error)
|
|
52
|
+
return res.status(response.status).send(response.error.message);
|
|
53
|
+
} else {
|
|
54
|
+
await self.getTokens({tags: false});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
assistant.log('Notification', assistant.request.data, response);
|
|
58
|
+
|
|
59
|
+
if (response.status === 200) {
|
|
60
|
+
return res.status(response.status).json(response.data);
|
|
61
|
+
} else {
|
|
62
|
+
return res.status(response.status).send(response.error.message);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
});
|
|
66
|
+
},
|
|
67
|
+
getTokens: getTokens,
|
|
68
|
+
sendBatch: sendBatch,
|
|
69
|
+
cleanTokens: cleanTokens,
|
|
70
|
+
deleteToken: deleteToken,
|
|
71
|
+
}
|
|
72
|
+
module.exports = Module;
|
|
73
|
+
|
|
74
|
+
// HELPERS //
|
|
75
|
+
let path_processing = 'notifications/processing/all/{notificationId}';
|
|
76
|
+
let path_subscriptions = 'notifications/subscriptions/all';
|
|
77
|
+
let badTokenReasons = ['messaging/invalid-registration-token', 'messaging/registration-token-not-registered']
|
|
78
|
+
let batchPromises = [];
|
|
79
|
+
|
|
80
|
+
function sendBatch(batch, id) {
|
|
81
|
+
let self = this;
|
|
82
|
+
// self.assistant.log(`Sending batch ID: ${id}`, batch);
|
|
83
|
+
self.assistant.log(`Sending batch ID: ${id}`);
|
|
84
|
+
|
|
85
|
+
// self.assistant.log('payload', payload);
|
|
86
|
+
return new Promise(async function(resolve, reject) {
|
|
87
|
+
let payload = {};
|
|
88
|
+
payload.notification = {};
|
|
89
|
+
payload.notification.title = self.assistant.request.data.payload.title;
|
|
90
|
+
payload.notification.click_action = self.assistant.request.data.payload.click_action;
|
|
91
|
+
payload.notification.body = self.assistant.request.data.payload.body;
|
|
92
|
+
payload.notification.icon = self.assistant.request.data.payload.icon;
|
|
93
|
+
|
|
94
|
+
await self.libraries.admin.messaging().sendToDevice(batch, payload)
|
|
95
|
+
.then(async function (response) {
|
|
96
|
+
// self.result.batches.list.push('#' + id + ' | ' + '✅ ' + response.successCount + ' | ' + '❌ ' + response.failureCount);
|
|
97
|
+
self.assistant.log('Sent batch #' + id);
|
|
98
|
+
// self.result.successes += response.successCount;
|
|
99
|
+
// self.result.failures += response.failureCount;
|
|
100
|
+
// console.log('RESP', response);
|
|
101
|
+
if (response.failureCount > 0) {
|
|
102
|
+
await self.cleanTokens(batch, response.results, id);
|
|
103
|
+
}
|
|
104
|
+
resolve();
|
|
105
|
+
})
|
|
106
|
+
.catch(function (e) {
|
|
107
|
+
self.assistant.error('Error sending batch #' + id, e);
|
|
108
|
+
// self.result.status = 'fail';
|
|
109
|
+
reject(e);
|
|
110
|
+
})
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function getTokens(options) {
|
|
115
|
+
let self = this;
|
|
116
|
+
options = options || {};
|
|
117
|
+
options.tags = options.tags || false;
|
|
118
|
+
return new Promise(async function(resolve, reject) {
|
|
119
|
+
let subs = self.libraries.admin.firestore().collection(path_subscriptions);
|
|
120
|
+
if (options.tags) {
|
|
121
|
+
subs.where('tags', 'array-contains-any', options.tags)
|
|
122
|
+
}
|
|
123
|
+
await subs
|
|
124
|
+
.get()
|
|
125
|
+
.then(function(querySnapshot) {
|
|
126
|
+
self.assistant.log(`Queried ${querySnapshot.size} tokens.`);
|
|
127
|
+
// self.result.subscriptionsStart = querySnapshot.size;
|
|
128
|
+
let batchCurrentSize = 0;
|
|
129
|
+
let batchSizeMax = 1000;
|
|
130
|
+
|
|
131
|
+
let batchCurrent = [];
|
|
132
|
+
let batchLoops = 1;
|
|
133
|
+
batchPromises = [];
|
|
134
|
+
|
|
135
|
+
querySnapshot.forEach(function(doc) {
|
|
136
|
+
// log(self, 'loading... ', batchLoops+'/'+querySnapshot.size);
|
|
137
|
+
if ((batchCurrentSize < batchSizeMax - 1) && (batchLoops < querySnapshot.size)) {
|
|
138
|
+
batchCurrent.push(doc.data().token);
|
|
139
|
+
batchCurrentSize++;
|
|
140
|
+
} else {
|
|
141
|
+
let batchId = batchPromises.length + 1;
|
|
142
|
+
batchCurrent.push(doc.data().token);
|
|
143
|
+
batchCurrentSize++;
|
|
144
|
+
console.log(`Got batch ID: ${batchId} with ${batchCurrentSize} tokens.`);
|
|
145
|
+
batchPromises.push(self.sendBatch(batchCurrent, batchId));
|
|
146
|
+
batchCurrent = [];
|
|
147
|
+
batchCurrentSize = 0;
|
|
148
|
+
}
|
|
149
|
+
batchLoops++;
|
|
150
|
+
});
|
|
151
|
+
})
|
|
152
|
+
.catch(function(e) {
|
|
153
|
+
self.assistant.error('Error querying tokens: ', e)
|
|
154
|
+
reject(error);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
await Promise.all(batchPromises)
|
|
158
|
+
.then(function(values) {
|
|
159
|
+
self.assistant.log('Finished all batches.');
|
|
160
|
+
})
|
|
161
|
+
.catch(function(e) {
|
|
162
|
+
self.assistant.error('Error sending batches: ', e)
|
|
163
|
+
});
|
|
164
|
+
resolve();
|
|
165
|
+
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function cleanTokens(batch, results, id) {
|
|
170
|
+
let self = this;
|
|
171
|
+
let cleanPromises = [];
|
|
172
|
+
// self.assistant.log(`Cleaning tokens of batch ID: ${id}`, results);
|
|
173
|
+
self.assistant.log(`Cleaning tokens of batch ID: ${id}`);
|
|
174
|
+
return new Promise(async function(resolve, reject) {
|
|
175
|
+
results.forEach(function (item, index) {
|
|
176
|
+
if (!item.error) { return false; }
|
|
177
|
+
let curCode = item.error.code;
|
|
178
|
+
let token = batch[index];
|
|
179
|
+
self.assistant.log(`Found bad token: ${index} = ${curCode}`);
|
|
180
|
+
if (badTokenReasons.includes(curCode)) {
|
|
181
|
+
cleanPromises.push(self.deleteToken(token, curCode));
|
|
182
|
+
}
|
|
183
|
+
})
|
|
184
|
+
await Promise.all(cleanPromises)
|
|
185
|
+
.catch(function(e) {
|
|
186
|
+
self.assistant.log('error', "Error cleaning failed tokens: ", e);
|
|
187
|
+
});
|
|
188
|
+
resolve();
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function deleteToken(token, errorCode) {
|
|
193
|
+
let self = this;
|
|
194
|
+
return new Promise(function(resolve, reject) {
|
|
195
|
+
self.libraries.admin.firestore().doc(`${path_subscriptions}/${token}`)
|
|
196
|
+
.delete()
|
|
197
|
+
.then(function() {
|
|
198
|
+
self.assistant.log(`Deleting bad token: ${token} for reason ${errorCode}`);
|
|
199
|
+
resolve();
|
|
200
|
+
})
|
|
201
|
+
.catch(function(error) {
|
|
202
|
+
self.assistant.log('error', `Error deleting bad token: ${token} for reason ${errorCode} because of error ${error}`);
|
|
203
|
+
resolve();
|
|
204
|
+
})
|
|
205
|
+
});
|
|
206
|
+
}
|