feat: per-Backend timeout server + Alerts-Deeplink + Retention + Cert-Prune — v1.3.4

- feat(backends): per-Backend `server_timeout_seconds` (nullable, Default 60s).
  Rendert `timeout server <N>s` im HAProxy-Backend-Block — für langsam
  antwortende Upstreams (KI-/Inferenz-Server mit gepufferter Antwort).
  Migration 0044 (+CHECK 1..86400), Model/Repo/Template/UI + Render-Test.
- fix(ui): Dashboard-Alert-Karte verlinkt auf /alerts?tab=events; Alerts-Seite
  respektiert ?tab= (Deeplink landete bisher auf leerem Channels-Tab).
- feat(scheduler): alert_events-Retention (90d) im täglichen Cleanup-Tick —
  Schutz vor unbounded growth der node-lokalen Health-Event-History.
- fix(cluster): Cert-Sync prunt jetzt lokale .pem die der Primary nicht mehr
  hat (Waisen gelöschter Domains); schützt _default.pem + eigenen Node-Cert.
- fix(security): x/text v0.37→v0.39 (GO-2026-5970, Infinite-Loop; via ACME+goose
  aktiv aufgerufen — govulncheck-Release-Gate).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Debian
2026-07-27 15:57:35 +02:00
parent 32ab2c7f47
commit 0ac91a7c59
17 changed files with 227 additions and 41 deletions

View File

@@ -8,6 +8,7 @@ import {
} from '@ant-design/icons'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { useTranslation } from 'react-i18next'
import { useSearchParams } from 'react-router-dom'
import dayjs from 'dayjs'
import apiClient, { isEnvelope } from '../../api/client'
@@ -69,6 +70,14 @@ export default function AlertsPage() {
const qc = useQueryClient()
const isViewer = useAuthStore((s) => s.user?.role) === 'viewer'
// Tab über ?tab= steuerbar, damit der Dashboard-Deeplink
// (/alerts?tab=events) direkt auf der Event-History landet statt auf
// dem Channels-Tab. Default bleibt 'channels' für den Direktaufruf.
const [searchParams, setSearchParams] = useSearchParams()
const activeTab = searchParams.get('tab') === 'events' ? 'events' : 'channels'
const setActiveTab = (key: string) =>
setSearchParams(key === 'events' ? { tab: 'events' } : {}, { replace: true })
const channels = useQuery({
queryKey: ['alerts', 'channels'],
queryFn: async () => {
@@ -304,7 +313,7 @@ export default function AlertsPage() {
description={t('alerts.scopeDesc')}
/>
<Tabs items={[
<Tabs activeKey={activeTab} onChange={setActiveTab} items={[
{
key: 'channels',
label: t('alerts.tabs.channels'),