Harden wechat summary and login input

This commit is contained in:
john
2026-06-20 17:02:16 +08:00
parent 78907792db
commit dae3e2f350
3 changed files with 636 additions and 22 deletions
+95 -19
View File
@@ -1,5 +1,5 @@
import { useEffect, useState } from 'react';
import { Navigate, Route, Routes, useNavigate } from 'react-router-dom';
import { Navigate, Route, Routes, useLocation, useNavigate } from 'react-router-dom';
import { checkAuth, login, logout, setUnauthorizedHandler } from './api/client';
import { AdminLayout } from './admin/AdminLayout';
import { BillingPage } from './admin/pages/BillingPage';
@@ -10,9 +10,36 @@ import { ProvidersPage } from './admin/pages/ProvidersPage';
import { SkillsPage } from './admin/pages/SkillsPage';
import { UserDetailPage } from './admin/pages/UserDetailPage';
import { UsersPage } from './admin/pages/UsersPage';
import { WechatPage } from './admin/pages/WechatPage';
import { defaultHomePath } from './lib/routes';
import { OpsLayout } from './ops/components/OpsLayout';
import { AnalyticsPage } from './ops/pages/AnalyticsPage';
import { CreatorsPage } from './ops/pages/CreatorsPage';
import { FeaturedPage } from './ops/pages/FeaturedPage';
import { ReportsPage } from './ops/pages/ReportsPage';
import { ReviewPage } from './ops/pages/ReviewPage';
import type { PortalUser } from './types';
function AdminLoginPage({ onAuth }: { onAuth: (user: PortalUser) => void }) {
function LegacyAdminRedirect() {
const { pathname } = useLocation();
const rest = pathname.replace(/^\/admin\/?/, '');
return <Navigate to={rest ? `/${rest}` : '/'} replace />;
}
function RejectOpsAdminRedirect() {
const { pathname } = useLocation();
const rest = pathname.replace(/^\/ops\/admin\/?/, '');
return <Navigate to={rest ? `/${rest}` : '/'} replace />;
}
function AdminLoginPage({
onAuth,
redirectTo,
}: {
onAuth: (user: PortalUser) => void;
redirectTo?: string;
}) {
const navigate = useNavigate();
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState<string | null>(null);
@@ -24,11 +51,12 @@ function AdminLoginPage({ onAuth }: { onAuth: (user: PortalUser) => void }) {
setLoading(true);
try {
const user = await login(username, password);
if (!user || user.role !== 'admin') {
setError('无管理员权限');
if (!user) {
setError('登录失败');
return;
}
onAuth(user);
navigate(redirectTo ?? defaultHomePath(user.role), { replace: true });
} catch (err) {
setError(err instanceof Error ? err.message : '登录失败');
} finally {
@@ -39,7 +67,10 @@ function AdminLoginPage({ onAuth }: { onAuth: (user: PortalUser) => void }) {
return (
<div className="admin-login-page">
<div className="admin-login-card">
<h1 className="admin-login-title"></h1>
<h1 className="admin-login-title">TKMind </h1>
<p className="muted" style={{ marginTop: -8, marginBottom: 20 }}>
使
</p>
{error && <p className="banner banner-error">{error}</p>}
<form onSubmit={handleSubmit} className="admin-login-form">
<input
@@ -54,6 +85,7 @@ function AdminLoginPage({ onAuth }: { onAuth: (user: PortalUser) => void }) {
<input
className="admin-login-input"
type="password"
autoComplete="current-password"
placeholder="密码"
value={password}
onChange={(e) => setPassword(e.target.value)}
@@ -71,24 +103,67 @@ function AdminLoginPage({ onAuth }: { onAuth: (user: PortalUser) => void }) {
function AdminApp({ user, onLogout }: { user: PortalUser; onLogout: () => void }) {
return (
<Routes>
<Route path="/admin" element={<AdminLayout user={user} onLogout={onLogout} />}>
<Route index element={<DashboardPage />} />
<Route path="users" element={<UsersPage />} />
<Route path="users/:userId" element={<UserDetailPage />} />
<Route path="billing/*" element={<BillingPage />} />
<Route path="capabilities" element={<CapabilitiesPage />} />
<Route path="skills" element={<SkillsPage />} />
<Route path="policies" element={<PoliciesPage />} />
<Route path="providers" element={<ProvidersPage />} />
<Route path="*" element={<Navigate to="/admin" replace />} />
{user.role === 'admin' ? (
<Route path="/" element={<AdminLayout user={user} onLogout={onLogout} />}>
<Route index element={<DashboardPage />} />
<Route path="users" element={<UsersPage />} />
<Route path="users/:userId" element={<UserDetailPage />} />
<Route path="billing/*" element={<BillingPage />} />
<Route path="capabilities" element={<CapabilitiesPage />} />
<Route path="skills" element={<SkillsPage />} />
<Route path="policies" element={<PoliciesPage />} />
<Route path="providers" element={<ProvidersPage />} />
<Route path="wechat" element={<WechatPage />} />
<Route path="*" element={<Navigate to="/" replace />} />
</Route>
) : (
<Route path="/" element={<Navigate to="/ops" replace />} />
)}
<Route path="/ops" element={<OpsLayout user={user} onLogout={onLogout} />}>
<Route index element={<ReviewPage />} />
<Route path="reports" element={<ReportsPage />} />
<Route path="featured" element={<FeaturedPage />} />
<Route path="creators" element={<CreatorsPage />} />
<Route path="analytics" element={<AnalyticsPage />} />
</Route>
<Route path="*" element={<Navigate to="/admin" replace />} />
<Route path="/admin/*" element={<LegacyAdminRedirect />} />
<Route path="/ops/admin" element={<Navigate to="/" replace />} />
<Route path="/ops/admin/*" element={<RejectOpsAdminRedirect />} />
<Route path="*" element={<Navigate to={defaultHomePath(user.role)} replace />} />
</Routes>
);
}
function loginRedirectPath(pathname: string, role: string | undefined) {
if (pathname.startsWith('/ops/admin')) {
const rest = pathname.replace(/^\/ops\/admin\/?/, '');
return rest ? `/${rest}` : '/';
}
if (pathname.startsWith('/admin')) {
const rest = pathname.replace(/^\/admin\/?/, '');
if (role === undefined) return rest ? `/${rest}` : '/';
return role === 'admin' ? (rest ? `/${rest}` : '/') : '/ops';
}
if (pathname === '/' || pathname.startsWith('/ops')) {
return pathname;
}
if (
pathname.startsWith('/users')
|| pathname.startsWith('/billing')
|| pathname.startsWith('/capabilities')
|| pathname.startsWith('/skills')
|| pathname.startsWith('/policies')
|| pathname.startsWith('/providers')
|| pathname.startsWith('/wechat')
) {
return role === 'admin' || role === undefined ? pathname : '/ops';
}
return defaultHomePath(role);
}
export function App() {
const navigate = useNavigate();
const location = useLocation();
const [authed, setAuthed] = useState<boolean | null>(null);
const [user, setUser] = useState<PortalUser | null>(null);
@@ -99,9 +174,8 @@ export function App() {
navigate('/', { replace: true });
});
void checkAuth().then((status) => {
const isAdmin = status.authenticated && status.user?.role === 'admin';
setAuthed(isAdmin);
setUser(isAdmin ? (status.user ?? null) : null);
setAuthed(status.authenticated);
setUser(status.authenticated ? (status.user ?? null) : null);
});
}, [navigate]);
@@ -112,6 +186,7 @@ export function App() {
if (!authed || !user) {
return (
<AdminLoginPage
redirectTo={loginRedirectPath(location.pathname, undefined)}
onAuth={(nextUser) => {
setAuthed(true);
setUser(nextUser);
@@ -124,6 +199,7 @@ export function App() {
void logout().finally(() => {
setAuthed(false);
setUser(null);
navigate('/', { replace: true });
});
};