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}>;
+};