RestoreNode.tsx raw
1 import { PowerCircleIcon } from "lucide-react";
2 import React, { ChangeEvent, useState } from "react";
3 import { useNavigate } from "react-router";
4 import Loading from "src/components/Loading";
5 import PasswordInput from "src/components/password/PasswordInput";
6 import TwoColumnLayoutHeader from "src/components/TwoColumnLayoutHeader";
7 import {
8 AlertDialog,
9 AlertDialogAction,
10 AlertDialogCancel,
11 AlertDialogContent,
12 AlertDialogDescription,
13 AlertDialogFooter,
14 AlertDialogHeader,
15 AlertDialogTitle,
16 AlertDialogTrigger,
17 } from "src/components/ui/alert-dialog";
18 import { LoadingButton } from "src/components/ui/custom/loading-button";
19 import { Input } from "src/components/ui/input";
20 import { Label } from "src/components/ui/label";
21
22 import { useInfo } from "src/hooks/useInfo";
23 import { handleRequestError } from "src/utils/handleRequestError";
24 import { isHttpMode } from "src/utils/isHttpMode";
25 import { request } from "src/utils/request";
26
27 export function RestoreNode() {
28 const navigate = useNavigate();
29
30 const [unlockPassword, setUnlockPassword] = useState("");
31 const [file, setFile] = useState<File | null>(null);
32
33 const [showAlert, setShowAlert] = useState(false);
34 const [loading, setLoading] = useState(false);
35 const [restored, setRestored] = useState(false);
36 const { data: info } = useInfo(restored);
37 const _isHttpMode = isHttpMode();
38
39 React.useEffect(() => {
40 if (restored && info?.setupCompleted) {
41 navigate("/");
42 }
43 }, [info?.setupCompleted, navigate, restored]);
44
45 if (restored) {
46 return (
47 <div className="flex flex-col gap-5 items-center">
48 <TwoColumnLayoutHeader
49 title="Restart your Hub"
50 pageTitle="Restart your Hub"
51 description="Alby Hub needs to restart to finish restoring your node"
52 />
53 <PowerCircleIcon className="w-32 h-32" />
54 <p className="max-w-sm text-center">
55 If you're running in a cloud VM or linux service, your Alby Hub will
56 restart automatically. Otherwise, please manually restart your Alby
57 Hub to finish the restore process.
58 </p>
59 <div className="flex items-center gap-2 text-muted-foreground">
60 <Loading /> <p>Waiting for restart...</p>
61 </div>
62 </div>
63 );
64 }
65
66 const onSubmit = (e: React.FormEvent) => {
67 e.preventDefault();
68 setShowAlert(true);
69 };
70
71 const restoreNode = async () => {
72 try {
73 setLoading(true);
74
75 if (_isHttpMode) {
76 const formData = new FormData();
77 formData.append("unlockPassword", unlockPassword);
78 if (file !== null) {
79 formData.append("backup", file);
80 }
81 await request("/api/restore", {
82 method: "POST",
83 body: formData,
84 });
85 } else {
86 await request("/api/restore", {
87 method: "POST",
88 body: JSON.stringify({
89 unlockPassword,
90 }),
91 });
92 }
93
94 setRestored(true);
95 } catch (error) {
96 handleRequestError("Failed to restore backup", error);
97 } finally {
98 setShowAlert(false);
99 setLoading(false);
100 }
101 };
102
103 const handleChangeFile = (e: ChangeEvent<HTMLInputElement>) => {
104 const files = e.currentTarget.files;
105 if (files) {
106 setFile(files[0]);
107 }
108 };
109
110 return (
111 <form
112 onSubmit={onSubmit}
113 className="flex flex-col gap-5 mx-auto max-w-2xl text-sm"
114 >
115 <TwoColumnLayoutHeader
116 // TODO: Show different message in wails mode
117 title="Import Wallet from Migration File"
118 pageTitle="Import Wallet from Migration File"
119 description="Upload your encrypted wallet migration file."
120 />
121 <div className="grid gap-2">
122 <Label htmlFor="password">Unlock Password</Label>
123 <PasswordInput
124 onChange={setUnlockPassword}
125 value={unlockPassword}
126 placeholder="Unlock Password"
127 />
128 </div>
129 {_isHttpMode && (
130 <div className="grid gap-2">
131 <Label htmlFor="backup">Migration File</Label>
132 <Input
133 type="file"
134 required
135 id="backup"
136 name="backup"
137 accept=".bkp"
138 onChange={handleChangeFile}
139 className="cursor-pointer pt-2"
140 />
141 </div>
142 )}
143 <AlertDialog open={showAlert}>
144 <AlertDialogTrigger asChild>
145 <LoadingButton type="submit" loading={loading}>
146 Import Wallet
147 </LoadingButton>
148 </AlertDialogTrigger>
149 <AlertDialogContent>
150 <AlertDialogHeader>
151 <AlertDialogTitle>
152 Restore Node from Migration File
153 </AlertDialogTitle>
154 <AlertDialogDescription>
155 <div>
156 <p>
157 As part of the node restore process your Alby Hub will be shut
158 down.
159 </p>
160 <p className="mt-4">
161 If you're running in a cloud VM or linux service, your Alby
162 Hub will restart automatically. Otherwise, please manually
163 restart your Alby Hub to finish the restore process.
164 </p>
165 </div>
166 </AlertDialogDescription>
167 </AlertDialogHeader>
168 <AlertDialogFooter>
169 <AlertDialogCancel onClick={() => setShowAlert(false)}>
170 Cancel
171 </AlertDialogCancel>
172 <AlertDialogAction onClick={restoreNode}>
173 Continue
174 </AlertDialogAction>
175 </AlertDialogFooter>
176 </AlertDialogContent>
177 </AlertDialog>
178 </form>
179 );
180 }
181