Build and publish Docker images / Build and publish images (push) Successful in 2m21s
265 lines
8.3 KiB
JavaScript
265 lines
8.3 KiB
JavaScript
import Cookies from 'js-cookie';
|
||
import { LOCAL_STORAGE_KEYS } from 'dashboard/constants/localStorage';
|
||
import { SESSION_STORAGE_KEYS } from 'dashboard/constants/sessionStorage';
|
||
import { LocalStorage } from 'shared/helpers/localStorage';
|
||
import SessionStorage from 'shared/helpers/sessionStorage';
|
||
import { emitter } from 'shared/helpers/mitt';
|
||
import {
|
||
ANALYTICS_IDENTITY,
|
||
ANALYTICS_RESET,
|
||
CHATWOOT_RESET,
|
||
CHATWOOT_SET_USER,
|
||
} from '../../constants/appEvents';
|
||
|
||
Cookies.defaults = { sameSite: 'Lax' };
|
||
|
||
export const getLoadingStatus = state => state.fetchAPIloadingStatus;
|
||
export const setLoadingStatus = (state, status) => {
|
||
state.fetchAPIloadingStatus = status;
|
||
};
|
||
|
||
export const setUser = user => {
|
||
emitter.emit(CHATWOOT_SET_USER, { user });
|
||
emitter.emit(ANALYTICS_IDENTITY, { user });
|
||
};
|
||
|
||
export const setAuthCredentials = response => {
|
||
setUser(response.data.data);
|
||
};
|
||
|
||
export const clearBrowserSessionCookies = () => {
|
||
Cookies.remove('cw_d_session_info');
|
||
Cookies.remove('cw_d_session_state');
|
||
Cookies.remove('auth_data');
|
||
Cookies.remove('user');
|
||
['access-token', 'client', 'uid', 'token-type', 'expiry'].forEach(key =>
|
||
localStorage.removeItem(key)
|
||
);
|
||
};
|
||
|
||
export const clearLocalStorageOnLogout = () => {
|
||
LocalStorage.remove(LOCAL_STORAGE_KEYS.DRAFT_MESSAGES);
|
||
};
|
||
|
||
export const clearSessionStorageOnLogout = () => {
|
||
SessionStorage.remove(SESSION_STORAGE_KEYS.IMPERSONATION_USER);
|
||
};
|
||
|
||
export const deleteIndexedDBOnLogout = async () => {
|
||
let dbs = [];
|
||
try {
|
||
dbs = await window.indexedDB.databases();
|
||
dbs = dbs.map(db => db.name);
|
||
} catch (e) {
|
||
dbs = JSON.parse(localStorage.getItem('cw-idb-names') || '[]');
|
||
}
|
||
|
||
dbs.forEach(dbName => {
|
||
const deleteRequest = window.indexedDB.deleteDatabase(dbName);
|
||
|
||
deleteRequest.onerror = event => {
|
||
// eslint-disable-next-line no-console
|
||
console.error(`Error deleting database ${dbName}.`, event);
|
||
};
|
||
|
||
deleteRequest.onsuccess = () => {
|
||
// eslint-disable-next-line no-console
|
||
console.log(`Database ${dbName} deleted successfully.`);
|
||
};
|
||
});
|
||
|
||
localStorage.removeItem('cw-idb-names');
|
||
};
|
||
|
||
const getSafeLogoutRedirectLink = redirectLink => {
|
||
try {
|
||
const url = new URL(redirectLink || '/', window.location.origin);
|
||
if (url.origin !== window.location.origin) return '/';
|
||
return `${url.pathname}${url.search}${url.hash}`;
|
||
} catch {
|
||
return '/';
|
||
}
|
||
};
|
||
|
||
export const clearCookiesOnLogout = () => {
|
||
emitter.emit(CHATWOOT_RESET);
|
||
emitter.emit(ANALYTICS_RESET);
|
||
clearBrowserSessionCookies();
|
||
clearLocalStorageOnLogout();
|
||
clearSessionStorageOnLogout();
|
||
const globalConfig = window.globalConfig || {};
|
||
const logoutRedirectLink = getSafeLogoutRedirectLink(
|
||
globalConfig.LOGOUT_REDIRECT_LINK
|
||
);
|
||
window.location.assign(logoutRedirectLink);
|
||
};
|
||
|
||
const API_ERROR_MESSAGES = Object.freeze({
|
||
400: '请求参数有误,请检查后重试。',
|
||
401: '身份验证失败,请重新登录。',
|
||
403: '您没有权限执行此操作。',
|
||
404: '请求的资源不存在。',
|
||
408: '请求超时,请稍后再试。',
|
||
409: '操作冲突,请刷新后重试。',
|
||
422: '提交的信息有误,请检查后重试。',
|
||
429: '操作过于频繁,请稍后再试。',
|
||
500: '服务器发生错误,请稍后再试。',
|
||
502: '服务暂时不可用,请稍后再试。',
|
||
503: '服务暂时不可用,请稍后再试。',
|
||
504: '服务暂时不可用,请稍后再试。',
|
||
});
|
||
|
||
const extractStatusFromMessage = message => {
|
||
const match = message.match(
|
||
/(?:request failed with status code|status code|error)\s*[:=]?\s*([45]\d{2})/i
|
||
);
|
||
return match ? Number(match[1]) : null;
|
||
};
|
||
|
||
const getStatusMessage = status => {
|
||
const numericStatus = Number(status);
|
||
if (!Number.isInteger(numericStatus)) return '';
|
||
return (
|
||
API_ERROR_MESSAGES[numericStatus] ||
|
||
(numericStatus >= 500 ? API_ERROR_MESSAGES[500] : '')
|
||
);
|
||
};
|
||
|
||
const normalizeAPIErrorText = (message, status) => {
|
||
if (typeof message !== 'string') return message;
|
||
|
||
const text = message.trim();
|
||
if (!text) return text;
|
||
if (/^(network error|failed to fetch|network request failed)$/i.test(text)) {
|
||
return '网络连接失败,请检查网络后重试。';
|
||
}
|
||
if (
|
||
/\b(?:timeout|timed out)\b/i.test(text) ||
|
||
/(?:^|(?:请求|连接|操作|网络|服务|服务器|上游)[^,。!?;;::]{0,8})超时(?:[,。!?;;::]|$)/.test(
|
||
text
|
||
)
|
||
) {
|
||
return '请求超时,请稍后再试。';
|
||
}
|
||
if (/^invalid email or password$/i.test(text)) {
|
||
return '邮箱或密码错误,请重试。';
|
||
}
|
||
if (/^email not confirmed\b/i.test(text)) {
|
||
return '邮箱尚未验证,请先完成邮箱验证。';
|
||
}
|
||
if (/^user account is inactive\b/i.test(text)) {
|
||
return '账户已停用,请联系管理员。';
|
||
}
|
||
if (/^this account uses .* authentication/i.test(text)) {
|
||
return '此账户使用第三方登录,请通过对应方式登录。';
|
||
}
|
||
if (
|
||
/^(invalid (token|credentials|headers)|unauthorized|unauthenticated|authentication failed)$/i.test(
|
||
text
|
||
)
|
||
) {
|
||
return '身份验证失败,请重新登录。';
|
||
}
|
||
if (/^account not identified$/i.test(text)) {
|
||
return '无法识别当前账户,请重新登录。';
|
||
}
|
||
if (/administrator role required/i.test(text)) {
|
||
return '需要管理员权限才能执行此操作。';
|
||
}
|
||
if (
|
||
/^(not authorized|authorization header required|bearer token required|widget_token required)$/i.test(
|
||
text
|
||
)
|
||
) {
|
||
return '缺少有效的登录凭证,请重新登录。';
|
||
}
|
||
if (/^account is suspended$/i.test(text)) {
|
||
return '账户已被停用,请联系管理员。';
|
||
}
|
||
if (/^sso session (expired or invalid|data corrupt)$/i.test(text)) {
|
||
return '单点登录会话无效,请重新登录。';
|
||
}
|
||
if (/^invalid (source )?url/i.test(text)) {
|
||
return '请求地址无效,请检查后重试。';
|
||
}
|
||
|
||
const messageStatus = extractStatusFromMessage(text);
|
||
if (messageStatus) {
|
||
return getStatusMessage(messageStatus) || text;
|
||
}
|
||
|
||
if (
|
||
status &&
|
||
/^(bad request|unauthorized|unauthenticated|forbidden|not found|request timeout|unprocessable entity|too many requests|internal server error|bad gateway|service unavailable|gateway timeout|invalid credentials|authentication failed)$/i.test(
|
||
text
|
||
)
|
||
) {
|
||
return getStatusMessage(status) || text;
|
||
}
|
||
if (
|
||
status >= 500 &&
|
||
/\b(error|failed|unavailable|gateway|server)\b/i.test(text)
|
||
) {
|
||
return getStatusMessage(status) || text;
|
||
}
|
||
|
||
if (/^(axioserror:\s*)?(error:\s*)?\[object object\]$/i.test(text)) {
|
||
return '请求失败,请稍后再试。';
|
||
}
|
||
if (
|
||
/^(failed|unable to|could not|cannot|can't|something went wrong|an error occurred)\b/i.test(
|
||
text
|
||
)
|
||
) {
|
||
return '请求失败,请稍后再试。';
|
||
}
|
||
return message;
|
||
};
|
||
|
||
const extractErrorText = value => {
|
||
if (typeof value === 'string') return value;
|
||
if (value?.message && typeof value.message === 'string') {
|
||
return value.message;
|
||
}
|
||
if (Array.isArray(value)) return extractErrorText(value[0]);
|
||
if (value && typeof value === 'object') return JSON.stringify(value);
|
||
return '';
|
||
};
|
||
|
||
export const parseAPIErrorResponse = error => {
|
||
if (typeof error === 'string') return normalizeAPIErrorText(error);
|
||
|
||
const status = error?.response?.status ?? error?.status;
|
||
if (error?.code === 'ERR_NETWORK') {
|
||
return '网络连接失败,请检查网络后重试。';
|
||
}
|
||
if (error?.code === 'ECONNABORTED') {
|
||
return '请求超时,请稍后再试。';
|
||
}
|
||
const data = error?.response?.data;
|
||
const message = extractErrorText(data?.message);
|
||
if (message) return normalizeAPIErrorText(message, status);
|
||
|
||
const apiError = extractErrorText(data?.error);
|
||
if (apiError) return normalizeAPIErrorText(apiError, status);
|
||
|
||
const errors = extractErrorText(data?.errors);
|
||
if (errors) return normalizeAPIErrorText(errors, status);
|
||
|
||
const errorMessage = extractErrorText(error?.message);
|
||
if (errorMessage) return normalizeAPIErrorText(errorMessage, status);
|
||
|
||
return getStatusMessage(status) || '请求失败,请稍后再试。';
|
||
};
|
||
|
||
export const throwErrorMessage = error => {
|
||
const errorMessage = parseAPIErrorResponse(error);
|
||
throw new Error(errorMessage);
|
||
};
|
||
|
||
export const parseLinearAPIErrorResponse = (error, defaultMessage) => {
|
||
const errorData = error.response.data;
|
||
const errorMessage = errorData?.error?.errors?.[0]?.message || defaultMessage;
|
||
return errorMessage;
|
||
};
|