mailsentry-auth 0.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/README.md +36 -0
- package/ZUSTAND_MIGRATION.md +348 -0
- package/dist/index.d.mts +1492 -0
- package/dist/index.d.ts +1492 -0
- package/dist/index.js +2996 -0
- package/dist/index.mjs +2996 -0
- package/dist/middleware.mjs +803 -0
- package/dist/utils/cookie-utils.js +225 -0
- package/dist/utils/cookie-utils.mjs +225 -0
- package/package.json +103 -0
- package/src/app/actions/auth-actions.ts +22 -0
- package/src/app/actions/index.ts +3 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2996 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __defProps = Object.defineProperties;
|
|
4
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
5
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
8
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
9
|
+
var __spreadValues = (a, b) => {
|
|
10
|
+
for (var prop in b || (b = {}))
|
|
11
|
+
if (__hasOwnProp.call(b, prop))
|
|
12
|
+
__defNormalProp(a, prop, b[prop]);
|
|
13
|
+
if (__getOwnPropSymbols)
|
|
14
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
15
|
+
if (__propIsEnum.call(b, prop))
|
|
16
|
+
__defNormalProp(a, prop, b[prop]);
|
|
17
|
+
}
|
|
18
|
+
return a;
|
|
19
|
+
};
|
|
20
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
21
|
+
var __objRest = (source, exclude) => {
|
|
22
|
+
var target = {};
|
|
23
|
+
for (var prop in source)
|
|
24
|
+
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
|
|
25
|
+
target[prop] = source[prop];
|
|
26
|
+
if (source != null && __getOwnPropSymbols)
|
|
27
|
+
for (var prop of __getOwnPropSymbols(source)) {
|
|
28
|
+
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
|
|
29
|
+
target[prop] = source[prop];
|
|
30
|
+
}
|
|
31
|
+
return target;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// src/types/http/http-types.ts
|
|
35
|
+
var HttpMethod = /* @__PURE__ */ ((HttpMethod2) => {
|
|
36
|
+
HttpMethod2["GET"] = "GET";
|
|
37
|
+
HttpMethod2["POST"] = "POST";
|
|
38
|
+
HttpMethod2["PUT"] = "PUT";
|
|
39
|
+
HttpMethod2["DELETE"] = "DELETE";
|
|
40
|
+
HttpMethod2["PATCH"] = "PATCH";
|
|
41
|
+
return HttpMethod2;
|
|
42
|
+
})(HttpMethod || {});
|
|
43
|
+
|
|
44
|
+
// src/types/components/profile.ts
|
|
45
|
+
var ProfileUIState = /* @__PURE__ */ ((ProfileUIState2) => {
|
|
46
|
+
ProfileUIState2["LOADING"] = "LOADING";
|
|
47
|
+
ProfileUIState2["UNAUTHENTICATED"] = "UNAUTHENTICATED";
|
|
48
|
+
ProfileUIState2["AUTHENTICATED"] = "AUTHENTICATED";
|
|
49
|
+
return ProfileUIState2;
|
|
50
|
+
})(ProfileUIState || {});
|
|
51
|
+
|
|
52
|
+
// src/types/auth/auth-stepper-enums.ts
|
|
53
|
+
var AuthFlowStep = /* @__PURE__ */ ((AuthFlowStep2) => {
|
|
54
|
+
AuthFlowStep2["EMAIL"] = "email";
|
|
55
|
+
AuthFlowStep2["PASSWORD"] = "password";
|
|
56
|
+
AuthFlowStep2["VERIFICATION"] = "verification";
|
|
57
|
+
return AuthFlowStep2;
|
|
58
|
+
})(AuthFlowStep || {});
|
|
59
|
+
var NextAction = /* @__PURE__ */ ((NextAction2) => {
|
|
60
|
+
NextAction2["LOGIN"] = "login";
|
|
61
|
+
NextAction2["SIGNUP"] = "signup";
|
|
62
|
+
NextAction2["LOGIN_VERIFICATION"] = "login-verification";
|
|
63
|
+
return NextAction2;
|
|
64
|
+
})(NextAction || {});
|
|
65
|
+
|
|
66
|
+
// src/types/auth/auth-types.ts
|
|
67
|
+
var RoleType = /* @__PURE__ */ ((RoleType2) => {
|
|
68
|
+
RoleType2["USER"] = "USER";
|
|
69
|
+
RoleType2["ADMIN"] = "ADMIN";
|
|
70
|
+
return RoleType2;
|
|
71
|
+
})(RoleType || {});
|
|
72
|
+
|
|
73
|
+
// src/types/event-bus/event-bus.interface.ts
|
|
74
|
+
var BaseEventBus = class {
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// src/types/event-bus/events.ts
|
|
78
|
+
var AuthEventType = /* @__PURE__ */ ((AuthEventType3) => {
|
|
79
|
+
AuthEventType3["LoggedIn"] = "auth.logged_in";
|
|
80
|
+
AuthEventType3["LoggedOut"] = "auth.logged_out";
|
|
81
|
+
AuthEventType3["EmailVerified"] = "auth.email_verified";
|
|
82
|
+
AuthEventType3["SignInRequiredModal"] = "auth.signin_required_modal";
|
|
83
|
+
return AuthEventType3;
|
|
84
|
+
})(AuthEventType || {});
|
|
85
|
+
var PageType = /* @__PURE__ */ ((PageType3) => {
|
|
86
|
+
PageType3["LOGIN"] = "/login";
|
|
87
|
+
PageType3["DASHBOARD"] = "/dashboard";
|
|
88
|
+
PageType3["HOME"] = "/";
|
|
89
|
+
return PageType3;
|
|
90
|
+
})(PageType || {});
|
|
91
|
+
var NavigationAction = /* @__PURE__ */ ((NavigationAction2) => {
|
|
92
|
+
NavigationAction2["NONE"] = "none";
|
|
93
|
+
NavigationAction2["REDIRECT"] = "redirect";
|
|
94
|
+
NavigationAction2["MODAL"] = "modal";
|
|
95
|
+
return NavigationAction2;
|
|
96
|
+
})(NavigationAction || {});
|
|
97
|
+
var PageTypePatterns = {
|
|
98
|
+
["/login" /* LOGIN */]: "/login" /* LOGIN */,
|
|
99
|
+
["/dashboard" /* DASHBOARD */]: "/dashboard" /* DASHBOARD */,
|
|
100
|
+
["/" /* HOME */]: "/" /* HOME */
|
|
101
|
+
};
|
|
102
|
+
var CrossTabBehaviorConfig = {
|
|
103
|
+
["/login" /* LOGIN */]: {
|
|
104
|
+
["auth.logged_in" /* LoggedIn */]: { action: "redirect" /* REDIRECT */, target: "/dashboard" /* DASHBOARD */ },
|
|
105
|
+
["auth.logged_out" /* LoggedOut */]: { action: "none" /* NONE */ },
|
|
106
|
+
["auth.email_verified" /* EmailVerified */]: { action: "none" /* NONE */ },
|
|
107
|
+
["auth.signin_required_modal" /* SignInRequiredModal */]: { action: "none" /* NONE */ }
|
|
108
|
+
},
|
|
109
|
+
["/dashboard" /* DASHBOARD */]: {
|
|
110
|
+
["auth.logged_in" /* LoggedIn */]: { action: "modal" /* MODAL */ },
|
|
111
|
+
["auth.logged_out" /* LoggedOut */]: { action: "redirect" /* REDIRECT */, target: "/dashboard" /* DASHBOARD */ },
|
|
112
|
+
["auth.email_verified" /* EmailVerified */]: { action: "modal" /* MODAL */ },
|
|
113
|
+
["auth.signin_required_modal" /* SignInRequiredModal */]: { action: "none" /* NONE */ }
|
|
114
|
+
},
|
|
115
|
+
["/" /* HOME */]: {
|
|
116
|
+
["auth.logged_in" /* LoggedIn */]: { action: "modal" /* MODAL */ },
|
|
117
|
+
["auth.logged_out" /* LoggedOut */]: { action: "redirect" /* REDIRECT */, target: "/" /* HOME */ },
|
|
118
|
+
["auth.email_verified" /* EmailVerified */]: { action: "modal" /* MODAL */ },
|
|
119
|
+
["auth.signin_required_modal" /* SignInRequiredModal */]: { action: "none" /* NONE */ }
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
// src/config/channels.ts
|
|
124
|
+
var Channel = /* @__PURE__ */ ((Channel2) => {
|
|
125
|
+
Channel2["AUTH"] = "auth-event-channel";
|
|
126
|
+
return Channel2;
|
|
127
|
+
})(Channel || {});
|
|
128
|
+
|
|
129
|
+
// src/config/auth-steps.tsx
|
|
130
|
+
import {
|
|
131
|
+
MailOutlined,
|
|
132
|
+
LockOutlined,
|
|
133
|
+
CheckCircleOutlined
|
|
134
|
+
} from "@ant-design/icons";
|
|
135
|
+
import { jsx } from "react/jsx-runtime";
|
|
136
|
+
var createAuthSteps = (options) => {
|
|
137
|
+
const { authIntent = "login" /* LOGIN */, nextAction } = options || {};
|
|
138
|
+
return [
|
|
139
|
+
{
|
|
140
|
+
id: "email" /* EMAIL */,
|
|
141
|
+
title: "Email",
|
|
142
|
+
description: "Enter your email",
|
|
143
|
+
icon: /* @__PURE__ */ jsx(MailOutlined, {})
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
id: "password" /* PASSWORD */,
|
|
147
|
+
title: authIntent === "login" /* LOGIN */ ? "Sign In" : "Sign Up",
|
|
148
|
+
description: authIntent === "login" /* LOGIN */ ? "Enter password" : "Create password",
|
|
149
|
+
icon: /* @__PURE__ */ jsx(LockOutlined, {})
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
id: "verification" /* VERIFICATION */,
|
|
153
|
+
title: "Verification",
|
|
154
|
+
description: "Verify email",
|
|
155
|
+
icon: /* @__PURE__ */ jsx(CheckCircleOutlined, {}),
|
|
156
|
+
disabled: authIntent === "login" /* LOGIN */ || nextAction === "login" /* LOGIN */
|
|
157
|
+
}
|
|
158
|
+
];
|
|
159
|
+
};
|
|
160
|
+
var getStepProgressMessage = (step) => {
|
|
161
|
+
const messages = {
|
|
162
|
+
["email" /* EMAIL */]: "Starting...",
|
|
163
|
+
["password" /* PASSWORD */]: "Authenticating...",
|
|
164
|
+
["verification" /* VERIFICATION */]: "Verifying..."
|
|
165
|
+
};
|
|
166
|
+
return messages[step] || "Processing...";
|
|
167
|
+
};
|
|
168
|
+
var getAuthPageStepMessage = (step) => {
|
|
169
|
+
const messages = {
|
|
170
|
+
["email" /* EMAIL */]: "Checking...",
|
|
171
|
+
["password" /* PASSWORD */]: "Authenticating...",
|
|
172
|
+
["verification" /* VERIFICATION */]: "Verifying..."
|
|
173
|
+
};
|
|
174
|
+
return messages[step] || "Processing...";
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
// src/config/step-navigation.ts
|
|
178
|
+
var EMAIL_SUBMISSION_NAVIGATION = {
|
|
179
|
+
["login-verification" /* LOGIN_VERIFICATION */]: "verification" /* VERIFICATION */,
|
|
180
|
+
["login" /* LOGIN */]: "password" /* PASSWORD */,
|
|
181
|
+
["signup" /* SIGNUP */]: "password" /* PASSWORD */
|
|
182
|
+
};
|
|
183
|
+
var PASSWORD_SUBMISSION_NAVIGATION = {
|
|
184
|
+
VERIFIED: "verification" /* VERIFICATION */,
|
|
185
|
+
UNVERIFIED: "verification" /* VERIFICATION */
|
|
186
|
+
};
|
|
187
|
+
var VERIFICATION_SUBMISSION_NAVIGATION = {
|
|
188
|
+
SUCCESS: "verification" /* VERIFICATION */,
|
|
189
|
+
ERROR: "verification" /* VERIFICATION */
|
|
190
|
+
// Stay on verification step on error
|
|
191
|
+
};
|
|
192
|
+
var getStepForEmailSubmission = (nextAction) => {
|
|
193
|
+
return EMAIL_SUBMISSION_NAVIGATION[nextAction] || "password" /* PASSWORD */;
|
|
194
|
+
};
|
|
195
|
+
var getStepForPasswordSubmission = (isVerified) => {
|
|
196
|
+
return isVerified ? PASSWORD_SUBMISSION_NAVIGATION.VERIFIED : PASSWORD_SUBMISSION_NAVIGATION.UNVERIFIED;
|
|
197
|
+
};
|
|
198
|
+
var getStepForVerificationSubmission = (success) => {
|
|
199
|
+
return success ? VERIFICATION_SUBMISSION_NAVIGATION.SUCCESS : VERIFICATION_SUBMISSION_NAVIGATION.ERROR;
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
// src/config/form-fields.tsx
|
|
203
|
+
import { Input } from "antd";
|
|
204
|
+
import { MailOutlined as MailOutlined2, LockOutlined as LockOutlined2, CheckCircleOutlined as CheckCircleOutlined2 } from "@ant-design/icons";
|
|
205
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
206
|
+
var getEmailField = (options = {}) => ({
|
|
207
|
+
name: "email",
|
|
208
|
+
label: "Email",
|
|
209
|
+
component: /* @__PURE__ */ jsx2(
|
|
210
|
+
Input,
|
|
211
|
+
__spreadProps(__spreadValues({}, options), {
|
|
212
|
+
prefix: /* @__PURE__ */ jsx2(MailOutlined2, {}),
|
|
213
|
+
placeholder: "Email",
|
|
214
|
+
size: "large"
|
|
215
|
+
})
|
|
216
|
+
),
|
|
217
|
+
rules: !options.readOnly ? [
|
|
218
|
+
{ required: true, message: "Please enter your email!" },
|
|
219
|
+
{ type: "email", message: "Please enter a valid email!" }
|
|
220
|
+
] : void 0
|
|
221
|
+
});
|
|
222
|
+
var getPasswordField = (isLogin, disabled = false, options = {}) => ({
|
|
223
|
+
name: "password",
|
|
224
|
+
label: isLogin ? "Password" : "Create a Password",
|
|
225
|
+
component: /* @__PURE__ */ jsx2(
|
|
226
|
+
Input.Password,
|
|
227
|
+
__spreadProps(__spreadValues({}, options), {
|
|
228
|
+
prefix: /* @__PURE__ */ jsx2(LockOutlined2, {}),
|
|
229
|
+
placeholder: isLogin ? "Enter your password" : "Create a password",
|
|
230
|
+
disabled,
|
|
231
|
+
size: "large"
|
|
232
|
+
})
|
|
233
|
+
),
|
|
234
|
+
rules: [
|
|
235
|
+
{ required: true, message: "Please enter your password!" }
|
|
236
|
+
]
|
|
237
|
+
});
|
|
238
|
+
var getVerificationField = (codeLength = 5, options = {}) => ({
|
|
239
|
+
name: "verificationCode",
|
|
240
|
+
component: /* @__PURE__ */ jsx2(
|
|
241
|
+
Input,
|
|
242
|
+
__spreadProps(__spreadValues({}, options), {
|
|
243
|
+
prefix: /* @__PURE__ */ jsx2(CheckCircleOutlined2, {}),
|
|
244
|
+
placeholder: "Enter verification code",
|
|
245
|
+
maxLength: codeLength,
|
|
246
|
+
size: "large"
|
|
247
|
+
})
|
|
248
|
+
),
|
|
249
|
+
rules: [
|
|
250
|
+
{ required: true, message: "Please enter the verification code!" },
|
|
251
|
+
{ len: codeLength, message: `Verification code must be ${codeLength} characters!` }
|
|
252
|
+
]
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// src/config/middleware.ts
|
|
256
|
+
var MiddlewareConfig = class {
|
|
257
|
+
};
|
|
258
|
+
// Protected routes
|
|
259
|
+
MiddlewareConfig.PROTECTED_ROUTES = {
|
|
260
|
+
DASHBOARD: "/"
|
|
261
|
+
};
|
|
262
|
+
// Auth routes
|
|
263
|
+
MiddlewareConfig.AUTH_ROUTES = {
|
|
264
|
+
LOGIN: "/login"
|
|
265
|
+
};
|
|
266
|
+
// HTTP methods to process
|
|
267
|
+
MiddlewareConfig.ALLOWED_METHODS = ["GET", "HEAD"];
|
|
268
|
+
// Query parameters
|
|
269
|
+
MiddlewareConfig.QUERY_PARAMS = {
|
|
270
|
+
LOGIN_REQUIRED: "sign_in_required",
|
|
271
|
+
AUTH_CHECKED: "auth_checked"
|
|
272
|
+
};
|
|
273
|
+
// Query parameter values
|
|
274
|
+
MiddlewareConfig.QUERY_VALUES = {
|
|
275
|
+
LOGIN_REQUIRED: "true",
|
|
276
|
+
AUTH_CHECKED: "1"
|
|
277
|
+
};
|
|
278
|
+
var middlewareMatcher = [
|
|
279
|
+
"/((?!api|_next/static|_next/image|_next/webpack-hmr|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp|ico|css|js)$).*)",
|
|
280
|
+
"/login"
|
|
281
|
+
];
|
|
282
|
+
var config = {
|
|
283
|
+
matcher: middlewareMatcher
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
// src/store/user-store.ts
|
|
287
|
+
import { create } from "zustand";
|
|
288
|
+
import { devtools, subscribeWithSelector } from "zustand/middleware";
|
|
289
|
+
import { immer } from "zustand/middleware/immer";
|
|
290
|
+
import { useShallow } from "zustand/react/shallow";
|
|
291
|
+
|
|
292
|
+
// node_modules/.pnpm/js-cookie@3.0.5/node_modules/js-cookie/dist/js.cookie.mjs
|
|
293
|
+
function assign(target) {
|
|
294
|
+
for (var i = 1; i < arguments.length; i++) {
|
|
295
|
+
var source = arguments[i];
|
|
296
|
+
for (var key in source) {
|
|
297
|
+
target[key] = source[key];
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return target;
|
|
301
|
+
}
|
|
302
|
+
var defaultConverter = {
|
|
303
|
+
read: function(value) {
|
|
304
|
+
if (value[0] === '"') {
|
|
305
|
+
value = value.slice(1, -1);
|
|
306
|
+
}
|
|
307
|
+
return value.replace(/(%[\dA-F]{2})+/gi, decodeURIComponent);
|
|
308
|
+
},
|
|
309
|
+
write: function(value) {
|
|
310
|
+
return encodeURIComponent(value).replace(
|
|
311
|
+
/%(2[346BF]|3[AC-F]|40|5[BDE]|60|7[BCD])/g,
|
|
312
|
+
decodeURIComponent
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
function init(converter, defaultAttributes) {
|
|
317
|
+
function set(name, value, attributes) {
|
|
318
|
+
if (typeof document === "undefined") {
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
attributes = assign({}, defaultAttributes, attributes);
|
|
322
|
+
if (typeof attributes.expires === "number") {
|
|
323
|
+
attributes.expires = new Date(Date.now() + attributes.expires * 864e5);
|
|
324
|
+
}
|
|
325
|
+
if (attributes.expires) {
|
|
326
|
+
attributes.expires = attributes.expires.toUTCString();
|
|
327
|
+
}
|
|
328
|
+
name = encodeURIComponent(name).replace(/%(2[346B]|5E|60|7C)/g, decodeURIComponent).replace(/[()]/g, escape);
|
|
329
|
+
var stringifiedAttributes = "";
|
|
330
|
+
for (var attributeName in attributes) {
|
|
331
|
+
if (!attributes[attributeName]) {
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
stringifiedAttributes += "; " + attributeName;
|
|
335
|
+
if (attributes[attributeName] === true) {
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
stringifiedAttributes += "=" + attributes[attributeName].split(";")[0];
|
|
339
|
+
}
|
|
340
|
+
return document.cookie = name + "=" + converter.write(value, name) + stringifiedAttributes;
|
|
341
|
+
}
|
|
342
|
+
function get(name) {
|
|
343
|
+
if (typeof document === "undefined" || arguments.length && !name) {
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
var cookies = document.cookie ? document.cookie.split("; ") : [];
|
|
347
|
+
var jar = {};
|
|
348
|
+
for (var i = 0; i < cookies.length; i++) {
|
|
349
|
+
var parts = cookies[i].split("=");
|
|
350
|
+
var value = parts.slice(1).join("=");
|
|
351
|
+
try {
|
|
352
|
+
var found = decodeURIComponent(parts[0]);
|
|
353
|
+
jar[found] = converter.read(value, found);
|
|
354
|
+
if (name === found) {
|
|
355
|
+
break;
|
|
356
|
+
}
|
|
357
|
+
} catch (e) {
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return name ? jar[name] : jar;
|
|
361
|
+
}
|
|
362
|
+
return Object.create(
|
|
363
|
+
{
|
|
364
|
+
set,
|
|
365
|
+
get,
|
|
366
|
+
remove: function(name, attributes) {
|
|
367
|
+
set(
|
|
368
|
+
name,
|
|
369
|
+
"",
|
|
370
|
+
assign({}, attributes, {
|
|
371
|
+
expires: -1
|
|
372
|
+
})
|
|
373
|
+
);
|
|
374
|
+
},
|
|
375
|
+
withAttributes: function(attributes) {
|
|
376
|
+
return init(this.converter, assign({}, this.attributes, attributes));
|
|
377
|
+
},
|
|
378
|
+
withConverter: function(converter2) {
|
|
379
|
+
return init(assign({}, this.converter, converter2), this.attributes);
|
|
380
|
+
}
|
|
381
|
+
},
|
|
382
|
+
{
|
|
383
|
+
attributes: { value: Object.freeze(defaultAttributes) },
|
|
384
|
+
converter: { value: Object.freeze(converter) }
|
|
385
|
+
}
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
var api = init(defaultConverter, { path: "/" });
|
|
389
|
+
|
|
390
|
+
// src/services/utils/cookie-utils.ts
|
|
391
|
+
var _CookieUtils = class _CookieUtils {
|
|
392
|
+
/**
|
|
393
|
+
* Get the access token cookie key from environment variables
|
|
394
|
+
*/
|
|
395
|
+
static getAccessTokenKey() {
|
|
396
|
+
return process.env.AUTH_ACCESS_TOKEN_KEY || "auth_access_token";
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Get the refresh token cookie key from environment variables
|
|
400
|
+
*/
|
|
401
|
+
static getRefreshTokenKey() {
|
|
402
|
+
return process.env.AUTH_REFRESH_TOKEN_KEY || "auth_refresh_token";
|
|
403
|
+
}
|
|
404
|
+
// Use current domain in development
|
|
405
|
+
/**
|
|
406
|
+
* Get root domain for subdomain support
|
|
407
|
+
*/
|
|
408
|
+
static getRootDomain() {
|
|
409
|
+
if (typeof window === "undefined") return void 0;
|
|
410
|
+
const hostname = window.location.hostname;
|
|
411
|
+
if (hostname === "localhost" || hostname === "127.0.0.1") {
|
|
412
|
+
return void 0;
|
|
413
|
+
}
|
|
414
|
+
if (/^\d+\.\d+\.\d+\.\d+$/.test(hostname)) {
|
|
415
|
+
return void 0;
|
|
416
|
+
}
|
|
417
|
+
const parts = hostname.split(".");
|
|
418
|
+
if (parts.length >= 2) {
|
|
419
|
+
return `.${parts.slice(-2).join(".")}`;
|
|
420
|
+
}
|
|
421
|
+
return void 0;
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Get common cookie options
|
|
425
|
+
*/
|
|
426
|
+
static getCookieOptions() {
|
|
427
|
+
return {
|
|
428
|
+
path: "/",
|
|
429
|
+
sameSite: "strict",
|
|
430
|
+
secure: process.env.NODE_ENV === "production",
|
|
431
|
+
// Only secure in production
|
|
432
|
+
domain: this.COOKIE_DOMAIN
|
|
433
|
+
// Support subdomains
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Check if running on client side
|
|
438
|
+
*/
|
|
439
|
+
static isClientSide() {
|
|
440
|
+
return typeof window !== "undefined";
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Check if running on server side
|
|
444
|
+
*/
|
|
445
|
+
static isServerSide() {
|
|
446
|
+
return typeof window === "undefined";
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Dynamically import server action for server-side operations
|
|
450
|
+
*/
|
|
451
|
+
static async getServerTokens() {
|
|
452
|
+
try {
|
|
453
|
+
const { getAuthTokens } = await import("mailsentry-auth/server");
|
|
454
|
+
return await getAuthTokens();
|
|
455
|
+
} catch (error) {
|
|
456
|
+
console.error("Failed to get server tokens:", error);
|
|
457
|
+
return { accessToken: null, refreshToken: null };
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Set access token in cookie (client-side only)
|
|
462
|
+
*/
|
|
463
|
+
static setAccessToken(token) {
|
|
464
|
+
if (this.isServerSide()) {
|
|
465
|
+
console.warn("setAccessToken called on server side - use server actions instead");
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
const options = this.getCookieOptions();
|
|
469
|
+
api.set(this.getAccessTokenKey(), token, options);
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Set refresh token in cookie (client-side only)
|
|
473
|
+
*/
|
|
474
|
+
static setRefreshToken(token) {
|
|
475
|
+
if (this.isServerSide()) {
|
|
476
|
+
console.warn("setRefreshToken called on server side - use server actions instead");
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
const options = this.getCookieOptions();
|
|
480
|
+
api.set(this.getRefreshTokenKey(), token, options);
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Get access token - automatically handles client/server side
|
|
484
|
+
*/
|
|
485
|
+
static async getAccessToken() {
|
|
486
|
+
if (this.isClientSide()) {
|
|
487
|
+
return api.get(this.getAccessTokenKey()) || null;
|
|
488
|
+
} else {
|
|
489
|
+
const { accessToken } = await this.getServerTokens();
|
|
490
|
+
return accessToken;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Get refresh token - automatically handles client/server side
|
|
495
|
+
*/
|
|
496
|
+
static async getRefreshToken() {
|
|
497
|
+
if (this.isClientSide()) {
|
|
498
|
+
return api.get(this.getRefreshTokenKey()) || null;
|
|
499
|
+
} else {
|
|
500
|
+
const { refreshToken } = await this.getServerTokens();
|
|
501
|
+
return refreshToken;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* Get both tokens - automatically handles client/server side
|
|
506
|
+
*/
|
|
507
|
+
static async getTokens() {
|
|
508
|
+
if (this.isClientSide()) {
|
|
509
|
+
return {
|
|
510
|
+
accessToken: api.get(this.getAccessTokenKey()) || null,
|
|
511
|
+
refreshToken: api.get(this.getRefreshTokenKey()) || null
|
|
512
|
+
};
|
|
513
|
+
} else {
|
|
514
|
+
return await this.getServerTokens();
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Clear all authentication cookies (client-side only)
|
|
519
|
+
*/
|
|
520
|
+
static clearAuthCookies() {
|
|
521
|
+
if (this.isServerSide()) {
|
|
522
|
+
console.warn("clearAuthCookies called on server side - use server actions instead");
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
const removeOptions = {
|
|
526
|
+
path: "/",
|
|
527
|
+
domain: this.COOKIE_DOMAIN
|
|
528
|
+
};
|
|
529
|
+
api.remove(this.getAccessTokenKey(), removeOptions);
|
|
530
|
+
api.remove(this.getRefreshTokenKey(), removeOptions);
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* Check if cookies are supported/enabled (client-side only)
|
|
534
|
+
*/
|
|
535
|
+
static areCookiesEnabled() {
|
|
536
|
+
if (this.isServerSide()) {
|
|
537
|
+
console.warn("areCookiesEnabled called on server side - not applicable");
|
|
538
|
+
return false;
|
|
539
|
+
}
|
|
540
|
+
try {
|
|
541
|
+
const testKey = "test_cookie_support";
|
|
542
|
+
const testOptions = this.getCookieOptions();
|
|
543
|
+
api.set(testKey, "test", testOptions);
|
|
544
|
+
const value = api.get(testKey);
|
|
545
|
+
api.remove(testKey, {
|
|
546
|
+
path: "/",
|
|
547
|
+
domain: this.COOKIE_DOMAIN
|
|
548
|
+
});
|
|
549
|
+
return value === "test";
|
|
550
|
+
} catch (e) {
|
|
551
|
+
return false;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Get all authentication cookies - automatically handles client/server side
|
|
556
|
+
*/
|
|
557
|
+
static async getAllAuthCookies() {
|
|
558
|
+
if (this.isClientSide()) {
|
|
559
|
+
return {
|
|
560
|
+
accessToken: api.get(this.getAccessTokenKey()) || "",
|
|
561
|
+
refreshToken: api.get(this.getRefreshTokenKey()) || ""
|
|
562
|
+
};
|
|
563
|
+
} else {
|
|
564
|
+
const { accessToken, refreshToken } = await this.getServerTokens();
|
|
565
|
+
return {
|
|
566
|
+
accessToken: accessToken || "",
|
|
567
|
+
refreshToken: refreshToken || ""
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Set multiple tokens at once (client-side only)
|
|
573
|
+
*/
|
|
574
|
+
static setTokens(accessToken, refreshToken) {
|
|
575
|
+
if (this.isServerSide()) {
|
|
576
|
+
console.warn("setTokens called on server side - use server actions instead");
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
this.setAccessToken(accessToken);
|
|
580
|
+
this.setRefreshToken(refreshToken);
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* Check if user has valid tokens - automatically handles client/server side
|
|
584
|
+
*/
|
|
585
|
+
static async hasValidTokens() {
|
|
586
|
+
const tokens = await this.getTokens();
|
|
587
|
+
return !!(tokens.accessToken && tokens.refreshToken);
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* Get current domain information for debugging
|
|
591
|
+
*/
|
|
592
|
+
static getDomainInfo() {
|
|
593
|
+
if (this.isServerSide()) {
|
|
594
|
+
return {
|
|
595
|
+
error: "Server-side rendering - no domain info available",
|
|
596
|
+
environment: process.env.NODE_ENV || "unknown",
|
|
597
|
+
recommendation: "Use server actions for server-side cookie access"
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
return {
|
|
601
|
+
hostname: window.location.hostname,
|
|
602
|
+
domain: this.COOKIE_DOMAIN || "current domain",
|
|
603
|
+
environment: process.env.NODE_ENV || "unknown",
|
|
604
|
+
protocol: window.location.protocol
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
};
|
|
608
|
+
// Domain configuration
|
|
609
|
+
_CookieUtils.COOKIE_DOMAIN = process.env.NODE_ENV === "production" ? _CookieUtils.getRootDomain() : void 0;
|
|
610
|
+
var CookieUtils = _CookieUtils;
|
|
611
|
+
|
|
612
|
+
// src/services/utils/localstorage-utils.ts
|
|
613
|
+
var LocalStorageUtils = class {
|
|
614
|
+
// 5 minutes
|
|
615
|
+
/**
|
|
616
|
+
* Check if localStorage is available
|
|
617
|
+
*/
|
|
618
|
+
static isAvailable() {
|
|
619
|
+
try {
|
|
620
|
+
if (typeof window === "undefined") return false;
|
|
621
|
+
localStorage.setItem("test", "test");
|
|
622
|
+
localStorage.removeItem("test");
|
|
623
|
+
return true;
|
|
624
|
+
} catch (e) {
|
|
625
|
+
return false;
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
/**
|
|
629
|
+
* Save user profile to localStorage with timestamp
|
|
630
|
+
*/
|
|
631
|
+
static saveUserProfile(userProfile) {
|
|
632
|
+
if (!this.isAvailable() || !userProfile) return false;
|
|
633
|
+
try {
|
|
634
|
+
localStorage.setItem(this.USER_PROFILE_STORAGE_KEY, JSON.stringify(userProfile));
|
|
635
|
+
localStorage.setItem(this.USER_PROFILE_TIMESTAMP_KEY, Date.now().toString());
|
|
636
|
+
return true;
|
|
637
|
+
} catch (e) {
|
|
638
|
+
return false;
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
/**
|
|
642
|
+
* Get user profile from localStorage with cache validation
|
|
643
|
+
*/
|
|
644
|
+
static getUserProfile(cacheDuration = this.DEFAULT_CACHE_DURATION) {
|
|
645
|
+
if (!this.isAvailable()) return null;
|
|
646
|
+
try {
|
|
647
|
+
const userProfileData = localStorage.getItem(this.USER_PROFILE_STORAGE_KEY);
|
|
648
|
+
const timestamp = localStorage.getItem(this.USER_PROFILE_TIMESTAMP_KEY);
|
|
649
|
+
if (!userProfileData || !timestamp) return null;
|
|
650
|
+
const cacheAge = Date.now() - parseInt(timestamp);
|
|
651
|
+
if (cacheAge >= cacheDuration) {
|
|
652
|
+
this.clearUserProfile();
|
|
653
|
+
return null;
|
|
654
|
+
}
|
|
655
|
+
return JSON.parse(userProfileData);
|
|
656
|
+
} catch (e) {
|
|
657
|
+
this.clearUserProfile();
|
|
658
|
+
return null;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* Clear user profile data from localStorage
|
|
663
|
+
*/
|
|
664
|
+
static clearUserProfile() {
|
|
665
|
+
if (!this.isAvailable()) return false;
|
|
666
|
+
try {
|
|
667
|
+
localStorage.removeItem(this.USER_PROFILE_STORAGE_KEY);
|
|
668
|
+
localStorage.removeItem(this.USER_PROFILE_TIMESTAMP_KEY);
|
|
669
|
+
return true;
|
|
670
|
+
} catch (e) {
|
|
671
|
+
return false;
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* Check if cached user profile is still valid
|
|
676
|
+
*/
|
|
677
|
+
static isCacheValid(cacheDuration = this.DEFAULT_CACHE_DURATION) {
|
|
678
|
+
if (!this.isAvailable()) return false;
|
|
679
|
+
try {
|
|
680
|
+
const timestamp = localStorage.getItem(this.USER_PROFILE_TIMESTAMP_KEY);
|
|
681
|
+
if (!timestamp) return false;
|
|
682
|
+
const cacheAge = Date.now() - parseInt(timestamp);
|
|
683
|
+
return cacheAge < cacheDuration;
|
|
684
|
+
} catch (e) {
|
|
685
|
+
return false;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
};
|
|
689
|
+
LocalStorageUtils.USER_PROFILE_STORAGE_KEY = "user_profile_data";
|
|
690
|
+
LocalStorageUtils.USER_PROFILE_TIMESTAMP_KEY = "user_profile_timestamp";
|
|
691
|
+
LocalStorageUtils.DEFAULT_CACHE_DURATION = 5 * 60 * 1e3;
|
|
692
|
+
|
|
693
|
+
// src/services/utils/event-bus.ts
|
|
694
|
+
var REGISTRY_KEY = "__bc_event_buses__";
|
|
695
|
+
function getRegistry() {
|
|
696
|
+
const g = globalThis;
|
|
697
|
+
if (!g[REGISTRY_KEY]) {
|
|
698
|
+
g[REGISTRY_KEY] = /* @__PURE__ */ new Map();
|
|
699
|
+
}
|
|
700
|
+
return g[REGISTRY_KEY];
|
|
701
|
+
}
|
|
702
|
+
function isBroadcastChannelSupported() {
|
|
703
|
+
return typeof window !== "undefined" && "BroadcastChannel" in window && typeof BroadcastChannel === "function";
|
|
704
|
+
}
|
|
705
|
+
var BroadcastChannelEventBus = class _BroadcastChannelEventBus {
|
|
706
|
+
constructor(name) {
|
|
707
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
708
|
+
this.name = name;
|
|
709
|
+
if (isBroadcastChannelSupported()) {
|
|
710
|
+
try {
|
|
711
|
+
this.channel = new BroadcastChannel(name);
|
|
712
|
+
this.channel.onmessage = (ev) => this.emit(ev.data);
|
|
713
|
+
} catch (error) {
|
|
714
|
+
console.warn("Failed to create BroadcastChannel:", error);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
static getInstance(name) {
|
|
719
|
+
const registry = getRegistry();
|
|
720
|
+
let bus = registry.get(name);
|
|
721
|
+
if (!bus) {
|
|
722
|
+
bus = new _BroadcastChannelEventBus(name);
|
|
723
|
+
registry.set(name, bus);
|
|
724
|
+
}
|
|
725
|
+
return bus;
|
|
726
|
+
}
|
|
727
|
+
publish(event) {
|
|
728
|
+
var _a;
|
|
729
|
+
this.emit(event);
|
|
730
|
+
(_a = this.channel) == null ? void 0 : _a.postMessage(event);
|
|
731
|
+
}
|
|
732
|
+
subscribe(handler) {
|
|
733
|
+
this.listeners.add(handler);
|
|
734
|
+
return { unsubscribe: () => this.listeners.delete(handler) };
|
|
735
|
+
}
|
|
736
|
+
emit(e) {
|
|
737
|
+
for (const h of this.listeners) h(e);
|
|
738
|
+
}
|
|
739
|
+
close() {
|
|
740
|
+
var _a;
|
|
741
|
+
(_a = this.channel) == null ? void 0 : _a.close();
|
|
742
|
+
this.listeners.clear();
|
|
743
|
+
getRegistry().delete(this.name);
|
|
744
|
+
}
|
|
745
|
+
};
|
|
746
|
+
|
|
747
|
+
// src/services/utils/cross-tab-behavior-handler.ts
|
|
748
|
+
var CrossTabBehaviorHandler = class {
|
|
749
|
+
/**
|
|
750
|
+
* Get current page type based on pathname using pattern matching
|
|
751
|
+
*/
|
|
752
|
+
static getCurrentPageType() {
|
|
753
|
+
var _a, _b;
|
|
754
|
+
if (typeof window === "undefined") return "/" /* HOME */;
|
|
755
|
+
const pathname = window.location.pathname;
|
|
756
|
+
return (_b = (_a = Object.entries(PageTypePatterns).find(
|
|
757
|
+
([pattern]) => pathname.includes(pattern)
|
|
758
|
+
)) == null ? void 0 : _a[1]) != null ? _b : "/" /* HOME */;
|
|
759
|
+
}
|
|
760
|
+
/**
|
|
761
|
+
* Get the action configuration for current route and event
|
|
762
|
+
*/
|
|
763
|
+
static getAction(currentPageType, eventType) {
|
|
764
|
+
var _a, _b;
|
|
765
|
+
return (_b = (_a = CrossTabBehaviorConfig[currentPageType]) == null ? void 0 : _a[eventType]) != null ? _b : { action: "none" /* NONE */ };
|
|
766
|
+
}
|
|
767
|
+
/**
|
|
768
|
+
* Check if current route requires redirect for given event
|
|
769
|
+
*/
|
|
770
|
+
static shouldRedirect(currentPageType, eventType) {
|
|
771
|
+
const action = this.getAction(currentPageType, eventType);
|
|
772
|
+
return action.action === "redirect" /* REDIRECT */ ? action.target : null;
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* Check if current route should show modal for given event
|
|
776
|
+
*/
|
|
777
|
+
static shouldShowModal(currentPageType, eventType) {
|
|
778
|
+
const action = this.getAction(currentPageType, eventType);
|
|
779
|
+
return action.action === "modal" /* MODAL */;
|
|
780
|
+
}
|
|
781
|
+
};
|
|
782
|
+
|
|
783
|
+
// src/services/auth/manager/token-manager.ts
|
|
784
|
+
var TokenManager = class {
|
|
785
|
+
constructor(cookieUtils) {
|
|
786
|
+
this.cookieUtils = cookieUtils;
|
|
787
|
+
}
|
|
788
|
+
saveTokens(accessToken, refreshToken) {
|
|
789
|
+
if (refreshToken) {
|
|
790
|
+
this.cookieUtils.setTokens(accessToken, refreshToken);
|
|
791
|
+
} else {
|
|
792
|
+
this.cookieUtils.setAccessToken(accessToken);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
clearTokens() {
|
|
796
|
+
this.cookieUtils.clearAuthCookies();
|
|
797
|
+
}
|
|
798
|
+
async getAccessToken() {
|
|
799
|
+
return await this.cookieUtils.getAccessToken();
|
|
800
|
+
}
|
|
801
|
+
async getRefreshToken() {
|
|
802
|
+
return await this.cookieUtils.getRefreshToken();
|
|
803
|
+
}
|
|
804
|
+
/**
|
|
805
|
+
* Get all stored tokens for debugging or validation
|
|
806
|
+
*/
|
|
807
|
+
async getAllTokens() {
|
|
808
|
+
return await this.cookieUtils.getAllAuthCookies();
|
|
809
|
+
}
|
|
810
|
+
/**
|
|
811
|
+
* Check if cookies are supported in the current environment
|
|
812
|
+
*/
|
|
813
|
+
areCookiesSupported() {
|
|
814
|
+
return this.cookieUtils.areCookiesEnabled();
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
817
|
+
* Get domain information for debugging
|
|
818
|
+
*/
|
|
819
|
+
getDomainInfo() {
|
|
820
|
+
return this.cookieUtils.getDomainInfo();
|
|
821
|
+
}
|
|
822
|
+
};
|
|
823
|
+
|
|
824
|
+
// src/services/api/base-service.ts
|
|
825
|
+
var HttpClient = class {
|
|
826
|
+
constructor(config2) {
|
|
827
|
+
this.config = config2;
|
|
828
|
+
}
|
|
829
|
+
/**
|
|
830
|
+
* Execute HTTP request
|
|
831
|
+
*/
|
|
832
|
+
async request(endpoint, options) {
|
|
833
|
+
try {
|
|
834
|
+
const url = `${this.config.baseURL}${endpoint}`;
|
|
835
|
+
const fetchOptions = {
|
|
836
|
+
method: options.method,
|
|
837
|
+
headers: await this.buildHeaders(options.headers),
|
|
838
|
+
body: options.body ? JSON.stringify(options.body) : void 0
|
|
839
|
+
};
|
|
840
|
+
const response = await this.executeWithTimeout(
|
|
841
|
+
fetch(url, fetchOptions),
|
|
842
|
+
options.timeout || this.config.timeout || 3e4
|
|
843
|
+
);
|
|
844
|
+
const httpResponse = {
|
|
845
|
+
data: await response.json(),
|
|
846
|
+
status: response.status,
|
|
847
|
+
statusText: response.statusText,
|
|
848
|
+
headers: response.headers,
|
|
849
|
+
ok: response.ok
|
|
850
|
+
};
|
|
851
|
+
return httpResponse;
|
|
852
|
+
} catch (error) {
|
|
853
|
+
const httpError = {
|
|
854
|
+
name: "HttpError",
|
|
855
|
+
message: error instanceof Error ? error.message : "Unknown error",
|
|
856
|
+
status: error instanceof Response ? error.status : void 0,
|
|
857
|
+
statusText: error instanceof Response ? error.statusText : void 0,
|
|
858
|
+
response: error
|
|
859
|
+
};
|
|
860
|
+
throw httpError;
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
/**
|
|
864
|
+
* Build headers with defaults including multiple API keys and Bearer token
|
|
865
|
+
*/
|
|
866
|
+
async buildHeaders(customHeaders) {
|
|
867
|
+
var _a, _b;
|
|
868
|
+
const defaultHeaders = {
|
|
869
|
+
"Content-Type": "application/json"
|
|
870
|
+
};
|
|
871
|
+
if (this.config.origin) {
|
|
872
|
+
defaultHeaders["Origin"] = this.config.origin;
|
|
873
|
+
}
|
|
874
|
+
if (this.config.apiKeys) {
|
|
875
|
+
Object.assign(defaultHeaders, this.config.apiKeys);
|
|
876
|
+
}
|
|
877
|
+
const accessToken = await ((_b = (_a = this.config).getAccessToken) == null ? void 0 : _b.call(_a));
|
|
878
|
+
if (accessToken) {
|
|
879
|
+
defaultHeaders["authorization"] = `Bearer ${accessToken}`;
|
|
880
|
+
}
|
|
881
|
+
return __spreadValues(__spreadValues({}, defaultHeaders), customHeaders);
|
|
882
|
+
}
|
|
883
|
+
/**
|
|
884
|
+
* Execute request with timeout
|
|
885
|
+
*/
|
|
886
|
+
async executeWithTimeout(promise, timeoutMs) {
|
|
887
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
888
|
+
setTimeout(() => reject(new Error(`Request timeout after ${timeoutMs}ms`)), timeoutMs);
|
|
889
|
+
});
|
|
890
|
+
return Promise.race([promise, timeoutPromise]);
|
|
891
|
+
}
|
|
892
|
+
};
|
|
893
|
+
var ApiError = class extends Error {
|
|
894
|
+
constructor(message2, status, statusText, apiError) {
|
|
895
|
+
super(message2);
|
|
896
|
+
this.name = "ApiError";
|
|
897
|
+
this.status = status;
|
|
898
|
+
this.statusText = statusText;
|
|
899
|
+
this.apiError = apiError;
|
|
900
|
+
}
|
|
901
|
+
};
|
|
902
|
+
var BaseService = class {
|
|
903
|
+
constructor(httpClient, tokenManager) {
|
|
904
|
+
this.tokenManager = tokenManager || new TokenManager(CookieUtils);
|
|
905
|
+
this.httpClient = httpClient || this.createDefaultHttpClient();
|
|
906
|
+
}
|
|
907
|
+
/**
|
|
908
|
+
* Create default HTTP client with configuration
|
|
909
|
+
*/
|
|
910
|
+
createDefaultHttpClient() {
|
|
911
|
+
const config2 = {
|
|
912
|
+
baseURL: process.env.NEXT_PUBLIC_API_BASE_URL || "",
|
|
913
|
+
apiKeys: {
|
|
914
|
+
"x-api-key": process.env.NEXT_PUBLIC_API_KEY || ""
|
|
915
|
+
},
|
|
916
|
+
timeout: 3e4,
|
|
917
|
+
retries: 3,
|
|
918
|
+
origin: process.env.NEXT_PUBLIC_API_ORIGIN || "",
|
|
919
|
+
getAccessToken: async () => await this.tokenManager.getAccessToken()
|
|
920
|
+
};
|
|
921
|
+
return new HttpClient(config2);
|
|
922
|
+
}
|
|
923
|
+
/**
|
|
924
|
+
* Handle HTTP response and throw structured error if needed
|
|
925
|
+
*/
|
|
926
|
+
handleResponse(response) {
|
|
927
|
+
if (response.ok) {
|
|
928
|
+
return response.data;
|
|
929
|
+
}
|
|
930
|
+
const apiError = this.createApiErrorFromResponse(response);
|
|
931
|
+
throw new ApiError(
|
|
932
|
+
apiError.message,
|
|
933
|
+
response.status,
|
|
934
|
+
response.statusText,
|
|
935
|
+
apiError
|
|
936
|
+
);
|
|
937
|
+
}
|
|
938
|
+
/**
|
|
939
|
+
* Create API error from HTTP response following Single Responsibility Principle
|
|
940
|
+
*/
|
|
941
|
+
createApiErrorFromResponse(response) {
|
|
942
|
+
const parsedError = this.tryParseApiErrorResponse(response.data);
|
|
943
|
+
if (parsedError) {
|
|
944
|
+
return parsedError;
|
|
945
|
+
}
|
|
946
|
+
return this.createGenericApiError(response);
|
|
947
|
+
}
|
|
948
|
+
/**
|
|
949
|
+
* Try to parse API error response from response data
|
|
950
|
+
*/
|
|
951
|
+
tryParseApiErrorResponse(data) {
|
|
952
|
+
try {
|
|
953
|
+
const responseData = data;
|
|
954
|
+
if (this.isValidApiErrorResponse(responseData)) {
|
|
955
|
+
return __spreadValues({
|
|
956
|
+
message: String(responseData.message),
|
|
957
|
+
error: String(responseData.error),
|
|
958
|
+
statusCode: Number(responseData.statusCode)
|
|
959
|
+
}, responseData);
|
|
960
|
+
}
|
|
961
|
+
return null;
|
|
962
|
+
} catch (e) {
|
|
963
|
+
return null;
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
/**
|
|
967
|
+
* Check if response data has valid API error structure
|
|
968
|
+
*/
|
|
969
|
+
isValidApiErrorResponse(data) {
|
|
970
|
+
return data && typeof data === "object" && "message" in data && "error" in data && "statusCode" in data;
|
|
971
|
+
}
|
|
972
|
+
/**
|
|
973
|
+
* Create generic API error as fallback
|
|
974
|
+
*/
|
|
975
|
+
createGenericApiError(response) {
|
|
976
|
+
return {
|
|
977
|
+
message: response.statusText || "Unknown error",
|
|
978
|
+
error: "HTTP Error",
|
|
979
|
+
statusCode: response.status
|
|
980
|
+
};
|
|
981
|
+
}
|
|
982
|
+
/**
|
|
983
|
+
* GET request
|
|
984
|
+
*/
|
|
985
|
+
async get(endpoint, headers) {
|
|
986
|
+
const response = await this.httpClient.request(endpoint, {
|
|
987
|
+
method: "GET" /* GET */,
|
|
988
|
+
url: endpoint,
|
|
989
|
+
headers
|
|
990
|
+
});
|
|
991
|
+
return this.handleResponse(response);
|
|
992
|
+
}
|
|
993
|
+
/**
|
|
994
|
+
* POST request
|
|
995
|
+
*/
|
|
996
|
+
async post(endpoint, body, headers) {
|
|
997
|
+
const response = await this.httpClient.request(endpoint, {
|
|
998
|
+
method: "POST" /* POST */,
|
|
999
|
+
url: endpoint,
|
|
1000
|
+
headers,
|
|
1001
|
+
body
|
|
1002
|
+
});
|
|
1003
|
+
return this.handleResponse(response);
|
|
1004
|
+
}
|
|
1005
|
+
/**
|
|
1006
|
+
* PUT request
|
|
1007
|
+
*/
|
|
1008
|
+
async put(endpoint, body, headers) {
|
|
1009
|
+
const response = await this.httpClient.request(endpoint, {
|
|
1010
|
+
method: "PUT" /* PUT */,
|
|
1011
|
+
url: endpoint,
|
|
1012
|
+
headers,
|
|
1013
|
+
body
|
|
1014
|
+
});
|
|
1015
|
+
return this.handleResponse(response);
|
|
1016
|
+
}
|
|
1017
|
+
/**
|
|
1018
|
+
* DELETE request
|
|
1019
|
+
*/
|
|
1020
|
+
async delete(endpoint, headers) {
|
|
1021
|
+
const response = await this.httpClient.request(endpoint, {
|
|
1022
|
+
method: "DELETE" /* DELETE */,
|
|
1023
|
+
url: endpoint,
|
|
1024
|
+
headers
|
|
1025
|
+
});
|
|
1026
|
+
return this.handleResponse(response);
|
|
1027
|
+
}
|
|
1028
|
+
/**
|
|
1029
|
+
* PATCH request
|
|
1030
|
+
*/
|
|
1031
|
+
async patch(endpoint, body, headers) {
|
|
1032
|
+
const response = await this.httpClient.request(endpoint, {
|
|
1033
|
+
method: "PATCH" /* PATCH */,
|
|
1034
|
+
url: endpoint,
|
|
1035
|
+
headers,
|
|
1036
|
+
body
|
|
1037
|
+
});
|
|
1038
|
+
return this.handleResponse(response);
|
|
1039
|
+
}
|
|
1040
|
+
};
|
|
1041
|
+
|
|
1042
|
+
// src/services/api/endpoints.ts
|
|
1043
|
+
var AUTH_ENDPOINTS = {
|
|
1044
|
+
// Email existence check
|
|
1045
|
+
CHECK_EMAIL_EXISTS: (email) => `/auth/user/exist-email/${encodeURIComponent(email)}`,
|
|
1046
|
+
// User authentication
|
|
1047
|
+
LOGIN: "/auth/user/login",
|
|
1048
|
+
VERIFY_EMAIL: "/auth/user/verify",
|
|
1049
|
+
// User profile
|
|
1050
|
+
GET_USER_PROFILE: "/auth/user/profile",
|
|
1051
|
+
// User logout
|
|
1052
|
+
LOGOUT: "/auth/logout"
|
|
1053
|
+
};
|
|
1054
|
+
var EndpointBuilder = class {
|
|
1055
|
+
};
|
|
1056
|
+
EndpointBuilder.auth = AUTH_ENDPOINTS;
|
|
1057
|
+
|
|
1058
|
+
// src/services/api/auth-service.ts
|
|
1059
|
+
var AuthService = class extends BaseService {
|
|
1060
|
+
/**
|
|
1061
|
+
* Check if an email exists in the system
|
|
1062
|
+
*/
|
|
1063
|
+
async checkEmailExists(email) {
|
|
1064
|
+
const endpoint = AUTH_ENDPOINTS.CHECK_EMAIL_EXISTS(email);
|
|
1065
|
+
return this.get(endpoint);
|
|
1066
|
+
}
|
|
1067
|
+
/**
|
|
1068
|
+
* Login with email and password
|
|
1069
|
+
*/
|
|
1070
|
+
async login(credentials) {
|
|
1071
|
+
const endpoint = AUTH_ENDPOINTS.LOGIN;
|
|
1072
|
+
return this.post(endpoint, credentials);
|
|
1073
|
+
}
|
|
1074
|
+
/**
|
|
1075
|
+
* Verify email with verification code
|
|
1076
|
+
*/
|
|
1077
|
+
async verifyEmail(verification) {
|
|
1078
|
+
const endpoint = AUTH_ENDPOINTS.VERIFY_EMAIL;
|
|
1079
|
+
return this.post(endpoint, verification);
|
|
1080
|
+
}
|
|
1081
|
+
/**
|
|
1082
|
+
* Get user profile
|
|
1083
|
+
*/
|
|
1084
|
+
async getUserProfile() {
|
|
1085
|
+
const endpoint = AUTH_ENDPOINTS.GET_USER_PROFILE;
|
|
1086
|
+
return this.get(endpoint);
|
|
1087
|
+
}
|
|
1088
|
+
/**
|
|
1089
|
+
* Logout user
|
|
1090
|
+
*/
|
|
1091
|
+
async logout() {
|
|
1092
|
+
const endpoint = AUTH_ENDPOINTS.LOGOUT;
|
|
1093
|
+
return this.post(endpoint);
|
|
1094
|
+
}
|
|
1095
|
+
};
|
|
1096
|
+
|
|
1097
|
+
// src/services/auth/manager/user-storage-manager.ts
|
|
1098
|
+
var UserStorageManager = class {
|
|
1099
|
+
constructor(storageUtils) {
|
|
1100
|
+
this.storageUtils = storageUtils;
|
|
1101
|
+
}
|
|
1102
|
+
/**
|
|
1103
|
+
* Save user profile data to storage
|
|
1104
|
+
*/
|
|
1105
|
+
saveUserProfile(userProfile) {
|
|
1106
|
+
return this.storageUtils.saveUserProfile(userProfile);
|
|
1107
|
+
}
|
|
1108
|
+
/**
|
|
1109
|
+
* Get user profile data from storage
|
|
1110
|
+
*/
|
|
1111
|
+
getUserProfile(cacheDuration) {
|
|
1112
|
+
return this.storageUtils.getUserProfile(cacheDuration);
|
|
1113
|
+
}
|
|
1114
|
+
/**
|
|
1115
|
+
* Clear user profile data from storage
|
|
1116
|
+
*/
|
|
1117
|
+
clearUserProfile() {
|
|
1118
|
+
return this.storageUtils.clearUserProfile();
|
|
1119
|
+
}
|
|
1120
|
+
/**
|
|
1121
|
+
* Check if cached user profile is still valid
|
|
1122
|
+
*/
|
|
1123
|
+
isCacheValid(cacheDuration) {
|
|
1124
|
+
return this.storageUtils.isCacheValid(cacheDuration);
|
|
1125
|
+
}
|
|
1126
|
+
/**
|
|
1127
|
+
* Get user profile with automatic cache validation
|
|
1128
|
+
* Returns null if cache is expired or invalid
|
|
1129
|
+
*/
|
|
1130
|
+
getValidUserProfile(cacheDuration) {
|
|
1131
|
+
if (!this.isCacheValid(cacheDuration)) {
|
|
1132
|
+
return null;
|
|
1133
|
+
}
|
|
1134
|
+
return this.getUserProfile(cacheDuration);
|
|
1135
|
+
}
|
|
1136
|
+
};
|
|
1137
|
+
|
|
1138
|
+
// src/services/auth/patterns/command/auth-result-factory.ts
|
|
1139
|
+
var AuthResultFactory = class {
|
|
1140
|
+
/**
|
|
1141
|
+
* Creates a successful authentication result
|
|
1142
|
+
*/
|
|
1143
|
+
static createSuccess(message2, data) {
|
|
1144
|
+
return {
|
|
1145
|
+
success: true,
|
|
1146
|
+
data,
|
|
1147
|
+
message: message2
|
|
1148
|
+
};
|
|
1149
|
+
}
|
|
1150
|
+
/**
|
|
1151
|
+
* Creates a failed authentication result
|
|
1152
|
+
*/
|
|
1153
|
+
static createFailure(message2, error) {
|
|
1154
|
+
const errorMessage = error instanceof Error ? error.message : typeof error === "string" ? error : error ? String(error) : "Authentication failed";
|
|
1155
|
+
return {
|
|
1156
|
+
success: false,
|
|
1157
|
+
error: errorMessage,
|
|
1158
|
+
message: message2
|
|
1159
|
+
};
|
|
1160
|
+
}
|
|
1161
|
+
};
|
|
1162
|
+
|
|
1163
|
+
// src/services/auth/patterns/logger/development-logger.ts
|
|
1164
|
+
var DevelopmentLogger = class {
|
|
1165
|
+
log(message2, data) {
|
|
1166
|
+
console.log(message2, data);
|
|
1167
|
+
}
|
|
1168
|
+
warn(message2, data) {
|
|
1169
|
+
console.warn(message2, data);
|
|
1170
|
+
}
|
|
1171
|
+
error(message2, data) {
|
|
1172
|
+
console.error(message2, data);
|
|
1173
|
+
}
|
|
1174
|
+
};
|
|
1175
|
+
|
|
1176
|
+
// src/services/auth/patterns/logger/production-logger.ts
|
|
1177
|
+
var ProductionLogger = class {
|
|
1178
|
+
log(_message, _data) {
|
|
1179
|
+
}
|
|
1180
|
+
warn(_message, _data) {
|
|
1181
|
+
}
|
|
1182
|
+
error(_message, _data) {
|
|
1183
|
+
}
|
|
1184
|
+
};
|
|
1185
|
+
|
|
1186
|
+
// src/services/auth/patterns/logger/logger-factory.ts
|
|
1187
|
+
var LoggerFactory = class {
|
|
1188
|
+
static create(environment) {
|
|
1189
|
+
const env = environment || process.env.NODE_ENV || "development";
|
|
1190
|
+
const loggerFactory = this.loggers.get(env) || this.loggers.get("development");
|
|
1191
|
+
return loggerFactory();
|
|
1192
|
+
}
|
|
1193
|
+
};
|
|
1194
|
+
LoggerFactory.loggers = /* @__PURE__ */ new Map([
|
|
1195
|
+
["development", () => new DevelopmentLogger()],
|
|
1196
|
+
["production", () => new ProductionLogger()],
|
|
1197
|
+
["test", () => new DevelopmentLogger()]
|
|
1198
|
+
]);
|
|
1199
|
+
|
|
1200
|
+
// src/services/auth/patterns/strategy/signup-flow-strategy.ts
|
|
1201
|
+
var SignupFlowStrategy = class {
|
|
1202
|
+
constructor(authService, tokenManager) {
|
|
1203
|
+
this.authService = authService;
|
|
1204
|
+
this.tokenManager = tokenManager;
|
|
1205
|
+
}
|
|
1206
|
+
async execute(credentials) {
|
|
1207
|
+
const loginResult = await this.authService.login(credentials);
|
|
1208
|
+
if (!loginResult.data.isVerifiedEmail) {
|
|
1209
|
+
return AuthResultFactory.createFailure("Email verification required");
|
|
1210
|
+
}
|
|
1211
|
+
return this.handleSuccessfulAuthentication(loginResult);
|
|
1212
|
+
}
|
|
1213
|
+
async handleSuccessfulAuthentication(loginResult) {
|
|
1214
|
+
if (loginResult.data.accessToken) {
|
|
1215
|
+
this.tokenManager.saveTokens(
|
|
1216
|
+
loginResult.data.accessToken,
|
|
1217
|
+
loginResult.data.refreshToken
|
|
1218
|
+
);
|
|
1219
|
+
LoggerFactory.create("development").log("Tokens saved successfully:", {
|
|
1220
|
+
hasAccessToken: !!loginResult.data.accessToken,
|
|
1221
|
+
hasRefreshToken: !!loginResult.data.refreshToken,
|
|
1222
|
+
domainInfo: this.tokenManager.getDomainInfo()
|
|
1223
|
+
});
|
|
1224
|
+
const profile = await this.authService.getUserProfile();
|
|
1225
|
+
return AuthResultFactory.createSuccess("Login successful", __spreadProps(__spreadValues({}, loginResult.data), {
|
|
1226
|
+
profile: profile.data,
|
|
1227
|
+
tokenInfo: {
|
|
1228
|
+
domain: this.tokenManager.getDomainInfo().domain
|
|
1229
|
+
}
|
|
1230
|
+
}));
|
|
1231
|
+
}
|
|
1232
|
+
return AuthResultFactory.createFailure("Authentication failed - no access token received");
|
|
1233
|
+
}
|
|
1234
|
+
};
|
|
1235
|
+
|
|
1236
|
+
// src/services/auth/patterns/strategy/existing-user-login-strategy.ts
|
|
1237
|
+
var ExistingUserLoginStrategy = class {
|
|
1238
|
+
constructor(authService, tokenManager) {
|
|
1239
|
+
this.authService = authService;
|
|
1240
|
+
this.tokenManager = tokenManager;
|
|
1241
|
+
}
|
|
1242
|
+
async execute(credentials) {
|
|
1243
|
+
const loginResult = await this.authService.login(credentials);
|
|
1244
|
+
return this.handleSuccessfulAuthentication(loginResult);
|
|
1245
|
+
}
|
|
1246
|
+
async handleSuccessfulAuthentication(loginResult) {
|
|
1247
|
+
if (loginResult.data.accessToken) {
|
|
1248
|
+
this.tokenManager.saveTokens(
|
|
1249
|
+
loginResult.data.accessToken,
|
|
1250
|
+
loginResult.data.refreshToken
|
|
1251
|
+
);
|
|
1252
|
+
LoggerFactory.create("development").log("Tokens saved successfully:", {
|
|
1253
|
+
hasAccessToken: !!loginResult.data.accessToken,
|
|
1254
|
+
hasRefreshToken: !!loginResult.data.refreshToken,
|
|
1255
|
+
domainInfo: this.tokenManager.getDomainInfo()
|
|
1256
|
+
});
|
|
1257
|
+
const profile = await this.authService.getUserProfile();
|
|
1258
|
+
return AuthResultFactory.createSuccess("Login successful", __spreadProps(__spreadValues({}, loginResult.data), {
|
|
1259
|
+
profile: profile.data,
|
|
1260
|
+
tokenInfo: {
|
|
1261
|
+
domain: this.tokenManager.getDomainInfo().domain
|
|
1262
|
+
}
|
|
1263
|
+
}));
|
|
1264
|
+
}
|
|
1265
|
+
return AuthResultFactory.createFailure("Authentication failed - no access token received");
|
|
1266
|
+
}
|
|
1267
|
+
};
|
|
1268
|
+
|
|
1269
|
+
// src/services/auth/patterns/strategy/login-flow-strategy-factory.ts
|
|
1270
|
+
var LoginFlowStrategyFactory = class {
|
|
1271
|
+
static createStrategy(action, authService, tokenManager) {
|
|
1272
|
+
const strategyFactory = this.strategies.get(action);
|
|
1273
|
+
if (!strategyFactory) {
|
|
1274
|
+
throw new Error(`No strategy found for action: ${action}`);
|
|
1275
|
+
}
|
|
1276
|
+
return strategyFactory(authService, tokenManager);
|
|
1277
|
+
}
|
|
1278
|
+
};
|
|
1279
|
+
LoginFlowStrategyFactory.strategies = /* @__PURE__ */ new Map([
|
|
1280
|
+
["signup" /* SIGNUP */, (authService, tokenManager) => new SignupFlowStrategy(authService, tokenManager)],
|
|
1281
|
+
["login" /* LOGIN */, (authService, tokenManager) => new ExistingUserLoginStrategy(authService, tokenManager)]
|
|
1282
|
+
]);
|
|
1283
|
+
|
|
1284
|
+
// src/services/auth/patterns/state/authenticated-state.ts
|
|
1285
|
+
var AuthenticatedState = class {
|
|
1286
|
+
async getStatus(tokenManager) {
|
|
1287
|
+
const authStatus = await this.buildAuthStatus(tokenManager, true);
|
|
1288
|
+
return AuthResultFactory.createSuccess("User is authenticated", authStatus);
|
|
1289
|
+
}
|
|
1290
|
+
async buildAuthStatus(tokenManager, isAuthenticated) {
|
|
1291
|
+
return {
|
|
1292
|
+
isAuthenticated,
|
|
1293
|
+
hasAccessToken: !!await tokenManager.getAccessToken(),
|
|
1294
|
+
hasRefreshToken: !!await tokenManager.getRefreshToken(),
|
|
1295
|
+
cookiesSupported: tokenManager.areCookiesSupported(),
|
|
1296
|
+
domainInfo: tokenManager.getDomainInfo()
|
|
1297
|
+
};
|
|
1298
|
+
}
|
|
1299
|
+
};
|
|
1300
|
+
|
|
1301
|
+
// src/services/auth/patterns/state/unauthenticated-state.ts
|
|
1302
|
+
var UnauthenticatedState = class {
|
|
1303
|
+
async getStatus(tokenManager) {
|
|
1304
|
+
const authStatus = await this.buildAuthStatus(tokenManager, false);
|
|
1305
|
+
return AuthResultFactory.createFailure("User is not authenticated", authStatus);
|
|
1306
|
+
}
|
|
1307
|
+
async buildAuthStatus(tokenManager, isAuthenticated) {
|
|
1308
|
+
return {
|
|
1309
|
+
isAuthenticated,
|
|
1310
|
+
hasAccessToken: !!await tokenManager.getAccessToken(),
|
|
1311
|
+
hasRefreshToken: !!await tokenManager.getRefreshToken(),
|
|
1312
|
+
cookiesSupported: tokenManager.areCookiesSupported(),
|
|
1313
|
+
domainInfo: tokenManager.getDomainInfo()
|
|
1314
|
+
};
|
|
1315
|
+
}
|
|
1316
|
+
};
|
|
1317
|
+
|
|
1318
|
+
// src/services/auth/patterns/state/authentication-status-context.ts
|
|
1319
|
+
var AuthenticationStatusContext = class {
|
|
1320
|
+
static async getStatus(tokenManager) {
|
|
1321
|
+
const isAuthenticated = !!await tokenManager.getAccessToken();
|
|
1322
|
+
const state = this.states.get(isAuthenticated) || new UnauthenticatedState();
|
|
1323
|
+
return await state.getStatus(tokenManager);
|
|
1324
|
+
}
|
|
1325
|
+
};
|
|
1326
|
+
AuthenticationStatusContext.states = /* @__PURE__ */ new Map([
|
|
1327
|
+
[true, new AuthenticatedState()],
|
|
1328
|
+
[false, new UnauthenticatedState()]
|
|
1329
|
+
]);
|
|
1330
|
+
|
|
1331
|
+
// src/services/auth/patterns/chain/base-error-handler.ts
|
|
1332
|
+
var BaseErrorHandler = class {
|
|
1333
|
+
setNext(handler) {
|
|
1334
|
+
this.nextHandler = handler;
|
|
1335
|
+
return handler;
|
|
1336
|
+
}
|
|
1337
|
+
handle(error, context) {
|
|
1338
|
+
if (this.canHandle(error)) {
|
|
1339
|
+
return this.handleError(error, context);
|
|
1340
|
+
}
|
|
1341
|
+
if (this.nextHandler) {
|
|
1342
|
+
return this.nextHandler.handle(error, context);
|
|
1343
|
+
}
|
|
1344
|
+
return AuthResultFactory.createFailure(`${context} failed with unknown error`, error);
|
|
1345
|
+
}
|
|
1346
|
+
};
|
|
1347
|
+
|
|
1348
|
+
// src/services/auth/patterns/chain/validation-error-handler.ts
|
|
1349
|
+
var ValidationErrorHandler = class extends BaseErrorHandler {
|
|
1350
|
+
canHandle(error) {
|
|
1351
|
+
return error instanceof Error && error.message.includes("validation");
|
|
1352
|
+
}
|
|
1353
|
+
handleError(error, context) {
|
|
1354
|
+
console.error(`${context} validation error:`, error);
|
|
1355
|
+
return AuthResultFactory.createFailure(`${context} validation failed`, error);
|
|
1356
|
+
}
|
|
1357
|
+
};
|
|
1358
|
+
|
|
1359
|
+
// src/services/auth/patterns/chain/network-error-handler.ts
|
|
1360
|
+
var NetworkErrorHandler = class extends BaseErrorHandler {
|
|
1361
|
+
canHandle(error) {
|
|
1362
|
+
return error instanceof Error && (error.message.includes("network") || error.message.includes("fetch") || error.message.includes("timeout"));
|
|
1363
|
+
}
|
|
1364
|
+
handleError(error, context) {
|
|
1365
|
+
console.error(`${context} network error:`, error);
|
|
1366
|
+
return AuthResultFactory.createFailure(`${context} network error`, error);
|
|
1367
|
+
}
|
|
1368
|
+
};
|
|
1369
|
+
|
|
1370
|
+
// src/services/auth/patterns/chain/generic-error-handler.ts
|
|
1371
|
+
var GenericErrorHandler = class extends BaseErrorHandler {
|
|
1372
|
+
canHandle(_error) {
|
|
1373
|
+
return true;
|
|
1374
|
+
}
|
|
1375
|
+
handleError(error, context) {
|
|
1376
|
+
console.error(`${context} error:`, error);
|
|
1377
|
+
return AuthResultFactory.createFailure(`${context} failed`, error);
|
|
1378
|
+
}
|
|
1379
|
+
};
|
|
1380
|
+
|
|
1381
|
+
// src/services/auth/auth-orchestrator.ts
|
|
1382
|
+
var AuthOrchestrator = class {
|
|
1383
|
+
constructor(authService, eventBus, tokenManager) {
|
|
1384
|
+
this.authService = authService;
|
|
1385
|
+
this.eventBus = eventBus;
|
|
1386
|
+
this.tokenManager = tokenManager || new TokenManager(CookieUtils);
|
|
1387
|
+
this.userStorageManager = new UserStorageManager(LocalStorageUtils);
|
|
1388
|
+
this.logger = LoggerFactory.create();
|
|
1389
|
+
this.errorHandler = this.setupErrorHandlerChain();
|
|
1390
|
+
this.validateCookieSupport();
|
|
1391
|
+
}
|
|
1392
|
+
/**
|
|
1393
|
+
* Setup Chain of Responsibility for error handling
|
|
1394
|
+
*/
|
|
1395
|
+
setupErrorHandlerChain() {
|
|
1396
|
+
const validationHandler = new ValidationErrorHandler();
|
|
1397
|
+
const networkHandler = new NetworkErrorHandler();
|
|
1398
|
+
const genericHandler = new GenericErrorHandler();
|
|
1399
|
+
validationHandler.setNext(networkHandler).setNext(genericHandler);
|
|
1400
|
+
return validationHandler;
|
|
1401
|
+
}
|
|
1402
|
+
/**
|
|
1403
|
+
* Validate that cookies are supported in the current environment
|
|
1404
|
+
*/
|
|
1405
|
+
validateCookieSupport() {
|
|
1406
|
+
if (!this.tokenManager.areCookiesSupported()) {
|
|
1407
|
+
this.logger.warn("Cookies are not supported in this environment. Authentication may not work properly.");
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
/**
|
|
1411
|
+
* Handle email check to determine if user exists and what action to take
|
|
1412
|
+
*/
|
|
1413
|
+
async handleEmailCheck(email) {
|
|
1414
|
+
try {
|
|
1415
|
+
const emailCheck = await this.authService.checkEmailExists(email);
|
|
1416
|
+
const message2 = {
|
|
1417
|
+
["signup" /* SIGNUP */]: "Welcome! Please create your account.",
|
|
1418
|
+
["login" /* LOGIN */]: "Welcome back! Please enter your password.",
|
|
1419
|
+
["login-verification" /* LOGIN_VERIFICATION */]: "Please verify your email."
|
|
1420
|
+
}[emailCheck.next_action];
|
|
1421
|
+
if (!message2) {
|
|
1422
|
+
throw new Error("Invalid next action");
|
|
1423
|
+
}
|
|
1424
|
+
return AuthResultFactory.createSuccess(message2, emailCheck);
|
|
1425
|
+
} catch (error) {
|
|
1426
|
+
return this.errorHandler.handle(error, "Email check");
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
/**
|
|
1430
|
+
* Handle complete login flow with proper error handling and token management
|
|
1431
|
+
*/
|
|
1432
|
+
async handleLoginFlow(credentials) {
|
|
1433
|
+
try {
|
|
1434
|
+
const emailCheck = await this.authService.checkEmailExists(credentials.email);
|
|
1435
|
+
const strategy = LoginFlowStrategyFactory.createStrategy(
|
|
1436
|
+
emailCheck.next_action,
|
|
1437
|
+
this.authService,
|
|
1438
|
+
this.tokenManager
|
|
1439
|
+
);
|
|
1440
|
+
const result = await strategy.execute(credentials);
|
|
1441
|
+
if (result.success) {
|
|
1442
|
+
const currentPageType = CrossTabBehaviorHandler.getCurrentPageType();
|
|
1443
|
+
this.eventBus.publish({
|
|
1444
|
+
type: "auth.logged_in" /* LoggedIn */,
|
|
1445
|
+
userId: String(result.data.profile.user._id),
|
|
1446
|
+
sourcePageType: currentPageType
|
|
1447
|
+
});
|
|
1448
|
+
}
|
|
1449
|
+
return result;
|
|
1450
|
+
} catch (error) {
|
|
1451
|
+
return this.errorHandler.handle(error, "Login flow");
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
/**
|
|
1455
|
+
* Handle email verification flow
|
|
1456
|
+
*/
|
|
1457
|
+
async handleEmailVerification(email, code) {
|
|
1458
|
+
var _a;
|
|
1459
|
+
try {
|
|
1460
|
+
const verificationRequest = { email, code };
|
|
1461
|
+
const verificationResult = await this.authService.verifyEmail(verificationRequest);
|
|
1462
|
+
const isVerified = verificationResult === true || ((_a = verificationResult == null ? void 0 : verificationResult.data) == null ? void 0 : _a.isVerified);
|
|
1463
|
+
if (isVerified) {
|
|
1464
|
+
this.eventBus.publish({ type: "auth.email_verified" /* EmailVerified */, email });
|
|
1465
|
+
return AuthResultFactory.createSuccess("Email verified successfully", { isVerified: true });
|
|
1466
|
+
}
|
|
1467
|
+
return AuthResultFactory.createFailure("Email verification failed", "Email verification failed");
|
|
1468
|
+
} catch (error) {
|
|
1469
|
+
return this.errorHandler.handle(error, "Email verification");
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
/**
|
|
1473
|
+
* Handle logout flow
|
|
1474
|
+
*/
|
|
1475
|
+
async handleLogout() {
|
|
1476
|
+
let tokenInfo = {};
|
|
1477
|
+
try {
|
|
1478
|
+
tokenInfo = await this.tokenManager.getAllTokens();
|
|
1479
|
+
await this.authService.logout();
|
|
1480
|
+
return AuthResultFactory.createSuccess("Logout successful", {
|
|
1481
|
+
clearedTokens: Object.keys(tokenInfo).filter((key) => tokenInfo[key])
|
|
1482
|
+
});
|
|
1483
|
+
} catch (error) {
|
|
1484
|
+
return this.errorHandler.handle(error, "Logout");
|
|
1485
|
+
} finally {
|
|
1486
|
+
this.tokenManager.clearTokens();
|
|
1487
|
+
this.userStorageManager.clearUserProfile();
|
|
1488
|
+
const currentPageType = CrossTabBehaviorHandler.getCurrentPageType();
|
|
1489
|
+
this.eventBus.publish({
|
|
1490
|
+
type: "auth.logged_out" /* LoggedOut */,
|
|
1491
|
+
sourcePageType: currentPageType
|
|
1492
|
+
});
|
|
1493
|
+
this.logger.log("Logout cleanup completed:", {
|
|
1494
|
+
clearedTokens: Object.keys(tokenInfo).filter((key) => tokenInfo[key]),
|
|
1495
|
+
domainInfo: this.tokenManager.getDomainInfo()
|
|
1496
|
+
});
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
/**
|
|
1500
|
+
* Check current authentication status using State Pattern
|
|
1501
|
+
*/
|
|
1502
|
+
async checkAuthenticationStatus() {
|
|
1503
|
+
try {
|
|
1504
|
+
return AuthenticationStatusContext.getStatus(this.tokenManager);
|
|
1505
|
+
} catch (error) {
|
|
1506
|
+
return this.errorHandler.handle(error, "Authentication status check");
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
/**
|
|
1510
|
+
* Get user profile data
|
|
1511
|
+
*/
|
|
1512
|
+
async getUserProfileData() {
|
|
1513
|
+
try {
|
|
1514
|
+
const hasAccessToken = !!await this.tokenManager.getAccessToken();
|
|
1515
|
+
if (!hasAccessToken) {
|
|
1516
|
+
return AuthResultFactory.createFailure("User not authenticated");
|
|
1517
|
+
}
|
|
1518
|
+
const profile = await this.authService.getUserProfile();
|
|
1519
|
+
return AuthResultFactory.createSuccess("Profile retrieved successfully", profile.data);
|
|
1520
|
+
} catch (error) {
|
|
1521
|
+
return this.errorHandler.handle(error, "Get user profile");
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
/**
|
|
1525
|
+
* Get detailed token information
|
|
1526
|
+
*/
|
|
1527
|
+
async getTokenInfo() {
|
|
1528
|
+
try {
|
|
1529
|
+
const accessToken = await this.tokenManager.getAccessToken();
|
|
1530
|
+
const refreshToken = await this.tokenManager.getRefreshToken();
|
|
1531
|
+
const allTokens = await this.tokenManager.getAllTokens();
|
|
1532
|
+
const domainInfo = this.tokenManager.getDomainInfo();
|
|
1533
|
+
const cookiesSupported = this.tokenManager.areCookiesSupported();
|
|
1534
|
+
const tokenInfo = {
|
|
1535
|
+
hasAccessToken: !!accessToken,
|
|
1536
|
+
hasRefreshToken: !!refreshToken,
|
|
1537
|
+
cookiesSupported,
|
|
1538
|
+
domainInfo,
|
|
1539
|
+
tokenCount: Object.keys(allTokens).filter((key) => allTokens[key]).length,
|
|
1540
|
+
environment: process.env.NODE_ENV || "unknown"
|
|
1541
|
+
};
|
|
1542
|
+
return AuthResultFactory.createSuccess("Token information retrieved", tokenInfo);
|
|
1543
|
+
} catch (error) {
|
|
1544
|
+
return this.errorHandler.handle(error, "Get token info");
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
};
|
|
1548
|
+
var AuthOrchestratorFactory = class {
|
|
1549
|
+
/**
|
|
1550
|
+
* Create AuthOrchestrator with default dependencies
|
|
1551
|
+
*/
|
|
1552
|
+
static create() {
|
|
1553
|
+
const authService = new AuthService();
|
|
1554
|
+
const eventBus = BroadcastChannelEventBus.getInstance("auth-event-channel" /* AUTH */);
|
|
1555
|
+
return new AuthOrchestrator(authService, eventBus);
|
|
1556
|
+
}
|
|
1557
|
+
/**
|
|
1558
|
+
* Create AuthOrchestrator with custom dependencies
|
|
1559
|
+
*/
|
|
1560
|
+
static createWithDependencies(authService, eventBus, tokenManager) {
|
|
1561
|
+
return new AuthOrchestrator(authService, eventBus, tokenManager);
|
|
1562
|
+
}
|
|
1563
|
+
};
|
|
1564
|
+
|
|
1565
|
+
// src/store/user-store.ts
|
|
1566
|
+
var ERROR_MESSAGES = {
|
|
1567
|
+
UNEXPECTED_ERROR: "An unexpected error occurred"
|
|
1568
|
+
};
|
|
1569
|
+
var useUserStore = create()(
|
|
1570
|
+
devtools(
|
|
1571
|
+
subscribeWithSelector(
|
|
1572
|
+
immer((set, get) => {
|
|
1573
|
+
const authOrchestrator = AuthOrchestratorFactory.create();
|
|
1574
|
+
const userStorageManager = new UserStorageManager(LocalStorageUtils);
|
|
1575
|
+
return {
|
|
1576
|
+
// Initial state
|
|
1577
|
+
user: null,
|
|
1578
|
+
isLoading: false,
|
|
1579
|
+
error: null,
|
|
1580
|
+
isAuthenticated: false,
|
|
1581
|
+
// Dependencies
|
|
1582
|
+
_authOrchestrator: authOrchestrator,
|
|
1583
|
+
_userStorageManager: userStorageManager,
|
|
1584
|
+
// Actions
|
|
1585
|
+
setUser: (user) => {
|
|
1586
|
+
const { _userStorageManager } = get();
|
|
1587
|
+
if (user) {
|
|
1588
|
+
_userStorageManager.saveUserProfile(user);
|
|
1589
|
+
} else {
|
|
1590
|
+
_userStorageManager.clearUserProfile();
|
|
1591
|
+
}
|
|
1592
|
+
set((state) => {
|
|
1593
|
+
state.user = user;
|
|
1594
|
+
state.isAuthenticated = !!user;
|
|
1595
|
+
state.error = null;
|
|
1596
|
+
});
|
|
1597
|
+
},
|
|
1598
|
+
setLoading: (loading) => {
|
|
1599
|
+
set((state) => {
|
|
1600
|
+
state.isLoading = loading;
|
|
1601
|
+
});
|
|
1602
|
+
},
|
|
1603
|
+
setError: (error) => {
|
|
1604
|
+
set((state) => {
|
|
1605
|
+
state.error = error;
|
|
1606
|
+
state.isLoading = false;
|
|
1607
|
+
});
|
|
1608
|
+
},
|
|
1609
|
+
clearUser: async () => {
|
|
1610
|
+
const { _authOrchestrator } = get();
|
|
1611
|
+
set((state) => {
|
|
1612
|
+
state.isLoading = true;
|
|
1613
|
+
});
|
|
1614
|
+
try {
|
|
1615
|
+
await _authOrchestrator.handleLogout();
|
|
1616
|
+
} catch (error) {
|
|
1617
|
+
console.error("Error during logout cleanup:", error);
|
|
1618
|
+
} finally {
|
|
1619
|
+
set((state) => {
|
|
1620
|
+
state.user = null;
|
|
1621
|
+
state.isLoading = false;
|
|
1622
|
+
state.error = null;
|
|
1623
|
+
state.isAuthenticated = false;
|
|
1624
|
+
});
|
|
1625
|
+
}
|
|
1626
|
+
},
|
|
1627
|
+
refreshUser: async (forceRefresh = false) => {
|
|
1628
|
+
const { _authOrchestrator, _userStorageManager } = get();
|
|
1629
|
+
const cachedUser = !forceRefresh ? _userStorageManager.getValidUserProfile() : null;
|
|
1630
|
+
if (cachedUser) {
|
|
1631
|
+
set((state) => {
|
|
1632
|
+
state.user = cachedUser;
|
|
1633
|
+
state.isAuthenticated = true;
|
|
1634
|
+
state.isLoading = false;
|
|
1635
|
+
state.error = null;
|
|
1636
|
+
});
|
|
1637
|
+
return;
|
|
1638
|
+
}
|
|
1639
|
+
set((state) => {
|
|
1640
|
+
state.isLoading = true;
|
|
1641
|
+
state.error = null;
|
|
1642
|
+
});
|
|
1643
|
+
try {
|
|
1644
|
+
const result = await _authOrchestrator.getUserProfileData();
|
|
1645
|
+
if (result.success) {
|
|
1646
|
+
const userProfile = result.data;
|
|
1647
|
+
_userStorageManager.saveUserProfile(userProfile);
|
|
1648
|
+
set((state) => {
|
|
1649
|
+
state.user = userProfile;
|
|
1650
|
+
state.isAuthenticated = true;
|
|
1651
|
+
state.isLoading = false;
|
|
1652
|
+
state.error = null;
|
|
1653
|
+
});
|
|
1654
|
+
} else {
|
|
1655
|
+
set((state) => {
|
|
1656
|
+
state.user = null;
|
|
1657
|
+
state.isAuthenticated = false;
|
|
1658
|
+
state.isLoading = false;
|
|
1659
|
+
state.error = result.error;
|
|
1660
|
+
});
|
|
1661
|
+
}
|
|
1662
|
+
} catch (error) {
|
|
1663
|
+
const errorMessage = error instanceof Error ? error.message : ERROR_MESSAGES.UNEXPECTED_ERROR;
|
|
1664
|
+
set((state) => {
|
|
1665
|
+
state.user = null;
|
|
1666
|
+
state.isAuthenticated = false;
|
|
1667
|
+
state.isLoading = false;
|
|
1668
|
+
state.error = errorMessage;
|
|
1669
|
+
});
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
};
|
|
1673
|
+
})
|
|
1674
|
+
),
|
|
1675
|
+
{
|
|
1676
|
+
name: "user-store",
|
|
1677
|
+
// Only serialize state for devtools, not the internal dependencies
|
|
1678
|
+
serialize: {
|
|
1679
|
+
options: {
|
|
1680
|
+
map: (state) => ({
|
|
1681
|
+
user: state.user,
|
|
1682
|
+
isLoading: state.isLoading,
|
|
1683
|
+
error: state.error,
|
|
1684
|
+
isAuthenticated: state.isAuthenticated
|
|
1685
|
+
})
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
)
|
|
1690
|
+
);
|
|
1691
|
+
var userSelectors = {
|
|
1692
|
+
// User data selectors (single values don't need shallow comparison)
|
|
1693
|
+
useUser: () => useUserStore((state) => state.user),
|
|
1694
|
+
useIsAuthenticated: () => useUserStore((state) => state.isAuthenticated),
|
|
1695
|
+
useIsLoading: () => useUserStore((state) => state.isLoading),
|
|
1696
|
+
useError: () => useUserStore((state) => state.error),
|
|
1697
|
+
// Combined selectors for common use cases (using useShallow to prevent unnecessary re-renders)
|
|
1698
|
+
useUserState: () => useUserStore(
|
|
1699
|
+
useShallow((state) => ({
|
|
1700
|
+
user: state.user,
|
|
1701
|
+
isAuthenticated: state.isAuthenticated,
|
|
1702
|
+
isLoading: state.isLoading,
|
|
1703
|
+
error: state.error
|
|
1704
|
+
}))
|
|
1705
|
+
),
|
|
1706
|
+
// Actions selectors (using useShallow for stable object reference)
|
|
1707
|
+
useActions: () => useUserStore(
|
|
1708
|
+
useShallow((state) => ({
|
|
1709
|
+
setUser: state.setUser,
|
|
1710
|
+
setLoading: state.setLoading,
|
|
1711
|
+
setError: state.setError,
|
|
1712
|
+
clearUser: state.clearUser,
|
|
1713
|
+
refreshUser: state.refreshUser
|
|
1714
|
+
}))
|
|
1715
|
+
)
|
|
1716
|
+
};
|
|
1717
|
+
|
|
1718
|
+
// src/components/auth/auth-flow-container.tsx
|
|
1719
|
+
import { useState as useState4 } from "react";
|
|
1720
|
+
import { Steps, Row, Col, Button as Button5, Image, Typography as Typography2, Spin as Spin2, message, Space as Space2, Flex as Flex3 } from "antd";
|
|
1721
|
+
import { ArrowLeftOutlined as ArrowLeftOutlined3 } from "@ant-design/icons";
|
|
1722
|
+
|
|
1723
|
+
// src/hooks/useAuthActionHandler.ts
|
|
1724
|
+
import { useState, useCallback } from "react";
|
|
1725
|
+
var useAuthActionHandler = () => {
|
|
1726
|
+
const [state, setState] = useState({
|
|
1727
|
+
isLoading: false,
|
|
1728
|
+
error: null,
|
|
1729
|
+
success: null
|
|
1730
|
+
});
|
|
1731
|
+
const transformError = useCallback((error) => {
|
|
1732
|
+
if (error instanceof Error) {
|
|
1733
|
+
return error.message;
|
|
1734
|
+
}
|
|
1735
|
+
if (typeof error === "string") {
|
|
1736
|
+
return error;
|
|
1737
|
+
}
|
|
1738
|
+
return "An unexpected error occurred";
|
|
1739
|
+
}, []);
|
|
1740
|
+
const clearError = useCallback(() => {
|
|
1741
|
+
setState((prev) => __spreadProps(__spreadValues({}, prev), { error: null }));
|
|
1742
|
+
}, []);
|
|
1743
|
+
const clearSuccess = useCallback(() => {
|
|
1744
|
+
setState((prev) => __spreadProps(__spreadValues({}, prev), { success: null }));
|
|
1745
|
+
}, []);
|
|
1746
|
+
const clearAll = useCallback(() => {
|
|
1747
|
+
setState((prev) => __spreadProps(__spreadValues({}, prev), { error: null, success: null }));
|
|
1748
|
+
}, []);
|
|
1749
|
+
const setSuccess = useCallback((message2) => {
|
|
1750
|
+
setState((prev) => __spreadProps(__spreadValues({}, prev), { success: message2, error: null }));
|
|
1751
|
+
}, []);
|
|
1752
|
+
const setError = useCallback((error) => {
|
|
1753
|
+
setState((prev) => __spreadProps(__spreadValues({}, prev), { error, success: null }));
|
|
1754
|
+
}, []);
|
|
1755
|
+
const executeAction = useCallback(async (action, options = {}) => {
|
|
1756
|
+
const {
|
|
1757
|
+
clearStatesBeforeAction = true,
|
|
1758
|
+
preserveSuccessOnError = false,
|
|
1759
|
+
onSuccess,
|
|
1760
|
+
onError
|
|
1761
|
+
} = options;
|
|
1762
|
+
if (clearStatesBeforeAction) {
|
|
1763
|
+
setState((prev) => __spreadProps(__spreadValues({}, prev), {
|
|
1764
|
+
isLoading: true,
|
|
1765
|
+
error: null,
|
|
1766
|
+
success: preserveSuccessOnError ? prev.success : null
|
|
1767
|
+
}));
|
|
1768
|
+
} else {
|
|
1769
|
+
setState((prev) => __spreadProps(__spreadValues({}, prev), { isLoading: true }));
|
|
1770
|
+
}
|
|
1771
|
+
try {
|
|
1772
|
+
const result = await action();
|
|
1773
|
+
if (result.success) {
|
|
1774
|
+
const successMessage = result.message || "";
|
|
1775
|
+
setState((prev) => __spreadProps(__spreadValues({}, prev), {
|
|
1776
|
+
isLoading: false,
|
|
1777
|
+
error: null,
|
|
1778
|
+
success: successMessage
|
|
1779
|
+
}));
|
|
1780
|
+
onSuccess == null ? void 0 : onSuccess(result.data, successMessage, result);
|
|
1781
|
+
return result;
|
|
1782
|
+
} else {
|
|
1783
|
+
const errorMessage = result.message || "Action failed";
|
|
1784
|
+
setState((prev) => __spreadProps(__spreadValues({}, prev), {
|
|
1785
|
+
isLoading: false,
|
|
1786
|
+
error: errorMessage,
|
|
1787
|
+
success: preserveSuccessOnError ? prev.success : null
|
|
1788
|
+
}));
|
|
1789
|
+
onError == null ? void 0 : onError(errorMessage);
|
|
1790
|
+
return null;
|
|
1791
|
+
}
|
|
1792
|
+
} catch (error) {
|
|
1793
|
+
const errorMessage = transformError(error);
|
|
1794
|
+
setState((prev) => __spreadProps(__spreadValues({}, prev), {
|
|
1795
|
+
isLoading: false,
|
|
1796
|
+
error: errorMessage,
|
|
1797
|
+
success: preserveSuccessOnError ? prev.success : null
|
|
1798
|
+
}));
|
|
1799
|
+
onError == null ? void 0 : onError(errorMessage);
|
|
1800
|
+
return null;
|
|
1801
|
+
}
|
|
1802
|
+
}, [transformError]);
|
|
1803
|
+
return {
|
|
1804
|
+
state,
|
|
1805
|
+
executeAction,
|
|
1806
|
+
clearError,
|
|
1807
|
+
clearSuccess,
|
|
1808
|
+
clearAll,
|
|
1809
|
+
setSuccess,
|
|
1810
|
+
setError
|
|
1811
|
+
};
|
|
1812
|
+
};
|
|
1813
|
+
|
|
1814
|
+
// src/hooks/useStepper.ts
|
|
1815
|
+
import { useState as useState2, useCallback as useCallback2, useMemo } from "react";
|
|
1816
|
+
var useStepper = (config2) => {
|
|
1817
|
+
const { steps, initialStep } = config2;
|
|
1818
|
+
if (!steps || steps.length === 0) {
|
|
1819
|
+
throw new Error("useStepper: steps array cannot be empty");
|
|
1820
|
+
}
|
|
1821
|
+
const firstStepId = initialStep || steps[0].id;
|
|
1822
|
+
const [currentStep, setCurrentStep] = useState2(firstStepId);
|
|
1823
|
+
const currentStepIndex = useMemo(() => {
|
|
1824
|
+
return steps.findIndex((step) => step.id === currentStep);
|
|
1825
|
+
}, [steps, currentStep]);
|
|
1826
|
+
const isFirstStep = useMemo(() => currentStepIndex === 0, [currentStepIndex]);
|
|
1827
|
+
const isLastStep = useMemo(() => currentStepIndex === steps.length - 1, [currentStepIndex, steps.length]);
|
|
1828
|
+
const progress = useMemo(() => {
|
|
1829
|
+
return steps.length > 0 ? (currentStepIndex + 1) / steps.length : 0;
|
|
1830
|
+
}, [currentStepIndex, steps.length]);
|
|
1831
|
+
const getStepIndex = useCallback2((step) => {
|
|
1832
|
+
return steps.findIndex((s) => s.id === step);
|
|
1833
|
+
}, [steps]);
|
|
1834
|
+
const getStepByIndex = useCallback2((index) => {
|
|
1835
|
+
return steps[index];
|
|
1836
|
+
}, [steps]);
|
|
1837
|
+
const isStepValid = useCallback2((step) => {
|
|
1838
|
+
const stepIndex = getStepIndex(step);
|
|
1839
|
+
if (stepIndex === -1) return false;
|
|
1840
|
+
const stepConfig = steps[stepIndex];
|
|
1841
|
+
return !stepConfig.disabled;
|
|
1842
|
+
}, [steps, getStepIndex]);
|
|
1843
|
+
const getProgressPercentage = useCallback2(() => {
|
|
1844
|
+
return Math.round(progress * 100);
|
|
1845
|
+
}, [progress]);
|
|
1846
|
+
const goToStep = useCallback2((step) => {
|
|
1847
|
+
const stepIndex = getStepIndex(step);
|
|
1848
|
+
if (stepIndex === -1) {
|
|
1849
|
+
console.warn(`useStepper: Step not found: ${step}`);
|
|
1850
|
+
return;
|
|
1851
|
+
}
|
|
1852
|
+
setCurrentStep(step);
|
|
1853
|
+
}, [getStepIndex]);
|
|
1854
|
+
const goToNext = useCallback2(() => {
|
|
1855
|
+
if (isLastStep) {
|
|
1856
|
+
console.warn("useStepper: Already at the last step");
|
|
1857
|
+
return;
|
|
1858
|
+
}
|
|
1859
|
+
const nextIndex = currentStepIndex + 1;
|
|
1860
|
+
const nextStep = getStepByIndex(nextIndex);
|
|
1861
|
+
if (nextStep) {
|
|
1862
|
+
setCurrentStep(nextStep.id);
|
|
1863
|
+
}
|
|
1864
|
+
}, [isLastStep, currentStepIndex, getStepByIndex]);
|
|
1865
|
+
const goToPrevious = useCallback2(() => {
|
|
1866
|
+
if (isFirstStep) {
|
|
1867
|
+
console.warn("useStepper: Already at the first step");
|
|
1868
|
+
return;
|
|
1869
|
+
}
|
|
1870
|
+
const prevIndex = currentStepIndex - 1;
|
|
1871
|
+
const prevStep = getStepByIndex(prevIndex);
|
|
1872
|
+
if (prevStep) {
|
|
1873
|
+
setCurrentStep(prevStep.id);
|
|
1874
|
+
}
|
|
1875
|
+
}, [isFirstStep, currentStepIndex, getStepByIndex]);
|
|
1876
|
+
const goToIndex = useCallback2((index) => {
|
|
1877
|
+
const step = getStepByIndex(index);
|
|
1878
|
+
if (step) {
|
|
1879
|
+
goToStep(step.id);
|
|
1880
|
+
} else {
|
|
1881
|
+
console.warn(`useStepper: Invalid step index: ${index}`);
|
|
1882
|
+
}
|
|
1883
|
+
}, [getStepByIndex, goToStep]);
|
|
1884
|
+
const reset = useCallback2(() => {
|
|
1885
|
+
setCurrentStep(firstStepId);
|
|
1886
|
+
}, [firstStepId]);
|
|
1887
|
+
return {
|
|
1888
|
+
state: {
|
|
1889
|
+
currentStep,
|
|
1890
|
+
currentStepIndex,
|
|
1891
|
+
steps,
|
|
1892
|
+
isFirstStep,
|
|
1893
|
+
isLastStep,
|
|
1894
|
+
progress
|
|
1895
|
+
},
|
|
1896
|
+
actions: {
|
|
1897
|
+
goToStep,
|
|
1898
|
+
goToNext,
|
|
1899
|
+
goToPrevious,
|
|
1900
|
+
goToIndex,
|
|
1901
|
+
reset
|
|
1902
|
+
},
|
|
1903
|
+
helpers: {
|
|
1904
|
+
getStepIndex,
|
|
1905
|
+
getStepByIndex,
|
|
1906
|
+
isStepValid,
|
|
1907
|
+
getProgressPercentage
|
|
1908
|
+
}
|
|
1909
|
+
};
|
|
1910
|
+
};
|
|
1911
|
+
|
|
1912
|
+
// src/hooks/useStepRegistry.ts
|
|
1913
|
+
import { useMemo as useMemo2 } from "react";
|
|
1914
|
+
|
|
1915
|
+
// src/components/auth/step-renderer.tsx
|
|
1916
|
+
import React from "react";
|
|
1917
|
+
|
|
1918
|
+
// src/components/auth/email-step.tsx
|
|
1919
|
+
import { Button as Button2, Divider, Flex as Flex2 } from "antd";
|
|
1920
|
+
import { GoogleOutlined } from "@ant-design/icons";
|
|
1921
|
+
|
|
1922
|
+
// src/components/auth/base-form.tsx
|
|
1923
|
+
import { Form as Form2, Button, Spin, Space } from "antd";
|
|
1924
|
+
|
|
1925
|
+
// src/components/auth/alert-display.tsx
|
|
1926
|
+
import { Alert } from "antd";
|
|
1927
|
+
import { Fragment, jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
1928
|
+
var AlertDisplay = ({ error, success }) => {
|
|
1929
|
+
if (!error && !success) return null;
|
|
1930
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1931
|
+
error ? /* @__PURE__ */ jsx3(Alert, { message: error, type: "error", showIcon: true, className: "mb-6" }) : null,
|
|
1932
|
+
success ? /* @__PURE__ */ jsx3(Alert, { message: success, type: "success", showIcon: true, className: "mb-6" }) : null
|
|
1933
|
+
] });
|
|
1934
|
+
};
|
|
1935
|
+
|
|
1936
|
+
// src/components/auth/form-header.tsx
|
|
1937
|
+
import { Flex, Typography } from "antd";
|
|
1938
|
+
import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1939
|
+
var { Title, Paragraph } = Typography;
|
|
1940
|
+
var FormHeader = ({ title, description }) => /* @__PURE__ */ jsxs2(Flex, { vertical: true, align: "center", className: "mb-6", children: [
|
|
1941
|
+
/* @__PURE__ */ jsx4(Title, { level: 2, className: "mb-2", children: title }),
|
|
1942
|
+
description && /* @__PURE__ */ jsx4(Paragraph, { className: "text-gray-600 text-center", children: description })
|
|
1943
|
+
] });
|
|
1944
|
+
|
|
1945
|
+
// src/components/auth/form-fields.tsx
|
|
1946
|
+
import { Form } from "antd";
|
|
1947
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
1948
|
+
var FormFields = ({ fields }) => fields.map((_a) => {
|
|
1949
|
+
var _b = _a, { component } = _b, formItemProps = __objRest(_b, ["component"]);
|
|
1950
|
+
return /* @__PURE__ */ jsx5(Form.Item, __spreadProps(__spreadValues({}, formItemProps), { children: component }), formItemProps.name);
|
|
1951
|
+
});
|
|
1952
|
+
|
|
1953
|
+
// src/components/auth/base-form.tsx
|
|
1954
|
+
import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1955
|
+
var BaseForm = ({
|
|
1956
|
+
title,
|
|
1957
|
+
description,
|
|
1958
|
+
fields,
|
|
1959
|
+
submitButtonText,
|
|
1960
|
+
onSubmit,
|
|
1961
|
+
isLoading = false,
|
|
1962
|
+
error,
|
|
1963
|
+
success,
|
|
1964
|
+
form,
|
|
1965
|
+
additionalActions,
|
|
1966
|
+
showAlerts = true,
|
|
1967
|
+
initialValues
|
|
1968
|
+
}) => {
|
|
1969
|
+
const [formInstance] = Form2.useForm();
|
|
1970
|
+
const activeForm = form || formInstance;
|
|
1971
|
+
const handleSubmit = async (values) => {
|
|
1972
|
+
try {
|
|
1973
|
+
await onSubmit(values);
|
|
1974
|
+
} catch (error2) {
|
|
1975
|
+
console.error("Form submission error:", error2);
|
|
1976
|
+
}
|
|
1977
|
+
};
|
|
1978
|
+
return /* @__PURE__ */ jsxs3(Space, { direction: "vertical", size: "large", className: "w-full max-w-md", children: [
|
|
1979
|
+
/* @__PURE__ */ jsx6(FormHeader, { title, description }),
|
|
1980
|
+
showAlerts && /* @__PURE__ */ jsx6(AlertDisplay, { error, success }),
|
|
1981
|
+
/* @__PURE__ */ jsx6(Spin, { spinning: isLoading, tip: "Processing...", children: /* @__PURE__ */ jsxs3(
|
|
1982
|
+
Form2,
|
|
1983
|
+
{
|
|
1984
|
+
form: activeForm,
|
|
1985
|
+
onFinish: handleSubmit,
|
|
1986
|
+
layout: "vertical",
|
|
1987
|
+
size: "large",
|
|
1988
|
+
initialValues,
|
|
1989
|
+
className: "w-full",
|
|
1990
|
+
children: [
|
|
1991
|
+
/* @__PURE__ */ jsx6(FormFields, { fields }),
|
|
1992
|
+
/* @__PURE__ */ jsx6(Form2.Item, { children: /* @__PURE__ */ jsx6(
|
|
1993
|
+
Button,
|
|
1994
|
+
{
|
|
1995
|
+
type: "primary",
|
|
1996
|
+
htmlType: "submit",
|
|
1997
|
+
loading: isLoading,
|
|
1998
|
+
block: true,
|
|
1999
|
+
size: "large",
|
|
2000
|
+
children: submitButtonText
|
|
2001
|
+
}
|
|
2002
|
+
) }),
|
|
2003
|
+
additionalActions
|
|
2004
|
+
]
|
|
2005
|
+
}
|
|
2006
|
+
) })
|
|
2007
|
+
] });
|
|
2008
|
+
};
|
|
2009
|
+
|
|
2010
|
+
// src/components/auth/email-step.tsx
|
|
2011
|
+
import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
2012
|
+
var EmailStep = ({
|
|
2013
|
+
title,
|
|
2014
|
+
description,
|
|
2015
|
+
onSubmit,
|
|
2016
|
+
onGoogleSignIn,
|
|
2017
|
+
onForgotPassword,
|
|
2018
|
+
isLoading = false,
|
|
2019
|
+
error,
|
|
2020
|
+
success,
|
|
2021
|
+
showGoogleButton = true,
|
|
2022
|
+
showForgotPassword = true,
|
|
2023
|
+
initialValues
|
|
2024
|
+
}) => {
|
|
2025
|
+
const handleSubmit = async (values) => {
|
|
2026
|
+
await onSubmit(values.email);
|
|
2027
|
+
};
|
|
2028
|
+
const fields = [getEmailField({ disabled: isLoading })];
|
|
2029
|
+
const additionalActions = /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
2030
|
+
showGoogleButton && /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
2031
|
+
/* @__PURE__ */ jsx7(Divider, { children: "OR" }),
|
|
2032
|
+
/* @__PURE__ */ jsx7(
|
|
2033
|
+
Button2,
|
|
2034
|
+
{
|
|
2035
|
+
icon: /* @__PURE__ */ jsx7(GoogleOutlined, {}),
|
|
2036
|
+
onClick: onGoogleSignIn,
|
|
2037
|
+
block: true,
|
|
2038
|
+
size: "large",
|
|
2039
|
+
className: "mb-4",
|
|
2040
|
+
disabled: isLoading,
|
|
2041
|
+
children: "Sign-in with Google"
|
|
2042
|
+
}
|
|
2043
|
+
)
|
|
2044
|
+
] }),
|
|
2045
|
+
showForgotPassword && /* @__PURE__ */ jsx7(Flex2, { justify: "end", children: /* @__PURE__ */ jsx7(
|
|
2046
|
+
Button2,
|
|
2047
|
+
{
|
|
2048
|
+
type: "link",
|
|
2049
|
+
className: "px-0",
|
|
2050
|
+
onClick: onForgotPassword,
|
|
2051
|
+
disabled: isLoading,
|
|
2052
|
+
children: "Forgot Password ?"
|
|
2053
|
+
}
|
|
2054
|
+
) })
|
|
2055
|
+
] });
|
|
2056
|
+
return /* @__PURE__ */ jsx7(
|
|
2057
|
+
BaseForm,
|
|
2058
|
+
{
|
|
2059
|
+
title,
|
|
2060
|
+
description,
|
|
2061
|
+
fields,
|
|
2062
|
+
submitButtonText: "Next",
|
|
2063
|
+
onSubmit: handleSubmit,
|
|
2064
|
+
isLoading,
|
|
2065
|
+
error,
|
|
2066
|
+
success,
|
|
2067
|
+
additionalActions,
|
|
2068
|
+
initialValues
|
|
2069
|
+
}
|
|
2070
|
+
);
|
|
2071
|
+
};
|
|
2072
|
+
|
|
2073
|
+
// src/components/auth/password-step.tsx
|
|
2074
|
+
import { Button as Button3 } from "antd";
|
|
2075
|
+
import { ArrowLeftOutlined } from "@ant-design/icons";
|
|
2076
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
2077
|
+
var PasswordStep = ({
|
|
2078
|
+
title,
|
|
2079
|
+
description,
|
|
2080
|
+
mode,
|
|
2081
|
+
email,
|
|
2082
|
+
onSubmit,
|
|
2083
|
+
onBack,
|
|
2084
|
+
isLoading = false,
|
|
2085
|
+
error,
|
|
2086
|
+
success,
|
|
2087
|
+
showEmailField = true,
|
|
2088
|
+
showBackButton = true,
|
|
2089
|
+
submitButtonText,
|
|
2090
|
+
initialValues
|
|
2091
|
+
}) => {
|
|
2092
|
+
const handleSubmit = async (values) => {
|
|
2093
|
+
await onSubmit(values.password);
|
|
2094
|
+
};
|
|
2095
|
+
const fields = [];
|
|
2096
|
+
if (showEmailField) {
|
|
2097
|
+
fields.push(getEmailField({ readOnly: true, value: email }));
|
|
2098
|
+
}
|
|
2099
|
+
fields.push(getPasswordField(mode === "login" /* LOGIN */, isLoading));
|
|
2100
|
+
const additionalActions = showBackButton ? /* @__PURE__ */ jsx8(
|
|
2101
|
+
Button3,
|
|
2102
|
+
{
|
|
2103
|
+
icon: /* @__PURE__ */ jsx8(ArrowLeftOutlined, {}),
|
|
2104
|
+
onClick: onBack,
|
|
2105
|
+
type: "link",
|
|
2106
|
+
block: true,
|
|
2107
|
+
disabled: isLoading,
|
|
2108
|
+
children: "Use different email"
|
|
2109
|
+
}
|
|
2110
|
+
) : null;
|
|
2111
|
+
return /* @__PURE__ */ jsx8(
|
|
2112
|
+
BaseForm,
|
|
2113
|
+
{
|
|
2114
|
+
title,
|
|
2115
|
+
description,
|
|
2116
|
+
fields,
|
|
2117
|
+
submitButtonText,
|
|
2118
|
+
onSubmit: handleSubmit,
|
|
2119
|
+
isLoading,
|
|
2120
|
+
error,
|
|
2121
|
+
success,
|
|
2122
|
+
additionalActions,
|
|
2123
|
+
initialValues
|
|
2124
|
+
}
|
|
2125
|
+
);
|
|
2126
|
+
};
|
|
2127
|
+
|
|
2128
|
+
// src/components/auth/verification-step.tsx
|
|
2129
|
+
import { Button as Button4 } from "antd";
|
|
2130
|
+
import { ArrowLeftOutlined as ArrowLeftOutlined2 } from "@ant-design/icons";
|
|
2131
|
+
import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2132
|
+
var VerificationStep = ({
|
|
2133
|
+
title,
|
|
2134
|
+
description,
|
|
2135
|
+
onSubmit,
|
|
2136
|
+
onBack,
|
|
2137
|
+
onResendCode,
|
|
2138
|
+
submitButtonText,
|
|
2139
|
+
isLoading = false,
|
|
2140
|
+
error,
|
|
2141
|
+
success,
|
|
2142
|
+
codeLength = 5,
|
|
2143
|
+
showBackButton = true,
|
|
2144
|
+
showResendButton = true,
|
|
2145
|
+
initialValues
|
|
2146
|
+
}) => {
|
|
2147
|
+
const handleSubmit = async (values) => {
|
|
2148
|
+
await onSubmit(values.verificationCode);
|
|
2149
|
+
};
|
|
2150
|
+
const fields = [
|
|
2151
|
+
getVerificationField(codeLength, { disabled: isLoading })
|
|
2152
|
+
];
|
|
2153
|
+
const additionalActions = /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
2154
|
+
showResendButton && /* @__PURE__ */ jsx9(
|
|
2155
|
+
Button4,
|
|
2156
|
+
{
|
|
2157
|
+
type: "link",
|
|
2158
|
+
onClick: onResendCode,
|
|
2159
|
+
disabled: isLoading,
|
|
2160
|
+
block: true,
|
|
2161
|
+
className: "mb-2",
|
|
2162
|
+
children: "Resend verification code"
|
|
2163
|
+
}
|
|
2164
|
+
),
|
|
2165
|
+
showBackButton && /* @__PURE__ */ jsx9(
|
|
2166
|
+
Button4,
|
|
2167
|
+
{
|
|
2168
|
+
icon: /* @__PURE__ */ jsx9(ArrowLeftOutlined2, {}),
|
|
2169
|
+
onClick: onBack,
|
|
2170
|
+
type: "link",
|
|
2171
|
+
block: true,
|
|
2172
|
+
disabled: isLoading,
|
|
2173
|
+
children: "Back to password"
|
|
2174
|
+
}
|
|
2175
|
+
)
|
|
2176
|
+
] });
|
|
2177
|
+
return /* @__PURE__ */ jsx9(
|
|
2178
|
+
BaseForm,
|
|
2179
|
+
{
|
|
2180
|
+
title,
|
|
2181
|
+
description,
|
|
2182
|
+
fields,
|
|
2183
|
+
submitButtonText,
|
|
2184
|
+
onSubmit: handleSubmit,
|
|
2185
|
+
isLoading,
|
|
2186
|
+
error,
|
|
2187
|
+
success,
|
|
2188
|
+
additionalActions,
|
|
2189
|
+
initialValues
|
|
2190
|
+
}
|
|
2191
|
+
);
|
|
2192
|
+
};
|
|
2193
|
+
|
|
2194
|
+
// src/components/auth/step-renderer.tsx
|
|
2195
|
+
var getEmailStepComponent = EmailStep;
|
|
2196
|
+
var getPasswordStepComponent = PasswordStep;
|
|
2197
|
+
var getVerificationStepComponent = VerificationStep;
|
|
2198
|
+
var createStepRegistry = () => ({
|
|
2199
|
+
["email" /* EMAIL */]: getEmailStepComponent,
|
|
2200
|
+
["password" /* PASSWORD */]: getPasswordStepComponent,
|
|
2201
|
+
["verification" /* VERIFICATION */]: getVerificationStepComponent
|
|
2202
|
+
});
|
|
2203
|
+
var createPropsFactoryRegistry = ({
|
|
2204
|
+
baseProps,
|
|
2205
|
+
handlers,
|
|
2206
|
+
state,
|
|
2207
|
+
configs
|
|
2208
|
+
}) => ({
|
|
2209
|
+
["email" /* EMAIL */]: () => __spreadProps(__spreadValues({}, baseProps), {
|
|
2210
|
+
title: "Sign In/Sign Up",
|
|
2211
|
+
description: "If you are an existing user, you will be signed in to your account. If you are a new user, an account will be created for you.",
|
|
2212
|
+
submitButtonText: "Continue",
|
|
2213
|
+
onSubmit: handlers.handleEmailSubmit,
|
|
2214
|
+
onGoogleSignIn: handlers.onGoogleSignIn,
|
|
2215
|
+
onForgotPassword: handlers.onForgotPassword,
|
|
2216
|
+
showGoogleButton: configs.emailStepConfig.showGoogleButton,
|
|
2217
|
+
showForgotPassword: configs.emailStepConfig.showForgotPassword
|
|
2218
|
+
}),
|
|
2219
|
+
["password" /* PASSWORD */]: () => __spreadProps(__spreadValues({}, baseProps), {
|
|
2220
|
+
title: state.authIntent === "login" /* LOGIN */ ? "Welcome Back" : "Create Account",
|
|
2221
|
+
description: state.authIntent === "login" /* LOGIN */ ? "Please enter your password to sign in" : "Please create a password for your account",
|
|
2222
|
+
submitButtonText: state.authIntent === "login" /* LOGIN */ ? "Sign In" : "Create Account",
|
|
2223
|
+
mode: state.authIntent,
|
|
2224
|
+
email: state.email,
|
|
2225
|
+
onSubmit: handlers.handlePasswordSubmit,
|
|
2226
|
+
onBack: handlers.goBackToEmail,
|
|
2227
|
+
showEmailField: configs.passwordStepConfig.showEmailField
|
|
2228
|
+
}),
|
|
2229
|
+
["verification" /* VERIFICATION */]: () => __spreadProps(__spreadValues({}, baseProps), {
|
|
2230
|
+
title: "Email Verification",
|
|
2231
|
+
description: `Please enter the verification code sent to ${state.email}`,
|
|
2232
|
+
submitButtonText: "Verify Email",
|
|
2233
|
+
email: state.email,
|
|
2234
|
+
onSubmit: handlers.handleVerificationSubmit,
|
|
2235
|
+
onBack: handlers.goBackToPassword,
|
|
2236
|
+
onResendCode: handlers.handleResendCode,
|
|
2237
|
+
codeLength: configs.verificationStepConfig.codeLength,
|
|
2238
|
+
showResendButton: configs.verificationStepConfig.showResendButton
|
|
2239
|
+
})
|
|
2240
|
+
});
|
|
2241
|
+
var useStepRenderer = () => {
|
|
2242
|
+
const registry = React.useMemo(() => createStepRegistry(), []);
|
|
2243
|
+
const getStepComponent = React.useCallback((step) => {
|
|
2244
|
+
return registry[step] || null;
|
|
2245
|
+
}, [registry]);
|
|
2246
|
+
return { getStepComponent };
|
|
2247
|
+
};
|
|
2248
|
+
|
|
2249
|
+
// src/hooks/useStepRegistry.ts
|
|
2250
|
+
function useStepRegistry({
|
|
2251
|
+
baseProps: actionState,
|
|
2252
|
+
handlers,
|
|
2253
|
+
state,
|
|
2254
|
+
configs,
|
|
2255
|
+
getStepComponent,
|
|
2256
|
+
stepperState
|
|
2257
|
+
}) {
|
|
2258
|
+
const baseProps = useMemo2(
|
|
2259
|
+
() => ({
|
|
2260
|
+
isLoading: actionState.isLoading,
|
|
2261
|
+
error: actionState.error,
|
|
2262
|
+
success: actionState.success
|
|
2263
|
+
}),
|
|
2264
|
+
[actionState]
|
|
2265
|
+
);
|
|
2266
|
+
const registryParams = useMemo2(
|
|
2267
|
+
() => ({ baseProps, handlers, state, configs }),
|
|
2268
|
+
[baseProps, handlers, state, configs]
|
|
2269
|
+
);
|
|
2270
|
+
const propsFactoryRegistry = useMemo2(
|
|
2271
|
+
() => createPropsFactoryRegistry(registryParams),
|
|
2272
|
+
[registryParams]
|
|
2273
|
+
);
|
|
2274
|
+
const currentStep = stepperState.currentStep;
|
|
2275
|
+
const SelectedComponent = getStepComponent(currentStep);
|
|
2276
|
+
const stepProps = useMemo2(() => {
|
|
2277
|
+
const factory = propsFactoryRegistry[currentStep];
|
|
2278
|
+
if (!factory) {
|
|
2279
|
+
console.warn(`No props factory found for step: ${currentStep}`);
|
|
2280
|
+
return baseProps;
|
|
2281
|
+
}
|
|
2282
|
+
return factory();
|
|
2283
|
+
}, [propsFactoryRegistry, currentStep, baseProps]);
|
|
2284
|
+
return { SelectedComponent, stepProps };
|
|
2285
|
+
}
|
|
2286
|
+
|
|
2287
|
+
// src/hooks/useAuthEventBus.ts
|
|
2288
|
+
import { useEffect } from "react";
|
|
2289
|
+
|
|
2290
|
+
// src/hooks/useSharedEventBus.ts
|
|
2291
|
+
import { useMemo as useMemo3 } from "react";
|
|
2292
|
+
function useSharedEventBus() {
|
|
2293
|
+
return useMemo3(() => BroadcastChannelEventBus.getInstance("auth-event-channel" /* AUTH */), []);
|
|
2294
|
+
}
|
|
2295
|
+
|
|
2296
|
+
// src/hooks/useAuthEventBus.ts
|
|
2297
|
+
var useAuthEventBus = ({ onLoggedOut, onLoggedIn } = {}) => {
|
|
2298
|
+
const eventBus = useSharedEventBus();
|
|
2299
|
+
useEffect(() => {
|
|
2300
|
+
const handleEvent = (e) => {
|
|
2301
|
+
var _a;
|
|
2302
|
+
const currentPageType = CrossTabBehaviorHandler.getCurrentPageType();
|
|
2303
|
+
const eventHandlers = {
|
|
2304
|
+
["auth.logged_out" /* LoggedOut */]: onLoggedOut,
|
|
2305
|
+
["auth.logged_in" /* LoggedIn */]: onLoggedIn,
|
|
2306
|
+
["auth.email_verified" /* EmailVerified */]: void 0,
|
|
2307
|
+
["auth.signin_required_modal" /* SignInRequiredModal */]: void 0
|
|
2308
|
+
};
|
|
2309
|
+
const handler = eventHandlers[e.type];
|
|
2310
|
+
const action = CrossTabBehaviorHandler.getAction(currentPageType, e.type);
|
|
2311
|
+
const actionHandlers = {
|
|
2312
|
+
["redirect" /* REDIRECT */]: () => {
|
|
2313
|
+
const target = CrossTabBehaviorHandler.shouldRedirect(currentPageType, e.type);
|
|
2314
|
+
if (target) {
|
|
2315
|
+
window.location.replace(target);
|
|
2316
|
+
}
|
|
2317
|
+
},
|
|
2318
|
+
["modal" /* MODAL */]: () => {
|
|
2319
|
+
if (e.type === "auth.logged_in" /* LoggedIn */) {
|
|
2320
|
+
window.location.replace(window.location.href);
|
|
2321
|
+
}
|
|
2322
|
+
},
|
|
2323
|
+
["none" /* NONE */]: () => {
|
|
2324
|
+
}
|
|
2325
|
+
};
|
|
2326
|
+
(_a = actionHandlers[action.action]) == null ? void 0 : _a.call(actionHandlers);
|
|
2327
|
+
handler == null ? void 0 : handler();
|
|
2328
|
+
};
|
|
2329
|
+
const subscription = eventBus.subscribe(handleEvent);
|
|
2330
|
+
return () => {
|
|
2331
|
+
subscription.unsubscribe();
|
|
2332
|
+
};
|
|
2333
|
+
}, [eventBus, onLoggedOut, onLoggedIn]);
|
|
2334
|
+
};
|
|
2335
|
+
|
|
2336
|
+
// src/hooks/useSignInRequiredParams.ts
|
|
2337
|
+
import { useEffect as useEffect2 } from "react";
|
|
2338
|
+
import { useSearchParams } from "next/navigation";
|
|
2339
|
+
function useSignInRequiredParams() {
|
|
2340
|
+
const searchParams = useSearchParams();
|
|
2341
|
+
const eventBus = useSharedEventBus();
|
|
2342
|
+
useEffect2(() => {
|
|
2343
|
+
const signInRequired = searchParams.get(MiddlewareConfig.QUERY_PARAMS.LOGIN_REQUIRED);
|
|
2344
|
+
const authChecked = searchParams.get(MiddlewareConfig.QUERY_PARAMS.AUTH_CHECKED);
|
|
2345
|
+
if (signInRequired === MiddlewareConfig.QUERY_VALUES.LOGIN_REQUIRED && authChecked === MiddlewareConfig.QUERY_VALUES.AUTH_CHECKED) {
|
|
2346
|
+
const currentPageType = CrossTabBehaviorHandler.getCurrentPageType();
|
|
2347
|
+
eventBus.publish({
|
|
2348
|
+
type: "auth.signin_required_modal" /* SignInRequiredModal */,
|
|
2349
|
+
sourcePageType: currentPageType
|
|
2350
|
+
});
|
|
2351
|
+
}
|
|
2352
|
+
}, [eventBus, searchParams]);
|
|
2353
|
+
}
|
|
2354
|
+
|
|
2355
|
+
// src/hooks/use-user.ts
|
|
2356
|
+
import { useEffect as useEffect3 } from "react";
|
|
2357
|
+
var useUser = () => {
|
|
2358
|
+
const userState = userSelectors.useUserState();
|
|
2359
|
+
const actions = userSelectors.useActions();
|
|
2360
|
+
return __spreadValues(__spreadValues({}, userState), actions);
|
|
2361
|
+
};
|
|
2362
|
+
var useUserData = () => {
|
|
2363
|
+
return userSelectors.useUserState();
|
|
2364
|
+
};
|
|
2365
|
+
var useUserActions = () => {
|
|
2366
|
+
return userSelectors.useActions();
|
|
2367
|
+
};
|
|
2368
|
+
var useAuth = () => {
|
|
2369
|
+
const isAuthenticated = userSelectors.useIsAuthenticated();
|
|
2370
|
+
const isLoading = userSelectors.useIsLoading();
|
|
2371
|
+
const error = userSelectors.useError();
|
|
2372
|
+
const user = userSelectors.useUser();
|
|
2373
|
+
return {
|
|
2374
|
+
isAuthenticated,
|
|
2375
|
+
isLoading,
|
|
2376
|
+
error,
|
|
2377
|
+
user
|
|
2378
|
+
};
|
|
2379
|
+
};
|
|
2380
|
+
var useAuthInitializer = () => {
|
|
2381
|
+
const { refreshUser } = useUserActions();
|
|
2382
|
+
const { isLoading } = useUserData();
|
|
2383
|
+
useEffect3(() => {
|
|
2384
|
+
const initializeUser = async () => {
|
|
2385
|
+
if (!isLoading) {
|
|
2386
|
+
try {
|
|
2387
|
+
await refreshUser();
|
|
2388
|
+
} catch (error) {
|
|
2389
|
+
console.error("Failed to initialize user:", error);
|
|
2390
|
+
}
|
|
2391
|
+
}
|
|
2392
|
+
};
|
|
2393
|
+
initializeUser();
|
|
2394
|
+
}, []);
|
|
2395
|
+
useAuthEventBus({
|
|
2396
|
+
onLoggedOut: () => {
|
|
2397
|
+
refreshUser(true).catch(console.error);
|
|
2398
|
+
},
|
|
2399
|
+
onLoggedIn: () => {
|
|
2400
|
+
refreshUser(true).catch(console.error);
|
|
2401
|
+
}
|
|
2402
|
+
});
|
|
2403
|
+
return useUser();
|
|
2404
|
+
};
|
|
2405
|
+
var useIsAuthenticated = () => {
|
|
2406
|
+
return userSelectors.useIsAuthenticated();
|
|
2407
|
+
};
|
|
2408
|
+
var useUserLoading = () => {
|
|
2409
|
+
return userSelectors.useIsLoading();
|
|
2410
|
+
};
|
|
2411
|
+
var useUserError = () => {
|
|
2412
|
+
return userSelectors.useError();
|
|
2413
|
+
};
|
|
2414
|
+
var useUserProfile = () => {
|
|
2415
|
+
return userSelectors.useUser();
|
|
2416
|
+
};
|
|
2417
|
+
var useLogout = () => {
|
|
2418
|
+
const { clearUser } = useUserActions();
|
|
2419
|
+
const logout = async () => {
|
|
2420
|
+
try {
|
|
2421
|
+
await clearUser();
|
|
2422
|
+
} catch (error) {
|
|
2423
|
+
console.error("Logout error:", error);
|
|
2424
|
+
throw error;
|
|
2425
|
+
}
|
|
2426
|
+
};
|
|
2427
|
+
return logout;
|
|
2428
|
+
};
|
|
2429
|
+
var useRefreshUser = () => {
|
|
2430
|
+
const { refreshUser } = useUserActions();
|
|
2431
|
+
const refresh = async (force = false) => {
|
|
2432
|
+
try {
|
|
2433
|
+
await refreshUser(force);
|
|
2434
|
+
} catch (error) {
|
|
2435
|
+
console.error("Refresh user error:", error);
|
|
2436
|
+
throw error;
|
|
2437
|
+
}
|
|
2438
|
+
};
|
|
2439
|
+
return refresh;
|
|
2440
|
+
};
|
|
2441
|
+
|
|
2442
|
+
// src/hooks/useAuthFlowModal.ts
|
|
2443
|
+
import { useState as useState3, useEffect as useEffect4 } from "react";
|
|
2444
|
+
var useAuthFlowModal = () => {
|
|
2445
|
+
const [isModalOpen, setIsModalOpen] = useState3(false);
|
|
2446
|
+
const [isInitialLoading, setIsInitialLoading] = useState3(true);
|
|
2447
|
+
const eventBus = useSharedEventBus();
|
|
2448
|
+
const { isLoading: userLoading } = useUserData();
|
|
2449
|
+
useEffect4(() => {
|
|
2450
|
+
const handleAuthEvent = (event) => {
|
|
2451
|
+
if (event.type === "auth.signin_required_modal" /* SignInRequiredModal */) {
|
|
2452
|
+
setIsModalOpen(true);
|
|
2453
|
+
setIsInitialLoading(false);
|
|
2454
|
+
}
|
|
2455
|
+
};
|
|
2456
|
+
const subscription = eventBus.subscribe(handleAuthEvent);
|
|
2457
|
+
return () => {
|
|
2458
|
+
subscription.unsubscribe();
|
|
2459
|
+
};
|
|
2460
|
+
}, [eventBus]);
|
|
2461
|
+
useEffect4(() => {
|
|
2462
|
+
if (!userLoading) {
|
|
2463
|
+
const timer = setTimeout(() => {
|
|
2464
|
+
setIsInitialLoading(false);
|
|
2465
|
+
}, 0);
|
|
2466
|
+
return () => clearTimeout(timer);
|
|
2467
|
+
}
|
|
2468
|
+
}, [userLoading]);
|
|
2469
|
+
const openModal = () => {
|
|
2470
|
+
setIsModalOpen(true);
|
|
2471
|
+
setIsInitialLoading(false);
|
|
2472
|
+
};
|
|
2473
|
+
return {
|
|
2474
|
+
isModalOpen,
|
|
2475
|
+
isInitialLoading,
|
|
2476
|
+
openModal
|
|
2477
|
+
};
|
|
2478
|
+
};
|
|
2479
|
+
|
|
2480
|
+
// src/components/auth/auth-flow-container.tsx
|
|
2481
|
+
import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
2482
|
+
var { Text } = Typography2;
|
|
2483
|
+
var AuthFlowContainer = () => {
|
|
2484
|
+
const authOrchestrator = AuthOrchestratorFactory.create();
|
|
2485
|
+
const { state: actionState, executeAction, clearAll } = useAuthActionHandler();
|
|
2486
|
+
const { getStepComponent } = useStepRenderer();
|
|
2487
|
+
const [email, setEmail] = useState4("");
|
|
2488
|
+
const [authIntent, setAuthIntent] = useState4("login" /* LOGIN */);
|
|
2489
|
+
const [authData, setAuthData] = useState4(null);
|
|
2490
|
+
const config2 = {
|
|
2491
|
+
showSteps: true,
|
|
2492
|
+
showProgressImage: true,
|
|
2493
|
+
showBackToHome: true,
|
|
2494
|
+
backgroundImage: "/api/placeholder/800/600",
|
|
2495
|
+
useAuthPageMessages: true,
|
|
2496
|
+
emailStepConfig: {
|
|
2497
|
+
showGoogleButton: false,
|
|
2498
|
+
showForgotPassword: false
|
|
2499
|
+
},
|
|
2500
|
+
passwordStepConfig: {
|
|
2501
|
+
showEmailField: true
|
|
2502
|
+
},
|
|
2503
|
+
verificationStepConfig: {
|
|
2504
|
+
codeLength: 5,
|
|
2505
|
+
showResendButton: false
|
|
2506
|
+
}
|
|
2507
|
+
};
|
|
2508
|
+
const stepperConfig = {
|
|
2509
|
+
steps: createAuthSteps({ authIntent }),
|
|
2510
|
+
initialStep: "email" /* EMAIL */
|
|
2511
|
+
};
|
|
2512
|
+
const { state: stepperState, actions: stepperActions } = useStepper(stepperConfig);
|
|
2513
|
+
const handleEmailSubmit = async (emailValue) => {
|
|
2514
|
+
await executeAction(
|
|
2515
|
+
async () => {
|
|
2516
|
+
return await authOrchestrator.handleEmailCheck(emailValue);
|
|
2517
|
+
},
|
|
2518
|
+
{
|
|
2519
|
+
onSuccess: (data) => {
|
|
2520
|
+
setEmail(emailValue);
|
|
2521
|
+
setAuthIntent(data.next_action);
|
|
2522
|
+
const nextStep = getStepForEmailSubmission(data.next_action);
|
|
2523
|
+
stepperActions.goToStep(nextStep);
|
|
2524
|
+
},
|
|
2525
|
+
onError: (error) => {
|
|
2526
|
+
message.error(error);
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2529
|
+
);
|
|
2530
|
+
};
|
|
2531
|
+
const handlePasswordSubmit = async (password) => {
|
|
2532
|
+
await executeAction(
|
|
2533
|
+
async () => {
|
|
2534
|
+
const loginRequest = { email, password };
|
|
2535
|
+
return await authOrchestrator.handleLoginFlow(loginRequest);
|
|
2536
|
+
},
|
|
2537
|
+
{
|
|
2538
|
+
onSuccess: (data) => {
|
|
2539
|
+
setAuthData(data);
|
|
2540
|
+
if (!data.isVerifiedEmail) {
|
|
2541
|
+
const nextStep = getStepForPasswordSubmission(data.isVerifiedEmail);
|
|
2542
|
+
stepperActions.goToStep(nextStep);
|
|
2543
|
+
}
|
|
2544
|
+
},
|
|
2545
|
+
onError: (error) => {
|
|
2546
|
+
message.error(error);
|
|
2547
|
+
}
|
|
2548
|
+
}
|
|
2549
|
+
);
|
|
2550
|
+
};
|
|
2551
|
+
const handleVerificationSubmit = async (code) => {
|
|
2552
|
+
await executeAction(
|
|
2553
|
+
async () => {
|
|
2554
|
+
return await authOrchestrator.handleEmailVerification(email, code);
|
|
2555
|
+
},
|
|
2556
|
+
{
|
|
2557
|
+
onSuccess: (data) => {
|
|
2558
|
+
setAuthData(data);
|
|
2559
|
+
handleSuccess();
|
|
2560
|
+
},
|
|
2561
|
+
onError: (error) => {
|
|
2562
|
+
message.error(error);
|
|
2563
|
+
}
|
|
2564
|
+
}
|
|
2565
|
+
);
|
|
2566
|
+
};
|
|
2567
|
+
const handleSuccess = () => {
|
|
2568
|
+
message.success("Authentication successful!");
|
|
2569
|
+
};
|
|
2570
|
+
const goBackToEmail = () => {
|
|
2571
|
+
stepperActions.goToStep("email" /* EMAIL */);
|
|
2572
|
+
setEmail("");
|
|
2573
|
+
clearAll();
|
|
2574
|
+
};
|
|
2575
|
+
const goBackToPassword = () => {
|
|
2576
|
+
stepperActions.goToStep("password" /* PASSWORD */);
|
|
2577
|
+
clearAll();
|
|
2578
|
+
};
|
|
2579
|
+
const goBackToHome = () => {
|
|
2580
|
+
window.location.href = "/";
|
|
2581
|
+
};
|
|
2582
|
+
const getCurrentStepMessage = () => {
|
|
2583
|
+
return config2.useAuthPageMessages ? getAuthPageStepMessage(stepperState.currentStep) : getStepProgressMessage(stepperState.currentStep);
|
|
2584
|
+
};
|
|
2585
|
+
const { SelectedComponent, stepProps } = useStepRegistry({
|
|
2586
|
+
baseProps: __spreadProps(__spreadValues({}, actionState), {
|
|
2587
|
+
error: actionState.error || void 0,
|
|
2588
|
+
success: actionState.success || void 0
|
|
2589
|
+
}),
|
|
2590
|
+
handlers: {
|
|
2591
|
+
handleEmailSubmit,
|
|
2592
|
+
handlePasswordSubmit,
|
|
2593
|
+
handleVerificationSubmit,
|
|
2594
|
+
handleResendCode: () => {
|
|
2595
|
+
},
|
|
2596
|
+
goBackToEmail,
|
|
2597
|
+
goBackToPassword,
|
|
2598
|
+
onGoogleSignIn: () => {
|
|
2599
|
+
},
|
|
2600
|
+
onForgotPassword: () => {
|
|
2601
|
+
}
|
|
2602
|
+
},
|
|
2603
|
+
state: { email, authIntent, authData },
|
|
2604
|
+
configs: {
|
|
2605
|
+
emailStepConfig: config2.emailStepConfig,
|
|
2606
|
+
passwordStepConfig: config2.passwordStepConfig,
|
|
2607
|
+
verificationStepConfig: config2.verificationStepConfig
|
|
2608
|
+
},
|
|
2609
|
+
getStepComponent,
|
|
2610
|
+
stepperState
|
|
2611
|
+
});
|
|
2612
|
+
return /* @__PURE__ */ jsx10("div", { className: "min-h-screen bg-gray-50", children: /* @__PURE__ */ jsxs6(Row, { className: "min-h-screen", children: [
|
|
2613
|
+
/* @__PURE__ */ jsx10(
|
|
2614
|
+
Col,
|
|
2615
|
+
{
|
|
2616
|
+
xs: 24,
|
|
2617
|
+
lg: config2.showProgressImage ? 12 : 24,
|
|
2618
|
+
className: "flex items-center justify-center p-8",
|
|
2619
|
+
children: /* @__PURE__ */ jsxs6(Space2, { direction: "vertical", size: "large", className: "w-full max-w-md", children: [
|
|
2620
|
+
config2.showBackToHome && /* @__PURE__ */ jsx10(
|
|
2621
|
+
Button5,
|
|
2622
|
+
{
|
|
2623
|
+
type: "link",
|
|
2624
|
+
icon: /* @__PURE__ */ jsx10(ArrowLeftOutlined3, {}),
|
|
2625
|
+
className: "px-0",
|
|
2626
|
+
onClick: goBackToHome,
|
|
2627
|
+
children: "Back to Home"
|
|
2628
|
+
}
|
|
2629
|
+
),
|
|
2630
|
+
config2.showSteps && /* @__PURE__ */ jsx10(
|
|
2631
|
+
Steps,
|
|
2632
|
+
{
|
|
2633
|
+
current: stepperState.currentStepIndex,
|
|
2634
|
+
size: "small",
|
|
2635
|
+
responsive: false,
|
|
2636
|
+
items: stepperState.steps.map((step) => ({
|
|
2637
|
+
title: step.title,
|
|
2638
|
+
description: step.description,
|
|
2639
|
+
icon: step.icon,
|
|
2640
|
+
disabled: step.disabled || false
|
|
2641
|
+
}))
|
|
2642
|
+
}
|
|
2643
|
+
),
|
|
2644
|
+
/* @__PURE__ */ jsx10(
|
|
2645
|
+
Spin2,
|
|
2646
|
+
{
|
|
2647
|
+
spinning: actionState.isLoading,
|
|
2648
|
+
tip: getCurrentStepMessage(),
|
|
2649
|
+
children: /* @__PURE__ */ jsx10(SelectedComponent, __spreadValues({}, stepProps))
|
|
2650
|
+
}
|
|
2651
|
+
)
|
|
2652
|
+
] })
|
|
2653
|
+
}
|
|
2654
|
+
),
|
|
2655
|
+
config2.showProgressImage && /* @__PURE__ */ jsxs6(
|
|
2656
|
+
Col,
|
|
2657
|
+
{
|
|
2658
|
+
xs: 0,
|
|
2659
|
+
lg: 12,
|
|
2660
|
+
className: "relative bg-gradient-to-br from-blue-500 to-purple-600 flex items-center justify-center",
|
|
2661
|
+
children: [
|
|
2662
|
+
/* @__PURE__ */ jsx10("div", { className: "absolute inset-0 bg-black bg-opacity-20" }),
|
|
2663
|
+
/* @__PURE__ */ jsx10(
|
|
2664
|
+
Image,
|
|
2665
|
+
{
|
|
2666
|
+
src: config2.backgroundImage,
|
|
2667
|
+
alt: "Authentication",
|
|
2668
|
+
className: "absolute inset-0 object-cover",
|
|
2669
|
+
preview: false,
|
|
2670
|
+
fallback: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODAwIiBoZWlnaHQ9IjYwMCIgdmlld0JveD0iMCAwIDgwMCA2MDAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSI4MDAiIGhlaWdodD0iNjAwIiBmaWxsPSIjRjNGNEY2Ii8+CjxwYXRoIGQ9Ik0wIDMwMEg4MDBWNDAwSDBWMzAwWiIgZmlsbD0iI0U1RTdFQiIvPgo8Y2lyY2xlIGN4PSI0MDAiIGN5PSIzMDAiIHI9IjEwMCIgZmlsbD0iI0QxRDVGRiIvPgo8L3N2Zz4K"
|
|
2671
|
+
}
|
|
2672
|
+
),
|
|
2673
|
+
/* @__PURE__ */ jsx10(Flex3, { vertical: true, align: "center", className: "relative z-10 text-white", children: /* @__PURE__ */ jsxs6("div", { className: "w-32 h-32 mb-4 relative", children: [
|
|
2674
|
+
/* @__PURE__ */ jsxs6("svg", { className: "w-full h-full transform -rotate-90", viewBox: "0 0 120 120", children: [
|
|
2675
|
+
/* @__PURE__ */ jsx10(
|
|
2676
|
+
"circle",
|
|
2677
|
+
{
|
|
2678
|
+
cx: "60",
|
|
2679
|
+
cy: "60",
|
|
2680
|
+
r: "54",
|
|
2681
|
+
fill: "none",
|
|
2682
|
+
stroke: "rgba(255,255,255,0.3)",
|
|
2683
|
+
strokeWidth: "8"
|
|
2684
|
+
}
|
|
2685
|
+
),
|
|
2686
|
+
/* @__PURE__ */ jsx10(
|
|
2687
|
+
"circle",
|
|
2688
|
+
{
|
|
2689
|
+
cx: "60",
|
|
2690
|
+
cy: "60",
|
|
2691
|
+
r: "54",
|
|
2692
|
+
fill: "none",
|
|
2693
|
+
stroke: "white",
|
|
2694
|
+
strokeWidth: "8",
|
|
2695
|
+
strokeDasharray: `${2 * Math.PI * 54}`,
|
|
2696
|
+
strokeDashoffset: `${2 * Math.PI * 54 * (1 - stepperState.progress)}`,
|
|
2697
|
+
strokeLinecap: "round",
|
|
2698
|
+
className: "transition-all duration-500"
|
|
2699
|
+
}
|
|
2700
|
+
)
|
|
2701
|
+
] }),
|
|
2702
|
+
/* @__PURE__ */ jsx10(Flex3, { align: "center", justify: "center", className: "absolute inset-0", children: /* @__PURE__ */ jsxs6(Text, { className: "text-2xl font-bold text-white", children: [
|
|
2703
|
+
Math.round(stepperState.progress * 100),
|
|
2704
|
+
"%"
|
|
2705
|
+
] }) })
|
|
2706
|
+
] }) })
|
|
2707
|
+
]
|
|
2708
|
+
}
|
|
2709
|
+
)
|
|
2710
|
+
] }) });
|
|
2711
|
+
};
|
|
2712
|
+
|
|
2713
|
+
// src/components/auth/auth-flow-modal.tsx
|
|
2714
|
+
import { Modal, Spin as Spin3 } from "antd";
|
|
2715
|
+
import { Fragment as Fragment4, jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2716
|
+
var AuthFlowModal = ({ children }) => {
|
|
2717
|
+
const { isModalOpen, isInitialLoading } = useAuthFlowModal();
|
|
2718
|
+
if (isInitialLoading) {
|
|
2719
|
+
return /* @__PURE__ */ jsx11("div", { className: "min-h-screen flex items-center justify-center bg-gray-50", children: /* @__PURE__ */ jsx11(Spin3, { size: "large" }) });
|
|
2720
|
+
}
|
|
2721
|
+
return /* @__PURE__ */ jsxs7(Fragment4, { children: [
|
|
2722
|
+
isModalOpen ? /* @__PURE__ */ jsx11("div", { className: "min-h-screen flex items-center justify-center bg-gray-50", children: /* @__PURE__ */ jsx11(Spin3, { size: "large" }) }) : children,
|
|
2723
|
+
/* @__PURE__ */ jsx11(
|
|
2724
|
+
Modal,
|
|
2725
|
+
{
|
|
2726
|
+
title: "Sign In Required",
|
|
2727
|
+
open: isModalOpen,
|
|
2728
|
+
footer: null,
|
|
2729
|
+
width: 800,
|
|
2730
|
+
centered: true,
|
|
2731
|
+
destroyOnHidden: true,
|
|
2732
|
+
maskClosable: false,
|
|
2733
|
+
className: "auth-flow-modal",
|
|
2734
|
+
children: /* @__PURE__ */ jsx11(AuthFlowContainer, {})
|
|
2735
|
+
}
|
|
2736
|
+
)
|
|
2737
|
+
] });
|
|
2738
|
+
};
|
|
2739
|
+
|
|
2740
|
+
// src/components/auth/auth-initializer.tsx
|
|
2741
|
+
import { Fragment as Fragment5, jsx as jsx12 } from "react/jsx-runtime";
|
|
2742
|
+
var AuthInitializer = ({ children }) => {
|
|
2743
|
+
useAuthInitializer();
|
|
2744
|
+
return /* @__PURE__ */ jsx12(Fragment5, { children });
|
|
2745
|
+
};
|
|
2746
|
+
|
|
2747
|
+
// src/components/profile/profile-state-renderer.tsx
|
|
2748
|
+
import { Dropdown, Button as Button6 } from "antd";
|
|
2749
|
+
import { MailOutlined as MailOutlined3, CrownOutlined, LogoutOutlined, UserOutlined } from "@ant-design/icons";
|
|
2750
|
+
import Link from "next/link";
|
|
2751
|
+
import { jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2752
|
+
var LoadingState = () => /* @__PURE__ */ jsx13("div", { className: "min-h-screen flex items-center justify-center", children: /* @__PURE__ */ jsx13("div", { className: "text-center", children: /* @__PURE__ */ jsx13("div", { className: "animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600 mx-auto mb-4" }) }) });
|
|
2753
|
+
var UnauthenticatedState2 = () => /* @__PURE__ */ jsx13(Link, { href: "/login", children: /* @__PURE__ */ jsx13(Button6, { type: "primary", shape: "circle", size: "large", icon: /* @__PURE__ */ jsx13(UserOutlined, {}) }) });
|
|
2754
|
+
var AuthenticatedState2 = ({ user, onLogout }) => {
|
|
2755
|
+
var _a, _b, _c;
|
|
2756
|
+
const userInfoMenu = {
|
|
2757
|
+
items: [
|
|
2758
|
+
{
|
|
2759
|
+
key: "email",
|
|
2760
|
+
icon: /* @__PURE__ */ jsx13(MailOutlined3, {}),
|
|
2761
|
+
label: /* @__PURE__ */ jsxs8("div", { children: [
|
|
2762
|
+
/* @__PURE__ */ jsx13("div", { className: "text-xs text-gray-500", children: "Email" }),
|
|
2763
|
+
/* @__PURE__ */ jsx13("div", { className: "font-medium", children: ((_a = user.user) == null ? void 0 : _a.email) || "N/A" })
|
|
2764
|
+
] })
|
|
2765
|
+
},
|
|
2766
|
+
{
|
|
2767
|
+
key: "role",
|
|
2768
|
+
icon: /* @__PURE__ */ jsx13(CrownOutlined, {}),
|
|
2769
|
+
label: /* @__PURE__ */ jsxs8("div", { children: [
|
|
2770
|
+
/* @__PURE__ */ jsx13("div", { className: "text-xs text-gray-500", children: "Role" }),
|
|
2771
|
+
/* @__PURE__ */ jsx13("div", { className: "font-medium", children: ((_c = (_b = user.user) == null ? void 0 : _b.role) == null ? void 0 : _c.name) || "N/A" })
|
|
2772
|
+
] })
|
|
2773
|
+
},
|
|
2774
|
+
{
|
|
2775
|
+
type: "divider"
|
|
2776
|
+
},
|
|
2777
|
+
{
|
|
2778
|
+
key: "logout",
|
|
2779
|
+
icon: /* @__PURE__ */ jsx13(LogoutOutlined, {}),
|
|
2780
|
+
label: "Logout",
|
|
2781
|
+
onClick: onLogout
|
|
2782
|
+
}
|
|
2783
|
+
]
|
|
2784
|
+
};
|
|
2785
|
+
return /* @__PURE__ */ jsx13(Dropdown, { menu: userInfoMenu, placement: "bottom", trigger: ["click"], children: /* @__PURE__ */ jsx13(Button6, { type: "primary", shape: "circle", size: "large", icon: /* @__PURE__ */ jsx13(UserOutlined, {}) }) });
|
|
2786
|
+
};
|
|
2787
|
+
var resolveState = (isLoading, isAuthenticated, user) => {
|
|
2788
|
+
if (isLoading) return "LOADING" /* LOADING */;
|
|
2789
|
+
if (!isAuthenticated || !user) return "UNAUTHENTICATED" /* UNAUTHENTICATED */;
|
|
2790
|
+
return "AUTHENTICATED" /* AUTHENTICATED */;
|
|
2791
|
+
};
|
|
2792
|
+
var getComponentProps = (state, user, callbacks) => {
|
|
2793
|
+
switch (state) {
|
|
2794
|
+
case "AUTHENTICATED" /* AUTHENTICATED */:
|
|
2795
|
+
return {
|
|
2796
|
+
user,
|
|
2797
|
+
onLogout: callbacks.onLogout
|
|
2798
|
+
};
|
|
2799
|
+
default:
|
|
2800
|
+
return {};
|
|
2801
|
+
}
|
|
2802
|
+
};
|
|
2803
|
+
var ProfileStateRenderer = () => {
|
|
2804
|
+
const { user, isLoading, isAuthenticated } = useAuth();
|
|
2805
|
+
const logout = useLogout();
|
|
2806
|
+
const currentState = resolveState(isLoading, isAuthenticated, user);
|
|
2807
|
+
const componentProps = getComponentProps(currentState, user, {
|
|
2808
|
+
onLogout: logout
|
|
2809
|
+
});
|
|
2810
|
+
switch (currentState) {
|
|
2811
|
+
case "LOADING" /* LOADING */:
|
|
2812
|
+
return /* @__PURE__ */ jsx13(LoadingState, {});
|
|
2813
|
+
case "UNAUTHENTICATED" /* UNAUTHENTICATED */:
|
|
2814
|
+
return /* @__PURE__ */ jsx13(UnauthenticatedState2, {});
|
|
2815
|
+
case "AUTHENTICATED" /* AUTHENTICATED */:
|
|
2816
|
+
return /* @__PURE__ */ jsx13(AuthenticatedState2, __spreadValues({}, componentProps));
|
|
2817
|
+
default:
|
|
2818
|
+
return /* @__PURE__ */ jsx13(LoadingState, {});
|
|
2819
|
+
}
|
|
2820
|
+
};
|
|
2821
|
+
|
|
2822
|
+
// src/components/demo/cross-tab-demo.tsx
|
|
2823
|
+
import { Card, Typography as Typography3, Tag, Space as Space3 } from "antd";
|
|
2824
|
+
import { Fragment as Fragment6, jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2825
|
+
var { Title: Title2, Text: Text2, Paragraph: Paragraph2 } = Typography3;
|
|
2826
|
+
var CrossTabDemo = () => {
|
|
2827
|
+
var _a;
|
|
2828
|
+
const { isAuthenticated, user } = useAuth();
|
|
2829
|
+
const currentPageType = CrossTabBehaviorHandler.getCurrentPageType();
|
|
2830
|
+
return /* @__PURE__ */ jsx14(
|
|
2831
|
+
Card,
|
|
2832
|
+
{
|
|
2833
|
+
title: "Cross-Tab Authentication Demo",
|
|
2834
|
+
className: "w-full max-w-2xl",
|
|
2835
|
+
style: { marginTop: "2rem" },
|
|
2836
|
+
children: /* @__PURE__ */ jsxs9(Space3, { direction: "vertical", size: "middle", className: "w-full", children: [
|
|
2837
|
+
/* @__PURE__ */ jsxs9("div", { children: [
|
|
2838
|
+
/* @__PURE__ */ jsx14(Title2, { level: 4, children: "Current Status" }),
|
|
2839
|
+
/* @__PURE__ */ jsxs9(Space3, { children: [
|
|
2840
|
+
/* @__PURE__ */ jsx14(Text2, { strong: true, children: "Authentication:" }),
|
|
2841
|
+
/* @__PURE__ */ jsx14(Tag, { color: isAuthenticated ? "green" : "red", children: isAuthenticated ? "Authenticated" : "Not Authenticated" })
|
|
2842
|
+
] }),
|
|
2843
|
+
/* @__PURE__ */ jsx14("br", {}),
|
|
2844
|
+
/* @__PURE__ */ jsxs9(Space3, { style: { marginTop: "0.5rem" }, children: [
|
|
2845
|
+
/* @__PURE__ */ jsx14(Text2, { strong: true, children: "Current Page:" }),
|
|
2846
|
+
/* @__PURE__ */ jsx14(Tag, { color: "blue", children: currentPageType })
|
|
2847
|
+
] }),
|
|
2848
|
+
user && /* @__PURE__ */ jsxs9(Fragment6, { children: [
|
|
2849
|
+
/* @__PURE__ */ jsx14("br", {}),
|
|
2850
|
+
/* @__PURE__ */ jsxs9(Space3, { style: { marginTop: "0.5rem" }, children: [
|
|
2851
|
+
/* @__PURE__ */ jsx14(Text2, { strong: true, children: "User:" }),
|
|
2852
|
+
/* @__PURE__ */ jsx14(Text2, { children: (_a = user.user) == null ? void 0 : _a.email })
|
|
2853
|
+
] })
|
|
2854
|
+
] })
|
|
2855
|
+
] }),
|
|
2856
|
+
/* @__PURE__ */ jsxs9("div", { children: [
|
|
2857
|
+
/* @__PURE__ */ jsx14(Title2, { level: 4, children: "How It Works" }),
|
|
2858
|
+
/* @__PURE__ */ jsx14(Paragraph2, { children: "This application now supports enhanced cross-tab authentication handling:" }),
|
|
2859
|
+
/* @__PURE__ */ jsxs9("div", { style: { marginLeft: "1rem" }, children: [
|
|
2860
|
+
/* @__PURE__ */ jsxs9(Paragraph2, { children: [
|
|
2861
|
+
/* @__PURE__ */ jsx14(Text2, { strong: true, children: "Login Scenario:" }),
|
|
2862
|
+
/* @__PURE__ */ jsx14("br", {}),
|
|
2863
|
+
"\u2022 Open multiple tabs with some on the login page and others on different pages",
|
|
2864
|
+
/* @__PURE__ */ jsx14("br", {}),
|
|
2865
|
+
"\u2022 When you log in from one tab, other login tabs will automatically redirect to the dashboard",
|
|
2866
|
+
/* @__PURE__ */ jsx14("br", {}),
|
|
2867
|
+
"\u2022 Non-login pages will receive the login event but won't redirect (they'll just update their state)"
|
|
2868
|
+
] }),
|
|
2869
|
+
/* @__PURE__ */ jsxs9(Paragraph2, { children: [
|
|
2870
|
+
/* @__PURE__ */ jsx14(Text2, { strong: true, children: "Logout Scenario:" }),
|
|
2871
|
+
/* @__PURE__ */ jsx14("br", {}),
|
|
2872
|
+
"\u2022 When you log out from the dashboard, other dashboard tabs will redirect to the login page",
|
|
2873
|
+
/* @__PURE__ */ jsx14("br", {}),
|
|
2874
|
+
"\u2022 Other pages will receive the logout event and update their authentication state"
|
|
2875
|
+
] })
|
|
2876
|
+
] })
|
|
2877
|
+
] }),
|
|
2878
|
+
/* @__PURE__ */ jsxs9("div", { children: [
|
|
2879
|
+
/* @__PURE__ */ jsx14(Title2, { level: 4, children: "Test Instructions" }),
|
|
2880
|
+
/* @__PURE__ */ jsxs9(Paragraph2, { children: [
|
|
2881
|
+
"1. Open this page in multiple browser tabs",
|
|
2882
|
+
/* @__PURE__ */ jsx14("br", {}),
|
|
2883
|
+
"2. Navigate to ",
|
|
2884
|
+
/* @__PURE__ */ jsx14(Text2, { code: true, children: "/login" }),
|
|
2885
|
+
" in some tabs and ",
|
|
2886
|
+
/* @__PURE__ */ jsx14(Text2, { code: true, children: "/dashboard" }),
|
|
2887
|
+
" in others",
|
|
2888
|
+
/* @__PURE__ */ jsx14("br", {}),
|
|
2889
|
+
"3. Log in from one tab and observe the automatic redirects in other tabs",
|
|
2890
|
+
/* @__PURE__ */ jsx14("br", {}),
|
|
2891
|
+
"4. Log out from the dashboard and see how other tabs react"
|
|
2892
|
+
] })
|
|
2893
|
+
] })
|
|
2894
|
+
] })
|
|
2895
|
+
}
|
|
2896
|
+
);
|
|
2897
|
+
};
|
|
2898
|
+
export {
|
|
2899
|
+
AUTH_ENDPOINTS,
|
|
2900
|
+
AlertDisplay,
|
|
2901
|
+
AuthEventType,
|
|
2902
|
+
AuthFlowContainer,
|
|
2903
|
+
AuthFlowModal,
|
|
2904
|
+
AuthFlowStep,
|
|
2905
|
+
AuthInitializer,
|
|
2906
|
+
AuthOrchestrator,
|
|
2907
|
+
AuthOrchestratorFactory,
|
|
2908
|
+
AuthResultFactory,
|
|
2909
|
+
AuthService,
|
|
2910
|
+
AuthenticatedState,
|
|
2911
|
+
AuthenticationStatusContext,
|
|
2912
|
+
BaseErrorHandler,
|
|
2913
|
+
BaseEventBus,
|
|
2914
|
+
BaseForm,
|
|
2915
|
+
BaseService,
|
|
2916
|
+
BroadcastChannelEventBus,
|
|
2917
|
+
Channel,
|
|
2918
|
+
CookieUtils,
|
|
2919
|
+
CrossTabBehaviorConfig,
|
|
2920
|
+
CrossTabBehaviorHandler,
|
|
2921
|
+
CrossTabDemo,
|
|
2922
|
+
DevelopmentLogger,
|
|
2923
|
+
EMAIL_SUBMISSION_NAVIGATION,
|
|
2924
|
+
EmailStep,
|
|
2925
|
+
EndpointBuilder,
|
|
2926
|
+
ExistingUserLoginStrategy,
|
|
2927
|
+
FormFields,
|
|
2928
|
+
FormHeader,
|
|
2929
|
+
GenericErrorHandler,
|
|
2930
|
+
HttpClient,
|
|
2931
|
+
HttpMethod,
|
|
2932
|
+
LocalStorageUtils,
|
|
2933
|
+
LoggerFactory,
|
|
2934
|
+
LoginFlowStrategyFactory,
|
|
2935
|
+
MiddlewareConfig,
|
|
2936
|
+
NavigationAction,
|
|
2937
|
+
NetworkErrorHandler,
|
|
2938
|
+
NextAction,
|
|
2939
|
+
PASSWORD_SUBMISSION_NAVIGATION,
|
|
2940
|
+
PageType,
|
|
2941
|
+
PageTypePatterns,
|
|
2942
|
+
PasswordStep,
|
|
2943
|
+
ProductionLogger,
|
|
2944
|
+
ProfileStateRenderer,
|
|
2945
|
+
ProfileUIState,
|
|
2946
|
+
RoleType,
|
|
2947
|
+
SignupFlowStrategy,
|
|
2948
|
+
TokenManager,
|
|
2949
|
+
UnauthenticatedState,
|
|
2950
|
+
UserStorageManager,
|
|
2951
|
+
VERIFICATION_SUBMISSION_NAVIGATION,
|
|
2952
|
+
ValidationErrorHandler,
|
|
2953
|
+
VerificationStep,
|
|
2954
|
+
config,
|
|
2955
|
+
createAuthSteps,
|
|
2956
|
+
createPropsFactoryRegistry,
|
|
2957
|
+
createStepRegistry,
|
|
2958
|
+
getAuthPageStepMessage,
|
|
2959
|
+
getEmailField,
|
|
2960
|
+
getEmailStepComponent,
|
|
2961
|
+
getPasswordField,
|
|
2962
|
+
getPasswordStepComponent,
|
|
2963
|
+
getStepForEmailSubmission,
|
|
2964
|
+
getStepForPasswordSubmission,
|
|
2965
|
+
getStepForVerificationSubmission,
|
|
2966
|
+
getStepProgressMessage,
|
|
2967
|
+
getVerificationField,
|
|
2968
|
+
getVerificationStepComponent,
|
|
2969
|
+
middlewareMatcher,
|
|
2970
|
+
useAuth,
|
|
2971
|
+
useAuthActionHandler,
|
|
2972
|
+
useAuthEventBus,
|
|
2973
|
+
useAuthFlowModal,
|
|
2974
|
+
useAuthInitializer,
|
|
2975
|
+
useIsAuthenticated,
|
|
2976
|
+
useLogout,
|
|
2977
|
+
useRefreshUser,
|
|
2978
|
+
useSharedEventBus,
|
|
2979
|
+
useSignInRequiredParams,
|
|
2980
|
+
useStepRegistry,
|
|
2981
|
+
useStepRenderer,
|
|
2982
|
+
useStepper,
|
|
2983
|
+
useUser,
|
|
2984
|
+
useUserActions,
|
|
2985
|
+
useUserData,
|
|
2986
|
+
useUserError,
|
|
2987
|
+
useUserLoading,
|
|
2988
|
+
useUserProfile,
|
|
2989
|
+
useUserStore,
|
|
2990
|
+
userSelectors
|
|
2991
|
+
};
|
|
2992
|
+
/*! Bundled license information:
|
|
2993
|
+
|
|
2994
|
+
js-cookie/dist/js.cookie.mjs:
|
|
2995
|
+
(*! js-cookie v3.0.5 | MIT *)
|
|
2996
|
+
*/
|