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 (/timeout|timed out|超时/i.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; };