diff --git a/backend/src/main/java/com/hitcommunications/servermanager/config/security/SecurityConfig.java b/backend/src/main/java/com/hitcommunications/servermanager/config/security/SecurityConfig.java index 6051a32..eddb0d5 100644 --- a/backend/src/main/java/com/hitcommunications/servermanager/config/security/SecurityConfig.java +++ b/backend/src/main/java/com/hitcommunications/servermanager/config/security/SecurityConfig.java @@ -12,6 +12,7 @@ import org.springframework.security.config.annotation.authentication.configurati import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; @@ -44,7 +45,7 @@ public class SecurityConfig { ) .authenticationProvider(authenticationProvider()) .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class) - .httpBasic(Customizer.withDefaults()); + .httpBasic(AbstractHttpConfigurer::disable); return http.build(); } diff --git a/frontend/src/Api.ts b/frontend/src/Api.ts index 89c661d..e126c59 100644 --- a/frontend/src/Api.ts +++ b/frontend/src/Api.ts @@ -116,4 +116,18 @@ api.interceptors.request.use(async (config) => { return config; }); +api.interceptors.response.use( + (response) => response, + (error) => { + if (error?.response?.status === 401) { + setAuthToken(undefined); + const currentPath = window.location.pathname; + if (currentPath !== "/login") { + window.location.href = "/login"; + } + } + return Promise.reject(error); + } +); + export default api; diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 55b0690..3d73571 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -2,13 +2,18 @@ import { BrowserRouter, Route, Routes } from 'react-router-dom'; import './App.css'; import { Login } from './pages/Login'; import { Dashboard } from './pages/Dashboard'; +import { ProtectedRoute } from './routes/ProtectedRoute'; function App() { return ( - } /> + + + + } /> } /> diff --git a/frontend/src/components/header/BulkImportModal.tsx b/frontend/src/components/header/BulkImportModal.tsx index c68a15b..0d38844 100644 --- a/frontend/src/components/header/BulkImportModal.tsx +++ b/frontend/src/components/header/BulkImportModal.tsx @@ -134,8 +134,8 @@ const Stat = ({ ); const Styles = { - modalOverlay: "fixed inset-0 z-40 flex items-center justify-center bg-black/40 px-4", - modal: "w-full max-w-2xl rounded-2xl border border-cardBorder bg-card p-6 shadow-xl", + modalOverlay: "fixed inset-0 z-40 flex items-center justify-center bg-black/40 backdrop-blur-sm px-4 !mt-0", + modal: "w-full max-w-2xl rounded-2xl border border-cardBorder bg-card p-6 shadow-xl transform transition-all duration-200 animate-fade-up", modalHeader: "flex items-start justify-between gap-4 pb-4 border-b border-cardBorder", modalTitle: "text-lg font-semibold text-text", closeButton: "text-2xl leading-none text-text-secondary hover:text-text", diff --git a/frontend/src/components/header/HeaderActions.tsx b/frontend/src/components/header/HeaderActions.tsx index 885de5e..1a97af8 100644 --- a/frontend/src/components/header/HeaderActions.tsx +++ b/frontend/src/components/header/HeaderActions.tsx @@ -1,4 +1,4 @@ -interface HeaderActionsProps { +export interface HeaderActionsProps { isMenuOpen: boolean; onToggleMenu: () => void; onAddServer: () => void; diff --git a/frontend/src/components/header/ProfileModal.tsx b/frontend/src/components/header/ProfileModal.tsx index e4e54f7..3c5922a 100644 --- a/frontend/src/components/header/ProfileModal.tsx +++ b/frontend/src/components/header/ProfileModal.tsx @@ -73,8 +73,8 @@ export const ProfileModal = ({ }; const Styles = { - modalOverlay: "fixed inset-0 z-40 flex items-center justify-center bg-black/40 px-4", - modal: "w-full max-w-2xl rounded-2xl border border-cardBorder bg-card p-6 shadow-xl", + modalOverlay: "fixed inset-0 z-40 flex items-center justify-center bg-black/40 backdrop-blur-sm px-4 !mt-0", + modal: "w-full max-w-2xl rounded-2xl border border-cardBorder bg-card p-6 shadow-xl transform transition-all duration-200 animate-fade-up", modalHeader: "flex items-start justify-between gap-4 pb-4 border-b border-cardBorder", modalTitle: "text-lg font-semibold text-text", closeButton: "text-2xl leading-none text-text-secondary hover:text-text", diff --git a/frontend/src/components/header/ServerModal.tsx b/frontend/src/components/header/ServerModal.tsx index e189297..c863d31 100644 --- a/frontend/src/components/header/ServerModal.tsx +++ b/frontend/src/components/header/ServerModal.tsx @@ -100,8 +100,8 @@ export const ServerModal = ({ }; const Styles = { - modalOverlay: "fixed inset-0 z-40 flex items-center justify-center bg-black/40 px-4", - modal: "w-full max-w-2xl rounded-2xl border border-cardBorder bg-card p-6 shadow-xl", + modalOverlay: "fixed inset-0 z-40 flex items-center justify-center bg-black/40 backdrop-blur-sm px-4 !mt-0", + modal: "w-full max-w-2xl rounded-2xl border border-cardBorder bg-card p-6 shadow-xl transform transition-all duration-200 animate-fade-up", modalHeader: "flex items-start justify-between gap-4 pb-4 border-b border-cardBorder", modalTitle: "text-lg font-semibold text-text", closeButton: "text-2xl leading-none text-text-secondary hover:text-text", diff --git a/frontend/src/routes/ProtectedRoute.tsx b/frontend/src/routes/ProtectedRoute.tsx new file mode 100644 index 0000000..9ec1a97 --- /dev/null +++ b/frontend/src/routes/ProtectedRoute.tsx @@ -0,0 +1,18 @@ +import type { ReactNode } from "react"; +import { Navigate, useLocation } from "react-router-dom"; +import { getAccessToken } from "../Api"; + +interface ProtectedRouteProps { + children: ReactNode; +} + +export const ProtectedRoute = ({ children }: ProtectedRouteProps) => { + const token = getAccessToken(); + const location = useLocation(); + + if (!token) { + return ; + } + + return <>{children}; +};