Skip to content

Commit 4051084

Browse files
committed
feat: enhance password management and improve logout functionality
- Implemented a direct API call for password updates in the ChangePassword component, with fallback to the existing service method on failure. - Added error handling and user feedback for password update failures. - Updated the logout function to ensure the authorization token is included in the API request. - Enhanced the dashboard layout to dynamically generate deployment URLs based on environment variables, improving clarity and usability.
1 parent dd94b82 commit 4051084

File tree

3 files changed

+93
-44
lines changed

3 files changed

+93
-44
lines changed

admin/src/components/UserSettings/ChangePassword.tsx

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import { useMutation } from "@tanstack/react-query"
33
import { type SubmitHandler, useForm } from "react-hook-form"
44
import { FiLock } from "react-icons/fi"
55

6-
import { type ApiError, type UpdatePassword, UsersService } from "@/client"
6+
import { type ApiError, type UpdatePassword, UsersService, OpenAPI } from "@/client"
7+
import { request } from "@/client/core/request"
78
import useCustomToast from "@/hooks/useCustomToast"
89
import { confirmPasswordRules, handleError, passwordRules } from "@/utils"
910
import { PasswordInput } from "../ui/password-input"
@@ -12,8 +13,26 @@ interface UpdatePasswordForm extends UpdatePassword {
1213
confirm_password: string
1314
}
1415

16+
// 直接调用MCP API更新密码
17+
const updatePasswordAPI = async (data: UpdatePassword): Promise<void> => {
18+
const token = localStorage.getItem("access_token")
19+
if (!token) {
20+
throw new Error("No access token found")
21+
}
22+
23+
return request(OpenAPI, {
24+
method: "POST",
25+
url: "/api/v1/users/me/password",
26+
headers: {
27+
Authorization: `Bearer ${token}`,
28+
"Content-Type": "application/json",
29+
},
30+
body: JSON.stringify(data),
31+
})
32+
}
33+
1534
const ChangePassword = () => {
16-
const { showSuccessToast } = useCustomToast()
35+
const { showSuccessToast, showErrorToast } = useCustomToast()
1736
const {
1837
register,
1938
handleSubmit,
@@ -26,14 +45,23 @@ const ChangePassword = () => {
2645
})
2746

2847
const mutation = useMutation({
29-
mutationFn: (data: UpdatePassword) =>
30-
UsersService.updatePasswordMe({ requestBody: data }),
48+
mutationFn: (data: UpdatePassword) => {
49+
// 尝试直接使用定制的API调用
50+
try {
51+
return updatePasswordAPI(data)
52+
} catch (error) {
53+
console.error("直接API调用失败,回退到服务方法", error)
54+
// 回退到默认服务方法
55+
return UsersService.updatePasswordMe({ requestBody: data })
56+
}
57+
},
3158
onSuccess: () => {
3259
showSuccessToast("Password updated successfully.")
3360
reset()
3461
},
3562
onError: (err: ApiError) => {
3663
handleError(err)
64+
showErrorToast("Password update failed", err.message || "Please try again")
3765
},
3866
})
3967

admin/src/hooks/useAuth.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,19 @@ const isLoggedIn = () => {
1818
return localStorage.getItem("access_token") !== null
1919
}
2020

21-
// 自定义logout API调用
21+
// 自定义logout API调用,确保包含授权令牌
2222
const callLogoutAPI = async (): Promise<void> => {
23+
const token = localStorage.getItem("access_token")
24+
if (!token) {
25+
throw new Error("No access token found")
26+
}
27+
2328
return request(OpenAPI, {
2429
method: "POST",
2530
url: "/api/v1/logout",
31+
headers: {
32+
Authorization: `Bearer ${token}`
33+
},
2634
})
2735
}
2836

@@ -70,8 +78,9 @@ const useAuth = () => {
7078

7179
const logout = async () => {
7280
try {
73-
// 尝试调用后端的logout接口
81+
// 尝试调用后端的logout接口,确保请求完成
7482
await callLogoutAPI()
83+
console.log("成功调用登出API")
7584
} catch (error) {
7685
console.error("登出API调用失败:", error)
7786
} finally {

admin/src/routes/_layout/index.tsx

Lines changed: 50 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Box, Container, Text, Grid, Heading, Link, Flex, Icon } from "@chakra-ui/react"
1+
import { Box, Container, Text, Grid, Heading, Link, Flex, Icon, Badge } from "@chakra-ui/react"
22
import { createFileRoute } from "@tanstack/react-router"
33
import { FiDatabase, FiServer, FiMonitor, FiCpu, FiExternalLink } from "react-icons/fi"
44
import { useColorModeValue } from "@/components/ui/color-mode"
@@ -14,16 +14,18 @@ function Dashboard() {
1414
const cardBg = useColorModeValue("white", "gray.800")
1515
const borderColor = useColorModeValue("gray.200", "gray.700")
1616
const statBg = useColorModeValue("blue.50", "blue.900")
17+
const domain = import.meta.env.VITE_DOMAIN || 'localhost.nip.io'
18+
const protocol = import.meta.env.VITE_USE_HTTPS === 'false' ? 'http' : 'https'
1719

18-
// Deployment information from environment
20+
// Deployment information from environment - updated format
1921
const deploymentTools = [
20-
{ name: "Admin Dashboard", url: `https://admin.${import.meta.env.VITE_DOMAIN || 'localhost.nip.io'}`, description: "Admin management interface" },
21-
{ name: "User Dashboard", url: `https://dashboard.${import.meta.env.VITE_DOMAIN || 'localhost.nip.io'}`, description: "User-facing dashboard" },
22-
{ name: "API Backend", url: `https://api.${import.meta.env.VITE_DOMAIN || 'localhost.nip.io'}`, description: "Backend API server" },
23-
{ name: "Adminer", url: `https://adminer.${import.meta.env.VITE_DOMAIN || 'localhost.nip.io'}`, description: "Database management" },
24-
{ name: "PG Admin", url: `https://pgadmin.${import.meta.env.VITE_DOMAIN || 'localhost.nip.io'}`, description: "PostgreSQL administration" },
25-
{ name: "LiteLLM", url: `https://llm.${import.meta.env.VITE_DOMAIN || 'localhost.nip.io'}`, description: "LLM proxy service" },
26-
{ name: "Prometheus", url: `https://prometheus.${import.meta.env.VITE_DOMAIN || 'localhost.nip.io'}`, description: "Monitoring and metrics" },
22+
{ name: "Admin Dashboard", subdomain: "admin", description: "Admin management interface" },
23+
{ name: "User Dashboard", subdomain: "dashboard", description: "User-facing dashboard" },
24+
{ name: "API Backend", subdomain: "api", description: "Backend API server" },
25+
{ name: "Adminer", subdomain: "adminer", description: "Database management" },
26+
{ name: "PG Admin", subdomain: "pgadmin", description: "PostgreSQL administration" },
27+
{ name: "LiteLLM", subdomain: "llm", description: "LLM proxy service" },
28+
{ name: "Prometheus", subdomain: "prometheus", description: "Monitoring and metrics" },
2729
]
2830

2931
// System information
@@ -75,38 +77,48 @@ function Dashboard() {
7577

7678
<Box h="1px" bg={borderColor} my={6} />
7779

78-
<Heading size="md" mb={4}>Project Deployment Resources</Heading>
80+
<Heading size="md" mb={4}>
81+
Project Deployment Resources
82+
<Badge ml={2} colorScheme={domain === 'localhost.nip.io' ? 'yellow' : 'green'}>
83+
{domain === 'localhost.nip.io' ? 'Local' : 'Production'}
84+
</Badge>
85+
</Heading>
7986

8087
<Grid templateColumns={{ base: "repeat(1, 1fr)", md: "repeat(2, 1fr)", lg: "repeat(3, 1fr)" }} gap={4}>
81-
{deploymentTools.map((tool) => (
82-
<Box
83-
key={tool.name}
84-
p={4}
85-
borderWidth="1px"
86-
borderRadius="lg"
87-
borderColor={borderColor}
88-
bg={cardBg}
89-
boxShadow="sm"
90-
transition="all 0.2s"
91-
_hover={{ transform: "translateY(-2px)", boxShadow: "md" }}
92-
>
93-
<Heading size="sm" mb={2}>{tool.name}</Heading>
94-
<Text fontSize="sm" color="gray.500" mb={3}>{tool.description}</Text>
95-
<Link
96-
href={tool.url}
97-
target="_blank"
98-
rel="noopener noreferrer"
99-
color="blue.500"
100-
fontSize="sm"
101-
display="flex"
102-
alignItems="center"
103-
_hover={{ textDecoration: "underline", color: "blue.600" }}
88+
{deploymentTools.map((tool) => {
89+
const url = `${protocol}://${tool.subdomain}.${domain}`;
90+
return (
91+
<Box
92+
key={tool.name}
93+
p={4}
94+
borderWidth="1px"
95+
borderRadius="lg"
96+
borderColor={borderColor}
97+
bg={cardBg}
98+
boxShadow="sm"
99+
transition="all 0.2s"
100+
_hover={{ transform: "translateY(-2px)", boxShadow: "md" }}
104101
>
105-
{tool.url.replace(/^https?:\/\//, '')}
106-
<Icon as={FiExternalLink} ml={1} />
107-
</Link>
108-
</Box>
109-
))}
102+
<Heading size="sm" mb={2}>{tool.name}</Heading>
103+
<Text fontSize="sm" color="gray.500" mb={3}>{tool.description}</Text>
104+
<Box title={url}>
105+
<Link
106+
href={url}
107+
target="_blank"
108+
rel="noopener noreferrer"
109+
color="blue.500"
110+
fontSize="sm"
111+
display="flex"
112+
alignItems="center"
113+
_hover={{ textDecoration: "underline", color: "blue.600" }}
114+
>
115+
{tool.subdomain}.{domain}
116+
<Icon as={FiExternalLink} ml={1} />
117+
</Link>
118+
</Box>
119+
</Box>
120+
);
121+
})}
110122
</Grid>
111123
</Box>
112124
</Container>

0 commit comments

Comments
 (0)