diff --git a/.env b/.env
index 5bf9e95..a4fb8f9 100644
--- a/.env
+++ b/.env
@@ -1 +1,2 @@
-VITE_SV_HOST="http://localhost:3000"
\ No newline at end of file
+VITE_SV_HOST="http://localhost:3000"
+VITE_JWT_TOKEN="phuocntbasdasasdasd"
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index d671642..eeb2682 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -13,6 +13,7 @@
"@tailwindcss/vite": "^4.1.13",
"antd": "^5.27.4",
"axios": "^1.12.2",
+ "jose": "^6.1.0",
"json-server": "^1.0.0-beta.3",
"react": "^19.1.1",
"react-dom": "^19.1.1",
@@ -4157,6 +4158,15 @@
"jiti": "lib/jiti-cli.mjs"
}
},
+ "node_modules/jose": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.0.tgz",
+ "integrity": "sha512-TTQJyoEoKcC1lscpVDCSsVgYzUDg/0Bt3WE//WiTPK6uOCQC2KZS4MpugbMWt/zyjkopgZoXhZuCi00gLudfUA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/panva"
+ }
+ },
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
diff --git a/package.json b/package.json
index b5aed0a..ff8cdfd 100644
--- a/package.json
+++ b/package.json
@@ -16,6 +16,7 @@
"@tailwindcss/vite": "^4.1.13",
"antd": "^5.27.4",
"axios": "^1.12.2",
+ "jose": "^6.1.0",
"json-server": "^1.0.0-beta.3",
"react": "^19.1.1",
"react-dom": "^19.1.1",
diff --git a/src/App.tsx b/src/App.tsx
index a0b0e53..e9a6ca8 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,9 +1,18 @@
import { createContext } from 'react'
import RouterSetup from './RouterSetup'
+import { useSelector } from 'react-redux'
+import type { StoreType } from './stores'
+import Loading from './components/Loading'
export default function App() {
-
+ const userStore = useSelector((store: StoreType) => store.user)
+
return (
-
+ <>
+ {
+ userStore.loading ?
+ :
+ }
+ >
)
}
diff --git a/src/apis/core/user.api.ts b/src/apis/core/user.api.ts
index 69d9567..e2cb097 100644
--- a/src/apis/core/user.api.ts
+++ b/src/apis/core/user.api.ts
@@ -2,6 +2,7 @@ import axios from "axios"
import type { User } from "../../types/user.type"
import { message } from "antd"
import { ApiUtil } from "../../utils/api.util"
+import * as jose from 'jose'
export interface UserSignInDTO {
emailOrUserName: string
@@ -16,10 +17,7 @@ export interface UserFindAllDTO {
}
export const UserApi = {
- signIn: async (data: UserSignInDTO): Promise<{
- message: string
- data: any
- }> => {
+ signIn: async (data: UserSignInDTO) => {
let userData = await axios.get(`${import.meta.env.VITE_SV_HOST}/users?email=${data.emailOrUserName}`)
if (userData.data.length == 0) {
userData = await axios.get(`${import.meta.env.VITE_SV_HOST}/users?userName=${data.emailOrUserName}`)
@@ -36,10 +34,7 @@ export const UserApi = {
data: null
})
}
- return {
- message: "Đăng nhập thành công!",
- data: userData.data[0] as User
- }
+ return createToken(userData.data[0].id)
}
},
signUp: async (data: User) => {
@@ -106,5 +101,59 @@ export const UserApi = {
findAll: async (query?: UserFindAllDTO) => {
let result = await axios.get(`${import.meta.env.VITE_SV_HOST}/users?` + ApiUtil.writeQuery(query))
return result.data
+ },
+ me: async (token: string) => {
+ let tokenData = await decodeToken(token)
+
+ if (!tokenData) {
+ throw ({
+ message: "Token không chính xác!"
+ })
+ }
+
+ let { userId } = tokenData;
+
+ let getUserByIdRes = await axios.get(`${import.meta.env.VITE_SV_HOST}/users/${userId}`)
+
+ if (!getUserByIdRes.data) {
+ throw ({
+ message: "Lỗi lấy dữ liệu"
+ })
+ }
+
+ let data = await new Promise((resolve) => {
+ setTimeout(() => {
+ resolve(getUserByIdRes.data)
+ }, 1000)
+ })
+
+ return data
+ }
+}
+
+
+async function createToken(userId: string) {
+ const secret = new TextEncoder().encode(import.meta.env.VITE_JWT_TOKEN);
+
+ const token = await new jose.SignJWT({ userId })
+ .setProtectedHeader({ alg: 'HS256' })
+ .setIssuedAt()
+ .setExpirationTime('2h')
+ .sign(secret);
+ return token
+}
+
+async function decodeToken(token: string) {
+ try {
+ const secret = new TextEncoder().encode(import.meta.env.VITE_JWT_TOKEN);
+
+ const { payload } = await jose.jwtVerify(token, secret, {
+ algorithms: ['HS256'],
+ });
+
+ return payload;
+ } catch (error) {
+ console.error('Token không hợp lệ hoặc đã hết hạn:', error);
+ return null;
}
}
\ No newline at end of file
diff --git a/src/components/Loading.tsx b/src/components/Loading.tsx
new file mode 100644
index 0000000..a913a7a
--- /dev/null
+++ b/src/components/Loading.tsx
@@ -0,0 +1,22 @@
+import React from 'react'
+
+export default function Loading() {
+ return (
+
+
+
Đang tải dữ liệu xác thực...
+
+ )
+}
diff --git a/src/pages/admin/auth/ProtectedAdmin.tsx b/src/pages/admin/auth/ProtectedAdmin.tsx
index d4543ff..38cda95 100644
--- a/src/pages/admin/auth/ProtectedAdmin.tsx
+++ b/src/pages/admin/auth/ProtectedAdmin.tsx
@@ -14,7 +14,7 @@ export default function ProtectedAdmin(
return store.user
})
-
+ /* Đã đăng nhập và là master hoặc admin */
if (userStore.data?.role == UserRole.MASTER || userStore.data?.role == UserRole.ADMIN) {
return (
<>
@@ -23,7 +23,8 @@ export default function ProtectedAdmin(
)
}
- if (!userStore.data?.role) {
+ /* chưa đăng nhập */
+ if (!userStore.data && !userStore.loading) {
return (
@@ -90,11 +91,16 @@ export default function ProtectedAdmin(
)
}
+ /* đã đăng nhập nhưng không phải master hoặc admin */
if (userStore.data?.role) {
- window.location.href="/"
+ window.location.href = "/"
return <>>
}
+
+ /* chưa vào case, default */
+
+
async function signInHandle(e: FormEvent) {
e.preventDefault()
let data: UserSignInDTO = {
@@ -103,10 +109,9 @@ export default function ProtectedAdmin(
}
try {
let result = await Apis.user.signIn(data)
- localStorage.setItem("userLogin", JSON.stringify(result.data))
+ localStorage.setItem("token", result)
Modal.confirm({
title: "Đăng nhập thành công",
- content: result.message,
onOk: () => {
window.location.reload()
},
diff --git a/src/pages/admin/components/Header.tsx b/src/pages/admin/components/Header.tsx
index 23ac9d5..6ca4e6a 100644
--- a/src/pages/admin/components/Header.tsx
+++ b/src/pages/admin/components/Header.tsx
@@ -28,7 +28,7 @@ export default function HeaderCom({ collapsed, setCollapsed }: { collapsed: bool
diff --git a/src/pages/home/auth/Auth.tsx b/src/pages/home/auth/Auth.tsx
index d23eec0..d7c5c4c 100644
--- a/src/pages/home/auth/Auth.tsx
+++ b/src/pages/home/auth/Auth.tsx
@@ -49,9 +49,9 @@ export default function Auth() {
console.log("loginData", loginData)
try {
let data = await Apis.user.signIn(loginData)
- localStorage.setItem("userLogin", data.data.id)
+ localStorage.setItem("token", data)
Modal.confirm({
- title: `Chào mừng ${data.data.userName} đã quay trở lại`,
+ title: `Chào mừng bạn đã quay trở lại`,
content: ``,
onOk: () => {
window.location.href = "/"
diff --git a/src/pages/home/components/Header/Header.tsx b/src/pages/home/components/Header/Header.tsx
index ebde218..8234030 100644
--- a/src/pages/home/components/Header/Header.tsx
+++ b/src/pages/home/components/Header/Header.tsx
@@ -70,7 +70,7 @@ export default function Header() {
window.location.href = "/admin"
}} className="fa-solid fa-lock">}
{
- window.localStorage.removeItem("userLogin")
+ window.localStorage.removeItem("token")
window.location.href = "/auth"
}} className="cursor-pointer fa-solid fa-right-from-bracket">
diff --git a/src/stores/slices/user.slice.ts b/src/stores/slices/user.slice.ts
index b8ebcfc..ad5992d 100644
--- a/src/stores/slices/user.slice.ts
+++ b/src/stores/slices/user.slice.ts
@@ -21,8 +21,16 @@ const userSlice = createSlice({
},
extraReducers: (bd) => {
+ bd.addCase(fetchUserData.pending, (state, action) => {
+ state.loading = true
+ })
bd.addCase(fetchUserData.fulfilled, (state, action) => {
- state.data = action.payload
+ console.log("đã vào full", action.payload)
+ state.loading = false
+ state.data = action.payload
+ })
+ bd.addCase(fetchUserData.rejected, (state, action) => {
+ state.loading = false
})
}
})
@@ -31,8 +39,8 @@ const userSlice = createSlice({
const fetchUserData = createAsyncThunk(
"user/fetchUserData",
async () => {
- let result = await Apis.user.findById(localStorage.getItem("userLogin"))
- return result.data
+ let result = await Apis.user.me(localStorage.getItem("token")) as any
+ return result
}
)