{{ t('login.title') }}

{{ t('login.subtitle') }}

{{ loginError }}

{{ t('groups.title') }}

{{ t('groups.desc') }}

{{ t('groups.noConfig') }}

{{ t('groups.id') }} {{ t('groups.version') }} {{ t('groups.nodeCount') }} {{ t('groups.routeCount') }} {{ t('groups.clusterCount') }} {{ t('groups.l4RouteCount') }} {{ t('common.actions') }}
{{ grp.groupId }} {{ grp.version }} {{ grp.nodeCount }} {{ grp.routeCount }} {{ grp.clusterCount }} {{ grp.l4RouteCount }}

{{ t('nodes.title') }}

{{ t('nodes.desc') }}

{{ t('nodes.noConfig') }}

{{ node.name || node.id }}

ID: {{ node.id }}
Group: {{ node.groupId }} Migrating to {{ node.targetGroupId }}...
{{ t('nodes.management') }}: {{ node.managementUrl }} Enabled Disabled
{{ t('nodes.lastHeartbeat') }}: {{ node.lastHeartbeat ? new Date(node.lastHeartbeat).toLocaleString() : 'N/A' }}

{{ t('clusters.title') }}

{{ t('clusters.desc') }}

{{ t('common.loadingData') }}
{{ cluster.clusterId }} {{ cluster.loadBalancing }} {{ t('clusters.healthCheck') }}

{{ t('clusters.destinations') }}

{{ t('clusters.noDestConfig') }}
{{ dest.address }}
ID: {{ dest.destId }}
{{ t('clusters.health') }}: {{ dest.health }}

{{ t('clusters.noConfig') }}

{{ t('routes.title') }}

{{ t('routes.desc') }}

{{ t('common.status') }} {{ t('routes.routeId') }} {{ t('routes.matchPath') }} {{ t('routes.hosts') }} {{ t('routes.clusterId') }} {{ t('routes.order') }} {{ t('common.actions') }}
{{ t('routes.noConfig') }}
{{ r.routeId }} {{ r.path }} {{ r.hosts || '*' }} {{ r.clusterId }} {{ r.order }}

{{ t('tcp.title') }}

{{ t('tcp.desc') }}

{{ t('common.status') }} {{ t('tcp.routeId') }} {{ t('tcp.protocol') }} {{ t('tcp.listenPort') }} {{ t('tcp.policy') }} {{ t('tcp.target') }} {{ t('common.actions') }}
{{ t('tcp.noConfig') }}
{{ r.routeId }} {{ r.protocol }} {{ r.listenPort }} {{ r.loadBalancingPolicy }}
{{ d.targetHost }}:{{ d.targetPort }} (W:{{ d.weight }})

{{ t('keys.title') }}

{{ t('keys.desc') }}

{{ t('keys.accessDenied') }}

{{ t('keys.accessDeniedDesc') }}

{{ t('common.status') }} {{ t('keys.name') }} {{ t('keys.role') }} {{ t('keys.keyPrefix') }} {{ t('keys.lastUsed') }} {{ t('common.actions') }}
{{ t('keys.desc') }}
{{ k.name }} {{ k.role }} {{ k.keyValue.substring(0,4) }}*** {{ k.lastUsedAt || t('keys.never') }}

{{ t('help.title') }}

{{ t('help.desc') }}

{{ t('help.routingQ1Title') }}

Path: /api/health

// ✅ Matches exactly
GET /api/health

// ❌ Fails (subpaths not matched)
GET /api/health/status

{{ t('help.routingQ2Title') }}

Path: /api/{**catch-all}

// ✅ Captures the rest of the path
GET /api/v1/users/123
➔ Captured: v1/users/123

// ➔ Proxy automatically forwards to:
GET https://backend-api.com/api/v1/users/123

{{ t('help.routingQ3Title') }}

Path: /api/users/{id:int}

// ✅ Matches because '123' is an integer
GET /api/users/123

// ❌ Fails because 'abc' is not an integer
GET /api/users/abc

{{ t('help.routingQ4Title') }}

// Route 1:
Hosts: api.customer-a.com, Path: /{**catch-all}Cluster A

// Route 2:
Hosts: api.customer-b.com, Path: /{**catch-all}Cluster B

{{ t('help.metaQ1Title') }}

{{ t('help.metaQ2Title') }}

Metadata JSON: [{"PathRemovePrefix": "/demo"}]

GET http://localhost:8080/demo/api/users
➔ Strips "/demo" ➔ Proxy forwards to ➔
GET https://backend-api.com/api/users

{{ t('help.metaQ3Title') }}

Metadata JSON: [{"PathPrefix": "/v1"}]

GET http://localhost:8080/api/users
➔ Adds "/v1" ➔ Proxy forwards to ➔
GET https://backend-api.com/v1/api/users

{{ t('help.metaQ4Title') }}

Metadata JSON:
[
{"RequestHeader": "X-Tenant-Id", "Set": "customer-A"}
]

➔ Proxy appends Header: X-Tenant-Id: customer-A

{{ t('help.metaQ5Title') }}

{{ t('help.metaQ6Title') }}

{{ t('help.metaDocsTitle') }}

{{ t('help.metaDocsDesc') }}

{{ t('help.lbQ1Title') }}

{{ t('help.lbQ2Title') }}

{ "Active": { "Enabled": true, "Interval": "00:00:10", "Timeout": "00:00:05", "Policy": "ConsecutiveFailures", "Path": "/health" } }

{{ modal.title }}

{{ t('keys.createdTitle') }}

{{ t('keys.createdDesc') }}

{{ newKeyResult }}
{{ toast.isError ? '!' : '\u2713' }} {{ toast.message }}