diff --git a/docker-compose.e2e.yml b/docker-compose.e2e.yml
index ec7f41b..e98a983 100644
--- a/docker-compose.e2e.yml
+++ b/docker-compose.e2e.yml
@@ -50,7 +50,7 @@ services:
ports:
- "3334:3000"
healthcheck:
- test: ["CMD", "curl", "-f", "http://localhost:3000/"]
+ test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
interval: 5s
timeout: 3s
retries: 10
@@ -58,7 +58,7 @@ services:
ports:
- "3334:3000"
healthcheck:
- test: ["CMD", "curl", "-f", "http://localhost:3000/"]
+ test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
interval: 5s
timeout: 3s
retries: 10
diff --git a/docker-compose.yml b/docker-compose.yml
index ebebc6e..0fc889a 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -19,7 +19,7 @@ services:
restart: unless-stopped
healthcheck:
- test: ["CMD", "curl", "-f", "http://localhost:3000/"]
+ test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
interval: 30s
timeout: 5s
retries: 3
diff --git a/frontend/index.html b/frontend/index.html
index e4b78ea..728704d 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -2,9 +2,9 @@
-
+
- Vite + React + TS
+ Gitea AI Assistant
diff --git a/frontend/public/favicon.svg b/frontend/public/favicon.svg
new file mode 100644
index 0000000..54c3b87
--- /dev/null
+++ b/frontend/public/favicon.svg
@@ -0,0 +1 @@
+
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 5c3715e..a3e54ea 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -6,6 +6,7 @@ import { RepositoryManager } from './components/RepositoryManager';
import { ConfigManager } from './components/ConfigManager';
import { ReviewConfigPage } from './components/ReviewConfigPage';
import { Toaster } from "@/components/ui/sonner"
+import { useTheme } from 'next-themes'
function AuthGuard({ children }: { children: React.ReactNode }) {
const { isAuthenticated, isLoading } = useAuth();
@@ -32,7 +33,9 @@ function AuthGuard({ children }: { children: React.ReactNode }) {
return <>{children}>;
}
-function App() {
+function AppContent() {
+ const { resolvedTheme } = useTheme();
+
return (
@@ -51,9 +54,13 @@ function App() {
} />
-
+
);
}
+function App() {
+ return ;
+}
+
export default App;
diff --git a/frontend/src/index.css b/frontend/src/index.css
index d858db0..1236f3f 100644
--- a/frontend/src/index.css
+++ b/frontend/src/index.css
@@ -72,11 +72,11 @@
}
.bg-grid-pattern {
background-size: 40px 40px;
- background-image: linear-gradient(to right, rgba(255, 255, 255, 0.05) 1px, transparent 1px),
- linear-gradient(to bottom, rgba(255, 255, 255, 0.05) 1px, transparent 1px);
+ background-image: linear-gradient(to right, hsl(var(--foreground) / 0.05) 1px, transparent 1px),
+ linear-gradient(to bottom, hsl(var(--foreground) / 0.05) 1px, transparent 1px);
}
.glass-panel {
- @apply bg-zinc-950/50 backdrop-blur-xl border border-white/10 shadow-2xl;
+ @apply bg-card/80 backdrop-blur-xl border border-border shadow-2xl;
}
.tech-glow {
box-shadow: 0 0 20px -5px hsl(var(--primary) / 0.5);
diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts
index 4b5549a..43c91a9 100644
--- a/frontend/src/lib/api.ts
+++ b/frontend/src/lib/api.ts
@@ -18,4 +18,20 @@ api.interceptors.request.use(
}
);
+// 添加响应拦截器,处理 401 未授权自动跳转登录页
+api.interceptors.response.use(
+ (response) => response,
+ (error) => {
+ if (axios.isAxiosError(error) && error.response?.status === 401) {
+ localStorage.removeItem('authToken');
+ // 避免在登录接口本身触发跳转
+ const isLoginRequest = error.config?.url?.includes('/login');
+ if (!isLoginRequest) {
+ window.location.href = '/';
+ }
+ }
+ return Promise.reject(error);
+ }
+);
+
export default api;
diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx
index 51afcdd..3d8be74 100644
--- a/frontend/src/main.tsx
+++ b/frontend/src/main.tsx
@@ -4,6 +4,7 @@ import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import axios from 'axios'
+import { ThemeProvider } from 'next-themes'
const queryClient = new QueryClient({
defaultOptions: {
@@ -21,13 +22,13 @@ const queryClient = new QueryClient({
},
});
-// Force dark mode as requested
-document.documentElement.classList.add('dark');
ReactDOM.createRoot(document.getElementById('root')!).render(
-
-
-
+
+
+
+
+
,
)
diff --git a/frontend/src/pages/DashboardPage.tsx b/frontend/src/pages/DashboardPage.tsx
index 9223012..41c30d6 100644
--- a/frontend/src/pages/DashboardPage.tsx
+++ b/frontend/src/pages/DashboardPage.tsx
@@ -1,7 +1,8 @@
import { useState, useEffect } from 'react';
import { NavLink, Outlet, useLocation } from 'react-router-dom';
import { Button } from '@/components/ui/button';
-import { LogOut, Bot, FolderGit2, Sliders, Menu, X, PanelLeftClose, PanelLeftOpen, FileSearch } from 'lucide-react';
+import { LogOut, Bot, FolderGit2, Sliders, Menu, X, PanelLeftClose, PanelLeftOpen, FileSearch, Sun, Moon } from 'lucide-react';
+import { useTheme } from 'next-themes';
const navItems = [
{ path: '/repos', label: '仓库管理', icon: FolderGit2 },
@@ -11,6 +12,7 @@ const navItems = [
export default function DashboardPage() {
const location = useLocation();
+ const { setTheme, resolvedTheme } = useTheme();
const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(false);
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
@@ -33,24 +35,24 @@ export default function DashboardPage() {
{/* Mobile Overlay */}
{isMobileMenuOpen && (
setIsMobileMenuOpen(false)}
/>
)}
{/* Sidebar */}