This commit is contained in:
Daniil Chemerkin 2025-12-07 01:17:14 +00:00
parent 9a455af203
commit 8a0ffb2e8a
2 changed files with 106 additions and 6 deletions

View File

@ -63,7 +63,62 @@ export function isShortDomainError(error: MaybeError): error is ShortDomainError
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 {
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 errors = Array.isArray(body) ? body : body.base
const firstError = errors.at(0)
@ -78,3 +133,24 @@ export function extractErrorMessage(apiError: ApiError): string {
}
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
}

View File

@ -1,4 +1,5 @@
import { ErrorPayload, useApi } from "@/api";
import { ApiError, extractErrorMessage, extractEmailSuggestion } from "@/api/errors";
import { EGender, ESourceAuthorization, ICreateAuthorizePayload, ICreateAuthorizeResponse } from "@/api/resources/User";
import { useAuth } from "@/auth";
import { getClientTimezone } from "@/locales";
@ -23,6 +24,7 @@ export const useAuthentication = () => {
const { updateSession } = useSession();
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [suggestedEmail, setSuggestedEmail] = useState<string | null>(null);
const [token, setToken] = useState<string | null>(null);
const { user, signUp } = useAuth();
const locale = i18n.language;
@ -215,8 +217,17 @@ export const useAuthentication = () => {
metricService.reachGoal(EGoals.ROSE_VIDEO_CREATION_START, [EMetrics.YANDEX, EMetrics.KLAVIYO]);
}
dispatch(actions.status.update("registred"));
} catch (error) {
setError((error as Error).message);
} catch (err) {
// 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 {
setIsLoading(false);
}
@ -251,31 +262,44 @@ export const useAuthentication = () => {
metricService.reachGoal(EGoals.ROSE_VIDEO_CREATION_START, [EMetrics.YANDEX, EMetrics.KLAVIYO]);
}
dispatch(actions.status.update("registred"));
} catch (error) {
setError((error as Error).message);
} catch (err) {
if (err instanceof ApiError) {
setError(extractErrorMessage(err));
} else {
setError((err as Error).message);
}
} finally {
setIsLoading(false);
}
}, [api, dispatch, getAuthorizationPayload, signUp])
const clearError = useCallback(() => {
setError(null);
setSuggestedEmail(null);
}, []);
return useMemo(
() => ({
isLoading,
error,
suggestedEmail,
token,
user,
authorization,
authorizationWithPassword,
anonymousAuthorization
anonymousAuthorization,
clearError
}),
[
isLoading,
error,
suggestedEmail,
token,
user,
authorization,
authorizationWithPassword,
anonymousAuthorization
anonymousAuthorization,
clearError
]
);
}