cleanup frontend notifications
All checks were successful
Build and Deploy MITM Webserver / build (push) Successful in 1m39s
All checks were successful
Build and Deploy MITM Webserver / build (push) Successful in 1m39s
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
//
|
||||
// NOTE: This file replaces the token lists and per-field UI to strictly follow the user's canonical list.
|
||||
|
||||
import { CopyOutlined, PlusOutlined, ReloadOutlined } from '@ant-design/icons';
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
@@ -22,11 +22,10 @@ import {
|
||||
InputNumber,
|
||||
message,
|
||||
Modal,
|
||||
notification,
|
||||
Radio,
|
||||
Row,
|
||||
Select,
|
||||
Space,
|
||||
Tooltip,
|
||||
Typography,
|
||||
} from 'antd';
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
@@ -584,9 +583,14 @@ export const RuleBuilder: React.FC<RuleBuilderProps> = (props: RuleBuilderProps)
|
||||
const out: ExecResult = await execFirewallRaw(cmd);
|
||||
const stderrText = out?.stderr ? String(out.stderr).trim() : '';
|
||||
if (stderrText) {
|
||||
message.error(`Command produced stderr: ${stderrText}`);
|
||||
notification.error({
|
||||
message: 'Command produced Error',
|
||||
description: stderrText,
|
||||
});
|
||||
} else if (out && (out.rc === 0 || out.rc === -1)) {
|
||||
message.success('Command executed (rc ' + out.rc + ')');
|
||||
notification.success({
|
||||
message: 'Command executed successfully',
|
||||
});
|
||||
if (props.refreshRules) await props.refreshRules();
|
||||
if (props.onCreated) await props.onCreated();
|
||||
} else {
|
||||
@@ -595,10 +599,16 @@ export const RuleBuilder: React.FC<RuleBuilderProps> = (props: RuleBuilderProps)
|
||||
(out.stdout ? ` stdout:${out.stdout}` : '') +
|
||||
(out.stderr ? ` stderr:${out.stderr}` : '')
|
||||
: 'unknown result';
|
||||
message.error('Command failed: ' + info);
|
||||
notification.error({
|
||||
message: 'Command failed',
|
||||
description: info,
|
||||
});
|
||||
}
|
||||
} catch (err: any) {
|
||||
message.error('Execution failed: ' + (err?.message ?? String(err)));
|
||||
notification.error({
|
||||
message: 'Execution failed',
|
||||
description: err?.message ?? String(err),
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -670,28 +680,7 @@ export const RuleBuilder: React.FC<RuleBuilderProps> = (props: RuleBuilderProps)
|
||||
};
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<Row justify="space-between" align="middle">
|
||||
<Col>
|
||||
<Title level={4} style={{ margin: 0 }}>
|
||||
Add Firewall Rule (raw)
|
||||
</Title>
|
||||
<Text type="secondary">
|
||||
Build an nft rule using canonical match list (meta/ct/ip/ip6/tcp/udp/icmp/ether/vlan).
|
||||
</Text>
|
||||
</Col>
|
||||
|
||||
<Col>
|
||||
<Space>
|
||||
<Button icon={<ReloadOutlined />} onClick={handleRefresh} loading={refreshing}>
|
||||
Refresh tables
|
||||
</Button>
|
||||
</Space>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Card title="Firewall Rule Builder" extra>
|
||||
<Form
|
||||
layout="vertical"
|
||||
form={form}
|
||||
@@ -1703,29 +1692,17 @@ export const RuleBuilder: React.FC<RuleBuilderProps> = (props: RuleBuilderProps)
|
||||
<Form.Item>
|
||||
<div style={{ display: 'flex', gap: 12, alignItems: 'center', width: '100%' }}>
|
||||
<div style={{ flex: 1 }}>
|
||||
<Text strong>nft command preview:</Text>
|
||||
<Title level={5}>nft command preview:</Title>
|
||||
<div style={{ marginTop: 6 }}>
|
||||
<Text code copyable={{ text: cmdPreview || '' }}>
|
||||
<Text code copyable={{ text: cmdPreview || '' }} style={{ fontSize: '16px' }}>
|
||||
{cmdPreview || '(no textual preview available)'}
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', gap: 8, minWidth: 320, justifyContent: 'flex-end' }}>
|
||||
<Tooltip title="Copy command to clipboard">
|
||||
<Button
|
||||
icon={<CopyOutlined />}
|
||||
size="small"
|
||||
onClick={() => {
|
||||
if (!cmdPreview) return;
|
||||
navigator.clipboard?.writeText(cmdPreview);
|
||||
message.success('Command copied');
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
|
||||
<Button type="primary" htmlType="submit" loading={loading} icon={<PlusOutlined />} disabled={noTables}>
|
||||
Run command
|
||||
Add Rule
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -132,7 +132,6 @@ type Props = {
|
||||
export default function FirewallTables({ tables, error, refreshRules: refresh }: Props): ReactElement {
|
||||
// local UI state, non-persistent
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
const [results, setResults] = useState<CmdResult[]>([]);
|
||||
const [isOpenTableCreatorModal, setIsOpenTableCreatorModal] = useState(false);
|
||||
const [isOpenChainCreatorModal, setIsOpenChainCreatorModal] = useState(false);
|
||||
|
||||
@@ -148,8 +147,6 @@ export default function FirewallTables({ tables, error, refreshRules: refresh }:
|
||||
acc.push({ cmd, err: err?.message ?? String(err) });
|
||||
}
|
||||
}
|
||||
// prepend new results to history
|
||||
setResults((prev) => [...acc, ...prev]);
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
@@ -466,38 +463,6 @@ export default function FirewallTables({ tables, error, refreshRules: refresh }:
|
||||
);
|
||||
})}
|
||||
</Card>
|
||||
|
||||
{/* Results panel */}
|
||||
{results.length > 0 && (
|
||||
<Card title="Command Results" style={{ marginTop: 20 }}>
|
||||
{results.map((r, i) => (
|
||||
<div key={i} style={{ marginBottom: 12 }}>
|
||||
<Text strong>{r.cmd}</Text>
|
||||
{r.err ? (
|
||||
<Paragraph style={{ color: 'var(--ant-error-color)', marginTop: 6 }}>{r.err}</Paragraph>
|
||||
) : r.out ? (
|
||||
<>
|
||||
<Paragraph>
|
||||
<Text type="secondary">rc:</Text> {r.out.rc}
|
||||
</Paragraph>
|
||||
{r.out.stdout ? (
|
||||
<>
|
||||
<Text type="secondary">stdout:</Text>
|
||||
<pre style={{ whiteSpace: 'pre-wrap', background: '#fff', padding: 8 }}>{r.out.stdout}</pre>
|
||||
</>
|
||||
) : null}
|
||||
{r.out.stderr ? (
|
||||
<>
|
||||
<Text type="secondary">stderr:</Text>
|
||||
<pre style={{ whiteSpace: 'pre-wrap', background: '#fff', padding: 8 }}>{r.out.stderr}</pre>
|
||||
</>
|
||||
) : null}
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
))}
|
||||
</Card>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
import { ReactElement, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { clearPackets, fetchPackets } from '../api/apiClient';
|
||||
|
||||
const { Text } = Typography;
|
||||
const { Text, Title } = Typography;
|
||||
const { Option } = Select;
|
||||
|
||||
type PacketRow = {
|
||||
@@ -409,12 +409,10 @@ export default function PacketViewer(): ReactElement {
|
||||
);
|
||||
|
||||
return (
|
||||
<div style={{ padding: 16 }}>
|
||||
<div>
|
||||
<Row justify="space-between" align="middle" style={{ marginBottom: 12 }}>
|
||||
<Col>
|
||||
<Text strong style={{ fontSize: 18 }}>
|
||||
Packets
|
||||
</Text>
|
||||
<Title level={3}>Packets</Title>
|
||||
<div>
|
||||
<Text type="secondary">Live packet viewer — history + live stream</Text>
|
||||
</div>
|
||||
|
||||
@@ -7,7 +7,21 @@ import {
|
||||
ReloadOutlined,
|
||||
StopOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { Button, Card, Form, List, message, Modal, Radio, Select, Space, Spin, Tag, Tooltip, Typography } from 'antd';
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Form,
|
||||
List,
|
||||
Modal,
|
||||
notification,
|
||||
Radio,
|
||||
Select,
|
||||
Space,
|
||||
Spin,
|
||||
Tag,
|
||||
Tooltip,
|
||||
Typography,
|
||||
} from 'antd';
|
||||
import { ReactElement, useMemo, useState } from 'react';
|
||||
import { startSniffer, stopSniffer, stopSnifferByInterface } from '../api/apiClient';
|
||||
import { BridgeInfo, InterfaceInfo } from '../types/network';
|
||||
@@ -52,18 +66,21 @@ export default function SnifferManager(props: SnifferManagerProps): ReactElement
|
||||
const handleStartSubmit = async (values: any) => {
|
||||
const { target } = values;
|
||||
if (!target) {
|
||||
message.warning('Please select a target to start sniffing on.');
|
||||
notification.warning({ message: 'Warning', description: 'Please select a target to start sniffing on.' });
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const payload = startMode === 'interface' ? { interface: target } : { bridge: target };
|
||||
const res = await startSniffer(payload);
|
||||
message.success(`Sniffer started on ${target} (session ${res.session_id})`);
|
||||
notification.success({
|
||||
message: 'Sniffer started',
|
||||
description: `Sniffer started on ${target} (session ${res.session_id})`,
|
||||
});
|
||||
await props.refreshAll();
|
||||
setIsModalOpen(false);
|
||||
} catch (err: any) {
|
||||
console.error('startSniffer error', err);
|
||||
message.error(err?.message ?? 'Failed to start sniffer');
|
||||
notification.error(err?.message ?? 'Failed to start sniffer');
|
||||
} finally {
|
||||
}
|
||||
};
|
||||
@@ -72,11 +89,13 @@ export default function SnifferManager(props: SnifferManagerProps): ReactElement
|
||||
const handleStopAll = async () => {
|
||||
try {
|
||||
await stopSniffer();
|
||||
message.success('All sniffers stopped');
|
||||
notification.success({
|
||||
message: 'All sniffers stopped',
|
||||
});
|
||||
await props.refreshStatus();
|
||||
} catch (err: any) {
|
||||
console.error('stopSniffer error', err);
|
||||
message.error(err?.message ?? 'Failed to stop sniffers');
|
||||
notification.error(err?.message ?? 'Failed to stop sniffers');
|
||||
} finally {
|
||||
}
|
||||
};
|
||||
@@ -86,7 +105,10 @@ export default function SnifferManager(props: SnifferManagerProps): ReactElement
|
||||
try {
|
||||
// prefer stop by interface
|
||||
await stopSnifferByInterface(ifaceName);
|
||||
message.success(`Stopped sniffing on ${ifaceName}`);
|
||||
notification.success({
|
||||
message: 'Sniffer stopped',
|
||||
description: `Sniffer stopped on interface ${ifaceName}`,
|
||||
});
|
||||
await props.refreshStatus();
|
||||
return;
|
||||
} catch (err: any) {
|
||||
@@ -95,14 +117,20 @@ export default function SnifferManager(props: SnifferManagerProps): ReactElement
|
||||
if (session_id) {
|
||||
try {
|
||||
await stopSniffer({ session_id });
|
||||
message.success(`Stopped session ${session_id}`);
|
||||
notification.success({
|
||||
message: 'Sniffer stopped',
|
||||
description: `Sniffer stopped on interface ${ifaceName} (session ${session_id})`,
|
||||
});
|
||||
await props.refreshStatus();
|
||||
return;
|
||||
} catch (err2: any) {
|
||||
console.error('stopSniffer by session fallback failed', err2);
|
||||
}
|
||||
}
|
||||
message.error(err?.message ?? 'Failed to stop sniffer');
|
||||
notification.error({
|
||||
message: 'Failed to stop sniffer',
|
||||
description: err?.message ?? 'Failed to stop sniffer',
|
||||
});
|
||||
} finally {
|
||||
}
|
||||
};
|
||||
@@ -169,11 +197,17 @@ export default function SnifferManager(props: SnifferManagerProps): ReactElement
|
||||
// start on this interface
|
||||
try {
|
||||
const res = await startSniffer({ interface: name });
|
||||
message.success(`Started sniffer on ${name} (session ${res.session_id})`);
|
||||
notification.success({
|
||||
message: 'Sniffer started',
|
||||
description: `Sniffer started on ${name} (session ${res.session_id})`,
|
||||
});
|
||||
await props.refreshStatus();
|
||||
} catch (err: any) {
|
||||
console.error('startSniffer quick', err);
|
||||
message.error(err?.message ?? 'Failed to start sniffer');
|
||||
notification.error({
|
||||
message: 'Failed to start sniffer',
|
||||
description: err?.message ?? 'Failed to start sniffer',
|
||||
});
|
||||
} finally {
|
||||
}
|
||||
}}
|
||||
|
||||
Reference in New Issue
Block a user