Use the debug info port to check the port forwarding

This commit is contained in:
Arnaud 2024-10-24 12:49:04 +02:00
parent a102f3835a
commit 21fe557157
No known key found for this signature in database
GPG Key ID: 69D6CE281FCAE663
10 changed files with 188 additions and 86 deletions

View File

@ -1,32 +1,10 @@
import { useQuery } from "@tanstack/react-query";
import { CodexSdk } from "../../sdk/codex";
import { Promises } from "../../utils/promises";
import { Spinner } from "@codex-storage/marketplace-ui-components";
import { useDebug } from "../../hooks/useDebug";
const throwOnError = true;
export function Debug() {
const { data, isPending } = useQuery({
queryFn: () =>
CodexSdk.debug()
.info()
.then((s) => Promises.rejectOnError(s)),
queryKey: ["debug"],
// No need to retry because if the connection to the node
// is back again, all the queries will be invalidated.
retry: false,
// The client node should be local, so display the cache value while
// making a background request looks good.
staleTime: 0,
// Refreshing when focus returns can be useful if a user comes back
// to the UI after performing an operation in the terminal.
refetchOnWindowFocus: true,
// Throw the error to the error boundary
throwOnError: true,
});
const { data, isPending } = useDebug(throwOnError);
if (isPending) {
return (

View File

@ -38,6 +38,11 @@
margin-bottom: 0.5rem;
}
.onboarding-codex {
margin-top: 1rem;
padding-left: 1rem;
}
@keyframes rotate {
from {
transform: rotate(0deg); /* Start at 0 degrees */

View File

@ -4,6 +4,7 @@ import "./OnBoardingStepThree.css";
import { usePortForwarding } from "../../hooks/usePortForwarding";
import { useCodexConnection } from "../../hooks/useCodexConnection";
import {
Alert,
ButtonIcon,
Input,
SimpleText,
@ -12,28 +13,33 @@ import { useEffect, useState } from "react";
import { CodexSdk } from "../../sdk/codex";
import { useQueryClient } from "@tanstack/react-query";
import { usePersistence } from "../../hooks/usePersistence";
import { useDebug } from "../../hooks/useDebug";
import { DebugUtils } from "../../utils/debug";
type Props = {
online: boolean;
onStepValid: (valid: boolean) => void;
};
const throwOnError = false;
const defaultPort = 8070;
export function OnBoardingStepThree({ online, onStepValid }: Props) {
const portForwarding = usePortForwarding(online);
const codex = useCodexConnection();
const persistence = usePersistence(codex.enabled);
const codex = useDebug(throwOnError);
const portForwarding = usePortForwarding(codex.data);
const persistence = usePersistence(codex.isSuccess);
const [url, setUrl] = useState(CodexSdk.url);
const queryClient = useQueryClient();
useEffect(() => {
onStepValid(online && portForwarding.enabled && codex.enabled);
}, [portForwarding.enabled, codex.enabled, onStepValid, online]);
onStepValid(online && portForwarding.enabled && codex.isSuccess);
}, [portForwarding.enabled, codex.isSuccess, onStepValid, online]);
useEffect(() => {
if (codex.enabled) {
if (codex.isSuccess) {
persistence.refetch();
}
}, [codex.enabled]);
}, [codex.isSuccess]);
const onChange = (e: React.FormEvent<HTMLInputElement>) => {
const value = e.currentTarget.value;
@ -49,9 +55,22 @@ export function OnBoardingStepThree({ online, onStepValid }: Props) {
const InternetIcon = online ? CheckIcon : X;
const PortForWarningIcon = portForwarding.enabled ? CheckIcon : X;
const CodexIcon = codex.enabled ? CheckIcon : X;
const CodexIcon = codex.isSuccess ? CheckIcon : X;
const PersistenceIcon = persistence.enabled ? CheckIcon : ShieldAlert;
let hasPortForwarningWarning = false;
let portValue = 0;
if (codex.isSuccess && codex.data) {
const port = DebugUtils.getTcpPort(codex.data);
if (port.error === false && port.data !== defaultPort) {
hasPortForwarningWarning = true;
}
if (!port.error) {
portValue = port.data;
}
}
return (
<div className="index-column-section">
<div className="onboarding-group">
@ -102,6 +121,14 @@ export function OnBoardingStepThree({ online, onStepValid }: Props) {
<SimpleText variant="light">
Status indicator for port forwarding activation.
</SimpleText>
{portValue && (
<>
<br />
<SimpleText variant="light">
TCP Port detected: {portValue}.
</SimpleText>
</>
)}
</div>
{!portForwarding.enabled && (
<a
@ -118,59 +145,72 @@ export function OnBoardingStepThree({ online, onStepValid }: Props) {
)}
</div>
</div>
<div
className={classnames(
["onboarding-check"],
["onboarding-check--valid", codex.enabled]
)}>
<CodexIcon
<p>Codex</p>
<div className="onboarding-codex">
<div
className={classnames(
["onboarding-check-icon--valid", codex.enabled],
["onboarding-check-icon--invalid", !codex.enabled]
)}
/>
<div className="onboarding-check-line">
<div>
<p>Codex connection</p>
<SimpleText variant="light">
Status indicator for the Codex network.
</SimpleText>
["onboarding-check"],
["onboarding-check--valid", codex.isSuccess]
)}>
<CodexIcon
className={classnames(
["onboarding-check-icon--valid", codex.isSuccess],
["onboarding-check-icon--invalid", !codex.isSuccess]
)}
/>
<div className="onboarding-check-line">
<div>
<p>Codex connection</p>
<SimpleText variant="light">
Status indicator for the Codex network.
</SimpleText>
</div>
{!persistence.enabled && (
<a
className="onboarding-check-refresh"
onClick={() => persistence.refetch()}>
<RefreshCcw
size={"1.25rem"}
className={classnames([
"onboarding-check-refresh--fetching",
persistence.isFetching,
])}
/>
</a>
)}
</div>
{!persistence.enabled && (
<a
className="onboarding-check-refresh"
onClick={() => persistence.refetch()}>
<RefreshCcw
size={"1.25rem"}
className={classnames([
"onboarding-check-refresh--fetching",
persistence.isFetching,
])}
/>
</a>
)}
</div>
</div>
<div
className={classnames(
["onboarding-check"],
["onboarding-check--valid", persistence.enabled]
)}>
<PersistenceIcon
<div
className={classnames(
["onboarding-check-icon--valid", persistence.enabled],
["onboarding-check-icon--warning", !persistence.enabled]
)}
/>
<div className="onboarding-check-line">
<div>
<p>Marketplace</p>
<SimpleText variant="light">
Status indicator for the marketplace on the Codex node.
</SimpleText>
["onboarding-check"],
["onboarding-check--valid", persistence.enabled]
)}>
<PersistenceIcon
className={classnames(
["onboarding-check-icon--valid", persistence.enabled],
["onboarding-check-icon--warning", !persistence.enabled]
)}
/>
<div className="onboarding-check-line">
<div>
<p>Marketplace</p>
<SimpleText variant="light">
Status indicator for the marketplace on the Codex node.
</SimpleText>
</div>
</div>
</div>
</div>
{hasPortForwarningWarning && (
<Alert variant="warning" title="Warning">
<span>
It seems like you are using a different port than the default one (
{defaultPort}). Be sure the port forwarning is enabled for the port
you are running.
</span>
</Alert>
)}
</div>
);
}

31
src/hooks/useDebug.ts Normal file
View File

@ -0,0 +1,31 @@
import { useQuery } from "@tanstack/react-query";
import { CodexSdk } from "../sdk/codex";
import { Promises } from "../utils/promises";
export function useDebug(throwOnError: boolean) {
const { data, isError, isPending, refetch, isSuccess } = useQuery({
queryFn: () =>
CodexSdk.debug()
.info()
.then((s) => Promises.rejectOnError(s)),
queryKey: ["debug"],
// No need to retry because if the connection to the node
// is back again, all the queries will be invalidated.
retry: false,
// The client node should be local, so display the cache value while
// making a background request looks good.
staleTime: 0,
// Refreshing when focus returns can be useful if a user comes back
// to the UI after performing an operation in the terminal.
refetchOnWindowFocus: true,
// Throw the error to the error boundary
throwOnError,
});
return { data, isPending, isError, isSuccess, refetch };
}

View File

@ -1,19 +1,28 @@
import { useQuery } from "@tanstack/react-query";
import { Echo } from "../utils/echo";
import { Errors } from "../utils/errors";
import { CodexDebugInfo } from "@codex-storage/sdk-js";
import { DebugUtils } from "../utils/debug";
type PortForwardingResponse = { reachable: boolean };
export function usePortForwarding(online: boolean) {
export function usePortForwarding(info: CodexDebugInfo | undefined) {
const { data, isFetching, refetch } = useQuery({
queryFn: (): Promise<PortForwardingResponse> =>
Echo.portForwarding().catch((e) => Errors.report(e)),
queryFn: (): Promise<PortForwardingResponse> => {
const port = DebugUtils.getTcpPort(info!);
if (port.error) {
Errors.report(port);
return Promise.resolve({ reachable: false });
} else {
return Echo.portForwarding(port.data).catch((e) => Errors.report(e));
}
},
queryKey: ["port-forwarding"],
initialData: { reachable: false },
// Enable only when the use has an internet connection
enabled: !!online,
enabled: !!info,
// No need to retry because we provide a retry button
retry: false,

View File

@ -138,6 +138,10 @@
justify-content: space-between;
}
.index-column-section {
max-width: 450px;
}
.index-column-section:not(:first-child) {
flex: 1;
}

View File

@ -12,6 +12,7 @@ import { OnBoardingStepThree } from "../components/OnBoarding/OnBoardingStepThre
import { attributes } from "../utils/attributes";
import { CodexLogo } from "../components/CodexLogo/CodexLogo";
import { OnBoardingImage } from "../components/OnBoarding/OnBoardingImage";
import { OnBoardingUtils } from "../utils/onboarding";
export const Route = createFileRoute("/")({
component: Index,
@ -24,7 +25,7 @@ export const Route = createFileRoute("/")({
function Index() {
const [isStepValid, setIsStepValid] = useState(true);
const [step, setStep] = useState(0);
const [step, setStep] = useState(OnBoardingUtils.getStep());
const online = useNetwork();
const navigate = useNavigate({ from: "/" });
const onStepValid = (valid: boolean) => setIsStepValid(valid);
@ -39,6 +40,8 @@ function Index() {
return;
}
OnBoardingUtils.setStep(step + 1);
setStep(step + 1);
setIsStepValid(false);
};

23
src/utils/debug.ts Normal file
View File

@ -0,0 +1,23 @@
import { CodexDebugInfo, CodexError, SafeValue } from "@codex-storage/sdk-js";
export const DebugUtils = {
getTcpPort(info: CodexDebugInfo): SafeValue<number> {
if (info.addrs.length === 0) {
return { error: true, data: new CodexError("Not existing address") }
}
const parts = info.addrs[0].split("/")
if (parts.length < 2) {
return { error: true, data: new CodexError("Address misformated") }
}
const port = parseInt(parts[parts.length - 1], 10)
if (isNaN(port)) {
return { error: true, data: new CodexError("Port misformated") }
}
return { error: false, data: port }
}
}

View File

@ -1,5 +1,5 @@
export const Echo = {
portForwarding: () => fetch(import.meta.env.VITE_GEO_IP_URL + "/port/8070")
portForwarding: (port: number) => fetch(import.meta.env.VITE_GEO_IP_URL + "/port/" + port)
.then((res) => res.json())
}

9
src/utils/onboarding.ts Normal file
View File

@ -0,0 +1,9 @@
export const OnBoardingUtils = {
getStep() {
return parseInt(localStorage.getItem("onboarding-step") || "0", 10)
},
setStep(step: number) {
localStorage.setItem("onboarding-step", step.toString())
}
}