- middleware.go: RequireAdminForMutations() blockiert POST/PUT/DELETE für Nicht-Admins (GET/HEAD passieren immer durch) - main.go: Middleware in den authed-Gruppe eingehängt — wirkt für alle ~30 Resource-Handler gleichzeitig - api/client.ts: 403 → AntD-Notification "Nur Lesezugriff" statt stiller Fehler - Header: "Nur lesen"-Badge + Tooltip wenn role=viewer - i18n de+en: viewerBadge + viewerHint Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1201 lines
61 KiB
JSON
1201 lines
61 KiB
JSON
{
|
||
"app": {
|
||
"title": "EdgeGuard",
|
||
"subtitle": "Native Reverse-Proxy / VPN / Firewall"
|
||
},
|
||
"nav": {
|
||
"dashboard": "Dashboard",
|
||
"domains": "Domains",
|
||
"backends": "Backends",
|
||
"routing": "Routing",
|
||
"networks": "Netzwerk-Interfaces",
|
||
"ipAddresses": "IP-Adressen",
|
||
"ssl": "SSL-Zertifikate",
|
||
"vpn": "VPN",
|
||
"wireguard": "WireGuard",
|
||
"forwardProxy": "Forward-Proxy",
|
||
"dns": "DNS",
|
||
"ntp": "Zeit (NTP)",
|
||
"firewall": "Firewall",
|
||
"firewallLive": "Firewall-Log",
|
||
"cluster": "Cluster",
|
||
"logs": "Logs",
|
||
"audit": "Audit-Log",
|
||
"backups": "Backups",
|
||
"diagnostics": "Diagnose",
|
||
"users": "Benutzer",
|
||
"alerts": "Alarme",
|
||
"license": "Lizenz",
|
||
"settings": "Einstellungen",
|
||
"section": {
|
||
"overview": "Übersicht",
|
||
"routing": "Routing",
|
||
"network": "Netzwerk",
|
||
"security": "Sicherheit",
|
||
"system": "System"
|
||
}
|
||
},
|
||
"fw": {
|
||
"title": "Firewall",
|
||
"intro": "Fortigate-Style: Regeln aus Zonen × Adress-Objekten/Gruppen × Services/Service-Gruppen × Action. NAT separat. Top-down, first-match.",
|
||
"tabs": {
|
||
"rules": "Regeln",
|
||
"nat": "NAT",
|
||
"live": "Live-Log",
|
||
"zones": "Zonen",
|
||
"addrObj": "Adress-Objekte",
|
||
"addrGrp": "Adress-Gruppen",
|
||
"services": "Services",
|
||
"svcGrp": "Service-Gruppen",
|
||
"system": "System-Regeln"
|
||
},
|
||
"zone": {
|
||
"name": "Name",
|
||
"description": "Beschreibung",
|
||
"builtin": "vordefiniert",
|
||
"builtinHint": "Vordefinierte Zonen können nicht gelöscht werden — Renderer und Anti-Lockout-Regeln verlassen sich darauf.",
|
||
"builtinNameLocked": "Name vordefiniert — kann nicht geändert werden, weil bestehende Regeln und Interfaces ihn referenzieren.",
|
||
"namePattern": "Nur Kleinbuchstaben, Ziffern, _ und -; muss mit Buchstaben beginnen, max. 32 Zeichen.",
|
||
"add": "Zone hinzufügen",
|
||
"edit": "Zone bearbeiten",
|
||
"deleteConfirm": "Zone {{name}} wirklich löschen?",
|
||
"emptyTitle": "Noch keine eigenen Firewall-Zonen.",
|
||
"emptyDesc": "Zonen gruppieren Interfaces (lan, wan, dmz). Vordefinierte Zonen sind bereits da; eigene anlegen wenn du z. B. eine separate dmz-Zone oder eine wg-Zone fürs VPN brauchst."
|
||
},
|
||
"ao": {
|
||
"name": "Name", "kind": "Typ", "value": "Wert", "description": "Beschreibung",
|
||
"add": "Adress-Objekt hinzufügen", "edit": "Adress-Objekt bearbeiten",
|
||
"deleteConfirm": "Adress-Objekt {{name}} wirklich löschen?",
|
||
"emptyTitle": "Noch keine Adress-Objekte.",
|
||
"emptyDesc": "Wiederverwendbare benannte IPs/Netze/Ranges für Firewall- und NAT-Regeln (z. B. office-net = 10.0.0.0/24, mailbox-1 = 10.0.1.42)."
|
||
},
|
||
"ag": {
|
||
"name": "Name", "members": "Mitglieder", "description": "Beschreibung",
|
||
"add": "Adress-Gruppe hinzufügen", "edit": "Adress-Gruppe bearbeiten",
|
||
"selectMembers": "Adress-Objekte wählen",
|
||
"deleteConfirm": "Adress-Gruppe {{name}} wirklich löschen?",
|
||
"emptyTitle": "Noch keine Adress-Gruppen.",
|
||
"emptyDesc": "Bündele mehrere Adress-Objekte zu einer Gruppe (z. B. office-locations = [hq-net, branch-1-net, branch-2-net]) — Regeln müssen dann nur eine Gruppe referenzieren."
|
||
},
|
||
"svc": {
|
||
"name": "Name", "proto": "Protokoll", "ports": "Ports",
|
||
"portStart": "Port (Start)", "portEnd": "Port (Ende)",
|
||
"description": "Beschreibung", "builtinHint": "Vordefiniert — nicht editierbar",
|
||
"add": "Service hinzufügen", "edit": "Service bearbeiten",
|
||
"deleteConfirm": "Service {{name}} wirklich löschen?",
|
||
"emptyTitle": "Noch keine eigenen Services.",
|
||
"emptyDesc": "Vordefinierte (HTTP, HTTPS, SSH, …) sind bereits da. Eigene Services für app-spezifische Ports (z. B. mailcow-imaps tcp/993) erleichtern Regeln deutlich."
|
||
},
|
||
"sg": {
|
||
"name": "Name", "members": "Mitglieder", "description": "Beschreibung",
|
||
"add": "Service-Gruppe hinzufügen", "edit": "Service-Gruppe bearbeiten",
|
||
"selectMembers": "Services wählen",
|
||
"deleteConfirm": "Service-Gruppe {{name}} wirklich löschen?",
|
||
"emptyTitle": "Noch keine Service-Gruppen.",
|
||
"emptyDesc": "Bündele mehrere Services zu einer Gruppe (z. B. web-stack = [HTTP, HTTPS, HTTP/3]) — eine Regel mit der Gruppe ersetzt drei Regeln pro Service."
|
||
},
|
||
"rule": {
|
||
"name": "Name", "priority": "Priority", "enabled": "Aktiv", "log": "Logging",
|
||
"action": "Aktion", "src": "Quelle", "dst": "Ziel", "service": "Service",
|
||
"srcZone": "Quell-Zone", "dstZone": "Ziel-Zone",
|
||
"srcKind": "Quell-Typ", "dstKind": "Ziel-Typ",
|
||
"object": "Adress-Objekt", "group": "Adress-Gruppe",
|
||
"serviceKind": "Service-Typ", "serviceGroup": "Service-Gruppe",
|
||
"comment": "Kommentar",
|
||
"hits": "Hits",
|
||
"add": "Regel hinzufügen", "edit": "Regel bearbeiten",
|
||
"deleteConfirm": "Diese Regel wirklich löschen?",
|
||
"emptyTitle": "Noch keine eigenen Firewall-Regeln.",
|
||
"emptyDesc": "Die System-Regeln oben halten SSH (rate-limited), HTTPS :443 und Mgmt-UI :3443 immer offen (Anti-Lockout). Eigene Regeln für app-spezifische Inbound-Ports oder zonenübergreifende Forwards anlegen."
|
||
},
|
||
"nat": {
|
||
"name": "Name", "priority": "Priority", "kind": "Typ", "enabled": "Aktiv",
|
||
"match": "Match", "target": "Ziel",
|
||
"inZone": "Eingangs-Zone", "outZone": "Ausgangs-Zone", "proto": "Protokoll",
|
||
"matchSrcCidr": "Source-CIDR (Match)", "matchDstCidr": "Dest-CIDR (Match)",
|
||
"matchDstCidrHint": "leer = jede dest-IP (z.B. öffentliche IP der Box)",
|
||
"dportStart": "Port (Start)", "dportEnd": "Port (Ende)",
|
||
"targetAddr": "Ziel-Adresse", "targetPortStart": "Ziel-Port (Start)", "targetPortEnd": "Ziel-Port (Ende)",
|
||
"comment": "Kommentar",
|
||
"add": "NAT-Regel hinzufügen", "edit": "NAT-Regel bearbeiten",
|
||
"deleteConfirm": "Diese NAT-Regel wirklich löschen?",
|
||
"emptyTitle": "Noch keine NAT-Regeln.",
|
||
"emptyDesc": "DNAT (z. B. extern :2030 → intern 10.10.20.12:22 für SSH zu einem internen Host) oder SNAT/MASQUERADE (Internet-Zugang für ein internes Subnetz über die Box-IP)."
|
||
},
|
||
"sys": {
|
||
"title": "System-Regeln (immer aktiv)",
|
||
"chain": "Chain", "match": "Match", "action": "Aktion", "note": "Hinweis",
|
||
"policy": "Default-Policy",
|
||
"policyValue": "Eingang DROP — alles muss explizit erlaubt werden.",
|
||
"order": "Auswertung",
|
||
"orderValue": "System-Regeln zuerst, danach Operator-Regeln top-down (priority asc, first-match).",
|
||
"lockout": "Anti-Lockout",
|
||
"lockoutValue": "SSH (22) und Management-UI (443) sind immer erreichbar — können auch vom Operator nicht versehentlich gesperrt werden."
|
||
}
|
||
},
|
||
"networks": {
|
||
"title": "Netzwerk",
|
||
"intro": "Interfaces (Ethernet/VLAN/Bond/Bridge) und Static-Routes. Live-Discovery + deklarative Konfiguration aus der DB.",
|
||
"tabs": {
|
||
"interfaces": "Interfaces",
|
||
"routes": "Routen"
|
||
},
|
||
"systemDiscovered": "System-Interfaces — Live-Traffic-Zähler (seit letztem HAProxy-Start)",
|
||
"addresses": "Adressen",
|
||
"linkType": "Typ",
|
||
"systemEmpty": "Keine Kernel-Interfaces gefunden",
|
||
"addInterface": "Interface hinzufügen",
|
||
"editInterface": "Interface bearbeiten",
|
||
"emptyTitle": "Noch keine verwalteten Interfaces.",
|
||
"emptyDesc": "System-Interfaces (siehe oben) werden read-only erkannt. Verwaltete Interfaces sind das was EdgeGuard selbst anlegt — VLANs, Bridges, Bond, GRE-Tunnels — und in /etc/network/interfaces.d rendert.",
|
||
"name": "Name",
|
||
"type": "Typ",
|
||
"parent": "Parent-Interface",
|
||
"selectParent": "Parent wählen",
|
||
"vlan": "VLAN",
|
||
"vlanId": "VLAN-ID",
|
||
"composition": "Zusammensetzung",
|
||
"members": "Member-Interfaces",
|
||
"selectMembers": "Physische Interfaces wählen",
|
||
"membersRequired": "Mindestens ein Member-Interface erforderlich",
|
||
"membersHintBridge": "Eine Bridge bündelt mehrere physische Ports auf L2 — typisch zwei Ports für einen Software-Switch.",
|
||
"membersHintBond": "Ein Bond aggregiert mehrere physische Ports zu einem logischen Link (LACP / active-backup).",
|
||
"role": "Zone",
|
||
"roleHint": "Zonen kommen aus Firewall → Zonen. Eigene Zonen (z.B. iot, guest) lassen sich dort anlegen.",
|
||
"mtu": "MTU",
|
||
"active": "Aktiv",
|
||
"description": "Beschreibung",
|
||
"actions": "Aktionen",
|
||
"deleteConfirm": "Interface {{name}} wirklich löschen?"
|
||
},
|
||
"ips": {
|
||
"title": "IP-Adressen",
|
||
"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",
|
||
"emptyTitle": "Noch keine eigenen IP-Adressen.",
|
||
"emptyDesc": "Lege deine erste IP an — Floating-VIPs für HA-Failover oder zusätzliche Listen-Adressen für HAProxy/Squid/Unbound. Box-eigene Distro-IPs werden oben unter \"Erkannte IPs\" gelesen.",
|
||
"editAddress": "Adresse bearbeiten",
|
||
"interface": "Interface",
|
||
"selectInterface": "Interface wählen",
|
||
"address": "Adresse",
|
||
"prefix": "Prefix",
|
||
"vip": "VIP",
|
||
"vipFlag": "Als VIP markieren",
|
||
"vipPriority": "VIP-Priorität (Cluster-Failover)",
|
||
"active": "Aktiv",
|
||
"description": "Beschreibung",
|
||
"actions": "Aktionen",
|
||
"deleteConfirm": "Adresse {{addr}} wirklich löschen?"
|
||
},
|
||
"auth": {
|
||
"loginTitle": "Anmelden",
|
||
"email": "E-Mail",
|
||
"password": "Passwort",
|
||
"login": "Anmelden",
|
||
"logout": "Abmelden",
|
||
"loginFailed": "Anmeldung fehlgeschlagen",
|
||
"loggedInAs": "Angemeldet als",
|
||
"forgotPassword": "Passwort vergessen?",
|
||
"viewerBadge": "Nur lesen",
|
||
"viewerHint": "Dieser Account hat die Rolle Betrachter — Änderungen sind gesperrt. Ein Admin kann die Rolle anpassen."
|
||
},
|
||
"reset": {
|
||
"title": "Admin-Passwort zurücksetzen",
|
||
"intro": "Self-Service-Reset über CLI-Token. Du brauchst SSH-Zugang zur Box.",
|
||
"cliTitle": "1. Auf der Box ausführen:",
|
||
"token": "Reset-Token",
|
||
"tokenExtra": "32 hex-Zeichen aus der CLI-Ausgabe. Token ist 30 Minuten gültig.",
|
||
"newPassword": "Neues Passwort (min. 12 Zeichen)",
|
||
"confirmPassword": "Passwort bestätigen",
|
||
"submit": "Passwort setzen",
|
||
"backToLogin": "← Zurück zum Login",
|
||
"mismatch": "Passwörter stimmen nicht überein",
|
||
"success": "Passwort erfolgreich gesetzt. Du wirst weitergeleitet …",
|
||
"failed": "Reset fehlgeschlagen"
|
||
},
|
||
"setup": {
|
||
"title": "Erst-Einrichtung",
|
||
"intro": "Lege den Admin-Account an, gib die öffentliche FQDN an und – optional – einen Lizenzschlüssel. Ohne Lizenz startet eine 30-Tage-Trial.",
|
||
"preflightTitle": "Vor dem Klick auf \"Setup abschließen\"",
|
||
"preflightDesc": "Die FQDN muss bereits per DNS auf diese Box zeigen (A/AAAA-Record). Ohne DNS-Auflösung scheitert die spätere ACME-HTTP-01-Challenge — dein Browser-URL muss schon jetzt diese FQDN sein, sonst klemmt's spätestens beim ersten Let's-Encrypt-Issue.",
|
||
"adminEmail": "Admin-E-Mail",
|
||
"adminEmailHint": "Login-Identifier für die Management-UI. Wird NICHT für ausgehende Mails benutzt (das ist die ACME-E-Mail unten bzw. die SMTP-Settings im Alerts-Channel).",
|
||
"adminPassword": "Admin-Passwort",
|
||
"passwordRule": "Mindestens 12 Zeichen.",
|
||
"fqdn": "Öffentliche FQDN",
|
||
"fqdnHint": "Vollqualifizierter Hostname dieser Box (z. B. eg.example.com). Wird Subject-CN des Self-Signed-Bootstrap-Cert UND wandert in das ACME-Cert sobald Let's Encrypt ausgestellt hat. Muss per DNS auf die Public-IP zeigen.",
|
||
"fqdnInvalid": "Sieht nicht wie eine gültige FQDN aus (mindestens label.tld).",
|
||
"acmeEmail": "ACME-/Let's-Encrypt-E-Mail",
|
||
"acmeEmailHint": "Wird Let's Encrypt als Account-Contact mitgegeben (Expiry-Warnungen + Compliance-Mails von LE). Kann gleich der Admin-E-Mail sein, muss aber nicht.",
|
||
"licenseKey": "Lizenzschlüssel (optional)",
|
||
"licenseKeyHint": "Wenn vorhanden: 30-Tage-Trial wird übersprungen, Features unmittelbar freigeschaltet. Kann auch später unter License nachgereicht werden.",
|
||
"submit": "Setup abschließen",
|
||
"successTitle": "Setup abgeschlossen",
|
||
"successHint": "Du wirst zur Anmeldung weitergeleitet."
|
||
},
|
||
"dashboard": {
|
||
"title": "Dashboard",
|
||
"welcomeHint": "Übersicht aller laufenden EdgeGuard-Komponenten."
|
||
},
|
||
"domains": {
|
||
"title": "Domains",
|
||
"intro": "Verwalte FQDNs, die HAProxy terminiert. Optionales Primary-Backend als Catch-all; Pfad-Routing via Routing-Regeln.",
|
||
"addDomain": "Domain hinzufügen",
|
||
"editDomain": "Domain bearbeiten",
|
||
"backToList": "Zurück zur Übersicht",
|
||
"settingsCard": "Domain-Einstellungen",
|
||
"moreOnDetailPage": "HSTS, Rate-Limit, Wartungsmodus und mehr auf der Domain-Detailseite nach dem Speichern.",
|
||
"emptyTitle": "Noch keine Domains.",
|
||
"emptyDesc": "Lege deine erste Domain an — HAProxy terminiert dann TLS für diesen Hostnamen und routet an das gewählte Backend.",
|
||
"name": "Name",
|
||
"active": "Aktiv",
|
||
"primaryBackend": "Primary-Backend",
|
||
"primaryBackendHint": "Catch-all-Backend für Requests, die kein Routing-Regel-Match haben. Optional — leer lassen, wenn alles über Routing-Regeln läuft.",
|
||
"selectBackend": "Backend wählen",
|
||
"noBackend": "kein Backend",
|
||
"quickBackendBtn": "+ neu",
|
||
"quickBackendBtnHint": "Backend + ersten Upstream-Server in einem Klick anlegen — spart den Umweg über die Backends-Seite.",
|
||
"quickBackendTitle": "Backend schnell anlegen",
|
||
"quickBackendName": "Backend-Name",
|
||
"quickBackendScheme": "Schema",
|
||
"quickBackendAddress": "Server-Adresse (Erster Upstream)",
|
||
"quickBackendPort": "Port",
|
||
"quickBackendCreated": "Backend + Server angelegt und ausgewählt.",
|
||
"quickBackendFailed": "Quick-Backend-Anlage fehlgeschlagen",
|
||
"httpToHttps": "HTTP→HTTPS",
|
||
"hsts": "HSTS",
|
||
"tlsCert": "TLS-Cert",
|
||
"tlsCertValid": "gültig",
|
||
"tlsCertExpiring": "noch {{days}}d",
|
||
"tlsCertExpired": "abgelaufen",
|
||
"tlsCertError": "Fehler",
|
||
"tlsCertNone": "fehlt",
|
||
"tlsCertNoneHint": "Kein eigener TLS-Cert für diese Domain — HAProxy serviert das Bootstrap-Self-Signed-Cert. Im SSL-Tab issuen.",
|
||
"hstsMaxAge": "HSTS max-age (Sek.)",
|
||
"hstsMaxAgeHint": "Wie lange Browser den HTTPS-Zwang cachen. Empfehlung 31536000 (1 Jahr).",
|
||
"hstsSubdomains": "includeSubDomains",
|
||
"hstsSubdomainsHint": "Erstreckt HSTS auf alle Sub-Domains. Nur aktivieren wenn alle Subs ausschließlich HTTPS sprechen.",
|
||
"hstsPreload": "preload",
|
||
"hstsPreloadHint": "Setzt den preload-Flag damit die Domain in die hstspreload.org-Liste aufgenommen werden kann. Voraussetzung: max-age ≥ 31536000 + includeSubDomains an.",
|
||
"maintenance": "Wartungs-Modus",
|
||
"maintenanceHint": "An: alle Requests auf diese Domain bekommen direkt 503 von HAProxy. Backends werden nicht kontaktiert.",
|
||
"maintenanceMessage": "Wartungs-Meldung",
|
||
"maintenanceMessagePlaceholder": "Service vorübergehend nicht verfügbar.",
|
||
"wwwRedirect": "www-Redirect",
|
||
"wwwRedirectHint": "Kanonisiert die Domain. „nach naked\": Name=example.com → www.example.com leitet auf example.com. „nach www\": Name=www.example.com → example.com leitet auf www.example.com.",
|
||
"wwwRedirectNone": "Kein Redirect",
|
||
"wwwRedirectToNaked": "→ naked (ohne www)",
|
||
"wwwRedirectToWWW": "→ www",
|
||
"rateLimit": "Rate-Limit (pro Client-IP)",
|
||
"rateLimitHint": "Max. Requests pro Sekunde je Client-IP. HAProxy zählt über ein 10-Sekunden-Fenster pro Stick-Table (max. 100k IPs). 0 = aus.",
|
||
"maxBody": "Max. Request-Body",
|
||
"maxBodyHint": "Cap auf Content-Length-Header. Größere Requests bekommen 413. Nicht erkannt: Chunked-Bodies (HAProxy bufferd nicht standardmäßig). 0 = aus.",
|
||
"disableH3": "HTTP/3 (QUIC) deaktivieren",
|
||
"disableH3Hint": "Unterdrückt den Alt-Svc-Response-Header für diese Domain. Browser erhalten keinen Hinweis auf h3/QUIC und bleiben auf h2/http1.1. Sinnvoll bei Clients die Probleme mit QUIC-Verbindungen melden.",
|
||
"headersBtn": "Headers",
|
||
"headersTitle": "Response-Headers — {{name}}",
|
||
"headersHint": "Diese Header werden von HAProxy auf jede Response für diese Domain gesetzt (http-response set-header). Reihenfolge via Position.",
|
||
"headersEmpty": "Noch keine Custom-Headers konfiguriert.",
|
||
"addHeader": "Header hinzufügen",
|
||
"editHeader": "Header bearbeiten",
|
||
"headerName": "Name",
|
||
"headerNameHint": "Nur Buchstaben, Zahlen, Bindestriche. Pro Domain case-insensitive eindeutig.",
|
||
"headerNamePattern": "Nur a-z, A-Z, 0-9, '-'",
|
||
"headerValue": "Wert",
|
||
"headerPosition": "Position",
|
||
"headerDeleteConfirm": "Header „{{name}}\" wirklich löschen?",
|
||
"settingsSection": "HAProxy-Einstellungen",
|
||
"notes": "Notizen",
|
||
"actions": "Aktionen",
|
||
"edit": "Bearbeiten",
|
||
"delete": "Löschen",
|
||
"deleteConfirm": "Domain {{name}} wirklich löschen?"
|
||
},
|
||
"backends": {
|
||
"title": "Backends",
|
||
"intro": "Upstream-Pools (Backend = N Server). HAProxy verteilt laut LB-Algorithmus; Health-Check-Pfad aktiviert HTTP-Probes alle 5s pro Server.",
|
||
"addBackend": "Backend-Pool hinzufügen",
|
||
"emptyTitle": "Noch keine Backend-Pools.",
|
||
"emptyDesc": "Lege deinen ersten Pool an — definiere Upstream-Server (z. B. App-Server / Mailbox-Knoten) damit Domains darauf routen können.",
|
||
"editBackend": "Backend-Pool bearbeiten",
|
||
"name": "Name",
|
||
"scheme": "Schema",
|
||
"target": "Ziel",
|
||
"healthCheck": "Health-Check-Pfad",
|
||
"liveStatus": "HAProxy-Status",
|
||
"active": "Aktiv",
|
||
"usedBy": "Genutzt von",
|
||
"noDomain": "keine Domain",
|
||
"attachedDomains": "Domains",
|
||
"attachedDomainsHint": "Domains, die dieses Backend als Primary verwenden. Auswahl umkonfiguriert die Domains direkt — gleiche Quelle wie der Backend-Picker im Domain-Modal.",
|
||
"selectDomains": "Domains wählen",
|
||
"lbAlgo": "Load-Balancing",
|
||
"lbAlgoHint": "roundrobin = gleichmäßig, leastconn = an den Server mit wenigsten Verbindungen, source = sticky per Client-IP (für stateful Apps ohne shared session).",
|
||
"websocket": "WebSocket-Support",
|
||
"websocketHint": "An: erlaubt langlebige WebSocket-/Long-Poll-Verbindungen (z. B. Proxmox-Console, SSH-WS, AsyncAPI) — Tunnel-Idle 1h statt 60s. Aus: strikte HTTP-Timeouts.",
|
||
"forceHttp1": "HTTP/1.1 erzwingen",
|
||
"forceHttp1Hint": "Deaktiviert HTTP/2 (h2) auf der Backend-Verbindung — HAProxy handelt nur noch HTTP/1.1 aus. Nötig für Backends die kein h2 unterstützen (z. B. ältere nginx-Konfigurationen ohne h2-Modul, Legacy-Apps).",
|
||
"servers": "Server",
|
||
"noServers": "kein Server",
|
||
"nServers": "{{n}} Server",
|
||
"serversIn": "Server in „{{name}}\"",
|
||
"serverHintCreate": "Speichern legt nur den Pool an. Server kommen im nächsten Schritt — Pool öffnen → „Server hinzufügen\".",
|
||
"actions": "Aktionen",
|
||
"deleteConfirm": "Backend-Pool {{name}} wirklich löschen? Alle Server im Pool werden mitentfernt.",
|
||
"backToList": "Zurück zur Übersicht",
|
||
"settingsCard": "Pool-Einstellungen",
|
||
"healthCheckHint": "HTTP-Pfad den HAProxy alle 5s pro Server prüft. Leer lassen = kein Health-Check.",
|
||
"server": {
|
||
"intro": "Upstream-Server im Pool. Reihenfolge in HAProxy egal — der LB-Algorithmus entscheidet.",
|
||
"add": "Server hinzufügen",
|
||
"edit": "Server bearbeiten",
|
||
"name": "Server-Name",
|
||
"address": "Adresse",
|
||
"port": "Port",
|
||
"target": "Endpoint",
|
||
"weight": "Gewicht",
|
||
"weightHint": "0–256. Höher = mehr Traffic. 100 = Standard.",
|
||
"backup": "Backup",
|
||
"backupHint": "Backup-Server werden nur angesprochen, wenn alle primären Server (non-backup) down sind.",
|
||
"empty": "Noch keine Server im Pool. „Server hinzufügen\" startet damit.",
|
||
"deleteConfirm": "Server {{name}} wirklich löschen?"
|
||
}
|
||
},
|
||
"routing": {
|
||
"title": "Routing-Regeln",
|
||
"intro": "Pfad-Präfix → Backend-Mapping pro Domain. Niedrige Priority gewinnt; Catch-all per Domain.primary_backend.",
|
||
"addRule": "Regel hinzufügen",
|
||
"emptyTitle": "Noch keine Routing-Regeln.",
|
||
"emptyDesc": "Pfad-spezifisches Routing — z. B. domain.tld/api → Backend A, domain.tld/* → Backend B. Optional. Ohne Regeln nutzt HAProxy die Primary-Backend-Auswahl der Domain.",
|
||
"editRule": "Regel bearbeiten",
|
||
"domain": "Domain",
|
||
"pathPrefix": "Pfad-Präfix",
|
||
"backend": "Backend",
|
||
"priority": "Priorität",
|
||
"active": "Aktiv",
|
||
"actions": "Aktionen",
|
||
"selectDomain": "Domain wählen",
|
||
"selectBackend": "Backend wählen",
|
||
"deleteConfirm": "Diese Routing-Regel wirklich löschen?"
|
||
},
|
||
"cluster": {
|
||
"title": "Cluster",
|
||
"intro": "{{count}} Node(s) registriert. Multi-Node (KeyDB-AA + PG-Streaming-Replication + Leader-Election) folgt schrittweise.",
|
||
"id": "Node-ID",
|
||
"fqdn": "FQDN",
|
||
"role": "Rolle",
|
||
"joinedAt": "Beigetreten",
|
||
"self": "diese Node",
|
||
"drift": "Drift",
|
||
"modeSingle": "Single-Node",
|
||
"modeCluster": "Cluster",
|
||
"health": {
|
||
"ok": "OK",
|
||
"degraded": "degraded",
|
||
"split-brain": "split-brain"
|
||
},
|
||
"selfTitle": "Dieser Knoten",
|
||
"noSelf": "Selbst-Registrierung in ha_nodes fehlgeschlagen — Setup-Wizard durchlaufen.",
|
||
"peersTitle": "Peers ({{count}})",
|
||
"singleNodeTitle": "Single-Node-Modus",
|
||
"singleNodeDesc": "Nur diese Box bekannt. Zusätzlicher Knoten? `edgeguard-ctl cluster-join` (kommt in Phase 3.2).",
|
||
"driftBanner": "Config-Drift erkannt",
|
||
"driftBannerDesc": "Ein oder mehrere Peers haben einen anderen Config-Hash als dieser Node. Entweder stehen noch Änderungen in der Outbox, oder auf einem Peer wurde direkt in der DB editiert. Warte bis die Outbox leer ist oder starte Diagnostics.",
|
||
"col": {
|
||
"node": "Knoten",
|
||
"status": "Status",
|
||
"role": "Rolle",
|
||
"apiUrl": "API-URL",
|
||
"configHash": "Config-Hash",
|
||
"version": "Version",
|
||
"lastSeen": "Last seen",
|
||
"mgmtIp": "MGMT-IP",
|
||
"load": "Load 1/5/15",
|
||
"mem": "Memory",
|
||
"disk": "Disk",
|
||
"conntrack": "Conntrack",
|
||
"uptime": "Uptime",
|
||
"fetchMs": "Fetch"
|
||
},
|
||
"loadTitle": "Per-Node Resources (mTLS-Aggregator)",
|
||
"loadEmpty": "Keine Node-Resources verfügbar — Agent-Listener nicht erreichbar?",
|
||
"certCardTitle": "Cluster-TLS-Zertifikate",
|
||
"certCALabel": "Cluster-CA",
|
||
"certPeerLabel": "Peer-Cert (diese Node)",
|
||
"certExpiry": "Läuft ab in",
|
||
"renewSelfBtn": "Peer-Cert erneuern",
|
||
"renewSelfConfirm": "Peer-Cert mit lokaler CA neu signieren (1 Jahr)? edgeguard-api Restart danach erforderlich.",
|
||
"certRenewedRestartHint": "Cert erneuert — bitte sudo systemctl restart edgeguard-api ausführen.",
|
||
"certRenewFailed": "Cert-Erneuerung fehlgeschlagen",
|
||
"removePeerBtn": "Entfernen",
|
||
"removePeerConfirmTitle": "Peer wirklich aus dem Cluster entfernen?",
|
||
"removePeerConfirmDesc": "{{fqdn}} wird aus ha_nodes gelöscht. Firewall-Renderer entfernt seine IP aus dem peer_ipv4-Set. Auf der Peer-Seite läuft edgeguard-api weiter; manuell stoppen + cluster-tls löschen für vollständigen Decommission.",
|
||
"removePeerOk": "Peer entfernt.",
|
||
"removePeerFailed": "Peer-Entfernung fehlgeschlagen",
|
||
"generateJoinToken": "Join-Token erzeugen",
|
||
"joinTokenTitle": "Cluster-Join-Token",
|
||
"joinTokenOneShot": "Einmalig anzeigen, einmalig nutzbar",
|
||
"joinTokenOneShotDesc": "Token jetzt sicher übertragen — er erscheint nie wieder. Gültig bis {{expires}}. Beim Einlösen via cluster-join wird er server-seitig als verbraucht markiert.",
|
||
"joinTokenFailed": "Token-Generierung fehlgeschlagen",
|
||
"joinCmdLabel": "Auf dem neuen Node ausführen:"
|
||
},
|
||
"ssl": {
|
||
"title": "SSL-Zertifikate",
|
||
"intro": "TLS-Zertifikate verwalten — entweder per Let's Encrypt automatisch ausstellen oder eigene PEMs hochladen. HAProxy lädt nach jeder Änderung automatisch neu.",
|
||
"tabLE": "Let's Encrypt",
|
||
"tabUpload": "Eigenes Zertifikat",
|
||
"leIntro": "Domain wählen, Issue klicken — EdgeGuard löst HTTP-01 über die ACME-Webroot, schreibt das PEM nach /etc/edgeguard/tls/ und reloaded HAProxy.",
|
||
"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 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",
|
||
"expiredAgo": "abgelaufen vor {{days}} Tagen",
|
||
"actions": "Aktionen",
|
||
"issueButton": "Zertifikat anfordern",
|
||
"uploadButton": "Hochladen",
|
||
"issueSuccess": "Zertifikat ausgestellt + installiert.",
|
||
"uploadSuccess": "Zertifikat hochgeladen + installiert.",
|
||
"renewBtn": "Renew",
|
||
"renewConfirmTitle": "Zertifikat jetzt erneuern?",
|
||
"renewConfirmDesc": "Triggert eine ACME-HTTP-01-Challenge für {{domain}}. Let's Encrypt hat Rate-Limits (50 Issues/Domain/Woche) — nicht öfter als nötig nutzen.",
|
||
"renewSuccess": "Zertifikat erneuert + installiert.",
|
||
"renewFailed": "Renewal fehlgeschlagen",
|
||
"deleteConfirm": "Zertifikat für {{domain}} löschen? HAProxy fällt für diese Domain auf das Default-Cert zurück.",
|
||
"installedTitle": "Installierte Zertifikate",
|
||
"lastRenewed": "Zuletzt erneuert",
|
||
"statTotal": "Zertifikate gesamt",
|
||
"statExpiring": "< 30 Tage gültig",
|
||
"statExpired": "Abgelaufen",
|
||
"statErrors": "Mit Fehler",
|
||
"relAgo": {
|
||
"justNow": "gerade eben",
|
||
"minutes": "vor {{n}} min",
|
||
"hours": "vor {{n}} h",
|
||
"days": "vor {{n}} Tagen"
|
||
},
|
||
"emptyTitle": "Noch keine Zertifikate installiert.",
|
||
"emptyDesc": "Nutze die Tabs oben — Let's Encrypt issued vollautomatisch per HTTP-01-Challenge, oder lade ein eigenes PEM hoch. Bis dahin liefert HAProxy das Bootstrap-Self-Signed-Cert für alle Domains.",
|
||
"certPem": "Zertifikat (PEM)",
|
||
"chainPem": "Chain (PEM, optional)",
|
||
"keyPem": "Private Key (PEM)"
|
||
},
|
||
"settings": {
|
||
"title": "Einstellungen",
|
||
"intro": "System-Information, Setup-Status und Admin-Account.",
|
||
"systemInfo": "System",
|
||
"version": "Version",
|
||
"status": "Status",
|
||
"dbSize": "PostgreSQL-DB-Größe",
|
||
"dbSizeTop": "Top-Tabellen",
|
||
"upgradeStatusCardTitle": "Letzter Update-Versuch",
|
||
"upgradeStatusStarted": "Gestartet",
|
||
"upgradeStatusFinished": "Beendet",
|
||
"upgradeStatusResult": "Ergebnis",
|
||
"upgradeStatusState": "Status",
|
||
"upgradeStatusOk": "Erfolgreich",
|
||
"upgradeStatusShowLog": "Vollständiges Log anzeigen ({{n}} Zeilen)",
|
||
"actionsCardTitle": "System-Aktionen",
|
||
"actionsHint": "Manuelle Trigger für Operator-Tasks die sonst nur automatisch beim Speichern in den jeweiligen Seiten passieren. Sinnvoll nach SSH-Eingriffen (z. B. /etc/edgeguard/tls/ manuell befüllt).",
|
||
"haproxyReloadBtn": "HAProxy reload",
|
||
"haproxyReloadOk": "HAProxy neu geladen.",
|
||
"haproxyReloadFailed": "HAProxy-Reload fehlgeschlagen",
|
||
"renderConfigsBtn": "Configs neu rendern (HAProxy)",
|
||
"renderConfigsOk": "Configs neu gerendert + reloaded.",
|
||
"renderConfigsFailed": "Config-Render fehlgeschlagen",
|
||
"backupNowBtn": "Backup jetzt erstellen",
|
||
"backupNowOk": "Backup ausgelöst — Status in der Backups-Seite verfolgen.",
|
||
"backupNowFailed": "Backup-Trigger fehlgeschlagen",
|
||
"serviceRestartCardTitle": "Dienste neu starten",
|
||
"serviceRestartBtn": "Restart",
|
||
"serviceRestartOk": "{{service}} wurde neu gestartet.",
|
||
"serviceRestartFailed": "Neustart von {{service}} fehlgeschlagen",
|
||
"serviceRestartHint": "Startet den Dienst via systemctl restart. edgeguard-api und PostgreSQL sind bewusst ausgeschlossen.",
|
||
"setupInfo": "Setup",
|
||
"adminEmail": "Admin-E-Mail",
|
||
"fqdn": "FQDN",
|
||
"setupCompleted": "Setup abgeschlossen",
|
||
"emailsCardTitle": "Kontakt-E-Mails",
|
||
"adminEmailHint": "Wird beim Login als Admin-Identifier genutzt. Änderung erfordert Re-Login mit der neuen Adresse.",
|
||
"acmeEmail": "ACME-E-Mail",
|
||
"acmeEmailHint": "Wird Let's Encrypt als Account-Contact mitgegeben. Bestehende Zertifikate sind nicht betroffen — die nächste Renew-Operation registriert die neue Adresse.",
|
||
"emailsSaved": "E-Mails aktualisiert.",
|
||
"emailsFailed": "E-Mail-Update fehlgeschlagen",
|
||
"maintenanceCardTitle": "Wartungs-Modus (gesamte Box)",
|
||
"maintenanceOn": "Aktiv — alle Customer-Domains liefern 503. Mgmt-UI bleibt erreichbar.",
|
||
"maintenanceOff": "Inaktiv — Customer-Traffic wird normal an die Backends geroutet.",
|
||
"maintenanceMessage": "Wartungs-Meldung",
|
||
"maintenanceMessagePlaceholder": "Service vorübergehend nicht verfügbar — wir sind in Kürze zurück.",
|
||
"maintenanceMessageHint": "Wird im Response-Body der 503-Antworten an Endkunden mitgeschickt. Plain text, max. 500 Zeichen.",
|
||
"maintenanceHint": "Schaltet HAProxy auf :443 in einen Default-503-Modus. Pro-Domain-Maintenance (Domains-Seite) wird hiervon übersteuert. Mgmt-UI auf :3443 ist NICHT betroffen.",
|
||
"maintenanceSaved": "Wartungs-Modus aktualisiert.",
|
||
"maintenanceFailed": "Wartungs-Modus-Toggle fehlgeschlagen",
|
||
"maintenanceActiveTitle": "Wartungs-Modus aktiv",
|
||
"maintenanceActiveDesc": "Alle Customer-Domains liefern aktuell 503. Mgmt-UI ist erreichbar — Customer-Traffic NICHT. Settings → Wartungs-Modus zum Deaktivieren.",
|
||
"backupRetentionCardTitle": "Backup-Aufbewahrung",
|
||
"backupRetentionUnit": "Backups",
|
||
"backupRetentionDefault": "Default ({{n}} Backups) — pro Tag bei täglichem Schedule = {{n}} Tage History.",
|
||
"backupRetentionCustom": "Custom — die letzten {{n}} erfolgreichen Backups werden behalten, ältere werden nach jedem Backup-Lauf gelöscht.",
|
||
"backupRetentionHint": "0 = Default (14). 1-365 = Custom-Limit. Bedenke: jedes Backup ist ein voller pg_dump + Files-tar (typisch 50-500 MB). Bei /var-Disk-Druck eher reduzieren.",
|
||
"backupRetentionSaved": "Backup-Retention aktualisiert.",
|
||
"backupRetentionFailed": "Backup-Retention-Update fehlgeschlagen",
|
||
"auditRetentionCardTitle": "Audit-Log-Aufbewahrung",
|
||
"auditRetentionUnit": "Tage",
|
||
"auditRetentionDefault": "Default ({{n}} Tage) — Audit-Einträge älter als {{n}} Tage werden täglich gepruned.",
|
||
"auditRetentionCustom": "Custom — Audit-Einträge werden für {{n}} Tage behalten.",
|
||
"auditRetentionHint": "0 = Default (90). 1-3650 (= 10 Jahre) für Compliance (SOX 7y = 2555, DSGVO meist <= 365). Cleanup läuft im Scheduler täglich.",
|
||
"auditRetentionSaved": "Audit-Retention aktualisiert.",
|
||
"auditRetentionFailed": "Audit-Retention-Update fehlgeschlagen",
|
||
"autoUpdateCardTitle": "Automatische Updates",
|
||
"autoUpdateOn": "Aktiviert — edgeguard-Pakete werden täglich automatisch installiert.",
|
||
"autoUpdateOff": "Deaktiviert — Updates müssen manuell über den Banner installiert werden.",
|
||
"autoUpdateHint": "Whitelist umfasst nur edgeguard, edgeguard-api, edgeguard-ui. Andere Pakete bleiben unter manueller Kontrolle. Verlangt unattended-upgrades (Distro-Standard auf Trixie). Conf-File: /etc/apt/apt.conf.d/52edgeguard-auto-updates.",
|
||
"autoUpdateToggled": "Auto-Update-Einstellung gespeichert.",
|
||
"autoUpdateFailed": "Auto-Update-Toggle fehlgeschlagen",
|
||
"ipv6CardTitle": "IPv6",
|
||
"ipv6On": "Aktiviert — HAProxy bindet zusätzlich zu IPv4 auf [::]:80, [::]:443 und [::]:3443.",
|
||
"ipv6Off": "Deaktiviert — HAProxy lauscht nur auf IPv4.",
|
||
"ipv6Hint": "Erfordert IPv6-Konfiguration auf den Netzwerk-Interfaces dieses Servers. HAProxy wird nach der Änderung automatisch neu geladen.",
|
||
"ipv6Toggled": "IPv6-Einstellung gespeichert.",
|
||
"ipv6Failed": "IPv6-Toggle fehlgeschlagen",
|
||
"passwordCardTitle": "Admin-Passwort ändern",
|
||
"currentPassword": "Aktuelles Passwort",
|
||
"newPassword": "Neues Passwort",
|
||
"newPasswordHint": "Mindestens 12 Zeichen. Wird mit bcrypt gehasht.",
|
||
"confirmPassword": "Neues Passwort bestätigen",
|
||
"changePasswordBtn": "Passwort ändern",
|
||
"passwordChanged": "Passwort geändert.",
|
||
"passwordChangeFailed": "Passwort-Änderung fehlgeschlagen",
|
||
"passwordMismatch": "Die Passwörter stimmen nicht überein.",
|
||
"passwordMinLen": "Mindestens 12 Zeichen erforderlich."
|
||
},
|
||
"update": {
|
||
"available": "Update verfügbar: Version {{version}}",
|
||
"multiPackageHint": "{{count}} Pakete werden aktualisiert.",
|
||
"applyNow": "Jetzt installieren",
|
||
"confirmTitle": "Update jetzt installieren?",
|
||
"confirmDesc": "Pakete werden auf Version {{version}} aktualisiert. edgeguard-api + scheduler restarten (~2-5s), HAProxy/nft/WG/Squid/Unbound/Chrony laufen durch.",
|
||
"checkNow": "Auf Updates prüfen",
|
||
"checkNowHint": "Server-seitigen apt-Cache jetzt aktualisieren und nach neueren Versionen schauen.",
|
||
"updateReady": "Update bereit: v{{version}}",
|
||
"checkDone": "Update verfügbar",
|
||
"noUpdate": "Keine neuen Updates",
|
||
"checkFailed": "Update-Check fehlgeschlagen",
|
||
"running": "Update läuft …",
|
||
"waitHint": "Bitte warten — die Seite lädt automatisch neu sobald die neue Version live ist.",
|
||
"success": "Update auf {{version}} abgeschlossen.",
|
||
"failed": "Update fehlgeschlagen",
|
||
"stepDownload": "Pakete laden",
|
||
"stepInstall": "Installation",
|
||
"stepRestart": "Service-Restart",
|
||
"stepVerify": "Verifizierung"
|
||
},
|
||
"wg": {
|
||
"title": "WireGuard",
|
||
"intro": "VPN-Tunnel über WireGuard. Server-Modus = wir lauschen für Peers; Client-Modus = wir verbinden zu einem festen Upstream. Privater Schlüssel liegt verschlüsselt in der DB.",
|
||
"tabs": { "servers": "Server-Tunnel", "clients": "Client-Tunnel" },
|
||
"serverIntro": "Server-Tunnel hosten ein Peer-Roster — z.B. Mitarbeiter-Geräte oder Niederlassungen. Pro Peer bekommt der Operator eine .conf zum Download (oder QR-Code für Mobile).",
|
||
"clientIntro": "Client-Tunnel verbinden EdgeGuard zu einem fremden WireGuard-Server (z.B. HQ-Datacenter). Allowed-IPs steuert, welcher Traffic durch den Tunnel geroutet wird.",
|
||
"iface": {
|
||
"name": "Name",
|
||
"namePattern": "wg gefolgt von Kleinbuchstaben/Ziffern/-, max. 15 Zeichen",
|
||
"nameExtra": "Empfehlung: wg0, wg1, wg-hq …",
|
||
"address": "Adresse (CIDR)",
|
||
"addressExtra": "Tunnel-IP der Box, z.B. 10.99.0.1/24 für /24-Pool",
|
||
"listenPort": "Listen-Port",
|
||
"publicKey": "Public-Key",
|
||
"privateKey": "Private-Key (paste)",
|
||
"privateKeyExtra": "Nur ausfüllen wenn nicht generieren — base64 32 Byte. Wird verschlüsselt gespeichert.",
|
||
"peerEndpoint": "Peer-Endpoint",
|
||
"peerPublicKey": "Peer Public-Key",
|
||
"peerPSK": "Pre-Shared-Key (PSK)",
|
||
"peerPSKExtra": "Optional, zusätzliche Schicht",
|
||
"allowedIPs": "Allowed IPs",
|
||
"allowedIPsExtra": "Was durch den Tunnel geroutet wird. Default = full-tunnel.",
|
||
"keepalive": "Persistent Keepalive (sec)",
|
||
"mtu": "MTU",
|
||
"zone": "Firewall-Zone",
|
||
"description": "Beschreibung",
|
||
"addServer": "Server-Tunnel hinzufügen",
|
||
"editServer": "Server-Tunnel bearbeiten",
|
||
"addClient": "Client-Tunnel hinzufügen",
|
||
"editClient": "Client-Tunnel bearbeiten",
|
||
"emptyServerTitle": "Noch keine WireGuard-Server-Tunnel.",
|
||
"emptyServerDesc": "Server-Modus: diese Box hört auf einem UDP-Port und akzeptiert Peer-Verbindungen (z. B. Roadwarrior-User, Site-to-Site-Niederlassungen).",
|
||
"emptyClientTitle": "Noch keine WireGuard-Client-Tunnel.",
|
||
"emptyClientDesc": "Client-Modus: diese Box verbindet sich zu einem externen WireGuard-Server (z. B. HQ-Datacenter, Cloud-Anbindung).",
|
||
"upstream": "Upstream-Peer",
|
||
"deleteConfirm": "Tunnel {{name}} wirklich löschen? wg-quick wird gestoppt.",
|
||
"keys": "Schlüssel",
|
||
"generateExtra": "Wenn an: Server erzeugt ein neues Curve25519-Keypair beim Speichern.",
|
||
"generateOn": "Server generiert",
|
||
"generateOff": "Manuell paste",
|
||
"editKeyWarning": "Achtung: neue Schlüssel = bestehende Peer-Configs ungültig. Nur ändern wenn explizit gewollt.",
|
||
"clientRoutes": "Push-Routes (Client)",
|
||
"clientRoutesExtra": "Zusätzliche Netzwerke die der Peer über den Tunnel erreichen soll, z. B. 10.0.10.0/24 für ein LAN hinter der Box. Kommagetrennt. Wird automatisch in den Peer-Config-Download eingetragen."
|
||
},
|
||
"peers": {
|
||
"button": "Peers",
|
||
"drawerTitle": "Peer-Roster"
|
||
},
|
||
"peer": {
|
||
"name": "Name",
|
||
"publicKey": "Public-Key",
|
||
"publicKeyExtra": "Wird vom Peer-Gerät erzeugt; hier nur paste-bar wenn der Peer schon ein Key-Pair hat.",
|
||
"allowedIPs": "Allowed IPs",
|
||
"allowedIPsExtra": "Die IP die dieser Peer im Tunnel bekommt — muss /32 sein (z. B. 10.0.10.2/32), nicht das ganze Subnetz (/24). Mit /24 würde der Server allen Verkehr zu diesem einen Peer routen und andere Peers wären nicht erreichbar.",
|
||
"keepalive": "Keepalive (sec)",
|
||
"keepaliveExtra": "0 = aus. Empfohlen 25 hinter NAT.",
|
||
"lastHandshake": "Letzter Handshake",
|
||
"never": "nie",
|
||
"description": "Beschreibung",
|
||
"add": "Peer hinzufügen",
|
||
"edit": "Peer bearbeiten",
|
||
"deleteConfirm": "Peer {{name}} wirklich entfernen?",
|
||
"emptyTitle": "Noch keine Peers in diesem Tunnel.",
|
||
"emptyDesc": "Lege Peers an — pro Peer eine WireGuard-Identität (Public-Key + Allowed-IPs). Server-generierte Keys liefern dir Config-Download bzw. QR-Code für Mobile-Clients direkt mit.",
|
||
"keys": "Schlüssel",
|
||
"generateExtra": "Wenn an: Server erzeugt für diesen Peer ein Keypair und kann die Config / QR-Code ausliefern. Wenn aus: nur den Public-Key paste-en — keine Config-Download möglich.",
|
||
"pskExtra": "Wenn an: Server generiert einen 32-Byte PSK für diesen Peer.",
|
||
"pskOn": "PSK generieren",
|
||
"pskOff": "kein PSK",
|
||
"downloadConf": "wg-quick.conf herunterladen",
|
||
"qrTitle": "WireGuard-QR",
|
||
"qrHint": "Mit der WireGuard-App (iOS/Android) scannen: \"Tunnel hinzufügen\" → \"QR-Code scannen\". Endpoint im Download-Conf bitte vor Verwendung anpassen.",
|
||
"online": "Online",
|
||
"offline": "Offline",
|
||
"traffic": "Traffic"
|
||
}
|
||
},
|
||
"dashboard": {
|
||
"title": "Dashboard",
|
||
"welcomeHint": "EdgeGuard-Übersicht — Health, Counts, Live-Status der wichtigsten Dienste.",
|
||
"kpi": {
|
||
"domains": "Domains",
|
||
"backends": "Backends",
|
||
"ifaces": "Interfaces",
|
||
"fwRules": "FW-Regeln",
|
||
"natRules": "NAT-Regeln",
|
||
"wg": "WG-Verbindungen"
|
||
},
|
||
"wgCard": {
|
||
"title": "WireGuard",
|
||
"empty": "Noch kein WG-Tunnel angelegt."
|
||
},
|
||
"firewallCard": {
|
||
"title": "Firewall",
|
||
"zones": "Zonen",
|
||
"activeRules": "{{rules}} aktive Regeln · {{nat}} NAT"
|
||
},
|
||
"sslCard": {
|
||
"title": "SSL-Zertifikate",
|
||
"total": "Verwaltete Zertifikate",
|
||
"expiringSoon": "{{count}} läuft bald ab (< 30 Tage)",
|
||
"allFresh": "Alle Zertifikate haben > 30 Tage Restlaufzeit."
|
||
},
|
||
"clusterCard": {
|
||
"title": "Cluster",
|
||
"nodes": "Knoten",
|
||
"modeSingle": "Single-Node",
|
||
"modeCluster": "Cluster",
|
||
"drift": "Config-Drift erkannt",
|
||
"health": {
|
||
"ok": "OK",
|
||
"degraded": "degraded",
|
||
"split-brain": "split-brain"
|
||
}
|
||
},
|
||
"routingCard": {
|
||
"title": "Routing",
|
||
"domains": "Domains",
|
||
"backends": "Backends",
|
||
"attached": "{{count}}/{{total}} Domains haben einen Primary-Backend"
|
||
},
|
||
"systemCard": {
|
||
"title": "System",
|
||
"version": "Version",
|
||
"api": "API",
|
||
"ifaces": "Interfaces",
|
||
"wg": "WireGuard"
|
||
},
|
||
"alertsCard": {
|
||
"title": "Aktuelle Alerts",
|
||
"viewAll": "Alle anzeigen"
|
||
},
|
||
"onboardingTitle": "Willkommen bei EdgeGuard",
|
||
"onboardingIntro": "Frische Box — hier die nächsten Schritte um Customer-Traffic zu routen:",
|
||
"onboardingStep1": "Backend-Pool anlegen (App-Server hinter HAProxy)",
|
||
"onboardingStep2": "Domain hinzufügen (FQDN, primary Backend zuweisen)",
|
||
"onboardingStep3": "TLS-Zertifikat ausstellen (Let's Encrypt HTTP-01)",
|
||
"servicesCard": {
|
||
"title": "Service-Status (live, 10s)"
|
||
},
|
||
"activityCard": {
|
||
"title": "Letzte Aktivität (Audit-Log)",
|
||
"empty": "Noch keine Aktivität — Mutationen werden hier protokolliert."
|
||
},
|
||
"haproxyCard": {
|
||
"title": "HAProxy-Backends (live)",
|
||
"empty": "Keine Backend-Stats erreichbar (HAProxy down oder admin.sock-permission)."
|
||
},
|
||
"resCard": {
|
||
"load": "Load",
|
||
"memory": "Memory",
|
||
"disk": "Disk /",
|
||
"free": "frei",
|
||
"conntrack": "Conntrack",
|
||
"uptime": "Uptime"
|
||
}
|
||
},
|
||
"ntp": {
|
||
"title": "Zeitserver (Chrony)",
|
||
"intro": "Chrony als Time-Sync-Daemon (NTP). Quellen oben, Listen-/Serve-Konfig im Settings-Tab. Wenn 'serve_clients' aktiv und LAN-IPs gebound sind, wird die Box selbst zum NTP-Server für das LAN.",
|
||
"tabs": { "pools": "Quellen", "settings": "Settings" },
|
||
"statusCard": {
|
||
"title": "Sync-Status (chronyc tracking)",
|
||
"sync": "Synchronisiert",
|
||
"synced": "Ja",
|
||
"notSynced": "Nein",
|
||
"source": "Quelle",
|
||
"stratum": "Stratum",
|
||
"offset": "Offset",
|
||
"offsetHint": "Zeitdifferenz zur Referenzquelle. Werte > 100 ms sind ungewöhnlich — Netzwerkprobleme oder fehlkonfigurierte Quelle prüfen.",
|
||
"loading": "Lade…"
|
||
},
|
||
"pool": {
|
||
"kind": "Typ",
|
||
"kindPool": "pool — DNS-Round-Robin (mehrere Server aus A-Records)",
|
||
"kindServer": "server — einzelner Host",
|
||
"address": "Adresse / Host",
|
||
"addressExtra": "FQDN (für pool: 0.de.pool.ntp.org) oder IP.",
|
||
"iburst": "iburst",
|
||
"prefer": "prefer",
|
||
"minpoll": "min-poll",
|
||
"maxpoll": "max-poll",
|
||
"options": "Optionen",
|
||
"description": "Beschreibung",
|
||
"add": "Quelle hinzufügen",
|
||
"edit": "Quelle bearbeiten",
|
||
"deleteConfirm": "NTP-Quelle {{addr}} wirklich löschen?",
|
||
"emptyTitle": "Noch keine NTP-Quellen.",
|
||
"emptyDesc": "Ohne konfigurierte Pool/Server-Einträge nutzt chrony nur seine compiled-in Default-Pool (debian.pool.ntp.org). Setze eigene Pools für genauere Zeitabstimmung oder interne Stratum-Server."
|
||
},
|
||
"settings": {
|
||
"intro": "Globale Chrony-Settings. Save reloaded chrony automatisch.",
|
||
"serveClients": "Als NTP-Server für Clients arbeiten",
|
||
"serveClientsExtra": "Wenn aus: chrony agiert nur als Client (port 0). Wenn an + Listen-IP: bindet UDP/123.",
|
||
"listenAddresses": "Listen-Adressen",
|
||
"listenAddressesPlaceholder": "IPs wählen (oder eintippen)",
|
||
"listenAddressesExtra": "Auf welchen IPs chrony :123/UDP bindet. 127.0.0.1+::1 = nur lokal; LAN-IPs öffnen für LAN-Clients (FW-Rule wird automatisch generiert).",
|
||
"allowACL": "Allow-ACL (CIDRs)",
|
||
"allowACLExtra": "Wer darf NTP-Time anfragen.",
|
||
"makestepSecs": "makestep secs",
|
||
"makestepSecsExtra": "Erlaube step (statt slew) wenn offset > N sec.",
|
||
"makestepLimit": "makestep limit",
|
||
"rtcsync": "RTC mit System-Time syncen",
|
||
"rtcsyncExtra": "Hardware-Clock alle 11 min synchron halten — nach Reboot ist die Zeit grob korrekt.",
|
||
"leapsectz": "Leap-Sec TZ",
|
||
"leapsectzExtra": "Optional, z.B. 'right/UTC' für leap-sec über tzdata."
|
||
}
|
||
},
|
||
"dns": {
|
||
"title": "DNS (Unbound)",
|
||
"intro": "Unbound-Resolver auf :53. Lokale Zonen (authoritativ aus DNS-Records) und Forward-Zonen (per stub-zone weiter zu fremden Resolvern). Default-Forwarder für alles andere.",
|
||
"tabs": { "zones": "Zonen", "settings": "Resolver-Settings" },
|
||
"zone": {
|
||
"name": "Zone-Name",
|
||
"nameExtra": "FQDN ohne führenden/abschließenden Punkt — z.B. internal.netcell-it.de",
|
||
"type": "Typ",
|
||
"typeLocal": "local — authoritativ (records hier)",
|
||
"typeForward": "forward — stub-zone zu fremdem Resolver",
|
||
"forwardTo": "Upstream-Resolver",
|
||
"forwardToExtra": "Komma-separierte IP-Liste — z.B. '10.0.0.53, 8.8.8.8'",
|
||
"description": "Beschreibung",
|
||
"records": "Records …",
|
||
"add": "Zone hinzufügen",
|
||
"edit": "Zone bearbeiten",
|
||
"deleteConfirm": "Zone {{name}} mit allen Records wirklich löschen?",
|
||
"emptyTitle": "Noch keine DNS-Zonen.",
|
||
"emptyDesc": "Unbound forwarded standardmäßig alles an die Upstream-Resolver. Lege eine Zone an, um z. B. interne FQDNs (internal.example.com) lokal zu hosten oder einen Upstream-Stub für eine fremde Domain einzurichten."
|
||
},
|
||
"record": {
|
||
"name": "Name",
|
||
"nameExtra": "Relativ zur Zone (z.B. 'mailcow') oder FQDN mit abschließendem Punkt.",
|
||
"type": "Typ",
|
||
"value": "Wert",
|
||
"valueExtra": "RDATA in Textform: A → IP, CNAME → FQDN, MX → 'priority host', TXT → 'string'.",
|
||
"ttl": "TTL (sec)",
|
||
"drawerTitle": "DNS-Records",
|
||
"add": "Record hinzufügen",
|
||
"edit": "Record bearbeiten",
|
||
"deleteConfirm": "Record {{name}} wirklich löschen?",
|
||
"emptyTitle": "Noch keine Records in dieser Zone.",
|
||
"emptyDesc": "A/AAAA/CNAME/MX/TXT-Einträge. Bei local-Zone authoritativ; bei forward-Zone hat das hier keinen Effekt (Upstream gewinnt)."
|
||
},
|
||
"settings": {
|
||
"intro": "Globale Resolver-Settings. Änderungen hier reloaden Unbound automatisch.",
|
||
"listenAddresses": "Listen-Adressen",
|
||
"listenAddressesPlaceholder": "IPs wählen (oder eintippen)",
|
||
"listenAddressesRequired": "Mindestens eine Adresse erforderlich.",
|
||
"listenAddressesExtra": "Mehrfachauswahl aus den IPs die der Kernel kennt. 127.0.0.1 + ::1 = nur lokal; weitere LAN-Iface-IPs (z.B. 10.10.20.3) öffnen den Resolver für LAN-Clients. Eigene IPs lassen sich auch eintippen (Enter).",
|
||
"listenPort": "Port",
|
||
"upstreamForwards": "Default-Forwarders",
|
||
"upstreamForwardsExtra": "Wo geht alles hin was nicht lokal ist. Default 1.1.1.1 + 9.9.9.9.",
|
||
"accessACL": "Access-ACL (CIDRs)",
|
||
"accessACLExtra": "Wer darf diesen Resolver benutzen.",
|
||
"dnssec": "DNSSEC validieren",
|
||
"qnameMin": "QName-Minimisation (privacy)",
|
||
"cacheMin": "Cache min-TTL",
|
||
"cacheMax": "Cache max-TTL"
|
||
}
|
||
},
|
||
"fwd": {
|
||
"title": "Forward-Proxy (Squid)",
|
||
"intro": "Squid-basierter Forward-Proxy auf :3128. ACLs werden top-down nach Priority ausgewertet — first-match wins. Wenn keine Regel passt, gewinnt der Default: nur localnet (10/8, 172.16/12, 192.168/16) darf raus.",
|
||
"helpTitle": "Tipp zur ACL-Reihenfolge",
|
||
"helpBody": "Höhere Priority = wird zuerst geprüft. Beispiel: 'deny .badsite.com' (priority 200) vor 'allow .com' (priority 100). Werte können Listen sein (mehrere Zeilen), Regex je nach acl_type.",
|
||
"name": "Name",
|
||
"nameExtra": "Squid-konformer Bezeichner — Kleinbuchstaben + _, kein Leerzeichen.",
|
||
"aclType": "Typ",
|
||
"aclTypeExtra": "Was Squid prüft (Quelle, Domain, Port, …).",
|
||
"value": "Wert",
|
||
"valueExtra": "Format hängt vom Typ ab — IPs/CIDRs für src/dst, Domain mit führendem . für dstdomain (.example.com matcht auch sub.example.com), Regex für *_regex-Typen.",
|
||
"action": "Aktion",
|
||
"priority": "Priority",
|
||
"priorityExtra": "Höher = wird zuerst geprüft.",
|
||
"comment": "Kommentar",
|
||
"add": "ACL hinzufügen",
|
||
"edit": "ACL bearbeiten",
|
||
"deleteConfirm": "ACL {{name}} wirklich löschen?",
|
||
"emptyTitle": "Noch keine Forward-Proxy-ACLs.",
|
||
"emptyDesc": "Default ohne ACLs: nur localnet (10/8, 172.16/12, 192.168/16) darf raus. Lege eine ACL an, um spezifische Domains/IPs/Ports gezielt zu erlauben oder zu blocken."
|
||
},
|
||
"common": {
|
||
"yes": "Ja",
|
||
"no": "Nein",
|
||
"save": "Speichern",
|
||
"cancel": "Abbrechen",
|
||
"loading": "Lädt …",
|
||
"error": "Fehler",
|
||
"edit": "Bearbeiten",
|
||
"delete": "Löschen",
|
||
"deleteConfirm": "Wirklich löschen?",
|
||
"search": "Suchen …",
|
||
"totalRows": "{{count}} Einträge",
|
||
"active": "Aktiv",
|
||
"inactive": "Inaktiv",
|
||
"noData": "Keine Einträge",
|
||
"actions": "Aktionen",
|
||
"add": "Hinzufügen",
|
||
"download": "Download",
|
||
"copy": "Kopieren",
|
||
"copied": "Kopiert",
|
||
"close": "Schließen",
|
||
"refresh": "Aktualisieren"
|
||
},
|
||
"license": {
|
||
"title": "Lizenz",
|
||
"status": "Status",
|
||
"product": "Produkt",
|
||
"key": "Lizenz-Schlüssel",
|
||
"noKey": "Kein Schlüssel hinterlegt",
|
||
"validUntil": "Gültig bis",
|
||
"expired": "Abgelaufen",
|
||
"daysLeft": "noch {{days}} Tage",
|
||
"lastVerifiedAt": "Letzte Verifizierung",
|
||
"verifiedBy": "Verifiziert von",
|
||
"limits": "Limits",
|
||
"unlimited": "Unbegrenzt",
|
||
"features": "Features",
|
||
"reverify": "Erneut prüfen",
|
||
"reverified": "Lizenz erfolgreich verifiziert",
|
||
"enterKey": "Schlüssel eingeben",
|
||
"replaceKey": "Schlüssel ersetzen",
|
||
"enterKeyHint": "Lizenz-Schlüssel aus dem Self-Service-Portal von license.netcell-it.com einfügen.",
|
||
"activate": "Aktivieren",
|
||
"saved": "Lizenz gespeichert und verifiziert",
|
||
"savedButVerifyFailed": "Schlüssel gespeichert, aber Server-Verifizierung fehlgeschlagen",
|
||
"clearKey": "Schlüssel entfernen",
|
||
"cleared": "Lizenz entfernt — System fällt auf Trial zurück",
|
||
"confirmClear": "Lizenz-Schlüssel wirklich entfernen?",
|
||
"confirmClearHint": "System fällt auf Trial-Modus zurück, sobald der Schlüssel gelöscht wird.",
|
||
"lastVerifyFailed": "Letzte Server-Verifizierung fehlgeschlagen",
|
||
"trialExpiring": "Trial läuft in {{days}} Tag(en) ab",
|
||
"trialExpiringHint": "Lizenz aktivieren, bevor die Trial-Periode endet."
|
||
},
|
||
"licenseBanner": {
|
||
"expired": "Lizenz abgelaufen oder ungültig.",
|
||
"trialExpiring": "Trial läuft in {{days}} Tag(en) ab.",
|
||
"verifyFailed": "Lizenz-Verifizierung fehlgeschlagen",
|
||
"cta": "Jetzt aktivieren →",
|
||
"openPage": "Lizenz-Seite öffnen →"
|
||
},
|
||
"routes": {
|
||
"liveTitle": "Live-Routen (Kernel)",
|
||
"liveIntro": "Aktueller Zustand aus `ip -j route show table all`. proto edgeguard markiert von EdgeGuard verwaltete Routen — andere Quellen (kernel/static/dhcp) bleiben unberührt.",
|
||
"liveEmpty": "Keine Routen im Kernel.",
|
||
"managedTitle": "Verwaltete Routen",
|
||
"managedIntro": "Statische Routen, die EdgeGuard via `ip route … proto edgeguard` beim Boot setzt. Änderung hier triggert sofort `systemctl restart edgeguard-routes.service`.",
|
||
"add": "Route hinzufügen",
|
||
"addTitle": "Statische Route anlegen",
|
||
"editTitle": "Statische Route bearbeiten",
|
||
"empty": "Keine verwalteten Routen.",
|
||
"emptyTitle": "Noch keine verwalteten Routen.",
|
||
"emptyDesc": "Statische Routen die EdgeGuard beim Boot setzt (z. B. nach 10.0.5.0/24 via VPN-Gateway). Live-Routen oben sind read-only — was hier eingetragen wird, persistiert.",
|
||
"confirmDelete": "Route nach {{dest}} wirklich löschen?",
|
||
"refreshTooltip": "Live-Routen neu laden",
|
||
"destExtra": "CIDR — z.B. 10.0.5.0/24 oder 0.0.0.0/0 für Default-Route.",
|
||
"gatewayExtra": "Optional. Leer = on-link via dev.",
|
||
"devExtra": "Output-Interface. Bei Gateway-only kann ip das auflösen, explizit ist aber stabiler.",
|
||
"metricExtra": "Niedriger gewinnt. Default 100. Standard-Linux-Defaults: dhcp 1024, kernel-link 0.",
|
||
"tableExtra": "Routing-Table. main = Standard. Custom-Tables via /etc/iproute2/rt_tables.",
|
||
"col": {
|
||
"destination": "Ziel",
|
||
"gateway": "Gateway",
|
||
"dev": "Interface",
|
||
"metric": "Metric",
|
||
"table": "Table",
|
||
"active": "Aktiv",
|
||
"comment": "Kommentar",
|
||
"proto": "Protokoll",
|
||
"scope": "Scope",
|
||
"src": "Quell-IP"
|
||
}
|
||
},
|
||
"alerts": {
|
||
"title": "Health-Alarme",
|
||
"intro": "Notification-Channels für kritische Events. Webhook (Slack/Discord/Teams/Generic-HTTP) oder Email (SMTP). Triggers: cert.expiring (<14 d), cert.renew_failed, backup.failed, license.invalid.",
|
||
"scopeTitle": "Was triggert Alarme?",
|
||
"scopeDesc": "cert.expiring — TLS-Zertifikat <14 Tage Restzeit (dedupe 12h). cert.renew_failed — ACME-Renewer hat Fails. backup.failed — Scheduled Backup konnte nicht erstellt werden. license.invalid — License-Server liefert valid=false. Mehr Triggers folgen (Backend-Down, Disk-Usage).",
|
||
"tabs": { "channels": "Channels", "events": "History" },
|
||
"add": "Channel hinzufügen",
|
||
"addTitle": "Notification-Channel anlegen",
|
||
"editTitle": "Channel bearbeiten",
|
||
"test": "Test-Alert",
|
||
"testDone": "Test gesendet — {{ok}}/{{total}} Channels erfolgreich",
|
||
"emptyChannels": "Keine Channels. Lege einen Webhook oder eine Email an.",
|
||
"emptyEvents": "Noch keine Alarme — Triggers haben noch keinen Event gefeuert.",
|
||
"emptyChannelsTitle": "Noch keine Alert-Channels.",
|
||
"emptyChannelsDesc": "Ohne Channels werden gefeuerte Events nur in die Datenbank (Tab \"Events\") geschrieben — niemand wird benachrichtigt. Lege einen Webhook (Mattermost/Slack/Discord/Custom) oder eine SMTP-Email an.",
|
||
"emptyEventsTitle": "Noch keine Alert-Events.",
|
||
"emptyEventsDesc": "Triggers (Cert-Expiry, Backup-Fail, Cluster-Drift, License-Invalid, etc.) haben noch keinen Event gefeuert. Wenn sie feuern, landen sie hier und werden an die konfigurierten Channels zugestellt.",
|
||
"noChannels": "kein Channel aktiv",
|
||
"confirmDelete": "Channel {{name}} wirklich löschen?",
|
||
"col": {
|
||
"name": "Name",
|
||
"kind": "Typ",
|
||
"target": "Ziel",
|
||
"targetWebhook": "Webhook-URL",
|
||
"targetEmail": "Empfänger-Email",
|
||
"active": "Aktiv",
|
||
"time": "Zeit",
|
||
"severity": "Severity",
|
||
"subject": "Betreff",
|
||
"delivered": "Gesendet"
|
||
}
|
||
},
|
||
"remotes": {
|
||
"scopeTitle": "Off-Site-Backup-Ziele",
|
||
"scopeDesc": "Nach jedem erfolgreichen lokalen Backup wird das tar.gz in alle aktiven Ziele hochgeladen. S3-Endpoints (AWS, MinIO, Backblaze B2, Cloudflare R2, Hetzner Object Storage) und SFTP/SSH. Schutz gegen Box-Total-Loss.",
|
||
"add": "Ziel hinzufügen",
|
||
"addTitle": "Off-Site-Ziel anlegen",
|
||
"editTitle": "Off-Site-Ziel bearbeiten",
|
||
"empty": "Keine Off-Site-Ziele. Lokale Backups schützen NICHT gegen Disk-Verlust oder Box-Total-Defekt.",
|
||
"test": "Test",
|
||
"testOk": "Test erfolgreich — Verbindung + Upload + Cleanup OK.",
|
||
"testFailed": "Test fehlgeschlagen",
|
||
"confirmDelete": "Ziel {{name}} wirklich löschen?",
|
||
"targetExtra": "Frei-Text-Label für die Übersicht. Setze hier den Bucket/Path-Hinweis (s3://my-bucket oder sftp://backup@host).",
|
||
"col": {
|
||
"name": "Name",
|
||
"kind": "Typ",
|
||
"target": "Ziel",
|
||
"lastUpload": "Letzter Upload",
|
||
"active": "Aktiv"
|
||
}
|
||
},
|
||
"diag": {
|
||
"title": "Diagnose",
|
||
"intro": "Operator-Tools direkt aus dem UI: ping, traceroute, DNS, HTTP-Probe, TCP-Connect. Alle Calls laufen authentifiziert auf dieser Box (nicht im Browser).",
|
||
"run": "Ausführen",
|
||
"runFromBoxTitle": "Tools laufen auf der EdgeGuard-Box",
|
||
"runFromBoxDesc": "Diese Aufrufe nutzen die Outbound-Connectivity der EdgeGuard — nicht deinen lokalen Rechner. Wenn ping/curl auf der Box scheitert, ist das Box-Network-Layer das Problem (Firewall-Rules, Default-Route, DNS).",
|
||
"ping": { "intro": "ICMP echo-request × 4, 2s Timeout pro Paket. Misst Loss + RTT." },
|
||
"trace": { "intro": "Hop-by-Hop-Pfad mit UDP-Probes (max 20 Hops, kein reverse-DNS)." },
|
||
"dig": { "intro": "DNS-Lookup über unbound (Box-Resolver). Zeigt Antwort + AUTHORITY/ADDITIONAL Section." },
|
||
"curl": { "intro": "HTTPS-Probe mit -IsSv: TLS-Handshake-Details + Header. Folgt KEINE Redirects." },
|
||
"tcp": { "intro": "Pure TCP-Connect ohne I/O. Ideal um zu prüfen ob ein Backend-Port erreichbar ist." },
|
||
"securityTitle": "Sicherheits-Hinweis",
|
||
"securityDesc": "Endpoints sind hinter Admin-Auth. Targets werden gegen eine strikte Allow-Liste validiert (keine Shell-Metazeichen). curl ist auf http(s) beschränkt — kein file:// / smb:// / data://."
|
||
},
|
||
"backups": {
|
||
"title": "Backups",
|
||
"intro": "Sicherungen der PostgreSQL-Datenbank + /var/lib/edgeguard (Setup, License, JWT, ACME-Account). Täglicher Auto-Job + manueller Trigger.",
|
||
"scopeTitle": "Was wird gesichert?",
|
||
"scopeDesc": "DB-Dump (pg_dump --clean), setup.json, license_key, license.cache, .jwt_fingerprint, acme-account/. Konfig-Dateien (haproxy.cfg, nft, …) sind aus der DB regenerierbar und werden NICHT mitgesichert.",
|
||
"tabs": { "history": "Sicherungen", "remotes": "Off-Site-Ziele" },
|
||
"statLastSuccess": "Letzte erfolgreiche Sicherung",
|
||
"statTotal": "Erfolgreiche Sicherungen",
|
||
"statSize": "Gesamtgröße",
|
||
"statFails24h": "Fehlschläge (24 h)",
|
||
"statHoursAgo": "vor {{n}} h",
|
||
"statDaysAgo": "vor {{n}} Tagen",
|
||
"runNow": "Backup jetzt erstellen",
|
||
"created": "Backup erstellt: {{file}}",
|
||
"failed": "Backup fehlgeschlagen",
|
||
"deleted": "Backup gelöscht",
|
||
"download": "Download",
|
||
"restore": "Wiederherstellen",
|
||
"restoreOk": "Restore starten",
|
||
"restoreDone": "Restore abgeschlossen — Seite wird neu geladen.",
|
||
"restoreFailed": "Restore fehlgeschlagen",
|
||
"restoreRunning": "Restore läuft …",
|
||
"restoreHint": "edgeguard-api startet automatisch neu; die UI lädt nach Restart neu.",
|
||
"confirmRestoreTitle": "Backup wiederherstellen?",
|
||
"confirmRestoreDesc": "DB-Inhalt wird durch {{file}} ersetzt + State-Dateien zurückgespielt. edgeguard-api + scheduler restarten. Aktuelle Änderungen seit dem Backup gehen verloren.",
|
||
"step": {
|
||
"extract": "Tar entpacken",
|
||
"psql": "DB-Restore (psql)",
|
||
"render": "Configs re-rendern",
|
||
"restart": "Services neu starten"
|
||
},
|
||
"downloadTooltip": "tar.gz herunterladen",
|
||
"refreshTooltip": "Liste neu laden",
|
||
"confirmDelete": "Backup {{file}} wirklich löschen?",
|
||
"empty": "Noch keine Backups. Klicke „Backup jetzt erstellen\" oder warte den nächsten Auto-Tick ab.",
|
||
"failedTag": "FEHLER",
|
||
"col": {
|
||
"time": "Zeit",
|
||
"file": "Datei",
|
||
"kind": "Typ",
|
||
"status": "Status",
|
||
"size": "Größe",
|
||
"duration": "Dauer"
|
||
}
|
||
},
|
||
"logs": {
|
||
"title": "System-Logs",
|
||
"intro": "Aggregierter Blick auf alle Service-Journals + audit_log. Multi-Source-Auswahl, Level-Filter, Freitext-Suche, Zeit-Range, Auto-Refresh (5s).",
|
||
"autoOn": "Auto",
|
||
"autoOff": "Manuell",
|
||
"refresh": "Aktualisieren",
|
||
"refreshTooltip": "Einmalig neu laden",
|
||
"export": "CSV",
|
||
"exportTooltip": "Aktuelle Tabelle als CSV exportieren",
|
||
"exportEmpty": "Keine Einträge zum Exportieren",
|
||
"found": "{{n}} Einträge",
|
||
"limit": "Limit",
|
||
"empty": "Keine Einträge gefunden. Quellen-Auswahl ändern oder Zeit-Range erweitern.",
|
||
"col": {
|
||
"time": "Zeit",
|
||
"source": "Quelle",
|
||
"level": "Level",
|
||
"message": "Nachricht"
|
||
},
|
||
"filter": {
|
||
"sources": "Quellen wählen (alle wenn leer)",
|
||
"levels": "Level filtern",
|
||
"grep": "Volltext-Suche",
|
||
"reset": "Filter zurücksetzen"
|
||
}
|
||
},
|
||
"fwlog": {
|
||
"title": "Firewall-Log (Live)",
|
||
"intro": "Pakete, die in nft-Regeln mit aktivem Log-Flag matchen, fließen via NFLOG → ulogd2 → JSONL hierher. WebSocket-Stream zeigt Live-Events; Ring-Buffer (1000) hält die letzten Treffer auch nach Reconnect.",
|
||
"start": "Live-Log starten",
|
||
"stop": "Stop",
|
||
"notStartedTitle": "Live-Log ist aus",
|
||
"notStartedDesc": "Standardmäßig pausiert — Klick zum Verbinden lässt Events live einfließen.",
|
||
"live": "Live",
|
||
"disconnected": "verbinde …",
|
||
"pause": "Pause",
|
||
"resume": "Fortsetzen",
|
||
"queued": "wartend",
|
||
"clear": "Leeren",
|
||
"clearTooltip": "Tabelle leeren (Server-Ringbuffer bleibt unverändert)",
|
||
"export": "CSV",
|
||
"exportTooltip": "Aktuelle Tabelle als CSV exportieren",
|
||
"exportEmpty": "Keine Events zum Exportieren",
|
||
"connError": "WebSocket-Fehler — versuche erneut",
|
||
"empty": "Noch keine Events. Aktiviere bei einer Firewall-Regel den Log-Schalter, dann fließen Treffer hier hinein.",
|
||
"connecting": "Verbinde …",
|
||
"col": {
|
||
"time": "Zeit",
|
||
"action": "Aktion",
|
||
"rule": "Rule",
|
||
"proto": "Proto",
|
||
"src": "Quelle",
|
||
"dst": "Ziel",
|
||
"iface": "Interface",
|
||
"size": "Größe"
|
||
},
|
||
"filter": {
|
||
"action": "Action filtern",
|
||
"proto": "Proto filtern",
|
||
"src": "Quell-IP",
|
||
"dst": "Ziel-IP",
|
||
"rule": "Rule-ID"
|
||
}
|
||
},
|
||
"users": {
|
||
"title": "Benutzerverwaltung",
|
||
"intro": "Login-Accounts verwalten. Alle hier gelisteten Accounts können sich über die Login-Seite authentifizieren.",
|
||
"addUser": "Benutzer anlegen",
|
||
"editUser": "Benutzer bearbeiten",
|
||
"email": "E-Mail",
|
||
"role": "Rolle",
|
||
"active": "Aktiv",
|
||
"lastLogin": "Letzter Login",
|
||
"created": "Angelegt",
|
||
"setPassword": "Passwort setzen",
|
||
"setPasswordTitle": "Neues Passwort setzen",
|
||
"newPassword": "Neues Passwort",
|
||
"newPasswordHint": "Mindestens 12 Zeichen.",
|
||
"roleAdmin": "Admin",
|
||
"roleViewer": "Betrachter (nur lesen)",
|
||
"deleteConfirm": "Benutzer {{email}} löschen? Dies kann nicht rückgängig gemacht werden.",
|
||
"emptyTitle": "Noch keine Benutzer",
|
||
"emptyDesc": "Oben den ersten zusätzlichen Benutzer anlegen.",
|
||
"errorEmailTaken": "Diese E-Mail-Adresse wird bereits verwendet.",
|
||
"cannotDeleteSelf": "Das eigene Konto kann nicht gelöscht werden.",
|
||
"never": "Nie"
|
||
},
|
||
"audit": {
|
||
"title": "Audit-Log",
|
||
"intro": "Wer hat was wann geändert. Jede Mutation in der API (Domain anlegen, Backend deaktivieren, Cert ausstellen, …) ist hier nachvollziehbar.",
|
||
"filter": {
|
||
"actor": "Actor",
|
||
"action": "Aktion",
|
||
"subject": "Subjekt",
|
||
"range": "Zeitraum",
|
||
"search": "Suchen",
|
||
"reset": "Zurücksetzen"
|
||
},
|
||
"col": {
|
||
"time": "Zeit",
|
||
"actor": "Actor",
|
||
"action": "Aktion",
|
||
"subject": "Subjekt",
|
||
"detail": "Details"
|
||
},
|
||
"detailShow": "Details anzeigen",
|
||
"empty": {
|
||
"title": "Keine Treffer",
|
||
"desc": "Mit diesen Filtern wurden keine Einträge gefunden. Filter ändern oder zurücksetzen."
|
||
},
|
||
"page": {
|
||
"prev": "Zurück",
|
||
"next": "Weiter",
|
||
"showing": "Zeile {{from}}–{{to}}"
|
||
}
|
||
}
|
||
}
|