From 5f8d06e8ba68faf9d7e3f1af63b51b0f20f5800a Mon Sep 17 00:00:00 2001 From: Debian Date: Sun, 10 May 2026 21:46:27 +0200 Subject: [PATCH] =?UTF-8?q?feat(ui):=20SSL-Domain-Picker=20=E2=80=94=20Man?= =?UTF-8?q?agement-FQDN=20+=20Cluster-Nodes=20+=20Free-Text?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vorher: SSL-Issue-Form bot nur die operator-managed /domains an. Wenn der Operator ein Cert für die Management-FQDN (utm-1.netcell-it.de aus setup.json) wollte, war diese nicht in der Auswahl — er hätte sie erst als Domain-Row anlegen müssen. Jetzt: AutoComplete (statt Select) mit drei Quellen kombiniert: * Management-FQDN aus /setup/status — als erste Option mit Hint * Alle Cluster-Node-FQDNs aus /cluster/nodes * Operator-/domains Plus: jede beliebige FQDN ist eintippbar (DNS muss zeigen). (combobox-mode in AntD ist deprecated — AutoComplete ist die empfohlene Variante für free-text-with-suggestions.) Version 1.0.15. Co-Authored-By: Claude Opus 4.7 (1M context) --- VERSION | 2 +- cmd/edgeguard-api/main.go | 2 +- cmd/edgeguard-ctl/main.go | 2 +- cmd/edgeguard-scheduler/main.go | 2 +- management-ui/package.json | 2 +- .../src/components/Layout/Sidebar.tsx | 2 +- management-ui/src/i18n/locales/de/common.json | 5 +- management-ui/src/i18n/locales/en/common.json | 5 +- management-ui/src/pages/SSL/index.tsx | 49 +++++++++++++++++-- 9 files changed, 58 insertions(+), 13 deletions(-) diff --git a/VERSION b/VERSION index 5b09c67..a970716 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.14 +1.0.15 diff --git a/cmd/edgeguard-api/main.go b/cmd/edgeguard-api/main.go index acc6f15..ce251c9 100644 --- a/cmd/edgeguard-api/main.go +++ b/cmd/edgeguard-api/main.go @@ -39,7 +39,7 @@ import ( wgsvc "git.netcell-it.de/projekte/edgeguard-native/internal/services/wireguard" ) -var version = "1.0.14" +var version = "1.0.15" func main() { addr := os.Getenv("EDGEGUARD_API_ADDR") diff --git a/cmd/edgeguard-ctl/main.go b/cmd/edgeguard-ctl/main.go index bc1a1f1..20cb162 100644 --- a/cmd/edgeguard-ctl/main.go +++ b/cmd/edgeguard-ctl/main.go @@ -9,7 +9,7 @@ import ( "os" ) -var version = "1.0.14" +var version = "1.0.15" const usage = `edgeguard-ctl — EdgeGuard CLI diff --git a/cmd/edgeguard-scheduler/main.go b/cmd/edgeguard-scheduler/main.go index c8f9bd7..a22630b 100644 --- a/cmd/edgeguard-scheduler/main.go +++ b/cmd/edgeguard-scheduler/main.go @@ -5,7 +5,7 @@ import ( "time" ) -var version = "1.0.14" +var version = "1.0.15" func main() { log.Printf("edgeguard-scheduler %s starting", version) diff --git a/management-ui/package.json b/management-ui/package.json index 2950fc0..fea1a59 100644 --- a/management-ui/package.json +++ b/management-ui/package.json @@ -1,7 +1,7 @@ { "name": "edgeguard-management-ui", "private": true, - "version": "1.0.14", + "version": "1.0.15", "type": "module", "scripts": { "dev": "vite", diff --git a/management-ui/src/components/Layout/Sidebar.tsx b/management-ui/src/components/Layout/Sidebar.tsx index e15e355..0f7202c 100644 --- a/management-ui/src/components/Layout/Sidebar.tsx +++ b/management-ui/src/components/Layout/Sidebar.tsx @@ -70,7 +70,7 @@ const NAV: NavSection[] = [ }, ] -const VERSION = '1.0.14' +const VERSION = '1.0.15' export default function Sidebar({ isOpen, onClose }: SidebarProps) { const { t } = useTranslation() diff --git a/management-ui/src/i18n/locales/de/common.json b/management-ui/src/i18n/locales/de/common.json index 0b9a624..bfe4c62 100644 --- a/management-ui/src/i18n/locales/de/common.json +++ b/management-ui/src/i18n/locales/de/common.json @@ -249,7 +249,10 @@ "uploadIntro": "Eigenes Zertifikat hochladen. Format: PEM-encoded. Cert + optional Chain + Private Key. EdgeGuard prüft die Cert/Key-Übereinstimmung vor dem Schreiben.", "uploadHint": "Tipp: bei Let's-Encrypt-Renewals nicht hier hochladen — den LE-Tab nutzen.", "domain": "Domain", - "selectDomain": "Domain wählen", + "selectDomain": "Domain wählen oder eintippen", + "domainExtra": "Inkludiert Management-FQDN (aus Setup), Cluster-Knoten und Operator-Domains. Du kannst auch eine andere Domain tippen, sofern DNS schon auf die Box zeigt.", + "fqdnHintMgmt": "Management-FQDN", + "fqdnHintCluster": "Cluster · {{role}}", "issuer": "Issuer", "status": "Status", "expiresIn": "Gültig noch", diff --git a/management-ui/src/i18n/locales/en/common.json b/management-ui/src/i18n/locales/en/common.json index 5c947aa..073ac72 100644 --- a/management-ui/src/i18n/locales/en/common.json +++ b/management-ui/src/i18n/locales/en/common.json @@ -249,7 +249,10 @@ "uploadIntro": "Upload your own certificate. Format: PEM-encoded. Cert + optional chain + private key. EdgeGuard validates cert/key match before writing.", "uploadHint": "Tip: for Let's Encrypt renewals don't upload here — use the LE tab.", "domain": "Domain", - "selectDomain": "Select domain", + "selectDomain": "Pick or type a domain", + "domainExtra": "Includes the management FQDN (from Setup), cluster nodes and operator domains. You can also type any other domain — as long as DNS resolves to this box.", + "fqdnHintMgmt": "management FQDN", + "fqdnHintCluster": "cluster · {{role}}", "issuer": "Issuer", "status": "Status", "expiresIn": "Expires in", diff --git a/management-ui/src/pages/SSL/index.tsx b/management-ui/src/pages/SSL/index.tsx index 110e15a..fb70936 100644 --- a/management-ui/src/pages/SSL/index.tsx +++ b/management-ui/src/pages/SSL/index.tsx @@ -1,5 +1,5 @@ import { useState } from 'react' -import { Alert, Button, Card, Form, Input, Select, Space, Tabs, Tag, Typography, message } from 'antd' +import { Alert, AutoComplete, Button, Card, Form, Input, Space, Tabs, Tag, Typography, message } from 'antd' import { SafetyCertificateOutlined } from '@ant-design/icons' import PageHeader from '../../components/PageHeader' import ActionButtons from '../../components/ActionButtons' @@ -47,6 +47,23 @@ async function listDomains(): Promise { return (r.data.data as { domains?: Domain[] }).domains ?? [] } +interface SetupStatus { completed: boolean; fqdn?: string } +async function fetchSetupStatus(): Promise { + try { + const r = await apiClient.get('/setup/status') + return isEnvelope(r.data) ? (r.data.data as SetupStatus) : null + } catch { return null } +} + +interface ClusterNode { id: string; fqdn: string; role: string } +async function listClusterNodes(): Promise { + try { + const r = await apiClient.get('/cluster/nodes') + if (!isEnvelope(r.data)) return [] + return (r.data.data as { nodes?: ClusterNode[] }).nodes ?? [] + } catch { return [] } +} + const STATUS_COLORS: Record = { active: 'green', renewing: 'blue', @@ -68,6 +85,24 @@ export default function SSLPage() { const { data: certs, isLoading } = useQuery({ queryKey: ['tls-certs'], queryFn: listCerts }) const { data: domains } = useQuery({ queryKey: ['domains'], queryFn: listDomains }) + const { data: setup } = useQuery({ queryKey: ['setup-status'], queryFn: fetchSetupStatus }) + const { data: nodes } = useQuery({ queryKey: ['cluster', 'nodes'], queryFn: listClusterNodes }) + + // Domain-Picker-Optionen: operator-managed Domains + Management-FQDN + // (utm-1.netcell-it.de aus setup.json) + alle Cluster-Node-FQDNs. + // Plus Combobox-Mode, sodass der Operator jede beliebige Domain + // tippen kann (z.B. wenn DNS schon zeigt aber Domain noch nicht + // unter /domains angelegt wurde). + const domainOptions: { value: string; label: string }[] = [] + const seen = new Set() + const add = (name: string, hint?: string) => { + if (!name || seen.has(name)) return + seen.add(name) + domainOptions.push({ value: name, label: hint ? `${name} — ${hint}` : name }) + } + if (setup?.fqdn) add(setup.fqdn, t('ssl.fqdnHintMgmt')) + for (const n of nodes ?? []) add(n.fqdn, t('ssl.fqdnHintCluster', { role: n.role })) + for (const d of domains ?? []) add(d.name) const [issueForm] = Form.useForm() const [uploadForm] = Form.useForm() @@ -141,11 +176,15 @@ export default function SSLPage() { {t('ssl.leIntro')} {issueErr && setIssueErr(null)} />}
issueMut.mutate(v)}> - -