diff --git a/frontend/app/src/App.tsx b/frontend/app/src/App.tsx index d51d5dd..12a9cd2 100644 --- a/frontend/app/src/App.tsx +++ b/frontend/app/src/App.tsx @@ -13,6 +13,7 @@ const App = () => { const [messageApi, contextHolder] = message.useMessage({ duration: 2, }); + return ( diff --git a/frontend/app/src/components/AuthModal.tsx b/frontend/app/src/components/AuthModal.tsx index dae0153..f66b94f 100644 --- a/frontend/app/src/components/AuthModal.tsx +++ b/frontend/app/src/components/AuthModal.tsx @@ -1,9 +1,14 @@ import { Form, Input, Menu, MenuProps, Modal, Spin } from "antd"; import "./styles.css"; import { KeyOutlined, UserAddOutlined } from "@ant-design/icons"; -import React, { useContext, useState } from "react"; -import { useLoginMutation, useRegisterMutation } from "../slice/AuthApi"; +import React, { useContext, useEffect, useState } from "react"; +import { + useGetUserQuery, + useLoginMutation, + useRegisterMutation, +} from "../slice/AuthApi"; import { MessageContext } from "../App"; +import { store, updateToken, updateUser } from "../config/store"; const AuthModal = (props: { open: boolean; @@ -15,6 +20,13 @@ const AuthModal = (props: { const [current, setCurrent] = useState("login"); const messageApi = useContext(MessageContext); + const { data, isFetching, isError } = useGetUserQuery({}); + useEffect(() => { + if (!isFetching && !isError) { + store.dispatch(updateUser(data)); + } + }, [data]); + const [loginUser, { isLoading: isLoggingIn }] = useLoginMutation(); const [registerUser, { isLoading: isRegistering }] = useRegisterMutation(); @@ -25,6 +37,9 @@ const AuthModal = (props: { loginUser(formData) .unwrap() + .then((data) => { + store.dispatch(updateToken(data.access_token)); + }) .then(() => props.setOpen(false)) .catch(() => messageApi.error("Login failed!")); }; diff --git a/frontend/app/src/components/HeaderComponent.tsx b/frontend/app/src/components/HeaderComponent.tsx index 097c44e..675668a 100644 --- a/frontend/app/src/components/HeaderComponent.tsx +++ b/frontend/app/src/components/HeaderComponent.tsx @@ -1,17 +1,23 @@ import { UserOutlined } from "@ant-design/icons"; import { Layout, Menu, MenuProps } from "antd"; -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import AuthModal from "./AuthModal"; import "./styles.css"; +import { getLocalToken, store, updateUser } from "../config/store"; +import { useGetUserQuery } from "../slice/AuthApi"; const { Header } = Layout; const HeaderComponent = () => { const [authModalOpen, setAuthModalOpen] = useState(false); + store.dispatch(getLocalToken()); + + const user = store.getState().auth.user; + console.log(user); const items: MenuProps["items"] = [ { - label: "Log In", + label: user ? user.username : "Log In", key: "login", icon: , onClick: () => setAuthModalOpen(true), diff --git a/frontend/app/src/config/baseUrl.ts b/frontend/app/src/config/baseUrl.ts index 331e71a..3f77342 100644 --- a/frontend/app/src/config/baseUrl.ts +++ b/frontend/app/src/config/baseUrl.ts @@ -1 +1 @@ -export const baseUrl = "http://localhost/api/" \ No newline at end of file +export const baseUrl = "http://localhost/api"; diff --git a/frontend/app/src/config/store.ts b/frontend/app/src/config/store.ts index 64f4f46..b579579 100644 --- a/frontend/app/src/config/store.ts +++ b/frontend/app/src/config/store.ts @@ -1,13 +1,35 @@ import { configureStore, createAction, createReducer } from "@reduxjs/toolkit"; import { setupListeners } from "@reduxjs/toolkit/query"; -import { AuthApi } from "../slice/AuthApi"; +import { AuthApi, User } from "../slice/AuthApi"; export type authState = { token: string | null; + user: { name: string | null; username: string } | null; }; const initialAuthState: authState = { token: null, + user: null, +}; + +export const updateToken = createAction("auth/updateToken"); +export const getLocalToken = createAction("auth/getLocalToken"); +export const updateUser = createAction("auth/updateUser"); + +const parseJwt = (token: string): { sub: string; exp: number } => { + const base64Url = token.split(".")[1]; + const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/"); + const jsonPayload = decodeURIComponent( + window + .atob(base64) + .split("") + .map(function (c) { + return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2); + }) + .join("") + ); + + return JSON.parse(jsonPayload); }; export const store = configureStore({ @@ -15,12 +37,19 @@ export const store = configureStore({ // Add the generated reducer as a specific top-level slice [AuthApi.reducerPath]: AuthApi.reducer, auth: createReducer(initialAuthState, (builder) => { - builder.addCase(createAction("auth/token"), (state, _) => { + builder.addCase(updateToken, (state, action) => { + state.token = action.payload; + localStorage.setItem("token", action.payload); + }); + builder.addCase(getLocalToken, (state) => { const token: string | null = localStorage.getItem("token"); if (token) { state.token = token; } }); + builder.addCase(updateUser, (state, action) => { + state.user = action.payload; + }); }), }, // Adding the api middleware enables caching, invalidation, polling, diff --git a/frontend/app/src/slice/AuthApi.ts b/frontend/app/src/slice/AuthApi.ts index 9c76edc..b85c436 100644 --- a/frontend/app/src/slice/AuthApi.ts +++ b/frontend/app/src/slice/AuthApi.ts @@ -19,10 +19,15 @@ export interface RegisterRequest { password2: string; } +type TokenResponse = { + access_token: string; + token_type: string; +}; + export const AuthApi = createApi({ reducerPath: "AuthApi", baseQuery: fetchBaseQuery({ - baseUrl: `${baseUrl}auth/`, + baseUrl: `${baseUrl}/auth`, prepareHeaders: (headers, { getState }) => { // By default, if we have a token in the store, let's use that for authenticated requests const token = (getState() as RootState).auth.token; @@ -34,11 +39,11 @@ export const AuthApi = createApi({ }), endpoints: (builder) => ({ getUser: builder.query({ - query: () => "me", + query: () => "/me", }), login: builder.mutation({ query: (data: FormData) => ({ - url: "token", + url: "/token", method: "POST", body: data, formData: true, @@ -46,7 +51,7 @@ export const AuthApi = createApi({ }), register: builder.mutation({ query: (data: RegisterRequest) => ({ - url: "register", + url: "/register", method: "POST", body: data, }),