@edgedev/firebase 2.0.6 → 2.0.8
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/edgeFirebase.ts +14 -2
- package/package.json +1 -1
- package/src/config.js +9 -1
- package/src/edgeFirebase.js +49 -7
- package/src/firestore.rules +10 -2
package/edgeFirebase.ts
CHANGED
|
@@ -400,9 +400,14 @@ export const EdgeFirebase = class {
|
|
|
400
400
|
this.unsubscibe['collection-data'] = unsubscribe
|
|
401
401
|
}
|
|
402
402
|
|
|
403
|
-
|
|
403
|
+
private ruleHelperReset = async (): Promise<void> => {
|
|
404
|
+
const initRoleHelper = { uid: this.user.uid, 'edge-assignment-helper': {permissionType: "roles"} }
|
|
405
|
+
this.state.ruleHelpers['edge-assignment-helper'] = {permissionType: "roles"}
|
|
406
|
+
await setDoc(doc(this.db, "rule-helpers", this.user.uid), initRoleHelper);
|
|
407
|
+
}
|
|
404
408
|
|
|
405
409
|
private startUserMetaSync = async (docSnap): Promise<void> => {
|
|
410
|
+
await this.ruleHelperReset();
|
|
406
411
|
await this.startCollectionPermissionsSync()
|
|
407
412
|
await this.initUserMetaPermissions(docSnap);
|
|
408
413
|
this.user.loggedIn = true;
|
|
@@ -977,7 +982,12 @@ export const EdgeFirebase = class {
|
|
|
977
982
|
}
|
|
978
983
|
let index = collection.length;
|
|
979
984
|
const ruleCheck: RuleCheck = { permissionType: "", permissionCheckPath: "", fullPath: collectionPath.replaceAll("/", "-"), action };
|
|
980
|
-
|
|
985
|
+
if (this.state.ruleHelpers[ruleKey]) {
|
|
986
|
+
const existingRuleHelper = this.state.ruleHelpers[ruleKey];
|
|
987
|
+
if (existingRuleHelper.fullPath === ruleCheck.fullPath && existingRuleHelper.action === ruleCheck.action) {
|
|
988
|
+
return;
|
|
989
|
+
}
|
|
990
|
+
}
|
|
981
991
|
while (index > 0) {
|
|
982
992
|
const collectionArray = JSON.parse(JSON.stringify(collection));
|
|
983
993
|
const permissionCheck = collectionArray.splice(0, index).join("-");
|
|
@@ -1017,6 +1027,7 @@ export const EdgeFirebase = class {
|
|
|
1017
1027
|
}
|
|
1018
1028
|
const check = {[ruleKey]: ruleCheck, uid: this.user.uid };
|
|
1019
1029
|
await setDoc(doc(this.db, "rule-helpers", this.user.uid), check, { merge: true });
|
|
1030
|
+
this.state.ruleHelpers[ruleKey] = ruleCheck;
|
|
1020
1031
|
}
|
|
1021
1032
|
|
|
1022
1033
|
public permissionCheckOnly = (action: action, collectionPath: string): boolean => {
|
|
@@ -1240,6 +1251,7 @@ export const EdgeFirebase = class {
|
|
|
1240
1251
|
users: {},
|
|
1241
1252
|
registrationCode: "",
|
|
1242
1253
|
registrationMeta: {},
|
|
1254
|
+
ruleHelpers: {},
|
|
1243
1255
|
});
|
|
1244
1256
|
|
|
1245
1257
|
public getDocData = async (
|
package/package.json
CHANGED
package/src/config.js
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
const functions = require('firebase-functions')
|
|
2
|
+
const { PubSub } = require('@google-cloud/pubsub')
|
|
2
3
|
const admin = require('firebase-admin')
|
|
3
4
|
|
|
5
|
+
const pubsub = new PubSub()
|
|
6
|
+
|
|
4
7
|
admin.initializeApp()
|
|
5
8
|
|
|
6
|
-
const {
|
|
9
|
+
const { onMessagePublished } = require('firebase-functions/v2/pubsub')
|
|
10
|
+
|
|
11
|
+
const { onCall, HttpsError, onRequest } = require('firebase-functions/v2/https')
|
|
7
12
|
const { onSchedule } = require('firebase-functions/v2/scheduler')
|
|
8
13
|
const {
|
|
9
14
|
onDocumentWritten,
|
|
@@ -19,6 +24,9 @@ const twilio = require('twilio')
|
|
|
19
24
|
const db = getFirestore()
|
|
20
25
|
|
|
21
26
|
module.exports = {
|
|
27
|
+
pubsub,
|
|
28
|
+
onMessagePublished,
|
|
29
|
+
onRequest,
|
|
22
30
|
onSchedule,
|
|
23
31
|
onDocumentWritten,
|
|
24
32
|
onDocumentCreated,
|
package/src/edgeFirebase.js
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
// This could function will right to "rules-helpers" collction a document with the user's uid and the collection path and the permission
|
|
3
|
-
// This will make the rules far more efficient and will allow for more complex rules // this only needs done really for snapshots...
|
|
4
|
-
// All the other document reads and writes should be made using a cloud functions exclusively and the cloud function will check the permissions
|
|
5
|
-
const { onCall, HttpsError, logger, getFirestore, functions, admin, twilio, db } = require('./config.js')
|
|
1
|
+
const { onCall, HttpsError, logger, getFirestore, functions, admin, twilio, db, onSchedule, pubsub } = require('./config.js')
|
|
6
2
|
|
|
7
3
|
const authToken = process.env.TWILIO_AUTH_TOKEN
|
|
8
4
|
const accountSid = process.env.TWILIO_SID
|
|
@@ -15,6 +11,52 @@ function formatPhoneNumber(phone) {
|
|
|
15
11
|
return `+1${numericPhone}`
|
|
16
12
|
}
|
|
17
13
|
|
|
14
|
+
exports.topicQueue = onSchedule({ schedule: 'every 1 minutes', timeoutSeconds: 180 }, async (event) => {
|
|
15
|
+
const queuedTopicsRef = db.collection('topic-queue')
|
|
16
|
+
const snapshot = await queuedTopicsRef.get()
|
|
17
|
+
|
|
18
|
+
for (const doc of snapshot.docs) {
|
|
19
|
+
await db.runTransaction(async (transaction) => {
|
|
20
|
+
const docSnapshot = await transaction.get(doc.ref)
|
|
21
|
+
if (!docSnapshot.exists) {
|
|
22
|
+
throw new Error('Document does not exist!')
|
|
23
|
+
}
|
|
24
|
+
const emailData = docSnapshot.data()
|
|
25
|
+
const emailTimestamp = emailData.timestamp ? emailData.timestamp.toMillis() : 0
|
|
26
|
+
const delayTimestamp = emailData.minuteDelay ? emailTimestamp + emailData.minuteDelay * 60 * 1000 : 0
|
|
27
|
+
const currentTimestamp = Date.now()
|
|
28
|
+
// Check if current time is beyond the timestamp + minuteDelay, or if timestamp or minuteDelay is not set
|
|
29
|
+
if (emailTimestamp > currentTimestamp || currentTimestamp >= delayTimestamp || !emailData.timestamp || !emailData.minuteDelay) {
|
|
30
|
+
// Check if topic and payload exist and are not empty
|
|
31
|
+
if (emailData.topic && emailData.payload && emailData.topic.trim() !== '' && emailData.payload.trim() !== '') {
|
|
32
|
+
try {
|
|
33
|
+
await pubsub.topic(emailData.topic).publishMessage({ data: Buffer.from(JSON.stringify(emailData.payload)) })
|
|
34
|
+
// Delete the document after successfully publishing the message
|
|
35
|
+
transaction.delete(doc.ref)
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
console.error(`Error publishing message to topic ${emailData.topic}:`, error)
|
|
39
|
+
// Increment retry count and set new delay
|
|
40
|
+
const retryCount = emailData.retry ? emailData.retry + 1 : 1
|
|
41
|
+
if (retryCount <= 3) {
|
|
42
|
+
const minuteDelay = retryCount === 1 ? 1 : retryCount === 2 ? 10 : 30
|
|
43
|
+
transaction.update(doc.ref, { retry: retryCount, minuteDelay })
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
// Delete the document if there was an error publishing the topic after 3 retries
|
|
47
|
+
transaction.delete(doc.ref)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// Delete the document if topic or payload does not exist or is empty
|
|
52
|
+
else {
|
|
53
|
+
transaction.delete(doc.ref)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
|
|
18
60
|
exports.sendVerificationCode = functions.https.onCall(async (data, context) => {
|
|
19
61
|
const code = (Math.floor(Math.random() * 1000000) + 1000000).toString().substring(1)
|
|
20
62
|
const phone = formatPhoneNumber(data.phone)
|
|
@@ -282,9 +324,9 @@ function setUser(userRef, newData, oldData, stagedDocId) {
|
|
|
282
324
|
let userUpdate = { meta: newData.meta, stagedDocId }
|
|
283
325
|
|
|
284
326
|
if (newData.meta && newData.meta.name) {
|
|
285
|
-
const publicUserRef = db.collection('public-users').doc(newData.
|
|
327
|
+
const publicUserRef = db.collection('public-users').doc(newData.userId)
|
|
286
328
|
const publicMeta = { name: newData.meta.name }
|
|
287
|
-
publicUserRef.set({ uid: newData.uid, meta: publicMeta, collectionPaths: newData.collectionPaths, userId: newData.
|
|
329
|
+
publicUserRef.set({ uid: newData.uid, meta: publicMeta, collectionPaths: newData.collectionPaths, userId: newData.userId })
|
|
288
330
|
}
|
|
289
331
|
|
|
290
332
|
if (Object.prototype.hasOwnProperty.call(newData, 'roles')) {
|
package/src/firestore.rules
CHANGED
|
@@ -9,6 +9,14 @@ service cloud.firestore {
|
|
|
9
9
|
allow delete: if false;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
match /databases/{database}/documents/topic-queue/{topic} {
|
|
13
|
+
allow read: if false;
|
|
14
|
+
allow create: if false;
|
|
15
|
+
allow update: if false;
|
|
16
|
+
allow delete: if false;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
12
20
|
match /databases/{database}/documents/public-users/{user} {
|
|
13
21
|
allow read: if request.auth != null;
|
|
14
22
|
allow list: if request.auth != null;
|
|
@@ -16,7 +24,7 @@ service cloud.firestore {
|
|
|
16
24
|
allow update: if false;
|
|
17
25
|
allow delete: if false;
|
|
18
26
|
}
|
|
19
|
-
|
|
27
|
+
|
|
20
28
|
match /databases/{database}/documents/events/{event} {
|
|
21
29
|
allow read: if false;
|
|
22
30
|
allow create: if false;
|
|
@@ -237,7 +245,7 @@ service cloud.firestore {
|
|
|
237
245
|
}
|
|
238
246
|
function checkPermission(collectionPath, permissionCheck) {
|
|
239
247
|
let user = get(/databases/$(database)/documents/users/$(request.auth.uid)).data;
|
|
240
|
-
let skipPaths = ["collection-data", "users", "staged-users", "events", "rule-helpers", "phone-auth", "public-users"];
|
|
248
|
+
let skipPaths = ["collection-data", "users", "staged-users", "events", "rule-helpers", "phone-auth", "public-users", "topic-queue"];
|
|
241
249
|
let ruleHelper = get(/databases/$(database)/documents/rule-helpers/$(request.auth.uid)).data;
|
|
242
250
|
return !(collectionPath in skipPaths) &&
|
|
243
251
|
!(permissionCheck == "write" &&
|