From c92e3992188769d32477a76aadf42fd10f9acbe4 Mon Sep 17 00:00:00 2001 From: zhi Date: Wed, 11 Mar 2026 10:09:33 +0000 Subject: [PATCH] fix: check wizard config for initialized flag instead of backend health - App checks wizard API for harborforge.json config with initialized=true - If not initialized, show embedded setup wizard (talks to wizard API via CORS) - Setup saves config with initialized:true to wizard config volume - After restart, backend reads config and starts, frontend sees initialized=true - Remove VITE_API_BASE build arg (not needed, api.ts uses /api relative path) - Fix Object.entries null crash in DashboardPage --- Dockerfile | 2 -- src/App.tsx | 30 ++++++++++++++++++------------ src/pages/DashboardPage.tsx | 4 ++-- src/pages/SetupWizardPage.tsx | 28 ++++++++++------------------ 4 files changed, 30 insertions(+), 34 deletions(-) diff --git a/Dockerfile b/Dockerfile index 55964ba..6fed7ee 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,9 +4,7 @@ WORKDIR /app COPY package.json package-lock.json* ./ RUN npm install COPY . . -ARG VITE_API_BASE=/api ARG VITE_WIZARD_PORT=18080 -ENV VITE_API_BASE=$VITE_API_BASE ENV VITE_WIZARD_PORT=$VITE_WIZARD_PORT RUN npm run build diff --git a/src/App.tsx b/src/App.tsx index f3674ed..8d7c061 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -13,9 +13,10 @@ import ProjectDetailPage from '@/pages/ProjectDetailPage' import MilestonesPage from '@/pages/MilestonesPage' import MilestoneDetailPage from '@/pages/MilestoneDetailPage' import NotificationsPage from '@/pages/NotificationsPage' -import api from '@/services/api' +import axios from 'axios' const WIZARD_PORT = Number(import.meta.env.VITE_WIZARD_PORT) || 18080 +const WIZARD_BASE = `http://127.0.0.1:${WIZARD_PORT}` type AppState = 'checking' | 'setup' | 'ready' @@ -23,31 +24,36 @@ export default function App() { const [appState, setAppState] = useState('checking') const { user, loading, login, logout } = useAuth() - const checkBackend = async () => { + useEffect(() => { + checkInitialized() + }, []) + + const checkInitialized = async () => { try { - await api.get('/health') - setAppState('ready') + const res = await axios.get(`${WIZARD_BASE}/api/v1/config/harborforge.json`, { + timeout: 5000, + }) + if (res.data && res.data.initialized === true) { + setAppState('ready') + } else { + setAppState('setup') + } } catch { + // Wizard unreachable or config doesn't exist → setup needed setAppState('setup') } } - useEffect(() => { checkBackend() }, []) - - // Checking backend availability if (appState === 'checking') { - return
检查后端状态...
+ 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}
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) { +export default function SetupWizardPage({ wizardBase, onComplete }: Props) { const [step, setStep] = useState(0) const [error, setError] = useState('') const [saving, setSaving] = useState(false) @@ -42,13 +37,12 @@ export default function SetupWizardPage({ wizardPort, onComplete }: Props) { 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}`, + baseURL: wizardBase, timeout: 5000, }) @@ -63,7 +57,7 @@ export default function SetupWizardPage({ wizardPort, onComplete }: Props) { setStep(1) } catch { setWizardOk(false) - setError(`无法连接 AbstractWizard (127.0.0.1:${wizardPort})。请确认已通过 SSH 隧道映射端口:\nssh -L ${wizardPort}:127.0.0.1:${wizardPort} user@server`) + setError(`无法连接 AbstractWizard (${wizardBase})。\n请确认已通过 SSH 隧道映射端口:\nssh -L :127.0.0.1: user@server`) } } @@ -72,6 +66,7 @@ export default function SetupWizardPage({ wizardPort, onComplete }: Props) { setSaving(true) try { const config = { + initialized: true, admin: { username: form.admin_username, password: form.admin_password, @@ -85,7 +80,6 @@ export default function SetupWizardPage({ wizardPort, onComplete }: Props) { 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, @@ -95,9 +89,6 @@ export default function SetupWizardPage({ wizardPort, onComplete }: Props) { 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}`) @@ -129,7 +120,7 @@ export default function SetupWizardPage({ wizardPort, onComplete }: Props) {

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

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

- ssh -L {wizardPort}:127.0.0.1:{wizardPort} user@your-server + ssh -L <wizard_port>:127.0.0.1:<wizard_port> user@your-server