Start.tsx raw
1 import React from "react";
2 import { toast } from "sonner";
3 import Container from "src/components/Container";
4 import PasswordInput from "src/components/password/PasswordInput";
5 import TwoColumnLayoutHeader from "src/components/TwoColumnLayoutHeader";
6 import { LoadingButton } from "src/components/ui/custom/loading-button";
7 import { Label } from "src/components/ui/label";
8
9 import { useInfo } from "src/hooks/useInfo";
10 import { saveAuthToken } from "src/lib/auth";
11 import { AuthTokenResponse } from "src/types";
12 import { handleRequestError } from "src/utils/handleRequestError";
13 import { request } from "src/utils/request";
14
15 export default function Start() {
16 const [unlockPassword, setUnlockPassword] = React.useState("");
17 const [loading, setLoading] = React.useState(false);
18 const [buttonText, setButtonText] = React.useState("Start");
19
20 const { data: info } = useInfo(true); // poll the info endpoint to auto-redirect when app is running
21
22 const startupState = info?.startupState;
23 const startupError = info?.startupError;
24 const startupErrorTime = info?.startupErrorTime;
25
26 React.useEffect(() => {
27 if (startupState) {
28 setButtonText(startupState);
29 }
30 }, [startupState]);
31
32 React.useEffect(() => {
33 if (startupError && startupErrorTime) {
34 toast.error("Failed to start", {
35 description: startupError,
36 });
37 setLoading(false);
38 setButtonText("Start");
39 setUnlockPassword("");
40 }
41 }, [startupError, startupErrorTime]);
42
43 async function onSubmit(e: React.FormEvent) {
44 e.preventDefault();
45 try {
46 setLoading(true);
47 setButtonText("Please wait...");
48
49 const authTokenResponse = await request<AuthTokenResponse>("/api/start", {
50 method: "POST",
51 headers: {
52 "Content-Type": "application/json",
53 },
54 body: JSON.stringify({
55 unlockPassword,
56 permission: "full",
57 }),
58 });
59 if (authTokenResponse) {
60 saveAuthToken(authTokenResponse.token);
61 }
62 } catch (error) {
63 handleRequestError("Failed to connect", error);
64 setLoading(false);
65 setButtonText("Start");
66 setUnlockPassword("");
67 }
68 }
69
70 return (
71 <Container>
72 <div className="mx-auto grid gap-5">
73 <TwoColumnLayoutHeader
74 title="Start"
75 pageTitle="Start"
76 description="Enter your password to unlock and start Alby Hub."
77 />
78 <form onSubmit={onSubmit}>
79 <div className="grid gap-4">
80 <div className="grid gap-1.5">
81 <Label htmlFor="password">Password</Label>
82 <PasswordInput
83 id="password"
84 onChange={setUnlockPassword}
85 autoFocus
86 value={unlockPassword}
87 />
88 </div>
89 <LoadingButton
90 type="submit"
91 loading={loading}
92 disabled={!unlockPassword}
93 >
94 {buttonText}
95 </LoadingButton>
96 {loading && (
97 <p className="text-muted-foreground text-xs text-center">
98 Starting Alby Hub may take a few minutes.
99 </p>
100 )}
101 </div>
102 </form>
103 </div>
104 </Container>
105 );
106 }
107