Develop
This commit is contained in:
parent
9a455af203
commit
8a0ffb2e8a
@ -63,7 +63,62 @@ export function isShortDomainError(error: MaybeError): error is ShortDomainError
|
|||||||
return typeof error === 'string'
|
return typeof error === 'string'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Express-validator error format
|
||||||
|
*/
|
||||||
|
interface ValidationError {
|
||||||
|
type: string
|
||||||
|
value: string
|
||||||
|
msg: string
|
||||||
|
path: string
|
||||||
|
location: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service error format (e.g., ZeroBounce)
|
||||||
|
*/
|
||||||
|
interface ServiceErrorResponse {
|
||||||
|
status: 'error'
|
||||||
|
message: string
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValidationErrorArray(data: unknown): data is ValidationError[] {
|
||||||
|
return Array.isArray(data) && data.length > 0 && 'msg' in data[0] && 'path' in data[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
function isServiceErrorResponse(data: unknown): data is ServiceErrorResponse {
|
||||||
|
return typeof data === 'object' && data !== null && 'status' in data && 'message' in data
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract error message from ApiError
|
||||||
|
* Handles multiple formats:
|
||||||
|
* 1. { errors: [{ msg, path }] } - express-validator
|
||||||
|
* 2. { status: "error", message: "..." } - service errors (ZeroBounce)
|
||||||
|
* 3. { error: "..." } - single error
|
||||||
|
* 4. { errors: { base: [...] } } - base errors
|
||||||
|
*/
|
||||||
export function extractErrorMessage(apiError: ApiError): string {
|
export function extractErrorMessage(apiError: ApiError): string {
|
||||||
|
const responseData = apiError.responseData
|
||||||
|
|
||||||
|
// Check for service error format: { status: "error", message: "..." }
|
||||||
|
if (isServiceErrorResponse(responseData)) {
|
||||||
|
return responseData.message
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for express-validator format: { errors: [{ msg, path }] }
|
||||||
|
if (responseData && typeof responseData === 'object' && 'errors' in responseData) {
|
||||||
|
const errors = (responseData as { errors: unknown }).errors
|
||||||
|
if (isValidationErrorArray(errors)) {
|
||||||
|
const emailError = errors.find(e => e.path === 'email')
|
||||||
|
if (emailError) {
|
||||||
|
return emailError.msg
|
||||||
|
}
|
||||||
|
return errors[0].msg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to original logic for other formats
|
||||||
const body = isSingleErrorResponse(apiError.body) ? [apiError.body.error] : apiError.body.errors
|
const body = isSingleErrorResponse(apiError.body) ? [apiError.body.error] : apiError.body.errors
|
||||||
const errors = Array.isArray(body) ? body : body.base
|
const errors = Array.isArray(body) ? body : body.base
|
||||||
const firstError = errors.at(0)
|
const firstError = errors.at(0)
|
||||||
@ -78,3 +133,24 @@ export function extractErrorMessage(apiError: ApiError): string {
|
|||||||
}
|
}
|
||||||
return firstError.title
|
return firstError.title
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract email suggestion from error message
|
||||||
|
* Parses "Did you mean user@gmail.com?" format
|
||||||
|
*/
|
||||||
|
export function extractEmailSuggestion(apiError: ApiError): string | null {
|
||||||
|
const responseData = apiError.responseData
|
||||||
|
|
||||||
|
if (responseData && typeof responseData === 'object' && 'errors' in responseData) {
|
||||||
|
const errors = (responseData as { errors: unknown }).errors
|
||||||
|
if (isValidationErrorArray(errors)) {
|
||||||
|
const emailError = errors.find(e => e.path === 'email')
|
||||||
|
if (emailError) {
|
||||||
|
const match = emailError.msg.match(/Did you mean (.+)\?/)
|
||||||
|
return match ? match[1] : null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { ErrorPayload, useApi } from "@/api";
|
import { ErrorPayload, useApi } from "@/api";
|
||||||
|
import { ApiError, extractErrorMessage, extractEmailSuggestion } from "@/api/errors";
|
||||||
import { EGender, ESourceAuthorization, ICreateAuthorizePayload, ICreateAuthorizeResponse } from "@/api/resources/User";
|
import { EGender, ESourceAuthorization, ICreateAuthorizePayload, ICreateAuthorizeResponse } from "@/api/resources/User";
|
||||||
import { useAuth } from "@/auth";
|
import { useAuth } from "@/auth";
|
||||||
import { getClientTimezone } from "@/locales";
|
import { getClientTimezone } from "@/locales";
|
||||||
@ -23,6 +24,7 @@ export const useAuthentication = () => {
|
|||||||
const { updateSession } = useSession();
|
const { updateSession } = useSession();
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
const [suggestedEmail, setSuggestedEmail] = useState<string | null>(null);
|
||||||
const [token, setToken] = useState<string | null>(null);
|
const [token, setToken] = useState<string | null>(null);
|
||||||
const { user, signUp } = useAuth();
|
const { user, signUp } = useAuth();
|
||||||
const locale = i18n.language;
|
const locale = i18n.language;
|
||||||
@ -215,8 +217,17 @@ export const useAuthentication = () => {
|
|||||||
metricService.reachGoal(EGoals.ROSE_VIDEO_CREATION_START, [EMetrics.YANDEX, EMetrics.KLAVIYO]);
|
metricService.reachGoal(EGoals.ROSE_VIDEO_CREATION_START, [EMetrics.YANDEX, EMetrics.KLAVIYO]);
|
||||||
}
|
}
|
||||||
dispatch(actions.status.update("registred"));
|
dispatch(actions.status.update("registred"));
|
||||||
} catch (error) {
|
} catch (err) {
|
||||||
setError((error as Error).message);
|
// Extract error message from API error
|
||||||
|
if (err instanceof ApiError) {
|
||||||
|
const message = extractErrorMessage(err);
|
||||||
|
const suggestion = extractEmailSuggestion(err);
|
||||||
|
setError(message);
|
||||||
|
setSuggestedEmail(suggestion);
|
||||||
|
} else {
|
||||||
|
setError((err as Error).message);
|
||||||
|
setSuggestedEmail(null);
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
@ -251,31 +262,44 @@ export const useAuthentication = () => {
|
|||||||
metricService.reachGoal(EGoals.ROSE_VIDEO_CREATION_START, [EMetrics.YANDEX, EMetrics.KLAVIYO]);
|
metricService.reachGoal(EGoals.ROSE_VIDEO_CREATION_START, [EMetrics.YANDEX, EMetrics.KLAVIYO]);
|
||||||
}
|
}
|
||||||
dispatch(actions.status.update("registred"));
|
dispatch(actions.status.update("registred"));
|
||||||
} catch (error) {
|
} catch (err) {
|
||||||
setError((error as Error).message);
|
if (err instanceof ApiError) {
|
||||||
|
setError(extractErrorMessage(err));
|
||||||
|
} else {
|
||||||
|
setError((err as Error).message);
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
}, [api, dispatch, getAuthorizationPayload, signUp])
|
}, [api, dispatch, getAuthorizationPayload, signUp])
|
||||||
|
|
||||||
|
const clearError = useCallback(() => {
|
||||||
|
setError(null);
|
||||||
|
setSuggestedEmail(null);
|
||||||
|
}, []);
|
||||||
|
|
||||||
return useMemo(
|
return useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
isLoading,
|
isLoading,
|
||||||
error,
|
error,
|
||||||
|
suggestedEmail,
|
||||||
token,
|
token,
|
||||||
user,
|
user,
|
||||||
authorization,
|
authorization,
|
||||||
authorizationWithPassword,
|
authorizationWithPassword,
|
||||||
anonymousAuthorization
|
anonymousAuthorization,
|
||||||
|
clearError
|
||||||
}),
|
}),
|
||||||
[
|
[
|
||||||
isLoading,
|
isLoading,
|
||||||
error,
|
error,
|
||||||
|
suggestedEmail,
|
||||||
token,
|
token,
|
||||||
user,
|
user,
|
||||||
authorization,
|
authorization,
|
||||||
authorizationWithPassword,
|
authorizationWithPassword,
|
||||||
anonymousAuthorization
|
anonymousAuthorization,
|
||||||
|
clearError
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user