@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.
- 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 +37 -11
- 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/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 +25 -2
- package/src/domain/interfaces/auth.repository.interface.ts +83 -0
- package/src/domain/interfaces/file.repository.interface.ts +143 -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 +19 -4
- 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 +10 -5
- package/src/infrastructure/utils/storage.util.ts +3 -3
- package/src/presentation/hooks/useAuth.ts +153 -0
- package/src/presentation/hooks/useFirestore.ts +125 -0
- package/src/presentation/hooks/useStorage.ts +141 -0
- package/src/presentation/providers/FirebaseProvider.tsx +40 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import { UserCredential, User as User$1 } from 'firebase/auth';
|
|
2
|
+
import { QueryConstraint } from 'firebase/firestore';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* User Domain Entities
|
|
6
|
+
* @description Core user-related entities following DDD principles
|
|
7
|
+
* Migrated from: /Users/umituz/Desktop/github/umituz/apps/web/app-growth-factory/src/domains/firebase/types/index.ts
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* User Profile Value Object
|
|
11
|
+
* Immutable user profile data
|
|
12
|
+
*/
|
|
13
|
+
interface UserProfile {
|
|
14
|
+
readonly id: string;
|
|
15
|
+
readonly email: string;
|
|
16
|
+
readonly displayName: string;
|
|
17
|
+
readonly photoURL?: string;
|
|
18
|
+
readonly phoneNumber?: string;
|
|
19
|
+
readonly createdAt: number;
|
|
20
|
+
readonly updatedAt: number;
|
|
21
|
+
readonly lastLoginAt: number;
|
|
22
|
+
readonly emailVerified: boolean;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* User Notifications Settings
|
|
26
|
+
*/
|
|
27
|
+
interface UserNotifications {
|
|
28
|
+
email: boolean;
|
|
29
|
+
push: boolean;
|
|
30
|
+
marketing: boolean;
|
|
31
|
+
security: boolean;
|
|
32
|
+
weeklyDigest: boolean;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* User Privacy Settings
|
|
36
|
+
*/
|
|
37
|
+
interface UserPrivacy {
|
|
38
|
+
profileVisibility: 'public' | 'private';
|
|
39
|
+
showEmail: boolean;
|
|
40
|
+
showPhone: boolean;
|
|
41
|
+
dataSharing: boolean;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* User Settings Value Object
|
|
45
|
+
*/
|
|
46
|
+
interface UserSettings {
|
|
47
|
+
theme: 'light' | 'dark' | 'system';
|
|
48
|
+
language: string;
|
|
49
|
+
timezone: string;
|
|
50
|
+
currency: string;
|
|
51
|
+
notifications: UserNotifications;
|
|
52
|
+
privacy: UserPrivacy;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* User Subscription Value Object
|
|
56
|
+
*/
|
|
57
|
+
interface UserSubscription {
|
|
58
|
+
plan: 'free' | 'standard' | 'professional' | 'business';
|
|
59
|
+
status: 'active' | 'inactive' | 'canceled' | 'past_due';
|
|
60
|
+
polarCustomerId?: string;
|
|
61
|
+
polarSubscriptionId?: string;
|
|
62
|
+
currentPeriodStart?: number;
|
|
63
|
+
currentPeriodEnd?: number;
|
|
64
|
+
cancelAtPeriodEnd: boolean;
|
|
65
|
+
readonly createdAt: number;
|
|
66
|
+
readonly updatedAt: number;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Account Metrics Value Object
|
|
70
|
+
*/
|
|
71
|
+
interface AccountMetrics {
|
|
72
|
+
followers: number;
|
|
73
|
+
following: number;
|
|
74
|
+
posts: number;
|
|
75
|
+
engagement: number;
|
|
76
|
+
lastSyncedAt: number;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* User Connected Account Entity
|
|
80
|
+
*/
|
|
81
|
+
interface UserConnectedAccount {
|
|
82
|
+
platform: 'twitter' | 'facebook' | 'instagram' | 'linkedin' | 'tiktok' | 'youtube' | 'pinterest' | 'reddit' | 'threads' | 'discord' | 'telegram' | 'mastodon' | 'medium';
|
|
83
|
+
connected: boolean;
|
|
84
|
+
readonly connectedAt: number;
|
|
85
|
+
username?: string;
|
|
86
|
+
profileId?: string;
|
|
87
|
+
accessToken?: string;
|
|
88
|
+
refreshToken?: string;
|
|
89
|
+
tokenExpiresAt?: number;
|
|
90
|
+
metrics?: AccountMetrics;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* User Content Entity
|
|
94
|
+
*/
|
|
95
|
+
interface UserContent {
|
|
96
|
+
readonly id: string;
|
|
97
|
+
type: 'post' | 'article' | 'video' | 'image' | 'story' | 'reel';
|
|
98
|
+
title: string;
|
|
99
|
+
content: string;
|
|
100
|
+
platforms: string[];
|
|
101
|
+
status: 'draft' | 'scheduled' | 'published' | 'failed';
|
|
102
|
+
scheduledFor?: number;
|
|
103
|
+
publishedAt?: number;
|
|
104
|
+
readonly createdAt: number;
|
|
105
|
+
readonly updatedAt: number;
|
|
106
|
+
mediaUrls?: string[];
|
|
107
|
+
metadata?: Record<string, unknown>;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Platform Breakdown for Analytics
|
|
111
|
+
*/
|
|
112
|
+
interface PlatformBreakdown {
|
|
113
|
+
[platform: string]: {
|
|
114
|
+
posts: number;
|
|
115
|
+
engagement: number;
|
|
116
|
+
reach: number;
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* User Analytics Value Object
|
|
121
|
+
*/
|
|
122
|
+
interface UserAnalytics {
|
|
123
|
+
totalPosts: number;
|
|
124
|
+
totalEngagement: number;
|
|
125
|
+
totalReach: number;
|
|
126
|
+
topPerformingPosts: string[];
|
|
127
|
+
platformBreakdown: PlatformBreakdown;
|
|
128
|
+
lastCalculatedAt: number;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* User Credits Value Object
|
|
132
|
+
*/
|
|
133
|
+
interface UserCredits {
|
|
134
|
+
standard: number;
|
|
135
|
+
professional: number;
|
|
136
|
+
total: number;
|
|
137
|
+
resetAt: number;
|
|
138
|
+
lastResetAt: number;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* User Aggregate Root
|
|
142
|
+
* Main user document containing all user-related data
|
|
143
|
+
* This is the aggregate root for the User domain
|
|
144
|
+
*/
|
|
145
|
+
interface User {
|
|
146
|
+
readonly profile: UserProfile;
|
|
147
|
+
settings: UserSettings;
|
|
148
|
+
subscription: UserSubscription;
|
|
149
|
+
connectedAccounts: UserConnectedAccount[];
|
|
150
|
+
content: UserContent[];
|
|
151
|
+
analytics: UserAnalytics;
|
|
152
|
+
credits: UserCredits;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Collection Constants
|
|
156
|
+
*/
|
|
157
|
+
declare const USER_COLLECTIONS: {
|
|
158
|
+
readonly USERS: "users";
|
|
159
|
+
readonly CONTENT: "content";
|
|
160
|
+
readonly ANALYTICS: "analytics";
|
|
161
|
+
readonly CONNECTED_ACCOUNTS: "connectedAccounts";
|
|
162
|
+
};
|
|
163
|
+
declare const USER_SUBCOLLECTIONS: {
|
|
164
|
+
readonly CONTENT: "content";
|
|
165
|
+
readonly ANALYTICS: "analytics";
|
|
166
|
+
readonly SCHEDULED: "scheduled";
|
|
167
|
+
readonly PUBLISHED: "published";
|
|
168
|
+
readonly DRAFTS: "drafts";
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Authentication Repository Interface
|
|
173
|
+
* @description Defines contract for authentication operations
|
|
174
|
+
*/
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Authentication Repository Interface
|
|
178
|
+
* Defines operations for user authentication and management
|
|
179
|
+
*/
|
|
180
|
+
interface IAuthRepository {
|
|
181
|
+
/**
|
|
182
|
+
* Sign in with email and password
|
|
183
|
+
*/
|
|
184
|
+
signIn(email: string, password: string): Promise<UserCredential>;
|
|
185
|
+
/**
|
|
186
|
+
* Sign up with email, password, and display name
|
|
187
|
+
*/
|
|
188
|
+
signUp(email: string, password: string, displayName: string): Promise<UserCredential>;
|
|
189
|
+
/**
|
|
190
|
+
* Sign in with Google OAuth
|
|
191
|
+
*/
|
|
192
|
+
signInWithGoogle(): Promise<UserCredential>;
|
|
193
|
+
/**
|
|
194
|
+
* Sign out current user
|
|
195
|
+
*/
|
|
196
|
+
signOut(): Promise<void>;
|
|
197
|
+
/**
|
|
198
|
+
* Send password reset email
|
|
199
|
+
*/
|
|
200
|
+
sendPasswordReset(email: string): Promise<void>;
|
|
201
|
+
/**
|
|
202
|
+
* Resend email verification
|
|
203
|
+
*/
|
|
204
|
+
resendEmailVerification(): Promise<void>;
|
|
205
|
+
/**
|
|
206
|
+
* Update user profile (displayName, photoURL)
|
|
207
|
+
*/
|
|
208
|
+
updateProfile(updates: Partial<Pick<User['profile'], 'displayName' | 'photoURL'>>): Promise<void>;
|
|
209
|
+
/**
|
|
210
|
+
* Update user email (requires password)
|
|
211
|
+
*/
|
|
212
|
+
updateEmail(newEmail: string, password: string): Promise<void>;
|
|
213
|
+
/**
|
|
214
|
+
* Update user password (requires current password)
|
|
215
|
+
*/
|
|
216
|
+
updatePassword(currentPassword: string, newPassword: string): Promise<void>;
|
|
217
|
+
/**
|
|
218
|
+
* Delete user account (requires password)
|
|
219
|
+
*/
|
|
220
|
+
deleteAccount(password: string): Promise<void>;
|
|
221
|
+
/**
|
|
222
|
+
* Get current authenticated user
|
|
223
|
+
*/
|
|
224
|
+
getCurrentUser(): User$1 | null;
|
|
225
|
+
/**
|
|
226
|
+
* Subscribe to auth state changes
|
|
227
|
+
*/
|
|
228
|
+
onAuthStateChanged(callback: (user: User$1 | null) => void): () => void;
|
|
229
|
+
/**
|
|
230
|
+
* Create user document in Firestore
|
|
231
|
+
*/
|
|
232
|
+
createUserDocument(userId: string, data: Partial<Omit<User, 'profile'>> & {
|
|
233
|
+
email: string;
|
|
234
|
+
displayName: string;
|
|
235
|
+
}): Promise<void>;
|
|
236
|
+
/**
|
|
237
|
+
* Update last login timestamp
|
|
238
|
+
*/
|
|
239
|
+
updateLastLogin(userId: string): Promise<void>;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* User Repository Interface
|
|
244
|
+
* @description Defines contract for user data operations
|
|
245
|
+
*/
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* User Repository Interface
|
|
249
|
+
* Defines operations for user data management
|
|
250
|
+
*/
|
|
251
|
+
interface IUserRepository {
|
|
252
|
+
/**
|
|
253
|
+
* Get user by ID
|
|
254
|
+
*/
|
|
255
|
+
getUser(userId: string): Promise<User | null>;
|
|
256
|
+
/**
|
|
257
|
+
* Get user by email
|
|
258
|
+
*/
|
|
259
|
+
getUserByEmail(email: string): Promise<User | null>;
|
|
260
|
+
/**
|
|
261
|
+
* Create user
|
|
262
|
+
*/
|
|
263
|
+
createUser(userId: string, data: Partial<User>): Promise<void>;
|
|
264
|
+
/**
|
|
265
|
+
* Update user
|
|
266
|
+
*/
|
|
267
|
+
updateUser(userId: string, data: Partial<User>): Promise<void>;
|
|
268
|
+
/**
|
|
269
|
+
* Delete user
|
|
270
|
+
*/
|
|
271
|
+
deleteUser(userId: string): Promise<void>;
|
|
272
|
+
/**
|
|
273
|
+
* Update user profile
|
|
274
|
+
*/
|
|
275
|
+
updateProfile(userId: string, updates: Partial<Pick<User['profile'], 'displayName' | 'photoURL' | 'phoneNumber'>>): Promise<void>;
|
|
276
|
+
/**
|
|
277
|
+
* Update user settings
|
|
278
|
+
*/
|
|
279
|
+
updateSettings(userId: string, settings: Partial<User['settings']>): Promise<void>;
|
|
280
|
+
/**
|
|
281
|
+
* Update user subscription
|
|
282
|
+
*/
|
|
283
|
+
updateSubscription(userId: string, subscription: Partial<User['subscription']>): Promise<void>;
|
|
284
|
+
/**
|
|
285
|
+
* Update last login timestamp
|
|
286
|
+
*/
|
|
287
|
+
updateLastLogin(userId: string): Promise<void>;
|
|
288
|
+
/**
|
|
289
|
+
* Query users with constraints
|
|
290
|
+
*/
|
|
291
|
+
queryUsers(constraints: QueryConstraint[]): Promise<User[]>;
|
|
292
|
+
/**
|
|
293
|
+
* Subscribe to user document changes
|
|
294
|
+
*/
|
|
295
|
+
subscribeToUser(userId: string, callback: (user: User | null) => void, onError?: (error: Error) => void): () => void;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
export { type AccountMetrics as A, type IAuthRepository as I, type PlatformBreakdown as P, USER_COLLECTIONS as U, type IUserRepository as a, USER_SUBCOLLECTIONS as b, type User as c, type UserAnalytics as d, type UserConnectedAccount as e, type UserContent as f, type UserCredits as g, type UserNotifications as h, type UserPrivacy as i, type UserProfile as j, type UserSettings as k, type UserSubscription as l };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/web-firebase",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
|
+
"description": "Comprehensive Firebase integration with DDD architecture for web applications",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -15,17 +15,34 @@
|
|
|
15
15
|
"import": "./dist/index.mjs",
|
|
16
16
|
"require": "./dist/index.js"
|
|
17
17
|
},
|
|
18
|
-
"./domain":
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
"./domain": {
|
|
19
|
+
"types": "./dist/domain/index.d.ts",
|
|
20
|
+
"import": "./dist/domain/index.mjs",
|
|
21
|
+
"require": "./dist/domain/index.js"
|
|
22
|
+
},
|
|
23
|
+
"./application": {
|
|
24
|
+
"types": "./dist/application/index.d.ts",
|
|
25
|
+
"import": "./dist/application/index.mjs",
|
|
26
|
+
"require": "./dist/application/index.js"
|
|
27
|
+
},
|
|
28
|
+
"./infrastructure": {
|
|
29
|
+
"types": "./dist/infrastructure/index.d.ts",
|
|
30
|
+
"import": "./dist/infrastructure/index.mjs",
|
|
31
|
+
"require": "./dist/infrastructure/index.js"
|
|
32
|
+
},
|
|
33
|
+
"./presentation": {
|
|
34
|
+
"types": "./dist/presentation/index.d.ts",
|
|
35
|
+
"import": "./dist/presentation/index.mjs",
|
|
36
|
+
"require": "./dist/presentation/index.js"
|
|
37
|
+
}
|
|
21
38
|
},
|
|
22
39
|
"files": [
|
|
23
40
|
"dist",
|
|
24
41
|
"src"
|
|
25
42
|
],
|
|
26
43
|
"scripts": {
|
|
27
|
-
"build": "tsup src/index.ts --format cjs,esm --dts --clean --external react --external firebase --external firebase/app --external firebase/auth --external firebase/firestore --external firebase/storage --external firebase/functions",
|
|
28
|
-
"dev": "tsup src/index.ts --format cjs,esm --dts --watch --external react --external firebase",
|
|
44
|
+
"build": "tsup src/index.ts src/domain/index.ts src/application/index.ts src/infrastructure/index.ts src/presentation/index.ts --format cjs,esm --dts --clean --external react --external firebase --external firebase/app --external firebase/auth --external firebase/firestore --external firebase/storage --external firebase/functions",
|
|
45
|
+
"dev": "tsup src/index.ts src/domain/index.ts src/application/index.ts src/infrastructure/index.ts src/presentation/index.ts --format cjs,esm --dts --watch --external react --external firebase",
|
|
29
46
|
"lint": "tsc --noEmit"
|
|
30
47
|
},
|
|
31
48
|
"keywords": [
|
|
@@ -33,18 +50,27 @@
|
|
|
33
50
|
"firestore",
|
|
34
51
|
"auth",
|
|
35
52
|
"storage",
|
|
53
|
+
"functions",
|
|
54
|
+
"analytics",
|
|
36
55
|
"react",
|
|
37
|
-
"web"
|
|
56
|
+
"web",
|
|
57
|
+
"typescript",
|
|
58
|
+
"ddd",
|
|
59
|
+
"domain-driven-design",
|
|
60
|
+
"clean-architecture",
|
|
61
|
+
"repository-pattern",
|
|
62
|
+
"use-cases",
|
|
63
|
+
"hexagonal-architecture"
|
|
38
64
|
],
|
|
39
|
-
"author": "
|
|
65
|
+
"author": "umituz",
|
|
40
66
|
"license": "MIT",
|
|
41
67
|
"peerDependencies": {
|
|
42
|
-
"firebase": ">=
|
|
68
|
+
"firebase": ">=12",
|
|
43
69
|
"react": ">=18"
|
|
44
70
|
},
|
|
45
71
|
"devDependencies": {
|
|
46
72
|
"@types/react": "^18.2.0",
|
|
47
|
-
"firebase": "^11.0
|
|
73
|
+
"firebase": "^12.11.0",
|
|
48
74
|
"tsup": "^8.0.0",
|
|
49
75
|
"typescript": "^5.0.0"
|
|
50
76
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication DTOs
|
|
3
|
+
* @description Data Transfer Objects for authentication operations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { UserCredential } from 'firebase/auth'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Sign In DTO
|
|
10
|
+
*/
|
|
11
|
+
export interface SignInDTO {
|
|
12
|
+
email: string
|
|
13
|
+
password: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Sign Up DTO
|
|
18
|
+
*/
|
|
19
|
+
export interface SignUpDTO {
|
|
20
|
+
email: string
|
|
21
|
+
password: string
|
|
22
|
+
displayName: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Sign Up Result
|
|
27
|
+
*/
|
|
28
|
+
export interface SignUpResult extends UserCredential {
|
|
29
|
+
userId: string
|
|
30
|
+
emailVerified: boolean
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Reset Password DTO
|
|
35
|
+
*/
|
|
36
|
+
export interface ResetPasswordDTO {
|
|
37
|
+
email: string
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Update Profile DTO
|
|
42
|
+
*/
|
|
43
|
+
export interface UpdateProfileDTO {
|
|
44
|
+
displayName?: string
|
|
45
|
+
photoURL?: string
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Update Email DTO
|
|
50
|
+
*/
|
|
51
|
+
export interface UpdateEmailDTO {
|
|
52
|
+
newEmail: string
|
|
53
|
+
currentPassword: string
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Update Password DTO
|
|
58
|
+
*/
|
|
59
|
+
export interface UpdatePasswordDTO {
|
|
60
|
+
currentPassword: string
|
|
61
|
+
newPassword: string
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Delete Account DTO
|
|
66
|
+
*/
|
|
67
|
+
export interface DeleteAccountDTO {
|
|
68
|
+
password: string
|
|
69
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User DTOs
|
|
3
|
+
* @description Data Transfer Objects for user operations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { User } from '../../domain/entities/user.entity'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Create User DTO
|
|
10
|
+
*/
|
|
11
|
+
export interface CreateUserDTO {
|
|
12
|
+
id: string
|
|
13
|
+
email: string
|
|
14
|
+
displayName: string
|
|
15
|
+
photoURL?: string
|
|
16
|
+
phoneNumber?: string
|
|
17
|
+
emailVerified: boolean
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Update User DTO
|
|
22
|
+
*/
|
|
23
|
+
export interface UpdateUserDTO {
|
|
24
|
+
displayName?: string
|
|
25
|
+
photoURL?: string
|
|
26
|
+
phoneNumber?: string
|
|
27
|
+
theme?: 'light' | 'dark' | 'system'
|
|
28
|
+
language?: string
|
|
29
|
+
timezone?: string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Update User Settings DTO
|
|
34
|
+
*/
|
|
35
|
+
export interface UpdateUserSettingsDTO {
|
|
36
|
+
theme?: 'light' | 'dark' | 'system'
|
|
37
|
+
language?: string
|
|
38
|
+
timezone?: string
|
|
39
|
+
currency?: string
|
|
40
|
+
notifications?: Partial<User['settings']['notifications']>
|
|
41
|
+
privacy?: Partial<User['settings']['privacy']>
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Update User Subscription DTO
|
|
46
|
+
*/
|
|
47
|
+
export interface UpdateUserSubscriptionDTO {
|
|
48
|
+
plan?: User['subscription']['plan']
|
|
49
|
+
status?: User['subscription']['status']
|
|
50
|
+
polarCustomerId?: string
|
|
51
|
+
polarSubscriptionId?: string
|
|
52
|
+
currentPeriodStart?: number
|
|
53
|
+
currentPeriodEnd?: number
|
|
54
|
+
cancelAtPeriodEnd?: boolean
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* User Query Result
|
|
59
|
+
*/
|
|
60
|
+
export interface UserQueryResult {
|
|
61
|
+
user: User | null
|
|
62
|
+
lastLoginAt?: number
|
|
63
|
+
createdAt?: number
|
|
64
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reset Password Use Case
|
|
3
|
+
* @description Handles password reset flow
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { IAuthRepository } from '../../../domain/interfaces/auth.repository.interface'
|
|
7
|
+
import type { ResetPasswordDTO } from '../../dto/auth.dto'
|
|
8
|
+
import { createAuthError, AuthErrorCode } from '../../../domain/errors/auth.errors'
|
|
9
|
+
|
|
10
|
+
export class ResetPasswordUseCase {
|
|
11
|
+
constructor(private readonly authRepository: IAuthRepository) {}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Execute password reset use case
|
|
15
|
+
*/
|
|
16
|
+
async execute(dto: ResetPasswordDTO): Promise<void> {
|
|
17
|
+
try {
|
|
18
|
+
// Validate input
|
|
19
|
+
this.validateDTO(dto)
|
|
20
|
+
|
|
21
|
+
// Send password reset email
|
|
22
|
+
await this.authRepository.sendPasswordReset(dto.email)
|
|
23
|
+
} catch (error) {
|
|
24
|
+
throw this.handleError(error)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Validate DTO
|
|
30
|
+
*/
|
|
31
|
+
private validateDTO(dto: ResetPasswordDTO): void {
|
|
32
|
+
if (!dto.email) {
|
|
33
|
+
throw createAuthError(AuthErrorCode.PASSWORD_RESET_FAILED, 'Email is required')
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Email validation
|
|
37
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
|
38
|
+
if (!emailRegex.test(dto.email)) {
|
|
39
|
+
throw createAuthError(AuthErrorCode.PASSWORD_RESET_FAILED, 'Invalid email format')
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Handle errors
|
|
45
|
+
*/
|
|
46
|
+
private handleError(error: unknown): Error {
|
|
47
|
+
if (error instanceof Error && 'code' in error) {
|
|
48
|
+
const code = (error as { code: string }).code
|
|
49
|
+
|
|
50
|
+
if (code === 'auth/invalid-email') {
|
|
51
|
+
return createAuthError(AuthErrorCode.PASSWORD_RESET_FAILED, 'Invalid email', error)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (code === 'auth/user-not-found') {
|
|
55
|
+
// Don't reveal if user exists
|
|
56
|
+
return createAuthError(AuthErrorCode.PASSWORD_RESET_FAILED, 'Password reset email sent if account exists')
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (code === 'auth/too-many-requests') {
|
|
60
|
+
return createAuthError(AuthErrorCode.TOO_MANY_REQUESTS, 'Too many requests', error)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return createAuthError(AuthErrorCode.PASSWORD_RESET_FAILED, 'Password reset failed', error)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sign In With Google Use Case
|
|
3
|
+
* @description Handles Google OAuth sign in/sign up
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { UserCredential } from 'firebase/auth'
|
|
7
|
+
import type { IAuthRepository } from '../../../domain/interfaces/auth.repository.interface'
|
|
8
|
+
import type { IUserRepository } from '../../../domain/interfaces/user.repository.interface'
|
|
9
|
+
import type { CreateUserDTO } from '../../dto/user.dto'
|
|
10
|
+
import { createAuthError, AuthErrorCode } from '../../../domain/errors/auth.errors'
|
|
11
|
+
|
|
12
|
+
export class SignInWithGoogleUseCase {
|
|
13
|
+
constructor(
|
|
14
|
+
private readonly authRepository: IAuthRepository,
|
|
15
|
+
private readonly userRepository: IUserRepository
|
|
16
|
+
) {}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Execute Google sign in use case
|
|
20
|
+
*/
|
|
21
|
+
async execute(): Promise<UserCredential> {
|
|
22
|
+
try {
|
|
23
|
+
// Perform Google sign in
|
|
24
|
+
const result = await this.authRepository.signInWithGoogle()
|
|
25
|
+
|
|
26
|
+
// Check if user needs onboarding
|
|
27
|
+
const existingUser = await this.userRepository.getUser(result.user.uid)
|
|
28
|
+
if (!existingUser) {
|
|
29
|
+
// New user - create user document
|
|
30
|
+
const createUserDTO: CreateUserDTO = {
|
|
31
|
+
id: result.user.uid,
|
|
32
|
+
email: result.user.email || '',
|
|
33
|
+
displayName: result.user.displayName || '',
|
|
34
|
+
photoURL: result.user.photoURL || undefined,
|
|
35
|
+
phoneNumber: result.user.phoneNumber || undefined,
|
|
36
|
+
emailVerified: result.user.emailVerified,
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
await this.authRepository.createUserDocument(result.user.uid, createUserDTO)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return result
|
|
43
|
+
} catch (error) {
|
|
44
|
+
throw this.handleError(error)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Handle errors
|
|
50
|
+
*/
|
|
51
|
+
private handleError(error: unknown): Error {
|
|
52
|
+
if (error instanceof Error && 'code' in error) {
|
|
53
|
+
const code = (error as { code: string }).code
|
|
54
|
+
|
|
55
|
+
if (code === 'auth/popup-closed-by-user') {
|
|
56
|
+
return createAuthError(AuthErrorCode.OAUTH_CANCELLED, 'Google sign in cancelled', error)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (code === 'auth/popup-blocked') {
|
|
60
|
+
return createAuthError(AuthErrorCode.OAUTH_ERROR, 'Popup blocked by browser', error)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (code === 'auth/account-exists-with-different-credential') {
|
|
64
|
+
return createAuthError(
|
|
65
|
+
AuthErrorCode.OAUTH_ACCOUNT_EXISTS,
|
|
66
|
+
'Account already exists with different credential',
|
|
67
|
+
error
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (code === 'auth/auth-domain-policy-required') {
|
|
72
|
+
return createAuthError(AuthErrorCode.OAUTH_ERROR, 'Auth domain not configured', error)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (code === 'auth/unauthorized-domain') {
|
|
76
|
+
return createAuthError(AuthErrorCode.OAUTH_ERROR, 'Unauthorized domain', error)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (code === 'auth/cancelled-popup-request') {
|
|
80
|
+
return createAuthError(AuthErrorCode.OAUTH_CANCELLED, 'Sign in cancelled', error)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return createAuthError(AuthErrorCode.OAUTH_ERROR, 'Google sign in failed', error)
|
|
85
|
+
}
|
|
86
|
+
}
|