diff --git a/Dockerfile b/Dockerfile index 8383ae180..5a7ac1ae0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,9 @@ -FROM node:20.8.1-alpine as remix +FROM node:20.8.1-alpine AS remix WORKDIR /app COPY ./package-production.json ./package.json RUN npm i --frozen-lockfile -FROM node:20.8.1-alpine as install +FROM node:20.8.1-alpine AS install RUN npm i -g pnpm WORKDIR /app COPY ./package.json ./package.json @@ -19,7 +19,7 @@ COPY ./src/generated/plugin/package.json ./src/generated/plugin/pnpm-lock.yaml RUN pnpm i -p --frozen-lockfile -FROM node:20.8.1-alpine as build +FROM node:20.8.1-alpine AS build RUN npm i -g pnpm WORKDIR /app ARG APP diff --git a/src/apps/console/components/alert-modal.tsx b/src/apps/console/components/alert-modal.tsx index 082d98375..aae883a60 100644 --- a/src/apps/console/components/alert-modal.tsx +++ b/src/apps/console/components/alert-modal.tsx @@ -13,6 +13,7 @@ export interface IAlertModal { title: ReactNode; okText?: string; okDisabled?: boolean; + showOkButton?: boolean; cancelText?: string; variant?: ButtonVariants; footer?: boolean; @@ -28,6 +29,7 @@ const AlertModal = ({ title, okDisabled = false, okText = 'Delete', + showOkButton = true, cancelText = 'Cancel', variant = 'critical', }: IAlertModal) => { @@ -55,14 +57,16 @@ const AlertModal = ({ {footer && ( - + {showOkButton && ( + + )} )} diff --git a/src/apps/console/hooks/use-cluster-status-v2.tsx b/src/apps/console/hooks/use-cluster-status-v2.tsx deleted file mode 100644 index 485b0d2ad..000000000 --- a/src/apps/console/hooks/use-cluster-status-v2.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import { useParams } from '@remix-run/react'; -import { - Dispatch, - ReactNode, - SetStateAction, - createContext, - useCallback, - useContext, - useEffect, - useMemo, - useState, -} from 'react'; -import { useSocketWatch } from '~/root/lib/client/helpers/socket/useWatch'; -import useDebounce from '~/root/lib/client/hooks/use-debounce'; -import { useConsoleApi } from '../server/gql/api-provider'; -import { IClustersStatus } from '../server/gql/queries/cluster-queries'; -import { ExtractNodeType, parseNodes } from '../server/r-utils/common'; - -type IClusterMap = { [key: string]: ExtractNodeType }; - -const ClusterStatusContext = createContext<{ - clusters: IClusterMap; - setClusters: Dispatch>; -}>({ clusters: {}, setClusters: () => {} }); - -const ClusterStatusProvider = ({ children }: { children: ReactNode }) => { - const [clusters, setClusters] = useState({}); - const api = useConsoleApi(); - const [update, setUpdate] = useState(false); - - const { account } = useParams(); - - const topic = useCallback(() => { - return Object.keys(clusters).map((c) => `account:${account}.cluster:${c}`); - }, [clusters])(); - - const listCluster = useCallback(async () => { - try { - const { data } = await api.listClusterStatus({ - pagination: { - first: 500, - }, - search: { - allClusters: { - exact: true, - matchType: 'exact', - }, - }, - }); - const parsed = parseNodes(data).reduce((acc, c) => { - acc[c.metadata.name] = c; - return acc; - }, {} as IClusterMap); - setClusters(parsed); - return parsed; - } catch (err) { - console.error(err); - return false; - } - }, []); - - useEffect(() => { - const interval = setInterval(() => { - listCluster(); - }, 30 * 1000); - - const onlineEvent = () => { - setTimeout(() => { - listCluster(); - }, 3000); - }; - - window.addEventListener('online', onlineEvent); - - return () => { - clearInterval(interval); - window.removeEventListener('online', onlineEvent); - }; - }, []); - - useDebounce( - () => { - listCluster(); - }, - 3000, - [update] - ); - - useSocketWatch(() => { - setUpdate((p) => !p); - }, topic); - - return ( - ({ clusters, setClusters }), - [clusters, setClusters] - )} - > - {children} - - ); -}; - -export default ClusterStatusProvider; - -export const useClusterStatusV2 = () => { - return useContext(ClusterStatusContext); -}; diff --git a/src/apps/console/hooks/use-cluster-status-v3.tsx b/src/apps/console/hooks/use-cluster-status-v3.tsx index 0bd2e77e7..f6a4783fa 100644 --- a/src/apps/console/hooks/use-cluster-status-v3.tsx +++ b/src/apps/console/hooks/use-cluster-status-v3.tsx @@ -1,3 +1,4 @@ +import { useOutletContext, useParams } from '@remix-run/react'; import { createContext, useCallback, @@ -7,29 +8,35 @@ import { useState, } from 'react'; import { ChildrenProps } from '~/components/types'; -import useDebounce from '~/root/lib/client/hooks/use-debounce'; import { useSocketWatch } from '~/root/lib/client/helpers/socket/useWatch'; -import { useParams } from '@remix-run/react'; +import useDebounce from '~/root/lib/client/hooks/use-debounce'; +import { IAccountContext } from '../routes/_main+/$account+/_layout'; import { useConsoleApi } from '../server/gql/api-provider'; const ctx = createContext<{ - clusters: { - [key: string]: string; - }; - setClusters: React.Dispatch>; + // clusters: { + // [key: string]: string; + // }; + // setClusters: React.Dispatch>; addToWatchList: (clusterNames: string[]) => void; removeFromWatchList: (clusterNames: string[]) => void; }>({ - clusters: {}, - setClusters: () => {}, + // clusters: {}, + // setClusters: () => {}, addToWatchList: () => {}, removeFromWatchList: () => {}, }); -const ClusterStatusProvider = ({ children }: ChildrenProps) => { - const [clusters, setClusters] = useState<{ - [key: string]: string; - }>({}); +const ClusterStatusProvider = ({ + children, + clustersMap, + setClustersMap, +}: ChildrenProps & { + clustersMap: { [key: string]: string }; + setClustersMap: React.Dispatch< + React.SetStateAction<{ [key: string]: string }> + >; +}) => { const [watchList, setWatchList] = useState<{ [key: string]: number; }>({}); @@ -57,38 +64,43 @@ const ClusterStatusProvider = ({ children }: ChildrenProps) => { const caller = (wl: { [key: string]: number }) => { const keys = Object.keys(wl); - // console.log('nayak', wl, keys, Object.entries(wl)); - for (let i = 0; i < keys.length; i += 1) { - (async () => { - const w = keys[i]; - try { - const { data: cluster } = await api.getClusterStatus({ - name: w, - }); - setClusters((s) => { - return { - ...s, - [w]: cluster.lastOnlineAt, - }; - }); - } catch (e) { - console.log('error', e); - } - })(); - } + + (async () => { + try { + const { data: clustersStatus } = await api.listClusterStatus({ + pagination: { + first: 100, + }, + search: { + allClusters: { + exact: true, + matchType: 'exact', + }, + text: { + array: keys, + matchType: 'array', + }, + }, + }); + + setClustersMap((s) => { + return { + ...s, + ...clustersStatus, + }; + }); + } catch (e) { + console.log('error', e); + } + })(); }; useEffect(() => { - const t2 = setTimeout(() => { - caller(watchList); - }, 1000); - const t = setInterval(() => { caller(watchList); }, 30 * 1000); return () => { - clearTimeout(t2); clearInterval(t); }; }, [watchList]); @@ -96,8 +108,10 @@ const ClusterStatusProvider = ({ children }: ChildrenProps) => { const { account } = useParams(); const topic = useCallback(() => { - return Object.keys(clusters).map((c) => `account:${account}.cluster:${c}`); - }, [clusters])(); + return Object.keys(clustersMap).map( + (c) => `account:${account}.cluster:${c}` + ); + }, [clustersMap])(); useSocketWatch(() => { caller(watchList); @@ -128,12 +142,10 @@ const ClusterStatusProvider = ({ children }: ChildrenProps) => { ({ - clusters, - setClusters, addToWatchList, removeFromWatchList, }), - [clusters, setClusters] + [] )} > {children} @@ -150,7 +162,8 @@ export const useClusterStatusV3 = ({ clusterName?: string; clusterNames?: string[]; }) => { - const { clusters, addToWatchList, removeFromWatchList } = useContext(ctx); + const { clustersMap } = useOutletContext(); + const { addToWatchList, removeFromWatchList: _ } = useContext(ctx); useDebounce( () => { if (!clusterName && !clusterNames) { @@ -164,11 +177,11 @@ export const useClusterStatusV3 = ({ } return () => { - if (clusterName) { - removeFromWatchList([clusterName]); - } else if (clusterNames) { - removeFromWatchList(clusterNames); - } + // if (clusterName) { + // removeFromWatchList([clusterName]); + // } else if (clusterNames) { + // removeFromWatchList(clusterNames); + // } }; }, 100, @@ -176,6 +189,6 @@ export const useClusterStatusV3 = ({ ); return { - clusters, + clustersMap, }; }; diff --git a/src/apps/console/page-components/app/compute.tsx b/src/apps/console/page-components/app/compute.tsx index b415a4256..0682a7365 100644 --- a/src/apps/console/page-components/app/compute.tsx +++ b/src/apps/console/page-components/app/compute.tsx @@ -1,5 +1,4 @@ import { useEffect, useState } from 'react'; -import { Button } from '~/components/atoms/button'; import { NumberInput } from '~/components/atoms/input'; import Select from '~/components/atoms/select'; import Slider from '~/components/atoms/slider'; @@ -257,7 +256,7 @@ const AppCompute = ({ mode = 'new' }: { mode: 'edit' | 'new' }) => { )}
-
)} @@ -405,7 +405,9 @@ const GridView = ({ items = [], onEdit, onDelete, onShowLogs }: IResource) => { }; const ListView = ({ items = [], onEdit, onDelete, onShowLogs }: IResource) => { const { account } = useParams(); - const { clusters } = useClusterStatusV2(); + const { clustersMap: clustersStatus } = useClusterStatusV3({ + clusterNames: items.map((i) => parseName(i)), + }); return ( { }, ], rows: items.map((i) => { - const { name, id, updateInfo, provider } = parseItem(i); + const { name, id, updateInfo } = parseItem(i); // const isLatest = dayjs(i.updateTime).isAfter( // dayjs().subtract(3, 'hour') @@ -473,7 +475,7 @@ const ListView = ({ items = [], onEdit, onDelete, onShowLogs }: IResource) => { return ( ); @@ -481,7 +483,7 @@ const ListView = ({ items = [], onEdit, onDelete, onShowLogs }: IResource) => { }, status: { render: () => ( - + ), }, updated: { diff --git a/src/apps/console/routes/_main+/$account+/infra+/clusters/handle-cluster-resource.tsx b/src/apps/console/routes/_main+/$account+/infra+/clusters/handle-cluster-resource.tsx new file mode 100644 index 000000000..7fa87be94 --- /dev/null +++ b/src/apps/console/routes/_main+/$account+/infra+/clusters/handle-cluster-resource.tsx @@ -0,0 +1,63 @@ +import { useParams } from '@remix-run/react'; +import { Button } from '~/components/atoms/button'; +import Popup from '~/components/molecule/popup'; +import CodeView from '~/console/components/code-view'; +import { ensureAccountClientSide } from '~/console/server/utils/auth-utils'; + +export const LocalDeviceClusterInstructions = ({ + show, + onClose, +}: { + show: boolean; + onClose: () => void; +}) => { + const params = useParams(); + ensureAccountClientSide(params); + + return ( + + Instructions to add your local device + +
+
+ + 1. Download and install kloudlite cli: + + + + + 2. Login to your account: + + + + + 3. Attach Compute: + + + + {/* {data.url} */} +
+
+
+ +