fix(web): 切换账号后权限缓存不刷新问题
- 登录/注册成功时先清除旧的权限缓存 - 确保切换账号后重新加载新账号的权限数据 feat(web): 系统状态接入真实 health API - 新增 health.service.ts 调用后端健康检查接口 - 新增 useHealth hook,每 30 秒自动刷新 - DashboardStats 显示真实健康状态(正常/部分异常/异常) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import type { OverallHealthStatus } from '@seclusion/shared';
|
||||
import { Users, UserCheck, UserPlus, Activity, RefreshCw } from 'lucide-react';
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
@@ -10,6 +11,7 @@ import {
|
||||
CardTitle,
|
||||
} from '@/components/ui/card';
|
||||
import { Skeleton } from '@/components/ui/skeleton';
|
||||
import { useHealthCheck } from '@/hooks/useHealth';
|
||||
import { useUsers, useDeletedUsers } from '@/hooks/useUsers';
|
||||
|
||||
interface StatCardProps {
|
||||
@@ -52,6 +54,12 @@ function StatCard({
|
||||
);
|
||||
}
|
||||
|
||||
const healthStatusMap: Record<OverallHealthStatus, { label: string; description: string }> = {
|
||||
ok: { label: '正常', description: '所有服务运行正常' },
|
||||
degraded: { label: '部分异常', description: '部分服务运行异常' },
|
||||
error: { label: '异常', description: '服务运行异常' },
|
||||
};
|
||||
|
||||
export function DashboardStats() {
|
||||
const {
|
||||
data: usersData,
|
||||
@@ -65,13 +73,24 @@ export function DashboardStats() {
|
||||
refetch: refetchDeleted,
|
||||
} = useDeletedUsers({ pageSize: 1 });
|
||||
|
||||
const isLoading = isLoadingUsers || isLoadingDeleted;
|
||||
const {
|
||||
data: healthData,
|
||||
isLoading: isLoadingHealth,
|
||||
refetch: refetchHealth,
|
||||
} = useHealthCheck();
|
||||
|
||||
const isLoading = isLoadingUsers || isLoadingDeleted || isLoadingHealth;
|
||||
|
||||
const handleRefresh = () => {
|
||||
refetchUsers();
|
||||
refetchDeleted();
|
||||
refetchHealth();
|
||||
};
|
||||
|
||||
const healthStatus = healthData?.status
|
||||
? healthStatusMap[healthData.status]
|
||||
: { label: '-', description: '加载中...' };
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
@@ -114,10 +133,10 @@ export function DashboardStats() {
|
||||
|
||||
<StatCard
|
||||
title="系统状态"
|
||||
value="正常"
|
||||
description="所有服务运行正常"
|
||||
value={healthStatus.label}
|
||||
description={healthStatus.description}
|
||||
icon={<Activity className="h-4 w-4" />}
|
||||
isLoading={false}
|
||||
isLoading={isLoadingHealth}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -20,11 +20,12 @@ import {
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { loginSchema, type LoginFormValues } from '@/lib/validations';
|
||||
import { authService } from '@/services/auth.service';
|
||||
import { useAuthStore } from '@/stores';
|
||||
import { useAuthStore, usePermissionStore } from '@/stores';
|
||||
|
||||
|
||||
export function LoginForm() {
|
||||
const { setAuth } = useAuthStore();
|
||||
const clearPermissionData = usePermissionStore((state) => state.clearPermissionData);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const captchaRef = useRef<CaptchaRef>(null);
|
||||
|
||||
@@ -48,6 +49,8 @@ export function LoginForm() {
|
||||
captchaCode: values.captchaCode,
|
||||
});
|
||||
|
||||
// 清除旧的权限缓存,确保切换账号后重新加载权限
|
||||
clearPermissionData();
|
||||
setAuth(
|
||||
response.accessToken,
|
||||
response.refreshToken,
|
||||
|
||||
@@ -21,12 +21,13 @@ import {
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { registerSchema, type RegisterFormValues } from '@/lib/validations';
|
||||
import { authService } from '@/services/auth.service';
|
||||
import { useAuthStore } from '@/stores';
|
||||
import { useAuthStore, usePermissionStore } from '@/stores';
|
||||
|
||||
|
||||
export function RegisterForm() {
|
||||
const router = useRouter();
|
||||
const { setAuth } = useAuthStore();
|
||||
const clearPermissionData = usePermissionStore((state) => state.clearPermissionData);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const form = useForm<RegisterFormValues>({
|
||||
@@ -52,6 +53,8 @@ export function RegisterForm() {
|
||||
captchaCode: values.captchaCode,
|
||||
});
|
||||
|
||||
// 清除旧的权限缓存,确保切换账号后重新加载权限
|
||||
clearPermissionData();
|
||||
setAuth(
|
||||
response.accessToken,
|
||||
response.refreshToken,
|
||||
|
||||
@@ -10,6 +10,7 @@ export const API_ENDPOINTS = {
|
||||
SEND_RESET_EMAIL: '/auth/send-reset-email',
|
||||
RESET_PASSWORD: '/auth/reset-password',
|
||||
},
|
||||
HEALTH: '/health',
|
||||
USERS: '/users',
|
||||
FILES: '/files',
|
||||
CAPTCHA: '/captcha',
|
||||
|
||||
20
apps/web/src/hooks/useHealth.ts
Normal file
20
apps/web/src/hooks/useHealth.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { healthService } from '@/services/health.service';
|
||||
import { useIsAuthenticated } from '@/stores';
|
||||
|
||||
export const healthKeys = {
|
||||
all: ['health'] as const,
|
||||
check: () => [...healthKeys.all, 'check'] as const,
|
||||
};
|
||||
|
||||
export function useHealthCheck() {
|
||||
const isAuthenticated = useIsAuthenticated();
|
||||
|
||||
return useQuery({
|
||||
queryKey: healthKeys.check(),
|
||||
queryFn: () => healthService.check(),
|
||||
enabled: isAuthenticated,
|
||||
refetchInterval: 30000, // 每 30 秒自动刷新
|
||||
});
|
||||
}
|
||||
11
apps/web/src/services/health.service.ts
Normal file
11
apps/web/src/services/health.service.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import type { HealthCheckResponse } from '@seclusion/shared';
|
||||
|
||||
import { API_ENDPOINTS } from '@/config/constants';
|
||||
import { http } from '@/lib/http';
|
||||
|
||||
export const healthService = {
|
||||
// 健康检查
|
||||
check: (): Promise<HealthCheckResponse> => {
|
||||
return http.get<HealthCheckResponse>(API_ENDPOINTS.HEALTH);
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user