@edgedev/firebase 1.8.12 → 1.9.2

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/README.md CHANGED
@@ -239,6 +239,63 @@ Calling this will generate a Microsoft Sign In Popup and register the user using
239
239
  );
240
240
  ```
241
241
 
242
+ ### Inviting an Existing User to Register with a New Organization or Member
243
+
244
+ To invite an existing user to register with a new organization or member's data and get the corresponding roles, use the `edgeFirebase.currentUserRegister(userRegister)` method.
245
+
246
+ ```javascript
247
+ const userRegisterData = {
248
+ registrationCode: "12345",
249
+ dynamicDocumentFieldValue: "fieldName",
250
+ };
251
+
252
+ const response = await edgeFirebase.currentUserRegister(userRegisterData);
253
+ ```
254
+
255
+ #### Parameters
256
+
257
+ - `userRegister` (object): An object containing the user registration data. It must include a `registrationCode` property provided by the inviting organization or member. It can also include a `dynamicDocumentFieldValue` property, which is a single string representing the name of an additional data field for registration.
258
+
259
+ ```typescript
260
+ interface userRegister {
261
+ registrationCode: string;
262
+ dynamicDocumentFieldValue?: string;
263
+ }
264
+ ```
265
+
266
+ #### Returns
267
+
268
+ The method returns a Promise that resolves to an `actionResponse` object:
269
+
270
+ ```typescript
271
+ interface actionResponse {
272
+ success: boolean;
273
+ message: string;
274
+ meta: {};
275
+ }
276
+ ```
277
+
278
+ Example usage:
279
+
280
+ ```javascript
281
+ <script setup>
282
+ async function inviteExistingUser() {
283
+ const userRegisterData = {
284
+ registrationCode: "12345",
285
+ dynamicDocumentFieldValue: "fieldName",
286
+ };
287
+
288
+ const response = await edgeFirebase.currentUserRegister(userRegisterData);
289
+ if (response.success) {
290
+ console.log("Existing user invited and registered successfully");
291
+ } else {
292
+ console.error("Error inviting and registering existing user:", response.message);
293
+ }
294
+ }
295
+ </script>
296
+ ```
297
+
298
+
242
299
  ### Explanation of permissions
243
300
 
244
301
  - **assign: boolean** - When a user has this permission for a collection they can assign other users to the collection and change permissions for that collection. For a user to be able run setUser, storeCollectionPermisions, storeUserRoles, removeUserRoles, storeUserSpecialPermissions, or removeUserSpecialPermissions, they must have assign access to any of the collection paths passed into those functions.
@@ -475,6 +532,7 @@ interface UserDataObject {
475
532
  firebaseUser: object; // contains the entire auth from firebase
476
533
  oAuthCredential: object; // contains oAuth ID and token information
477
534
  loggedIn: boolean;
535
+ loggingIn: boolean: // true while logging in used for loading screens
478
536
  logInError: boolean;
479
537
  logInErrorMessage: string;
480
538
  meta: object;
@@ -502,6 +560,8 @@ interface permissions {
502
560
  ```
503
561
  The reactive item **edgeFirebase.user.loggedIn** can be used in code or templates to determine if the user is logged in.
504
562
 
563
+ **edgeFirebase.user.logginIn** is true while the user is logging in. This can be used to show a loading screen.
564
+
505
565
  If there is an error logging in, **edgeFirebase.user.logInError** will be true and **edgeFirebase.user.logInErrorMessage** can be used to return that error to the user.
506
566
 
507
567
  After logging in, **edgeFirebase.logOut** becomes available. Logging out will also automatically disconnect all FireStore listeners.
package/edgeFirebase.ts CHANGED
@@ -103,6 +103,7 @@ interface UserDataObject {
103
103
  firebaseUser: object;
104
104
  oAuthCredential: { accessToken: string; idToken: string;}
105
105
  loggedIn: boolean;
106
+ loggingIn: boolean;
106
107
  logInError: boolean;
107
108
  logInErrorMessage: string;
108
109
  meta: object;
@@ -126,6 +127,7 @@ interface newUser {
126
127
  }
127
128
 
128
129
  interface userRegister {
130
+ uid?: string;
129
131
  email?: string;
130
132
  password?: string;
131
133
  meta: object;
@@ -233,7 +235,7 @@ export const EdgeFirebase = class {
233
235
 
234
236
  private functions = null;
235
237
 
236
- public runFunction = async (functionName: string, data: { [key: string]: unknown }) => {
238
+ public runFunction = async (functionName: string, data: Record<string, unknown>) => {
237
239
  data.uid = this.user.uid;
238
240
  const callable = httpsCallable(this.functions, functionName);
239
241
  return await callable(data);
@@ -387,6 +389,7 @@ export const EdgeFirebase = class {
387
389
  await this.startCollectionPermissionsSync()
388
390
  await this.initUserMetaPermissions(docSnap);
389
391
  this.user.loggedIn = true;
392
+ this.user.loggingIn = false;
390
393
  };
391
394
 
392
395
  private waitForUser = async(): Promise<void> => {
@@ -405,6 +408,7 @@ export const EdgeFirebase = class {
405
408
  private setOnAuthStateChanged = (): void => {
406
409
  onAuthStateChanged(this.auth, (userAuth) => {
407
410
  if (userAuth) {
411
+ this.user.loggingIn = true;
408
412
  this.user.email = userAuth.email;
409
413
  this.user.uid = userAuth.uid;
410
414
  this.user.firebaseUser = userAuth;
@@ -419,6 +423,7 @@ export const EdgeFirebase = class {
419
423
  this.user.oAuthCredential.accessToken = "";
420
424
  this.user.oAuthCredential.idToken = "";
421
425
  this.user.loggedIn = false;
426
+ this.user.loggingIn = false;
422
427
  }
423
428
  });
424
429
  };
@@ -462,6 +467,32 @@ export const EdgeFirebase = class {
462
467
  }
463
468
  }
464
469
 
470
+ public currentUserRegister = async (userRegister: userRegister): Promise<actionResponse> => {
471
+ if (!Object.prototype.hasOwnProperty.call(userRegister, 'registrationCode') || userRegister.registrationCode === "") {
472
+ return this.sendResponse({
473
+ success: false,
474
+ message: "Registration code is required.",
475
+ meta: {}
476
+ });
477
+ }
478
+ // userRegister.uid = this.user.uid;
479
+ const result = await this.runFunction("currentUserRegister", userRegister as unknown as Record<string, unknown>);
480
+ const resultData = result.data as {success: boolean, message: string};
481
+ if (resultData.success) {
482
+ return this.sendResponse({
483
+ success: true,
484
+ message: "",
485
+ meta: {}
486
+ });
487
+ } else {
488
+ return this.sendResponse({
489
+ success: false,
490
+ message: resultData.message,
491
+ meta: {}
492
+ });
493
+ }
494
+ }
495
+
465
496
  public registerUser = async (
466
497
  userRegister: userRegister,
467
498
  authProvider: authProviders = "email",
@@ -532,6 +563,7 @@ export const EdgeFirebase = class {
532
563
  }
533
564
  const initRoleHelper = {uid: response.user.uid}
534
565
  initRoleHelper["edge-assignment-helper"] = {permissionType: "roles"}
566
+ this.user.loggingIn = true;
535
567
  await setDoc(doc(this.db, "rule-helpers", response.user.uid), initRoleHelper);
536
568
  await updateDoc(doc(this.db, "staged-users/" + userRegister.registrationCode), stagedUserUpdate)
537
569
  this.logAnalyticsEvent("sign_up", { uid: response.user.uid});
@@ -959,6 +991,7 @@ export const EdgeFirebase = class {
959
991
  this.user.oAuthCredential.idToken = "";
960
992
  this.user.email = "";
961
993
  this.user.loggedIn = false;
994
+ this.user.loggingIn = false;
962
995
  this.user.meta = {};
963
996
  this.user.roles = [];
964
997
  this.user.specialPermissions = [];
@@ -984,6 +1017,7 @@ export const EdgeFirebase = class {
984
1017
  this.user.oAuthCredential.idToken = "";
985
1018
  this.user.loggedIn = false;
986
1019
  this.user.logInError = true;
1020
+ this.user.loggingIn = false;
987
1021
  this.user.logInErrorMessage = error.code + ": " + error.message;
988
1022
  });
989
1023
  };
@@ -1005,6 +1039,7 @@ export const EdgeFirebase = class {
1005
1039
  public user: UserDataObject = reactive({
1006
1040
  uid: null,
1007
1041
  email: "",
1042
+ loggingIn: false,
1008
1043
  loggedIn: false,
1009
1044
  logInError: false,
1010
1045
  logInErrorMessage: "",
@@ -1300,7 +1335,7 @@ export const EdgeFirebase = class {
1300
1335
  }
1301
1336
  };
1302
1337
 
1303
- public startSnapshot = async(
1338
+ public startSnapshot = async (
1304
1339
  collectionPath: string,
1305
1340
  queryList: FirestoreQuery[] = [],
1306
1341
  orderList: FirestoreOrderBy[] = [],
@@ -1312,20 +1347,32 @@ export const EdgeFirebase = class {
1312
1347
  this.unsubscibe[collectionPath] = null;
1313
1348
  if (canRead) {
1314
1349
  const q = this.getQuery(collectionPath, queryList, orderList, max);
1315
- const unsubscribe = onSnapshot(q, (querySnapshot) => {
1316
- const items = {};
1317
- querySnapshot.forEach((doc) => {
1318
- const item = doc.data();
1319
- item.docId = doc.id;
1320
- items[doc.id] = item;
1350
+
1351
+ return new Promise<actionResponse>((resolve, reject) => {
1352
+ const unsubscribe = onSnapshot(q, (querySnapshot) => {
1353
+ const items = {};
1354
+ querySnapshot.forEach((doc) => {
1355
+ const item = doc.data();
1356
+ item.docId = doc.id;
1357
+ items[doc.id] = item;
1358
+ });
1359
+ this.data[collectionPath] = items;
1360
+ this.unsubscibe[collectionPath] = unsubscribe;
1361
+
1362
+ // Resolve the Promise with the success response
1363
+ resolve(this.sendResponse({
1364
+ success: true,
1365
+ message: "",
1366
+ meta: {}
1367
+ }));
1368
+ }, (error) => {
1369
+ // Reject the Promise with the error response
1370
+ reject(this.sendResponse({
1371
+ success: false,
1372
+ message: `Error fetching data from "${collectionPath}": ${error.message}`,
1373
+ meta: {}
1374
+ }));
1321
1375
  });
1322
- this.data[collectionPath] = items;
1323
- });
1324
- this.unsubscibe[collectionPath] = unsubscribe;
1325
- return this.sendResponse({
1326
- success: true,
1327
- message: "",
1328
- meta: {}
1329
1376
  });
1330
1377
  } else {
1331
1378
  return this.sendResponse({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@edgedev/firebase",
3
- "version": "1.8.12",
3
+ "version": "1.9.2",
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": {
package/src/functions.js CHANGED
@@ -34,6 +34,56 @@ exports.removeNonRegisteredUser = functions.https.onCall(async (data, context) =
34
34
  return { success: false, message: 'Non-registered user not found.' }
35
35
  })
36
36
 
37
+ exports.currentUserRegister = functions.https.onCall(async (data, context) => {
38
+ if (data.uid === context.auth.uid) {
39
+ const stagedUser = await db.collection('staged-users').doc(data.registrationCode).get()
40
+ if (!stagedUser.exists) {
41
+ return { success: false, message: 'Registration code not found.' }
42
+ }
43
+ else {
44
+ const stagedUserData = await stagedUser.data()
45
+ let process = false
46
+ if (stagedUserData.isTemplate) {
47
+ process = true
48
+ }
49
+ if (!stagedUserData.isTemplate && stagedUserData.userId === '') {
50
+ process = true
51
+ }
52
+ if (!process) {
53
+ return { success: false, message: 'Registration code not valid.' }
54
+ }
55
+ const newRoles = stagedUserData.roles || {}
56
+ const currentUser = await db.collection('users').doc(data.uid).get()
57
+ const currentUserData = await currentUser.data()
58
+ const currentRoles = currentUserData.roles || {}
59
+ const currentUserCollectionPaths = currentUserData.collectionPaths || []
60
+ let newRole = {}
61
+ if (stagedUserData.subCreate && Object.keys(stagedUserData.subCreate).length !== 0 && stagedUserData.isTemplate) {
62
+ if (!data.dynamicDocumentFieldValue) {
63
+ return { success: false, message: 'Dynamic document field value is required.' }
64
+ }
65
+ const rootPath = stagedUserData.subCreate.rootPath
66
+ const newDoc = stagedUserData.subCreate.documentStructure
67
+ newDoc[stagedUserData.subCreate.dynamicDocumentField] = data.dynamicDocumentFieldValue
68
+ const addedDoc = await db.collection(rootPath).add(newDoc)
69
+ await db.collection(rootPath).doc(addedDoc.id).update({ docId: addedDoc.id })
70
+ newRole = { [`${rootPath}-${addedDoc.id}`]: { collectionPath: `${rootPath}-${addedDoc.id}`, role: stagedUserData.subCreate.role } }
71
+ }
72
+ const combinedRoles = { ...currentRoles, ...newRoles, ...newRole }
73
+ Object.values(combinedRoles).forEach((role) => {
74
+ if (!currentUserCollectionPaths.includes(role.collectionPath)) {
75
+ currentUserCollectionPaths.push(role.collectionPath)
76
+ }
77
+ })
78
+ await db.collection('staged-users').doc(currentUserData.stagedDocId).update({ roles: combinedRoles, collectionPaths: currentUserCollectionPaths })
79
+ if (!stagedUserData.isTemplate) {
80
+ await db.collection('staged-users').doc(data.registrationCode).delete()
81
+ }
82
+ return { success: true, message: '' }
83
+ }
84
+ }
85
+ })
86
+
37
87
  exports.updateUser = functions.firestore.document('staged-users/{docId}').onUpdate((change, context) => {
38
88
  const eventId = context.eventId
39
89
  const eventRef = db.collection('events').doc(eventId)