Cleanup error messages (#1412)
This commit is contained in:
commit
1e85b4488a
|
@ -35,7 +35,7 @@ export default function Login(props: LoginProps) {
|
|||
router.push(PAGES.CREDENTIALS);
|
||||
}
|
||||
} catch (e) {
|
||||
setFieldError(`${t('UNKNOWN_ERROR} ${e.message}')}`);
|
||||
setFieldError(`${t('UNKNOWN_ERROR')} (reason:${e.message})`);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ export default function SignUp(props: SignUpProps) {
|
|||
setData(LS_KEYS.USER, { email });
|
||||
await sendOtt(email);
|
||||
} catch (e) {
|
||||
setFieldError('confirm', `${t('UNKNOWN_ERROR} ${e.message}')}`);
|
||||
setFieldError('confirm', `${t('UNKNOWN_ERROR')} ${e.message}`);
|
||||
throw e;
|
||||
}
|
||||
try {
|
||||
|
|
|
@ -12,7 +12,7 @@ import { syncFiles, trashFiles } from 'services/fileService';
|
|||
import { EnteFile } from 'types/file';
|
||||
import { SelectedState } from 'types/gallery';
|
||||
|
||||
import { ServerErrorCodes } from 'utils/error';
|
||||
import { ApiError } from 'utils/error';
|
||||
import { constructFileToCollectionMap, getSelectedFiles } from 'utils/file';
|
||||
import {
|
||||
DeduplicateContextType,
|
||||
|
@ -30,6 +30,7 @@ import { VerticallyCentered } from 'components/Container';
|
|||
import Typography from '@mui/material/Typography';
|
||||
import useMemoSingleThreaded from 'hooks/useMemoSingleThreaded';
|
||||
import InMemoryStore, { MS_KEYS } from 'services/InMemoryStore';
|
||||
import { HttpStatusCode } from 'axios';
|
||||
|
||||
export const DeduplicateContext = createContext<DeduplicateContextType>(
|
||||
DefaultDeduplicateContext
|
||||
|
@ -124,21 +125,24 @@ export default function Deduplicate() {
|
|||
const selectedFiles = getSelectedFiles(selected, duplicateFiles);
|
||||
await trashFiles(selectedFiles);
|
||||
} catch (e) {
|
||||
switch (e.status?.toString()) {
|
||||
case ServerErrorCodes.FORBIDDEN:
|
||||
setDialogMessage({
|
||||
title: t('ERROR'),
|
||||
if (
|
||||
e instanceof ApiError &&
|
||||
e.httpStatusCode === HttpStatusCode.Forbidden
|
||||
) {
|
||||
setDialogMessage({
|
||||
title: t('ERROR'),
|
||||
|
||||
close: { variant: 'critical' },
|
||||
content: t('NOT_FILE_OWNER'),
|
||||
});
|
||||
close: { variant: 'critical' },
|
||||
content: t('NOT_FILE_OWNER'),
|
||||
});
|
||||
} else {
|
||||
setDialogMessage({
|
||||
title: t('ERROR'),
|
||||
|
||||
close: { variant: 'critical' },
|
||||
content: t('UNKNOWN_ERROR'),
|
||||
});
|
||||
}
|
||||
setDialogMessage({
|
||||
title: t('ERROR'),
|
||||
|
||||
close: { variant: 'critical' },
|
||||
content: t('UNKNOWN_ERROR'),
|
||||
});
|
||||
} finally {
|
||||
await syncWithRemote();
|
||||
finishLoading();
|
||||
|
|
|
@ -70,7 +70,7 @@ import {
|
|||
TRASH_SECTION,
|
||||
} from 'constants/collection';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import { CustomError, ServerErrorCodes } from 'utils/error';
|
||||
import { CustomError } from 'utils/error';
|
||||
import { PAGES } from 'constants/pages';
|
||||
import {
|
||||
COLLECTION_OPS_TYPE,
|
||||
|
@ -679,7 +679,7 @@ export default function Gallery() {
|
|||
}
|
||||
const tokenValid = await isTokenValid(token);
|
||||
if (!tokenValid) {
|
||||
throw new Error(ServerErrorCodes.SESSION_EXPIRED);
|
||||
throw new Error(CustomError.SESSION_EXPIRED);
|
||||
}
|
||||
!silent && startLoading();
|
||||
const collections = await getAllLatestCollections();
|
||||
|
@ -694,7 +694,7 @@ export default function Gallery() {
|
|||
await syncMapEnabled();
|
||||
} catch (e) {
|
||||
switch (e.message) {
|
||||
case ServerErrorCodes.SESSION_EXPIRED:
|
||||
case CustomError.SESSION_EXPIRED:
|
||||
showSessionExpiredMessage();
|
||||
break;
|
||||
case CustomError.KEY_MISSING:
|
||||
|
|
|
@ -24,6 +24,8 @@ import { Trans } from 'react-i18next';
|
|||
import { Link } from '@mui/material';
|
||||
import { SUPPORT_EMAIL } from 'constants/urls';
|
||||
import { DialogBoxAttributes } from 'types/dialogBox';
|
||||
import { ApiError } from 'utils/error';
|
||||
import { HttpStatusCode } from 'axios';
|
||||
|
||||
const bip39 = require('bip39');
|
||||
// mobile client library only supports english.
|
||||
|
@ -68,7 +70,10 @@ export default function Recover() {
|
|||
});
|
||||
}
|
||||
} catch (e) {
|
||||
if (e.status === 404) {
|
||||
if (
|
||||
e instanceof ApiError &&
|
||||
e.httpStatusCode === HttpStatusCode.NotFound
|
||||
) {
|
||||
logoutUser();
|
||||
} else {
|
||||
logError(e, 'two factor recovery page setup failed');
|
||||
|
|
|
@ -15,6 +15,8 @@ import FormTitle from 'components/Form/FormPaper/Title';
|
|||
import FormPaperFooter from 'components/Form/FormPaper/Footer';
|
||||
import { VerticallyCentered } from 'components/Container';
|
||||
import InMemoryStore, { MS_KEYS } from 'services/InMemoryStore';
|
||||
import { ApiError } from 'utils/error';
|
||||
import { HttpStatusCode } from 'axios';
|
||||
|
||||
export default function Home() {
|
||||
const [sessionID, setSessionID] = useState('');
|
||||
|
@ -52,7 +54,10 @@ export default function Home() {
|
|||
InMemoryStore.delete(MS_KEYS.REDIRECT_URL);
|
||||
router.push(redirectURL ?? PAGES.CREDENTIALS);
|
||||
} catch (e) {
|
||||
if (e.status === 404) {
|
||||
if (
|
||||
e instanceof ApiError &&
|
||||
e.httpStatusCode === HttpStatusCode.NotFound
|
||||
) {
|
||||
logoutUser();
|
||||
} else {
|
||||
throw e;
|
||||
|
|
|
@ -33,6 +33,8 @@ import SingleInputForm, {
|
|||
import EnteSpinner from 'components/EnteSpinner';
|
||||
import { VerticallyCentered } from 'components/Container';
|
||||
import InMemoryStore, { MS_KEYS } from 'services/InMemoryStore';
|
||||
import { ApiError } from 'utils/error';
|
||||
import { HttpStatusCode } from 'axios';
|
||||
|
||||
export default function Verify() {
|
||||
const [email, setEmail] = useState('');
|
||||
|
@ -122,10 +124,12 @@ export default function Verify() {
|
|||
}
|
||||
}
|
||||
} catch (e) {
|
||||
if (e?.status === 401) {
|
||||
setFieldError(t('INVALID_CODE'));
|
||||
} else if (e?.status === 410) {
|
||||
setFieldError(t('EXPIRED_CODE'));
|
||||
if (e instanceof ApiError) {
|
||||
if (e?.httpStatusCode === HttpStatusCode.Unauthorized) {
|
||||
setFieldError(t('INVALID_CODE'));
|
||||
} else if (e?.httpStatusCode === HttpStatusCode.Gone) {
|
||||
setFieldError(t('EXPIRED_CODE'));
|
||||
}
|
||||
} else {
|
||||
setFieldError(`${t('UNKNOWN_ERROR')} ${e.message}`);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import axios, { AxiosRequestConfig } from 'axios';
|
||||
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios';
|
||||
import { addLogLine } from 'utils/logging';
|
||||
import { logError } from 'utils/sentry';
|
||||
import { ApiError, CustomError, isApiErrorResponse } from 'utils/error';
|
||||
|
||||
interface IHTTPHeaders {
|
||||
[headerKey: string]: any;
|
||||
|
@ -18,34 +19,66 @@ class HTTPService {
|
|||
axios.interceptors.response.use(
|
||||
(response) => Promise.resolve(response),
|
||||
(error) => {
|
||||
const config = error.config as AxiosRequestConfig;
|
||||
if (error.response) {
|
||||
const response = error.response as AxiosResponse;
|
||||
let apiError: ApiError;
|
||||
// The request was made and the server responded with a status code
|
||||
// that falls out of the range of 2xx
|
||||
logError(error, 'HTTP Service Error', {
|
||||
url: error.config.url,
|
||||
method: error.config.method,
|
||||
cfRay: error.response?.headers['cf-ray'],
|
||||
xRequestId: error.response?.headers['x-request-id'],
|
||||
status: error.response.status,
|
||||
if (isApiErrorResponse(response.data)) {
|
||||
const responseData = response.data;
|
||||
logError(error, 'HTTP Service Error', {
|
||||
url: config.url,
|
||||
method: config.method,
|
||||
xRequestId: response.headers['x-request-id'],
|
||||
httpStatus: response.status,
|
||||
errMessage: responseData.message,
|
||||
errCode: responseData.code,
|
||||
});
|
||||
apiError = new ApiError(
|
||||
responseData.message,
|
||||
responseData.code,
|
||||
response.status
|
||||
);
|
||||
} else {
|
||||
if (response.status >= 400 && response.status < 500) {
|
||||
apiError = new ApiError(
|
||||
CustomError.CLIENT_ERROR,
|
||||
'',
|
||||
response.status
|
||||
);
|
||||
} else {
|
||||
apiError = new ApiError(
|
||||
CustomError.ServerError,
|
||||
'',
|
||||
response.status
|
||||
);
|
||||
}
|
||||
}
|
||||
logError(apiError, 'HTTP Service Error', {
|
||||
url: config.url,
|
||||
method: config.method,
|
||||
cfRay: response.headers['cf-ray'],
|
||||
xRequestId: response.headers['x-request-id'],
|
||||
httpStatus: response.status,
|
||||
});
|
||||
const { response } = error;
|
||||
return Promise.reject(response);
|
||||
throw apiError;
|
||||
} else if (error.request) {
|
||||
// The request was made but no response was received
|
||||
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
|
||||
// http.ClientRequest in node.js
|
||||
addLogLine(
|
||||
'request failed- no response',
|
||||
`url: ${error.config.url}`,
|
||||
`method: ${error.config.method}`
|
||||
`url: ${config.url}`,
|
||||
`method: ${config.method}`
|
||||
);
|
||||
return Promise.reject(error);
|
||||
} else {
|
||||
// Something happened in setting up the request that triggered an Error
|
||||
addLogLine(
|
||||
'request failed- axios error',
|
||||
`url: ${error.config.url}`,
|
||||
`method: ${error.config.method}`
|
||||
`url: ${config.url}`,
|
||||
`method: ${config.method}`
|
||||
);
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { HttpStatusCode } from 'axios';
|
||||
import HTTPService from 'services/HTTPService';
|
||||
import { AuthEntity, AuthKey } from 'types/authenticator/api';
|
||||
import { Code } from 'types/authenticator/code';
|
||||
import ComlinkCryptoWorker from 'utils/comlink/ComlinkCryptoWorker';
|
||||
import { getEndpoint } from 'utils/common/apiUtil';
|
||||
import { getActualKey, getToken } from 'utils/common/key';
|
||||
import { CustomError } from 'utils/error';
|
||||
import { ApiError, CustomError } from 'utils/error';
|
||||
import { logError } from 'utils/sentry';
|
||||
|
||||
const ENDPOINT = getEndpoint();
|
||||
|
@ -77,7 +78,10 @@ export const getAuthKey = async (): Promise<AuthKey> => {
|
|||
);
|
||||
return resp.data;
|
||||
} catch (e) {
|
||||
if (e.status === 404) {
|
||||
if (
|
||||
e instanceof ApiError &&
|
||||
e.httpStatusCode === HttpStatusCode.NotFound
|
||||
) {
|
||||
throw Error(CustomError.AUTH_KEY_NOT_FOUND);
|
||||
} else {
|
||||
logError(e, 'Get key failed');
|
||||
|
|
|
@ -40,13 +40,13 @@ import {
|
|||
UpdateSRPAndKeysResponse,
|
||||
GetSRPAttributesResponse,
|
||||
} from 'types/user';
|
||||
import { ServerErrorCodes } from 'utils/error';
|
||||
import { ApiError, CustomError } from 'utils/error';
|
||||
import isElectron from 'is-electron';
|
||||
import safeStorageService from './electron/safeStorage';
|
||||
import { deleteAllCache } from 'utils/storage/cache';
|
||||
import { B64EncryptionResult } from 'types/crypto';
|
||||
import { getLocalFamilyData, isPartOfFamily } from 'utils/user/family';
|
||||
import { AxiosResponse } from 'axios';
|
||||
import { AxiosResponse, HttpStatusCode } from 'axios';
|
||||
import { APPS, getAppName } from 'constants/apps';
|
||||
import { addLocalLog } from 'utils/logging';
|
||||
import { convertBase64ToBuffer, convertBufferToBase64 } from 'utils/user';
|
||||
|
@ -224,7 +224,10 @@ export const isTokenValid = async (token: string) => {
|
|||
return true;
|
||||
} catch (e) {
|
||||
logError(e, 'session-validity api call failed');
|
||||
if (e.status?.toString() === ServerErrorCodes.SESSION_EXPIRED) {
|
||||
if (
|
||||
e instanceof ApiError &&
|
||||
e.httpStatusCode === HttpStatusCode.Unauthorized
|
||||
) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
|
@ -554,11 +557,8 @@ export const getSRPAttributes = async (
|
|||
});
|
||||
return (resp.data as GetSRPAttributesResponse).attributes;
|
||||
} catch (e) {
|
||||
if (e.status?.toString() === ServerErrorCodes.NOT_FOUND) {
|
||||
return null;
|
||||
}
|
||||
logError(e, 'failed to get SRP attributes');
|
||||
throw e;
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -730,7 +730,14 @@ export const verifySRPSession = async (
|
|||
return resp.data as UserVerificationResponse;
|
||||
} catch (e) {
|
||||
logError(e, 'verifySRPSession failed');
|
||||
throw e;
|
||||
if (
|
||||
e instanceof ApiError &&
|
||||
e.httpStatusCode === HttpStatusCode.Forbidden
|
||||
) {
|
||||
throw Error(CustomError.INCORRECT_PASSWORD);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,15 +1,24 @@
|
|||
export const ServerErrorCodes = {
|
||||
SESSION_EXPIRED: '401',
|
||||
NO_ACTIVE_SUBSCRIPTION: '402',
|
||||
FORBIDDEN: '403',
|
||||
STORAGE_LIMIT_EXCEEDED: '426',
|
||||
FILE_TOO_LARGE: '413',
|
||||
TOKEN_EXPIRED: '410',
|
||||
TOO_MANY_REQUEST: '429',
|
||||
BAD_REQUEST: '400',
|
||||
PAYMENT_REQUIRED: '402',
|
||||
NOT_FOUND: '404',
|
||||
};
|
||||
import { HttpStatusCode } from 'axios';
|
||||
|
||||
export class ApiErrorResponse {
|
||||
code: string;
|
||||
message: string;
|
||||
}
|
||||
export class ApiError extends Error {
|
||||
httpStatusCode: number;
|
||||
errCode: string;
|
||||
|
||||
constructor(message: string, errCode: string, httpStatus: number) {
|
||||
super(message);
|
||||
this.name = 'ApiError';
|
||||
this.errCode = errCode;
|
||||
this.httpStatusCode = httpStatus;
|
||||
}
|
||||
}
|
||||
|
||||
export function isApiErrorResponse(object: any): object is ApiErrorResponse {
|
||||
return object && 'code' in object && 'message' in object;
|
||||
}
|
||||
|
||||
export const CustomError = {
|
||||
THUMBNAIL_GENERATION_FAILED: 'thumbnail generation failed',
|
||||
|
@ -72,34 +81,10 @@ export const CustomError = {
|
|||
PROCESSING_FAILED: 'processing failed',
|
||||
EXPORT_RECORD_JSON_PARSING_FAILED: 'export record json parsing failed',
|
||||
TWO_FACTOR_ENABLED: 'two factor enabled',
|
||||
CLIENT_ERROR: 'client error',
|
||||
ServerError: 'server error',
|
||||
};
|
||||
|
||||
export function parseUploadErrorCodes(error) {
|
||||
let parsedMessage = null;
|
||||
if (error?.status) {
|
||||
const errorCode = error.status.toString();
|
||||
switch (errorCode) {
|
||||
case ServerErrorCodes.NO_ACTIVE_SUBSCRIPTION:
|
||||
parsedMessage = CustomError.SUBSCRIPTION_EXPIRED;
|
||||
break;
|
||||
case ServerErrorCodes.STORAGE_LIMIT_EXCEEDED:
|
||||
parsedMessage = CustomError.STORAGE_QUOTA_EXCEEDED;
|
||||
break;
|
||||
case ServerErrorCodes.SESSION_EXPIRED:
|
||||
parsedMessage = CustomError.SESSION_EXPIRED;
|
||||
break;
|
||||
case ServerErrorCodes.FILE_TOO_LARGE:
|
||||
parsedMessage = CustomError.FILE_TOO_LARGE;
|
||||
break;
|
||||
default:
|
||||
parsedMessage = `${CustomError.UNKNOWN_ERROR} statusCode:${errorCode}`;
|
||||
}
|
||||
} else {
|
||||
parsedMessage = error.message;
|
||||
}
|
||||
return new Error(parsedMessage);
|
||||
}
|
||||
|
||||
export function handleUploadError(error): Error {
|
||||
const parsedError = parseUploadErrorCodes(error);
|
||||
|
||||
|
@ -123,29 +108,53 @@ export function errorWithContext(originalError: Error, context: string) {
|
|||
return errorWithContext;
|
||||
}
|
||||
|
||||
export function parseUploadErrorCodes(error) {
|
||||
let parsedMessage = null;
|
||||
if (error instanceof ApiError) {
|
||||
switch (error.httpStatusCode) {
|
||||
case HttpStatusCode.PaymentRequired:
|
||||
parsedMessage = CustomError.SUBSCRIPTION_EXPIRED;
|
||||
break;
|
||||
case HttpStatusCode.UpgradeRequired:
|
||||
parsedMessage = CustomError.STORAGE_QUOTA_EXCEEDED;
|
||||
break;
|
||||
case HttpStatusCode.Unauthorized:
|
||||
parsedMessage = CustomError.SESSION_EXPIRED;
|
||||
break;
|
||||
case HttpStatusCode.PayloadTooLarge:
|
||||
parsedMessage = CustomError.FILE_TOO_LARGE;
|
||||
break;
|
||||
default:
|
||||
parsedMessage = `${CustomError.UNKNOWN_ERROR} statusCode:${error.httpStatusCode}`;
|
||||
}
|
||||
} else {
|
||||
parsedMessage = error.message;
|
||||
}
|
||||
return new Error(parsedMessage);
|
||||
}
|
||||
|
||||
export const parseSharingErrorCodes = (error) => {
|
||||
let parsedMessage = null;
|
||||
if (error?.status) {
|
||||
const errorCode = error.status.toString();
|
||||
switch (errorCode) {
|
||||
case ServerErrorCodes.BAD_REQUEST:
|
||||
if (error instanceof ApiError) {
|
||||
switch (error.httpStatusCode) {
|
||||
case HttpStatusCode.BadRequest:
|
||||
parsedMessage = CustomError.BAD_REQUEST;
|
||||
break;
|
||||
case ServerErrorCodes.PAYMENT_REQUIRED:
|
||||
case HttpStatusCode.PaymentRequired:
|
||||
parsedMessage = CustomError.SUBSCRIPTION_NEEDED;
|
||||
break;
|
||||
case ServerErrorCodes.NOT_FOUND:
|
||||
case HttpStatusCode.NotFound:
|
||||
parsedMessage = CustomError.NOT_FOUND;
|
||||
break;
|
||||
case ServerErrorCodes.SESSION_EXPIRED:
|
||||
case ServerErrorCodes.TOKEN_EXPIRED:
|
||||
case HttpStatusCode.Unauthorized:
|
||||
case HttpStatusCode.Gone:
|
||||
parsedMessage = CustomError.TOKEN_EXPIRED;
|
||||
break;
|
||||
case ServerErrorCodes.TOO_MANY_REQUEST:
|
||||
case HttpStatusCode.TooManyRequests:
|
||||
parsedMessage = CustomError.TOO_MANY_REQUESTS;
|
||||
break;
|
||||
default:
|
||||
parsedMessage = `${CustomError.UNKNOWN_ERROR} statusCode:${errorCode}`;
|
||||
parsedMessage = `${CustomError.UNKNOWN_ERROR} statusCode:${error.httpStatusCode}`;
|
||||
}
|
||||
} else {
|
||||
parsedMessage = error.message;
|
||||
|
|
|
@ -3,6 +3,7 @@ import { addLocalLog, addLogLine } from 'utils/logging';
|
|||
import { getSentryUserID } from 'utils/user';
|
||||
import InMemoryStore, { MS_KEYS } from 'services/InMemoryStore';
|
||||
import { getHasOptedOutOfCrashReports } from 'utils/storage';
|
||||
import { ApiError } from 'utils/error';
|
||||
|
||||
export const logError = async (
|
||||
error: any,
|
||||
|
@ -12,11 +13,20 @@ export const logError = async (
|
|||
) => {
|
||||
const err = errorWithContext(error, msg);
|
||||
if (!skipAddLogLine) {
|
||||
addLogLine(
|
||||
`error: ${error?.name} ${error?.message} ${
|
||||
error?.stack
|
||||
} msg: ${msg} ${info ? `info: ${JSON.stringify(info)}` : ''}`
|
||||
);
|
||||
if (error instanceof ApiError) {
|
||||
addLogLine(`error: ${error?.name} ${error?.message}
|
||||
msg: ${msg} errorCode: ${JSON.stringify(error?.errCode)}
|
||||
httpStatusCode: ${JSON.stringify(error?.httpStatusCode)} ${
|
||||
info ? `info: ${JSON.stringify(info)}` : ''
|
||||
}
|
||||
${error?.stack}`);
|
||||
} else {
|
||||
addLogLine(
|
||||
`error: ${error?.name} ${error?.message}
|
||||
msg: ${msg} ${info ? `info: ${JSON.stringify(info)}` : ''}
|
||||
${error?.stack}`
|
||||
);
|
||||
}
|
||||
}
|
||||
if (!InMemoryStore.has(MS_KEYS.OPT_OUT_OF_CRASH_REPORTS)) {
|
||||
const optedOutOfCrashReports = getHasOptedOutOfCrashReports();
|
||||
|
|
Loading…
Reference in a new issue