@umituz/web-dashboard 2.0.8 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +10 -1
- package/src/domains/auth/components/AuthLayout.tsx +114 -0
- package/src/domains/auth/components/ForgotPasswordForm.tsx +181 -0
- package/src/domains/auth/components/LoginForm.tsx +228 -0
- package/src/domains/auth/components/RegisterForm.tsx +296 -0
- package/src/domains/auth/components/ResetPasswordForm.tsx +230 -0
- package/src/domains/auth/components/index.ts +11 -0
- package/src/domains/auth/hooks/index.ts +7 -0
- package/src/domains/auth/hooks/useAuth.ts +301 -0
- package/src/domains/auth/index.ts +58 -0
- package/src/domains/auth/types/auth.ts +265 -0
- package/src/domains/auth/types/index.ts +24 -0
- package/src/domains/auth/utils/auth.ts +280 -0
- package/src/domains/auth/utils/index.ts +23 -0
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Utilities
|
|
3
|
+
*
|
|
4
|
+
* Utility functions for authentication operations
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { LoginCredentials, RegisterData, User } from "../types/auth";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Validate email format
|
|
11
|
+
*
|
|
12
|
+
* @param email - Email address
|
|
13
|
+
* @returns Whether email is valid
|
|
14
|
+
*/
|
|
15
|
+
export function isValidEmail(email: string): boolean {
|
|
16
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
17
|
+
return emailRegex.test(email);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Validate password strength
|
|
22
|
+
*
|
|
23
|
+
* @param password - Password
|
|
24
|
+
* @param minLength - Minimum length (default: 8)
|
|
25
|
+
* @returns Whether password meets requirements
|
|
26
|
+
*/
|
|
27
|
+
export function isValidPassword(password: string, minLength: number = 8): boolean {
|
|
28
|
+
return password.length >= minLength;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Validate login credentials
|
|
33
|
+
*
|
|
34
|
+
* @param credentials - Login credentials
|
|
35
|
+
* @returns Validation result with error message
|
|
36
|
+
*/
|
|
37
|
+
export function validateLogin(credentials: LoginCredentials): {
|
|
38
|
+
valid: boolean;
|
|
39
|
+
error?: string;
|
|
40
|
+
} {
|
|
41
|
+
if (!credentials.email) {
|
|
42
|
+
return { valid: false, error: "Email is required" };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!isValidEmail(credentials.email)) {
|
|
46
|
+
return { valid: false, error: "Invalid email format" };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!credentials.password) {
|
|
50
|
+
return { valid: false, error: "Password is required" };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return { valid: true };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Validate registration data
|
|
58
|
+
*
|
|
59
|
+
* @param data - Registration data
|
|
60
|
+
* @param requireName - Whether name is required (default: false)
|
|
61
|
+
* @param requirePasswordConfirm - Whether password confirmation is required
|
|
62
|
+
* @returns Validation result with error message
|
|
63
|
+
*/
|
|
64
|
+
export function validateRegister(
|
|
65
|
+
data: RegisterData,
|
|
66
|
+
requireName: boolean = false,
|
|
67
|
+
requirePasswordConfirm?: boolean
|
|
68
|
+
): {
|
|
69
|
+
valid: boolean;
|
|
70
|
+
error?: string;
|
|
71
|
+
} {
|
|
72
|
+
if (!data.email) {
|
|
73
|
+
return { valid: false, error: "Email is required" };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (!isValidEmail(data.email)) {
|
|
77
|
+
return { valid: false, error: "Invalid email format" };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (requireName && !data.name) {
|
|
81
|
+
return { valid: false, error: "Name is required" };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (!data.password) {
|
|
85
|
+
return { valid: false, error: "Password is required" };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (!isValidPassword(data.password)) {
|
|
89
|
+
return { valid: false, error: "Password must be at least 8 characters" };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (requirePasswordConfirm && data.password !== data.confirmPassword) {
|
|
93
|
+
return { valid: false, error: "Passwords do not match" };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return { valid: true };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Validate password reset request
|
|
101
|
+
*
|
|
102
|
+
* @param data - Forgot password data
|
|
103
|
+
* @returns Validation result with error message
|
|
104
|
+
*/
|
|
105
|
+
export function validateForgotPassword(data: { email: string }): {
|
|
106
|
+
valid: boolean;
|
|
107
|
+
error?: string;
|
|
108
|
+
} {
|
|
109
|
+
if (!data.email) {
|
|
110
|
+
return { valid: false, error: "Email is required" };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (!isValidEmail(data.email)) {
|
|
114
|
+
return { valid: false, error: "Invalid email format" };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return { valid: true };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Validate password reset confirmation
|
|
122
|
+
*
|
|
123
|
+
* @param data - Reset password data
|
|
124
|
+
* @returns Validation result with error message
|
|
125
|
+
*/
|
|
126
|
+
export function validateResetPassword(data: {
|
|
127
|
+
token: string;
|
|
128
|
+
password: string;
|
|
129
|
+
confirmPassword: string;
|
|
130
|
+
}): {
|
|
131
|
+
valid: boolean;
|
|
132
|
+
error?: string;
|
|
133
|
+
} {
|
|
134
|
+
if (!data.token) {
|
|
135
|
+
return { valid: false, error: "Invalid reset token" };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (!data.password) {
|
|
139
|
+
return { valid: false, error: "Password is required" };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (!isValidPassword(data.password)) {
|
|
143
|
+
return { valid: false, error: "Password must be at least 8 characters" };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (data.password !== data.confirmPassword) {
|
|
147
|
+
return { valid: false, error: "Passwords do not match" };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return { valid: true };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Get user display name
|
|
155
|
+
*
|
|
156
|
+
* @param user - User object
|
|
157
|
+
* @returns Display name or email fallback
|
|
158
|
+
*/
|
|
159
|
+
export function getUserDisplayName(user: User | null): string {
|
|
160
|
+
if (!user) return "Guest";
|
|
161
|
+
return user.name || user.email || "User";
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Get user initials
|
|
166
|
+
*
|
|
167
|
+
* @param user - User object
|
|
168
|
+
* @returns User initials (up to 2 characters)
|
|
169
|
+
*/
|
|
170
|
+
export function getUserInitials(user: User | null): string {
|
|
171
|
+
if (!user) return "G";
|
|
172
|
+
const name = user.name || user.email || "";
|
|
173
|
+
const parts = name.trim().split(" ");
|
|
174
|
+
if (parts.length >= 2) {
|
|
175
|
+
return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
|
|
176
|
+
}
|
|
177
|
+
return name.slice(0, 2).toUpperCase();
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Check if user has verified email
|
|
182
|
+
*
|
|
183
|
+
* @param user - User object
|
|
184
|
+
* @returns Whether email is verified
|
|
185
|
+
*/
|
|
186
|
+
export function isEmailVerified(user: User | null): boolean {
|
|
187
|
+
return user?.emailVerified === true;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Format user creation date
|
|
192
|
+
*
|
|
193
|
+
* @param user - User object
|
|
194
|
+
* @param locale - Locale for formatting (default: en-US)
|
|
195
|
+
* @returns Formatted date string or empty string
|
|
196
|
+
*/
|
|
197
|
+
export function formatUserCreatedAt(user: User | null, locale: string = "en-US"): string {
|
|
198
|
+
if (!user.createdAt) return "";
|
|
199
|
+
return new Date(user.createdAt).toLocaleDateString(locale, {
|
|
200
|
+
year: "numeric",
|
|
201
|
+
month: "long",
|
|
202
|
+
day: "numeric",
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Mask email for privacy (e.g., u***@example.com)
|
|
208
|
+
*
|
|
209
|
+
* @param email - Email address
|
|
210
|
+
* @returns Masked email
|
|
211
|
+
*/
|
|
212
|
+
export function maskEmail(email: string): string {
|
|
213
|
+
const [local, domain] = email.split("@");
|
|
214
|
+
if (!local || !domain) return email;
|
|
215
|
+
|
|
216
|
+
const maskedLocal = local[0] + "***";
|
|
217
|
+
return `${maskedLocal}@${domain}`;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Generate secure random token (for demo purposes)
|
|
222
|
+
* In production, use a proper crypto library
|
|
223
|
+
*
|
|
224
|
+
* @param length - Token length in bytes (default: 32)
|
|
225
|
+
* @returns Random hex token
|
|
226
|
+
*/
|
|
227
|
+
export function generateResetToken(length: number = 32): string {
|
|
228
|
+
const chars = "0123456789abcdef";
|
|
229
|
+
let token = "";
|
|
230
|
+
for (let i = 0; i < length * 2; i++) {
|
|
231
|
+
token += chars[Math.floor(Math.random() * chars.length)];
|
|
232
|
+
}
|
|
233
|
+
return token;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Calculate password strength score (0-100)
|
|
238
|
+
*
|
|
239
|
+
* @param password - Password to evaluate
|
|
240
|
+
* @returns Strength score
|
|
241
|
+
*/
|
|
242
|
+
export function calculatePasswordStrength(password: string): number {
|
|
243
|
+
let score = 0;
|
|
244
|
+
|
|
245
|
+
// Length
|
|
246
|
+
if (password.length >= 8) score += 25;
|
|
247
|
+
if (password.length >= 12) score += 25;
|
|
248
|
+
|
|
249
|
+
// Variety
|
|
250
|
+
if (/[a-z]/.test(password)) score += 12.5;
|
|
251
|
+
if (/[A-Z]/.test(password)) score += 12.5;
|
|
252
|
+
if (/[0-9]/.test(password)) score += 12.5;
|
|
253
|
+
if (/[^a-zA-Z0-9]/.test(password)) score += 12.5;
|
|
254
|
+
|
|
255
|
+
return Math.min(score, 100);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Get password strength label
|
|
260
|
+
*
|
|
261
|
+
* @param password - Password to evaluate
|
|
262
|
+
* @returns Strength label
|
|
263
|
+
*/
|
|
264
|
+
export function getPasswordStrengthLabel(password: string): string {
|
|
265
|
+
const score = calculatePasswordStrength(password);
|
|
266
|
+
if (score < 25) return "Weak";
|
|
267
|
+
if (score < 50) return "Fair";
|
|
268
|
+
if (score < 75) return "Good";
|
|
269
|
+
return "Strong";
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Sanitize user input for display
|
|
274
|
+
*
|
|
275
|
+
* @param input - User input string
|
|
276
|
+
* @returns Sanitized string
|
|
277
|
+
*/
|
|
278
|
+
export function sanitizeInput(input: string): string {
|
|
279
|
+
return input.trim().slice(0, 1000);
|
|
280
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Utilities
|
|
3
|
+
*
|
|
4
|
+
* Export all auth utilities
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export {
|
|
8
|
+
isValidEmail,
|
|
9
|
+
isValidPassword,
|
|
10
|
+
validateLogin,
|
|
11
|
+
validateRegister,
|
|
12
|
+
validateForgotPassword,
|
|
13
|
+
validateResetPassword,
|
|
14
|
+
getUserDisplayName,
|
|
15
|
+
getUserInitials,
|
|
16
|
+
isEmailVerified,
|
|
17
|
+
formatUserCreatedAt,
|
|
18
|
+
maskEmail,
|
|
19
|
+
generateResetToken,
|
|
20
|
+
calculatePasswordStrength,
|
|
21
|
+
getPasswordStrengthLabel,
|
|
22
|
+
sanitizeInput,
|
|
23
|
+
} from "./auth";
|