diff --git a/frontend/providers/kubepanel/src/components/common/sidebar/sidebar.tsx b/frontend/providers/kubepanel/src/components/common/sidebar/sidebar.tsx
index 64ec359c2..70355d3ae 100644
--- a/frontend/providers/kubepanel/src/components/common/sidebar/sidebar.tsx
+++ b/frontend/providers/kubepanel/src/components/common/sidebar/sidebar.tsx
@@ -18,7 +18,8 @@ export enum SideNavItemKey {
PersistentVolumeClaim = 'volume-claim',
StatefulSet = 'stateful-set',
Secret = 'secret',
- Ingress = 'ingress'
+ Ingress = 'ingress',
+ Service = 'service'
}
function getItem(
@@ -48,7 +49,10 @@ const items: MenuProps['items'] = [
getItem('Config Maps', SideNavItemKey.ConfigMap),
getItem('Secrets', SideNavItemKey.Secret)
]),
- getItem('Network', 'network', , [getItem('Ingress', SideNavItemKey.Ingress)]),
+ getItem('Network', 'network', , [
+ getItem('Ingress', SideNavItemKey.Ingress),
+ getItem('Service', SideNavItemKey.Service)
+ ]),
getItem('Storage', 'storage', , [
getItem('Persistent Volume Claims', SideNavItemKey.PersistentVolumeClaim)
])
diff --git a/frontend/providers/kubepanel/src/constants/kube-object.ts b/frontend/providers/kubepanel/src/constants/kube-object.ts
index 1ac6a1ed4..7b5251fab 100644
--- a/frontend/providers/kubepanel/src/constants/kube-object.ts
+++ b/frontend/providers/kubepanel/src/constants/kube-object.ts
@@ -6,5 +6,6 @@ export enum KubeObjectKind {
PersistentVolumeClaim = 'PersistentVolumeClaim',
Secret = 'Secret',
Ingress = 'Ingress',
- Event = 'Event'
+ Event = 'Event',
+ Service = 'Service'
}
diff --git a/frontend/providers/kubepanel/src/pages/kubepanel/index.tsx b/frontend/providers/kubepanel/src/pages/kubepanel/index.tsx
index 12636e382..4fada9931 100644
--- a/frontend/providers/kubepanel/src/pages/kubepanel/index.tsx
+++ b/frontend/providers/kubepanel/src/pages/kubepanel/index.tsx
@@ -12,6 +12,7 @@ import { CreateResourceModal } from '@/components/common/action/create-resource-
import { PlusOutlined } from '@ant-design/icons';
import SecretOverviewPage from './kube-object/config/secret/secret';
import IngressOverviewPage from './kube-object/network/ingress/ingress';
+import ServiceOverviewPage from './kube-object/network/service/service';
const switchPage = (key: SideNavItemKey): React.ReactNode => {
switch (key) {
@@ -31,6 +32,8 @@ const switchPage = (key: SideNavItemKey): React.ReactNode => {
return ;
case SideNavItemKey.Ingress:
return ;
+ case SideNavItemKey.Service:
+ return ;
default:
return ;
}
diff --git a/frontend/providers/kubepanel/src/pages/kubepanel/kube-object/network/service/service-detail.tsx b/frontend/providers/kubepanel/src/pages/kubepanel/kube-object/network/service/service-detail.tsx
new file mode 100644
index 000000000..bffe0a2c1
--- /dev/null
+++ b/frontend/providers/kubepanel/src/pages/kubepanel/kube-object/network/service/service-detail.tsx
@@ -0,0 +1,87 @@
+import { KubeObjectInfoList } from '@/components/kube/object/detail/kube-object-detail-info-list';
+import {
+ ComputedIngressRoute,
+ ILoadBalancerIngress,
+ Service,
+ computeRuleDeclarations
+} from '@/k8slens/kube-object';
+import { DrawerPanel } from '@/components/common/drawer/drawer-panel';
+import { Drawer } from '@/components/common/drawer/drawer';
+import { DrawerItem } from '@/components/common/drawer/drawer-item';
+import { Button, Table } from 'antd';
+import { ColumnsType } from 'antd/lib/table';
+
+const rulesColumns: ColumnsType = [
+ {
+ key: 'path',
+ title: 'Path',
+ dataIndex: 'pathname',
+ render: (pathname: string) => (pathname === '' ? '_' : pathname)
+ },
+ {
+ key: 'link',
+ title: 'Link',
+ render: (_, { displayAsLink, url }) =>
+ displayAsLink ? (
+
+ ) : (
+ url
+ )
+ },
+ {
+ key: 'backends',
+ title: 'Backends',
+ dataIndex: 'service'
+ }
+];
+
+const pointsColumns: ColumnsType = [
+ {
+ key: 'hostname',
+ title: 'Hostname',
+ dataIndex: 'hostname',
+ render: (hostname?: string) => (hostname ? '_' : hostname)
+ },
+ {
+ key: 'ip',
+ title: 'IP',
+ dataIndex: 'ip',
+ render: (ip?: string) => (ip ? '_' : ip)
+ }
+];
+
+const IngressPoints = ({ points }: { points?: ILoadBalancerIngress[] }) => {
+ if (!points || points.length === 0) {
+ return null;
+ }
+
+ return (
+
+ );
+};
+
+const ServiceDetail = ({ obj: service, open, onClose }: DetailDrawerProps) => {
+ if (!service || !(service instanceof Service)) return null;
+ return (
+
+
+
+
+
+
+ port.toString()).join(", ")} />
+ {/* Add other relevant details here */}
+
+
+)
+};
+
+export default ServiceDetail;
diff --git a/frontend/providers/kubepanel/src/pages/kubepanel/kube-object/network/service/service.tsx b/frontend/providers/kubepanel/src/pages/kubepanel/kube-object/network/service/service.tsx
new file mode 100644
index 000000000..fe9475bac
--- /dev/null
+++ b/frontend/providers/kubepanel/src/pages/kubepanel/kube-object/network/service/service.tsx
@@ -0,0 +1,66 @@
+import { KubeObjectAge } from '@/components/kube/object/kube-object-age';
+import { KubeObject,KubeObjectMetadata,KubeObjectScope,Service, computeRouteDeclarations } from '@/k8slens/kube-object';
+import { ColumnsType } from 'antd/lib/table';
+import { useServiceStore } from '@/store/kube';
+import { Button } from 'antd';
+import ServiceDetail from './service-detail';
+import { PanelTable } from '@/components/common/panel-table/table';
+import { ActionButton } from '@/components/common/action/action-button';
+
+const columns: ColumnsType = [
+ {
+ title: 'ClusterIps',
+ key: 'ClusterIps',
+ render: (_, service: Service) => service.getClusterIps().map(ip => {ip}
)
+ },
+ {
+ title: 'Type',
+ key: 'type',
+ render: (_, service: Service) => {service.getType()}
+ },
+ {
+ title: 'Selector',
+ key: 'selector',
+ render: (_, service: Service) => service.getSelector().map(selector => {selector}
)
+ },
+ {
+ title: 'Ports',
+ key: 'ports',
+ render: (_, service: Service) =>
+ service.getPorts().map(port => (
+
+ {port.toString()}
+
+ ))
+ },
+ {
+ title: 'Load Balancer IPs',
+ key: 'load-balancer-ips',
+ render: (_, service: Service) => service.getExternalIps().map(ip => {ip}
)
+ },
+ {
+ title: 'Status',
+ key: 'status',
+ render: (_, service: Service) => {service.getStatus()}
+ },
+];
+
+const ServiceOverviewPage = () => {
+ const { items, initialize, isLoaded, watch } = useServiceStore();
+
+ return (
+ service.getId()}
+ initializers={[initialize]}
+ watchers={[watch]}
+ getDetailItem={(service) => service}
+ />
+ );
+};
+
+export default ServiceOverviewPage;
diff --git a/frontend/providers/kubepanel/src/services/backend/api.ts b/frontend/providers/kubepanel/src/services/backend/api.ts
index 145b01c7e..9e191f58a 100644
--- a/frontend/providers/kubepanel/src/services/backend/api.ts
+++ b/frontend/providers/kubepanel/src/services/backend/api.ts
@@ -73,5 +73,10 @@ const ApiBaseParamsRecord: Record = {
apiPrefix: 'api',
apiVersion: 'v1',
resource: 'events'
+ },
+ [KubeObjectKind.Service]: {
+ apiPrefix: 'api',
+ apiVersion: 'v1',
+ resource: 'services'
}
};
diff --git a/frontend/providers/kubepanel/src/store/k8s/service.store.ts b/frontend/providers/kubepanel/src/store/k8s/service.store.ts
new file mode 100644
index 000000000..0f2df3778
--- /dev/null
+++ b/frontend/providers/kubepanel/src/store/k8s/service.store.ts
@@ -0,0 +1,8 @@
+import { ServiceStore} from '@/types/state';
+import { create } from 'zustand';
+import { createKubeStoreSlice } from './kube.store';
+import { Service } from '@/k8slens/kube-object';
+
+export const useServiceStore = create()((...a) => ({
+ ...createKubeStoreSlice(Service.kind, Service)(...a)
+}));
diff --git a/frontend/providers/kubepanel/src/store/kube.ts b/frontend/providers/kubepanel/src/store/kube.ts
index 7a06c6ae1..0165bfaa8 100644
--- a/frontend/providers/kubepanel/src/store/kube.ts
+++ b/frontend/providers/kubepanel/src/store/kube.ts
@@ -6,3 +6,4 @@ export * from './k8s/volume-claim.store';
export * from './k8s/ingress.store';
export * from './k8s/secret.store';
export * from './k8s/kube.store';
+export * from './k8s/service.store';
diff --git a/frontend/providers/kubepanel/src/types/state.d.ts b/frontend/providers/kubepanel/src/types/state.d.ts
index 80c0f10be..5a670f476 100644
--- a/frontend/providers/kubepanel/src/types/state.d.ts
+++ b/frontend/providers/kubepanel/src/types/state.d.ts
@@ -45,3 +45,4 @@ type VolumeClaimStore = KubeStore;
type SecretStore = KubeStore;
type IngressStore = KubeStore;
type EventStore = KubeStore;
+type ServiceStore = KubeStore;
\ No newline at end of file