From 643225520346a40fdd5b229c5f573452465db808 Mon Sep 17 00:00:00 2001 From: hzhang Date: Wed, 15 Apr 2026 07:14:36 +0100 Subject: [PATCH] quick fix --- .env | 1 - src/App.tsx | 42 ++++++------ src/pages/SetupWizardPage.tsx | 121 +++++++++++++++------------------- src/vite-env.d.ts | 1 - 4 files changed, 76 insertions(+), 89 deletions(-) diff --git a/.env b/.env index c789158..b7f9167 100644 --- a/.env +++ b/.env @@ -1,2 +1 @@ VITE_API_BASE=http://backend:8000 -VITE_WIZARD_PORT=8080 diff --git a/src/App.tsx b/src/App.tsx index 335f93e..7b5b05f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -22,8 +22,10 @@ import SupportDetailPage from '@/pages/SupportDetailPage' import MeetingDetailPage from '@/pages/MeetingDetailPage' 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}` +const getStoredWizardPort = (): number | null => { + const stored = Number(localStorage.getItem('HF_WIZARD_PORT')) + return stored && stored > 0 ? stored : null +} const getApiBase = () => { return localStorage.getItem('HF_BACKEND_BASE_URL') ?? undefined @@ -55,24 +57,26 @@ export default function App() { // Backend unreachable — fall through to wizard check } - // Fallback: try the wizard directly (needed during initial setup before backend starts) - try { - const res = await axios.get(`${WIZARD_BASE}/api/v1/config/harborforge.json`, { - timeout: 5000, - }) - const cfg = res.data || {} - if (cfg.backend_url) { - localStorage.setItem('HF_BACKEND_BASE_URL', cfg.backend_url) + // Fallback: if a wizard port was previously saved during setup, try it directly + const storedPort = getStoredWizardPort() + if (storedPort) { + try { + const res = await axios.get(`http://127.0.0.1:${storedPort}/api/v1/config/harborforge.json`, { + timeout: 5000, + }) + const cfg = res.data || {} + if (cfg.backend_url) { + localStorage.setItem('HF_BACKEND_BASE_URL', cfg.backend_url) + } + if (cfg.initialized === true) { + setAppState('ready') + return + } + } catch { + // ignore — fall through to setup } - if (cfg.initialized === true) { - setAppState('ready') - } else { - setAppState('setup') - } - } catch { - // Neither backend nor wizard reachable → setup needed - setAppState('setup') } + setAppState('setup') } if (appState === 'checking') { @@ -80,7 +84,7 @@ export default function App() { } if (appState === 'setup') { - return + return } if (loading) return
Loading...
diff --git a/src/pages/SetupWizardPage.tsx b/src/pages/SetupWizardPage.tsx index 3e9bc47..1cf9542 100644 --- a/src/pages/SetupWizardPage.tsx +++ b/src/pages/SetupWizardPage.tsx @@ -2,7 +2,7 @@ import { useState } from 'react' import axios from 'axios' interface Props { - wizardBase: string + initialWizardPort: number | null onComplete: () => void } @@ -11,55 +11,53 @@ interface SetupForm { admin_password: string admin_email: string admin_full_name: string - db_host: string - db_port: number - db_user: string - db_password: string - db_database: string backend_base_url: string project_name: string project_description: string } -const STEPS = ['Welcome', 'Database', 'Admin', 'Backend', 'Finish'] +const STEPS = ['Wizard', 'Admin', 'Backend', 'Finish'] -export default function SetupWizardPage({ wizardBase, onComplete }: Props) { +export default function SetupWizardPage({ initialWizardPort, onComplete }: Props) { const [step, setStep] = useState(0) const [error, setError] = useState('') const [saving, setSaving] = useState(false) - const [wizardOk, setWizardOk] = useState(null) + const [connecting, setConnecting] = useState(false) + const [wizardPortInput, setWizardPortInput] = useState( + initialWizardPort ? String(initialWizardPort) : '' + ) + const [wizardBase, setWizardBase] = useState('') 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_base_url: 'http://backend:8000', + backend_base_url: '', project_name: '', project_description: '', }) - const wizardApi = axios.create({ - baseURL: wizardBase, - timeout: 5000, - }) - const set = (key: keyof SetupForm, value: string | number) => setForm((f) => ({ ...f, [key]: value })) const checkWizard = async () => { setError('') + const port = Number(wizardPortInput) + if (!port || port <= 0 || port > 65535) { + setError('Please enter a valid wizard port (1-65535).') + return + } + const base = `http://127.0.0.1:${port}` + setConnecting(true) try { - await wizardApi.get('/health') - setWizardOk(true) + await axios.get(`${base}/health`, { timeout: 5000 }) + setWizardBase(base) + localStorage.setItem('HF_WIZARD_PORT', String(port)) setStep(1) } catch { - setWizardOk(false) - setError(`Unable to connect to AbstractWizard (${wizardBase}).\nPlease ensure the SSH tunnel is configured:\nssh -L :127.0.0.1: user@server`) + setError(`Unable to connect to AbstractWizard at ${base}.\nMake sure the SSH tunnel is up:\nssh -L ${port}:127.0.0.1:${port} user@server`) + } finally { + setConnecting(false) } } @@ -75,25 +73,19 @@ export default function SetupWizardPage({ wizardBase, onComplete }: Props) { 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_base_url || undefined, } - await wizardApi.put('/api/v1/config/harborforge.json', config, { + await axios.put(`${wizardBase}/api/v1/config/harborforge.json`, config, { headers: { 'Content-Type': 'application/json' }, + timeout: 5000, }) if (form.backend_base_url) { localStorage.setItem('HF_BACKEND_BASE_URL', form.backend_base_url) } - setStep(4) + setStep(3) } catch (err: any) { setError(`Failed to save configuration: ${err.message}`) } finally { @@ -117,45 +109,38 @@ export default function SetupWizardPage({ wizardBase, onComplete }: Props) { {error &&
{error}
} - {/* Step 0: Welcome */} + {/* Step 0: Wizard connection */} {step === 0 && (
-

Welcome to HarborForge

-

Agent/Human collaborative task management platform

+

Connect to AbstractWizard

+

Enter the local port that forwards to AbstractWizard, then test the connection.

-

⚠️ The setup wizard connects to AbstractWizard via SSH tunnel. Ensure the port is forwarded:

+

⚠️ AbstractWizard is reached over an SSH tunnel. Forward the port first:

ssh -L <wizard_port>:127.0.0.1:<wizard_port> user@your-server
- - {wizardOk === false && ( -

Connection failed. Check the SSH tunnel.

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

Database configuration

-

Configure MySQL connection (docker-compose defaults are fine if using the bundled MySQL).

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

Admin account

Create the first admin user

@@ -166,26 +151,26 @@ export default function SetupWizardPage({ wizardBase, onComplete }: Props) {
- +
)} - {/* Step 3: Backend */} - {step === 3 && ( + {/* Step 2: Backend */} + {step === 2 && (

Backend URL

-

Configure the HarborForge backend API URL

+

Configure the HarborForge backend API URL (leave blank to use the frontend default).

- + @@ -193,8 +178,8 @@ export default function SetupWizardPage({ wizardBase, onComplete }: Props) {
)} - {/* Step 4: Done */} - {step === 4 && ( + {/* Step 3: Done */} + {step === 3 && (

✅ Setup complete!

diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index dd6f269..7019602 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -2,7 +2,6 @@ interface ImportMetaEnv { readonly VITE_API_BASE: string - readonly VITE_WIZARD_PORT: string } interface ImportMeta {