fix: correct login entry routing and errors
This commit is contained in:
+19
-2
@@ -6,6 +6,23 @@ type RequestOptions = RequestInit & {
|
|||||||
|
|
||||||
export const DEFAULT_CLIENT_TENANT_ID = 'tenant-a';
|
export const DEFAULT_CLIENT_TENANT_ID = 'tenant-a';
|
||||||
|
|
||||||
|
async function readErrorMessage(response: Response) {
|
||||||
|
const fallback = `请求失败(${response.status})`;
|
||||||
|
const text = await response.text();
|
||||||
|
if (!text) return fallback;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(text) as { message?: string | string[]; error?: string };
|
||||||
|
if (Array.isArray(parsed.message)) return parsed.message.join(';');
|
||||||
|
if (parsed.message) return parsed.message;
|
||||||
|
if (parsed.error) return parsed.error;
|
||||||
|
} catch {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
async function request<T>(path: string, options: RequestOptions = {}): Promise<T> {
|
async function request<T>(path: string, options: RequestOptions = {}): Promise<T> {
|
||||||
const headers = new Headers(options.headers);
|
const headers = new Headers(options.headers);
|
||||||
headers.set('Content-Type', 'application/json');
|
headers.set('Content-Type', 'application/json');
|
||||||
@@ -19,7 +36,7 @@ async function request<T>(path: string, options: RequestOptions = {}): Promise<T
|
|||||||
}
|
}
|
||||||
const response = await fetch(`/api${path}`, { ...options, headers });
|
const response = await fetch(`/api${path}`, { ...options, headers });
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(await response.text());
|
throw new Error(await readErrorMessage(response));
|
||||||
}
|
}
|
||||||
return response.json() as Promise<T>;
|
return response.json() as Promise<T>;
|
||||||
}
|
}
|
||||||
@@ -36,7 +53,7 @@ async function requestBlob(path: string, options: RequestOptions = {}): Promise<
|
|||||||
}
|
}
|
||||||
const response = await fetch(`/api${path}`, { ...options, headers });
|
const response = await fetch(`/api${path}`, { ...options, headers });
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(await response.text());
|
throw new Error(await readErrorMessage(response));
|
||||||
}
|
}
|
||||||
return response.blob();
|
return response.blob();
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-1
@@ -8,6 +8,19 @@ type LoginPageProps = {
|
|||||||
portal: Portal;
|
portal: Portal;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function loginErrorMessage(err: unknown) {
|
||||||
|
const message = err instanceof Error ? err.message : '';
|
||||||
|
if (message.includes('Invalid login or password')) return '用户名或密码错误';
|
||||||
|
if (message.includes('login and password are required')) return '请输入用户名和密码';
|
||||||
|
if (message.includes('Captcha expired')) return '验证码已过期,请刷新后重试';
|
||||||
|
if (message.includes('Captcha is incorrect')) return '验证码错误,请重新输入';
|
||||||
|
if (message.includes('User is locked')) return '账号已锁定,请 24 小时后再试';
|
||||||
|
if (message.includes('User is disabled or deleted')) return '账号已停用或已删除';
|
||||||
|
if (message.includes('Only platform admins can login to admin portal')) return '该账号不是运营端管理员';
|
||||||
|
if (message.includes('Only enterprise admins linked to a tenant can login to client portal')) return '该账号不是已绑定企业的客户端管理员';
|
||||||
|
return message || '登录失败,请检查账号信息后重试';
|
||||||
|
}
|
||||||
|
|
||||||
export function LoginPage({ portal }: LoginPageProps) {
|
export function LoginPage({ portal }: LoginPageProps) {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [login, setLogin] = useState('');
|
const [login, setLogin] = useState('');
|
||||||
@@ -42,7 +55,7 @@ export function LoginPage({ portal }: LoginPageProps) {
|
|||||||
writeSession(session);
|
writeSession(session);
|
||||||
navigate(isAdmin ? '/admin' : '/client', { replace: true });
|
navigate(isAdmin ? '/admin' : '/client', { replace: true });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err instanceof Error ? err.message : '登录失败');
|
setError(loginErrorMessage(err));
|
||||||
await refreshCaptcha();
|
await refreshCaptcha();
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
|||||||
@@ -6,6 +6,17 @@ import '@/styles/tokens.css';
|
|||||||
import '@/styles/global.css';
|
import '@/styles/global.css';
|
||||||
import '@/styles/components.css';
|
import '@/styles/components.css';
|
||||||
|
|
||||||
|
function normalizeInitialHashRoute() {
|
||||||
|
if (window.location.hash) return;
|
||||||
|
|
||||||
|
const path = window.location.pathname;
|
||||||
|
if (path === '/admin' || path.startsWith('/admin/') || path === '/client' || path.startsWith('/client/')) {
|
||||||
|
window.history.replaceState(null, '', `/#${path}${window.location.search}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
normalizeInitialHashRoute();
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<HashRouter>
|
<HashRouter>
|
||||||
|
|||||||
Reference in New Issue
Block a user