@umituz/web-firebase 1.0.4 → 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.
- package/README.md +555 -0
- package/dist/application/index.d.mts +273 -0
- package/dist/application/index.d.ts +273 -0
- package/dist/application/index.js +490 -0
- package/dist/application/index.mjs +19 -0
- package/dist/chunk-34DL2QWQ.mjs +87 -0
- package/dist/chunk-4FP2ELQ5.mjs +96 -0
- package/dist/chunk-7TX3OU3O.mjs +721 -0
- package/dist/chunk-I6WGBPFB.mjs +439 -0
- package/dist/chunk-RZ4QR6TB.mjs +96 -0
- package/dist/chunk-U2XI4MGO.mjs +397 -0
- package/dist/domain/index.d.mts +325 -0
- package/dist/domain/index.d.ts +325 -0
- package/dist/domain/index.js +662 -0
- package/dist/domain/index.mjs +36 -0
- package/dist/file.repository.interface-v5vHgVsZ.d.mts +241 -0
- package/dist/file.repository.interface-v5vHgVsZ.d.ts +241 -0
- package/dist/firebase.entity-xvfEPjXZ.d.mts +15 -0
- package/dist/firebase.entity-xvfEPjXZ.d.ts +15 -0
- package/dist/index.d.mts +14 -96
- package/dist/index.d.ts +14 -96
- package/dist/index.js +1717 -78
- package/dist/index.mjs +88 -175
- package/dist/infrastructure/index.d.mts +170 -0
- package/dist/infrastructure/index.d.ts +170 -0
- package/dist/infrastructure/index.js +856 -0
- package/dist/infrastructure/index.mjs +46 -0
- package/dist/presentation/index.d.mts +25 -0
- package/dist/presentation/index.d.ts +25 -0
- package/dist/presentation/index.js +105 -0
- package/dist/presentation/index.mjs +6 -0
- package/dist/user.repository.interface-DS74TsJ5.d.mts +298 -0
- package/dist/user.repository.interface-DS74TsJ5.d.ts +298 -0
- package/package.json +39 -12
- package/src/application/dto/auth.dto.ts +69 -0
- package/src/application/dto/index.ts +7 -0
- package/src/application/dto/user.dto.ts +64 -0
- package/src/application/index.ts +7 -0
- package/src/application/use-cases/auth/reset-password.use-case.ts +66 -0
- package/src/application/use-cases/auth/sign-in-with-google.use-case.ts +86 -0
- package/src/application/use-cases/auth/sign-in.use-case.ts +77 -0
- package/src/application/use-cases/auth/sign-out.use-case.ts +22 -0
- package/src/application/use-cases/auth/sign-up.use-case.ts +99 -0
- package/src/application/use-cases/index.ts +12 -0
- package/src/application/use-cases/user/delete-account.use-case.ts +77 -0
- package/src/application/use-cases/user/update-profile.use-case.ts +98 -0
- package/src/domain/entities/file.entity.ts +151 -0
- package/src/domain/entities/firebase.entity.ts +13 -0
- package/src/domain/entities/timestamp.entity.ts +116 -0
- package/src/domain/entities/user.entity.ts +193 -0
- package/src/domain/errors/auth.errors.ts +115 -0
- package/src/domain/errors/repository.errors.ts +121 -0
- package/src/domain/index.ts +30 -0
- package/src/domain/interfaces/auth.repository.interface.ts +83 -0
- package/src/domain/interfaces/file.repository.interface.ts +143 -0
- package/src/domain/interfaces/repository.interface.ts +11 -0
- package/src/domain/interfaces/user.repository.interface.ts +75 -0
- package/src/domain/value-objects/email.vo.ts +105 -0
- package/src/domain/value-objects/file-path.vo.ts +184 -0
- package/src/domain/value-objects/user-id.vo.ts +87 -0
- package/src/index.ts +23 -0
- package/src/infrastructure/firebase/auth.adapter.ts +220 -0
- package/src/infrastructure/firebase/client.ts +141 -0
- package/src/infrastructure/firebase/firestore.adapter.ts +190 -0
- package/src/infrastructure/firebase/storage.adapter.ts +323 -0
- package/src/infrastructure/index.ts +13 -0
- package/src/infrastructure/services/firebase.service.ts +30 -0
- package/src/infrastructure/services/firestore.repository.ts +66 -0
- package/src/infrastructure/utils/storage.util.ts +46 -0
- package/src/presentation/hooks/useAuth.ts +153 -0
- package/src/presentation/hooks/useFirebaseAuth.ts +122 -0
- package/src/presentation/hooks/useFirestore.ts +125 -0
- package/src/presentation/hooks/useStorage.ts +141 -0
- package/src/presentation/index.ts +6 -0
- package/src/presentation/providers/FirebaseProvider.tsx +40 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User Domain Entities
|
|
3
|
+
* @description Core user-related entities following DDD principles
|
|
4
|
+
* Migrated from: /Users/umituz/Desktop/github/umituz/apps/web/app-growth-factory/src/domains/firebase/types/index.ts
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* User Profile Value Object
|
|
9
|
+
* Immutable user profile data
|
|
10
|
+
*/
|
|
11
|
+
export interface UserProfile {
|
|
12
|
+
readonly id: string
|
|
13
|
+
readonly email: string
|
|
14
|
+
readonly displayName: string
|
|
15
|
+
readonly photoURL?: string
|
|
16
|
+
readonly phoneNumber?: string
|
|
17
|
+
readonly createdAt: number
|
|
18
|
+
readonly updatedAt: number
|
|
19
|
+
readonly lastLoginAt: number
|
|
20
|
+
readonly emailVerified: boolean
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* User Notifications Settings
|
|
25
|
+
*/
|
|
26
|
+
export interface UserNotifications {
|
|
27
|
+
email: boolean
|
|
28
|
+
push: boolean
|
|
29
|
+
marketing: boolean
|
|
30
|
+
security: boolean
|
|
31
|
+
weeklyDigest: boolean
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* User Privacy Settings
|
|
36
|
+
*/
|
|
37
|
+
export interface UserPrivacy {
|
|
38
|
+
profileVisibility: 'public' | 'private'
|
|
39
|
+
showEmail: boolean
|
|
40
|
+
showPhone: boolean
|
|
41
|
+
dataSharing: boolean
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* User Settings Value Object
|
|
46
|
+
*/
|
|
47
|
+
export interface UserSettings {
|
|
48
|
+
theme: 'light' | 'dark' | 'system'
|
|
49
|
+
language: string
|
|
50
|
+
timezone: string
|
|
51
|
+
currency: string
|
|
52
|
+
notifications: UserNotifications
|
|
53
|
+
privacy: UserPrivacy
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* User Subscription Value Object
|
|
58
|
+
*/
|
|
59
|
+
export interface UserSubscription {
|
|
60
|
+
plan: 'free' | 'standard' | 'professional' | 'business'
|
|
61
|
+
status: 'active' | 'inactive' | 'canceled' | 'past_due'
|
|
62
|
+
polarCustomerId?: string
|
|
63
|
+
polarSubscriptionId?: string
|
|
64
|
+
currentPeriodStart?: number
|
|
65
|
+
currentPeriodEnd?: number
|
|
66
|
+
cancelAtPeriodEnd: boolean
|
|
67
|
+
readonly createdAt: number
|
|
68
|
+
readonly updatedAt: number
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Account Metrics Value Object
|
|
73
|
+
*/
|
|
74
|
+
export interface AccountMetrics {
|
|
75
|
+
followers: number
|
|
76
|
+
following: number
|
|
77
|
+
posts: number
|
|
78
|
+
engagement: number
|
|
79
|
+
lastSyncedAt: number
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* User Connected Account Entity
|
|
84
|
+
*/
|
|
85
|
+
export interface UserConnectedAccount {
|
|
86
|
+
platform:
|
|
87
|
+
| 'twitter'
|
|
88
|
+
| 'facebook'
|
|
89
|
+
| 'instagram'
|
|
90
|
+
| 'linkedin'
|
|
91
|
+
| 'tiktok'
|
|
92
|
+
| 'youtube'
|
|
93
|
+
| 'pinterest'
|
|
94
|
+
| 'reddit'
|
|
95
|
+
| 'threads'
|
|
96
|
+
| 'discord'
|
|
97
|
+
| 'telegram'
|
|
98
|
+
| 'mastodon'
|
|
99
|
+
| 'medium'
|
|
100
|
+
connected: boolean
|
|
101
|
+
readonly connectedAt: number
|
|
102
|
+
username?: string
|
|
103
|
+
profileId?: string
|
|
104
|
+
accessToken?: string
|
|
105
|
+
refreshToken?: string
|
|
106
|
+
tokenExpiresAt?: number
|
|
107
|
+
metrics?: AccountMetrics
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* User Content Entity
|
|
112
|
+
*/
|
|
113
|
+
export interface UserContent {
|
|
114
|
+
readonly id: string
|
|
115
|
+
type: 'post' | 'article' | 'video' | 'image' | 'story' | 'reel'
|
|
116
|
+
title: string
|
|
117
|
+
content: string
|
|
118
|
+
platforms: string[]
|
|
119
|
+
status: 'draft' | 'scheduled' | 'published' | 'failed'
|
|
120
|
+
scheduledFor?: number
|
|
121
|
+
publishedAt?: number
|
|
122
|
+
readonly createdAt: number
|
|
123
|
+
readonly updatedAt: number
|
|
124
|
+
mediaUrls?: string[]
|
|
125
|
+
metadata?: Record<string, unknown>
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Platform Breakdown for Analytics
|
|
130
|
+
*/
|
|
131
|
+
export interface PlatformBreakdown {
|
|
132
|
+
[platform: string]: {
|
|
133
|
+
posts: number
|
|
134
|
+
engagement: number
|
|
135
|
+
reach: number
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* User Analytics Value Object
|
|
141
|
+
*/
|
|
142
|
+
export interface UserAnalytics {
|
|
143
|
+
totalPosts: number
|
|
144
|
+
totalEngagement: number
|
|
145
|
+
totalReach: number
|
|
146
|
+
topPerformingPosts: string[] // content IDs
|
|
147
|
+
platformBreakdown: PlatformBreakdown
|
|
148
|
+
lastCalculatedAt: number
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* User Credits Value Object
|
|
153
|
+
*/
|
|
154
|
+
export interface UserCredits {
|
|
155
|
+
standard: number
|
|
156
|
+
professional: number
|
|
157
|
+
total: number
|
|
158
|
+
resetAt: number
|
|
159
|
+
lastResetAt: number
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* User Aggregate Root
|
|
164
|
+
* Main user document containing all user-related data
|
|
165
|
+
* This is the aggregate root for the User domain
|
|
166
|
+
*/
|
|
167
|
+
export interface User {
|
|
168
|
+
readonly profile: UserProfile
|
|
169
|
+
settings: UserSettings
|
|
170
|
+
subscription: UserSubscription
|
|
171
|
+
connectedAccounts: UserConnectedAccount[]
|
|
172
|
+
content: UserContent[]
|
|
173
|
+
analytics: UserAnalytics
|
|
174
|
+
credits: UserCredits
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Collection Constants
|
|
179
|
+
*/
|
|
180
|
+
export const USER_COLLECTIONS = {
|
|
181
|
+
USERS: 'users',
|
|
182
|
+
CONTENT: 'content',
|
|
183
|
+
ANALYTICS: 'analytics',
|
|
184
|
+
CONNECTED_ACCOUNTS: 'connectedAccounts',
|
|
185
|
+
} as const
|
|
186
|
+
|
|
187
|
+
export const USER_SUBCOLLECTIONS = {
|
|
188
|
+
CONTENT: 'content',
|
|
189
|
+
ANALYTICS: 'analytics',
|
|
190
|
+
SCHEDULED: 'scheduled',
|
|
191
|
+
PUBLISHED: 'published',
|
|
192
|
+
DRAFTS: 'drafts',
|
|
193
|
+
} as const
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain Errors for Authentication
|
|
3
|
+
* @description Custom error types for authentication-related failures
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export class AuthError extends Error {
|
|
7
|
+
constructor(
|
|
8
|
+
message: string,
|
|
9
|
+
public code: AuthErrorCode,
|
|
10
|
+
public originalError?: unknown
|
|
11
|
+
) {
|
|
12
|
+
super(message)
|
|
13
|
+
this.name = 'AuthError'
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export enum AuthErrorCode {
|
|
18
|
+
// Auth Errors
|
|
19
|
+
USER_NOT_FOUND = 'USER_NOT_FOUND',
|
|
20
|
+
USER_ALREADY_EXISTS = 'USER_ALREADY_EXISTS',
|
|
21
|
+
INVALID_CREDENTIALS = 'INVALID_CREDENTIALS',
|
|
22
|
+
WEAK_PASSWORD = 'WEAK_PASSWORD',
|
|
23
|
+
EMAIL_NOT_VERIFIED = 'EMAIL_NOT_VERIFIED',
|
|
24
|
+
|
|
25
|
+
// Session Errors
|
|
26
|
+
SESSION_EXPIRED = 'SESSION_EXPIRED',
|
|
27
|
+
UNAUTHENTICATED = 'UNAUTHENTICATED',
|
|
28
|
+
TOO_MANY_REQUESTS = 'TOO_MANY_REQUESTS',
|
|
29
|
+
|
|
30
|
+
// Provider Errors
|
|
31
|
+
OAUTH_ERROR = 'OAUTH_ERROR',
|
|
32
|
+
OAUTH_CANCELLED = 'OAUTH_CANCELLED',
|
|
33
|
+
OAUTH_ACCOUNT_EXISTS = 'OAUTH_ACCOUNT_EXISTS',
|
|
34
|
+
|
|
35
|
+
// Operation Errors
|
|
36
|
+
SIGN_IN_FAILED = 'SIGN_IN_FAILED',
|
|
37
|
+
SIGN_UP_FAILED = 'SIGN_UP_FAILED',
|
|
38
|
+
SIGN_OUT_FAILED = 'SIGN_OUT_FAILED',
|
|
39
|
+
PASSWORD_RESET_FAILED = 'PASSWORD_RESET_FAILED',
|
|
40
|
+
EMAIL_VERIFICATION_FAILED = 'EMAIL_VERIFICATION_FAILED',
|
|
41
|
+
|
|
42
|
+
// Profile Errors
|
|
43
|
+
PROFILE_UPDATE_FAILED = 'PROFILE_UPDATE_FAILED',
|
|
44
|
+
EMAIL_UPDATE_FAILED = 'EMAIL_UPDATE_FAILED',
|
|
45
|
+
PASSWORD_UPDATE_FAILED = 'PASSWORD_UPDATE_FAILED',
|
|
46
|
+
ACCOUNT_DELETE_FAILED = 'ACCOUNT_DELETE_FAILED',
|
|
47
|
+
|
|
48
|
+
// Reauthentication Errors
|
|
49
|
+
REAUTHENTICATION_REQUIRED = 'REAUTHENTICATION_REQUIRED',
|
|
50
|
+
REAUTHENTICATION_FAILED = 'REAUTHENTICATION_FAILED',
|
|
51
|
+
|
|
52
|
+
// Unknown Error
|
|
53
|
+
UNKNOWN = 'UNKNOWN',
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function createAuthError(
|
|
57
|
+
code: AuthErrorCode,
|
|
58
|
+
message?: string,
|
|
59
|
+
originalError?: unknown
|
|
60
|
+
): AuthError {
|
|
61
|
+
const defaultMessage = getAuthErrorMessage(code)
|
|
62
|
+
return new AuthError(message || defaultMessage, code, originalError)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function getAuthErrorMessage(code: AuthErrorCode): string {
|
|
66
|
+
switch (code) {
|
|
67
|
+
case AuthErrorCode.USER_NOT_FOUND:
|
|
68
|
+
return 'User not found'
|
|
69
|
+
case AuthErrorCode.USER_ALREADY_EXISTS:
|
|
70
|
+
return 'User already exists'
|
|
71
|
+
case AuthErrorCode.INVALID_CREDENTIALS:
|
|
72
|
+
return 'Invalid credentials'
|
|
73
|
+
case AuthErrorCode.WEAK_PASSWORD:
|
|
74
|
+
return 'Password is too weak'
|
|
75
|
+
case AuthErrorCode.EMAIL_NOT_VERIFIED:
|
|
76
|
+
return 'Email not verified'
|
|
77
|
+
case AuthErrorCode.SESSION_EXPIRED:
|
|
78
|
+
return 'Session expired'
|
|
79
|
+
case AuthErrorCode.UNAUTHENTICATED:
|
|
80
|
+
return 'User not authenticated'
|
|
81
|
+
case AuthErrorCode.TOO_MANY_REQUESTS:
|
|
82
|
+
return 'Too many requests'
|
|
83
|
+
case AuthErrorCode.OAUTH_ERROR:
|
|
84
|
+
return 'OAuth error occurred'
|
|
85
|
+
case AuthErrorCode.OAUTH_CANCELLED:
|
|
86
|
+
return 'OAuth cancelled by user'
|
|
87
|
+
case AuthErrorCode.OAUTH_ACCOUNT_EXISTS:
|
|
88
|
+
return 'Account already exists with this provider'
|
|
89
|
+
case AuthErrorCode.SIGN_IN_FAILED:
|
|
90
|
+
return 'Sign in failed'
|
|
91
|
+
case AuthErrorCode.SIGN_UP_FAILED:
|
|
92
|
+
return 'Sign up failed'
|
|
93
|
+
case AuthErrorCode.SIGN_OUT_FAILED:
|
|
94
|
+
return 'Sign out failed'
|
|
95
|
+
case AuthErrorCode.PASSWORD_RESET_FAILED:
|
|
96
|
+
return 'Password reset failed'
|
|
97
|
+
case AuthErrorCode.EMAIL_VERIFICATION_FAILED:
|
|
98
|
+
return 'Email verification failed'
|
|
99
|
+
case AuthErrorCode.PROFILE_UPDATE_FAILED:
|
|
100
|
+
return 'Profile update failed'
|
|
101
|
+
case AuthErrorCode.EMAIL_UPDATE_FAILED:
|
|
102
|
+
return 'Email update failed'
|
|
103
|
+
case AuthErrorCode.PASSWORD_UPDATE_FAILED:
|
|
104
|
+
return 'Password update failed'
|
|
105
|
+
case AuthErrorCode.ACCOUNT_DELETE_FAILED:
|
|
106
|
+
return 'Account deletion failed'
|
|
107
|
+
case AuthErrorCode.REAUTHENTICATION_REQUIRED:
|
|
108
|
+
return 'Reauthentication required'
|
|
109
|
+
case AuthErrorCode.REAUTHENTICATION_FAILED:
|
|
110
|
+
return 'Reauthentication failed'
|
|
111
|
+
case AuthErrorCode.UNKNOWN:
|
|
112
|
+
default:
|
|
113
|
+
return 'An unknown error occurred'
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain Errors for Repository Operations
|
|
3
|
+
* @description Custom error types for repository-related failures
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export class RepositoryError extends Error {
|
|
7
|
+
constructor(
|
|
8
|
+
message: string,
|
|
9
|
+
public code: RepositoryErrorCode,
|
|
10
|
+
public originalError?: unknown
|
|
11
|
+
) {
|
|
12
|
+
super(message)
|
|
13
|
+
this.name = 'RepositoryError'
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export enum RepositoryErrorCode {
|
|
18
|
+
// Document Errors
|
|
19
|
+
DOCUMENT_NOT_FOUND = 'DOCUMENT_NOT_FOUND',
|
|
20
|
+
DOCUMENT_ALREADY_EXISTS = 'DOCUMENT_ALREADY_EXISTS',
|
|
21
|
+
DOCUMENT_INVALID = 'DOCUMENT_INVALID',
|
|
22
|
+
|
|
23
|
+
// Collection Errors
|
|
24
|
+
COLLECTION_NOT_FOUND = 'COLLECTION_NOT_FOUND',
|
|
25
|
+
COLLECTION_INVALID = 'COLLECTION_INVALID',
|
|
26
|
+
|
|
27
|
+
// Query Errors
|
|
28
|
+
QUERY_INVALID = 'QUERY_INVALID',
|
|
29
|
+
QUERY_FAILED = 'QUERY_FAILED',
|
|
30
|
+
|
|
31
|
+
// Transaction Errors
|
|
32
|
+
TRANSACTION_FAILED = 'TRANSACTION_FAILED',
|
|
33
|
+
TRANSACTION_ABORTED = 'TRANSACTION_ABORTED',
|
|
34
|
+
|
|
35
|
+
// Network Errors
|
|
36
|
+
NETWORK_ERROR = 'NETWORK_ERROR',
|
|
37
|
+
TIMEOUT = 'TIMEOUT',
|
|
38
|
+
OFFLINE = 'OFFLINE',
|
|
39
|
+
|
|
40
|
+
// Permission Errors
|
|
41
|
+
PERMISSION_DENIED = 'PERMISSION_DENIED',
|
|
42
|
+
UNAUTHORIZED = 'UNAUTHORIZED',
|
|
43
|
+
|
|
44
|
+
// Validation Errors
|
|
45
|
+
VALIDATION_FAILED = 'VALIDATION_FAILED',
|
|
46
|
+
INVALID_DATA = 'INVALID_DATA',
|
|
47
|
+
|
|
48
|
+
// Concurrency Errors
|
|
49
|
+
CONFLICT = 'CONFLICT',
|
|
50
|
+
VERSION_MISMATCH = 'VERSION_MISMATCH',
|
|
51
|
+
|
|
52
|
+
// Storage Errors
|
|
53
|
+
STORAGE_ERROR = 'STORAGE_ERROR',
|
|
54
|
+
FILE_NOT_FOUND = 'FILE_NOT_FOUND',
|
|
55
|
+
FILE_TOO_LARGE = 'FILE_TOO_LARGE',
|
|
56
|
+
INVALID_FILE_TYPE = 'INVALID_FILE_TYPE',
|
|
57
|
+
|
|
58
|
+
// Unknown Error
|
|
59
|
+
UNKNOWN = 'UNKNOWN',
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function createRepositoryError(
|
|
63
|
+
code: RepositoryErrorCode,
|
|
64
|
+
message?: string,
|
|
65
|
+
originalError?: unknown
|
|
66
|
+
): RepositoryError {
|
|
67
|
+
const defaultMessage = getRepositoryErrorMessage(code)
|
|
68
|
+
return new RepositoryError(message || defaultMessage, code, originalError)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function getRepositoryErrorMessage(code: RepositoryErrorCode): string {
|
|
72
|
+
switch (code) {
|
|
73
|
+
case RepositoryErrorCode.DOCUMENT_NOT_FOUND:
|
|
74
|
+
return 'Document not found'
|
|
75
|
+
case RepositoryErrorCode.DOCUMENT_ALREADY_EXISTS:
|
|
76
|
+
return 'Document already exists'
|
|
77
|
+
case RepositoryErrorCode.DOCUMENT_INVALID:
|
|
78
|
+
return 'Document is invalid'
|
|
79
|
+
case RepositoryErrorCode.COLLECTION_NOT_FOUND:
|
|
80
|
+
return 'Collection not found'
|
|
81
|
+
case RepositoryErrorCode.COLLECTION_INVALID:
|
|
82
|
+
return 'Collection is invalid'
|
|
83
|
+
case RepositoryErrorCode.QUERY_INVALID:
|
|
84
|
+
return 'Query is invalid'
|
|
85
|
+
case RepositoryErrorCode.QUERY_FAILED:
|
|
86
|
+
return 'Query failed'
|
|
87
|
+
case RepositoryErrorCode.TRANSACTION_FAILED:
|
|
88
|
+
return 'Transaction failed'
|
|
89
|
+
case RepositoryErrorCode.TRANSACTION_ABORTED:
|
|
90
|
+
return 'Transaction aborted'
|
|
91
|
+
case RepositoryErrorCode.NETWORK_ERROR:
|
|
92
|
+
return 'Network error'
|
|
93
|
+
case RepositoryErrorCode.TIMEOUT:
|
|
94
|
+
return 'Request timeout'
|
|
95
|
+
case RepositoryErrorCode.OFFLINE:
|
|
96
|
+
return 'Client is offline'
|
|
97
|
+
case RepositoryErrorCode.PERMISSION_DENIED:
|
|
98
|
+
return 'Permission denied'
|
|
99
|
+
case RepositoryErrorCode.UNAUTHORIZED:
|
|
100
|
+
return 'Unauthorized'
|
|
101
|
+
case RepositoryErrorCode.VALIDATION_FAILED:
|
|
102
|
+
return 'Validation failed'
|
|
103
|
+
case RepositoryErrorCode.INVALID_DATA:
|
|
104
|
+
return 'Invalid data'
|
|
105
|
+
case RepositoryErrorCode.CONFLICT:
|
|
106
|
+
return 'Conflict occurred'
|
|
107
|
+
case RepositoryErrorCode.VERSION_MISMATCH:
|
|
108
|
+
return 'Version mismatch'
|
|
109
|
+
case RepositoryErrorCode.STORAGE_ERROR:
|
|
110
|
+
return 'Storage error'
|
|
111
|
+
case RepositoryErrorCode.FILE_NOT_FOUND:
|
|
112
|
+
return 'File not found'
|
|
113
|
+
case RepositoryErrorCode.FILE_TOO_LARGE:
|
|
114
|
+
return 'File is too large'
|
|
115
|
+
case RepositoryErrorCode.INVALID_FILE_TYPE:
|
|
116
|
+
return 'Invalid file type'
|
|
117
|
+
case RepositoryErrorCode.UNKNOWN:
|
|
118
|
+
default:
|
|
119
|
+
return 'An unknown error occurred'
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain Layer
|
|
3
|
+
* Subpath: @umituz/web-firebase/domain
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Domain Layer Public API
|
|
8
|
+
* @description Exports all domain entities, value objects, interfaces, and errors
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// Entities
|
|
12
|
+
export type { FirebaseUser, FirebaseTimestamp } from './entities/firebase.entity'
|
|
13
|
+
export * from './entities/user.entity'
|
|
14
|
+
export * from './entities/file.entity'
|
|
15
|
+
export * from './entities/timestamp.entity'
|
|
16
|
+
|
|
17
|
+
// Value Objects
|
|
18
|
+
export * from './value-objects/email.vo'
|
|
19
|
+
export * from './value-objects/user-id.vo'
|
|
20
|
+
export * from './value-objects/file-path.vo'
|
|
21
|
+
|
|
22
|
+
// Interfaces
|
|
23
|
+
export * from './interfaces/repository.interface'
|
|
24
|
+
export * from './interfaces/auth.repository.interface'
|
|
25
|
+
export * from './interfaces/user.repository.interface'
|
|
26
|
+
export * from './interfaces/file.repository.interface'
|
|
27
|
+
|
|
28
|
+
// Errors
|
|
29
|
+
export * from './errors/auth.errors'
|
|
30
|
+
export * from './errors/repository.errors'
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication Repository Interface
|
|
3
|
+
* @description Defines contract for authentication operations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { User as FirebaseUser, UserCredential } from 'firebase/auth'
|
|
7
|
+
import type { User } from '../entities/user.entity'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Authentication Repository Interface
|
|
11
|
+
* Defines operations for user authentication and management
|
|
12
|
+
*/
|
|
13
|
+
export interface IAuthRepository {
|
|
14
|
+
/**
|
|
15
|
+
* Sign in with email and password
|
|
16
|
+
*/
|
|
17
|
+
signIn(email: string, password: string): Promise<UserCredential>
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Sign up with email, password, and display name
|
|
21
|
+
*/
|
|
22
|
+
signUp(email: string, password: string, displayName: string): Promise<UserCredential>
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Sign in with Google OAuth
|
|
26
|
+
*/
|
|
27
|
+
signInWithGoogle(): Promise<UserCredential>
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Sign out current user
|
|
31
|
+
*/
|
|
32
|
+
signOut(): Promise<void>
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Send password reset email
|
|
36
|
+
*/
|
|
37
|
+
sendPasswordReset(email: string): Promise<void>
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Resend email verification
|
|
41
|
+
*/
|
|
42
|
+
resendEmailVerification(): Promise<void>
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Update user profile (displayName, photoURL)
|
|
46
|
+
*/
|
|
47
|
+
updateProfile(updates: Partial<Pick<User['profile'], 'displayName' | 'photoURL'>>): Promise<void>
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Update user email (requires password)
|
|
51
|
+
*/
|
|
52
|
+
updateEmail(newEmail: string, password: string): Promise<void>
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Update user password (requires current password)
|
|
56
|
+
*/
|
|
57
|
+
updatePassword(currentPassword: string, newPassword: string): Promise<void>
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Delete user account (requires password)
|
|
61
|
+
*/
|
|
62
|
+
deleteAccount(password: string): Promise<void>
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Get current authenticated user
|
|
66
|
+
*/
|
|
67
|
+
getCurrentUser(): FirebaseUser | null
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Subscribe to auth state changes
|
|
71
|
+
*/
|
|
72
|
+
onAuthStateChanged(callback: (user: FirebaseUser | null) => void): () => void
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Create user document in Firestore
|
|
76
|
+
*/
|
|
77
|
+
createUserDocument(userId: string, data: Partial<Omit<User, 'profile'>> & { email: string; displayName: string }): Promise<void>
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Update last login timestamp
|
|
81
|
+
*/
|
|
82
|
+
updateLastLogin(userId: string): Promise<void>
|
|
83
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Repository Interface
|
|
3
|
+
* @description Defines contract for file storage operations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type {
|
|
7
|
+
FileMetadata,
|
|
8
|
+
UploadResult,
|
|
9
|
+
UploadOptions,
|
|
10
|
+
FileFilters,
|
|
11
|
+
FileQueryResult,
|
|
12
|
+
StorageStats,
|
|
13
|
+
} from '../entities/file.entity'
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* File Repository Interface
|
|
17
|
+
* Defines operations for file storage and management
|
|
18
|
+
*/
|
|
19
|
+
export interface IFileRepository {
|
|
20
|
+
/**
|
|
21
|
+
* Upload file to storage
|
|
22
|
+
*/
|
|
23
|
+
uploadFile(
|
|
24
|
+
userId: string,
|
|
25
|
+
path: string,
|
|
26
|
+
file: File | Blob,
|
|
27
|
+
options?: UploadOptions
|
|
28
|
+
): Promise<UploadResult>
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Upload image with automatic categorization
|
|
32
|
+
*/
|
|
33
|
+
uploadImage(userId: string, file: File, filename?: string): Promise<UploadResult>
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Upload video with automatic categorization
|
|
37
|
+
*/
|
|
38
|
+
uploadVideo(userId: string, file: File, filename?: string): Promise<UploadResult>
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Upload document with automatic categorization
|
|
42
|
+
*/
|
|
43
|
+
uploadDocument(userId: string, file: File, filename?: string): Promise<UploadResult>
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Upload profile picture
|
|
47
|
+
*/
|
|
48
|
+
uploadProfilePicture(userId: string, file: File): Promise<UploadResult>
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Get download URL for a file
|
|
52
|
+
*/
|
|
53
|
+
getDownloadURL(path: string): Promise<string>
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Delete file by path
|
|
57
|
+
*/
|
|
58
|
+
deleteFile(path: string): Promise<void>
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Delete all user files
|
|
62
|
+
*/
|
|
63
|
+
deleteUserFiles(userId: string): Promise<void>
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Delete user image
|
|
67
|
+
*/
|
|
68
|
+
deleteImage(userId: string, filename: string): Promise<void>
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Delete user video
|
|
72
|
+
*/
|
|
73
|
+
deleteVideo(userId: string, filename: string): Promise<void>
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Delete profile picture
|
|
77
|
+
*/
|
|
78
|
+
deleteProfilePicture(userId: string, filename: string): Promise<void>
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* List user files
|
|
82
|
+
*/
|
|
83
|
+
listUserFiles(userId: string, path?: string): Promise<string[]>
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* List user images
|
|
87
|
+
*/
|
|
88
|
+
listUserImages(userId: string): Promise<string[]>
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* List user videos
|
|
92
|
+
*/
|
|
93
|
+
listUserVideos(userId: string): Promise<string[]>
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Get file metadata
|
|
97
|
+
*/
|
|
98
|
+
getFileMetadata(path: string): Promise<FileMetadata>
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Query files with filters
|
|
102
|
+
*/
|
|
103
|
+
queryFiles(userId: string, filters?: FileFilters): Promise<FileQueryResult>
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Get storage statistics
|
|
107
|
+
*/
|
|
108
|
+
getStorageStats(userId: string): Promise<StorageStats>
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Validate file before upload
|
|
112
|
+
*/
|
|
113
|
+
validateFile(file: File, options?: {
|
|
114
|
+
maxSizeBytes?: number
|
|
115
|
+
maxSizeMB?: number
|
|
116
|
+
allowedTypes?: string[]
|
|
117
|
+
}): boolean
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Check if file is an image
|
|
121
|
+
*/
|
|
122
|
+
isImageFile(file: File): boolean
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Check if file is a video
|
|
126
|
+
*/
|
|
127
|
+
isVideoFile(file: File): boolean
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Check if file is a document
|
|
131
|
+
*/
|
|
132
|
+
isDocumentFile(file: File): boolean
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Generate unique filename
|
|
136
|
+
*/
|
|
137
|
+
generateUniqueFilename(originalName: string): string
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Get file extension
|
|
141
|
+
*/
|
|
142
|
+
getFileExtension(filename: string): string
|
|
143
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base Repository Interface
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface IBaseRepository<T> {
|
|
6
|
+
getById(id: string, parentPath?: string): Promise<T | null>;
|
|
7
|
+
getAll(constraints?: any[], parentPath?: string): Promise<T[]>;
|
|
8
|
+
create(id: string, data: Omit<T, 'id'>, parentPath?: string): Promise<void>;
|
|
9
|
+
update(id: string, data: Partial<T>, parentPath?: string): Promise<void>;
|
|
10
|
+
delete(id: string, parentPath?: string): Promise<void>;
|
|
11
|
+
}
|