feat: umfangreiches UI+API-Polish (v1.1.36–1.1.42)
Backend: - Audit-Log: Search-Endpoint mit ILIKE-Filter (actor/action/subject/date) - NTP: /ntp/status via chronyc tracking (Stratum, Offset, Quelle) - System: /service-restart mit Allowlist (haproxy/squid/unbound/chrony/scheduler) - Domain-Response-Headers + Rate-Limit (Migration 0024) - Join-Tokens (Migration 0025), Cluster-mTLS, Aggregator-Fan-Out - apt-Service für Update-Banner (apt-get update + Versionsprüfung) - Backup-Retry mit exponential backoff (retry_apt 3×) - publish.sh fail-fast + cleanup-old.sh (max 10 Versionen) Frontend: - Audit-Log-Page (/audit) mit Filter + Pagination - ErrorBoundary an React-Root + Vite build-target festgenagelt (iOS 15+) - Storage-Schema-Stamp: auto-wipe bei Versions-Mismatch (blank-page-Fix) - EmptyState-Komponente überall ausgerollt - SSL: Aggregate-Karte (total/expiring/expired/errors) - Backups: Aggregate-Karte (letzter Backup/Größe/Fehlschläge 24h) + Backup-Now - NTP: Sync-Status-Karte (chronyc tracking live) - Domains: Backend-UP/DOWN-Chip aus HAProxy-Stats - Backends: HAProxy-Status-Spalte (UP/DEGRADED/DOWN) - Settings: Service-Neustart-Karte (haproxy/squid/unbound/chrony/scheduler) - Settings: Upgrade-Status-Card, Wartungsmodus, Auto-Update, Retention - Dashboard: Recent-Alerts, Cluster-Health, License-Chip, Onboarding-Hint - System-Regeln im Firewall als eigener Tab Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -20,6 +20,7 @@
|
||||
"firewallLive": "Firewall log",
|
||||
"cluster": "Cluster",
|
||||
"logs": "Logs",
|
||||
"audit": "Audit log",
|
||||
"backups": "Backups",
|
||||
"diagnostics": "Diagnostics",
|
||||
"alerts": "Alerts",
|
||||
@@ -44,7 +45,8 @@
|
||||
"addrObj": "Address objects",
|
||||
"addrGrp": "Address groups",
|
||||
"services": "Services",
|
||||
"svcGrp": "Service groups"
|
||||
"svcGrp": "Service groups",
|
||||
"system": "System rules"
|
||||
},
|
||||
"zone": {
|
||||
"name": "Name",
|
||||
@@ -55,31 +57,41 @@
|
||||
"namePattern": "Lowercase letters, digits, _ and -; must start with a letter, up to 32 chars.",
|
||||
"add": "Add zone",
|
||||
"edit": "Edit zone",
|
||||
"deleteConfirm": "Really delete zone {{name}}?"
|
||||
"deleteConfirm": "Really delete zone {{name}}?",
|
||||
"emptyTitle": "No custom firewall zones yet.",
|
||||
"emptyDesc": "Zones group interfaces (lan, wan, dmz). Built-in zones already exist; add custom ones for e.g. a separate dmz or a wg zone for VPN."
|
||||
},
|
||||
"ao": {
|
||||
"name": "Name", "kind": "Kind", "value": "Value", "description": "Description",
|
||||
"add": "Add address object", "edit": "Edit address object",
|
||||
"deleteConfirm": "Really delete address object {{name}}?"
|
||||
"deleteConfirm": "Really delete address object {{name}}?",
|
||||
"emptyTitle": "No address objects yet.",
|
||||
"emptyDesc": "Reusable named IPs/networks/ranges for firewall + NAT rules (e.g. office-net = 10.0.0.0/24, mailbox-1 = 10.0.1.42)."
|
||||
},
|
||||
"ag": {
|
||||
"name": "Name", "members": "Members", "description": "Description",
|
||||
"add": "Add address group", "edit": "Edit address group",
|
||||
"selectMembers": "Select address objects",
|
||||
"deleteConfirm": "Really delete address group {{name}}?"
|
||||
"deleteConfirm": "Really delete address group {{name}}?",
|
||||
"emptyTitle": "No address groups yet.",
|
||||
"emptyDesc": "Bundle multiple address objects into a group (e.g. office-locations = [hq-net, branch-1-net, branch-2-net]) — rules then reference one group."
|
||||
},
|
||||
"svc": {
|
||||
"name": "Name", "proto": "Protocol", "ports": "Ports",
|
||||
"portStart": "Port (start)", "portEnd": "Port (end)",
|
||||
"description": "Description", "builtinHint": "Built-in — not editable",
|
||||
"add": "Add service", "edit": "Edit service",
|
||||
"deleteConfirm": "Really delete service {{name}}?"
|
||||
"deleteConfirm": "Really delete service {{name}}?",
|
||||
"emptyTitle": "No custom services yet.",
|
||||
"emptyDesc": "Built-ins (HTTP, HTTPS, SSH, …) already exist. Add app-specific ports (e.g. mailcow-imaps tcp/993) to make rules easier to read."
|
||||
},
|
||||
"sg": {
|
||||
"name": "Name", "members": "Members", "description": "Description",
|
||||
"add": "Add service group", "edit": "Edit service group",
|
||||
"selectMembers": "Select services",
|
||||
"deleteConfirm": "Really delete service group {{name}}?"
|
||||
"deleteConfirm": "Really delete service group {{name}}?",
|
||||
"emptyTitle": "No service groups yet.",
|
||||
"emptyDesc": "Bundle multiple services into a group (e.g. web-stack = [HTTP, HTTPS, HTTP/3]) — one rule with the group replaces three rules per service."
|
||||
},
|
||||
"rule": {
|
||||
"name": "Name", "priority": "Priority", "enabled": "Enabled", "log": "Log",
|
||||
@@ -90,7 +102,9 @@
|
||||
"serviceKind": "Service kind", "serviceGroup": "Service group",
|
||||
"comment": "Comment",
|
||||
"add": "Add rule", "edit": "Edit rule",
|
||||
"deleteConfirm": "Really delete this rule?"
|
||||
"deleteConfirm": "Really delete this rule?",
|
||||
"emptyTitle": "No custom firewall rules yet.",
|
||||
"emptyDesc": "The system rules above keep SSH (rate-limited), HTTPS :443 and the mgmt UI :3443 open (anti-lockout). Add custom rules for app-specific inbound ports or cross-zone forwards."
|
||||
},
|
||||
"nat": {
|
||||
"name": "Name", "priority": "Priority", "kind": "Kind", "enabled": "Enabled",
|
||||
@@ -102,7 +116,9 @@
|
||||
"targetAddr": "Target address", "targetPortStart": "Target port (start)", "targetPortEnd": "Target port (end)",
|
||||
"comment": "Comment",
|
||||
"add": "Add NAT rule", "edit": "Edit NAT rule",
|
||||
"deleteConfirm": "Really delete this NAT rule?"
|
||||
"deleteConfirm": "Really delete this NAT rule?",
|
||||
"emptyTitle": "No NAT rules yet.",
|
||||
"emptyDesc": "DNAT (e.g. external :2030 → internal 10.10.20.12:22 for SSH to an internal host) or SNAT/MASQUERADE (internet access for an internal subnet via the box IP)."
|
||||
},
|
||||
"sys": {
|
||||
"title": "System rules (always active)",
|
||||
@@ -125,6 +141,8 @@
|
||||
"systemDiscovered": "System interfaces (read-only)",
|
||||
"addInterface": "Add interface",
|
||||
"editInterface": "Edit interface",
|
||||
"emptyTitle": "No managed interfaces yet.",
|
||||
"emptyDesc": "System interfaces (above) are detected read-only. Managed interfaces are what EdgeGuard creates itself — VLANs, bridges, bonds, GRE tunnels — and renders into /etc/network/interfaces.d.",
|
||||
"name": "Name",
|
||||
"type": "Type",
|
||||
"parent": "Parent interface",
|
||||
@@ -152,6 +170,8 @@
|
||||
"managedTitle": "Managed addresses",
|
||||
"family": "Family",
|
||||
"addAddress": "Add address",
|
||||
"emptyTitle": "No managed IP addresses yet.",
|
||||
"emptyDesc": "Add your first IP — floating VIPs for HA failover or extra listen addresses for HAProxy/Squid/Unbound. Distro-owned IPs are shown above under \"Detected IPs\".",
|
||||
"editAddress": "Edit address",
|
||||
"interface": "Interface",
|
||||
"selectInterface": "Select interface",
|
||||
@@ -192,12 +212,19 @@
|
||||
"setup": {
|
||||
"title": "First-time setup",
|
||||
"intro": "Create the admin account, declare the public FQDN, and — optionally — paste a license key. Without one, a 30-day trial starts.",
|
||||
"preflightTitle": "Before you hit \"Finish setup\"",
|
||||
"preflightDesc": "The FQDN must already resolve to this box via DNS (A/AAAA record). Without DNS resolution the later ACME HTTP-01 challenge fails — your browser URL should already be this FQDN, otherwise things break at first Let's Encrypt issue.",
|
||||
"adminEmail": "Admin email",
|
||||
"adminEmailHint": "Login identifier for the management UI. NOT used for outbound mail (use the ACME email below or the SMTP settings inside the Alerts channel).",
|
||||
"adminPassword": "Admin password",
|
||||
"passwordRule": "At least 12 characters.",
|
||||
"fqdn": "Public FQDN",
|
||||
"fqdnHint": "Fully-qualified hostname of this box (e.g. eg.example.com). Becomes the Subject-CN of the self-signed bootstrap cert AND ends up in the ACME cert once Let's Encrypt has issued. Must resolve to the public IP via DNS.",
|
||||
"fqdnInvalid": "Doesn't look like a valid FQDN (at least label.tld).",
|
||||
"acmeEmail": "ACME / Let's Encrypt email",
|
||||
"acmeEmailHint": "Sent to Let's Encrypt as account contact (expiry warnings + LE compliance mail). Can be the same as the admin email, but doesn't have to be.",
|
||||
"licenseKey": "License key (optional)",
|
||||
"licenseKeyHint": "If present: 30-day trial is skipped, features unlock immediately. Can also be added later under License.",
|
||||
"submit": "Finish setup",
|
||||
"successTitle": "Setup complete",
|
||||
"successHint": "Redirecting you to sign-in."
|
||||
@@ -211,14 +238,64 @@
|
||||
"intro": "Manage FQDNs that HAProxy terminates. Optional primary backend as catch-all; path-based routing via routing rules.",
|
||||
"addDomain": "Add domain",
|
||||
"editDomain": "Edit domain",
|
||||
"emptyTitle": "No domains yet.",
|
||||
"emptyDesc": "Add your first domain — HAProxy will then terminate TLS for that hostname and route to the chosen backend.",
|
||||
"name": "Name",
|
||||
"active": "Active",
|
||||
"primaryBackend": "Primary backend",
|
||||
"primaryBackendHint": "Catch-all backend for requests with no matching routing rule. Optional — leave empty if all traffic is routed via routing rules.",
|
||||
"selectBackend": "Select backend",
|
||||
"noBackend": "no backend",
|
||||
"quickBackendBtn": "+ new",
|
||||
"quickBackendBtnHint": "Create backend + first upstream server in one click — skips the trip to the Backends page.",
|
||||
"quickBackendTitle": "Quick-add backend",
|
||||
"quickBackendName": "Backend name",
|
||||
"quickBackendScheme": "Scheme",
|
||||
"quickBackendAddress": "Server address (first upstream)",
|
||||
"quickBackendPort": "Port",
|
||||
"quickBackendCreated": "Backend + server created and selected.",
|
||||
"quickBackendFailed": "Quick-add backend failed",
|
||||
"httpToHttps": "HTTP→HTTPS",
|
||||
"hsts": "HSTS",
|
||||
"tlsCert": "TLS cert",
|
||||
"tlsCertValid": "valid",
|
||||
"tlsCertExpiring": "{{days}}d left",
|
||||
"tlsCertExpired": "expired",
|
||||
"tlsCertError": "error",
|
||||
"tlsCertNone": "missing",
|
||||
"tlsCertNoneHint": "No own TLS cert for this domain — HAProxy serves the bootstrap self-signed cert. Issue one in the SSL tab.",
|
||||
"hstsMaxAge": "HSTS max-age (sec)",
|
||||
"hstsMaxAgeHint": "How long browsers cache the HTTPS-only mandate. Recommended: 31536000 (1 year).",
|
||||
"hstsSubdomains": "includeSubDomains",
|
||||
"hstsSubdomainsHint": "Extends HSTS to all subdomains. Enable only if every subdomain speaks HTTPS exclusively.",
|
||||
"hstsPreload": "preload",
|
||||
"hstsPreloadHint": "Sets the preload flag so the domain may be added to hstspreload.org. Requires max-age ≥ 31536000 and includeSubDomains enabled.",
|
||||
"maintenance": "Maintenance mode",
|
||||
"maintenanceHint": "On: every request to this domain receives 503 directly from HAProxy. Backends are not contacted.",
|
||||
"maintenanceMessage": "Maintenance message",
|
||||
"maintenanceMessagePlaceholder": "Service temporarily unavailable.",
|
||||
"wwwRedirect": "www redirect",
|
||||
"wwwRedirectHint": "Canonicalises the domain. \"to naked\": Name=example.com → www.example.com redirects to example.com. \"to www\": Name=www.example.com → example.com redirects to www.example.com.",
|
||||
"wwwRedirectNone": "No redirect",
|
||||
"wwwRedirectToNaked": "→ naked (no www)",
|
||||
"wwwRedirectToWWW": "→ www",
|
||||
"rateLimit": "Rate limit (per client IP)",
|
||||
"rateLimitHint": "Max requests per second per client IP. HAProxy counts over a 10-second window per stick table (max. 100k IPs). 0 = off.",
|
||||
"maxBody": "Max request body",
|
||||
"maxBodyHint": "Cap on the Content-Length header. Larger requests get 413. Chunked bodies are not detected (HAProxy doesn't buffer by default). 0 = off.",
|
||||
"headersBtn": "Headers",
|
||||
"headersTitle": "Response headers — {{name}}",
|
||||
"headersHint": "These headers are set by HAProxy on every response for this domain (http-response set-header). Ordering via position.",
|
||||
"headersEmpty": "No custom headers configured yet.",
|
||||
"addHeader": "Add header",
|
||||
"editHeader": "Edit header",
|
||||
"headerName": "Name",
|
||||
"headerNameHint": "Letters, digits, hyphens only. Case-insensitive unique per domain.",
|
||||
"headerNamePattern": "Only a-z, A-Z, 0-9, '-'",
|
||||
"headerValue": "Value",
|
||||
"headerPosition": "Position",
|
||||
"headerDeleteConfirm": "Really delete header \"{{name}}\"?",
|
||||
"settingsSection": "HAProxy settings",
|
||||
"notes": "Notes",
|
||||
"actions": "Actions",
|
||||
"edit": "Edit",
|
||||
@@ -229,11 +306,14 @@
|
||||
"title": "Backends",
|
||||
"intro": "Upstream pools (one backend = N servers). HAProxy balances load by the chosen algorithm; health-check path enables HTTP probes every 5s per server.",
|
||||
"addBackend": "Add backend pool",
|
||||
"emptyTitle": "No backend pools yet.",
|
||||
"emptyDesc": "Add your first pool — define upstream servers (e.g. app servers / mailbox nodes) so domains can route to them.",
|
||||
"editBackend": "Edit backend pool",
|
||||
"name": "Name",
|
||||
"scheme": "Scheme",
|
||||
"target": "Target",
|
||||
"healthCheck": "Health check path",
|
||||
"liveStatus": "HAProxy status",
|
||||
"active": "Active",
|
||||
"usedBy": "Used by",
|
||||
"noDomain": "no domain",
|
||||
@@ -271,6 +351,8 @@
|
||||
"title": "Routing rules",
|
||||
"intro": "Path-prefix → backend mapping per domain. Lowest priority wins; catch-all via domain.primary_backend.",
|
||||
"addRule": "Add rule",
|
||||
"emptyTitle": "No routing rules yet.",
|
||||
"emptyDesc": "Path-specific routing — e.g. domain.tld/api → Backend A, domain.tld/* → Backend B. Optional. Without rules HAProxy uses the primary backend from the domain.",
|
||||
"editRule": "Edit rule",
|
||||
"domain": "Domain",
|
||||
"pathPrefix": "Path prefix",
|
||||
@@ -313,8 +395,35 @@
|
||||
"configHash": "Config hash",
|
||||
"version": "Version",
|
||||
"lastSeen": "Last seen",
|
||||
"mgmtIp": "MGMT IP"
|
||||
}
|
||||
"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": "No node resources available — agent listener unreachable?",
|
||||
"certCardTitle": "Cluster TLS certificates",
|
||||
"certCALabel": "Cluster CA",
|
||||
"certPeerLabel": "Peer cert (this node)",
|
||||
"certExpiry": "Expires in",
|
||||
"renewSelfBtn": "Renew peer cert",
|
||||
"renewSelfConfirm": "Re-sign peer cert with the local CA (1 year)? edgeguard-api restart required after.",
|
||||
"certRenewedRestartHint": "Cert renewed — please run sudo systemctl restart edgeguard-api.",
|
||||
"certRenewFailed": "Cert renewal failed",
|
||||
"removePeerBtn": "Remove",
|
||||
"removePeerConfirmTitle": "Really remove peer from the cluster?",
|
||||
"removePeerConfirmDesc": "{{fqdn}} is deleted from ha_nodes. Firewall renderer drops its IP from the peer_ipv4 set. On the peer side edgeguard-api keeps running; manually stop it + delete cluster-tls for a full decommission.",
|
||||
"removePeerOk": "Peer removed.",
|
||||
"removePeerFailed": "Peer removal failed",
|
||||
"generateJoinToken": "Generate join token",
|
||||
"joinTokenTitle": "Cluster join token",
|
||||
"joinTokenOneShot": "Shown once, usable once",
|
||||
"joinTokenOneShotDesc": "Transfer the token securely now — it won't be shown again. Valid until {{expires}}. Server marks it as consumed when redeemed via cluster-join.",
|
||||
"joinTokenFailed": "Token generation failed",
|
||||
"joinCmdLabel": "Run on the new node:"
|
||||
},
|
||||
"ssl": {
|
||||
"title": "SSL certificates",
|
||||
@@ -337,23 +446,113 @@
|
||||
"issueButton": "Issue certificate",
|
||||
"uploadButton": "Upload",
|
||||
"issueSuccess": "Certificate issued + installed.",
|
||||
"renewBtn": "Renew",
|
||||
"renewConfirmTitle": "Renew certificate now?",
|
||||
"renewConfirmDesc": "Triggers an ACME HTTP-01 challenge for {{domain}}. Let's Encrypt has rate limits (50 issues/domain/week) — only use when needed.",
|
||||
"renewSuccess": "Certificate renewed + installed.",
|
||||
"renewFailed": "Renewal failed",
|
||||
"uploadSuccess": "Certificate uploaded + installed.",
|
||||
"deleteConfirm": "Delete certificate for {{domain}}? HAProxy falls back to the default cert for this domain.",
|
||||
"installedTitle": "Installed certificates",
|
||||
"lastRenewed": "Last renewed",
|
||||
"statTotal": "Certificates total",
|
||||
"statExpiring": "Expiring < 30 days",
|
||||
"statExpired": "Expired",
|
||||
"statErrors": "With errors",
|
||||
"relAgo": {
|
||||
"justNow": "just now",
|
||||
"minutes": "{{n}} min ago",
|
||||
"hours": "{{n}} h ago",
|
||||
"days": "{{n}} d ago"
|
||||
},
|
||||
"emptyTitle": "No certificates installed yet.",
|
||||
"emptyDesc": "Use the tabs above — Let's Encrypt issues automatically via HTTP-01 challenge, or upload your own PEM. Until then HAProxy serves the bootstrap self-signed cert for all domains.",
|
||||
"certPem": "Certificate (PEM)",
|
||||
"chainPem": "Chain (PEM, optional)",
|
||||
"keyPem": "Private key (PEM)"
|
||||
},
|
||||
"settings": {
|
||||
"title": "Settings",
|
||||
"intro": "System information and setup status. Editable values come in a later release.",
|
||||
"intro": "System information, setup status and admin account.",
|
||||
"systemInfo": "System",
|
||||
"version": "Version",
|
||||
"status": "Status",
|
||||
"dbSize": "PostgreSQL DB size",
|
||||
"dbSizeTop": "Top tables",
|
||||
"upgradeStatusCardTitle": "Last upgrade attempt",
|
||||
"upgradeStatusStarted": "Started",
|
||||
"upgradeStatusFinished": "Finished",
|
||||
"upgradeStatusResult": "Result",
|
||||
"upgradeStatusState": "State",
|
||||
"upgradeStatusOk": "Successful",
|
||||
"upgradeStatusShowLog": "Show full log ({{n}} lines)",
|
||||
"actionsCardTitle": "System actions",
|
||||
"actionsHint": "Manual triggers for operator tasks that normally happen automatically on save in the respective pages. Useful after SSH interventions (e.g. /etc/edgeguard/tls/ filled manually).",
|
||||
"haproxyReloadBtn": "HAProxy reload",
|
||||
"haproxyReloadOk": "HAProxy reloaded.",
|
||||
"haproxyReloadFailed": "HAProxy reload failed",
|
||||
"renderConfigsBtn": "Re-render configs (HAProxy)",
|
||||
"renderConfigsOk": "Configs re-rendered + reloaded.",
|
||||
"renderConfigsFailed": "Config render failed",
|
||||
"backupNowBtn": "Backup now",
|
||||
"serviceRestartCardTitle": "Restart services",
|
||||
"serviceRestartBtn": "Restart",
|
||||
"serviceRestartOk": "{{service}} restarted successfully.",
|
||||
"serviceRestartFailed": "Restart of {{service}} failed",
|
||||
"serviceRestartHint": "Restarts the service via systemctl restart. edgeguard-api and PostgreSQL are intentionally excluded.",
|
||||
"backupNowOk": "Backup triggered — watch status on the Backups page.",
|
||||
"backupNowFailed": "Backup trigger failed",
|
||||
"setupInfo": "Setup",
|
||||
"adminEmail": "Admin email",
|
||||
"fqdn": "FQDN",
|
||||
"setupCompleted": "Setup completed"
|
||||
"setupCompleted": "Setup completed",
|
||||
"emailsCardTitle": "Contact emails",
|
||||
"adminEmailHint": "Used as the admin login identifier. Change requires a fresh login with the new address.",
|
||||
"acmeEmail": "ACME email",
|
||||
"acmeEmailHint": "Sent to Let's Encrypt as account contact. Existing certs are not affected — the next renew op registers the new address.",
|
||||
"emailsSaved": "Emails updated.",
|
||||
"emailsFailed": "Email update failed",
|
||||
"maintenanceCardTitle": "Maintenance mode (whole box)",
|
||||
"maintenanceOn": "Active — all customer domains return 503. Mgmt UI stays reachable.",
|
||||
"maintenanceOff": "Inactive — customer traffic is routed normally to backends.",
|
||||
"maintenanceMessage": "Maintenance message",
|
||||
"maintenanceMessagePlaceholder": "Service temporarily unavailable — we'll be back shortly.",
|
||||
"maintenanceMessageHint": "Returned in the body of 503 responses to end users. Plain text, max 500 chars.",
|
||||
"maintenanceHint": "Switches HAProxy on :443 into default-503 mode. Per-domain maintenance (Domains page) is overridden by this. Mgmt UI on :3443 is NOT affected.",
|
||||
"maintenanceSaved": "Maintenance mode updated.",
|
||||
"maintenanceFailed": "Maintenance toggle failed",
|
||||
"maintenanceActiveTitle": "Maintenance mode active",
|
||||
"maintenanceActiveDesc": "All customer domains currently return 503. Mgmt UI is reachable — customer traffic is NOT. Settings → Maintenance mode to disable.",
|
||||
"backupRetentionCardTitle": "Backup retention",
|
||||
"backupRetentionUnit": "backups",
|
||||
"backupRetentionDefault": "Default ({{n}} backups) — with daily schedule = {{n}} days of history.",
|
||||
"backupRetentionCustom": "Custom — the last {{n}} successful backups are kept; older ones are pruned after each backup run.",
|
||||
"backupRetentionHint": "0 = default (14). 1-365 = custom limit. Each backup is a full pg_dump + files tar (typically 50-500 MB). Reduce when /var disk gets tight.",
|
||||
"backupRetentionSaved": "Backup retention updated.",
|
||||
"backupRetentionFailed": "Backup retention update failed",
|
||||
"auditRetentionCardTitle": "Audit log retention",
|
||||
"auditRetentionUnit": "days",
|
||||
"auditRetentionDefault": "Default ({{n}} days) — audit entries older than {{n}} days are pruned daily.",
|
||||
"auditRetentionCustom": "Custom — audit entries are kept for {{n}} days.",
|
||||
"auditRetentionHint": "0 = default (90). 1-3650 (= 10 years) for compliance (SOX 7y = 2555, GDPR usually <= 365). Cleanup runs daily in the scheduler.",
|
||||
"auditRetentionSaved": "Audit retention updated.",
|
||||
"auditRetentionFailed": "Audit retention update failed",
|
||||
"autoUpdateCardTitle": "Automatic updates",
|
||||
"autoUpdateOn": "Enabled — edgeguard packages install automatically every day.",
|
||||
"autoUpdateOff": "Disabled — install updates manually via the banner.",
|
||||
"autoUpdateHint": "Whitelist covers edgeguard, edgeguard-api, edgeguard-ui only. Other packages stay under manual control. Requires unattended-upgrades (Trixie distro default). Conf file: /etc/apt/apt.conf.d/52edgeguard-auto-updates.",
|
||||
"autoUpdateToggled": "Auto-update setting saved.",
|
||||
"autoUpdateFailed": "Auto-update toggle failed",
|
||||
"passwordCardTitle": "Change admin password",
|
||||
"currentPassword": "Current password",
|
||||
"newPassword": "New password",
|
||||
"newPasswordHint": "At least 12 characters. Stored as bcrypt hash.",
|
||||
"confirmPassword": "Confirm new password",
|
||||
"changePasswordBtn": "Change password",
|
||||
"passwordChanged": "Password changed.",
|
||||
"passwordChangeFailed": "Password change failed",
|
||||
"passwordMismatch": "Passwords do not match.",
|
||||
"passwordMinLen": "Minimum 12 characters required."
|
||||
},
|
||||
"update": {
|
||||
"available": "Update available: version {{version}}",
|
||||
@@ -361,6 +560,8 @@
|
||||
"applyNow": "Install now",
|
||||
"confirmTitle": "Install update now?",
|
||||
"confirmDesc": "Packages will be updated to version {{version}}. edgeguard-api + scheduler restart (~2-5 s), HAProxy/nft/WG/Squid/Unbound/Chrony stay running.",
|
||||
"checkNowHint": "Refresh the server-side apt cache now and look for newer versions.",
|
||||
"updateReady": "Update ready: v{{version}}",
|
||||
"checkNow": "Check for updates",
|
||||
"checkDone": "Update available",
|
||||
"noUpdate": "No new updates",
|
||||
@@ -404,6 +605,10 @@
|
||||
"editServer": "Edit server tunnel",
|
||||
"addClient": "Add client tunnel",
|
||||
"editClient": "Edit client tunnel",
|
||||
"emptyServerTitle": "No WireGuard server tunnels yet.",
|
||||
"emptyServerDesc": "Server mode: this box listens on a UDP port and accepts peer connections (e.g. roadwarrior users, site-to-site branches).",
|
||||
"emptyClientTitle": "No WireGuard client tunnels yet.",
|
||||
"emptyClientDesc": "Client mode: this box connects to an external WireGuard server (e.g. HQ datacenter, cloud uplink).",
|
||||
"upstream": "Upstream peer",
|
||||
"deleteConfirm": "Really delete tunnel {{name}}? wg-quick will be stopped.",
|
||||
"keys": "Keys",
|
||||
@@ -430,6 +635,8 @@
|
||||
"add": "Add peer",
|
||||
"edit": "Edit peer",
|
||||
"deleteConfirm": "Really remove peer {{name}}?",
|
||||
"emptyTitle": "No peers in this tunnel yet.",
|
||||
"emptyDesc": "Add peers — each is a WireGuard identity (public key + allowed IPs). Server-generated keys give you config download / QR code for mobile clients in one go.",
|
||||
"keys": "Keys",
|
||||
"generateExtra": "If on: server generates a keypair for this peer and can hand out the config / QR. If off: paste the peer's public key only — no config download.",
|
||||
"pskExtra": "If on: server generates a 32-byte PSK for this peer.",
|
||||
@@ -471,7 +678,15 @@
|
||||
},
|
||||
"clusterCard": {
|
||||
"title": "Cluster",
|
||||
"nodes": "Nodes"
|
||||
"nodes": "Nodes",
|
||||
"modeSingle": "Single-Node",
|
||||
"modeCluster": "Cluster",
|
||||
"drift": "Config drift detected",
|
||||
"health": {
|
||||
"ok": "OK",
|
||||
"degraded": "degraded",
|
||||
"split-brain": "split-brain"
|
||||
}
|
||||
},
|
||||
"routingCard": {
|
||||
"title": "Routing",
|
||||
@@ -486,6 +701,15 @@
|
||||
"ifaces": "Interfaces",
|
||||
"wg": "WireGuard"
|
||||
},
|
||||
"alertsCard": {
|
||||
"title": "Recent alerts",
|
||||
"viewAll": "View all"
|
||||
},
|
||||
"onboardingTitle": "Welcome to EdgeGuard",
|
||||
"onboardingIntro": "Fresh box — here are the next steps to route customer traffic:",
|
||||
"onboardingStep1": "Create a backend pool (app servers behind HAProxy)",
|
||||
"onboardingStep2": "Add a domain (FQDN, assign primary backend)",
|
||||
"onboardingStep3": "Issue TLS certificate (Let's Encrypt HTTP-01)",
|
||||
"servicesCard": {
|
||||
"title": "Service status (live, 10s)"
|
||||
},
|
||||
@@ -510,6 +734,17 @@
|
||||
"title": "Time server (Chrony)",
|
||||
"intro": "Chrony as time-sync daemon (NTP). Sources on top, listen/serve config on the settings tab. With 'serve_clients' on and LAN-IPs bound, the box itself becomes an NTP server for the LAN.",
|
||||
"tabs": { "pools": "Sources", "settings": "Settings" },
|
||||
"statusCard": {
|
||||
"title": "Sync status (chronyc tracking)",
|
||||
"sync": "Synchronized",
|
||||
"synced": "Yes",
|
||||
"notSynced": "No",
|
||||
"source": "Source",
|
||||
"stratum": "Stratum",
|
||||
"offset": "Offset",
|
||||
"offsetHint": "Time difference to reference source. Values > 100 ms are unusual — check network or misconfigured source.",
|
||||
"loading": "Loading…"
|
||||
},
|
||||
"pool": {
|
||||
"kind": "Type",
|
||||
"kindPool": "pool — DNS round-robin (multiple servers from A records)",
|
||||
@@ -524,7 +759,9 @@
|
||||
"description": "Description",
|
||||
"add": "Add source",
|
||||
"edit": "Edit source",
|
||||
"deleteConfirm": "Really delete NTP source {{addr}}?"
|
||||
"deleteConfirm": "Really delete NTP source {{addr}}?",
|
||||
"emptyTitle": "No NTP sources yet.",
|
||||
"emptyDesc": "Without configured pool/server entries chrony falls back to its compiled-in default pool (debian.pool.ntp.org). Set custom pools for better time sync or internal stratum servers."
|
||||
},
|
||||
"settings": {
|
||||
"intro": "Global chrony settings. Saves reload chrony automatically.",
|
||||
@@ -560,7 +797,9 @@
|
||||
"records": "Records …",
|
||||
"add": "Add zone",
|
||||
"edit": "Edit zone",
|
||||
"deleteConfirm": "Really delete zone {{name}} and all its records?"
|
||||
"deleteConfirm": "Really delete zone {{name}} and all its records?",
|
||||
"emptyTitle": "No DNS zones yet.",
|
||||
"emptyDesc": "Unbound forwards everything to upstream resolvers by default. Add a zone to host internal FQDNs (internal.example.com) locally or set up an upstream stub for a foreign domain."
|
||||
},
|
||||
"record": {
|
||||
"name": "Name",
|
||||
@@ -572,7 +811,9 @@
|
||||
"drawerTitle": "DNS records",
|
||||
"add": "Add record",
|
||||
"edit": "Edit record",
|
||||
"deleteConfirm": "Really delete record {{name}}?"
|
||||
"deleteConfirm": "Really delete record {{name}}?",
|
||||
"emptyTitle": "No records in this zone yet.",
|
||||
"emptyDesc": "A/AAAA/CNAME/MX/TXT entries. Authoritative on local zones; on forward zones records here have no effect (upstream wins)."
|
||||
},
|
||||
"settings": {
|
||||
"intro": "Global resolver settings. Saves reload Unbound automatically.",
|
||||
@@ -608,7 +849,9 @@
|
||||
"comment": "Comment",
|
||||
"add": "Add ACL",
|
||||
"edit": "Edit ACL",
|
||||
"deleteConfirm": "Really delete ACL {{name}}?"
|
||||
"deleteConfirm": "Really delete ACL {{name}}?",
|
||||
"emptyTitle": "No forward-proxy ACLs yet.",
|
||||
"emptyDesc": "Default with no ACLs: only localnet (10/8, 172.16/12, 192.168/16) is allowed out. Add an ACL to selectively allow or block specific domains/IPs/ports."
|
||||
},
|
||||
"common": {
|
||||
"yes": "Yes",
|
||||
@@ -629,7 +872,9 @@
|
||||
"add": "Add",
|
||||
"download": "Download",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied"
|
||||
"copied": "Copied",
|
||||
"close": "Close",
|
||||
"refresh": "Refresh"
|
||||
},
|
||||
"license": {
|
||||
"title": "License",
|
||||
@@ -678,6 +923,8 @@
|
||||
"addTitle": "Add static route",
|
||||
"editTitle": "Edit static route",
|
||||
"empty": "No managed routes yet.",
|
||||
"emptyTitle": "No managed routes yet.",
|
||||
"emptyDesc": "Static routes EdgeGuard installs on boot (e.g. to 10.0.5.0/24 via VPN gateway). Live routes above are read-only — what you add here persists.",
|
||||
"confirmDelete": "Really delete route to {{dest}}?",
|
||||
"refreshTooltip": "Reload live routes",
|
||||
"destExtra": "CIDR — e.g. 10.0.5.0/24 or 0.0.0.0/0 for the default route.",
|
||||
@@ -711,6 +958,10 @@
|
||||
"testDone": "Test sent — {{ok}}/{{total}} channels OK",
|
||||
"emptyChannels": "No channels. Add a webhook or an email.",
|
||||
"emptyEvents": "No alerts yet — triggers haven't fired any events.",
|
||||
"emptyChannelsTitle": "No alert channels yet.",
|
||||
"emptyChannelsDesc": "Without channels, fired events are only written to the database (Events tab) — nobody is notified. Add a webhook (Mattermost/Slack/Discord/custom) or an SMTP email.",
|
||||
"emptyEventsTitle": "No alert events yet.",
|
||||
"emptyEventsDesc": "Triggers (cert expiry, backup failure, cluster drift, license invalid, etc.) haven't fired any events yet. When they do, they land here and get delivered to the configured channels.",
|
||||
"noChannels": "no active channel",
|
||||
"confirmDelete": "Really delete channel {{name}}?",
|
||||
"col": {
|
||||
@@ -766,6 +1017,12 @@
|
||||
"scopeTitle": "What is backed up?",
|
||||
"scopeDesc": "DB dump (pg_dump --clean), setup.json, license_key, license.cache, .jwt_fingerprint, acme-account/. Generated configs (haproxy.cfg, nft, …) are reproducible from the DB and are NOT included.",
|
||||
"tabs": { "history": "Backups", "remotes": "Off-site targets" },
|
||||
"statLastSuccess": "Last successful backup",
|
||||
"statTotal": "Successful backups",
|
||||
"statSize": "Total size",
|
||||
"statFails24h": "Failures (24 h)",
|
||||
"statHoursAgo": "{{n}} h ago",
|
||||
"statDaysAgo": "{{n}} days ago",
|
||||
"runNow": "Run backup now",
|
||||
"created": "Backup created: {{file}}",
|
||||
"failed": "Backup failed",
|
||||
@@ -821,7 +1078,8 @@
|
||||
"filter": {
|
||||
"sources": "Select sources (all if empty)",
|
||||
"levels": "Filter levels",
|
||||
"grep": "Full-text search"
|
||||
"grep": "Full-text search",
|
||||
"reset": "Reset filters"
|
||||
}
|
||||
},
|
||||
"fwlog": {
|
||||
@@ -861,5 +1119,34 @@
|
||||
"dst": "Dest IP",
|
||||
"rule": "Rule ID"
|
||||
}
|
||||
},
|
||||
"audit": {
|
||||
"title": "Audit log",
|
||||
"intro": "Who changed what, when. Every mutation through the API (domain create, backend disable, cert issue, …) is recorded here.",
|
||||
"filter": {
|
||||
"actor": "Actor",
|
||||
"action": "Action",
|
||||
"subject": "Subject",
|
||||
"range": "Time range",
|
||||
"search": "Search",
|
||||
"reset": "Reset"
|
||||
},
|
||||
"col": {
|
||||
"time": "Time",
|
||||
"actor": "Actor",
|
||||
"action": "Action",
|
||||
"subject": "Subject",
|
||||
"detail": "Details"
|
||||
},
|
||||
"detailShow": "Show details",
|
||||
"empty": {
|
||||
"title": "No matches",
|
||||
"desc": "No entries matched these filters. Adjust or reset them."
|
||||
},
|
||||
"page": {
|
||||
"prev": "Prev",
|
||||
"next": "Next",
|
||||
"showing": "Row {{from}}–{{to}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user