feat(backends): WebSocket-Toggle pro Backend

Migration 0017 fügt backends.websocket BOOL. Wenn aktiv emittiert der
HAProxy-Renderer `timeout tunnel 1h` IM Backend-Block; defaults-Section
hat den Global-Timeout dafür verloren. Backends ohne WS-Workload bleiben
bei strikten HTTP-Timeouts (Connection-Hygiene). Migrations-Heuristik
schaltet vm-pool/proxmox/console/vnc-Namen auto auf true damit Proxmox-
Konsole nach Deploy weiterhin durchläuft.

UI: Switch im Backend-Modal + WS-Tag in der Übersichtstabelle.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Debian
2026-05-11 21:51:09 +02:00
parent da35097041
commit 26f321de9d
13 changed files with 108 additions and 24 deletions

View File

@@ -77,7 +77,7 @@ const NAV: NavSection[] = [
},
]
const VERSION = '1.0.50'
const VERSION = '1.0.51'
export default function Sidebar({ isOpen, onClose }: SidebarProps) {
const { t } = useTranslation()

View File

@@ -217,6 +217,8 @@
"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.",
"servers": "Server",
"noServers": "kein Server",
"nServers": "{{n}} Server",

View File

@@ -217,6 +217,8 @@
"selectDomains": "Select domains",
"lbAlgo": "Load balancing",
"lbAlgoHint": "roundrobin = evenly, leastconn = pick the server with fewest active connections, source = sticky per client-IP hash (for stateful apps without shared session).",
"websocket": "WebSocket support",
"websocketHint": "On: allow long-lived WebSocket / long-poll connections (Proxmox console, SSH-over-WS, AsyncAPI) — tunnel idle 1h instead of 60s. Off: strict HTTP timeouts.",
"servers": "Servers",
"noServers": "no server",
"nServers": "{{n}} servers",

View File

@@ -22,6 +22,7 @@ interface Backend {
scheme: string
health_check_path?: string | null
lb_algorithm: 'roundrobin' | 'leastconn' | 'source'
websocket: boolean
active: boolean
created_at: string
updated_at: string
@@ -43,6 +44,7 @@ interface BackendFormValues {
scheme: 'http' | 'https'
health_check_path?: string
lb_algorithm: 'roundrobin' | 'leastconn' | 'source'
websocket: boolean
active: boolean
domain_ids?: number[]
}
@@ -193,7 +195,12 @@ export default function BackendsPage() {
},
{
title: t('backends.lbAlgo'), dataIndex: 'lb_algorithm', key: 'lb',
render: (v: string) => <Tag>{v}</Tag>,
render: (v: string, row) => (
<Space size={4}>
<Tag>{v}</Tag>
{row.websocket && <Tag color="cyan">WS</Tag>}
</Space>
),
},
{ title: t('backends.healthCheck'), dataIndex: 'health_check_path', key: 'hc', render: (v?: string) => v ?? '—' },
{
@@ -216,6 +223,7 @@ export default function BackendsPage() {
scheme: row.scheme as 'http' | 'https',
health_check_path: row.health_check_path ?? undefined,
lb_algorithm: row.lb_algorithm,
websocket: row.websocket,
active: row.active,
domain_ids: domainsForBackend(row.id).map(d => d.id),
})
@@ -246,7 +254,7 @@ export default function BackendsPage() {
extraActions={
<Button type="primary" icon={<PlusOutlined />} onClick={() => {
setCreating(true); form.resetFields()
form.setFieldsValue({ scheme: 'http', lb_algorithm: 'roundrobin', active: true })
form.setFieldsValue({ scheme: 'http', lb_algorithm: 'roundrobin', websocket: false, active: true })
}}>
{t('backends.addBackend')}
</Button>
@@ -294,6 +302,10 @@ export default function BackendsPage() {
<Form.Item label={t('backends.healthCheck')} name="health_check_path">
<Input placeholder="/health" />
</Form.Item>
<Form.Item label={t('backends.websocket')} name="websocket" valuePropName="checked"
extra={t('backends.websocketHint')}>
<Switch />
</Form.Item>
<Form.Item label={t('backends.active')} name="active" valuePropName="checked">
<Switch />
</Form.Item>