Skip to content
This repository has been archived by the owner on Sep 2, 2024. It is now read-only.

Commit

Permalink
feat: update start api to be async (#558)
Browse files Browse the repository at this point in the history
Co-authored-by: Roland Bewick <[email protected]>
  • Loading branch information
im-adithya and rolznz authored Jul 4, 2024
1 parent 1d46d8d commit 46f4504
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 33 deletions.
10 changes: 1 addition & 9 deletions frontend/src/components/redirects/SetupRedirect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { Outlet, useLocation, useNavigate } from "react-router-dom";
import Loading from "src/components/Loading";
import { useInfo } from "src/hooks/useInfo";

let didSetupThisSession = false;
export function SetupRedirect() {
const { data: info } = useInfo();
const location = useLocation();
Expand All @@ -13,17 +12,10 @@ export function SetupRedirect() {
if (!info) {
return;
}
if (didSetupThisSession) {
// ensure redirect does not happen as node may still be starting
// which would then incorrectly redirect to the login page
console.info("Skipping setup redirect on initial setup");
return;
}
if (info.setupCompleted) {
if (info.setupCompleted && info.running) {
navigate("/");
return;
}
didSetupThisSession = true;
}, [info, location, navigate]);

if (!info) {
Expand Down
56 changes: 48 additions & 8 deletions frontend/src/screens/Start.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React from "react";
import { useNavigate } from "react-router-dom";
import Container from "src/components/Container";
import TwoColumnLayoutHeader from "src/components/TwoColumnLayoutHeader";
import { Input } from "src/components/ui/input";
Expand All @@ -11,18 +10,61 @@ import { useInfo } from "src/hooks/useInfo";
import { handleRequestError } from "src/utils/handleRequestError";
import { request } from "src/utils/request";

const messages: string[] = [
"Unlocking",
"Starting the wallet",
"Connecting to the network",
"Syncing",
"Still syncing, please wait...",
];

export default function Start() {
const [unlockPassword, setUnlockPassword] = React.useState("");
const [loading, setLoading] = React.useState(false);
const navigate = useNavigate();
const [buttonText, setButtonText] = React.useState("Login");
useInfo(true); // poll the info endpoint to auto-redirect when app is running
const { data: csrf } = useCSRF();
const { mutate: refetchInfo } = useInfo();
const { toast } = useToast();

React.useEffect(() => {
if (!loading) {
return;
}
let messageIndex = 1;
const intervalId = setInterval(() => {
if (messageIndex < messages.length) {
setButtonText(messages[messageIndex]);
messageIndex++;
} else {
clearInterval(intervalId);
}
}, 5000);

const timeoutId = setTimeout(() => {
// if redirection didn't happen in 3 minutes info.running is false
toast({
title: "Failed to start",
description: "Please try starting the node again.",
variant: "destructive",
});

setLoading(false);
setButtonText("Login");
setUnlockPassword("");
return;
}, 180000); // wait for 3 minutes

return () => {
clearInterval(intervalId);
clearTimeout(timeoutId);
};
}, [loading, toast]);

async function onSubmit(e: React.FormEvent) {
e.preventDefault();
try {
setLoading(true);
setButtonText(messages[0]);
if (!csrf) {
throw new Error("csrf not loaded");
}
Expand All @@ -36,13 +78,11 @@ export default function Start() {
unlockPassword,
}),
});
await refetchInfo();

navigate("/");
} catch (error) {
handleRequestError(toast, "Failed to connect", error);
} finally {
setLoading(false);
setButtonText("Login");
setUnlockPassword("");
}
}

Expand All @@ -68,7 +108,7 @@ export default function Start() {
/>
</div>
<LoadingButton type="submit" loading={loading}>
Login
{buttonText}
</LoadingButton>
</div>
</form>
Expand Down
35 changes: 24 additions & 11 deletions frontend/src/screens/setup/SetupFinish.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ import { request } from "src/utils/request";
export function SetupFinish() {
const navigate = useNavigate();
const { nodeInfo, unlockPassword } = useSetupStore();

const { mutate: refetchInfo } = useInfo();
useInfo(true); // poll the info endpoint to auto-redirect when app is running
const { data: csrf } = useCSRF();
const [loading, setLoading] = React.useState(false);
const [connectionError, setConnectionError] = React.useState(false);
const hasFetchedRef = React.useRef(false);

Expand All @@ -30,6 +30,22 @@ export function SetupFinish() {
},
};

useEffect(() => {
if (!loading) {
return;
}
const timer = setTimeout(() => {
// SetupRedirect takes care of redirection once info.running is true
// if it still didn't redirect after 3 minutes, we show an error
setLoading(false);
setConnectionError(true);
}, 180000);

return () => {
clearTimeout(timer);
};
}, [loading]);

useEffect(() => {
// ensure setup call is only called once
if (!csrf || hasFetchedRef.current) {
Expand All @@ -38,26 +54,23 @@ export function SetupFinish() {
hasFetchedRef.current = true;

(async () => {
setLoading(true);
const succeeded = await finishSetup(csrf, nodeInfo, unlockPassword);
if (succeeded) {
const info = await refetchInfo();
if (!info) {
throw new Error("Failed to re-fetch info");
}
navigate("/");
} else {
// only setup call is successful as start is async
if (!succeeded) {
setLoading(false);
setConnectionError(true);
}
})();
}, [csrf, nodeInfo, refetchInfo, navigate, unlockPassword]);
}, [csrf, nodeInfo, navigate, unlockPassword]);

if (connectionError) {
return (
<Container>
<div className="flex flex-col gap-5 text-center items-center">
<div className="grid gap-2">
<h1 className="font-semibold text-lg">Connection Failed</h1>
<p>Please check your node configuration.</p>
<p>Please check your node configuration and try again.</p>
</div>
<Button
onClick={() => {
Expand Down
16 changes: 11 additions & 5 deletions http/http_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,21 +180,27 @@ func (httpSvc *HttpService) startHandler(c echo.Context) error {
})
}

err := httpSvc.api.Start(&startRequest)
if err != nil {
return c.JSON(http.StatusInternalServerError, ErrorResponse{
Message: fmt.Sprintf("Failed to start node: %s", err.Error()),
if !httpSvc.cfg.CheckUnlockPassword(startRequest.UnlockPassword) {
return c.JSON(http.StatusUnauthorized, ErrorResponse{
Message: "Invalid password",
})
}

err = httpSvc.saveSessionCookie(c)
err := httpSvc.saveSessionCookie(c)

if err != nil {
return c.JSON(http.StatusInternalServerError, ErrorResponse{
Message: fmt.Sprintf("Failed to save session: %s", err.Error()),
})
}

go func() {
err := httpSvc.api.Start(&startRequest)
if err != nil {
logger.Logger.WithError(err).Error("Failed to start node")
}
}()

return c.NoContent(http.StatusNoContent)
}

Expand Down

0 comments on commit 46f4504

Please sign in to comment.