Skip to content

Commit

Permalink
feat(provider): control-machine add/edit - online/offline functionali…
Browse files Browse the repository at this point in the history
…ty (#447)

* added server-access and server-form files

* added wallet import page - wip, fixed issue with file upload in server access forms

* added become-provider steps ui

* server access step added with state support

* added provider config and provider attribute screen

* added provider process with hoc to prevent access to pages

* added progress for becoming prvider for final stage

* Code clean up and added navigation logic to homecontainer

* package lock updated

* more cleanup and remove general warnings

* removed unused npm package

* fix minor error on api

* Added dashboard and actions page

* change status api endpoint

* added stat line and pie charts

* Added console apis to get dashboard data and show appropriate details

* fixed actions and changed home component

* token varification and refresh token fix

* changed wallet connect from wallet status to wallet provider

* fixed issue in loading provider status

* fixed home loading issue

* fixed refresh token, added disabled menu items

* fixed build process

* feat(provider): added sentry and docker

* fix(provider): fixed wallet switching and getting status

* feat(provider): reduced number of events in dashboard

* feat(provider): added docker compose changes for provider-console

* feat(provider): added deployments and deployment detail page

* fix(provider): change hours to seconds for calculation purpose)

* feat(provider): added auth for deployments and deployment details page

* feat(provider): added env and removed settingsprovider

* fix(provider): fix lint errors and removed console.logs

* fix(provider): become-provider looped, fixed it

* fix(provider): router and reset process fixed

* fix(provider): removed Get Started button for now

* fix(provider): removed unused import in nav

* fix(provider): change functions to react fc component

* fix(provider): fix lint issues

* fix(provider): change functions to react fc component

* fix(provider): added docker build and fix build related issues

* feat(provider): control machine edit, add from sidebar

* fix(provider): control machine auto connect on page load

* fix(provider): fix loading not showing while connecting provider control machine

* fix(provider): close drawer on successfull connection

* feat(provider): change favicon to akash favicon

* fix(provider): merge issues with main

* chore: removed comments
  • Loading branch information
jigar-arc10 authored Nov 26, 2024
1 parent 2da4828 commit ec70124
Show file tree
Hide file tree
Showing 13 changed files with 293 additions and 68 deletions.
Binary file modified apps/provider-console/public/favicon.ico
Binary file not shown.
56 changes: 35 additions & 21 deletions apps/provider-console/src/components/become-provider/ServerForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -55,12 +58,16 @@ type AccountFormValues = z.infer<typeof accountFormSchema>;
interface ServerFormProp {
currentServerNumber: number;
onComplete: () => void;
editMode?: boolean;
controlMachine?: ControlMachineWithAddress | null;
}

export const ServerForm: React.FC<ServerFormProp> = ({ currentServerNumber, onComplete }) => {
export const ServerForm: React.FC<ServerFormProp> = ({ currentServerNumber, onComplete, editMode = false, controlMachine }) => {
const [providerProcess, setProviderProcess] = useAtom(providerProcessStore.providerProcessAtom);
const [selectedFile, setSelectedFile] = useState<File | null>(null);
const [storedFileContent, setStoredFileContent] = useState<string | null>(null);
const { setControlMachine } = useControlMachine();
const { address } = useWallet();

const getDefaultValues = () => {
if (currentServerNumber === 0 || !providerProcess?.storeInformation) {
Expand All @@ -84,18 +91,18 @@ export const ServerForm: React.FC<ServerFormProp> = ({ currentServerNumber, onCo

const form = useForm<AccountFormValues>({
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);
Expand Down Expand Up @@ -127,7 +134,7 @@ export const ServerForm: React.FC<ServerFormProp> = ({ 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" }
});
Expand All @@ -152,21 +159,29 @@ export const ServerForm: React.FC<ServerFormProp> = ({ 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) {
Expand Down Expand Up @@ -206,10 +221,9 @@ export const ServerForm: React.FC<ServerFormProp> = ({ currentServerNumber, onCo
<div className="space-y-6">
<div>
<h3 className="text-xl font-bold">
{currentServerNumber === 0 && "Control Plane Machine Access"}
{currentServerNumber !== 0 && "Node Access"}
{editMode ? "Control Machine Access" : currentServerNumber === 0 ? "Control Plane Machine Access" : "Node Access"}
</h3>
<p className="text-muted-foreground text-sm">Enter the required details for your control plane setup</p>
<p className="text-muted-foreground text-sm">Enter the required details for your {editMode ? "control machine" : "control plane setup"}</p>
</div>
<div>
<Separator />
Expand Down Expand Up @@ -345,16 +359,16 @@ export const ServerForm: React.FC<ServerFormProp> = ({ currentServerNumber, onCo
</div>
<div className="flex justify-end">
<div className="flex w-full justify-between">
<div className="flex justify-start">
<ResetProviderForm />
</div>
<div className="flex justify-start">{!editMode && <ResetProviderForm />}</div>
<div className="flex justify-end">
<Button type="submit" disabled={isVerifying}>
{isVerifying ? (
<>
<Spinner />
Verifying...
</>
) : editMode ? (
"Update"
) : (
"Next"
)}
Expand All @@ -379,7 +393,7 @@ export const ServerForm: React.FC<ServerFormProp> = ({ currentServerNumber, onCo
</Alert>
)}
</div>
{currentServerNumber === 0 && (
{currentServerNumber === 0 && !editMode && (
<div className="rounded-md border">
<div className="space-y-2 p-4">
<h4 className="text-lg font-bold">Heads up!</h4>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -77,6 +80,8 @@ export const WalletImport: React.FC<WalletImportProps> = ({ onComplete }) => {

const [providerProcess] = useAtom(providerProcessStore.providerProcessAtom);
const [, resetProviderProcess] = useAtom(providerProcessStore.resetProviderProcess);
const { setControlMachine } = useControlMachine();
const { address } = useWallet();

const defaultValues: Partial<AppearanceFormValues> = {
walletMode: "seed"
Expand All @@ -95,51 +100,58 @@ export const WalletImport: React.FC<WalletImportProps> = ({ 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();
}
};

Expand Down
8 changes: 2 additions & 6 deletions apps/provider-console/src/components/home/HomeContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
38 changes: 36 additions & 2 deletions apps/provider-console/src/components/layout/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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";
Expand All @@ -35,6 +36,8 @@ export const Sidebar: React.FC<Props> = ({ isMobileOpen, handleDrawerToggle, isN
const muiTheme = useMuiTheme();
const smallScreen = useMediaQuery(muiTheme.breakpoints.down("md"));

const { activeControlMachine, openControlMachineDrawer, controlMachineLoading } = useControlMachine();

const routeGroups: ISidebarGroupMenu[] = [
{
hasDivider: false,
Expand Down Expand Up @@ -162,6 +165,37 @@ export const Sidebar: React.FC<Props> = ({ isMobileOpen, handleDrawerToggle, isN
{_isNavOpen && (
<div className="space-y-2 pb-4 pl-4 pr-4">
{/* <NodeStatusBar /> */}
{controlMachineLoading ? (
<div className="flex flex-col space-y-2">
<div className="text-muted-foreground flex items-center gap-2 text-sm">
Machine:
<div className="relative flex items-center gap-2">
<Spinner size="small" />
<div className="text-xs">Connecting...</div>
</div>
</div>
</div>
) : activeControlMachine ? (
<div className="flex flex-col space-y-2">
<div className="text-muted-foreground hover:text-foreground flex cursor-pointer items-center gap-2 text-sm" onClick={openControlMachineDrawer}>
Machine:
<div className="relative flex items-center gap-2">
<div className="h-2 w-2 rounded-full bg-green-500" />
{activeControlMachine.access.hostname}
</div>
</div>
</div>
) : (
<div className="flex flex-col space-y-2">
<div className="text-muted-foreground hover:text-foreground flex cursor-pointer items-center gap-2 text-sm" onClick={openControlMachineDrawer}>
Machine:
<div className="relative flex items-center gap-2">
<div className="h-2 w-2 rounded-full bg-red-500" />
<div className="roundedpx-2 py-1 text-xs">Not Connected</div>
</div>
</div>
</div>
)}

<div className="flex items-center justify-center space-x-1 pt-4">
<Link
Expand Down Expand Up @@ -228,7 +262,7 @@ export const Sidebar: React.FC<Props> = ({ isMobileOpen, handleDrawerToggle, isN

return (
<nav
className={cn("ease bg-header/95 fixed z-[100] md:flex-shrink-0", {
className={cn("ease bg-header/95 fixed md:flex-shrink-0", {
["md:w-[240px]"]: _isNavOpen || isHovering,
["md:w-[57px]"]: !(_isNavOpen || isHovering)
})}
Expand Down
Loading

0 comments on commit ec70124

Please sign in to comment.