@edgedev/create-edge-app 0.0.1

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 (44) hide show
  1. package/.eslintrc +10 -0
  2. package/.vscode/settings.json +14 -0
  3. package/README.md +63 -0
  4. package/app.vue +106 -0
  5. package/bin/cli-edge-app.js +99 -0
  6. package/components/.gitkeep +0 -0
  7. package/components/account.vue +84 -0
  8. package/components/bottomMenu.vue +35 -0
  9. package/components/dashboard.vue +186 -0
  10. package/components/editor.vue +250 -0
  11. package/components/formSubtypes/.gitkeep +0 -0
  12. package/components/topMenu.vue +17 -0
  13. package/components/userMenu.vue +64 -0
  14. package/composables/global.ts +15 -0
  15. package/composables/vuetify.ts +10 -0
  16. package/deploy.sh +6 -0
  17. package/emulator.sh +17 -0
  18. package/firebase.json +56 -0
  19. package/firestore.indexes.json +4 -0
  20. package/firestore.rules +294 -0
  21. package/firestore.rules.backup +1 -0
  22. package/functions/.runtimeconfig.json +5 -0
  23. package/functions/index.js +226 -0
  24. package/functions/index.js.backup +4 -0
  25. package/functions/package-lock.json +4535 -0
  26. package/functions/package.json +25 -0
  27. package/middleware/auth.ts +17 -0
  28. package/nuxt.config.ts +27 -0
  29. package/package.json +34 -0
  30. package/pages/app/[[page]]/[[collection]]/[[docId]].vue +48 -0
  31. package/pages/app/login.vue +16 -0
  32. package/pages/app/signup.vue +17 -0
  33. package/plugins/draggable.ts +5 -0
  34. package/plugins/edgeFirebaseFramework.ts +5 -0
  35. package/plugins/firebase.client.ts +16 -0
  36. package/plugins/maska.ts +5 -0
  37. package/plugins/number.ts +4 -0
  38. package/plugins/vuetify.ts +14 -0
  39. package/public/favicon.ico +0 -0
  40. package/public/images/logo-square.png +0 -0
  41. package/public/images/logo.png +0 -0
  42. package/server/tsconfig.json +3 -0
  43. package/storage.rules +8 -0
  44. package/tsconfig.json +4 -0
@@ -0,0 +1,294 @@
1
+ rules_version = '2';
2
+ // #EDGE FIREBASE RULES START
3
+ service cloud.firestore {
4
+
5
+ match /databases/{database}/documents/events/{event} {
6
+ allow read: if false;
7
+ allow create: if false;
8
+ allow update: if false;
9
+ allow delete: if false;
10
+ }
11
+
12
+ match /databases/{database}/documents/rule-helpers/{helper} {
13
+ allow read: if false;
14
+ allow create: if request.auth.uid == request.resource.data.uid;
15
+ allow update: if request.auth.uid == request.resource.data.uid;
16
+ allow delete: if false;
17
+ }
18
+
19
+ match /databases/{database}/documents/users/{user} {
20
+ function readSelf() {
21
+ return resource == null ||
22
+ (
23
+ "userId" in resource.data &&
24
+ resource.data.userId == request.auth.uid
25
+ );
26
+ }
27
+
28
+ allow read: if readSelf();
29
+ allow create: if false;
30
+ allow update: if false;
31
+ allow delete: if false;
32
+ }
33
+
34
+ match /databases/{database}/documents/collection-data/{collectionPath} {
35
+ // TODO: these rules need tested.
36
+ function getRolePermission(role, collection, permissionCheck) {
37
+ let pathCollectionPermissions = get(/databases/$(database)/documents/collection-data/$(collection)).data;
38
+ let defaultPermissions = get(/databases/$(database)/documents/collection-data/-default-).data;
39
+ return (role in pathCollectionPermissions && pathCollectionPermissions[role][permissionCheck]) ||
40
+ (role in defaultPermissions && defaultPermissions[role][permissionCheck]);
41
+ }
42
+ function canAssign() {
43
+ let user = get(/databases/$(database)/documents/users/$(request.auth.uid)).data;
44
+ let ruleHelper = get(/databases/$(database)/documents/rule-helpers/$(request.auth.uid)).data['edge-assignment-helper'];
45
+ return collectionPath.matches("^" + ruleHelper[collectionPath].permissionCheckPath + ".*$") &&
46
+ (
47
+ "specialPermissions" in user &&
48
+ ruleHelper[collectionPath].permissionCheckPath in user.specialPermissions &&
49
+ "assign" in user.specialPermissions[ruleHelper[collectionPath].permissionCheckPath] &&
50
+ user.specialPermissions[ruleHelper[collectionPath].permissionCheckPath]["assign"]
51
+ ) ||
52
+ (
53
+ "roles" in user &&
54
+ ruleHelper[collectionPath].permissionCheckPath in user.roles &&
55
+ "role" in user.roles[ruleHelper[collectionPath].permissionCheckPath] &&
56
+ getRolePermission(user.roles[ruleHelper[collectionPath].permissionCheckPath].role, collectionPath, "assign")
57
+ );
58
+ }
59
+ allow read: if request.auth != null; // All signed in users can read collection-data
60
+ allow create: if canAssign();
61
+ allow update: if canAssign();
62
+ allow delete: if canAssign();
63
+ }
64
+
65
+ match /databases/{database}/documents/staged-users/{user} {
66
+
67
+ function canUpdate() {
68
+ let user = get(/databases/$(database)/documents/users/$(request.auth.uid)).data;
69
+ let ruleHelper = get(/databases/$(database)/documents/rule-helpers/$(request.auth.uid)).data;
70
+
71
+ return (
72
+ request.auth.uid == request.resource.data.uid &&
73
+ (
74
+ (
75
+ (
76
+ request.resource.data.userId == resource.data.userId ||
77
+ resource.data.userId == ""
78
+ ) &&
79
+ (
80
+ request.resource.data.userId == request.auth.uid ||
81
+ request.resource.data.templateUserId == request.auth.uid
82
+ )
83
+ ) ||
84
+ (
85
+ request.resource.data.userId == resource.data.userId &&
86
+ "edge-assignment-helper" in ruleHelper &&
87
+ permissionUpdatesCheck(user, ruleHelper, "roles") &&
88
+ permissionUpdatesCheck(user, ruleHelper, "specialPermssions")
89
+ )
90
+ )
91
+ );
92
+
93
+ }
94
+
95
+
96
+ function permissionUpdatesCheck(user, ruleHelper, permissionType) {
97
+ return !(permissionType in request.resource.data) ||
98
+ (
99
+ resource.data.userId == request.auth.uid &&
100
+ request.resource.data[permissionType].keys().hasOnly(resource.data[permissionType].keys())
101
+ ) ||
102
+ (
103
+ resource.data.userId != request.auth.uid &&
104
+ permissionCheck(permissionType, user, ruleHelper)
105
+ );
106
+ }
107
+ function permissionCheck(permissionType, user, ruleHelper) {
108
+ let lastPathUpdated = ruleHelper["edge-assignment-helper"].fullPath;
109
+ let permissionCheckPath = ruleHelper["edge-assignment-helper"].permissionCheckPath;
110
+ return request.resource.data[permissionType].diff(resource.data[permissionType]).affectedKeys().size() == 0 ||
111
+ (
112
+ request.resource.data[permissionType].diff(resource.data[permissionType]).affectedKeys().size() == 1 &&
113
+ request.resource.data[permissionType].diff(resource.data[permissionType]).affectedKeys() == [lastPathUpdated].toSet() &&
114
+ (
115
+ permissionCheckPath == "-" ||
116
+ lastPathUpdated.matches("^" + permissionCheckPath + ".*$")
117
+ ) &&
118
+ (
119
+ (
120
+ "roles" in user &&
121
+ getRolePermission(user.roles[permissionCheckPath].role, permissionCheckPath, "assign")
122
+ ) ||
123
+ (
124
+ "specialPermissions" in user &&
125
+ permissionCheckPath in user.specialPermissions &&
126
+ "assign" in user.specialPermissions[permissionCheckPath] &&
127
+ user.specialPermissions[permissionCheckPath]["assign"]
128
+ )
129
+ )
130
+ );
131
+ }
132
+
133
+ function canAssign(user, ruleHelper) {
134
+ return request.auth != null &&
135
+ "edge-assignment-helper" in ruleHelper &&
136
+ (
137
+ (
138
+ "roles" in user &&
139
+ ruleHelper["edge-assignment-helper"].permissionCheckPath in user.roles &&
140
+ getRolePermission(user.roles[ruleHelper["edge-assignment-helper"].permissionCheckPath].role, ruleHelper["edge-assignment-helper"].permissionCheckPath, 'assign')
141
+ ) ||
142
+ (
143
+ "specialPermissions" in user &&
144
+ ruleHelper["edge-assignment-helper"].permissionCheckPath in user.specialPermissions &&
145
+ "assign" in user.specialPermissions[ruleHelper["edge-assignment-helper"].permissionCheckPath] &&
146
+ user.specialPermissions[ruleHelper["edge-assignment-helper"].permissionCheckPath]["assign"]
147
+ )
148
+ )
149
+ }
150
+
151
+ function canAssignSubCreatePath(user, ruleHelper) {
152
+ let permissionCheckPath = ruleHelper["edge-assignment-helper"].permissionCheckPath;
153
+ return (
154
+ !("subCreate" in request.resource.data) ||
155
+ (
156
+ "subCreate" in request.resource.data &&
157
+ request.resource.data.subCreate.keys().size() == 0
158
+ )
159
+ )||
160
+ (
161
+ permissionCheckPath == "-" ||
162
+ request.resource.data.subCreate.rootPath.matches("^" + permissionCheckPath + ".*$")
163
+ ) &&
164
+ (
165
+ (
166
+ "roles" in user &&
167
+ permissionCheckPath in user.roles &&
168
+ getRolePermission(user.roles[permissionCheckPath].role, permissionCheckPath, "assign")
169
+ ) ||
170
+ (
171
+ "specialPermissions" in user &&
172
+ permissionCheckPath in user.specialPermissions &&
173
+ "assign" in user.specialPermissions[permissionCheckPath] &&
174
+ user.specialPermissions[permissionCheckPath]["assign"]
175
+ )
176
+ )
177
+
178
+ }
179
+
180
+ function canList() {
181
+ let user = get(/databases/$(database)/documents/users/$(request.auth.uid)).data;
182
+ let ruleHelper = get(/databases/$(database)/documents/rule-helpers/$(request.auth.uid)).data;
183
+ return canAssign(user, ruleHelper);
184
+ }
185
+
186
+ function canCreate() {
187
+ let user = get(/databases/$(database)/documents/users/$(request.auth.uid)).data;
188
+ let ruleHelper = get(/databases/$(database)/documents/rule-helpers/$(request.auth.uid)).data;
189
+ return noPermissionData() && canAssign(user, ruleHelper) && canAssignSubCreatePath(user, ruleHelper);
190
+ }
191
+
192
+ function noPermissionData() {
193
+ return request.resource.data.roles.size() == 0 && request.resource.data.specialPermissions.size() == 0;
194
+ }
195
+
196
+ function getRolePermission(role, collection, permissionCheck) {
197
+ let pathCollectionPermissions = get(/databases/$(database)/documents/collection-data/$(collection)).data;
198
+ let defaultPermissions = get(/databases/$(database)/documents/collection-data/-default-).data;
199
+ return (role in pathCollectionPermissions && pathCollectionPermissions[role][permissionCheck]) ||
200
+ (role in defaultPermissions && defaultPermissions[role][permissionCheck]);
201
+ }
202
+
203
+ function canGet () {
204
+ return resource == null ||
205
+ ("userId" in resource.data && resource.data.userId == "") ||
206
+ ("userId" in resource.data && resource.data.userId == request.auth.uid) ||
207
+ canAssign(get(/databases/$(database)/documents/users/$(request.auth.uid)).data, get(/databases/$(database)/documents/rule-helpers/$(request.auth.uid)).data);
208
+ }
209
+ allow get: if canGet();
210
+ allow list: if canList();
211
+ allow create: if canCreate();
212
+ allow update: if canUpdate();
213
+ allow delete: if false // TODO if isTemplate is true... can delete... otherwise users never deleted just removed from collection paths
214
+ }
215
+
216
+ match /databases/{database}/documents/{seg1} {
217
+ function getRolePermission(role, collection, permissionCheck) {
218
+ let pathCollectionPermissions = get(/databases/$(database)/documents/collection-data/$(collection)).data;
219
+ let defaultPermissions = get(/databases/$(database)/documents/collection-data/-default-).data;
220
+ return (role in pathCollectionPermissions && pathCollectionPermissions[role][permissionCheck]) ||
221
+ (role in defaultPermissions && defaultPermissions[role][permissionCheck]);
222
+ }
223
+ function checkPermission(collectionPath, permissionCheck) {
224
+ let user = get(/databases/$(database)/documents/users/$(request.auth.uid)).data;
225
+ let skipPaths = ["collection-data", "users", "staged-users", "events", "rule-helpers"];
226
+ let ruleHelper = get(/databases/$(database)/documents/rule-helpers/$(request.auth.uid)).data;
227
+ return !(collectionPath in skipPaths) &&
228
+ request.auth != null &&
229
+ collectionPath in ruleHelper &&
230
+ "permissionCheckPath" in ruleHelper[collectionPath] &&
231
+ (
232
+ ruleHelper[collectionPath].permissionCheckPath == "-" ||
233
+ collectionPath.matches("^" + ruleHelper[collectionPath].permissionCheckPath + ".*$")
234
+ ) &&
235
+ (
236
+ (
237
+ "roles" in user &&
238
+ ruleHelper[collectionPath].permissionCheckPath in user.roles &&
239
+ getRolePermission(user.roles[ruleHelper[collectionPath].permissionCheckPath].role, ruleHelper[collectionPath].permissionCheckPath, permissionCheck)
240
+ ) ||
241
+ (
242
+ "specialPermissions" in user &&
243
+ ruleHelper[collectionPath].permissionCheckPath in user.specialPermissions &&
244
+ permissionCheck in user.specialPermissions[ruleHelper[collectionPath].permissionCheckPath] &&
245
+ user.specialPermissions[ruleHelper[collectionPath].permissionCheckPath][permissionCheck]
246
+ )
247
+ );
248
+ }
249
+ match /{seg2} {
250
+ allow get: if checkPermission(seg1 + "-" + seg2, "read");
251
+ allow list: if checkPermission(seg1, "read");
252
+ allow create: if request.auth.uid == request.resource.data.uid && checkPermission(seg1, "write");
253
+ allow update: if request.auth.uid == request.resource.data.uid && checkPermission(seg1 + "-" + seg2, "write");
254
+ allow delete: if checkPermission(seg1, "delete");
255
+ match /{seg3} {
256
+ allow get: if checkPermission(seg1 + "-" + seg2 + "-" + seg3, "read");
257
+ allow list: if checkPermission(seg1 + "-" + seg2, "read");
258
+ allow create: if request.auth.uid == request.resource.data.uid && checkPermission(seg1 + "-" + seg2, "write");
259
+ allow update: if request.auth.uid == request.resource.data.uid && checkPermission(seg1 + "-" + seg2 + "-" + seg3, "write");
260
+ allow delete: if checkPermission(seg1 + "-" + seg2, "delete");
261
+ match /{seg4} {
262
+ allow get: if checkPermission(seg1 + "-" + seg2 + "-" + seg3 + "-" + seg4, "read");
263
+ allow list: if checkPermission(seg1 + "-" + seg2 + "-" + seg3, "read");
264
+ allow create: if request.auth.uid == request.resource.data.uid && checkPermission(seg1 + "-" + seg2 + "-" + seg3, "write");
265
+ allow update: if request.auth.uid == request.resource.data.uid && checkPermission(seg1 + "-" + seg2 + "-" + seg3 + "-" + seg4, "write");
266
+ allow delete: if checkPermission(seg1 + "-" + seg2 + "-" + seg3, "delete");
267
+
268
+ match /{seg5} {
269
+ allow get: if checkPermission(seg1 + "-" + seg2 + "-" + seg3 + "-" + seg4 + "-" + seg5, "read");
270
+ allow list: if checkPermission(seg1 + "-" + seg2 + "-" + seg3 + "-" + seg4, "read");
271
+ allow create: if request.auth.uid == request.resource.data.uid && checkPermission(seg1 + "-" + seg2 + "-" + seg3 + "-" + seg4, "write");
272
+ allow update: if request.auth.uid == request.resource.data.uid && checkPermission(seg1 + "-" + seg2 + "-" + seg3 + "-" + seg4 + "-" + seg5, "write");
273
+ allow delete: if checkPermission(seg1 + "-" + seg2 + "-" + seg3 + "-" + seg4, "delete");
274
+ match /{seg6} {
275
+ allow get: if checkPermission(seg1 + "-" + seg2 + "-" + seg3 + "-" + seg4 + "-" + seg5 + "-" + seg6, "read");
276
+ allow list: if checkPermission(seg1 + "-" + seg2 + "-" + seg3 + "-" + seg4 + "-" + seg5, "read");
277
+ allow create: if request.auth.uid == request.resource.data.uid && checkPermission(seg1 + "-" + seg2 + "-" + seg3 + "-" + seg4 + "-" + seg5, "write");
278
+ allow update: if request.auth.uid == request.resource.data.uid && checkPermission(seg1 + "-" + seg2 + "-" + seg3 + "-" + seg4 + "-" + seg5 + "-" + seg6, "write");
279
+ allow delete: if checkPermission(seg1 + "-" + seg2 + "-" + seg3 + "-" + seg4 + "-" + seg5, "delete");
280
+ match /{seg7} {
281
+ allow get: if checkPermission(seg1 + "-" + seg2 + "-" + seg3 + "-" + seg4 + "-" + seg5 + "-" + seg6 + "-" + seg7, "read");
282
+ allow list: if checkPermission(seg1 + "-" + seg2 + "-" + seg3 + "-" + seg4 + "-" + seg5 + "-" + seg6, "read");
283
+ allow create: if request.auth.uid == request.resource.data.uid && checkPermission(seg1 + "-" + seg2 + "-" + seg3 + "-" + seg4 + "-" + seg5 + "-" + seg6, "write");
284
+ allow update: if request.auth.uid == request.resource.data.uid && checkPermission(seg1 + "-" + seg2 + "-" + seg3 + "-" + seg4 + "-" + seg5 + "-" + seg6 + "-" + seg7, "write");
285
+ allow delete: if checkPermission(seg1 + "-" + seg2 + "-" + seg3 + "-" + seg4 + "-" + seg5 + "-" + seg6, "delete");
286
+ }
287
+ }
288
+ }
289
+ }
290
+ }
291
+ }
292
+ }
293
+ }
294
+ // #EDGE FIREBASE RULES END
@@ -0,0 +1 @@
1
+ rules_version = '2';
@@ -0,0 +1,5 @@
1
+ {
2
+ "openai": {
3
+ "api_key": "some-key-here"
4
+ }
5
+ }
@@ -0,0 +1,226 @@
1
+ const functions = require('firebase-functions')
2
+ const admin = require('firebase-admin')
3
+ admin.initializeApp()
4
+ const db = admin.firestore()
5
+ // START @edge/firebase functions
6
+
7
+ exports.initFirestore = functions.https.onCall(async (data, context) => {
8
+ // checks to see of the collections 'collection-data' and 'staged-users' exist if not will seed them with data
9
+ const collectionData = await db.collection('collection-data').get()
10
+ const stagedUsers = await db.collection('staged-users').get()
11
+ if (collectionData.empty) {
12
+ // create a document with the id of '-' and one called '-default-':
13
+ const admin = { assign: true, delete: true, read: true, write: true }
14
+ const editor = { assign: false, delete: true, read: true, write: true }
15
+ const writer = { assign: false, delete: false, read: true, write: true }
16
+ const user = { assign: false, delete: false, read: true, write: false }
17
+ await db.collection('collection-data').doc('-').set({ admin, editor, writer, user })
18
+ await db.collection('collection-data').doc('-default-').set({ admin, editor, writer, user })
19
+ }
20
+ if (stagedUsers.empty) {
21
+ const templateUser = {
22
+ docId: 'organization-registration-template',
23
+ isTemplate: true,
24
+ meta: {
25
+ name: 'Organization Registration Template',
26
+ },
27
+ subCreate: {
28
+ documentStructure: {
29
+ name: '',
30
+ },
31
+ dynamicDocumentField: 'name',
32
+ role: 'admin',
33
+ rootPath: 'organizations',
34
+ },
35
+ userId: '',
36
+ }
37
+ await db.collection('staged-users').doc('organization-registration-template').set(templateUser)
38
+ }
39
+ })
40
+
41
+ exports.removeNonRegisteredUser = functions.https.onCall(async (data, context) => {
42
+ if (data.uid === context.auth.uid) {
43
+ const stagedUser = await db.collection('staged-users').doc(data.docId).get()
44
+ if (stagedUser.exists) {
45
+ const stagedUserData = stagedUser.data()
46
+
47
+ const rolesExist = stagedUserData.roles && Object.keys(stagedUserData.roles).length !== 0
48
+ const specialPermissionsExist = stagedUserData.specialPermissions && Object.keys(stagedUserData.specialPermissions).length !== 0
49
+ const userIdExistsAndNotBlank = stagedUserData.userId && stagedUserData.userId !== ''
50
+
51
+ if (!rolesExist && !specialPermissionsExist && !userIdExistsAndNotBlank) {
52
+ await db.collection('staged-users').doc(data.docId).delete()
53
+ return { success: true, message: '' }
54
+ }
55
+ else {
56
+ let message = ''
57
+ if (rolesExist && specialPermissionsExist) {
58
+ message = 'Cannot delete because the non-registered user still has roles and special permissions assigned.'
59
+ }
60
+ else if (rolesExist) {
61
+ message = 'Cannot delete because the non-registered user still has roles assigned.'
62
+ }
63
+ else if (specialPermissionsExist) {
64
+ message = 'Cannot delete because the non-registered user still has special permissions assigned.'
65
+ }
66
+ else if (userIdExistsAndNotBlank) {
67
+ message = 'Cannot delete because the user is registered.'
68
+ }
69
+ return { success: false, message }
70
+ }
71
+ }
72
+ }
73
+ return { success: false, message: 'Non-registered user not found.' }
74
+ })
75
+
76
+ exports.currentUserRegister = functions.https.onCall(async (data, context) => {
77
+ if (data.uid === context.auth.uid) {
78
+ const stagedUser = await db.collection('staged-users').doc(data.registrationCode).get()
79
+ if (!stagedUser.exists) {
80
+ return { success: false, message: 'Registration code not found.' }
81
+ }
82
+ else {
83
+ const stagedUserData = await stagedUser.data()
84
+ let process = false
85
+ if (stagedUserData.isTemplate) {
86
+ process = true
87
+ }
88
+ if (!stagedUserData.isTemplate && stagedUserData.userId === '') {
89
+ process = true
90
+ }
91
+ if (!process) {
92
+ return { success: false, message: 'Registration code not valid.' }
93
+ }
94
+ const newRoles = stagedUserData.roles || {}
95
+ const currentUser = await db.collection('users').doc(data.uid).get()
96
+ const currentUserData = await currentUser.data()
97
+ const currentRoles = currentUserData.roles || {}
98
+ const currentUserCollectionPaths = currentUserData.collectionPaths || []
99
+ let newRole = {}
100
+ if (stagedUserData.subCreate && Object.keys(stagedUserData.subCreate).length !== 0 && stagedUserData.isTemplate) {
101
+ if (!data.dynamicDocumentFieldValue) {
102
+ return { success: false, message: 'Dynamic document field value is required.' }
103
+ }
104
+ const rootPath = stagedUserData.subCreate.rootPath
105
+ const newDoc = stagedUserData.subCreate.documentStructure
106
+ newDoc[stagedUserData.subCreate.dynamicDocumentField] = data.dynamicDocumentFieldValue
107
+ const addedDoc = await db.collection(rootPath).add(newDoc)
108
+ await db.collection(rootPath).doc(addedDoc.id).update({ docId: addedDoc.id })
109
+ newRole = { [`${rootPath}-${addedDoc.id}`]: { collectionPath: `${rootPath}-${addedDoc.id}`, role: stagedUserData.subCreate.role } }
110
+ }
111
+ const combinedRoles = { ...currentRoles, ...newRoles, ...newRole }
112
+ Object.values(combinedRoles).forEach((role) => {
113
+ if (!currentUserCollectionPaths.includes(role.collectionPath)) {
114
+ currentUserCollectionPaths.push(role.collectionPath)
115
+ }
116
+ })
117
+ await db.collection('staged-users').doc(currentUserData.stagedDocId).update({ roles: combinedRoles, collectionPaths: currentUserCollectionPaths })
118
+ if (!stagedUserData.isTemplate) {
119
+ await db.collection('staged-users').doc(data.registrationCode).delete()
120
+ }
121
+ return { success: true, message: '' }
122
+ }
123
+ }
124
+ })
125
+
126
+ exports.updateUser = functions.firestore.document('staged-users/{docId}').onUpdate((change, context) => {
127
+ const eventId = context.eventId
128
+ const eventRef = db.collection('events').doc(eventId)
129
+ const stagedDocId = context.params.docId
130
+ let newData = change.after.data()
131
+ const oldData = change.before.data()
132
+ return shouldProcess(eventRef).then((process) => {
133
+ if (process) {
134
+ // Note: we can trust on newData.uid because we are checking in rules that it matches the auth.uid
135
+ if (newData.userId) {
136
+ const userRef = db.collection('users').doc(newData.userId)
137
+ setUser(userRef, newData, oldData, stagedDocId).then(() => {
138
+ return markProcessed(eventRef)
139
+ })
140
+ }
141
+ else {
142
+ if (newData.templateUserId !== oldData.templateUserId) {
143
+ newData.isTemplate = false
144
+ const templateUserId = newData.templateUserId
145
+ newData.meta = newData.templateMeta
146
+ delete newData.templateMeta
147
+ delete newData.templateUserId
148
+ if (Object.prototype.hasOwnProperty.call(newData, 'subCreate') && Object.values(newData.subCreate).length > 0) {
149
+ const subCreate = newData.subCreate
150
+ delete newData.subCreate
151
+ db.collection(subCreate.rootPath).add({ [subCreate.dynamicDocumentField]: newData.dynamicDocumentFieldValue }).then((addedDoc) => {
152
+ db.collection(subCreate.rootPath).doc(addedDoc.id).update({ docId: addedDoc.id }).then(() => {
153
+ delete newData.dynamicDocumentFieldValue
154
+ const newRole = { [`${subCreate.rootPath}-${addedDoc.id}`]: { collectionPath: `${subCreate.rootPath}-${addedDoc.id}`, role: subCreate.role } }
155
+ if (Object.prototype.hasOwnProperty.call(newData, 'collectionPaths')) {
156
+ newData.collectionPaths.push(`${subCreate.rootPath}-${addedDoc.id}`)
157
+ }
158
+ else {
159
+ newData.collectionPaths = [`${subCreate.rootPath}-${addedDoc.id}`]
160
+ }
161
+ const newRoles = { ...newData.roles, ...newRole }
162
+ newData = { ...newData, roles: newRoles }
163
+ const stagedUserRef = db.collection('staged-users').doc(templateUserId)
164
+ return stagedUserRef.set({ ...newData, userId: templateUserId }).then(() => {
165
+ const userRef = db.collection('users').doc(templateUserId)
166
+ setUser(userRef, newData, oldData, templateUserId).then(() => {
167
+ return markProcessed(eventRef)
168
+ })
169
+ })
170
+ })
171
+ })
172
+ }
173
+ else {
174
+ const stagedUserRef = db.collection('staged-users').doc(templateUserId)
175
+ return stagedUserRef.set({ ...newData, userId: templateUserId }).then(() => {
176
+ const userRef = db.collection('users').doc(templateUserId)
177
+ setUser(userRef, newData, oldData, templateUserId).then(() => {
178
+ return markProcessed(eventRef)
179
+ })
180
+ })
181
+ }
182
+ }
183
+ }
184
+ return markProcessed(eventRef)
185
+ }
186
+ })
187
+ })
188
+
189
+ function setUser(userRef, newData, oldData, stagedDocId) {
190
+ // IT's OK If "users" doesn't match exactly matched "staged-users" because this is only preventing
191
+ // writing from outside the @edgdev/firebase functions, so discrepancies will be rare since
192
+ // the package will prevent before it gets this far.
193
+ return userRef.get().then((user) => {
194
+ let userUpdate = { meta: newData.meta, stagedDocId }
195
+
196
+ if (Object.prototype.hasOwnProperty.call(newData, 'roles')) {
197
+ userUpdate = { ...userUpdate, roles: newData.roles }
198
+ }
199
+ if (Object.prototype.hasOwnProperty.call(newData, 'specialPermissions')) {
200
+ userUpdate = { ...userUpdate, specialPermissions: newData.specialPermissions }
201
+ }
202
+
203
+ if (!oldData.userId) {
204
+ userUpdate = { ...userUpdate, userId: newData.uid }
205
+ }
206
+ if (!user.exists) {
207
+ return userRef.set(userUpdate)
208
+ }
209
+ else {
210
+ return userRef.update(userUpdate)
211
+ }
212
+ })
213
+ }
214
+
215
+ function shouldProcess(eventRef) {
216
+ return eventRef.get().then((eventDoc) => {
217
+ return !eventDoc.exists || !eventDoc.data().processed
218
+ })
219
+ }
220
+
221
+ function markProcessed(eventRef) {
222
+ return eventRef.set({ processed: true }).then(() => {
223
+ return null
224
+ })
225
+ }
226
+ // END @edge/firebase functions
@@ -0,0 +1,4 @@
1
+ const functions = require('firebase-functions')
2
+ const admin = require('firebase-admin')
3
+ admin.initializeApp()
4
+ const db = admin.firestore()