MigrateNode.tsx raw
1 import { DatabaseIcon, InfoIcon, TriangleAlertIcon } from "lucide-react";
2 import React, { useState } from "react";
3 import { useNavigate } from "react-router";
4 import PasswordInput from "src/components/password/PasswordInput";
5
6 import SettingsHeader from "src/components/SettingsHeader";
7 import { Button } from "src/components/ui/button";
8 import { LinkButton } from "src/components/ui/custom/link-button";
9 import { LoadingButton } from "src/components/ui/custom/loading-button";
10 import { Label } from "src/components/ui/label";
11 import {
12 Tooltip,
13 TooltipContent,
14 TooltipProvider,
15 TooltipTrigger,
16 } from "src/components/ui/tooltip";
17 import { useInfo } from "src/hooks/useInfo";
18
19 import { handleRequestError } from "src/utils/handleRequestError";
20 import { isHttpMode } from "src/utils/isHttpMode";
21 import { request } from "src/utils/request";
22
23 export function MigrateNode() {
24 const navigate = useNavigate();
25 const { data: info } = useInfo();
26
27 const [unlockPassword, setUnlockPassword] = React.useState("");
28 const [showPasswordScreen, setShowPasswordScreen] = useState<boolean>(false);
29 const [loading, setLoading] = React.useState(false);
30
31 const onSubmitPassword = async (e: React.FormEvent) => {
32 e.preventDefault();
33
34 const _isHttpMode = isHttpMode();
35
36 try {
37 setLoading(true);
38
39 if (_isHttpMode) {
40 const response = await fetch("/api/backup", {
41 method: "POST",
42 headers: {
43 "Content-Type": "application/json",
44 },
45 body: JSON.stringify({
46 UnlockPassword: unlockPassword,
47 }),
48 });
49
50 if (!response.ok) {
51 throw new Error(await response.text());
52 }
53 const blob = await response.blob();
54 const url = window.URL.createObjectURL(blob);
55 const a = document.createElement("a");
56 a.href = url;
57 a.download = "albyhub.bkp";
58 document.body.appendChild(a);
59 a.click();
60 window.URL.revokeObjectURL(url);
61 a.remove();
62 } else {
63 await request("/api/backup", {
64 method: "POST",
65 headers: {
66 "Content-Type": "application/json",
67 },
68 body: JSON.stringify({
69 unlockPassword,
70 }),
71 });
72 }
73
74 navigate("/create-node-migration-file-success");
75 } catch (error) {
76 handleRequestError("Failed to backup the node", error);
77 } finally {
78 setLoading(false);
79 }
80 };
81
82 return (
83 <>
84 <SettingsHeader
85 title="Migrate Alby Hub"
86 pageTitle="Migrate"
87 description="Create migration file in order to move your Alby Hub to another device or server."
88 />
89
90 <div className="flex flex-col gap-6">
91 <div className="flex flex-col gap-1">
92 <div className="flex gap-3 items-center">
93 <TriangleAlertIcon className="size-4" />
94 <h3>Do not run your Alby Hub on multiple devices</h3>
95 </div>
96 <p className="text-sm ml-7">
97 After creating this migration file, do not restart Alby Hub on this
98 device, as this will cause problems and may cause force channel
99 closures.
100 </p>
101 </div>
102 <div className="flex flex-col gap-1">
103 <div className="flex gap-3 items-center">
104 <TriangleAlertIcon className="size-4" />
105 <h3>Migrate this file only to fresh Alby Hub</h3>
106 </div>
107 <p className="text-sm ml-7">
108 To import the migration file, you must have a brand new Alby Hub on
109 another device and use the “Advanced” option during the onboarding.
110 </p>
111 </div>
112 <div className="flex flex-col gap-1">
113 <div className="flex gap-3 items-center">
114 <TriangleAlertIcon className="size-4" />
115 <h3>Never share your migration file</h3>
116 </div>
117 <p className="text-sm ml-7">
118 Anyone with this file and your unlock password can access your
119 funds. Never send it to anyone. Alby support will never ask for it.
120 </p>
121 </div>
122 <div className="flex flex-col gap-1">
123 <div className="flex gap-3 items-center">
124 <InfoIcon className="size-4" />
125 <h3>What happens next?</h3>
126 </div>
127 <p className="text-sm ml-7">
128 After typing your unlock password, you'll be able to download a
129 migration file which contains a backup of your Alby Hub data. Then
130 you'll see instructions on how to import this migration file into
131 another device or server.
132 </p>
133 </div>
134 {info?.databaseType === "postgres" && (
135 <div className="flex gap-3 items-center">
136 <DatabaseIcon className="size-4" />
137 <h3>Your database will be migrated</h3>
138 <TooltipProvider>
139 <Tooltip>
140 <TooltipTrigger type="button">
141 <InfoIcon className="size-4 shrink-0 text-muted-foreground" />
142 </TooltipTrigger>
143 <TooltipContent className="max-w-xs">
144 The contents of your PostgreSQL database will be copied into a
145 local SQLite database while the migration file is being
146 created. Your new Alby Hub will use this SQLite database.
147 </TooltipContent>
148 </Tooltip>
149 </TooltipProvider>
150 </div>
151 )}
152 </div>
153
154 {showPasswordScreen ? (
155 <div>
156 <h1 className="font-medium mb-1">Enter unlock password</h1>
157 <p className="text-muted-foreground mb-4">
158 Your unlock password will be used to encrypt your migration file
159 </p>
160 <form
161 onSubmit={onSubmitPassword}
162 className="w-full md:w-96 flex flex-col gap-6"
163 >
164 <>
165 <div className="grid gap-2">
166 <Label htmlFor="password">Password</Label>
167 <PasswordInput
168 id="password"
169 autoFocus
170 onChange={setUnlockPassword}
171 value={unlockPassword}
172 />
173 </div>
174 </>
175 <LoadingButton loading={loading}>Continue</LoadingButton>
176 </form>
177 </div>
178 ) : (
179 <div className="flex gap-4">
180 <Button
181 type="submit"
182 disabled={loading}
183 onClick={() => setShowPasswordScreen(true)}
184 >
185 Create Alby Hub Migration File
186 </Button>
187
188 <LinkButton to="/settings/backup" variant={"secondary"}>
189 Backup Without Migrating Alby Hub
190 </LinkButton>
191 </div>
192 )}
193 </>
194 );
195 }
196