Skip to content

Commit c277737

Browse files
authored
Persist auth in the new manager experience (#2287)
1 parent aa28725 commit c277737

File tree

13 files changed

+310
-173
lines changed

13 files changed

+310
-173
lines changed

web/src/App.tsx

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
22
import { ConfigProvider } from "./contexts/ConfigContext";
33
import { WizardModeProvider } from "./contexts/WizardModeContext";
44
import { BrandingProvider } from "./contexts/BrandingContext";
5+
import { AuthProvider } from "./contexts/AuthContext";
56
import InstallWizard from "./components/wizard/InstallWizard";
67
import { QueryClientProvider } from "@tanstack/react-query";
78
import { getQueryClient } from "./query-client";
@@ -10,26 +11,28 @@ function App() {
1011
const queryClient = getQueryClient();
1112
return (
1213
<QueryClientProvider client={queryClient}>
13-
<ConfigProvider>
14-
<BrandingProvider>
15-
<div className="min-h-screen bg-gray-50 text-gray-900 font-sans">
16-
<BrowserRouter>
17-
<Routes>
18-
<Route
19-
path="/"
20-
element={
21-
<WizardModeProvider mode="install">
22-
<InstallWizard />
23-
</WizardModeProvider>
24-
}
25-
/>
14+
<AuthProvider>
15+
<ConfigProvider>
16+
<BrandingProvider>
17+
<div className="min-h-screen bg-gray-50 text-gray-900 font-sans">
18+
<BrowserRouter>
19+
<Routes>
20+
<Route
21+
path="/"
22+
element={
23+
<WizardModeProvider mode="install">
24+
<InstallWizard />
25+
</WizardModeProvider>
26+
}
27+
/>
2628

27-
<Route path="*" element={<Navigate to="/" replace />} />
28-
</Routes>
29-
</BrowserRouter>
30-
</div>
31-
</BrandingProvider>
32-
</ConfigProvider>
29+
<Route path="*" element={<Navigate to="/" replace />} />
30+
</Routes>
31+
</BrowserRouter>
32+
</div>
33+
</BrandingProvider>
34+
</ConfigProvider>
35+
</AuthProvider>
3336
</QueryClientProvider>
3437
);
3538
}

web/src/components/wizard/InstallationStep.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { useConfig } from "../../contexts/ConfigContext";
55
import { CheckCircle, ExternalLink, Loader2 } from "lucide-react";
66
import { useQuery, Query } from "@tanstack/react-query";
77
import { useWizardMode } from "../../contexts/WizardModeContext";
8+
import { useAuth } from "../../contexts/AuthContext";
89

910
interface InstallStatus {
1011
state: "Succeeded" | "Failed" | "InProgress";
@@ -18,6 +19,7 @@ const InstallationStep: React.FC = () => {
1819
const [showAdminLink, setShowAdminLink] = useState(false);
1920
const [error, setError] = useState<string | null>(null);
2021
const [isLoading, setIsLoading] = useState(true);
22+
const { token } = useAuth();
2123

2224
const { data: installStatus } = useQuery<InstallStatus, Error>({
2325
queryKey: ["installStatus"],
@@ -26,9 +28,7 @@ const InstallationStep: React.FC = () => {
2628
method: "GET",
2729
headers: {
2830
"Content-Type": "application/json",
29-
...(localStorage.getItem("auth") && {
30-
Authorization: `Bearer ${localStorage.getItem("auth")}`,
31-
}),
31+
Authorization: `Bearer ${token}`,
3232
},
3333
});
3434

web/src/components/wizard/SetupStep.tsx

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,36 @@ import { useWizardMode } from "../../contexts/WizardModeContext";
66
import { ChevronLeft, ChevronRight } from "lucide-react";
77
import LinuxSetup from "./setup/LinuxSetup";
88
import { useQuery, useMutation } from "@tanstack/react-query";
9+
import { useAuth } from "../../contexts/AuthContext";
910

1011
interface SetupStepProps {
1112
onNext: () => void;
1213
onBack: () => void;
1314
}
1415

16+
interface Status {
17+
state: string;
18+
description?: string;
19+
}
20+
21+
interface ConfigError extends Error {
22+
errors?: { field: string; message: string }[];
23+
}
24+
1525
const SetupStep: React.FC<SetupStepProps> = ({ onNext, onBack }) => {
1626
const { config, updateConfig, prototypeSettings } = useConfig();
1727
const { text } = useWizardMode();
1828
const [showAdvanced, setShowAdvanced] = useState(true);
1929
const [error, setError] = useState<string | null>(null);
30+
const { token } = useAuth();
2031

2132
// Query for fetching install configuration
2233
const { isLoading: isConfigLoading } = useQuery({
2334
queryKey: ["installConfig"],
2435
queryFn: async () => {
2536
const response = await fetch("/api/install/installation/config", {
2637
headers: {
27-
...(localStorage.getItem("auth") && {
28-
Authorization: `Bearer ${localStorage.getItem("auth")}`,
29-
}),
38+
Authorization: `Bearer ${token}`,
3039
},
3140
});
3241
if (!response.ok) {
@@ -44,9 +53,7 @@ const SetupStep: React.FC<SetupStepProps> = ({ onNext, onBack }) => {
4453
queryFn: async () => {
4554
const response = await fetch("/api/console/available-network-interfaces", {
4655
headers: {
47-
...(localStorage.getItem("auth") && {
48-
Authorization: `Bearer ${localStorage.getItem("auth")}`,
49-
}),
56+
Authorization: `Bearer ${token}`,
5057
},
5158
});
5259
if (!response.ok) {
@@ -57,15 +64,13 @@ const SetupStep: React.FC<SetupStepProps> = ({ onNext, onBack }) => {
5764
});
5865

5966
// Mutation for submitting the configuration
60-
const { mutate: submitConfig, error: submitError } = useMutation({
67+
const { mutate: submitConfig, error: submitError } = useMutation<Status, ConfigError, typeof config>({
6168
mutationFn: async (configData: typeof config) => {
6269
const response = await fetch("/api/install/installation/configure", {
6370
method: "POST",
6471
headers: {
6572
"Content-Type": "application/json",
66-
...(localStorage.getItem("auth") && {
67-
Authorization: `Bearer ${localStorage.getItem("auth")}`,
68-
}),
73+
Authorization: `Bearer ${token}`,
6974
},
7075
body: JSON.stringify(configData),
7176
});
@@ -79,7 +84,7 @@ const SetupStep: React.FC<SetupStepProps> = ({ onNext, onBack }) => {
7984
onSuccess: () => {
8085
onNext();
8186
},
82-
onError: (err: Error) => {
87+
onError: (err: ConfigError) => {
8388
setError(err.message || "Failed to setup cluster");
8489
return err;
8590
},

web/src/components/wizard/ValidationStep.tsx

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { useWizardMode } from "../../contexts/WizardModeContext";
55
import { ChevronLeft, ChevronRight } from "lucide-react";
66
import LinuxPreflightCheck from "./preflight/LinuxPreflightCheck";
77
import { useMutation } from "@tanstack/react-query";
8+
import { useAuth } from "../../contexts/AuthContext";
89

910
interface ValidationStepProps {
1011
onNext: () => void;
@@ -16,6 +17,7 @@ const ValidationStep: React.FC<ValidationStepProps> = ({ onNext, onBack }) => {
1617
const [preflightComplete, setPreflightComplete] = React.useState(false);
1718
const [preflightSuccess, setPreflightSuccess] = React.useState(false);
1819
const [error, setError] = React.useState<string | null>(null);
20+
const { token } = useAuth();
1921

2022
const handlePreflightComplete = (success: boolean) => {
2123
setPreflightComplete(true);
@@ -27,9 +29,7 @@ const ValidationStep: React.FC<ValidationStepProps> = ({ onNext, onBack }) => {
2729
const response = await fetch("/api/install/node/setup", {
2830
method: "POST",
2931
headers: {
30-
...(localStorage.getItem("auth") && {
31-
Authorization: `Bearer ${localStorage.getItem("auth")}`,
32-
}),
32+
Authorization: `Bearer ${token}`,
3333
},
3434
});
3535

@@ -52,29 +52,17 @@ const ValidationStep: React.FC<ValidationStepProps> = ({ onNext, onBack }) => {
5252
<div className="space-y-6">
5353
<Card>
5454
<div className="mb-6">
55-
<h2 className="text-2xl font-bold text-gray-900">
56-
{text.validationTitle}
57-
</h2>
58-
<p className="text-gray-600 mt-1">
59-
{text.validationDescription}
60-
</p>
55+
<h2 className="text-2xl font-bold text-gray-900">{text.validationTitle}</h2>
56+
<p className="text-gray-600 mt-1">{text.validationDescription}</p>
6157
</div>
6258

6359
<LinuxPreflightCheck onComplete={handlePreflightComplete} />
6460

65-
{error && (
66-
<div className="mt-4 p-3 bg-red-50 text-red-500 rounded-md">
67-
{error}
68-
</div>
69-
)}
61+
{error && <div className="mt-4 p-3 bg-red-50 text-red-500 rounded-md">{error}</div>}
7062
</Card>
7163

7264
<div className="flex justify-between">
73-
<Button
74-
variant="outline"
75-
onClick={onBack}
76-
icon={<ChevronLeft className="w-5 h-5" />}
77-
>
65+
<Button variant="outline" onClick={onBack} icon={<ChevronLeft className="w-5 h-5" />}>
7866
Back
7967
</Button>
8068
<Button

web/src/components/wizard/WelcomeStep.tsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState } from "react";
1+
import React, { useEffect, useState } from "react";
22
import Card from "../common/Card";
33
import Button from "../common/Button";
44
import Input from "../common/Input";
@@ -7,6 +7,7 @@ import { ChevronRight, Lock, AlertTriangle } from "lucide-react";
77
import { useWizardMode } from "../../contexts/WizardModeContext";
88
import { useConfig } from "../../contexts/ConfigContext";
99
import { useMutation } from "@tanstack/react-query";
10+
import { useAuth } from "../../contexts/AuthContext";
1011

1112
interface WelcomeStepProps {
1213
onNext: () => void;
@@ -20,8 +21,16 @@ const WelcomeStep: React.FC<WelcomeStepProps> = ({ onNext }) => {
2021
const { text } = useWizardMode();
2122
const { prototypeSettings } = useConfig();
2223
const [password, setPassword] = useState("");
24+
const { setToken, isAuthenticated } = useAuth();
2325
const [showPasswordInput, setShowPasswordInput] = useState(!prototypeSettings.useSelfSignedCert);
2426

27+
// Automatically redirect to SetupStep if already authenticated
28+
useEffect(() => {
29+
if (isAuthenticated) {
30+
onNext();
31+
}
32+
}, [isAuthenticated, onNext]);
33+
2534
const {
2635
mutate: login,
2736
isPending: isLoading,
@@ -43,7 +52,7 @@ const WelcomeStep: React.FC<WelcomeStepProps> = ({ onNext }) => {
4352
return response.json();
4453
},
4554
onSuccess: (data) => {
46-
localStorage.setItem("auth", data.token);
55+
setToken(data.token);
4756
onNext();
4857
},
4958
});
@@ -70,6 +79,10 @@ const WelcomeStep: React.FC<WelcomeStepProps> = ({ onNext }) => {
7079
handleSubmit();
7180
}
7281
};
82+
// If already authenticated, don't render the welcome step
83+
if (isAuthenticated) {
84+
return null;
85+
}
7386

7487
return (
7588
<div className="space-y-6">

web/src/components/wizard/preflight/LinuxPreflightCheck.tsx

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { useConfig } from "../../../contexts/ConfigContext";
33
import { XCircle, CheckCircle, Loader2, AlertTriangle, RefreshCw } from "lucide-react";
44
import { useQuery, useMutation } from "@tanstack/react-query";
55
import Button from "../../common/Button";
6+
import { useAuth } from "../../../contexts/AuthContext";
67

78
interface LinuxPreflightCheckProps {
89
onComplete: (success: boolean) => void;
@@ -42,6 +43,7 @@ const LinuxPreflightCheck: React.FC<LinuxPreflightCheckProps> = ({ onComplete })
4243
const [isInstallationStatusPolling, setIsInstallationStatusPolling] = useState(true);
4344
const { prototypeSettings } = useConfig();
4445
const themeColor = prototypeSettings.themeColor;
46+
const { token } = useAuth();
4547

4648
const hasFailures = (output?: PreflightOutput) => (output?.fail?.length ?? 0) > 0;
4749
const hasWarnings = (output?: PreflightOutput) => (output?.warn?.length ?? 0) > 0;
@@ -66,9 +68,7 @@ const LinuxPreflightCheck: React.FC<LinuxPreflightCheckProps> = ({ onComplete })
6668
const response = await fetch("/api/install/host-preflights/run", {
6769
method: "POST",
6870
headers: {
69-
...(localStorage.getItem("auth") && {
70-
Authorization: `Bearer ${localStorage.getItem("auth")}`,
71-
}),
71+
Authorization: `Bearer ${token}`,
7272
},
7373
});
7474
if (!response.ok) {
@@ -86,10 +86,7 @@ const LinuxPreflightCheck: React.FC<LinuxPreflightCheckProps> = ({ onComplete })
8686
});
8787

8888
// Query to poll installation status
89-
const { data: installationStatus } = useQuery<
90-
InstallationStatusResponse,
91-
Error
92-
>({
89+
const { data: installationStatus } = useQuery<InstallationStatusResponse, Error>({
9390
queryKey: ["installationStatus"],
9491
queryFn: async () => {
9592
const response = await fetch("/api/install/installation/status", {
@@ -153,10 +150,7 @@ const LinuxPreflightCheck: React.FC<LinuxPreflightCheckProps> = ({ onComplete })
153150
if (isInstallationStatusPolling) {
154151
return (
155152
<div className="flex flex-col items-center justify-center py-12">
156-
<Loader2
157-
className="w-8 h-8 animate-spin mb-4"
158-
style={{ color: themeColor }}
159-
/>
153+
<Loader2 className="w-8 h-8 animate-spin mb-4" style={{ color: themeColor }} />
160154
<p className="text-lg font-medium text-gray-900">Initializing...</p>
161155
<p className="text-sm text-gray-500 mt-2">Preparing the host.</p>
162156
</div>
@@ -166,10 +160,7 @@ const LinuxPreflightCheck: React.FC<LinuxPreflightCheckProps> = ({ onComplete })
166160
if (isPreflightsPolling) {
167161
return (
168162
<div className="flex flex-col items-center justify-center py-12">
169-
<Loader2
170-
className="w-8 h-8 animate-spin mb-4"
171-
style={{ color: themeColor }}
172-
/>
163+
<Loader2 className="w-8 h-8 animate-spin mb-4" style={{ color: themeColor }} />
173164
<p className="text-lg font-medium text-gray-900">Validating host requirements...</p>
174165
<p className="text-sm text-gray-500 mt-2">Please wait while we check your system.</p>
175166
</div>
@@ -180,7 +171,7 @@ const LinuxPreflightCheck: React.FC<LinuxPreflightCheckProps> = ({ onComplete })
180171
if (isSuccessful(preflightResponse)) {
181172
return (
182173
<div className="flex flex-col items-center justify-center py-12">
183-
<div
174+
<div
184175
className="w-12 h-12 rounded-full flex items-center justify-center mb-4"
185176
style={{ backgroundColor: `${themeColor}1A` }}
186177
>
@@ -281,16 +272,11 @@ const LinuxPreflightCheck: React.FC<LinuxPreflightCheckProps> = ({ onComplete })
281272
<li>• Click "Back" to modify your setup if needed</li>
282273
</>
283274
)}
284-
{hasWarnings(preflightResponse?.output) && (
285-
<li>• Review the warnings above and take action if needed</li>
286-
)}
275+
{hasWarnings(preflightResponse?.output) && <li>• Review the warnings above and take action if needed</li>}
287276
<li>• Re-run the validation once issues are addressed</li>
288277
</ul>
289278
<div className="mt-4">
290-
<Button
291-
onClick={() => runPreflights()}
292-
icon={<RefreshCw className="w-4 h-4" />}
293-
>
279+
<Button onClick={() => runPreflights()} icon={<RefreshCw className="w-4 h-4" />}>
294280
Run Validation Again
295281
</Button>
296282
</div>

0 commit comments

Comments
 (0)