diff --git a/apps/provider-console/public/favicon.ico b/apps/provider-console/public/favicon.ico index 718d6fea4..d1c22ac79 100644 Binary files a/apps/provider-console/public/favicon.ico and b/apps/provider-console/public/favicon.ico differ diff --git a/apps/provider-console/src/components/become-provider/ServerForm.tsx b/apps/provider-console/src/components/become-provider/ServerForm.tsx index 951ee7f09..a20da9747 100644 --- a/apps/provider-console/src/components/become-provider/ServerForm.tsx +++ b/apps/provider-console/src/components/become-provider/ServerForm.tsx @@ -26,7 +26,10 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { useAtom } from "jotai/react"; import { z } from "zod"; +import { useControlMachine } from "@src/context/ControlMachineProvider"; +import { useWallet } from "@src/context/WalletProvider"; import providerProcessStore from "@src/store/providerProcessStore"; +import { ControlMachineWithAddress } from "@src/types/controlMachine"; import restClient from "@src/utils/restClient"; import { ResetProviderForm } from "./ResetProviderProcess"; @@ -55,12 +58,16 @@ type AccountFormValues = z.infer; interface ServerFormProp { currentServerNumber: number; onComplete: () => void; + editMode?: boolean; + controlMachine?: ControlMachineWithAddress | null; } -export const ServerForm: React.FC = ({ currentServerNumber, onComplete }) => { +export const ServerForm: React.FC = ({ currentServerNumber, onComplete, editMode = false, controlMachine }) => { const [providerProcess, setProviderProcess] = useAtom(providerProcessStore.providerProcessAtom); const [selectedFile, setSelectedFile] = useState(null); const [storedFileContent, setStoredFileContent] = useState(null); + const { setControlMachine } = useControlMachine(); + const { address } = useWallet(); const getDefaultValues = () => { if (currentServerNumber === 0 || !providerProcess?.storeInformation) { @@ -84,18 +91,18 @@ export const ServerForm: React.FC = ({ currentServerNumber, onCo const form = useForm({ resolver: zodResolver(accountFormSchema), - defaultValues: getDefaultValues() as any + defaultValues: editMode ? controlMachine?.access : (getDefaultValues() as any) }); useEffect(() => { if (currentServerNumber > 0 && providerProcess?.storeInformation) { - const firstServer = providerProcess.machines[0]?.access; - if (firstServer.file) { + const firstServer = editMode ? controlMachine?.access : providerProcess.machines[0]?.access; + if (firstServer?.file) { setStoredFileContent(typeof firstServer.file === "string" ? firstServer.file : null); form.setValue("authType", "file"); } } - }, [currentServerNumber, providerProcess, form]); + }, [currentServerNumber, providerProcess, form, editMode, controlMachine]); const [verificationError, setVerificationError] = useState<{ message: string; details: string[] } | null>(null); const [, setVerificationResult] = useState(null); @@ -127,7 +134,7 @@ export const ServerForm: React.FC = ({ currentServerNumber, onCo } let response: any; - if (currentServerNumber === 0) { + if (currentServerNumber === 0 || editMode) { response = await restClient.post("/verify/control-machine", jsonData, { headers: { "Content-Type": "application/json" } }); @@ -152,21 +159,29 @@ export const ServerForm: React.FC = ({ currentServerNumber, onCo } if (response.status === "success") { - const machines = [...(providerProcess?.machines ?? [])]; - machines[currentServerNumber] = { + const machine = { access: { ...formValues, file: formValues.file && formValues.file[0] ? await readFileAsBase64(formValues.file[0]) : storedFileContent }, systemInfo: response.data.system_info }; + if (!editMode) { + const machines = [...(providerProcess?.machines ?? [])]; + machines[currentServerNumber] = machine; - setProviderProcess({ - ...providerProcess, - machines, - storeInformation: currentServerNumber === 0 ? formValues.saveInformation : providerProcess?.storeInformation, - process: providerProcess.process - }); + setProviderProcess({ + ...providerProcess, + machines, + storeInformation: currentServerNumber === 0 ? formValues.saveInformation : providerProcess?.storeInformation, + process: providerProcess.process + }); + } else { + setControlMachine({ + address, + ...machine + }); + } onComplete(); } } catch (error: any) { @@ -206,10 +221,9 @@ export const ServerForm: React.FC = ({ currentServerNumber, onCo

- {currentServerNumber === 0 && "Control Plane Machine Access"} - {currentServerNumber !== 0 && "Node Access"} + {editMode ? "Control Machine Access" : currentServerNumber === 0 ? "Control Plane Machine Access" : "Node Access"}

-

Enter the required details for your control plane setup

+

Enter the required details for your {editMode ? "control machine" : "control plane setup"}

@@ -345,9 +359,7 @@ export const ServerForm: React.FC = ({ currentServerNumber, onCo
-
- -
+
{!editMode && }
- {currentServerNumber === 0 && ( + {currentServerNumber === 0 && !editMode && (

Heads up!

diff --git a/apps/provider-console/src/components/become-provider/WalletImport.tsx b/apps/provider-console/src/components/become-provider/WalletImport.tsx index c6d78f2ef..c31905623 100644 --- a/apps/provider-console/src/components/become-provider/WalletImport.tsx +++ b/apps/provider-console/src/components/become-provider/WalletImport.tsx @@ -21,7 +21,10 @@ import { useAtom } from "jotai"; import { useRouter } from "next/router"; import { z } from "zod"; +import { useControlMachine } from "@src/context/ControlMachineProvider"; +import { useWallet } from "@src/context/WalletProvider"; import providerProcessStore from "@src/store/providerProcessStore"; +import { ControlMachineWithAddress } from "@src/types/controlMachine"; import restClient from "@src/utils/restClient"; import { ResetProviderForm } from "./ResetProviderProcess"; @@ -77,6 +80,8 @@ export const WalletImport: React.FC = ({ onComplete }) => { const [providerProcess] = useAtom(providerProcessStore.providerProcessAtom); const [, resetProviderProcess] = useAtom(providerProcessStore.resetProviderProcess); + const { setControlMachine } = useControlMachine(); + const { address } = useWallet(); const defaultValues: Partial = { walletMode: "seed" @@ -95,51 +100,58 @@ export const WalletImport: React.FC = ({ onComplete }) => { }; const submitForm = async (data: SeedFormValues) => { - if (!providerProcess.machines || providerProcess.machines.length === 0) { - setError("No machine information available"); - } setIsLoading(true); setError(null); try { - const publicKey = providerProcess.machines[0].systemInfo.public_key; - const keyId = providerProcess.machines[0].systemInfo.key_id; - const encryptedSeedPhrase = await encrypt(data.seedPhrase, publicKey); + if (providerProcess.machines && providerProcess.machines.length > 0) { + const publicKey = providerProcess.machines[0].systemInfo.public_key; + const keyId = providerProcess.machines[0].systemInfo.key_id; + const encryptedSeedPhrase = await encrypt(data.seedPhrase, publicKey); - const finalRequest = { - wallet: { - key_id: keyId, - wallet_phrase: encryptedSeedPhrase - }, - nodes: providerProcess.machines.map(machine => ({ - hostname: machine.access.hostname, - port: machine.access.port, - username: machine.access.username, - keyfile: machine.access.file, - password: machine.access.password, - install_gpu_drivers: machine.systemInfo.gpu.count > 0 ? true : false - })), - provider: { - attributes: providerProcess.attributes, - pricing: providerProcess.pricing, - config: providerProcess.config - } - }; + const finalRequest = { + wallet: { + key_id: keyId, + wallet_phrase: encryptedSeedPhrase + }, + nodes: providerProcess.machines.map(machine => ({ + hostname: machine.access.hostname, + port: machine.access.port, + username: machine.access.username, + keyfile: machine.access.file, + password: machine.access.password, + install_gpu_drivers: machine.systemInfo.gpu.count > 0 ? true : false + })), + provider: { + attributes: providerProcess.attributes, + pricing: providerProcess.pricing, + config: providerProcess.config + } + }; - const response: any = await restClient.post("/build-provider", finalRequest, { - headers: { "Content-Type": "application/json" } - }); + const response: any = await restClient.post("/build-provider", finalRequest, { + headers: { "Content-Type": "application/json" } + }); - if (response.action_id) { - resetProviderProcess(); - router.push(`/action?id=${response.action_id}`); + if (response.action_id) { + const machineWithAddress: ControlMachineWithAddress = { + address: address, + ...providerProcess.machines[0] + }; + await setControlMachine(machineWithAddress); + resetProviderProcess(); + router.push(`/action?id=${response.action_id}`); + } else { + throw new Error("Invalid response from server"); + } } else { - throw new Error("Invalid response from server"); + throw new Error("No machine information available"); } } catch (error) { + console.error("Error during wallet verification:", error); setError("An error occurred while processing your request. Please try again."); } finally { - onComplete(); setIsLoading(false); + onComplete(); } }; diff --git a/apps/provider-console/src/components/home/HomeContainer.tsx b/apps/provider-console/src/components/home/HomeContainer.tsx index a0aca8862..8f9ba9105 100644 --- a/apps/provider-console/src/components/home/HomeContainer.tsx +++ b/apps/provider-console/src/components/home/HomeContainer.tsx @@ -12,14 +12,10 @@ import { WalletNotConnected } from "./WalletNotConnected"; export const HomeContainer: React.FC = () => { const router = useRouter(); - const { isWalletConnected, isWalletArbitrarySigned, isProvider, isOnline, isProviderStatusFetched } = useWallet(); - const [isLoading, setIsLoading] = useState(false); + const { isWalletConnected, isProvider, isOnline, isProviderStatusFetched } = useWallet(); + const [isLoading] = useState(false); const { data: providerActions } = useProviderActions(); - useEffect(() => { - setIsLoading(true); - }, [isProvider, isOnline, isWalletArbitrarySigned]); - useEffect(() => { if (isWalletConnected && isProvider) { router.push("/dashboard"); diff --git a/apps/provider-console/src/components/layout/Sidebar.tsx b/apps/provider-console/src/components/layout/Sidebar.tsx index 28b1bad51..c476aae7f 100644 --- a/apps/provider-console/src/components/layout/Sidebar.tsx +++ b/apps/provider-console/src/components/layout/Sidebar.tsx @@ -1,6 +1,6 @@ "use client"; import React, { ReactNode, useState } from "react"; -import { Button, buttonVariants } from "@akashnetwork/ui/components"; +import { Button, buttonVariants, Spinner } from "@akashnetwork/ui/components"; import Drawer from "@mui/material/Drawer"; import { useTheme as useMuiTheme } from "@mui/material/styles"; import useMediaQuery from "@mui/material/useMediaQuery"; @@ -10,6 +10,7 @@ import getConfig from "next/config"; import Image from "next/image"; import Link from "next/link"; +import { useControlMachine } from "@src/context/ControlMachineProvider"; import { useWallet } from "@src/context/WalletProvider"; import { ISidebarGroupMenu } from "@src/types"; import { closedDrawerWidth, drawerWidth } from "@src/utils/constants"; @@ -35,6 +36,8 @@ export const Sidebar: React.FC = ({ isMobileOpen, handleDrawerToggle, isN const muiTheme = useMuiTheme(); const smallScreen = useMediaQuery(muiTheme.breakpoints.down("md")); + const { activeControlMachine, openControlMachineDrawer, controlMachineLoading } = useControlMachine(); + const routeGroups: ISidebarGroupMenu[] = [ { hasDivider: false, @@ -162,6 +165,37 @@ export const Sidebar: React.FC = ({ isMobileOpen, handleDrawerToggle, isN {_isNavOpen && (
{/* */} + {controlMachineLoading ? ( +
+
+ Machine: +
+ +
Connecting...
+
+
+
+ ) : activeControlMachine ? ( +
+
+ Machine: +
+
+ {activeControlMachine.access.hostname} +
+
+
+ ) : ( +
+
+ Machine: +
+
+
Not Connected
+
+
+
+ )}
= ({ isMobileOpen, handleDrawerToggle, isN return (