@edgedev/firebase 1.2.5 → 1.3.0
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 +248 -20
- package/edgeFirebase.ts +904 -102
- package/images/root-collection-roles.png +0 -0
- package/images/root-user.jpg +0 -0
- package/package.json +6 -6
- package/pnpm-lock.yaml +1993 -0
package/edgeFirebase.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { initializeApp } from "firebase/app";
|
|
2
2
|
import { reactive } from "vue";
|
|
3
|
-
|
|
4
3
|
import {
|
|
5
4
|
getFirestore,
|
|
6
5
|
collection,
|
|
@@ -20,7 +19,9 @@ import {
|
|
|
20
19
|
Query,
|
|
21
20
|
startAfter,
|
|
22
21
|
DocumentData,
|
|
23
|
-
setDoc
|
|
22
|
+
setDoc,
|
|
23
|
+
updateDoc,
|
|
24
|
+
deleteField
|
|
24
25
|
} from "firebase/firestore";
|
|
25
26
|
|
|
26
27
|
import {
|
|
@@ -31,7 +32,8 @@ import {
|
|
|
31
32
|
Persistence,
|
|
32
33
|
signInWithEmailAndPassword,
|
|
33
34
|
onAuthStateChanged,
|
|
34
|
-
signOut
|
|
35
|
+
signOut,
|
|
36
|
+
createUserWithEmailAndPassword
|
|
35
37
|
} from "firebase/auth";
|
|
36
38
|
|
|
37
39
|
interface FirestoreQuery {
|
|
@@ -57,12 +59,67 @@ interface CollectionDataObject {
|
|
|
57
59
|
[key: string]: object;
|
|
58
60
|
}
|
|
59
61
|
|
|
62
|
+
interface permissions {
|
|
63
|
+
assign: boolean;
|
|
64
|
+
read: boolean;
|
|
65
|
+
write: boolean;
|
|
66
|
+
delete: boolean;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
type action = "assign" | "read" | "write" | "delete";
|
|
70
|
+
|
|
71
|
+
interface role {
|
|
72
|
+
collectionPath: "-" | string; // - is root
|
|
73
|
+
role: "admin" | "user";
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// TODO: PASSWORD RESET FUNCTION <-- NOT LOGGED IN, PASSWORD UPDATE FUNTION <-- WHEN LOGGED IN, AND USER META UPDATE FUNCTION (ONLY FOR THEMSELVES)
|
|
77
|
+
|
|
78
|
+
interface specialPermission {
|
|
79
|
+
collectionPath: "-" | string; // - is root
|
|
80
|
+
permissions: permissions;
|
|
81
|
+
}
|
|
82
|
+
|
|
60
83
|
interface UserDataObject {
|
|
61
84
|
uid: string | null;
|
|
62
85
|
email: string;
|
|
63
86
|
loggedIn: boolean;
|
|
64
87
|
logInError: boolean;
|
|
65
88
|
logInErrorMessage: string;
|
|
89
|
+
meta: object;
|
|
90
|
+
roles: role[];
|
|
91
|
+
specialPermissions: specialPermission[];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
interface newUser {
|
|
95
|
+
email: string;
|
|
96
|
+
roles: role[];
|
|
97
|
+
specialPermissions: specialPermission[];
|
|
98
|
+
meta: object;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
interface user {
|
|
102
|
+
email: string;
|
|
103
|
+
role: "admin" | "user" | null;
|
|
104
|
+
specialPermission: permissions | null;
|
|
105
|
+
userId: string;
|
|
106
|
+
docId: string;
|
|
107
|
+
uid: string;
|
|
108
|
+
last_updated: Date;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
interface usersByCollection {
|
|
112
|
+
[collectionPath: string]: [user];
|
|
113
|
+
}
|
|
114
|
+
interface userMeta extends newUser {
|
|
115
|
+
docId: string;
|
|
116
|
+
userId: string;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
interface userRegister {
|
|
120
|
+
email: string;
|
|
121
|
+
password: string;
|
|
122
|
+
meta: object;
|
|
66
123
|
}
|
|
67
124
|
|
|
68
125
|
interface Credentials {
|
|
@@ -84,6 +141,16 @@ interface firebaseConfig {
|
|
|
84
141
|
appId: string;
|
|
85
142
|
}
|
|
86
143
|
|
|
144
|
+
interface actionResponse {
|
|
145
|
+
success: boolean;
|
|
146
|
+
message: string;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
interface permissionStatus {
|
|
150
|
+
canDo: boolean;
|
|
151
|
+
badCollectionPaths: string[];
|
|
152
|
+
}
|
|
153
|
+
|
|
87
154
|
export const EdgeFirebase = class {
|
|
88
155
|
constructor(
|
|
89
156
|
firebaseConfig: firebaseConfig = {
|
|
@@ -104,25 +171,86 @@ export const EdgeFirebase = class {
|
|
|
104
171
|
|
|
105
172
|
private firebaseConfig = null;
|
|
106
173
|
|
|
107
|
-
// Initialize Firebase
|
|
108
174
|
public app = null;
|
|
109
175
|
public auth = null;
|
|
110
176
|
public db = null;
|
|
111
177
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
178
|
+
private initUserMetaPermissions = async (): Promise<void> => {
|
|
179
|
+
updateDoc(doc(this.db, "users", this.user.email), {
|
|
180
|
+
userId: this.user.uid
|
|
181
|
+
});
|
|
182
|
+
this.user.meta = {};
|
|
183
|
+
const docRef = doc(this.db, "users", this.user.email);
|
|
184
|
+
const docSnap = await getDoc(docRef);
|
|
185
|
+
if (docSnap) {
|
|
186
|
+
this.user.meta = docSnap.data().meta;
|
|
187
|
+
const roles: role[] = [];
|
|
188
|
+
if (docSnap.data().roles) {
|
|
189
|
+
for (const collectionPath in docSnap.data().roles) {
|
|
190
|
+
roles.push({
|
|
191
|
+
collectionPath,
|
|
192
|
+
role: docSnap.data().roles[collectionPath].role
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
this.user.roles = roles;
|
|
197
|
+
|
|
198
|
+
const specialPermissions: specialPermission[] = [];
|
|
199
|
+
if (docSnap.data().specialPermissions) {
|
|
200
|
+
for (const collectionPath in docSnap.data().specialPermissions) {
|
|
201
|
+
specialPermissions.push({
|
|
202
|
+
collectionPath,
|
|
203
|
+
permissions:
|
|
204
|
+
docSnap.data().specialPermissions[collectionPath].permissions
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
this.user.specialPermissions = specialPermissions;
|
|
209
|
+
}
|
|
210
|
+
const metaUnsubscribe = onSnapshot(
|
|
211
|
+
doc(this.db, "users", this.user.email),
|
|
212
|
+
(doc) => {
|
|
213
|
+
if (!doc.exists()) {
|
|
214
|
+
this.setUser({
|
|
215
|
+
email: this.user.email,
|
|
216
|
+
roles: [],
|
|
217
|
+
specialPermissions: [],
|
|
218
|
+
meta: {}
|
|
219
|
+
});
|
|
220
|
+
this.user.meta = {};
|
|
221
|
+
} else {
|
|
222
|
+
this.user.meta = doc.data().meta;
|
|
223
|
+
const roles: role[] = [];
|
|
224
|
+
if (doc.data().roles) {
|
|
225
|
+
for (const collectionPath in doc.data().roles) {
|
|
226
|
+
roles.push({
|
|
227
|
+
collectionPath,
|
|
228
|
+
role: doc.data().roles[collectionPath].role
|
|
229
|
+
});
|
|
230
|
+
}
|
|
120
231
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
232
|
+
this.user.roles = roles;
|
|
233
|
+
|
|
234
|
+
const specialPermissions: specialPermission[] = [];
|
|
235
|
+
if (doc.data().specialPermissions) {
|
|
236
|
+
for (const collectionPath in doc.data().specialPermissions) {
|
|
237
|
+
specialPermissions.push({
|
|
238
|
+
collectionPath,
|
|
239
|
+
permissions:
|
|
240
|
+
doc.data().specialPermissions[collectionPath].permissions
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
this.user.specialPermissions = specialPermissions;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
);
|
|
248
|
+
this.unsubscibe.userMeta = metaUnsubscribe;
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
private startUserMetaSync = async (): Promise<void> => {
|
|
252
|
+
await this.initUserMetaPermissions();
|
|
253
|
+
this.user.loggedIn = true;
|
|
126
254
|
};
|
|
127
255
|
|
|
128
256
|
private setOnAuthStateChanged = (): void => {
|
|
@@ -130,9 +258,10 @@ export const EdgeFirebase = class {
|
|
|
130
258
|
if (userAuth) {
|
|
131
259
|
this.user.email = userAuth.email;
|
|
132
260
|
this.user.uid = userAuth.uid;
|
|
133
|
-
this.user.loggedIn = true;
|
|
134
261
|
this.user.logInError = false;
|
|
135
262
|
this.user.logInErrorMessage = "";
|
|
263
|
+
|
|
264
|
+
this.startUserMetaSync();
|
|
136
265
|
} else {
|
|
137
266
|
this.user.email = "";
|
|
138
267
|
this.user.uid = null;
|
|
@@ -143,6 +272,320 @@ export const EdgeFirebase = class {
|
|
|
143
272
|
});
|
|
144
273
|
};
|
|
145
274
|
|
|
275
|
+
public registerUser = async (
|
|
276
|
+
userRegister: userRegister
|
|
277
|
+
): Promise<actionResponse> => {
|
|
278
|
+
const userRef = doc(this.db, "users", userRegister.email);
|
|
279
|
+
const userSnap = await getDoc(userRef);
|
|
280
|
+
if (userSnap.exists()) {
|
|
281
|
+
const user = userSnap.data();
|
|
282
|
+
if (user.userId) {
|
|
283
|
+
return this.sendResponse({
|
|
284
|
+
success: false,
|
|
285
|
+
message: "User already registered"
|
|
286
|
+
});
|
|
287
|
+
} else {
|
|
288
|
+
createUserWithEmailAndPassword(
|
|
289
|
+
this.auth,
|
|
290
|
+
userRegister.email,
|
|
291
|
+
userRegister.password
|
|
292
|
+
).then(() => {
|
|
293
|
+
const metaUpdate = {};
|
|
294
|
+
for (const [key, value] of Object.entries(userRegister.meta)) {
|
|
295
|
+
metaUpdate["meta." + key] = value;
|
|
296
|
+
}
|
|
297
|
+
if (Object.keys(metaUpdate).length > 0) {
|
|
298
|
+
updateDoc(doc(this.db, "users", this.user.email), metaUpdate);
|
|
299
|
+
}
|
|
300
|
+
return this.sendResponse({
|
|
301
|
+
success: true,
|
|
302
|
+
message: ""
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
} else {
|
|
307
|
+
return this.sendResponse({
|
|
308
|
+
success: false,
|
|
309
|
+
message: "User doesn't exist"
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
public removeUser = async (email: string): Promise<actionResponse> => {
|
|
315
|
+
const removedFrom = [];
|
|
316
|
+
const userRef = doc(this.db, "users", email);
|
|
317
|
+
const userSnap = await getDoc(userRef);
|
|
318
|
+
if (userSnap.data().roles) {
|
|
319
|
+
for (const collectionPath in userSnap.data().roles) {
|
|
320
|
+
const canAssign = await this.permissionCheck(
|
|
321
|
+
"assign",
|
|
322
|
+
collectionPath.replaceAll("-", "/")
|
|
323
|
+
);
|
|
324
|
+
if (canAssign) {
|
|
325
|
+
this.removeUserRoles(email, collectionPath.replaceAll("-", "/"));
|
|
326
|
+
removedFrom.push(collectionPath.replaceAll("-", "/"));
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
if (userSnap.data().specialPermissions) {
|
|
331
|
+
for (const collectionPath in userSnap.data().specialPermissions) {
|
|
332
|
+
const canAssign = await this.permissionCheck(
|
|
333
|
+
"assign",
|
|
334
|
+
collectionPath.replaceAll("-", "/")
|
|
335
|
+
);
|
|
336
|
+
if (canAssign) {
|
|
337
|
+
this.removeUserSpecialPermissions(
|
|
338
|
+
email,
|
|
339
|
+
collectionPath.replaceAll("-", "/")
|
|
340
|
+
);
|
|
341
|
+
removedFrom.push(collectionPath.replaceAll("-", "/"));
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
if (removedFrom.length > 0) {
|
|
346
|
+
return this.sendResponse({
|
|
347
|
+
success: true,
|
|
348
|
+
message: ""
|
|
349
|
+
});
|
|
350
|
+
} else {
|
|
351
|
+
return this.sendResponse({
|
|
352
|
+
success: false,
|
|
353
|
+
message: "You do not have permission to remove this user"
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
public setUser = async (newUser: newUser): Promise<actionResponse> => {
|
|
359
|
+
const canAssignRole = await this.multiPermissionCheck(
|
|
360
|
+
"assign",
|
|
361
|
+
newUser.roles
|
|
362
|
+
);
|
|
363
|
+
const canAssignSpecialPermissions = await this.multiPermissionCheck(
|
|
364
|
+
"assign",
|
|
365
|
+
newUser.specialPermissions
|
|
366
|
+
);
|
|
367
|
+
if (canAssignRole.canDo && canAssignSpecialPermissions.canDo) {
|
|
368
|
+
const userMeta: userMeta = {
|
|
369
|
+
docId: newUser.email,
|
|
370
|
+
userId: "",
|
|
371
|
+
email: newUser.email,
|
|
372
|
+
roles: newUser.roles,
|
|
373
|
+
specialPermissions: newUser.specialPermissions,
|
|
374
|
+
meta: newUser.meta
|
|
375
|
+
};
|
|
376
|
+
this.generateUserMeta(userMeta);
|
|
377
|
+
return this.sendResponse({
|
|
378
|
+
success: true,
|
|
379
|
+
message: ""
|
|
380
|
+
});
|
|
381
|
+
} else {
|
|
382
|
+
return this.sendResponse({
|
|
383
|
+
success: false,
|
|
384
|
+
message:
|
|
385
|
+
"Cannot assign role or special permission for collection path(s): " +
|
|
386
|
+
canAssignRole.badCollectionPaths
|
|
387
|
+
.concat(canAssignSpecialPermissions.badCollectionPaths)
|
|
388
|
+
.join(", ")
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
private multiPermissionCheck = async (
|
|
394
|
+
action: action,
|
|
395
|
+
collections = []
|
|
396
|
+
): Promise<permissionStatus> => {
|
|
397
|
+
let canDo = true;
|
|
398
|
+
const badCollectionPaths = [];
|
|
399
|
+
// if (collections.length === 0) {
|
|
400
|
+
// canDo = false;
|
|
401
|
+
// }
|
|
402
|
+
for (const collection of collections) {
|
|
403
|
+
if (!(await this.permissionCheck(action, collection.collectionPath))) {
|
|
404
|
+
badCollectionPaths.push(collection.collectionPath);
|
|
405
|
+
canDo = false;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
if (!canDo) {
|
|
409
|
+
return {
|
|
410
|
+
canDo: false,
|
|
411
|
+
badCollectionPaths
|
|
412
|
+
};
|
|
413
|
+
} else {
|
|
414
|
+
return {
|
|
415
|
+
canDo: true,
|
|
416
|
+
badCollectionPaths: []
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
private sendResponse = (response: actionResponse): actionResponse => {
|
|
422
|
+
console.log(response);
|
|
423
|
+
return response;
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
private permissionCheck = async (
|
|
427
|
+
action: action,
|
|
428
|
+
collectionPath: string
|
|
429
|
+
): Promise<boolean> => {
|
|
430
|
+
const collection = collectionPath.split("/");
|
|
431
|
+
let index = collection.length;
|
|
432
|
+
let permissionData = {};
|
|
433
|
+
permissionData = {
|
|
434
|
+
read: false,
|
|
435
|
+
write: false,
|
|
436
|
+
delete: false,
|
|
437
|
+
assign: false
|
|
438
|
+
};
|
|
439
|
+
while (index > 0) {
|
|
440
|
+
if (!permissionData[action]) {
|
|
441
|
+
const collectionArray = JSON.parse(JSON.stringify(collection));
|
|
442
|
+
const permissionCheck = collectionArray.splice(0, index).join("-");
|
|
443
|
+
const role = this.user.roles.find(
|
|
444
|
+
(r) => r.collectionPath === permissionCheck
|
|
445
|
+
);
|
|
446
|
+
|
|
447
|
+
if (role) {
|
|
448
|
+
permissionData = await this.getCollectionPermissions(
|
|
449
|
+
permissionCheck,
|
|
450
|
+
role.role
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
const specialPermission = this.user.specialPermissions.find(
|
|
454
|
+
(r) => r.collectionPath === permissionCheck
|
|
455
|
+
);
|
|
456
|
+
if (specialPermission) {
|
|
457
|
+
permissionData = specialPermission.permissions;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
index--;
|
|
461
|
+
}
|
|
462
|
+
if (!permissionData[action]) {
|
|
463
|
+
const rootRole = this.user.roles.find((r) => r.collectionPath === "-");
|
|
464
|
+
if (rootRole) {
|
|
465
|
+
permissionData = await this.getCollectionPermissions(
|
|
466
|
+
"-",
|
|
467
|
+
rootRole.role
|
|
468
|
+
);
|
|
469
|
+
}
|
|
470
|
+
const rootSpecialPermission = this.user.specialPermissions.find(
|
|
471
|
+
(r) => r.collectionPath === "-"
|
|
472
|
+
);
|
|
473
|
+
if (rootSpecialPermission) {
|
|
474
|
+
permissionData = rootSpecialPermission.permissions;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
return permissionData[action];
|
|
478
|
+
};
|
|
479
|
+
|
|
480
|
+
private getCollectionPermissions = async (
|
|
481
|
+
collectionPath: string,
|
|
482
|
+
role: string
|
|
483
|
+
): Promise<permissions> => {
|
|
484
|
+
const collectionRef = doc(
|
|
485
|
+
this.db,
|
|
486
|
+
"collection-data",
|
|
487
|
+
collectionPath.replaceAll("/", "-")
|
|
488
|
+
);
|
|
489
|
+
const collectionSnap = await getDoc(collectionRef);
|
|
490
|
+
|
|
491
|
+
if (collectionSnap.exists()) {
|
|
492
|
+
const permissionData = collectionSnap.data()[role];
|
|
493
|
+
return {
|
|
494
|
+
read: permissionData.read,
|
|
495
|
+
write: permissionData.write,
|
|
496
|
+
delete: permissionData.delete,
|
|
497
|
+
assign: permissionData.assign
|
|
498
|
+
};
|
|
499
|
+
} else {
|
|
500
|
+
return {
|
|
501
|
+
read: false,
|
|
502
|
+
write: false,
|
|
503
|
+
delete: false,
|
|
504
|
+
assign: false
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
private generateUserMeta = async (userMeta: userMeta): Promise<void> => {
|
|
510
|
+
const roles: role[] = userMeta.roles;
|
|
511
|
+
const specialPermissions: specialPermission[] = userMeta.specialPermissions;
|
|
512
|
+
delete userMeta.roles;
|
|
513
|
+
delete userMeta.specialPermissions;
|
|
514
|
+
|
|
515
|
+
const docRef = doc(this.db, "users", userMeta.docId);
|
|
516
|
+
const docSnap = await getDoc(docRef);
|
|
517
|
+
const docData = docSnap.data();
|
|
518
|
+
const canWrite = await this.permissionCheck("write", "users");
|
|
519
|
+
if (!docData || canWrite) {
|
|
520
|
+
setDoc(doc(this.db, "users", userMeta.docId), userMeta);
|
|
521
|
+
}
|
|
522
|
+
for (const role of roles) {
|
|
523
|
+
await this.generatePermissions(role.collectionPath);
|
|
524
|
+
this.storeUserRoles(userMeta.docId, role.collectionPath, role.role);
|
|
525
|
+
}
|
|
526
|
+
for (const specialPermission of specialPermissions) {
|
|
527
|
+
await this.generatePermissions(specialPermission.collectionPath);
|
|
528
|
+
this.storeUserSpecialPermissions(
|
|
529
|
+
userMeta.docId,
|
|
530
|
+
specialPermission.collectionPath,
|
|
531
|
+
specialPermission.permissions
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
};
|
|
535
|
+
|
|
536
|
+
private generatePermissions = async (
|
|
537
|
+
collectionPath: string
|
|
538
|
+
): Promise<void> => {
|
|
539
|
+
const collection = collectionPath.split("/");
|
|
540
|
+
let index = collection.length;
|
|
541
|
+
while (index > 0) {
|
|
542
|
+
const collectionArray = JSON.parse(JSON.stringify(collection));
|
|
543
|
+
const permissionCheck = collectionArray.splice(0, index).join("/");
|
|
544
|
+
const hasPermissions = await this.collectionExists(permissionCheck);
|
|
545
|
+
const adminPermission: permissions = {
|
|
546
|
+
assign: true,
|
|
547
|
+
read: true,
|
|
548
|
+
write: true,
|
|
549
|
+
delete: true
|
|
550
|
+
};
|
|
551
|
+
const userPermission: permissions = {
|
|
552
|
+
assign: false,
|
|
553
|
+
read: false,
|
|
554
|
+
write: false,
|
|
555
|
+
delete: false
|
|
556
|
+
};
|
|
557
|
+
if (!hasPermissions) {
|
|
558
|
+
await this.storeCollectionPermissions(
|
|
559
|
+
permissionCheck,
|
|
560
|
+
"admin",
|
|
561
|
+
adminPermission
|
|
562
|
+
);
|
|
563
|
+
await this.storeCollectionPermissions(
|
|
564
|
+
permissionCheck,
|
|
565
|
+
"user",
|
|
566
|
+
userPermission
|
|
567
|
+
);
|
|
568
|
+
}
|
|
569
|
+
index = index - 1;
|
|
570
|
+
}
|
|
571
|
+
};
|
|
572
|
+
|
|
573
|
+
// Composable to logout
|
|
574
|
+
public logOut = (): void => {
|
|
575
|
+
signOut(this.auth)
|
|
576
|
+
.then(() => {
|
|
577
|
+
Object.keys(this.unsubscibe).forEach((key) => {
|
|
578
|
+
if (this.unsubscibe[key] instanceof Function) {
|
|
579
|
+
this.unsubscibe[key]();
|
|
580
|
+
this.unsubscibe[key] = null;
|
|
581
|
+
}
|
|
582
|
+
});
|
|
583
|
+
})
|
|
584
|
+
.catch(() => {
|
|
585
|
+
// Do nothing
|
|
586
|
+
});
|
|
587
|
+
};
|
|
588
|
+
|
|
146
589
|
// Composable to login and set persistence
|
|
147
590
|
public logIn = (credentials: Credentials, isPersistant = false): void => {
|
|
148
591
|
this.logOut();
|
|
@@ -196,7 +639,10 @@ export const EdgeFirebase = class {
|
|
|
196
639
|
email: "",
|
|
197
640
|
loggedIn: false,
|
|
198
641
|
logInError: false,
|
|
199
|
-
logInErrorMessage: ""
|
|
642
|
+
logInErrorMessage: "",
|
|
643
|
+
meta: {},
|
|
644
|
+
roles: [],
|
|
645
|
+
specialPermissions: []
|
|
200
646
|
});
|
|
201
647
|
|
|
202
648
|
public getDocData = async (
|
|
@@ -210,6 +656,21 @@ export const EdgeFirebase = class {
|
|
|
210
656
|
return docData;
|
|
211
657
|
};
|
|
212
658
|
|
|
659
|
+
private collectionExists = async (
|
|
660
|
+
collectionPath: string
|
|
661
|
+
): Promise<boolean> => {
|
|
662
|
+
const collectionRef = doc(
|
|
663
|
+
this.db,
|
|
664
|
+
"collection-data",
|
|
665
|
+
collectionPath.replaceAll("/", "-")
|
|
666
|
+
);
|
|
667
|
+
const collectionSnap = await getDoc(collectionRef);
|
|
668
|
+
if (collectionSnap.exists()) {
|
|
669
|
+
return true;
|
|
670
|
+
}
|
|
671
|
+
return false;
|
|
672
|
+
};
|
|
673
|
+
|
|
213
674
|
private getStaticData = async (
|
|
214
675
|
collectionPath: string,
|
|
215
676
|
queryList: FirestoreQuery[] = [],
|
|
@@ -223,7 +684,6 @@ export const EdgeFirebase = class {
|
|
|
223
684
|
|
|
224
685
|
const docs = await getDocs(q);
|
|
225
686
|
const nextLast: DocumentData = docs.docs[docs.docs.length - 1];
|
|
226
|
-
|
|
227
687
|
docs.forEach((doc) => {
|
|
228
688
|
const item = doc.data();
|
|
229
689
|
item.docId = doc.id;
|
|
@@ -235,6 +695,8 @@ export const EdgeFirebase = class {
|
|
|
235
695
|
// Class for wrapping a getSaticData to handle pagination
|
|
236
696
|
get SearchStaticData() {
|
|
237
697
|
const getStaticData = this.getStaticData;
|
|
698
|
+
const permissionCheck = this.permissionCheck;
|
|
699
|
+
const sendResponse = this.sendResponse;
|
|
238
700
|
return class {
|
|
239
701
|
private collectionPath = "";
|
|
240
702
|
private queryList: FirestoreQuery[] = [];
|
|
@@ -281,7 +743,7 @@ export const EdgeFirebase = class {
|
|
|
281
743
|
|
|
282
744
|
private afterNextPrev = async (last): Promise<void> => {
|
|
283
745
|
let results = await getStaticData(
|
|
284
|
-
|
|
746
|
+
this.collectionPath,
|
|
285
747
|
this.queryList,
|
|
286
748
|
this.orderList,
|
|
287
749
|
this.max,
|
|
@@ -301,7 +763,7 @@ export const EdgeFirebase = class {
|
|
|
301
763
|
this.results.pagination[this.results.pagination.length - 2].key;
|
|
302
764
|
}
|
|
303
765
|
results = await getStaticData(
|
|
304
|
-
|
|
766
|
+
this.collectionPath,
|
|
305
767
|
this.queryList,
|
|
306
768
|
this.orderList,
|
|
307
769
|
this.max,
|
|
@@ -335,63 +797,51 @@ export const EdgeFirebase = class {
|
|
|
335
797
|
queryList: FirestoreQuery[] = [],
|
|
336
798
|
orderList: FirestoreOrderBy[] = [],
|
|
337
799
|
max = 0
|
|
338
|
-
): Promise<
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
this.results.pagination = [];
|
|
347
|
-
this.results.pagination = [];
|
|
348
|
-
this.results.data = {};
|
|
349
|
-
const results = await getStaticData(
|
|
350
|
-
collectionPath,
|
|
351
|
-
queryList,
|
|
352
|
-
orderList,
|
|
353
|
-
max
|
|
354
|
-
);
|
|
355
|
-
if (Object.keys(results.data).length > 0) {
|
|
356
|
-
this.results.staticIsLastPage = false;
|
|
357
|
-
this.results.data = results.data;
|
|
358
|
-
this.results.staticCurrentPage = results.next.id;
|
|
359
|
-
this.results.pagination.push({
|
|
360
|
-
key: results.next.id,
|
|
361
|
-
next: results.next
|
|
362
|
-
});
|
|
363
|
-
} else {
|
|
800
|
+
): Promise<actionResponse> => {
|
|
801
|
+
const canRead = await permissionCheck("read", collectionPath);
|
|
802
|
+
|
|
803
|
+
if (canRead) {
|
|
804
|
+
this.collectionPath = collectionPath;
|
|
805
|
+
this.queryList = queryList;
|
|
806
|
+
this.orderList = orderList;
|
|
807
|
+
this.max = max;
|
|
364
808
|
this.results.staticIsLastPage = true;
|
|
365
809
|
this.results.staticIsFirstPage = true;
|
|
810
|
+
this.results.staticCurrentPage = "";
|
|
811
|
+
this.results.pagination = [];
|
|
812
|
+
this.results.data = {};
|
|
813
|
+
const results = await getStaticData(
|
|
814
|
+
collectionPath,
|
|
815
|
+
queryList,
|
|
816
|
+
orderList,
|
|
817
|
+
max
|
|
818
|
+
);
|
|
819
|
+
if (Object.keys(results.data).length > 0) {
|
|
820
|
+
this.results.staticIsLastPage = false;
|
|
821
|
+
this.results.data = results.data;
|
|
822
|
+
this.results.staticCurrentPage = results.next.id;
|
|
823
|
+
this.results.pagination.push({
|
|
824
|
+
key: results.next.id,
|
|
825
|
+
next: results.next
|
|
826
|
+
});
|
|
827
|
+
} else {
|
|
828
|
+
this.results.staticIsLastPage = true;
|
|
829
|
+
this.results.staticIsFirstPage = true;
|
|
830
|
+
}
|
|
831
|
+
return sendResponse({
|
|
832
|
+
success: true,
|
|
833
|
+
message: ""
|
|
834
|
+
});
|
|
835
|
+
} else {
|
|
836
|
+
return sendResponse({
|
|
837
|
+
success: false,
|
|
838
|
+
message: `You do not have permission to read from "${collectionPath}"`
|
|
839
|
+
});
|
|
366
840
|
}
|
|
367
841
|
};
|
|
368
842
|
};
|
|
369
843
|
}
|
|
370
844
|
|
|
371
|
-
// Class for wrapping a getSaticData to handle pagination
|
|
372
|
-
public SearchStaticDatas = new (class {})();
|
|
373
|
-
|
|
374
|
-
// Composable to start snapshot listener and set unsubscribe function
|
|
375
|
-
public startSnapshot = (
|
|
376
|
-
collectionPath: string,
|
|
377
|
-
queryList: FirestoreQuery[] = [],
|
|
378
|
-
orderList: FirestoreOrderBy[] = [],
|
|
379
|
-
max = 0
|
|
380
|
-
): void => {
|
|
381
|
-
this.data[collectionPath] = {};
|
|
382
|
-
const q = this.getQuery(collectionPath, queryList, orderList, max);
|
|
383
|
-
const unsubscribe = onSnapshot(q, (querySnapshot) => {
|
|
384
|
-
const items = {};
|
|
385
|
-
querySnapshot.forEach((doc) => {
|
|
386
|
-
const item = doc.data();
|
|
387
|
-
item.docId = doc.id;
|
|
388
|
-
items[doc.id] = item;
|
|
389
|
-
});
|
|
390
|
-
this.data[collectionPath] = items;
|
|
391
|
-
});
|
|
392
|
-
this.unsubscibe[collectionPath] = unsubscribe;
|
|
393
|
-
};
|
|
394
|
-
|
|
395
845
|
private getQuery = (
|
|
396
846
|
collectionPath: string,
|
|
397
847
|
queryList: FirestoreQuery[] = [],
|
|
@@ -432,47 +882,399 @@ export const EdgeFirebase = class {
|
|
|
432
882
|
);
|
|
433
883
|
};
|
|
434
884
|
|
|
885
|
+
public startSnapshot = async (
|
|
886
|
+
collectionPath: string,
|
|
887
|
+
queryList: FirestoreQuery[] = [],
|
|
888
|
+
orderList: FirestoreOrderBy[] = [],
|
|
889
|
+
max = 0
|
|
890
|
+
): Promise<actionResponse> => {
|
|
891
|
+
const canRead = await this.permissionCheck("read", collectionPath);
|
|
892
|
+
this.data[collectionPath] = {};
|
|
893
|
+
this.unsubscibe[collectionPath] = null;
|
|
894
|
+
if (canRead) {
|
|
895
|
+
const q = this.getQuery(collectionPath, queryList, orderList, max);
|
|
896
|
+
const unsubscribe = onSnapshot(q, (querySnapshot) => {
|
|
897
|
+
const items = {};
|
|
898
|
+
querySnapshot.forEach((doc) => {
|
|
899
|
+
const item = doc.data();
|
|
900
|
+
item.docId = doc.id;
|
|
901
|
+
items[doc.id] = item;
|
|
902
|
+
});
|
|
903
|
+
this.data[collectionPath] = items;
|
|
904
|
+
});
|
|
905
|
+
this.unsubscibe[collectionPath] = unsubscribe;
|
|
906
|
+
return this.sendResponse({
|
|
907
|
+
success: true,
|
|
908
|
+
message: ""
|
|
909
|
+
});
|
|
910
|
+
} else {
|
|
911
|
+
return this.sendResponse({
|
|
912
|
+
success: false,
|
|
913
|
+
message: `You do not have permission to read from "${collectionPath}"`
|
|
914
|
+
});
|
|
915
|
+
}
|
|
916
|
+
};
|
|
917
|
+
|
|
918
|
+
public listCollectionsCanAssign = async (): Promise<string[]> => {
|
|
919
|
+
let collectionPaths = [];
|
|
920
|
+
for (const role of this.user.roles) {
|
|
921
|
+
const canAssign = await this.permissionCheck(
|
|
922
|
+
"assign",
|
|
923
|
+
role.collectionPath
|
|
924
|
+
);
|
|
925
|
+
if (canAssign) {
|
|
926
|
+
collectionPaths.push(role.collectionPath);
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
for (const specialPermission of this.user.specialPermissions) {
|
|
930
|
+
const canAssign = await this.permissionCheck(
|
|
931
|
+
"assign",
|
|
932
|
+
specialPermission.collectionPath
|
|
933
|
+
);
|
|
934
|
+
if (canAssign) {
|
|
935
|
+
collectionPaths.push(specialPermission.collectionPath);
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
collectionPaths = [...new Set(collectionPaths)];
|
|
939
|
+
let collectionPathList = [];
|
|
940
|
+
for (const collectionPath of collectionPaths) {
|
|
941
|
+
if (collectionPath === "-") {
|
|
942
|
+
const collections = await getDocs(
|
|
943
|
+
collection(this.db, "collection-data")
|
|
944
|
+
);
|
|
945
|
+
collections.forEach((doc) => {
|
|
946
|
+
collectionPathList.push(doc.id);
|
|
947
|
+
});
|
|
948
|
+
} else {
|
|
949
|
+
const collections = await getDocs(
|
|
950
|
+
query(
|
|
951
|
+
collection(this.db, "collection-data"),
|
|
952
|
+
where("collectionPath", ">=", collectionPath),
|
|
953
|
+
where("collectionPath", "<", collectionPath + "\uF8FF")
|
|
954
|
+
)
|
|
955
|
+
);
|
|
956
|
+
collections.forEach((doc) => {
|
|
957
|
+
collectionPathList.push(doc.id);
|
|
958
|
+
});
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
collectionPathList = [...new Set(collectionPathList)];
|
|
962
|
+
return collectionPathList;
|
|
963
|
+
};
|
|
964
|
+
|
|
965
|
+
public listUsers = async (): Promise<usersByCollection> => {
|
|
966
|
+
const userList = {};
|
|
967
|
+
const collectionPathList = await this.listCollectionsCanAssign();
|
|
968
|
+
for (const collectionPath of collectionPathList) {
|
|
969
|
+
userList[collectionPath] = [];
|
|
970
|
+
const roleUsers = await getDocs(
|
|
971
|
+
query(
|
|
972
|
+
collection(this.db, "users"),
|
|
973
|
+
where(
|
|
974
|
+
"roles." + collectionPath + ".collectionPath",
|
|
975
|
+
"==",
|
|
976
|
+
collectionPath
|
|
977
|
+
)
|
|
978
|
+
)
|
|
979
|
+
);
|
|
980
|
+
roleUsers.forEach((doc) => {
|
|
981
|
+
const user = doc.data();
|
|
982
|
+
userList[collectionPath].push({
|
|
983
|
+
docId: user.docId,
|
|
984
|
+
email: user.email,
|
|
985
|
+
role: user.roles[collectionPath].role,
|
|
986
|
+
specialPermission: null,
|
|
987
|
+
meta: user.meta,
|
|
988
|
+
last_updated: user.last_updated,
|
|
989
|
+
userId: user.userId,
|
|
990
|
+
uid: user.uid
|
|
991
|
+
});
|
|
992
|
+
});
|
|
993
|
+
const specialPermissionsUsers = await getDocs(
|
|
994
|
+
query(
|
|
995
|
+
collection(this.db, "users"),
|
|
996
|
+
where(
|
|
997
|
+
"specialPermissions." + collectionPath + ".collectionPath",
|
|
998
|
+
"==",
|
|
999
|
+
collectionPath
|
|
1000
|
+
)
|
|
1001
|
+
)
|
|
1002
|
+
);
|
|
1003
|
+
specialPermissionsUsers.forEach((doc) => {
|
|
1004
|
+
const user = doc.data();
|
|
1005
|
+
userList[collectionPath].push({
|
|
1006
|
+
docId: user.docId,
|
|
1007
|
+
email: user.email,
|
|
1008
|
+
role: null,
|
|
1009
|
+
specialPermission:
|
|
1010
|
+
user.specialPermissions[collectionPath].permissions,
|
|
1011
|
+
meta: user.meta,
|
|
1012
|
+
last_updated: user.last_updated,
|
|
1013
|
+
userId: user.userId,
|
|
1014
|
+
uid: user.uid
|
|
1015
|
+
});
|
|
1016
|
+
});
|
|
1017
|
+
}
|
|
1018
|
+
return userList;
|
|
1019
|
+
};
|
|
1020
|
+
|
|
1021
|
+
public removeUserRoles = async (
|
|
1022
|
+
email: string,
|
|
1023
|
+
collectionPath: string
|
|
1024
|
+
): Promise<actionResponse> => {
|
|
1025
|
+
const canAssign = await this.permissionCheck("assign", collectionPath);
|
|
1026
|
+
if (canAssign) {
|
|
1027
|
+
await updateDoc(doc(this.db, "users/" + email), {
|
|
1028
|
+
["roles." + collectionPath.replaceAll("/", "-")]: deleteField()
|
|
1029
|
+
});
|
|
1030
|
+
return this.sendResponse({
|
|
1031
|
+
success: true,
|
|
1032
|
+
message: ""
|
|
1033
|
+
});
|
|
1034
|
+
} else {
|
|
1035
|
+
return this.sendResponse({
|
|
1036
|
+
success: false,
|
|
1037
|
+
message:
|
|
1038
|
+
"Cannot remove permissions for collection path: " + collectionPath
|
|
1039
|
+
});
|
|
1040
|
+
}
|
|
1041
|
+
};
|
|
1042
|
+
|
|
1043
|
+
public removeUserSpecialPermissions = async (
|
|
1044
|
+
email: string,
|
|
1045
|
+
collectionPath: string
|
|
1046
|
+
): Promise<actionResponse> => {
|
|
1047
|
+
const canAssign = await this.permissionCheck("assign", collectionPath);
|
|
1048
|
+
if (canAssign) {
|
|
1049
|
+
await updateDoc(doc(this.db, "users/" + email), {
|
|
1050
|
+
["specialPermissions." + collectionPath.replaceAll("/", "-")]:
|
|
1051
|
+
deleteField()
|
|
1052
|
+
});
|
|
1053
|
+
return this.sendResponse({
|
|
1054
|
+
success: true,
|
|
1055
|
+
message: ""
|
|
1056
|
+
});
|
|
1057
|
+
} else {
|
|
1058
|
+
return this.sendResponse({
|
|
1059
|
+
success: false,
|
|
1060
|
+
message:
|
|
1061
|
+
"Cannot remove permissions for collection path: " + collectionPath
|
|
1062
|
+
});
|
|
1063
|
+
}
|
|
1064
|
+
};
|
|
1065
|
+
|
|
1066
|
+
public storeUserSpecialPermissions = async (
|
|
1067
|
+
email: string,
|
|
1068
|
+
collectionPath: string,
|
|
1069
|
+
permissions: permissions
|
|
1070
|
+
): Promise<actionResponse> => {
|
|
1071
|
+
const canAssign = await this.permissionCheck("assign", collectionPath);
|
|
1072
|
+
if (canAssign) {
|
|
1073
|
+
const collectionExists = await this.collectionExists(collectionPath);
|
|
1074
|
+
if (collectionExists) {
|
|
1075
|
+
const permissionItem = {
|
|
1076
|
+
["specialPermissions." + collectionPath.replaceAll("/", "-")]: {
|
|
1077
|
+
collectionPath: collectionPath.replaceAll("/", "-"),
|
|
1078
|
+
permissions
|
|
1079
|
+
}
|
|
1080
|
+
};
|
|
1081
|
+
updateDoc(doc(this.db, "users/" + email), permissionItem);
|
|
1082
|
+
return this.sendResponse({
|
|
1083
|
+
success: true,
|
|
1084
|
+
message: ""
|
|
1085
|
+
});
|
|
1086
|
+
} else {
|
|
1087
|
+
return this.sendResponse({
|
|
1088
|
+
success: false,
|
|
1089
|
+
message: collectionPath + " is not a valid collection path"
|
|
1090
|
+
});
|
|
1091
|
+
}
|
|
1092
|
+
} else {
|
|
1093
|
+
return this.sendResponse({
|
|
1094
|
+
success: false,
|
|
1095
|
+
message:
|
|
1096
|
+
"Cannot assign permissions for collection path: " + collectionPath
|
|
1097
|
+
});
|
|
1098
|
+
}
|
|
1099
|
+
};
|
|
1100
|
+
|
|
1101
|
+
public storeUserRoles = async (
|
|
1102
|
+
email: string,
|
|
1103
|
+
collectionPath: string,
|
|
1104
|
+
role: "admin" | "user"
|
|
1105
|
+
): Promise<actionResponse> => {
|
|
1106
|
+
const canAssign = await this.permissionCheck("assign", collectionPath);
|
|
1107
|
+
|
|
1108
|
+
if (canAssign) {
|
|
1109
|
+
if (role === "admin" || role === "user") {
|
|
1110
|
+
const collectionExists = await this.collectionExists(collectionPath);
|
|
1111
|
+
if (collectionExists) {
|
|
1112
|
+
const roleItem = {
|
|
1113
|
+
["roles." + collectionPath.replaceAll("/", "-")]: {
|
|
1114
|
+
collectionPath: collectionPath.replaceAll("/", "-"),
|
|
1115
|
+
role
|
|
1116
|
+
}
|
|
1117
|
+
};
|
|
1118
|
+
|
|
1119
|
+
updateDoc(doc(this.db, "users/" + email), roleItem);
|
|
1120
|
+
return this.sendResponse({
|
|
1121
|
+
success: true,
|
|
1122
|
+
message: ""
|
|
1123
|
+
});
|
|
1124
|
+
} else {
|
|
1125
|
+
return this.sendResponse({
|
|
1126
|
+
success: false,
|
|
1127
|
+
message: collectionPath + " is not a valid collection path"
|
|
1128
|
+
});
|
|
1129
|
+
}
|
|
1130
|
+
} else {
|
|
1131
|
+
return this.sendResponse({
|
|
1132
|
+
success: false,
|
|
1133
|
+
message: "Role must be either 'admin' or 'user'"
|
|
1134
|
+
});
|
|
1135
|
+
}
|
|
1136
|
+
} else {
|
|
1137
|
+
return this.sendResponse({
|
|
1138
|
+
success: false,
|
|
1139
|
+
message:
|
|
1140
|
+
"Cannot assign permissions for collection path: " + collectionPath
|
|
1141
|
+
});
|
|
1142
|
+
}
|
|
1143
|
+
};
|
|
1144
|
+
|
|
1145
|
+
public storeCollectionPermissions = async (
|
|
1146
|
+
collectionPath: string,
|
|
1147
|
+
role: "admin" | "user",
|
|
1148
|
+
permissions: permissions
|
|
1149
|
+
): Promise<actionResponse> => {
|
|
1150
|
+
const canAssign = await this.permissionCheck("assign", collectionPath);
|
|
1151
|
+
|
|
1152
|
+
if (canAssign) {
|
|
1153
|
+
if (role === "admin" || role === "user") {
|
|
1154
|
+
const currentTime = new Date().getTime();
|
|
1155
|
+
|
|
1156
|
+
const collectionItem = {
|
|
1157
|
+
collectionPath: collectionPath.replaceAll("/", "-"),
|
|
1158
|
+
docId: collectionPath.replaceAll("/", "-")
|
|
1159
|
+
};
|
|
1160
|
+
const collectionRef = doc(
|
|
1161
|
+
this.db,
|
|
1162
|
+
"collection-data",
|
|
1163
|
+
collectionItem.collectionPath
|
|
1164
|
+
);
|
|
1165
|
+
const collectionSnap = await getDoc(collectionRef);
|
|
1166
|
+
if (!collectionSnap.exists()) {
|
|
1167
|
+
await setDoc(
|
|
1168
|
+
doc(this.db, "collection-data", collectionItem.collectionPath),
|
|
1169
|
+
collectionItem
|
|
1170
|
+
);
|
|
1171
|
+
}
|
|
1172
|
+
await updateDoc(
|
|
1173
|
+
doc(this.db, "collection-data/" + collectionItem.collectionPath),
|
|
1174
|
+
{ [role]: permissions, uid: this.user.uid, last_updated: currentTime }
|
|
1175
|
+
);
|
|
1176
|
+
|
|
1177
|
+
return this.sendResponse({
|
|
1178
|
+
success: true,
|
|
1179
|
+
message: ""
|
|
1180
|
+
});
|
|
1181
|
+
} else {
|
|
1182
|
+
return this.sendResponse({
|
|
1183
|
+
success: false,
|
|
1184
|
+
message: "Role must be either 'admin' or 'user'"
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1187
|
+
} else {
|
|
1188
|
+
return this.sendResponse({
|
|
1189
|
+
success: false,
|
|
1190
|
+
message:
|
|
1191
|
+
"Cannot assign permissions for collection path: " + collectionPath
|
|
1192
|
+
});
|
|
1193
|
+
}
|
|
1194
|
+
};
|
|
1195
|
+
|
|
435
1196
|
// Composable to update/add a document
|
|
436
1197
|
public storeDoc = async (
|
|
437
1198
|
collectionPath: string,
|
|
438
|
-
item: object
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
const
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
if (Object.prototype.hasOwnProperty.call(cloneItem, "docId")) {
|
|
448
|
-
const docId = cloneItem.docId;
|
|
449
|
-
if (Object.prototype.hasOwnProperty.call(this.data, collectionPath)) {
|
|
450
|
-
this.data[collectionPath][docId] = cloneItem;
|
|
451
|
-
}
|
|
452
|
-
setDoc(doc(this.db, collectionPath, docId), cloneItem);
|
|
1199
|
+
item: object,
|
|
1200
|
+
generatePermissions = true
|
|
1201
|
+
): Promise<actionResponse> => {
|
|
1202
|
+
const canWrite = await this.permissionCheck("write", collectionPath);
|
|
1203
|
+
if (!canWrite) {
|
|
1204
|
+
return this.sendResponse({
|
|
1205
|
+
success: false,
|
|
1206
|
+
message: `You do not have permission to write to "${collectionPath}"`
|
|
1207
|
+
});
|
|
453
1208
|
} else {
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
1209
|
+
if (generatePermissions) {
|
|
1210
|
+
collectionPath = collectionPath.replaceAll("-", "_");
|
|
1211
|
+
this.generatePermissions(collectionPath);
|
|
1212
|
+
}
|
|
1213
|
+
const cloneItem = JSON.parse(JSON.stringify(item));
|
|
1214
|
+
const currentTime = new Date().getTime();
|
|
1215
|
+
cloneItem.last_updated = currentTime;
|
|
1216
|
+
cloneItem.uid = this.user.uid;
|
|
1217
|
+
if (!Object.prototype.hasOwnProperty.call(cloneItem, "doc_created_at")) {
|
|
1218
|
+
cloneItem.doc_created_at = currentTime;
|
|
1219
|
+
}
|
|
1220
|
+
if (Object.prototype.hasOwnProperty.call(cloneItem, "docId")) {
|
|
1221
|
+
const docId = cloneItem.docId;
|
|
1222
|
+
const canRead = await this.permissionCheck("read", collectionPath);
|
|
1223
|
+
if (canRead) {
|
|
1224
|
+
if (Object.prototype.hasOwnProperty.call(this.data, collectionPath)) {
|
|
1225
|
+
this.data[collectionPath][docId] = cloneItem;
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
setDoc(doc(this.db, collectionPath, docId), cloneItem);
|
|
1229
|
+
} else {
|
|
1230
|
+
const docRef = await addDoc(
|
|
1231
|
+
collection(this.db, collectionPath),
|
|
1232
|
+
cloneItem
|
|
1233
|
+
);
|
|
1234
|
+
const canRead = await this.permissionCheck("read", collectionPath);
|
|
1235
|
+
if (canRead) {
|
|
1236
|
+
if (Object.prototype.hasOwnProperty.call(this.data, collectionPath)) {
|
|
1237
|
+
this.data[collectionPath][docRef.id] = cloneItem;
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
this.storeDoc(
|
|
1241
|
+
collectionPath,
|
|
1242
|
+
{ ...cloneItem, docId: docRef.id },
|
|
1243
|
+
generatePermissions
|
|
1244
|
+
);
|
|
460
1245
|
}
|
|
461
|
-
this.
|
|
1246
|
+
return this.sendResponse({
|
|
1247
|
+
success: true,
|
|
1248
|
+
message: ""
|
|
1249
|
+
});
|
|
462
1250
|
}
|
|
463
1251
|
};
|
|
464
1252
|
|
|
465
1253
|
// Composable to delete a document
|
|
466
|
-
public removeDoc = (
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
1254
|
+
public removeDoc = async (
|
|
1255
|
+
collectionPath: string,
|
|
1256
|
+
docId: string
|
|
1257
|
+
): Promise<actionResponse> => {
|
|
1258
|
+
const canDelete = await this.permissionCheck("delete", collectionPath);
|
|
1259
|
+
if (canDelete) {
|
|
1260
|
+
if (Object.prototype.hasOwnProperty.call(this.data, collectionPath)) {
|
|
1261
|
+
if (
|
|
1262
|
+
Object.prototype.hasOwnProperty.call(this.data[collectionPath], docId)
|
|
1263
|
+
) {
|
|
1264
|
+
delete this.data[collectionPath][docId];
|
|
1265
|
+
}
|
|
473
1266
|
}
|
|
1267
|
+
deleteDoc(doc(this.db, collectionPath, docId));
|
|
1268
|
+
return this.sendResponse({
|
|
1269
|
+
success: true,
|
|
1270
|
+
message: ""
|
|
1271
|
+
});
|
|
1272
|
+
} else {
|
|
1273
|
+
return this.sendResponse({
|
|
1274
|
+
success: false,
|
|
1275
|
+
message: `You do not have permission to delete from "${collectionPath}"`
|
|
1276
|
+
});
|
|
474
1277
|
}
|
|
475
|
-
deleteDoc(doc(this.db, collectionPath, docId));
|
|
476
1278
|
};
|
|
477
1279
|
|
|
478
1280
|
// Composable to stop snapshot listener
|