feat(ui): Quick-Toggles, Config-Preview, Dashboard-Alerts, Domain-Detail-Health
Quick-Toggle-Switches (kein Modal nötig) für: Backends, Backend-Server, DNS-Zonen, DNS-Records, Domains (active), Firewall-Rules, NAT-Rules, Forward-Proxy ACLs, Routing-Rules. Dashboard: Alert-Banner für komplett ausgefallene Backends (HAProxy-Stats) und Domains im Maintenance-Mode. Domain-Detail: HAProxy-Live-Health-Badge (15s Polling), TLS-Cert ausstellen/erneuern direkt aus dem Detail, Routing-Rules-Panel inline. Config-Preview (Settings): alle 4 Generatoren (haproxy, nftables, squid, unbound) rendern via RenderToString ohne Disk-Write — GET /system/config-preview. ActionButtons: Viewer-Rolle blendet Delete aus (RBAC-Ergänzung). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { Alert, Button, Card, Descriptions, Form, Input, InputNumber, Space, Spin, Switch, Typography, message } from 'antd'
|
||||
import { CloudDownloadOutlined, CloudSyncOutlined, DatabaseOutlined, ExclamationCircleOutlined, FileSearchOutlined, GlobalOutlined, LockOutlined, MailOutlined, ReloadOutlined, SettingOutlined, StopOutlined, ToolOutlined } from '@ant-design/icons'
|
||||
import { Alert, Button, Card, Descriptions, Form, Input, InputNumber, Select, Space, Spin, Switch, Typography, message } from 'antd'
|
||||
import { CloudDownloadOutlined, CloudSyncOutlined, CodeOutlined, DatabaseOutlined, ExclamationCircleOutlined, FileSearchOutlined, GlobalOutlined, LockOutlined, MailOutlined, ReloadOutlined, SettingOutlined, StopOutlined, ToolOutlined } from '@ant-design/icons'
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
@@ -260,6 +260,20 @@ export default function SettingsPage() {
|
||||
},
|
||||
})
|
||||
|
||||
const [previewGen, setPreviewGen] = useState('haproxy')
|
||||
const {
|
||||
data: previewData,
|
||||
isFetching: previewLoading,
|
||||
refetch: loadPreview,
|
||||
} = useQuery({
|
||||
queryKey: ['system', 'config-preview', previewGen],
|
||||
queryFn: async () => {
|
||||
const r = await apiClient.get('/system/config-preview', { params: { generator: previewGen } })
|
||||
return isEnvelope(r.data) ? (r.data.data as { generator: string; content: string }) : null
|
||||
},
|
||||
enabled: false,
|
||||
})
|
||||
|
||||
const changePassword = useMutation({
|
||||
mutationFn: async (v: { current_password: string; new_password: string }) => {
|
||||
const r = await apiClient.post('/auth/change-password', v)
|
||||
@@ -649,6 +663,47 @@ export default function SettingsPage() {
|
||||
</Space>
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
title={<><CodeOutlined /> {t('settings.configPreviewCardTitle')}</>}
|
||||
className="mb-12"
|
||||
size="small"
|
||||
>
|
||||
<Space direction="vertical" size={8} style={{ width: '100%' }}>
|
||||
<Space>
|
||||
<Select
|
||||
value={previewGen}
|
||||
onChange={(v) => setPreviewGen(v)}
|
||||
style={{ width: 140 }}
|
||||
options={[
|
||||
{ value: 'haproxy', label: 'haproxy' },
|
||||
{ value: 'nftables', label: 'nftables' },
|
||||
{ value: 'squid', label: 'squid' },
|
||||
{ value: 'unbound', label: 'unbound' },
|
||||
]}
|
||||
/>
|
||||
<Button
|
||||
icon={<ReloadOutlined />}
|
||||
loading={previewLoading}
|
||||
onClick={() => { void loadPreview() }}
|
||||
>
|
||||
{t('settings.configPreviewBtn')}
|
||||
</Button>
|
||||
</Space>
|
||||
{previewData?.content && (
|
||||
<pre style={{
|
||||
marginTop: 4, padding: 10, background: '#f8fafc',
|
||||
fontSize: 11, lineHeight: 1.5, overflow: 'auto', maxHeight: 400,
|
||||
border: '1px solid #e2e8f0', borderRadius: 4, whiteSpace: 'pre',
|
||||
}}>
|
||||
{previewData.content}
|
||||
</pre>
|
||||
)}
|
||||
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
|
||||
{t('settings.configPreviewHint')}
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
</Card>
|
||||
|
||||
<Card title={<><LockOutlined /> {t('settings.passwordCardTitle')}</>} size="small">
|
||||
<Form<ChangePasswordValues>
|
||||
form={pwForm}
|
||||
|
||||
Reference in New Issue
Block a user