summary history files

frontend/src/App.vue
<script lang="ts">
import { defineComponent } from 'vue';
import { SecretUpdateOptions } from './types/custom';
import NavigationPane from './components/NavigationPane.vue';
import DeploymentsList from './components/DeploymentsList.vue';
import StatefulSetsList from './components/StatefulsetsList.vue';
import PodsList from './components/PodsList.vue';
import ConfigMapsList from './components/ConfigMapsList.vue';
import IngressesList from './components/IngressesList.vue';
import NamespacesList from './components/NamespacesList.vue';
import SecretsList from './components/SecretsList.vue';
import PersistentVolumeClaimsList from './components/PersistentVolumeClaimsList.vue';
import PersistentVolumesList from './components/PersistentVolumesList.vue';
import BottomTabs from './components/BottomTabs.vue';
import { CustomPod } from './types/custom';
import { KubernetesPod } from './types/kubernetes';
import { kubernetesService } from './lib/kubernetes';
import {
  KubernetesCluster,
  KubernetesDeployment,
  KubernetesStatefulSet,
  KubernetesConfigMap,
  KubernetesIngress,
  KubernetesNamespace,
  KubernetesSecret,
  KubernetesPersistentVolumeClaim,
  KubernetesPersistentVolume,
  ApiResponse,
  } from './types/kubernetes';

export default defineComponent({
  components: {
    NavigationPane,
    DeploymentsList,
    StatefulSetsList,
    PodsList,
    ConfigMapsList,
    IngressesList,
    NamespacesList,
    SecretsList,
    PersistentVolumeClaimsList,
    PersistentVolumesList,
    BottomTabs
  },

  computed: {
    currentResourceComponent() {
      if (this.selectedResource === 'pods') {
        return 'PodsList';
      } else if (this.selectedResource === 'deployments') {
        return 'DeploymentsList';
      } else if (this.selectedResource === 'statefulsets') {
        return 'StatefulSetsList';
      } else if (this.selectedResource === 'configmaps') {
        return 'ConfigMapsList';
      } else if (this.selectedResource === 'namespaces') {
        return 'NamespacesList';
      } else if (this.selectedResource === 'secrets') {
        return 'SecretsList';
      } else if (this.selectedResource === 'persistentvolumeclaims') {
        return 'PersistentVolumeClaimsList';
      } else if (this.selectedResource === 'persistentVolumes') {
        return 'PersistentVolumesList';
      } else {
        return 'IngressesList';
      }
    }
  },

  data() {
    return {
      clusters: [] as KubernetesCluster[],
      selectedCluster: null as KubernetesCluster | null,
      selectedResource: null as string | null,
      selectedResourceItem: null as any | null,
      deployments: [] as KubernetesDeployment[],
      statefulSets: [] as KubernetesStatefulSet[],
      pods: [] as any[],
      configMaps: [] as KubernetesConfigMap[],
      ingresses: [] as KuberntesIngress[],
      namespaces: [] as KubernetesNamespaces[],
      secrets: [] as KubernetesSecret[],
      persistentvolumeclaims: [] as KubernetesPersistentVolumeClaim[],
      persistentVolumes: [] as KubernetesPersistentVolume[],
      selectedTab: 'logs' as 'logs' | 'terminal' | 'describe',
    };
  },

  async created() {
    await this.loadClusters();
  },

  methods: {
    handleStatefulSetSelect(statefulSet: KubernetesStatefulSet) {
      this.selectedResourceItem = statefulSet;
    },

    handleDeploymentSelect(deployment: KubernetesDeployment) {
      this.selectedResourceItem = deployment;
    },

    handlePodSelect(pod: any) {
      this.selectedResourceItem = pod;
    },

    handleConfigMapSelect(configMap: KubernetesConfigMap) {
      this.selectedResourceItem = configMap;
    },

    handleIngressSelect(ingress: KubernetesIngress) {
      this.selectedResourceItem = ingress;
    },

    handleNamespaceSelect(namespace: KubernetesNamespace) {
      this.selectedResourceItem = namespace;
    },

    handleSecretSelect(secret: KubernetesSecret) {
      this.selectedResourceItem = secret;
    },

    handlePersistentVolumeClaimSelect(persistentVolumeClaim: KubernetesPersistentVolumeClaim) {
      this.selectedResourceItem = persistentVolumeClaim;
    },

    handlePersistentVolumeSelect(persistentVolume: KubernetesPersistentVolume) {
      this.selectedResourceItem = persistentVolume;
    },

    async loadClusters() {
      try {
        const response = await kubernetesService.getClusters();
        if (response.success && response.data) {
          this.clusters = response.data.map(cluster => ({
            ...cluster,
            name: cluster.name
          }));
        }
      } catch (error) {
        console.error('Failed to load clusters:', error);
      }
    },

    getClusterName(server: string): string {
      try {
        const url = new URL(server);
        return url.hostname;
      } catch {
        return server;
      }
    },

    async handleUpdateSecret(opts: SecretUpdateOptions) {
      try {
        const response = await kubernetesService.updateSecret(opts.context, opts.definition, opts.origNamespace, opts.origName);
        if (response.success) {
          alert(response.msg);
          await this.loadResources();
        } else {
          throw new Error(response.msg);
        }
      } catch (error: any) {
        alert(`Error updating Secret: ${error}`);
      }
    },

    async handleDeleteSecret({cluster, secret}: { cluster: KubernetesCluster, secret: KubernetesSecret}) {
      try {
        const response = await kubernetesService.deleteSecret(cluster.contextName, secret.metadata.namespace, secret.metadata.name);
        if (response.success) {
          alert(response.msg);
          await this.loadResources();
        } else {
          throw new Error(response.msg);
        }
      } catch (error) {
        alert(`Error deleting Secret: ${error}`);
      }
    },

    async handleDeleteNamespace({cluster, namespace}: { cluster: KubernetesCluster, namespace: KubernetesNamespace }) {
      try {
        const response = await kubernetesService.deleteNamespace(cluster.contextName, namespace.metadata.name);
        if (response.success) {
          alert(`Namespace ${namespace.metadata.name} deleted successfully`);
          await this.loadResources();
        } else {
          throw new Error(response.msg || 'Unknown error');
        }
      } catch (error) {
        alert(`Error deleting Namespace: ${error}`);
      }
    },

    async handleDeletePersistentVolume({cluster, definition}: { cluster: KubernetesCluster, definition: KubernetesPersistentVolume}) {
      try {
        const response = await kubernetesService.deletePersistentVolume(cluster.contextName, definition.metadata.name);
        if (response.success) {
          alert(response.msg);
          await this.loadResources();
        } else {
          throw new Error(response.msg);
        }
      } catch (error) {
        alert(`Error deleting Persistent Volume: ${error}`);
      }
    },

    async handleCreateSecret(opts: SecretCreateOptions) {
      try {
        let response;
        response = await kubernetesService.createSecret(opts.context, opts.opts);
        if (response.success) {
          alert(response.msg)
          await this.loadResources();
        } else {
          throw new Error(response.msg);
        }
      } catch (error) {
        alert(`Error creating Secret: ${error}`);
      }
    },

    async handleCreatePersistentVolumeClaim({cluster, persistentVolumeClaim}: {cluster: KubernetesCluster, persistentVolumeClaim: KubernetesPersistentVolumeClaim}) {
      try {
        let response;
        response = await kubernetesService.createPersistentVolumeClaim(cluster.contextName, persistentVolumeClaim);
        if (response.success) {
          alert(response.msg)
          await this.loadResources();
        } else {
          throw new Error(response.msg);
        }
      } catch (error) {
        alert(`Error creating Persistent Volume Claim: ${error}`);
      }
    },

    async handleDeletePersistentVolumeClaim({cluster, persistentVolumeClaim}: { cluster: KubernetesCluster, persistentVolumeClaim: KubernetesPersistentVolumeClaim}) {
      try {
        const response = await kubernetesService.deletePersistentVolumeClaim(cluster.contextName, persistentVolumeClaim.metadata.namespace, persistentVolumeClaim.metadata.name);
        if (response.success) {
          alert(response.msg);
          await this.loadResources();
        } else {
          throw new Error(response.msg);
        }
      } catch (error) {
        alert(`Error deleting Persistent Volume Claim: ${error}`);
      }
    },

    async handleCreatePersistentVolume({cluster, definition}: {cluster: KubernetesCluster, definition: KubernetesPersistentVolume}) {
      try {
        let response;
        response = await kubernetesService.createPersistentVolume(cluster.contextName, definition);
        if (response.success) {
          alert(response.msg)
          await this.loadResources();
        } else {
          throw new Error(response.msg);
        }
      } catch (error) {
        alert(`Error creating Persistent Volume: ${error}`);
      }
    },

    async handleCreateNamespace(
      {cluster, definition, yamlContent, isYaml}: 
      {
        cluster: { contextName: string },
        definition?: any,
        yamlContent?: string,
        isYaml?: boolean,
      }
    ) {
      try {
        let response;
        if (isYaml && yamlContent) {
          response = await kubernetesService.createNamespaceYAML(cluster.contextName, yamlContent);
        } else if (!isYaml && definition) {
          response = await kubernetesService.createNamespace(cluster.contextName, definition);
        } else {
          throw new Error("Invalid namespace creation parameters");
        }

        if (response.success) {
          alert(response.msg)
          await this.loadResources();
        } else {
          throw new Error(response.msg);
        }
      } catch (error) {
        alert(`Error creating namespace: ${error}`);
      }
    },

    async handleCreatePod({
      cluster,
      podDefinition,
      yamlContent,
      isYaml
    }: {
      cluster: { contextName: string }, 
      podDefinition?: any,
      yamlContent?: string,
      isYaml?: boolean
    }) {
      try {
        let response;

        if (isYaml && yamlContent) {
          response = await kubernetesService.createPodYAML(cluster.contextName, yamlContent);
        } else if (!isYaml && podDefinition) {
          response = await kubernetesService.createPod(cluster.contextName, podDefinition);
        } else {
          throw new Error('Invalid pod creation parameters');
        }

        if (response.success) {
          alert(response.msg)
          await this.loadResources();
        } else {
          throw new Error(response.msg);
        }
      } catch (error) {
        console.error('Error creating pod:', error);
        alert(`Error creating pod: ${error}`);
      }
    },

    async handleDeletePod({cluster, pod}: { cluster: KubernetesCluster, pod: KubernetesPod }) {
      try {
        const response = await kubernetesService.deletePod(cluster.contextName, pod.metadata.namespace, pod.metadata.name);
        if (response.success) {
          alert(`Pod ${pod.metadata.name} deleted successfully`);
          await this.loadResources();
        } else {
          throw new Error(response.msg || 'Unknown error');
        }
      } catch (error) {
        console.error('Error deleting pod:', error);
        alert(`Error deleting pod: ${error}`);
      }
    },

    async handleDeleteIngress({cluster, ingress}: { cluster: KubernetesCluster, ingress: KubernetesIngress }) {
      try {
        const response = await kubernetesService.deleteIngress(cluster.contextName, ingress.metadata.namespace, ingress.metadata.name);
        if (response.success) {
          alert(`Ingress ${ingress.metadata.name} deleted successfully`);
          await this.loadResources();
        } else {
          throw new Error(response.msg);
        }
      } catch (error) {
        console.error('Error deleting ingress:', error);
        alert(`Error deleting ingress: ${error}`);
      }
    },

    async handleRestartDeployment({ cluster, deployment }: { cluster: KubernetesCluster, deployment: KubernetesDeployment }) {
      try {
        const response = await kubernetesService.restartDeployment(cluster.contextName, deployment.metadata.namespace, deployment.metadata.name);
        if (response.success) {
          alert(`Deployment ${deployment.metadata.name} restarted successfully`);
          await this.loadResources();
        } else {
          throw new Error(response.msg || 'Unknown error');
        }
      } catch (error) {
        alert(`Error restarting deployment: ${error}`);
      }
    },

    async handleRestartStatefulSet({ cluster, statefulSet }: { cluster: KubernetesCluster, statefulSet: KubernetesStatefulSet }) {
      try {
        const response = await kubernetesService.restartStatefulSet(cluster.contextName, statefulSet.metadata.namespace, statefulSet.metadata.name);
        if (response.success) {
          alert(`StatefulSet ${statefulSet.metadata.name} restarted successfully`);
          await this.loadResources();
        } else {
          throw new Error(response.msg || 'Unknown error');
        }
      } catch (error) {
        alert(`Error restarting statefulset: ${error}`);
      }
    },

    handleClusterSelect(cluster: KubernetesCluster) {
      this.selectedCluster = cluster;
      this.selectedResource = null;
      this.deployments = [];
      this.pods = [];
      this.statefulSets = [];
      this.configMaps = [];
      console.log("selected cluster: " + cluster.contextName);
    },

    handleResourceSelect(resource: string) {
      this.selectedResource = resource;
      this.selectedResourceItem = null; // Reset selected item when changing resource type
      this.loadResources();
    },

    handleResourceItemSelect(item: any) {
      this.selectedResourceItem = item;
    },

    handleTabChange(tab: 'logs' | 'terminal' | 'describe') {
      this.selectedTab = tab;
    },

    async loadResources() {
      if (!this.selectedCluster || !this.selectedResource) return;

      try {
        if (this.selectedResource === 'deployments') {
          try {
            const response = await kubernetesService.getDeployments(this.selectedCluster.contextName);
            if (response.success && response.data) {
              this.deployments = response.data;
            }
          } catch (error) {
            console.error('Failed to load deployments:', error);
          }
        } else if (this.selectedResource === 'pods') {
          // Clear existing pods first
          this.pods = [];
          try {
            const response = await kubernetesService.getPods(this.selectedCluster.contextName);
            if (response.success && response.data) {
              this.pods = response.data;
            }
          } catch (error) {
            console.error('Failed to load pods:', error);
          }
        } else if (this.selectedResource === 'statefulsets') {
          try {
            const response = await kubernetesService.getStatefulSets(this.selectedCluster.contextName);
            if (response.success && response.data) {
              this.statefulSets = response.data;
            }
          } catch (error) {
            console.error('Failed to load statefulsets:', error);
          }
        } else if (this.selectedResource === 'configmaps') {
          try {
            const response = await kubernetesService.getConfigMaps(this.selectedCluster.contextName);
            if (response.success && response.data) {
              this.configMaps = response.data;
            }
          } catch (error) {
            console.error('Failed to load configmaps:', error);
          }
        } else if (this.selectedResource === 'ingresses') {
          try {
            const response = await kubernetesService.getIngresses(this.selectedCluster.contextName);
            if (response.success && response.data) {
              this.ingresses = response.data;
            }
          } catch (error) {
            console.error('Failed to load ingresses:', error);
          }
        } else if (this.selectedResource === 'namespaces') {
          try {
            const response = await kubernetesService.getNamespaces(this.selectedCluster.contextName);
            if (response.success && response.data) {
              this.namespaces = response.data;
            }
          } catch (error) {
            console.error('Failed to load namespaces:', error);
          }
        } else if (this.selectedResource === 'secrets') {
          try {
            const response = await kubernetesService.listSecrets(this.selectedCluster.contextName);
            if (response.success && response.data) {
              this.secrets = response.data;
            }
          } catch (error) {
            console.error('Failed to load Secrets:', error);
          }
        } else if (this.selectedResource === 'persistentvolumeclaims') {
          try {
            const response = await kubernetesService.listPersistentVolumeClaims(this.selectedCluster.contextName);
            if (response.success && response.data) {
              this.persistentvolumeclaims = response.data;
            }
          } catch (error) {
            console.error('Failed to load Persistent Volume Claims:', error);
          }
        } else if (this.selectedResource === 'persistentVolumes') {
          try {
            const response = await kubernetesService.listPersistentVolumes(this.selectedCluster.contextName);
            if (response.success && response.data) {
              this.persistentVolumes = response.data;
            }
          } catch (error) {
            console.error('Failed to load Persistent Volumes:', error);
          }

          try {
            const response = await kubernetesService.listPersistentVolumeClaims(this.selectedCluster.contextName);
            if (response.success && response.data) {
              this.persistentvolumeclaims = response.data;
            }
          } catch (error) {
            console.error('Failed to load Persistent Volume Claims:', error);
          }
        }
      } catch (error) {
        console.error('Failed to load resources:', error);
      }
    },
  },
});
</script>

<template>
  <div class="app-container">
    <NavigationPane 
      :clusters="clusters"
      :selectedCluster="selectedCluster"
      :selectedResource="selectedResource"
      @select-cluster="handleClusterSelect"
      @select-resource="handleResourceSelect"
    />
    <div class="right-container">
      <component 
        :is="currentResourceComponent"
        :selectedCluster="selectedCluster"
        :deployments="deployments"
        :statefulSets="statefulSets"
        :configMaps="configMaps"
        :pods="pods"
        :ingresses="ingresses"
        :namespaces="namespaces"
        :secrets="secrets"
        :persistentvolumeclaims="persistentvolumeclaims"
        :persistentVolumes="persistentVolumes"
        @create-namespace="handleCreateNamespace"
        @create-pod="handleCreatePod"
        @create-secret="handleCreateSecret"
        @create-persistentvolume="handleCreatePersistentVolume"
        @create-persistentvolumeclaim="handleCreatePersistentVolumeClaim"
        @delete-ingress="handleDeleteIngress"
        @delete-namespace="handleDeleteNamespace"
        @delete-pod="handleDeletePod"
        @delete-secret="handleDeleteSecret"
        @delete-persistentvolume="handleDeletePersistentVolume"
        @delete-persistentvolumeclaim="handleDeletePersistentVolumeClaim"
        @load-resources="loadResources"
        @restart-deployment="handleRestartDeployment"
        @restart-statefulset="handleRestartStatefulSet"

        @pod-selected="handlePodSelect"
        @deployment-selected="handleDeploymentSelect"
        @configmap-selected="handleConfigMapSelect"
        @namespace-selected="handleNamespaceSelect"
        @ingress-selected="handleIngressSelect"
        @secret-selected="handleSecretSelect"
        @statefulset-selected="handleStatefulSetSelect"
        @persistentVolumeClaim-selected="handlePersistentVolumeClaimSelect"
        @persistentVolume-selected="handlePersistentVolumeSelect"

        @update-secret="handleUpdateSecret"
      />
      <BottomTabs
        v-if="selectedResourceItem"
        :selectedResource="selectedResourceItem"
        :selectedCluster="selectedCluster"
        :selectedTab="selectedTab"
        @tab-changed="handleTabChange"
      />
    </div>
  </div>
</template>

<style>
.app-container {
  display: flex;
  height: 100vh;
  background-color: #1e1e1e;
  color: #ffffff;
}

.right-container {
  flex: 1;
  display: flex;
  flex-direction: column;
  border-left: 1px solid #333333;
}

.right-container > :first-child {
  flex: 1;
  overflow-y: auto;
}

.right-container > :last-child {
  border-top: 1px solid #333333;
}
</style>