canvasframework 0.4.4 → 0.4.5
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/components/QRCodeReader.js +2 -2
- package/components/TimePicker.js +2 -2
- package/index.js +10 -1
- package/package.json +1 -1
- package/utils/FirebaseAuth.js +653 -0
- package/utils/FirebaseCore.js +246 -0
- package/utils/FirebaseFirestore.js +581 -0
- package/utils/FirebaseFunctions.js +97 -0
- package/utils/FirebaseRealtimeDB.js +498 -0
- package/utils/FirebaseStorage.js +612 -0
- package/utils/PayPalPayment.js +678 -0
- package/utils/StripePayment.js +552 -0
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FirebaseRealtimeDB - Utilitaire pour Firebase Realtime Database
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* const realtimeDB = new FirebaseRealtimeDB(firebaseCore);
|
|
6
|
+
* await realtimeDB.set('users/123', { name: 'John', age: 30 });
|
|
7
|
+
* const data = await realtimeDB.get('users/123');
|
|
8
|
+
* realtimeDB.listen('users/123', (snapshot) => console.log(snapshot.val()));
|
|
9
|
+
*/
|
|
10
|
+
class FirebaseRealtimeDB {
|
|
11
|
+
constructor(firebaseCore) {
|
|
12
|
+
this.core = firebaseCore;
|
|
13
|
+
this.db = null;
|
|
14
|
+
this.listeners = new Map(); // Stocker les listeners pour les nettoyer
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Initialiser la base de données
|
|
19
|
+
*/
|
|
20
|
+
initialize() {
|
|
21
|
+
if (!this.db) {
|
|
22
|
+
this.db = this.core.getDatabase();
|
|
23
|
+
}
|
|
24
|
+
return this.db;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Obtenir une référence
|
|
29
|
+
*/
|
|
30
|
+
ref(path = '') {
|
|
31
|
+
if (!this.db) this.initialize();
|
|
32
|
+
return this.db.ref(path);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// ==================== OPERATIONS CRUD ====================
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Écrire des données (écrase les données existantes)
|
|
39
|
+
*/
|
|
40
|
+
async set(path, data) {
|
|
41
|
+
try {
|
|
42
|
+
await this.ref(path).set(data);
|
|
43
|
+
return { success: true };
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error(`❌ Erreur set ${path}:`, error);
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Mettre à jour des données (merge)
|
|
52
|
+
*/
|
|
53
|
+
async update(path, updates) {
|
|
54
|
+
try {
|
|
55
|
+
await this.ref(path).update(updates);
|
|
56
|
+
return { success: true };
|
|
57
|
+
} catch (error) {
|
|
58
|
+
console.error(`❌ Erreur update ${path}:`, error);
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Ajouter des données avec une clé auto-générée
|
|
65
|
+
*/
|
|
66
|
+
async push(path, data) {
|
|
67
|
+
try {
|
|
68
|
+
const newRef = this.ref(path).push();
|
|
69
|
+
await newRef.set(data);
|
|
70
|
+
return {
|
|
71
|
+
success: true,
|
|
72
|
+
key: newRef.key,
|
|
73
|
+
ref: newRef
|
|
74
|
+
};
|
|
75
|
+
} catch (error) {
|
|
76
|
+
console.error(`❌ Erreur push ${path}:`, error);
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Lire des données une fois
|
|
83
|
+
*/
|
|
84
|
+
async get(path) {
|
|
85
|
+
try {
|
|
86
|
+
const snapshot = await this.ref(path).once('value');
|
|
87
|
+
return snapshot.val();
|
|
88
|
+
} catch (error) {
|
|
89
|
+
console.error(`❌ Erreur get ${path}:`, error);
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Supprimer des données
|
|
96
|
+
*/
|
|
97
|
+
async remove(path) {
|
|
98
|
+
try {
|
|
99
|
+
await this.ref(path).remove();
|
|
100
|
+
return { success: true };
|
|
101
|
+
} catch (error) {
|
|
102
|
+
console.error(`❌ Erreur remove ${path}:`, error);
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ==================== LISTENERS EN TEMPS RÉEL ====================
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Écouter les changements en temps réel
|
|
111
|
+
*/
|
|
112
|
+
listen(path, callback, eventType = 'value') {
|
|
113
|
+
const ref = this.ref(path);
|
|
114
|
+
const listenerId = `${path}_${eventType}_${Date.now()}`;
|
|
115
|
+
|
|
116
|
+
const listener = ref.on(eventType,
|
|
117
|
+
(snapshot) => {
|
|
118
|
+
callback(snapshot, null);
|
|
119
|
+
},
|
|
120
|
+
(error) => {
|
|
121
|
+
console.error(`❌ Erreur listener ${path}:`, error);
|
|
122
|
+
callback(null, error);
|
|
123
|
+
}
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
// Stocker le listener pour le nettoyage
|
|
127
|
+
this.listeners.set(listenerId, { ref, eventType, listener });
|
|
128
|
+
|
|
129
|
+
// Retourner une fonction pour se désabonner
|
|
130
|
+
return () => this.unlisten(listenerId);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Écouter les ajouts d'enfants
|
|
135
|
+
*/
|
|
136
|
+
listenChildAdded(path, callback) {
|
|
137
|
+
return this.listen(path, callback, 'child_added');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Écouter les changements d'enfants
|
|
142
|
+
*/
|
|
143
|
+
listenChildChanged(path, callback) {
|
|
144
|
+
return this.listen(path, callback, 'child_changed');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Écouter les suppressions d'enfants
|
|
149
|
+
*/
|
|
150
|
+
listenChildRemoved(path, callback) {
|
|
151
|
+
return this.listen(path, callback, 'child_removed');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Arrêter d'écouter
|
|
156
|
+
*/
|
|
157
|
+
unlisten(listenerId) {
|
|
158
|
+
const listenerData = this.listeners.get(listenerId);
|
|
159
|
+
if (listenerData) {
|
|
160
|
+
listenerData.ref.off(listenerData.eventType, listenerData.listener);
|
|
161
|
+
this.listeners.delete(listenerId);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Arrêter tous les listeners
|
|
167
|
+
*/
|
|
168
|
+
unlistenAll() {
|
|
169
|
+
this.listeners.forEach((listenerData, listenerId) => {
|
|
170
|
+
listenerData.ref.off(listenerData.eventType, listenerData.listener);
|
|
171
|
+
});
|
|
172
|
+
this.listeners.clear();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// ==================== QUERIES ====================
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Requête avec limite
|
|
179
|
+
*/
|
|
180
|
+
async queryLimit(path, limit, direction = 'first') {
|
|
181
|
+
try {
|
|
182
|
+
let query;
|
|
183
|
+
if (direction === 'first') {
|
|
184
|
+
query = this.ref(path).limitToFirst(limit);
|
|
185
|
+
} else {
|
|
186
|
+
query = this.ref(path).limitToLast(limit);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const snapshot = await query.once('value');
|
|
190
|
+
return this.snapshotToArray(snapshot);
|
|
191
|
+
} catch (error) {
|
|
192
|
+
console.error(`❌ Erreur queryLimit ${path}:`, error);
|
|
193
|
+
throw error;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Requête avec ordre
|
|
199
|
+
*/
|
|
200
|
+
async queryOrderBy(path, orderByChild, options = {}) {
|
|
201
|
+
try {
|
|
202
|
+
let query = this.ref(path).orderByChild(orderByChild);
|
|
203
|
+
|
|
204
|
+
// Filtres optionnels
|
|
205
|
+
if (options.equalTo !== undefined) {
|
|
206
|
+
query = query.equalTo(options.equalTo);
|
|
207
|
+
}
|
|
208
|
+
if (options.startAt !== undefined) {
|
|
209
|
+
query = query.startAt(options.startAt);
|
|
210
|
+
}
|
|
211
|
+
if (options.endAt !== undefined) {
|
|
212
|
+
query = query.endAt(options.endAt);
|
|
213
|
+
}
|
|
214
|
+
if (options.limitToFirst) {
|
|
215
|
+
query = query.limitToFirst(options.limitToFirst);
|
|
216
|
+
}
|
|
217
|
+
if (options.limitToLast) {
|
|
218
|
+
query = query.limitToLast(options.limitToLast);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const snapshot = await query.once('value');
|
|
222
|
+
return this.snapshotToArray(snapshot);
|
|
223
|
+
} catch (error) {
|
|
224
|
+
console.error(`❌ Erreur queryOrderBy ${path}:`, error);
|
|
225
|
+
throw error;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Requête avec ordre par clé
|
|
231
|
+
*/
|
|
232
|
+
async queryOrderByKey(path, options = {}) {
|
|
233
|
+
try {
|
|
234
|
+
let query = this.ref(path).orderByKey();
|
|
235
|
+
|
|
236
|
+
if (options.startAt) query = query.startAt(options.startAt);
|
|
237
|
+
if (options.endAt) query = query.endAt(options.endAt);
|
|
238
|
+
if (options.limitToFirst) query = query.limitToFirst(options.limitToFirst);
|
|
239
|
+
if (options.limitToLast) query = query.limitToLast(options.limitToLast);
|
|
240
|
+
|
|
241
|
+
const snapshot = await query.once('value');
|
|
242
|
+
return this.snapshotToArray(snapshot);
|
|
243
|
+
} catch (error) {
|
|
244
|
+
console.error(`❌ Erreur queryOrderByKey ${path}:`, error);
|
|
245
|
+
throw error;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Requête avec ordre par valeur
|
|
251
|
+
*/
|
|
252
|
+
async queryOrderByValue(path, options = {}) {
|
|
253
|
+
try {
|
|
254
|
+
let query = this.ref(path).orderByValue();
|
|
255
|
+
|
|
256
|
+
if (options.startAt) query = query.startAt(options.startAt);
|
|
257
|
+
if (options.endAt) query = query.endAt(options.endAt);
|
|
258
|
+
if (options.limitToFirst) query = query.limitToFirst(options.limitToFirst);
|
|
259
|
+
if (options.limitToLast) query = query.limitToLast(options.limitToLast);
|
|
260
|
+
|
|
261
|
+
const snapshot = await query.once('value');
|
|
262
|
+
return this.snapshotToArray(snapshot);
|
|
263
|
+
} catch (error) {
|
|
264
|
+
console.error(`❌ Erreur queryOrderByValue ${path}:`, error);
|
|
265
|
+
throw error;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// ==================== TRANSACTIONS ====================
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Transaction (opération atomique)
|
|
273
|
+
*/
|
|
274
|
+
async transaction(path, updateFunction) {
|
|
275
|
+
try {
|
|
276
|
+
const result = await this.ref(path).transaction(updateFunction);
|
|
277
|
+
|
|
278
|
+
if (result.committed) {
|
|
279
|
+
return {
|
|
280
|
+
committed: true,
|
|
281
|
+
snapshot: result.snapshot,
|
|
282
|
+
value: result.snapshot.val()
|
|
283
|
+
};
|
|
284
|
+
} else {
|
|
285
|
+
return {
|
|
286
|
+
committed: false,
|
|
287
|
+
snapshot: result.snapshot
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
} catch (error) {
|
|
291
|
+
console.error(`❌ Erreur transaction ${path}:`, error);
|
|
292
|
+
throw error;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Incrémenter une valeur de manière atomique
|
|
298
|
+
*/
|
|
299
|
+
async increment(path, amount = 1) {
|
|
300
|
+
return this.transaction(path, (current) => {
|
|
301
|
+
return (current || 0) + amount;
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Décrémenter une valeur de manière atomique
|
|
307
|
+
*/
|
|
308
|
+
async decrement(path, amount = 1) {
|
|
309
|
+
return this.increment(path, -amount);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// ==================== BATCH OPERATIONS ====================
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Mise à jour multiple (atomique)
|
|
316
|
+
*/
|
|
317
|
+
async batchUpdate(updates) {
|
|
318
|
+
try {
|
|
319
|
+
await this.ref().update(updates);
|
|
320
|
+
return { success: true };
|
|
321
|
+
} catch (error) {
|
|
322
|
+
console.error('❌ Erreur batch update:', error);
|
|
323
|
+
throw error;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Suppression multiple
|
|
329
|
+
*/
|
|
330
|
+
async batchRemove(paths) {
|
|
331
|
+
const updates = {};
|
|
332
|
+
paths.forEach(path => {
|
|
333
|
+
updates[path] = null;
|
|
334
|
+
});
|
|
335
|
+
return this.batchUpdate(updates);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// ==================== PRESENCE ====================
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Gérer la présence utilisateur (online/offline)
|
|
342
|
+
*/
|
|
343
|
+
setupPresence(userId, userData = {}) {
|
|
344
|
+
const userStatusRef = this.ref(`status/${userId}`);
|
|
345
|
+
const connectedRef = this.ref('.info/connected');
|
|
346
|
+
|
|
347
|
+
connectedRef.on('value', (snapshot) => {
|
|
348
|
+
if (snapshot.val() === true) {
|
|
349
|
+
// Utilisateur connecté
|
|
350
|
+
userStatusRef.onDisconnect().set({
|
|
351
|
+
state: 'offline',
|
|
352
|
+
last_changed: firebase.database.ServerValue.TIMESTAMP
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
userStatusRef.set({
|
|
356
|
+
state: 'online',
|
|
357
|
+
last_changed: firebase.database.ServerValue.TIMESTAMP,
|
|
358
|
+
...userData
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
// Retourner une fonction pour nettoyer
|
|
364
|
+
return () => {
|
|
365
|
+
userStatusRef.set({
|
|
366
|
+
state: 'offline',
|
|
367
|
+
last_changed: firebase.database.ServerValue.TIMESTAMP
|
|
368
|
+
});
|
|
369
|
+
connectedRef.off();
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// ==================== HELPERS ====================
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Convertir un snapshot en tableau
|
|
377
|
+
*/
|
|
378
|
+
snapshotToArray(snapshot) {
|
|
379
|
+
const array = [];
|
|
380
|
+
snapshot.forEach((childSnapshot) => {
|
|
381
|
+
array.push({
|
|
382
|
+
key: childSnapshot.key,
|
|
383
|
+
...childSnapshot.val()
|
|
384
|
+
});
|
|
385
|
+
});
|
|
386
|
+
return array;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Convertir un snapshot en objet
|
|
391
|
+
*/
|
|
392
|
+
snapshotToObject(snapshot) {
|
|
393
|
+
const obj = {};
|
|
394
|
+
snapshot.forEach((childSnapshot) => {
|
|
395
|
+
obj[childSnapshot.key] = childSnapshot.val();
|
|
396
|
+
});
|
|
397
|
+
return obj;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Obtenir une nouvelle clé unique
|
|
402
|
+
*/
|
|
403
|
+
generateKey(path = '') {
|
|
404
|
+
return this.ref(path).push().key;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Obtenir le timestamp serveur
|
|
409
|
+
*/
|
|
410
|
+
getServerTimestamp() {
|
|
411
|
+
return firebase.database.ServerValue.TIMESTAMP;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Vérifier si un chemin existe
|
|
416
|
+
*/
|
|
417
|
+
async exists(path) {
|
|
418
|
+
const snapshot = await this.ref(path).once('value');
|
|
419
|
+
return snapshot.exists();
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Compter les enfants
|
|
424
|
+
*/
|
|
425
|
+
async count(path) {
|
|
426
|
+
const snapshot = await this.ref(path).once('value');
|
|
427
|
+
return snapshot.numChildren();
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Obtenir la priorité
|
|
432
|
+
*/
|
|
433
|
+
async getPriority(path) {
|
|
434
|
+
const snapshot = await this.ref(path).once('value');
|
|
435
|
+
return snapshot.getPriority();
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Définir la priorité
|
|
440
|
+
*/
|
|
441
|
+
async setPriority(path, priority) {
|
|
442
|
+
try {
|
|
443
|
+
await this.ref(path).setPriority(priority);
|
|
444
|
+
return { success: true };
|
|
445
|
+
} catch (error) {
|
|
446
|
+
console.error(`❌ Erreur setPriority ${path}:`, error);
|
|
447
|
+
throw error;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// ==================== SECURITY ====================
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Se connecter de manière anonyme
|
|
455
|
+
*/
|
|
456
|
+
async signInAnonymously() {
|
|
457
|
+
try {
|
|
458
|
+
const auth = this.core.getAuth();
|
|
459
|
+
await auth.signInAnonymously();
|
|
460
|
+
return { success: true };
|
|
461
|
+
} catch (error) {
|
|
462
|
+
console.error('❌ Erreur connexion anonyme:', error);
|
|
463
|
+
throw error;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Obtenir l'utilisateur actuel
|
|
469
|
+
*/
|
|
470
|
+
getCurrentUser() {
|
|
471
|
+
return this.core.getAuth().currentUser;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Déconnexion
|
|
476
|
+
*/
|
|
477
|
+
async signOut() {
|
|
478
|
+
try {
|
|
479
|
+
await this.core.getAuth().signOut();
|
|
480
|
+
return { success: true };
|
|
481
|
+
} catch (error) {
|
|
482
|
+
console.error('❌ Erreur déconnexion:', error);
|
|
483
|
+
throw error;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// ==================== CLEANUP ====================
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Nettoyer les ressources
|
|
491
|
+
*/
|
|
492
|
+
destroy() {
|
|
493
|
+
this.unlistenAll();
|
|
494
|
+
this.db = null;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
export default FirebaseRealtimeDB;
|