From 8779db53707e6973a81f4f2f400d8c1d44e530a7 Mon Sep 17 00:00:00 2001 From: zhi Date: Wed, 11 Mar 2026 09:51:41 +0000 Subject: [PATCH] fix: replace embedded wizard with redirect to wizard port - Frontend no longer makes cross-origin AJAX calls to wizard - checkBackend validates response is actual JSON (not serve SPA fallback) - When backend is down, show simple redirect page to wizard URL - After wizard config + service restart, frontend detects backend and shows login - Remove SetupWizardPage (embedded wizard), add SetupRedirectPage - Fix Object.entries null crash in DashboardPage --- src/App.tsx | 24 ++-- src/pages/DashboardPage.tsx | 4 +- src/pages/SetupRedirectPage.tsx | 44 +++++++ src/pages/SetupWizardPage.tsx | 222 -------------------------------- 4 files changed, 60 insertions(+), 234 deletions(-) create mode 100644 src/pages/SetupRedirectPage.tsx delete mode 100644 src/pages/SetupWizardPage.tsx diff --git a/src/App.tsx b/src/App.tsx index f3674ed..a07d7e0 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3,7 +3,7 @@ import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom' import { useAuth } from '@/hooks/useAuth' import Sidebar from '@/components/Sidebar' import LoginPage from '@/pages/LoginPage' -import SetupWizardPage from '@/pages/SetupWizardPage' +import SetupRedirectPage from '@/pages/SetupRedirectPage' import DashboardPage from '@/pages/DashboardPage' import IssuesPage from '@/pages/IssuesPage' import IssueDetailPage from '@/pages/IssueDetailPage' @@ -23,31 +23,35 @@ export default function App() { const [appState, setAppState] = useState('checking') const { user, loading, login, logout } = useAuth() + useEffect(() => { + checkBackend() + }, []) + const checkBackend = async () => { try { - await api.get('/health') - setAppState('ready') + const res = await api.get('/health') + // Verify the response is actually from the backend (JSON with status field), + // not serve's SPA fallback returning index.html as 200 + if (res.data && typeof res.data === 'object' && res.data.status === 'healthy') { + setAppState('ready') + } else { + setAppState('setup') + } } catch { setAppState('setup') } } - useEffect(() => { checkBackend() }, []) - - // Checking backend availability if (appState === 'checking') { return
检查后端状态...
} - // Backend not ready — show setup wizard if (appState === 'setup') { - return + return } - // Backend ready but auth loading if (loading) return
加载中...
- // Not logged in if (!user) { return ( diff --git a/src/pages/DashboardPage.tsx b/src/pages/DashboardPage.tsx index 072ae19..4de833f 100644 --- a/src/pages/DashboardPage.tsx +++ b/src/pages/DashboardPage.tsx @@ -28,7 +28,7 @@ export default function DashboardPage() { {stats.total_issues} 总 Issues - {Object.entries(stats.by_status).map(([k, v]) => ( + {Object.entries(stats.by_status || {}).map(([k, v]) => (
{v} {k} @@ -39,7 +39,7 @@ export default function DashboardPage() {

按优先级

- {Object.entries(stats.by_priority).map(([k, v]) => ( + {Object.entries(stats.by_priority || {}).map(([k, v]) => (
{k}
+
+
+

⚓ HarborForge 初始化

+
+ +
+

后端尚未配置

+

请先通过 AbstractWizard 完成初始化配置。

+ +
+

⚠️ 需要通过 SSH 隧道访问 Wizard:

+ ssh -L {wizardPort}:127.0.0.1:{wizardPort} user@your-server +
+ + + 打开 AbstractWizard → + + +
+

配置完成后:

+
    +
  1. 在服务器上重启服务:docker compose restart
  2. +
  3. 等待后端启动完成
  4. +
  5. 刷新本页面进入登录界面
  6. +
+
+
+
+
+ ) +} diff --git a/src/pages/SetupWizardPage.tsx b/src/pages/SetupWizardPage.tsx deleted file mode 100644 index bffd16c..0000000 --- a/src/pages/SetupWizardPage.tsx +++ /dev/null @@ -1,222 +0,0 @@ -import { useState } from 'react' -import axios from 'axios' - -interface Props { - wizardPort: number - onComplete: () => void -} - -interface SetupForm { - // Admin - admin_username: string - admin_password: string - admin_email: string - admin_full_name: string - // Database - db_host: string - db_port: number - db_user: string - db_password: string - db_database: string - // Backend - backend_url: string - // Default project - project_name: string - project_description: string -} - -const STEPS = ['欢迎', '数据库', '管理员', '项目', '完成'] - -export default function SetupWizardPage({ wizardPort, onComplete }: Props) { - const [step, setStep] = useState(0) - const [error, setError] = useState('') - const [saving, setSaving] = useState(false) - const [wizardOk, setWizardOk] = useState(null) - const [form, setForm] = useState({ - admin_username: 'admin', - admin_password: '', - admin_email: '', - admin_full_name: 'Admin', - db_host: 'mysql', - db_port: 3306, - db_user: 'harborforge', - db_password: 'harborforge_pass', - db_database: 'harborforge', - backend_url: '', - project_name: 'Default', - project_description: '默认项目', - }) - - const wizardApi = axios.create({ - baseURL: `http://127.0.0.1:${wizardPort}`, - timeout: 5000, - }) - - const set = (key: keyof SetupForm, value: string | number) => - setForm((f) => ({ ...f, [key]: value })) - - const checkWizard = async () => { - setError('') - try { - await wizardApi.get('/health') - setWizardOk(true) - setStep(1) - } catch { - setWizardOk(false) - setError(`无法连接 AbstractWizard (127.0.0.1:${wizardPort})。请确认已通过 SSH 隧道映射端口:\nssh -L ${wizardPort}:127.0.0.1:${wizardPort} user@server`) - } - } - - const saveConfig = async () => { - setError('') - setSaving(true) - try { - const config = { - admin: { - username: form.admin_username, - password: form.admin_password, - email: form.admin_email, - full_name: form.admin_full_name, - }, - database: { - host: form.db_host, - port: form.db_port, - user: form.db_user, - password: form.db_password, - database: form.db_database, - }, - backend_url: form.backend_url || undefined, - default_project: form.project_name - ? { name: form.project_name, description: form.project_description } - : undefined, - } - - await wizardApi.put('/api/v1/config/harborforge.json', config, { - headers: { 'Content-Type': 'application/json' }, - }) - - // Switch wizard to readonly after setup - await wizardApi.put('/api/v1/mode', { mode: 'readonly' }).catch(() => {}) - - setStep(4) - } catch (err: any) { - setError(`保存配置失败: ${err.message}`) - } finally { - setSaving(false) - } - } - - return ( -
-
-
-

⚓ HarborForge 初始化向导

-
- {STEPS.map((s, i) => ( - - {i < step ? '✓' : i + 1}. {s} - - ))} -
-
- - {error &&
{error}
} - - {/* Step 0: Welcome */} - {step === 0 && ( -
-

欢迎使用 HarborForge

-

Agent/人类协同任务管理平台

-
-

⚠️ 初始化向导通过 SSH 隧道连接 AbstractWizard,请确保已映射端口:

- ssh -L {wizardPort}:127.0.0.1:{wizardPort} user@your-server -
- - {wizardOk === false && ( -

连接失败,请检查 SSH 隧道是否正确建立。

- )} -
- )} - - {/* Step 1: Database */} - {step === 1 && ( -
-

数据库配置

-

配置 MySQL 连接信息(使用 docker-compose 内置 MySQL 可保持默认值)

-
- - - - - -
-
- - -
-
- )} - - {/* Step 2: Admin */} - {step === 2 && ( -
-

管理员账号

-

创建首个管理员用户

-
- - - - -
-
- - -
-
- )} - - {/* Step 3: Project */} - {step === 3 && ( -
-

默认项目(可选)

-

创建一个初始项目,也可以跳过

-
- - -
-
- - -
-
- )} - - {/* Step 4: Done */} - {step === 4 && ( -
-
-

✅ 初始化完成!

-

配置已保存到 AbstractWizard,Wizard 已切换为只读模式。

-

后端将在检测到配置后自动启动。

-
-

请等待后端启动完成,然后刷新页面进入登录界面。

-

管理员账号: {form.admin_username}

-
- -
-
- )} -
-
- ) -}