Advanced pages. Model-driven controls.

This commit is contained in:
2026-07-11 02:31:14 +00:00
parent cd2f28df34
commit 1f3fcf50b4
23 changed files with 1600 additions and 444 deletions

View File

@@ -0,0 +1,22 @@
import { ShieldAlert } from 'lucide-react';
import { useCrowdSec } from '../../hooks/useCrowdSec';
import { SubsystemPage } from '../SubsystemPage';
export function CrowdsecSubsystem() {
const { status, isLoadingStatus } = useCrowdSec();
return (
<SubsystemPage
title="CrowdSec"
description="Collaborative intrusion detection"
icon={ShieldAlert}
daemonKey="crowdsec"
isLoadingConfig={isLoadingStatus}
metadata={[
{ label: 'Machines', value: status?.machines?.length },
{ label: 'Bouncers', value: status?.bouncers?.length },
{ label: 'Firewall Bouncer', value: status?.firewallBouncer ? 'Active' : 'Inactive' },
]}
/>
);
}

View File

@@ -0,0 +1,33 @@
import { useEffect } from 'react';
import { Wifi } from 'lucide-react';
import { useDnsmasq } from '../../hooks/useDnsmasq';
import { SubsystemPage } from '../SubsystemPage';
export function DnsmasqSubsystem() {
const {
config, isLoadingConfig, fetchConfig,
generateConfig, isGenerating, generateData, generateError,
restart, isRestarting, restartError,
} = useDnsmasq();
useEffect(() => { fetchConfig(); }, [fetchConfig]);
return (
<SubsystemPage
title="dnsmasq"
description="DNS resolver and DHCP server"
icon={Wifi}
daemonKey="dnsmasq"
configContent={config?.content}
isLoadingConfig={isLoadingConfig}
onGenerate={() => generateConfig(false)}
isGenerating={isGenerating}
generateLabel="Regenerate"
onRestart={() => restart()}
isRestarting={isRestarting}
restartLabel="Restart dnsmasq"
successMessage={generateData?.message}
errorMessage={generateError?.message ?? restartError?.message ?? null}
/>
);
}

View File

@@ -0,0 +1,41 @@
import { useQuery } from '@tanstack/react-query';
import { Network } from 'lucide-react';
import { useHaproxy } from '../../hooks/useHaproxy';
import { haproxyApi } from '../../services/api/networking';
import { SubsystemPage } from '../SubsystemPage';
export function HaproxySubsystem() {
const {
status,
generate, isGenerating, generateData, generateError,
restart, isRestarting,
} = useHaproxy();
const configQuery = useQuery({
queryKey: ['haproxy', 'config'],
queryFn: () => haproxyApi.getConfig(),
});
return (
<SubsystemPage
title="HAProxy"
description="Reverse proxy and load balancer"
icon={Network}
daemonKey="haproxy"
configContent={configQuery.data?.content}
isLoadingConfig={configQuery.isLoading}
onGenerate={() => generate()}
isGenerating={isGenerating}
generateLabel="Generate & Apply"
onRestart={() => restart()}
isRestarting={isRestarting}
restartLabel="Restart HAProxy"
successMessage={generateData?.message}
errorMessage={generateError ? (generateError as Error).message : null}
metadata={[
{ label: 'Config File', value: status?.configFile },
{ label: 'PID', value: status?.pid },
]}
/>
);
}

View File

@@ -0,0 +1,25 @@
import { Shield } from 'lucide-react';
import { useNftables } from '../../hooks/useNftables';
import { SubsystemPage } from '../SubsystemPage';
export function NftablesSubsystem() {
const { status, isLoadingStatus, apply, isApplying, applyError } = useNftables();
return (
<SubsystemPage
title="nftables"
description="Kernel packet filtering and firewall"
icon={Shield}
daemonKey="nftables"
configContent={status?.rules ?? (isLoadingStatus ? undefined : '# No rules loaded')}
isLoadingConfig={isLoadingStatus}
onGenerate={() => apply()}
isGenerating={isApplying}
generateLabel="Apply Rules"
errorMessage={applyError ? (applyError as Error).message : null}
metadata={[
{ label: 'Rules File', value: status?.rulesFile },
]}
/>
);
}

View File

@@ -0,0 +1,29 @@
import { Lock } from 'lucide-react';
import { useVpn } from '../../hooks/useVpn';
import { SubsystemPage } from '../SubsystemPage';
export function WireguardSubsystem() {
const { status, config, isLoadingConfig, apply, isApplying, applyError } = useVpn();
return (
<SubsystemPage
title="WireGuard"
description="VPN tunnel interface"
icon={Lock}
daemonKey="wireguard"
configContent={config ? JSON.stringify(config, null, 2) : undefined}
isLoadingConfig={isLoadingConfig}
onRestart={() => apply()}
isRestarting={isApplying}
restartLabel="Apply Config"
errorMessage={applyError ? (applyError as Error).message : null}
rawStatusLabel="WireGuard Interface Status"
rawStatusContent={status?.rawOutput}
metadata={[
{ label: 'Interface', value: status?.interface },
{ label: 'Listen Port', value: status?.listenPort },
{ label: 'Peers', value: status?.peerCount },
]}
/>
);
}

View File

@@ -0,0 +1,5 @@
export { HaproxySubsystem } from './HaproxySubsystem';
export { DnsmasqSubsystem } from './DnsmasqSubsystem';
export { NftablesSubsystem } from './NftablesSubsystem';
export { WireguardSubsystem } from './WireguardSubsystem';
export { CrowdsecSubsystem } from './CrowdsecSubsystem';