From ee5ed78d35e28e94b95e388f70befef665a49a5c Mon Sep 17 00:00:00 2001 From: "andreas.dinauer" Date: Sat, 1 Nov 2025 16:58:35 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Add=20secrets=20and=20view=20popups?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.vue | 6 +- assets/base-style.css | 5 ++ assets/style.css | 26 ++++-- classes/Ingress.ts | 60 ++++++++++++- classes/Metadata.ts | 7 +- classes/Secret.ts | 8 ++ components/IngressComponent.vue | 14 --- components/InspectSidebar.vue | 25 ++++-- components/LogPopup.vue | 27 ++++-- components/NamespacePicker.vue | 17 ++++ components/TableComponent.vue | 2 +- components/ingress/IngressComponent.vue | 23 +++++ components/ingress/view/IngressViewPopup.vue | 47 ++++++++++ .../inspect/resources/IngressComponent.vue | 5 +- .../inspect/resources/SecretComponent.vue | 33 +++++++ components/pod/PodComponent.vue | 5 +- components/pod/env/EnvVar.ts | 18 ++++ components/pod/view/PodViewPopup.vue | 90 +++++++++++++++++++ components/{ => popup}/PopupTemplate.vue | 13 ++- components/secrets/SecretAddComponent.vue | 52 +++++++++++ components/secrets/SecretComponent.vue | 23 +++++ components/secrets/types/AddTlsSecret.vue | 13 +++ components/ui/UiButton.vue | 6 ++ components/ui/UiLoadingIcon.vue | 8 ++ pages/account.vue | 5 +- pages/account/inspect.vue | 14 ++- .../inspect/[resource]/[namespace].vue | 2 + pages/account/settings/index.vue | 13 +++ requests/logs.ts | 12 ++- stores/NamespaceStore.ts | 15 ++++ utils/Optional.ts | 42 +++++++++ utils/StringUtils.ts | 22 +++++ 32 files changed, 604 insertions(+), 54 deletions(-) create mode 100644 classes/Secret.ts delete mode 100644 components/IngressComponent.vue create mode 100644 components/NamespacePicker.vue create mode 100644 components/ingress/IngressComponent.vue create mode 100644 components/ingress/view/IngressViewPopup.vue create mode 100644 components/inspect/resources/SecretComponent.vue create mode 100644 components/pod/env/EnvVar.ts create mode 100644 components/pod/view/PodViewPopup.vue rename components/{ => popup}/PopupTemplate.vue (85%) create mode 100644 components/secrets/SecretAddComponent.vue create mode 100644 components/secrets/SecretComponent.vue create mode 100644 components/secrets/types/AddTlsSecret.vue create mode 100644 pages/account/settings/index.vue create mode 100644 utils/Optional.ts create mode 100644 utils/StringUtils.ts diff --git a/app.vue b/app.vue index a4ceee2..3a09116 100644 --- a/app.vue +++ b/app.vue @@ -16,10 +16,14 @@ guard(useRoute().fullPath); function guard(route: string) { - if(route.startsWith('/dashboard') && getToken() == null) + if(route.startsWith('/account') && getToken() == null) { useRouter().push('/'); } + if(route === '/' && getToken() != null) + { + useRouter().push('/account/inspect/nodes/_all'); + } } useHead({ diff --git a/assets/base-style.css b/assets/base-style.css index 91676b9..944ee9b 100644 --- a/assets/base-style.css +++ b/assets/base-style.css @@ -236,4 +236,9 @@ .base-shape { height: 2.5rem; padding: 0.5rem; +} + + +.width-6rem { + width: 6rem; } \ No newline at end of file diff --git a/assets/style.css b/assets/style.css index e34e259..e22836e 100644 --- a/assets/style.css +++ b/assets/style.css @@ -11,8 +11,10 @@ html { --primary-color: rgb(87, 75, 255); - --tile-color: rgb(255, 255, 255); - background-color: rgb(240, 240, 240); + --tile-color: #ebf1ff; + background-color: rgb(255, 255, 255); + --shade-light: #f3f3f3; + --shade-dark: rgb(233, 233, 233); } html * { @@ -28,26 +30,36 @@ html, body, #__nuxt { display: grid; grid-template-columns: auto auto auto 1fr auto auto; align-content: start; - background-color: rgb(247, 247, 247); + background-color: rgb(255, 255, 255); } .resource, .header { display: contents; } .header > * { padding: 0.75rem; - background-color: rgb(12, 12, 12); - color: white; + background-color: var(--tile-color); + color: rgb(0, 0, 0); font-weight: bold; position: sticky; top: 0; + margin-bottom: 1rem; +} +.header *:first-of-type { + border-top-left-radius: 0rem; + border-bottom-left-radius: 0rem; +} +.header *:last-of-type { + border-top-right-radius: 0rem; + border-bottom-right-radius: 0rem; } .resource > .grid-element { padding: 0.25rem 0.75rem; display: flex; align-items: center; + background-color: #f3f3f3; } .resource:hover > .grid-element { - background-color: #c2c2c2; + background-color: #dbdbdb; } .resource p { font-weight: 600; @@ -55,4 +67,4 @@ html, body, #__nuxt { } .even > .grid-element { background-color: rgb(233, 233, 233); -} \ No newline at end of file +} diff --git a/classes/Ingress.ts b/classes/Ingress.ts index 6b1cec7..1f48150 100644 --- a/classes/Ingress.ts +++ b/classes/Ingress.ts @@ -2,5 +2,63 @@ import type { Metadata } from "./Metadata"; export class Ingress { - metadata?: Metadata; + constructor ( + public metadata: Metadata, + public spec: IngressSpec + ) { } +} + +export class IngressSpec +{ + constructor ( + public ingressClassName: string, + public rules: IngressRule[], + public tls: IngressTLS[] + ) { } +} + +export class IngressRule +{ + constructor ( + public host: string, + public http: IngressRuleHttp + ) { } +} + +export class IngressRuleHttp +{ + constructor ( + public paths: IngressRulePath[] + ) { } +} + +export class IngressRulePath +{ + constructor ( + public path: string, + public pathType: string, + public backend: IngressRuleBackend + ) { } +} + +export class IngressRuleBackend +{ + constructor ( + public service: IngressRuleBackendService + ) { } +} + +export class IngressRuleBackendService +{ + constructor ( + public name: string + ) { } +} + +export class IngressTLS +{ + constructor ( + public hosts: string[], + public secretName: string + ) {} } \ No newline at end of file diff --git a/classes/Metadata.ts b/classes/Metadata.ts index 8dd76dc..a9cc69d 100644 --- a/classes/Metadata.ts +++ b/classes/Metadata.ts @@ -1,7 +1,10 @@ export class Metadata { - name?: string; - namespace?: string; creationTimestamp?: string; uid?: string; + + constructor ( + public namespace: string, + public name: string + ) { } } \ No newline at end of file diff --git a/classes/Secret.ts b/classes/Secret.ts new file mode 100644 index 0000000..8b7b371 --- /dev/null +++ b/classes/Secret.ts @@ -0,0 +1,8 @@ +import type { Metadata } from "./Metadata"; + +export class Secret +{ + constructor ( + public metadata: Metadata + ) { } +} \ No newline at end of file diff --git a/components/IngressComponent.vue b/components/IngressComponent.vue deleted file mode 100644 index d46fcee..0000000 --- a/components/IngressComponent.vue +++ /dev/null @@ -1,14 +0,0 @@ - - - \ No newline at end of file diff --git a/components/InspectSidebar.vue b/components/InspectSidebar.vue index 2b6477a..37287b1 100644 --- a/components/InspectSidebar.vue +++ b/components/InspectSidebar.vue @@ -3,12 +3,11 @@

Kubooboo

@@ -21,20 +20,30 @@ + + \ No newline at end of file diff --git a/components/TableComponent.vue b/components/TableComponent.vue index 3212c9b..875943e 100644 --- a/components/TableComponent.vue +++ b/components/TableComponent.vue @@ -2,7 +2,7 @@
- +
diff --git a/components/ingress/IngressComponent.vue b/components/ingress/IngressComponent.vue new file mode 100644 index 0000000..f0066a8 --- /dev/null +++ b/components/ingress/IngressComponent.vue @@ -0,0 +1,23 @@ + + + \ No newline at end of file diff --git a/components/ingress/view/IngressViewPopup.vue b/components/ingress/view/IngressViewPopup.vue new file mode 100644 index 0000000..270a67b --- /dev/null +++ b/components/ingress/view/IngressViewPopup.vue @@ -0,0 +1,47 @@ + + + \ No newline at end of file diff --git a/components/inspect/resources/IngressComponent.vue b/components/inspect/resources/IngressComponent.vue index 56911a6..60e8661 100644 --- a/components/inspect/resources/IngressComponent.vue +++ b/components/inspect/resources/IngressComponent.vue @@ -4,6 +4,9 @@

Name

Namespace

+

Ingress Class Name

+

Rules

+

Actions

@@ -19,6 +22,6 @@ const ingresses = ResourceRepo.init().load('ingresses').get(); \ No newline at end of file diff --git a/components/inspect/resources/SecretComponent.vue b/components/inspect/resources/SecretComponent.vue new file mode 100644 index 0000000..3d19b17 --- /dev/null +++ b/components/inspect/resources/SecretComponent.vue @@ -0,0 +1,33 @@ + + + + + \ No newline at end of file diff --git a/components/pod/PodComponent.vue b/components/pod/PodComponent.vue index 694dfc2..c23f8c7 100644 --- a/components/pod/PodComponent.vue +++ b/components/pod/PodComponent.vue @@ -1,5 +1,5 @@ @@ -27,4 +28,6 @@ defineProps<{ }>(); const logPopup = ref(); + +const podViewPopup = ref(); \ No newline at end of file diff --git a/components/pod/env/EnvVar.ts b/components/pod/env/EnvVar.ts new file mode 100644 index 0000000..0253539 --- /dev/null +++ b/components/pod/env/EnvVar.ts @@ -0,0 +1,18 @@ +import axios from "axios"; + +export class EnvVar +{ + constructor ( + public key: string, + public value: string + ) {} + + static get(namespace: string, name: string, onSuccess: (envVars: EnvVar[]) => void) + { + const url = StringUtils.format('%s/pods/%s/%s/env', useRuntimeConfig().public.apiBase, namespace, name); + axios.get(url) + .then((response) => { + onSuccess(response.data); + }) + } +} \ No newline at end of file diff --git a/components/pod/view/PodViewPopup.vue b/components/pod/view/PodViewPopup.vue new file mode 100644 index 0000000..7fb3978 --- /dev/null +++ b/components/pod/view/PodViewPopup.vue @@ -0,0 +1,90 @@ + + + + + diff --git a/components/PopupTemplate.vue b/components/popup/PopupTemplate.vue similarity index 85% rename from components/PopupTemplate.vue rename to components/popup/PopupTemplate.vue index 1f82852..efb600e 100644 --- a/components/PopupTemplate.vue +++ b/components/popup/PopupTemplate.vue @@ -1,6 +1,6 @@ @@ -15,6 +16,7 @@ import IngressComponent from '~/components/inspect/resources/IngressComponent.vu import ServiceComponent from '~/components/inspect/resources/ServiceComponent.vue'; import DeploymentComponent from '~/components/inspect/resources/DeploymentComponent.vue'; import NodeComponent from '~/components/inspect/resources/NodeComponent.vue'; +import SecretComponent from '~/components/inspect/resources/SecretComponent.vue'; const resource = useRoute().params.resource as string; \ No newline at end of file diff --git a/pages/account/settings/index.vue b/pages/account/settings/index.vue new file mode 100644 index 0000000..542020b --- /dev/null +++ b/pages/account/settings/index.vue @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/requests/logs.ts b/requests/logs.ts index 9c1aa35..ae641c3 100644 --- a/requests/logs.ts +++ b/requests/logs.ts @@ -1,8 +1,16 @@ import axios from "axios"; -export function getLogs(podId: string | undefined, onSuccess: (logs: string[]) => void) +export class Log { - axios.get(useRuntimeConfig().public.apiBase + '/pods/' + podId + "/logs", { + constructor ( + public timestamp: string, + public message: string + ) {} +} + +export function getLogs(podId: string | undefined, onSuccess: (logs: Log[]) => void) +{ + axios.get(useRuntimeConfig().public.apiBase + '/pods/' + podId + "/logs", { headers: { Authorization: "Bearer " + requireToken() } diff --git a/stores/NamespaceStore.ts b/stores/NamespaceStore.ts index a2a7faf..a6201e5 100644 --- a/stores/NamespaceStore.ts +++ b/stores/NamespaceStore.ts @@ -12,6 +12,21 @@ export const useNamespaceStore = defineStore('namespace', { return state.namespaces; } }, + getNamespaceNames: (state) => { + return (includeAll?: boolean): Map | undefined => { + if (state.namespaces) + { + const result = new Map(); + if (includeAll === true) + { + result.set('_all', 'Alle') + } + state.namespaces.forEach(namespace => { result.set(namespace.metadata.name, namespace.metadata.name) }); + return result; + } + return undefined; + } + }, getCurrentNamespace: (state) => { return (): Namespace | undefined => state.currentNamespace; } diff --git a/utils/Optional.ts b/utils/Optional.ts new file mode 100644 index 0000000..83be0f7 --- /dev/null +++ b/utils/Optional.ts @@ -0,0 +1,42 @@ +export class Optional +{ + private object?: T; + + private constructor(object?: T) + { + this.object = object; + } + + static ofNullable(object?: T): Optional + { + return new Optional(object); + } + + isEmpty() + { + return this.object == null; + } + + isPresent() + { + return this.object != null; + } + + orElse(other: T): T + { + if(this.object != null) + { + return this.object; + } + return other; + } + + get(): T + { + if(this.object != null) + { + return this.object; + } + throw new Error("No value present in Optional."); + } +} \ No newline at end of file diff --git a/utils/StringUtils.ts b/utils/StringUtils.ts new file mode 100644 index 0000000..a60aa7e --- /dev/null +++ b/utils/StringUtils.ts @@ -0,0 +1,22 @@ +export class StringUtils +{ + static format(template: string, ...varContext: string[]): string + { + const context = Array.from(varContext); + let contextIndex = 0; + while (template.includes("%s")) + { + const currentContext = context.at(contextIndex); + if (currentContext != null) + { + template = template.replace("%s", currentContext); + contextIndex++; + } + else + { + return template; + } + } + return template; + } +} \ No newline at end of file