@edgedev/firebase 2.0.22 → 2.0.24

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/edgeFirebase.js +127 -45
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@edgedev/firebase",
3
- "version": "2.0.22",
3
+ "version": "2.0.24",
4
4
  "description": "Vue 3 / Nuxt 3 Plugin or Nuxt 3 plugin for firebase authentication and firestore.",
5
5
  "main": "index.ts",
6
6
  "scripts": {
@@ -1,4 +1,4 @@
1
- const { onCall, HttpsError, logger, getFirestore, functions, admin, twilio, db, onSchedule, pubsub } = require('./config.js')
1
+ const { onCall, HttpsError, logger, getFirestore, functions, admin, twilio, db, onSchedule, onDocumentUpdated, pubsub } = require('./config.js')
2
2
 
3
3
  const authToken = process.env.TWILIO_AUTH_TOKEN
4
4
  const accountSid = process.env.TWILIO_SID
@@ -57,7 +57,8 @@ exports.topicQueue = onSchedule({ schedule: 'every 1 minutes', timeoutSeconds: 1
57
57
  }
58
58
  })
59
59
 
60
- exports.sendVerificationCode = functions.https.onCall(async (data, context) => {
60
+ exports.sendVerificationCode = onCall(async (request) => {
61
+ const data = request.data
61
62
  const code = (Math.floor(Math.random() * 1000000) + 1000000).toString().substring(1)
62
63
  const phone = formatPhoneNumber(data.phone)
63
64
 
@@ -87,7 +88,8 @@ exports.sendVerificationCode = functions.https.onCall(async (data, context) => {
87
88
  }
88
89
  })
89
90
 
90
- exports.verifyPhoneNumber = functions.https.onCall(async (data, context) => {
91
+ exports.verifyPhoneNumber = onCall(async (request) => {
92
+ const data = request.data
91
93
  const phone = data.phone
92
94
  const code = data.code
93
95
 
@@ -120,7 +122,7 @@ exports.verifyPhoneNumber = functions.https.onCall(async (data, context) => {
120
122
  }
121
123
  })
122
124
 
123
- exports.initFirestore = functions.https.onCall(async (data, context) => {
125
+ exports.initFirestore = onCall(async (request) => {
124
126
  // checks to see of the collections 'collection-data' and 'staged-users' exist if not will seed them with data
125
127
  const collectionData = await db.collection('collection-data').get()
126
128
  const stagedUsers = await db.collection('staged-users').get()
@@ -154,8 +156,10 @@ exports.initFirestore = functions.https.onCall(async (data, context) => {
154
156
  }
155
157
  })
156
158
 
157
- exports.removeNonRegisteredUser = functions.https.onCall(async (data, context) => {
158
- if (data.uid === context.auth.uid) {
159
+ exports.removeNonRegisteredUser = onCall(async (request) => {
160
+ const data = request.data
161
+ const auth = request.auth
162
+ if (data.uid === auth.uid) {
159
163
  const stagedUser = await db.collection('staged-users').doc(data.docId).get()
160
164
  if (stagedUser.exists) {
161
165
  const stagedUserData = stagedUser.data()
@@ -189,8 +193,10 @@ exports.removeNonRegisteredUser = functions.https.onCall(async (data, context) =
189
193
  return { success: false, message: 'Non-registered user not found.' }
190
194
  })
191
195
 
192
- exports.currentUserRegister = functions.https.onCall(async (data, context) => {
193
- if (data.uid === context.auth.uid) {
196
+ exports.currentUserRegister = onCall(async (request) => {
197
+ const data = request.data
198
+ const auth = request.auth
199
+ if (data.uid === auth.uid) {
194
200
  const stagedUser = await db.collection('staged-users').doc(data.registrationCode).get()
195
201
  if (!stagedUser.exists) {
196
202
  return { success: false, message: 'Registration code not found.' }
@@ -246,10 +252,76 @@ exports.checkOrgIdExists = onCall(async (request) => {
246
252
  return { exists: orgDoc.exists }
247
253
  })
248
254
 
249
- exports.updateUser = functions.firestore.document('staged-users/{docId}').onUpdate(async (change, context) => {
250
- const eventId = context.eventId
255
+ const permissionCheck = async (userId, action, collectionPath) => {
256
+ // Fetch user document
257
+ const userDoc = await db.collection('users').doc(userId).get()
258
+ const userData = userDoc.data()
259
+
260
+ // Fetch roles from user data
261
+ const roles = userData.roles || []
262
+
263
+ // Check each role for permission
264
+ for (let role of roles) {
265
+ if (role.collectionPath === collectionPath) {
266
+ // Fetch collection data
267
+ const collectionDoc = await db.collection('collection-data').doc(collectionPath).get()
268
+ const collectionData = collectionDoc.exists ? collectionDoc.data() : await db.collection('collection-data').doc('-default-').get().then(doc => doc.data())
269
+
270
+ // Check if action is permitted
271
+ if (collectionData && collectionData[role.role] && collectionData[role.role][action]) {
272
+ return true
273
+ }
274
+ }
275
+ }
276
+
277
+ // If no permission found, return false
278
+ return false
279
+ }
280
+
281
+ exports.deleteSelf = onCall(async (request) => {
282
+ if (request.data.uid === request.auth.uid) {
283
+ try {
284
+ const userDoc = await db.collection('staged-users').doc(request.auth.uid).get()
285
+ const userData = userDoc.data()
286
+ const userCollectionPaths = userData.collectionPaths || []
287
+
288
+ for (let path of userCollectionPaths) {
289
+ const usersWithSamePath = await db.collection('staged-users').where('collectionPaths', 'array-contains', path).get()
290
+
291
+ // If no other users have the same collection path, delete the path and all documents and collections under it
292
+ if (usersWithSamePath.size <= 1) {
293
+ const docsToDelete = await db.collection(path).get()
294
+ const batch = db.batch()
295
+ docsToDelete.docs.forEach((doc) => {
296
+ batch.delete(doc.ref)
297
+ })
298
+ await batch.commit()
299
+ }
300
+ }
301
+
302
+ // Delete from 'staged-users' collection
303
+ await db.collection('staged-users').doc(request.data.uid).delete()
304
+
305
+ // Delete from 'users' collection
306
+ await db.collection('users').doc(request.data.uid).delete()
307
+
308
+ // Delete the user from Firebase
309
+ await admin.auth().deleteUser(request.data.uid)
310
+
311
+ return { success: true }
312
+ }
313
+ catch (error) {
314
+ console.error('Error deleting user:', error)
315
+ return { success: false, error }
316
+ }
317
+ }
318
+ })
319
+
320
+ exports.updateUser = onDocumentUpdated({ document: 'staged-users/{docId}', timeoutSeconds: 180 }, async (event) => {
321
+ const change = event.data
322
+ const eventId = event.id
251
323
  const eventRef = db.collection('events').doc(eventId)
252
- const stagedDocId = context.params.docId
324
+ const stagedDocId = event.params.docId
253
325
  let newData = change.after.data()
254
326
  const oldData = change.before.data()
255
327
 
@@ -286,7 +358,26 @@ exports.updateUser = functions.firestore.document('staged-users/{docId}').onUpda
286
358
  if (Object.prototype.hasOwnProperty.call(newData, 'subCreate') && Object.values(newData.subCreate).length > 0) {
287
359
  const subCreate = newData.subCreate
288
360
  delete newData.subCreate
289
- const addedDoc = await db.collection(subCreate.rootPath).add({ [subCreate.dynamicDocumentField]: newData.dynamicDocumentFieldValue })
361
+ let newDocId = ''
362
+ if (Object.prototype.hasOwnProperty.call(newData, 'requestedOrgId')) {
363
+ newDocId = newData.requestedOrgId.toLowerCase()
364
+ delete newData.requestedOrgId
365
+ }
366
+ let addedDoc
367
+ if (newDocId) {
368
+ const docRef = db.collection(subCreate.rootPath).doc(newDocId)
369
+ const doc = await docRef.get()
370
+ if (!doc.exists) {
371
+ await docRef.set({ [subCreate.dynamicDocumentField]: newData.dynamicDocumentFieldValue })
372
+ addedDoc = docRef
373
+ }
374
+ else {
375
+ addedDoc = await db.collection(subCreate.rootPath).add({ [subCreate.dynamicDocumentField]: newData.dynamicDocumentFieldValue })
376
+ }
377
+ }
378
+ else {
379
+ addedDoc = await db.collection(subCreate.rootPath).add({ [subCreate.dynamicDocumentField]: newData.dynamicDocumentFieldValue })
380
+ }
290
381
  await db.collection(subCreate.rootPath).doc(addedDoc.id).update({ docId: addedDoc.id })
291
382
  delete newData.dynamicDocumentFieldValue
292
383
  const newRole = { [`${subCreate.rootPath}-${addedDoc.id}`]: { collectionPath: `${subCreate.rootPath}-${addedDoc.id}`, role: subCreate.role } }
@@ -316,42 +407,33 @@ exports.updateUser = functions.firestore.document('staged-users/{docId}').onUpda
316
407
  await markProcessed(eventRef)
317
408
  })
318
409
 
319
- function setUser(userRef, newData, oldData, stagedDocId) {
320
- // IT's OK If "users" doesn't match exactly matched "staged-users" because this is only preventing
321
- // writing from outside the @edgdev/firebase functions, so discrepancies will be rare since
322
- // the package will prevent before it gets this far.
323
- return userRef.get().then((user) => {
324
- let userUpdate = { meta: newData.meta, stagedDocId }
325
-
326
- if (newData.meta && newData.meta.name) {
327
- const publicUserRef = db.collection('public-users').doc(newData.userId)
328
- const publicMeta = { name: newData.meta.name }
329
- publicUserRef.set({ uid: newData.uid, meta: publicMeta, collectionPaths: newData.collectionPaths, userId: newData.userId })
330
- }
410
+ async function setUser(userRef, newData, oldData, stagedDocId) {
331
411
 
332
- if (Object.prototype.hasOwnProperty.call(newData, 'roles')) {
333
- userUpdate = { ...userUpdate, roles: newData.roles }
334
- }
335
- if (Object.prototype.hasOwnProperty.call(newData, 'specialPermissions')) {
336
- userUpdate = { ...userUpdate, specialPermissions: newData.specialPermissions }
337
- }
412
+ const user = await userRef.get()
413
+ let userUpdate = { meta: newData.meta, stagedDocId }
338
414
 
339
- if (!oldData.userId) {
340
- userUpdate = { ...userUpdate, userId: newData.uid }
341
- }
342
- if (!user.exists) {
343
- return userRef.set(userUpdate)
344
- }
345
- else {
346
- return userRef.update(userUpdate)
347
- }
348
- })
349
- }
415
+ if (newData.meta && newData.meta.name) {
416
+ const publicUserRef = db.collection('public-users').doc(stagedDocId)
417
+ const publicMeta = { name: newData.meta.name }
418
+ publicUserRef.set({ uid: newData.uid, meta: publicMeta, collectionPaths: newData.collectionPaths, userId: stagedDocId })
419
+ }
350
420
 
351
- function shouldProcess(eventRef) {
352
- return eventRef.get().then((eventDoc) => {
353
- return !eventDoc.exists || !eventDoc.data().processed
354
- })
421
+ if (Object.prototype.hasOwnProperty.call(newData, 'roles')) {
422
+ userUpdate = { ...userUpdate, roles: newData.roles }
423
+ }
424
+ if (Object.prototype.hasOwnProperty.call(newData, 'specialPermissions')) {
425
+ userUpdate = { ...userUpdate, specialPermissions: newData.specialPermissions }
426
+ }
427
+
428
+ if (!oldData.userId) {
429
+ userUpdate = { ...userUpdate, userId: newData.uid }
430
+ }
431
+ if (!user.exists) {
432
+ return userRef.set(userUpdate)
433
+ }
434
+ else {
435
+ return userRef.update(userUpdate)
436
+ }
355
437
  }
356
438
 
357
439
  function markProcessed(eventRef) {