From 4f6b7b34fc8bbd72d9686cdab2a6b2c163fa0e75 Mon Sep 17 00:00:00 2001 From: Debian Date: Sat, 9 May 2026 16:23:58 +0200 Subject: [PATCH] feat: install.sh One-Liner-Bootstrap + System-Adressen-Card auf IP-Page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit scripts/install.sh: full curl-Onliner für Debian 13 trixie analog mail-gateway/scripts/install.sh — OS+Arch-Detection, Pre-flight- Tools, GPG-Key (nmg.asc, geteilt mit mail-gateway), APT-Source-Line trixie main, apt install edgeguard, Service-Smoke + healthz-Probe. Bestimmungsort: get.netcell-edgeguard.de (Hosting separat). UI: IP-Adressen-Page bekommt eine "Adressen am Kernel"-Card oben, analog zur Networks-Page. Listet jede vom Kernel sichtbare IP (lo + eth0 + …) mit Family-Tag (IPv4/IPv6) — read-only. Verwaltete Adressen darunter wie zuvor. User-Feedback: "die bestehenden IP-Adressen werden nicht angezeigt" — adressiert. Co-Authored-By: Claude Opus 4.7 (1M context) --- management-ui/src/i18n/locales/de/common.json | 5 +- management-ui/src/i18n/locales/en/common.json | 5 +- management-ui/src/pages/IPAddresses/index.tsx | 63 +++++- scripts/install.sh | 202 ++++++++++++++++-- 4 files changed, 258 insertions(+), 17 deletions(-) diff --git a/management-ui/src/i18n/locales/de/common.json b/management-ui/src/i18n/locales/de/common.json index 4e333ca..0f161c1 100644 --- a/management-ui/src/i18n/locales/de/common.json +++ b/management-ui/src/i18n/locales/de/common.json @@ -42,7 +42,10 @@ }, "ips": { "title": "IP-Adressen", - "intro": "Adressen, die EdgeGuard verwaltet — inklusive VIPs für Cluster-Failover. Bindung an die deklarierten Interfaces oben.", + "intro": "Adressen, die das Betriebssystem zeigt (Read-only oben) plus die Adressen, die EdgeGuard zusätzlich verwaltet — inklusive VIPs für Cluster-Failover.", + "systemDiscovered": "Adressen am Kernel (read-only)", + "managedTitle": "Verwaltete Adressen", + "family": "Familie", "addAddress": "Adresse hinzufügen", "editAddress": "Adresse bearbeiten", "interface": "Interface", diff --git a/management-ui/src/i18n/locales/en/common.json b/management-ui/src/i18n/locales/en/common.json index 6466c9e..ead9bd1 100644 --- a/management-ui/src/i18n/locales/en/common.json +++ b/management-ui/src/i18n/locales/en/common.json @@ -42,7 +42,10 @@ }, "ips": { "title": "IP addresses", - "intro": "Addresses managed by EdgeGuard — including VIPs that follow the active cluster node on failover.", + "intro": "Addresses the kernel currently has (read-only above) plus addresses EdgeGuard additionally manages — including VIPs that follow the active cluster node on failover.", + "systemDiscovered": "Kernel addresses (read-only)", + "managedTitle": "Managed addresses", + "family": "Family", "addAddress": "Add address", "editAddress": "Edit address", "interface": "Interface", diff --git a/management-ui/src/pages/IPAddresses/index.tsx b/management-ui/src/pages/IPAddresses/index.tsx index 681ea92..938514c 100644 --- a/management-ui/src/pages/IPAddresses/index.tsx +++ b/management-ui/src/pages/IPAddresses/index.tsx @@ -1,5 +1,5 @@ import { useState } from 'react' -import { Button, Form, Input, InputNumber, Modal, Popconfirm, Select, Space, Switch, Table, Tag, Typography, message } from 'antd' +import { Button, Card, Form, Input, InputNumber, Modal, Popconfirm, Select, Space, Switch, Table, Tag, Typography, message } from 'antd' import type { ColumnsType } from 'antd/es/table' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { useTranslation } from 'react-i18next' @@ -19,6 +19,20 @@ interface IPAddress { updated_at: string } +interface SystemInterface { + ifname: string + flags?: string[] + link_type?: string + addr_info?: Array<{ family: 'inet' | 'inet6'; local: string; prefixlen: number }> +} + +interface SystemAddress { + ifname: string + family: 'inet' | 'inet6' + address: string + prefix: number +} + interface IPFormValues { interface_id: number address: string @@ -42,12 +56,38 @@ async function listIfaces(): Promise { return (r.data.data as { interfaces?: NetworkInterface[] }).interfaces ?? [] } +// Flatten /system/interfaces into one row per (ifname, address) so +// the operator sees every kernel-side IP at a glance — including +// addresses that EdgeGuard hasn't taken under management yet. +async function listSystemAddresses(): Promise { + const r = await apiClient.get('/system/interfaces') + if (!isEnvelope(r.data)) return [] + const ifs = (r.data.data as { interfaces?: SystemInterface[] }).interfaces ?? [] + const out: SystemAddress[] = [] + for (const i of ifs) { + for (const a of i.addr_info ?? []) { + out.push({ + ifname: i.ifname, + family: a.family, + address: a.local, + prefix: a.prefixlen, + }) + } + } + return out +} + export default function IPAddressesPage() { const { t } = useTranslation() const qc = useQueryClient() const { data: ips, isLoading } = useQuery({ queryKey: ['ip-addresses'], queryFn: listIPs }) const { data: ifs } = useQuery({ queryKey: ['network-interfaces'], queryFn: listIfaces }) + const { data: sysAddrs } = useQuery({ + queryKey: ['system', 'addresses'], + queryFn: listSystemAddresses, + refetchInterval: 60_000, + }) const ifaceLabel = (id: number) => { const i = ifs?.find((x) => x.id === id) @@ -122,6 +162,27 @@ export default function IPAddressesPage() {
{t('ips.title')} {t('ips.intro')} + + + {(sysAddrs ?? []).length === 0 + ? + : ( + `${r.ifname}-${r.address}`} + dataSource={sysAddrs ?? []} + pagination={false} + columns={[ + { title: t('ips.interface'), dataIndex: 'ifname', key: 'ifname', render: (s: string) => {s} }, + { title: t('ips.address'), key: 'addr', render: (_, row: SystemAddress) => {row.address}/{row.prefix} }, + { title: t('ips.family'), dataIndex: 'family', key: 'family', render: (f: string) => {f === 'inet' ? 'IPv4' : 'IPv6'} }, + ]} + /> + ) + } + + + {t('ips.managedTitle')}