|
@@ -1,16 +1,16 @@
|
|
|
"use client";
|
|
"use client";
|
|
|
|
|
|
|
|
import { message } from "antd";
|
|
import { message } from "antd";
|
|
|
|
|
+import { sha256 } from "js-sha256";
|
|
|
import { useRouter } from "next/navigation";
|
|
import { useRouter } from "next/navigation";
|
|
|
import type React from "react";
|
|
import type React from "react";
|
|
|
import { createContext, useContext, useEffect, useState } from "react";
|
|
import { createContext, useContext, useEffect, useState } from "react";
|
|
|
-
|
|
|
|
|
-interface User {
|
|
|
|
|
- id: string;
|
|
|
|
|
- username: string;
|
|
|
|
|
- email: string;
|
|
|
|
|
- role: string;
|
|
|
|
|
-}
|
|
|
|
|
|
|
+import {
|
|
|
|
|
+ getUserInfo,
|
|
|
|
|
+ login as loginApi,
|
|
|
|
|
+ logout as logoutApi,
|
|
|
|
|
+} from "@/services/auth";
|
|
|
|
|
+import type { User } from "@/types/api";
|
|
|
|
|
|
|
|
interface AuthContextType {
|
|
interface AuthContextType {
|
|
|
user: User | null;
|
|
user: User | null;
|
|
@@ -22,6 +22,41 @@ interface AuthContextType {
|
|
|
|
|
|
|
|
const AuthContext = createContext<AuthContextType | undefined>(undefined);
|
|
const AuthContext = createContext<AuthContextType | undefined>(undefined);
|
|
|
|
|
|
|
|
|
|
+// In-memory token cache to avoid repeated localStorage reads
|
|
|
|
|
+let tokenCache: string | null = null;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * Get token from memory cache
|
|
|
|
|
+ * This is much faster than reading from localStorage on every request
|
|
|
|
|
+ */
|
|
|
|
|
+export function getToken(): string | null {
|
|
|
|
|
+ return tokenCache;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * Set token in both memory cache and localStorage
|
|
|
|
|
+ */
|
|
|
|
|
+export function setToken(token: string | null): void {
|
|
|
|
|
+ tokenCache = token;
|
|
|
|
|
+ if (typeof window !== "undefined") {
|
|
|
|
|
+ if (token) {
|
|
|
|
|
+ localStorage.setItem("token", token);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ localStorage.removeItem("token");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * Clear token from both memory cache and localStorage
|
|
|
|
|
+ */
|
|
|
|
|
+export function clearToken(): void {
|
|
|
|
|
+ tokenCache = null;
|
|
|
|
|
+ if (typeof window !== "undefined") {
|
|
|
|
|
+ localStorage.removeItem("token");
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({
|
|
export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({
|
|
|
children,
|
|
children,
|
|
|
}) => {
|
|
}) => {
|
|
@@ -32,15 +67,17 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({
|
|
|
// Check if user is logged in on mount
|
|
// Check if user is logged in on mount
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
|
const storedUser = localStorage.getItem("user");
|
|
const storedUser = localStorage.getItem("user");
|
|
|
- const token = localStorage.getItem("token");
|
|
|
|
|
|
|
+ const storedToken = localStorage.getItem("token");
|
|
|
|
|
|
|
|
- if (storedUser && token) {
|
|
|
|
|
|
|
+ if (storedUser && storedToken) {
|
|
|
try {
|
|
try {
|
|
|
setUser(JSON.parse(storedUser));
|
|
setUser(JSON.parse(storedUser));
|
|
|
|
|
+ // Initialize token cache from localStorage on mount (only read once)
|
|
|
|
|
+ setToken(storedToken);
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
console.error("Failed to parse user data:", error);
|
|
console.error("Failed to parse user data:", error);
|
|
|
localStorage.removeItem("user");
|
|
localStorage.removeItem("user");
|
|
|
- localStorage.removeItem("token");
|
|
|
|
|
|
|
+ clearToken();
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
setIsLoading(false);
|
|
setIsLoading(false);
|
|
@@ -51,45 +88,72 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({
|
|
|
password: string,
|
|
password: string,
|
|
|
): Promise<boolean> => {
|
|
): Promise<boolean> => {
|
|
|
try {
|
|
try {
|
|
|
- // TODO: Replace with actual API call
|
|
|
|
|
- // Simulating API call with setTimeout
|
|
|
|
|
- await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
|
|
|
-
|
|
|
|
|
- // Mock authentication logic
|
|
|
|
|
- if (username === "admin" && password === "admin123") {
|
|
|
|
|
- const mockUser: User = {
|
|
|
|
|
- id: "1",
|
|
|
|
|
- username: "admin",
|
|
|
|
|
- email: "admin@lanu.com",
|
|
|
|
|
- role: "admin",
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- const mockToken = "mock-jwt-token-" + Date.now();
|
|
|
|
|
-
|
|
|
|
|
- setUser(mockUser);
|
|
|
|
|
- localStorage.setItem("user", JSON.stringify(mockUser));
|
|
|
|
|
- localStorage.setItem("token", mockToken);
|
|
|
|
|
-
|
|
|
|
|
- message.success("登录成功!");
|
|
|
|
|
- router.push("/home");
|
|
|
|
|
- return true;
|
|
|
|
|
- } else {
|
|
|
|
|
- message.error("用户名或密码错误!");
|
|
|
|
|
|
|
+ // Call actual login API
|
|
|
|
|
+ const loginResponse = await loginApi({
|
|
|
|
|
+ username,
|
|
|
|
|
+ password: sha256(password),
|
|
|
|
|
+ imageCode: "",
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ const { token, useMfa, mfaAuthToken } = loginResponse;
|
|
|
|
|
+
|
|
|
|
|
+ // Store token in both memory cache and localStorage
|
|
|
|
|
+ setToken(token);
|
|
|
|
|
+
|
|
|
|
|
+ // If MFA is required, handle MFA flow
|
|
|
|
|
+ if (useMfa) {
|
|
|
|
|
+ // TODO: Implement MFA flow
|
|
|
|
|
+ localStorage.setItem("mfaAuthToken", mfaAuthToken);
|
|
|
|
|
+ message.warning("需要进行 MFA 认证");
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // Get user info after successful login
|
|
|
|
|
+ const userInfo = await getUserInfo();
|
|
|
|
|
+ const userData: User = {
|
|
|
|
|
+ uid: userInfo.uid,
|
|
|
|
|
+ username: userInfo.username,
|
|
|
|
|
+ nickname: userInfo.nickname,
|
|
|
|
|
+ avatar: userInfo.avatar,
|
|
|
|
|
+ superAdmin: userInfo.superAdmin,
|
|
|
|
|
+ useMfa: userInfo.useMfa,
|
|
|
|
|
+ locationInfo: userInfo.locationInfo,
|
|
|
|
|
+ ip: userInfo.ip,
|
|
|
|
|
+ permissions: userInfo.permissions,
|
|
|
|
|
+ token: userInfo.token,
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // Store user info
|
|
|
|
|
+ setUser(userData);
|
|
|
|
|
+ localStorage.setItem("user", JSON.stringify(userData));
|
|
|
|
|
+
|
|
|
|
|
+ message.success("登录成功!");
|
|
|
|
|
+ router.push("/home");
|
|
|
|
|
+ return true;
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
console.error("Login error:", error);
|
|
console.error("Login error:", error);
|
|
|
- message.error("登录失败,请稍后再试!");
|
|
|
|
|
|
|
+ const errorMessage =
|
|
|
|
|
+ error instanceof Error ? error.message : "登录失败,请稍后再试!";
|
|
|
|
|
+ message.error(errorMessage);
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- const logout = () => {
|
|
|
|
|
- setUser(null);
|
|
|
|
|
- localStorage.removeItem("user");
|
|
|
|
|
- localStorage.removeItem("token");
|
|
|
|
|
- message.success("已退出登录");
|
|
|
|
|
- router.push("/login");
|
|
|
|
|
|
|
+ const logout = async () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ // Call logout API
|
|
|
|
|
+ await logoutApi();
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error("Logout error:", error);
|
|
|
|
|
+ // Continue with local logout even if API call fails
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ // Clear local state
|
|
|
|
|
+ setUser(null);
|
|
|
|
|
+ localStorage.removeItem("user");
|
|
|
|
|
+ clearToken();
|
|
|
|
|
+ message.success("已退出登录");
|
|
|
|
|
+ router.push("/login");
|
|
|
|
|
+ }
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const value: AuthContextType = {
|
|
const value: AuthContextType = {
|