@umituz/web-firebase 1.0.5 → 2.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 (69) hide show
  1. package/README.md +555 -0
  2. package/dist/application/index.d.mts +273 -0
  3. package/dist/application/index.d.ts +273 -0
  4. package/dist/application/index.js +490 -0
  5. package/dist/application/index.mjs +19 -0
  6. package/dist/chunk-34DL2QWQ.mjs +87 -0
  7. package/dist/chunk-4FP2ELQ5.mjs +96 -0
  8. package/dist/chunk-7TX3OU3O.mjs +721 -0
  9. package/dist/chunk-I6WGBPFB.mjs +439 -0
  10. package/dist/chunk-RZ4QR6TB.mjs +96 -0
  11. package/dist/chunk-U2XI4MGO.mjs +397 -0
  12. package/dist/domain/index.d.mts +325 -0
  13. package/dist/domain/index.d.ts +325 -0
  14. package/dist/domain/index.js +662 -0
  15. package/dist/domain/index.mjs +36 -0
  16. package/dist/file.repository.interface-v5vHgVsZ.d.mts +241 -0
  17. package/dist/file.repository.interface-v5vHgVsZ.d.ts +241 -0
  18. package/dist/firebase.entity-xvfEPjXZ.d.mts +15 -0
  19. package/dist/firebase.entity-xvfEPjXZ.d.ts +15 -0
  20. package/dist/index.d.mts +14 -96
  21. package/dist/index.d.ts +14 -96
  22. package/dist/index.js +1717 -78
  23. package/dist/index.mjs +88 -175
  24. package/dist/infrastructure/index.d.mts +170 -0
  25. package/dist/infrastructure/index.d.ts +170 -0
  26. package/dist/infrastructure/index.js +856 -0
  27. package/dist/infrastructure/index.mjs +46 -0
  28. package/dist/presentation/index.d.mts +25 -0
  29. package/dist/presentation/index.d.ts +25 -0
  30. package/dist/presentation/index.js +105 -0
  31. package/dist/presentation/index.mjs +6 -0
  32. package/dist/user.repository.interface-DS74TsJ5.d.mts +298 -0
  33. package/dist/user.repository.interface-DS74TsJ5.d.ts +298 -0
  34. package/package.json +37 -11
  35. package/src/application/dto/auth.dto.ts +69 -0
  36. package/src/application/dto/index.ts +7 -0
  37. package/src/application/dto/user.dto.ts +64 -0
  38. package/src/application/index.ts +7 -0
  39. package/src/application/use-cases/auth/reset-password.use-case.ts +66 -0
  40. package/src/application/use-cases/auth/sign-in-with-google.use-case.ts +86 -0
  41. package/src/application/use-cases/auth/sign-in.use-case.ts +77 -0
  42. package/src/application/use-cases/auth/sign-out.use-case.ts +22 -0
  43. package/src/application/use-cases/auth/sign-up.use-case.ts +99 -0
  44. package/src/application/use-cases/index.ts +12 -0
  45. package/src/application/use-cases/user/delete-account.use-case.ts +77 -0
  46. package/src/application/use-cases/user/update-profile.use-case.ts +98 -0
  47. package/src/domain/entities/file.entity.ts +151 -0
  48. package/src/domain/entities/timestamp.entity.ts +116 -0
  49. package/src/domain/entities/user.entity.ts +193 -0
  50. package/src/domain/errors/auth.errors.ts +115 -0
  51. package/src/domain/errors/repository.errors.ts +121 -0
  52. package/src/domain/index.ts +25 -2
  53. package/src/domain/interfaces/auth.repository.interface.ts +83 -0
  54. package/src/domain/interfaces/file.repository.interface.ts +143 -0
  55. package/src/domain/interfaces/user.repository.interface.ts +75 -0
  56. package/src/domain/value-objects/email.vo.ts +105 -0
  57. package/src/domain/value-objects/file-path.vo.ts +184 -0
  58. package/src/domain/value-objects/user-id.vo.ts +87 -0
  59. package/src/index.ts +19 -4
  60. package/src/infrastructure/firebase/auth.adapter.ts +220 -0
  61. package/src/infrastructure/firebase/client.ts +141 -0
  62. package/src/infrastructure/firebase/firestore.adapter.ts +190 -0
  63. package/src/infrastructure/firebase/storage.adapter.ts +323 -0
  64. package/src/infrastructure/index.ts +10 -5
  65. package/src/infrastructure/utils/storage.util.ts +3 -3
  66. package/src/presentation/hooks/useAuth.ts +153 -0
  67. package/src/presentation/hooks/useFirestore.ts +125 -0
  68. package/src/presentation/hooks/useStorage.ts +141 -0
  69. package/src/presentation/providers/FirebaseProvider.tsx +40 -0
@@ -0,0 +1,721 @@
1
+ import {
2
+ createRepositoryError
3
+ } from "./chunk-4FP2ELQ5.mjs";
4
+ import {
5
+ createAuthError
6
+ } from "./chunk-RZ4QR6TB.mjs";
7
+
8
+ // src/infrastructure/firebase/client.ts
9
+ import { initializeApp, getApps } from "firebase/app";
10
+ import { initializeAuth, getAuth, browserLocalPersistence } from "firebase/auth";
11
+ import { getFirestore } from "firebase/firestore";
12
+ import { getStorage } from "firebase/storage";
13
+ import { getAnalytics } from "firebase/analytics";
14
+ import { getFunctions } from "firebase/functions";
15
+ var firebaseConfig = {
16
+ apiKey: process.env.VITE_FIREBASE_API_KEY,
17
+ authDomain: process.env.VITE_FIREBASE_AUTH_DOMAIN,
18
+ projectId: process.env.VITE_FIREBASE_PROJECT_ID,
19
+ storageBucket: process.env.VITE_FIREBASE_STORAGE_BUCKET,
20
+ messagingSenderId: process.env.VITE_FIREBASE_MESSAGING_SENDER_ID,
21
+ appId: process.env.VITE_FIREBASE_APP_ID,
22
+ measurementId: process.env.VITE_FIREBASE_MEASUREMENT_ID
23
+ };
24
+ var app;
25
+ var auth;
26
+ var db;
27
+ var storage;
28
+ var functions;
29
+ var analytics = null;
30
+ function initializeFirebase() {
31
+ if (!getApps().length) {
32
+ app = initializeApp(firebaseConfig);
33
+ } else {
34
+ app = getApps()[0];
35
+ }
36
+ return app;
37
+ }
38
+ function getFirebaseApp() {
39
+ return app || initializeFirebase();
40
+ }
41
+ function getFirebaseAuth() {
42
+ if (!auth) {
43
+ const firebaseApp = getFirebaseApp();
44
+ if (typeof window !== "undefined") {
45
+ try {
46
+ auth = getAuth(firebaseApp);
47
+ } catch (e) {
48
+ console.warn("getAuth failed, trying initializeAuth...", e);
49
+ auth = initializeAuth(firebaseApp, {
50
+ persistence: browserLocalPersistence
51
+ });
52
+ }
53
+ } else {
54
+ auth = getAuth(firebaseApp);
55
+ }
56
+ }
57
+ return auth;
58
+ }
59
+ function getFirebaseDB() {
60
+ if (!db) {
61
+ db = getFirestore(getFirebaseApp());
62
+ }
63
+ return db;
64
+ }
65
+ function getFirebaseStorage() {
66
+ if (!storage) {
67
+ storage = getStorage(getFirebaseApp());
68
+ }
69
+ return storage;
70
+ }
71
+ function getFirebaseFunctions() {
72
+ if (!functions) {
73
+ functions = getFunctions(getFirebaseApp());
74
+ }
75
+ return functions;
76
+ }
77
+ function getFirebaseAnalytics() {
78
+ if (!analytics && typeof window !== "undefined") {
79
+ analytics = getAnalytics(getFirebaseApp());
80
+ }
81
+ return analytics;
82
+ }
83
+ function getFirebaseInstances() {
84
+ return {
85
+ app: getFirebaseApp(),
86
+ auth: getFirebaseAuth(),
87
+ db: getFirebaseDB(),
88
+ storage: getFirebaseStorage(),
89
+ functions: getFirebaseFunctions(),
90
+ analytics: getFirebaseAnalytics()
91
+ };
92
+ }
93
+
94
+ // src/infrastructure/firebase/auth.adapter.ts
95
+ import {
96
+ signInWithEmailAndPassword,
97
+ signInWithPopup,
98
+ createUserWithEmailAndPassword,
99
+ signOut as firebaseSignOut,
100
+ sendPasswordResetEmail,
101
+ sendEmailVerification,
102
+ updateProfile as updateAuthProfile,
103
+ updateEmail as updateAuthEmail,
104
+ updatePassword as updateAuthPassword,
105
+ reauthenticateWithCredential,
106
+ EmailAuthProvider
107
+ } from "firebase/auth";
108
+ import { GoogleAuthProvider } from "firebase/auth";
109
+ var AuthAdapter = class {
110
+ get auth() {
111
+ return getFirebaseAuth();
112
+ }
113
+ // Authentication Methods
114
+ async signIn(email, password) {
115
+ try {
116
+ return await signInWithEmailAndPassword(this.auth, email, password);
117
+ } catch (error) {
118
+ throw this.handleAuthError(error);
119
+ }
120
+ }
121
+ async signUp(email, password, displayName) {
122
+ try {
123
+ const result = await createUserWithEmailAndPassword(this.auth, email, password);
124
+ await updateAuthProfile(result.user, { displayName });
125
+ await sendEmailVerification(result.user);
126
+ return result;
127
+ } catch (error) {
128
+ throw this.handleAuthError(error);
129
+ }
130
+ }
131
+ async signInWithGoogle() {
132
+ try {
133
+ const provider = new GoogleAuthProvider();
134
+ provider.addScope("profile");
135
+ provider.addScope("email");
136
+ return await signInWithPopup(this.auth, provider);
137
+ } catch (error) {
138
+ throw this.handleAuthError(error);
139
+ }
140
+ }
141
+ async signOut() {
142
+ try {
143
+ await firebaseSignOut(this.auth);
144
+ } catch (error) {
145
+ throw createAuthError("SIGN_OUT_FAILED" /* SIGN_OUT_FAILED */, "Sign out failed", error);
146
+ }
147
+ }
148
+ async sendPasswordReset(email) {
149
+ try {
150
+ await sendPasswordResetEmail(this.auth, email);
151
+ } catch (error) {
152
+ throw this.handleAuthError(error);
153
+ }
154
+ }
155
+ async resendEmailVerification() {
156
+ try {
157
+ const user = this.auth.currentUser;
158
+ if (!user) {
159
+ throw createAuthError("UNAUTHENTICATED" /* UNAUTHENTICATED */, "No user logged in");
160
+ }
161
+ await sendEmailVerification(user);
162
+ } catch (error) {
163
+ throw createAuthError("EMAIL_VERIFICATION_FAILED" /* EMAIL_VERIFICATION_FAILED */, "Failed to resend verification", error);
164
+ }
165
+ }
166
+ // Profile Management
167
+ async updateProfile(updates) {
168
+ try {
169
+ const user = this.auth.currentUser;
170
+ if (!user) {
171
+ throw createAuthError("UNAUTHENTICATED" /* UNAUTHENTICATED */, "No user logged in");
172
+ }
173
+ await updateAuthProfile(user, updates);
174
+ } catch (error) {
175
+ throw createAuthError("PROFILE_UPDATE_FAILED" /* PROFILE_UPDATE_FAILED */, "Profile update failed", error);
176
+ }
177
+ }
178
+ async updateEmail(newEmail, password) {
179
+ try {
180
+ const user = this.auth.currentUser;
181
+ if (!user || !user.email) {
182
+ throw createAuthError("UNAUTHENTICATED" /* UNAUTHENTICATED */, "No user logged in");
183
+ }
184
+ const credential = EmailAuthProvider.credential(user.email, password);
185
+ await reauthenticateWithCredential(user, credential);
186
+ await updateAuthEmail(user, newEmail);
187
+ } catch (error) {
188
+ throw createAuthError("EMAIL_UPDATE_FAILED" /* EMAIL_UPDATE_FAILED */, "Email update failed", error);
189
+ }
190
+ }
191
+ async updatePassword(currentPassword, newPassword) {
192
+ try {
193
+ const user = this.auth.currentUser;
194
+ if (!user || !user.email) {
195
+ throw createAuthError("UNAUTHENTICATED" /* UNAUTHENTICATED */, "No user logged in");
196
+ }
197
+ const credential = EmailAuthProvider.credential(user.email, currentPassword);
198
+ await reauthenticateWithCredential(user, credential);
199
+ await updateAuthPassword(user, newPassword);
200
+ } catch (error) {
201
+ throw createAuthError("PASSWORD_UPDATE_FAILED" /* PASSWORD_UPDATE_FAILED */, "Password update failed", error);
202
+ }
203
+ }
204
+ async deleteAccount(password) {
205
+ try {
206
+ const user = this.auth.currentUser;
207
+ if (!user || !user.email) {
208
+ throw createAuthError("UNAUTHENTICATED" /* UNAUTHENTICATED */, "No user logged in");
209
+ }
210
+ const credential = EmailAuthProvider.credential(user.email, password);
211
+ await reauthenticateWithCredential(user, credential);
212
+ await user.delete();
213
+ } catch (error) {
214
+ throw createAuthError("ACCOUNT_DELETE_FAILED" /* ACCOUNT_DELETE_FAILED */, "Account deletion failed", error);
215
+ }
216
+ }
217
+ // State Management
218
+ getCurrentUser() {
219
+ return this.auth.currentUser;
220
+ }
221
+ onAuthStateChanged(callback) {
222
+ return this.auth.onAuthStateChanged(callback);
223
+ }
224
+ // Note: User document operations should be handled by UserAdapter
225
+ // These methods are part of IAuthRepository interface but should be implemented separately
226
+ async createUserDocument(_userId, _data) {
227
+ throw new Error("createUserDocument should be handled by UserAdapter");
228
+ }
229
+ async updateLastLogin(_userId) {
230
+ throw new Error("updateLastLogin should be handled by UserAdapter");
231
+ }
232
+ /**
233
+ * Handle Firebase Auth errors
234
+ */
235
+ handleAuthError(error) {
236
+ if (error instanceof Error && "code" in error) {
237
+ const code = error.code;
238
+ switch (code) {
239
+ case "auth/user-not-found":
240
+ return createAuthError("USER_NOT_FOUND" /* USER_NOT_FOUND */, "User not found", error);
241
+ case "auth/wrong-password":
242
+ case "auth/invalid-credential":
243
+ return createAuthError("INVALID_CREDENTIALS" /* INVALID_CREDENTIALS */, "Invalid credentials", error);
244
+ case "auth/email-already-in-use":
245
+ return createAuthError("USER_ALREADY_EXISTS" /* USER_ALREADY_EXISTS */, "Email already in use", error);
246
+ case "auth/weak-password":
247
+ return createAuthError("WEAK_PASSWORD" /* WEAK_PASSWORD */, "Password is too weak", error);
248
+ case "auth/invalid-email":
249
+ return createAuthError("INVALID_CREDENTIALS" /* INVALID_CREDENTIALS */, "Invalid email", error);
250
+ case "auth/user-disabled":
251
+ return createAuthError("USER_NOT_FOUND" /* USER_NOT_FOUND */, "Account disabled", error);
252
+ case "auth/too-many-requests":
253
+ return createAuthError("TOO_MANY_REQUESTS" /* TOO_MANY_REQUESTS */, "Too many requests", error);
254
+ case "auth/popup-closed-by-user":
255
+ return createAuthError("OAUTH_CANCELLED" /* OAUTH_CANCELLED */, "Sign in cancelled", error);
256
+ case "auth/account-exists-with-different-credential":
257
+ return createAuthError("OAUTH_ACCOUNT_EXISTS" /* OAUTH_ACCOUNT_EXISTS */, "Account exists with different provider", error);
258
+ case "auth/requires-recent-login":
259
+ return createAuthError("REAUTHENTICATION_REQUIRED" /* REAUTHENTICATION_REQUIRED */, "Please reauthenticate", error);
260
+ default:
261
+ return createAuthError("UNKNOWN" /* UNKNOWN */, `Auth error: ${code}`, error);
262
+ }
263
+ }
264
+ return createAuthError("UNKNOWN" /* UNKNOWN */, "Unknown auth error", error);
265
+ }
266
+ };
267
+
268
+ // src/infrastructure/firebase/firestore.adapter.ts
269
+ import {
270
+ doc,
271
+ getDoc,
272
+ setDoc,
273
+ updateDoc,
274
+ deleteDoc,
275
+ query,
276
+ where,
277
+ onSnapshot,
278
+ collection,
279
+ getDocs
280
+ } from "firebase/firestore";
281
+ var FirestoreAdapter = class {
282
+ get db() {
283
+ return getFirebaseDB();
284
+ }
285
+ USERS_COLLECTION = "users";
286
+ async getUser(userId) {
287
+ try {
288
+ const docRef = doc(this.db, this.USERS_COLLECTION, userId);
289
+ const snap = await getDoc(docRef);
290
+ if (!snap.exists()) {
291
+ return null;
292
+ }
293
+ return snap.data();
294
+ } catch (error) {
295
+ throw createRepositoryError("DOCUMENT_NOT_FOUND" /* DOCUMENT_NOT_FOUND */, "User not found", error);
296
+ }
297
+ }
298
+ async getUserByEmail(email) {
299
+ try {
300
+ const q = query(collection(this.db, this.USERS_COLLECTION), where("profile.email", "==", email));
301
+ const snap = await getDocs(q);
302
+ if (snap.empty) {
303
+ return null;
304
+ }
305
+ const doc2 = snap.docs[0];
306
+ return doc2.data();
307
+ } catch (error) {
308
+ throw createRepositoryError("QUERY_FAILED" /* QUERY_FAILED */, "Failed to query user", error);
309
+ }
310
+ }
311
+ async createUser(userId, data) {
312
+ try {
313
+ const docRef = doc(this.db, this.USERS_COLLECTION, userId);
314
+ await setDoc(docRef, data, { merge: true });
315
+ } catch (error) {
316
+ throw createRepositoryError("DOCUMENT_INVALID" /* DOCUMENT_INVALID */, "Failed to create user", error);
317
+ }
318
+ }
319
+ async updateUser(userId, data) {
320
+ try {
321
+ const docRef = doc(this.db, this.USERS_COLLECTION, userId);
322
+ await updateDoc(docRef, {
323
+ ...data,
324
+ "profile.updatedAt": Date.now()
325
+ });
326
+ } catch (error) {
327
+ throw createRepositoryError("DOCUMENT_NOT_FOUND" /* DOCUMENT_NOT_FOUND */, "Failed to update user", error);
328
+ }
329
+ }
330
+ async deleteUser(userId) {
331
+ try {
332
+ const docRef = doc(this.db, this.USERS_COLLECTION, userId);
333
+ await deleteDoc(docRef);
334
+ } catch (error) {
335
+ throw createRepositoryError("DOCUMENT_NOT_FOUND" /* DOCUMENT_NOT_FOUND */, "Failed to delete user", error);
336
+ }
337
+ }
338
+ async updateProfile(userId, updates) {
339
+ try {
340
+ const docRef = doc(this.db, this.USERS_COLLECTION, userId);
341
+ const updateData = {
342
+ "profile.updatedAt": Date.now()
343
+ };
344
+ if (updates.displayName !== void 0) {
345
+ updateData["profile.displayName"] = updates.displayName;
346
+ }
347
+ if (updates.photoURL !== void 0) {
348
+ updateData["profile.photoURL"] = updates.photoURL;
349
+ }
350
+ if (updates.phoneNumber !== void 0) {
351
+ updateData["profile.phoneNumber"] = updates.phoneNumber;
352
+ }
353
+ await updateDoc(docRef, updateData);
354
+ } catch (error) {
355
+ throw createRepositoryError("DOCUMENT_NOT_FOUND" /* DOCUMENT_NOT_FOUND */, "Failed to update profile", error);
356
+ }
357
+ }
358
+ async updateSettings(userId, settings) {
359
+ try {
360
+ const docRef = doc(this.db, this.USERS_COLLECTION, userId);
361
+ await updateDoc(docRef, {
362
+ settings: {
363
+ ...settings,
364
+ updatedAt: Date.now()
365
+ }
366
+ });
367
+ } catch (error) {
368
+ throw createRepositoryError("DOCUMENT_NOT_FOUND" /* DOCUMENT_NOT_FOUND */, "Failed to update settings", error);
369
+ }
370
+ }
371
+ async updateSubscription(userId, subscription) {
372
+ try {
373
+ const docRef = doc(this.db, this.USERS_COLLECTION, userId);
374
+ await updateDoc(docRef, {
375
+ subscription: {
376
+ ...subscription,
377
+ updatedAt: Date.now()
378
+ }
379
+ });
380
+ } catch (error) {
381
+ throw createRepositoryError("DOCUMENT_NOT_FOUND" /* DOCUMENT_NOT_FOUND */, "Failed to update subscription", error);
382
+ }
383
+ }
384
+ async updateLastLogin(userId) {
385
+ try {
386
+ const docRef = doc(this.db, this.USERS_COLLECTION, userId);
387
+ await updateDoc(docRef, {
388
+ "profile.lastLoginAt": Date.now()
389
+ });
390
+ } catch (error) {
391
+ throw createRepositoryError("DOCUMENT_NOT_FOUND" /* DOCUMENT_NOT_FOUND */, "Failed to update last login", error);
392
+ }
393
+ }
394
+ async queryUsers(constraints) {
395
+ try {
396
+ const q = query(collection(this.db, this.USERS_COLLECTION), ...constraints);
397
+ const snap = await getDocs(q);
398
+ return snap.docs.map((doc2) => doc2.data());
399
+ } catch (error) {
400
+ throw createRepositoryError("QUERY_FAILED" /* QUERY_FAILED */, "Failed to query users", error);
401
+ }
402
+ }
403
+ subscribeToUser(userId, callback, onError) {
404
+ const docRef = doc(this.db, this.USERS_COLLECTION, userId);
405
+ const unsubscribe = onSnapshot(
406
+ docRef,
407
+ (snap) => {
408
+ if (snap.exists()) {
409
+ callback(snap.data());
410
+ } else {
411
+ callback(null);
412
+ }
413
+ },
414
+ (error) => {
415
+ onError?.(error);
416
+ }
417
+ );
418
+ return unsubscribe;
419
+ }
420
+ };
421
+
422
+ // src/infrastructure/firebase/storage.adapter.ts
423
+ import {
424
+ ref,
425
+ uploadBytes,
426
+ uploadBytesResumable,
427
+ getDownloadURL,
428
+ deleteObject,
429
+ listAll,
430
+ getMetadata
431
+ } from "firebase/storage";
432
+ var StorageAdapter = class {
433
+ get storage() {
434
+ return getFirebaseStorage();
435
+ }
436
+ // Upload Methods
437
+ async uploadFile(userId, path, file, options) {
438
+ const storageRef = ref(this.storage, `users/${userId}/${path}`);
439
+ const uploadTask = uploadBytesResumable(storageRef, file);
440
+ return new Promise((resolve, reject) => {
441
+ uploadTask.on(
442
+ "state_changed",
443
+ (snapshot) => {
444
+ if (options?.onProgress) {
445
+ const progress = {
446
+ bytesTransferred: snapshot.bytesTransferred,
447
+ totalBytes: snapshot.totalBytes,
448
+ progress: snapshot.bytesTransferred / snapshot.totalBytes * 100,
449
+ state: "running"
450
+ };
451
+ options.onProgress(progress);
452
+ }
453
+ },
454
+ (error) => reject(createRepositoryError("STORAGE_ERROR" /* STORAGE_ERROR */, "Upload failed", error)),
455
+ async () => {
456
+ const downloadURL = await getDownloadURL(uploadTask.snapshot.ref);
457
+ const metadata = await getMetadata(uploadTask.snapshot.ref);
458
+ resolve({
459
+ id: uploadTask.snapshot.ref.name,
460
+ name: metadata.name || uploadTask.snapshot.ref.name,
461
+ fullPath: metadata.fullPath || uploadTask.snapshot.ref.fullPath,
462
+ downloadURL,
463
+ contentType: metadata.contentType || "",
464
+ size: metadata.size || 0,
465
+ createdAt: metadata.timeCreated ? new Date(metadata.timeCreated).getTime() : Date.now()
466
+ });
467
+ }
468
+ );
469
+ });
470
+ }
471
+ async uploadImage(userId, file, filename) {
472
+ const name = filename || `${Date.now()}_${file.name}`;
473
+ return this.uploadFile(userId, `images/${name}`, file);
474
+ }
475
+ async uploadVideo(userId, file, filename) {
476
+ const name = filename || `${Date.now()}_${file.name}`;
477
+ return this.uploadFile(userId, `videos/${name}`, file);
478
+ }
479
+ async uploadDocument(userId, file, filename) {
480
+ const name = filename || `${Date.now()}_${file.name}`;
481
+ return this.uploadFile(userId, `documents/${name}`, file);
482
+ }
483
+ async uploadProfilePicture(userId, file) {
484
+ const storageRef = ref(this.storage, `users/${userId}/profile/${Date.now()}_${file.name}`);
485
+ await uploadBytes(storageRef, file);
486
+ const downloadURL = await getDownloadURL(storageRef);
487
+ return {
488
+ id: storageRef.name,
489
+ name: file.name,
490
+ fullPath: storageRef.fullPath,
491
+ downloadURL,
492
+ contentType: file.type,
493
+ size: file.size,
494
+ createdAt: Date.now()
495
+ };
496
+ }
497
+ // Download Methods
498
+ async getDownloadURL(path) {
499
+ try {
500
+ const storageRef = ref(this.storage, path);
501
+ return await getDownloadURL(storageRef);
502
+ } catch (error) {
503
+ throw createRepositoryError("FILE_NOT_FOUND" /* FILE_NOT_FOUND */, "File not found", error);
504
+ }
505
+ }
506
+ // Delete Methods
507
+ async deleteFile(path) {
508
+ try {
509
+ const storageRef = ref(this.storage, path);
510
+ await deleteObject(storageRef);
511
+ } catch (error) {
512
+ throw createRepositoryError("FILE_NOT_FOUND" /* FILE_NOT_FOUND */, "File not found", error);
513
+ }
514
+ }
515
+ async deleteUserFiles(userId) {
516
+ try {
517
+ const userRef = ref(this.storage, `users/${userId}`);
518
+ const result = await listAll(userRef);
519
+ for (const prefix of result.prefixes) {
520
+ const prefixResult = await listAll(prefix);
521
+ await Promise.all(prefixResult.items.map((item) => deleteObject(item)));
522
+ }
523
+ await Promise.all(result.items.map((item) => deleteObject(item)));
524
+ } catch (error) {
525
+ throw createRepositoryError("STORAGE_ERROR" /* STORAGE_ERROR */, "Failed to delete user files", error);
526
+ }
527
+ }
528
+ async deleteImage(userId, filename) {
529
+ await this.deleteFile(`users/${userId}/images/${filename}`);
530
+ }
531
+ async deleteVideo(userId, filename) {
532
+ await this.deleteFile(`users/${userId}/videos/${filename}`);
533
+ }
534
+ async deleteProfilePicture(userId, filename) {
535
+ await this.deleteFile(`users/${userId}/profile/${filename}`);
536
+ }
537
+ // List Methods
538
+ async listUserFiles(userId, path) {
539
+ const userRef = ref(this.storage, path ? `users/${userId}/${path}` : `users/${userId}`);
540
+ const result = await listAll(userRef);
541
+ const urls = await Promise.all(result.items.map((item) => getDownloadURL(item)));
542
+ return urls;
543
+ }
544
+ async listUserImages(userId) {
545
+ return this.listUserFiles(userId, "images");
546
+ }
547
+ async listUserVideos(userId) {
548
+ return this.listUserFiles(userId, "videos");
549
+ }
550
+ // Metadata
551
+ async getFileMetadata(path) {
552
+ try {
553
+ const storageRef = ref(this.storage, path);
554
+ const metadata = await getMetadata(storageRef);
555
+ return {
556
+ id: storageRef.name,
557
+ name: metadata.name,
558
+ fullPath: metadata.fullPath,
559
+ contentType: metadata.contentType || "application/octet-stream",
560
+ size: metadata.size,
561
+ createdAt: metadata.timeCreated ? new Date(metadata.timeCreated).getTime() : Date.now(),
562
+ updatedAt: metadata.updated ? new Date(metadata.updated).getTime() : Date.now(),
563
+ userId: this.extractUserId(path) || "unknown",
564
+ type: this.extractFileType(metadata.contentType || "")
565
+ };
566
+ } catch (error) {
567
+ throw createRepositoryError("FILE_NOT_FOUND" /* FILE_NOT_FOUND */, "File not found", error);
568
+ }
569
+ }
570
+ async queryFiles(userId, _filters) {
571
+ const userRef = ref(this.storage, `users/${userId}`);
572
+ const result = await listAll(userRef);
573
+ const files = await Promise.all(
574
+ result.items.map(async (item) => {
575
+ const metadata = await getMetadata(item);
576
+ return {
577
+ id: item.name,
578
+ name: metadata.name,
579
+ fullPath: metadata.fullPath,
580
+ contentType: metadata.contentType || "application/octet-stream",
581
+ size: metadata.size,
582
+ createdAt: metadata.timeCreated ? new Date(metadata.timeCreated).getTime() : Date.now(),
583
+ updatedAt: metadata.updated ? new Date(metadata.updated).getTime() : Date.now(),
584
+ userId,
585
+ type: this.extractFileType(metadata.contentType || "")
586
+ };
587
+ })
588
+ );
589
+ return {
590
+ files,
591
+ totalCount: files.length,
592
+ hasMore: false
593
+ };
594
+ }
595
+ async getStorageStats(userId) {
596
+ const { files, totalCount } = await this.queryFiles(userId);
597
+ const stats = {
598
+ totalFiles: totalCount,
599
+ totalSize: files.reduce((sum, file) => sum + file.size, 0),
600
+ filesByType: {
601
+ image: 0,
602
+ video: 0,
603
+ audio: 0,
604
+ document: 0,
605
+ other: 0
606
+ },
607
+ filesByCategory: {
608
+ profile: 0,
609
+ content: 0,
610
+ document: 0,
611
+ attachment: 0,
612
+ backup: 0
613
+ }
614
+ };
615
+ files.forEach((file) => {
616
+ stats.filesByType[file.type]++;
617
+ stats.lastUploadAt = Math.max(stats.lastUploadAt || 0, file.createdAt);
618
+ });
619
+ return stats;
620
+ }
621
+ // Validation
622
+ validateFile(file, options) {
623
+ const maxSizeBytes = options?.maxSizeBytes || (options?.maxSizeMB ? options.maxSizeMB * 1024 * 1024 : 10 * 1024 * 1024);
624
+ if (file.size > maxSizeBytes) {
625
+ return false;
626
+ }
627
+ if (options?.allowedTypes && !options.allowedTypes.includes(file.type)) {
628
+ return false;
629
+ }
630
+ return true;
631
+ }
632
+ isImageFile(file) {
633
+ return file.type.startsWith("image/");
634
+ }
635
+ isVideoFile(file) {
636
+ return file.type.startsWith("video/");
637
+ }
638
+ isDocumentFile(file) {
639
+ const docTypes = [
640
+ "application/pdf",
641
+ "application/msword",
642
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
643
+ "application/vnd.ms-excel",
644
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
645
+ "text/plain"
646
+ ];
647
+ return docTypes.includes(file.type);
648
+ }
649
+ // Utility Methods
650
+ generateUniqueFilename(originalName) {
651
+ const timestamp = Date.now();
652
+ const random = Math.random().toString(36).substring(2, 8);
653
+ const extension = this.getFileExtension(originalName);
654
+ return `${timestamp}_${random}.${extension}`;
655
+ }
656
+ getFileExtension(filename) {
657
+ return filename.slice((filename.lastIndexOf(".") - 1 >>> 0) + 2);
658
+ }
659
+ // Helper Methods
660
+ extractUserId(path) {
661
+ const match = path.match(/users\/([^\/]+)/);
662
+ return match ? match[1] : "";
663
+ }
664
+ extractFileType(contentType) {
665
+ if (contentType.startsWith("image/")) return "image";
666
+ if (contentType.startsWith("video/")) return "video";
667
+ if (contentType.startsWith("audio/")) return "audio";
668
+ if (contentType.includes("pdf") || contentType.includes("document") || contentType.includes("text")) {
669
+ return "document";
670
+ }
671
+ return "other";
672
+ }
673
+ };
674
+
675
+ // src/infrastructure/utils/storage.util.ts
676
+ import {
677
+ ref as ref2,
678
+ uploadBytes as uploadBytes2,
679
+ uploadString,
680
+ getDownloadURL as getDownloadURL2,
681
+ deleteObject as deleteObject2
682
+ } from "firebase/storage";
683
+ async function uploadFile(storage2, path, file) {
684
+ const storageRef = ref2(storage2, path);
685
+ await uploadBytes2(storageRef, file);
686
+ const url = await getDownloadURL2(storageRef);
687
+ return { url, path };
688
+ }
689
+ async function uploadBase64(storage2, path, base64, mimeType = "image/jpeg") {
690
+ const storageRef = ref2(storage2, path);
691
+ const dataUrl = base64.startsWith("data:") ? base64 : `data:${mimeType};base64,${base64}`;
692
+ await uploadString(storageRef, dataUrl, "data_url");
693
+ const url = await getDownloadURL2(storageRef);
694
+ return { url, path };
695
+ }
696
+ async function deleteFile(storage2, path) {
697
+ await deleteObject2(ref2(storage2, path));
698
+ }
699
+
700
+ export {
701
+ app,
702
+ auth,
703
+ db,
704
+ storage,
705
+ functions,
706
+ analytics,
707
+ initializeFirebase,
708
+ getFirebaseApp,
709
+ getFirebaseAuth,
710
+ getFirebaseDB,
711
+ getFirebaseStorage,
712
+ getFirebaseFunctions,
713
+ getFirebaseAnalytics,
714
+ getFirebaseInstances,
715
+ AuthAdapter,
716
+ FirestoreAdapter,
717
+ StorageAdapter,
718
+ uploadFile,
719
+ uploadBase64,
720
+ deleteFile
721
+ };