feat: добавлена поддержка отображения устройства в журнале трафика
Refs: None
This commit is contained in:
@@ -33,10 +33,22 @@ const CATEGORY_BADGE = {
|
||||
other: { cls: '', label: 'other' },
|
||||
};
|
||||
|
||||
function getDeviceName(sourceIp, deviceRules) {
|
||||
if (!sourceIp || !deviceRules?.length) return null;
|
||||
for (const d of deviceRules) {
|
||||
if (d.enabled === false) continue;
|
||||
for (const ip of (d.sourceIps || [])) {
|
||||
const plain = ip.endsWith('/32') ? ip.slice(0, -3) : ip;
|
||||
if (plain === sourceIp) return d.name;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function groupTraffic(list, sortBy = 'time') {
|
||||
const map = new Map();
|
||||
for (const e of list) {
|
||||
const key = `${e.category}|${e.host}|${e.port}|${e.matchedRule || ''}`;
|
||||
const key = `${e.sourceIp || ''}|${e.category}|${e.host}|${e.port}|${e.matchedRule || ''}`;
|
||||
const ts = new Date(e.ts).getTime();
|
||||
if (map.has(key)) {
|
||||
const g = map.get(key);
|
||||
@@ -52,7 +64,7 @@ function groupTraffic(list, sortBy = 'time') {
|
||||
return arr.sort((a, b) => b._lastTs - a._lastTs);
|
||||
}
|
||||
|
||||
function TrafficTab() {
|
||||
function TrafficTab({ deviceRules = [] }) {
|
||||
const [traffic, setTraffic] = useState([]);
|
||||
const [paused, setPaused] = useState(false);
|
||||
const [filter, setFilter] = useState('all'); // all | direct | vpn | block
|
||||
@@ -90,7 +102,9 @@ function TrafficTab() {
|
||||
e.host?.toLowerCase().includes(s) ||
|
||||
String(e.port || '').includes(s) ||
|
||||
e.outbound?.toLowerCase().includes(s) ||
|
||||
e.matchedRule?.toLowerCase().includes(s),
|
||||
e.matchedRule?.toLowerCase().includes(s) ||
|
||||
e.sourceIp?.toLowerCase().includes(s) ||
|
||||
getDeviceName(e.sourceIp, deviceRules)?.toLowerCase().includes(s),
|
||||
);
|
||||
}
|
||||
return grouped ? groupTraffic(list, sortBy) : list;
|
||||
@@ -159,6 +173,7 @@ function TrafficTab() {
|
||||
<tr>
|
||||
<th style={{ width: 70 }}>Время</th>
|
||||
<th style={{ width: 70 }}>Туннель</th>
|
||||
<th style={{ width: 110 }}>Устройство</th>
|
||||
<th>Хост / IP</th>
|
||||
<th style={{ width: 55 }}>Порт</th>
|
||||
<th>Правило</th>
|
||||
@@ -168,12 +183,20 @@ function TrafficTab() {
|
||||
<tbody>
|
||||
{filtered.map((e, i) => {
|
||||
const badge = CATEGORY_BADGE[e.category] || CATEGORY_BADGE.other;
|
||||
const deviceName = getDeviceName(e.sourceIp, deviceRules);
|
||||
return (
|
||||
<tr key={i} style={{ opacity: e.category === 'block' ? 0.6 : 1 }}>
|
||||
<td className="muted text-mono" style={{ whiteSpace: 'nowrap' }}>{formatTime(e.ts)}</td>
|
||||
<td>
|
||||
<span className={`badge ${badge.cls}`} style={{ fontSize: 11 }}>{badge.label}</span>
|
||||
</td>
|
||||
<td className="text-mono" style={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', maxWidth: 110 }}>
|
||||
{deviceName
|
||||
? <span style={{ fontSize: 11 }}>{deviceName}</span>
|
||||
: e.sourceIp
|
||||
? <span className="muted" style={{ fontSize: 10 }}>{e.sourceIp}</span>
|
||||
: <span className="muted" style={{ fontSize: 11 }}>—</span>}
|
||||
</td>
|
||||
<td className="text-mono" style={{ wordBreak: 'break-all' }}>{e.host || '—'}</td>
|
||||
<td className="muted text-mono">{e.port || '—'}</td>
|
||||
<td>
|
||||
@@ -195,7 +218,7 @@ function TrafficTab() {
|
||||
);
|
||||
}
|
||||
|
||||
export function LogsPage() {
|
||||
export function LogsPage({ deviceRules = [] }) {
|
||||
const [tab, setTab] = useState('traffic'); // traffic | logs
|
||||
const [entries, setEntries] = useState([]);
|
||||
const [paused, setPaused] = useState(false);
|
||||
@@ -253,7 +276,7 @@ export function LogsPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{tab === 'traffic' && <TrafficTab />}
|
||||
{tab === 'traffic' && <TrafficTab deviceRules={deviceRules} />}
|
||||
|
||||
{tab === 'logs' && (
|
||||
<>
|
||||
|
||||
Reference in New Issue
Block a user