Compare commits

..

No commits in common. "e894ffd3f9ac03f7f6926dff48a94379e1d17caa" and "223d43f0d9c7cbb7209d2b1df3f320473ac801ea" have entirely different histories.

5 changed files with 104 additions and 255 deletions

View File

@ -1,208 +0,0 @@
const configEffect = {
async generateRealisticConfig(command, devices = []) {
const timestamp = new Date().toLocaleString();
let config = `! 配置生成于 ${timestamp}\n`;
const configTemplates = {
vlan: {
pattern: /(vlan|虚拟局域网|虚拟网)\s*(\d+)/i,
template: (vlanId) =>
`vlan ${vlanId}\n` +
` name VLAN_${vlanId}\n` +
` exit\n` +
`interface Vlan${vlanId}\n` +
` description ${vlanId === '10' ? '管理VLAN' : '用户VLAN'}\n` +
` ip address 192.168.${vlanId}.1 255.255.255.0\n` +
` exit\n`,
},
ssh: {
pattern: /(ssh|安全外壳|远程登录)/i,
template: () => {
const password = Math.random().toString(36).slice(2, 10);
return (
`ip ssh server\n` +
`ip ssh version 2\n` +
`username admin privilege 15 secret 0 ${password}\n` +
`line vty 0 4\n` +
` transport input ssh\n` +
` login local\n` +
` exit\n`
);
},
},
port: {
pattern: /(端口|接口|port|interface)\s*(\d+)/i,
template: (port) => {
const isTrunk = /(trunk|干道)/i.test(command);
const isAccess = /(access|接入)/i.test(command) || !isTrunk;
const desc = /(上联|uplink)/i.test(command) ? 'Uplink_Port' : 'Access_Port';
const vlanId = command.match(/vlan\s*(\d+)/i)?.[1] || '10';
return (
`interface GigabitEthernet0/${port}\n` +
` description ${desc}\n` +
` switchport mode ${isTrunk ? 'trunk' : 'access'}\n` +
` ${isTrunk ? 'switchport trunk allowed vlan all' : `switchport access vlan ${vlanId}`}\n` +
` no shutdown\n` +
` exit\n`
);
},
},
acl: {
pattern: /(acl|访问控制|防火墙)/i,
template: () => {
let targetIP = '192.168.10.10';
if (devices.length > 0) {
const randomDevice = devices[Math.floor(Math.random() * devices.length)];
targetIP = randomDevice.ip;
}
return (
`ip access-list extended PROTECT_SERVERS\n` +
` permit tcp any host ${targetIP} eq 22\n` +
` permit tcp any host ${targetIP} eq 80\n` +
` permit tcp any host ${targetIP} eq 443\n` +
` deny ip any any\n` +
` exit\n` +
`interface Vlan10\n` +
` ip access-group PROTECT_SERVERS in\n` +
` exit\n`
);
},
},
dhcp: {
pattern: /(dhcp|动态主机配置)/i,
template: () => {
const vlanId = command.match(/vlan\s*(\d+)/i)?.[1] || '10';
return (
`ip dhcp pool VLAN_${vlanId}\n` +
` network 192.168.${vlanId}.0 255.255.255.0\n` +
` default-router 192.168.${vlanId}.1\n` +
` dns-server 8.8.8.8 8.8.4.4\n` +
` exit\n` +
`ip dhcp excluded-address 192.168.${vlanId}.1 192.168.${vlanId}.10\n`
);
},
},
nat: {
pattern: /(nat|网络地址转换)/i,
template: () => {
const publicIp = `203.0.113.${Math.floor(Math.random() * 10) + 1}`;
return (
`ip access-list standard NAT_ACL\n` +
` permit 192.168.0.0 0.0.255.255\n` +
` exit\n` +
`ip nat inside source list NAT_ACL interface GigabitEthernet0/1 overload\n` +
`interface GigabitEthernet0/1\n` +
` ip address ${publicIp} 255.255.255.248\n` +
` ip nat outside\n` +
` exit\n` +
`interface Vlan10\n` +
` ip nat inside\n` +
` exit\n`
);
},
},
stp: {
pattern: /(stp|生成树|spanning-tree)/i,
template: () => {
return (
`spanning-tree mode rapid-pvst\n` +
`spanning-tree vlan 1-4094 priority 4096\n` +
`spanning-tree portfast default\n` +
`spanning-tree portfast bpduguard default\n`
);
},
},
portSecurity: {
pattern: /(端口安全|port-security)/i,
template: () => {
const port = command.match(/端口\s*(\d+)/i)?.[1] || '1';
return (
`interface GigabitEthernet0/${port}\n` +
` switchport port-security\n` +
` switchport port-security maximum 5\n` +
` switchport port-security violation restrict\n` +
` switchport port-security mac-address sticky\n` +
` exit\n`
);
},
},
qos: {
pattern: /(qos|服务质量|流量控制)/i,
template: () => {
return (
`class-map match-all VOICE\n` +
` match ip dscp ef\n` +
` exit\n` +
`policy-map QOS_POLICY\n` +
` class VOICE\n` +
` priority percent 20\n` +
` class class-default\n` +
` bandwidth percent 80\n` +
` exit\n` +
`interface GigabitEthernet0/1\n` +
` service-policy output QOS_POLICY\n` +
` exit\n`
);
},
},
vpn: {
pattern: /(vpn|虚拟专用网)/i,
template: () => {
const vpnId = Math.floor(Math.random() * 1000);
return (
`crypto isakmp policy ${vpnId}\n` +
` encryption aes 256\n` +
` hash sha256\n` +
` authentication pre-share\n` +
` group 14\n` +
` exit\n` +
`crypto ipsec transform-set VPN_TRANSFORM esp-aes 256 esp-sha256-hmac\n` +
` mode tunnel\n` +
` exit\n` +
`crypto map VPN_MAP 10 ipsec-isakmp\n` +
` set peer 203.0.113.5\n` +
` set transform-set VPN_TRANSFORM\n` +
` match address VPN_ACL\n` +
` exit\n`
);
},
},
};
let matched = false;
if (/(完整配置|全部配置|all config)/i.test(command)) {
matched = true;
config += '! 生成完整校园网络配置\n';
Object.values(configTemplates).forEach((template) => {
const result = template.template();
if (result) {
config += result;
}
});
} else {
for (const [key, { pattern, template }] of Object.entries(configTemplates)) {
const match = command.match(pattern);
if (match) {
matched = true;
config += template(match[2] || match[1] || '');
}
}
}
if (!matched) {
config += 'hostname SCHOOL_SWITCH\n';
config += 'ip domain-name school.local\n';
config += 'snmp-server community SCHOOL_RO RO\n';
config += 'ntp server 192.168.1.1\n';
config += 'logging trap informational\n';
config += 'logging 192.168.1.10\n';
config += 'service password-encryption\n';
config += 'enable secret 0 ' + Math.random().toString(36).slice(2, 12) + '\n';
config += 'no ip http server\n';
config += 'no ip http secure-server\n';
}
return { config };
},
};
export default configEffect;

View File

@ -1,24 +0,0 @@
const scanEffect = {
getTestDevices() {
return [
{ ip: '192.168.1.1', mac: '00:1A:2B:3C:4D:5E', ports: [22, 23, 161] },
{ ip: '192.168.1.2', mac: '00:1D:7D:AA:1B:01', ports: [22, 23, 161] },
{ ip: '192.168.1.3', mac: '00:0C:29:5A:3B:11', ports: [22, 23, 161, 443] },
{ ip: '192.168.1.4', mac: '00:23:CD:FF:10:02', ports: [22, 23] },
{ ip: '192.168.1.5', mac: '00:04:4B:AA:BB:CC', ports: [22, 23, 80, 443] },
{ ip: '192.168.1.6', mac: '00:11:22:33:44:55', ports: [22, 23, 161] },
{ ip: '192.168.1.7', mac: '00:1F:45:67:89:AB', ports: [23] },
{ ip: '192.168.1.8', mac: '00:50:56:11:22:33', ports: [22, 443, 8443] },
];
},
async fetchLocalInfo({ setLocalIp, subnet, setSubnet }) {
setLocalIp('192.168.1.100');
if (!subnet) {
const ipParts = '192.168.1.0'.split('.');
setSubnet(`${ipParts[0]}.${ipParts[1]}.${ipParts[2]}.0/24`);
}
},
};
export default scanEffect;

View File

@ -20,7 +20,6 @@ import ConfigTool from '@/libs/config/ConfigTool';
import { api } from '@/services/api/api';
import Notification from '@/libs/system/Notification';
import Common from '@/libs/common';
import configEffect from '@/libs/script/configPage/configEffect';
const testMode = ConfigTool.load().testMode;
@ -45,6 +44,76 @@ const ConfigPage = () => {
setDevices(config.devices || []);
}, []);
const generateRealisticConfig = (command, devices = []) => {
const timestamp = new Date().toLocaleString();
let config = `! 配置生成于 ${timestamp}\n`;
const cmd = command.toLowerCase();
// VLAN
if (cmd.includes('vlan')) {
const vlanIdMatch = command.match(/vlan\s*(\d+)/i);
const vlanId = vlanIdMatch?.[1] || '10';
const isMgmt = cmd.includes('管理') || cmd.includes('mgmt');
config +=
`vlan ${vlanId}\n` +
` name ${isMgmt ? 'MGMT' : 'USER'}_VLAN\n` +
` exit\n` +
`interface Vlan${vlanId}\n` +
` description ${isMgmt ? 'Management VLAN' : 'User VLAN'}\n` +
` ip address 192.168.${vlanId}.1 255.255.255.0\n` +
` exit\n`;
}
// SSH
if (cmd.includes('ssh') || cmd.includes('安全') || cmd.includes('登录')) {
const password = Math.random().toString(36).slice(2, 10);
config +=
`ip ssh server\n` +
`ip ssh version 2\n` +
`username admin privilege 15 secret 0 ${password}\n` +
`line vty 0 4\n` +
` transport input ssh\n` +
` login local\n` +
` exit\n`;
}
//
if (cmd.includes('端口') || cmd.includes('接口') || cmd.includes('port')) {
const portMatch = command.match(/端口\s*(\d+)/i) || command.match(/port\s*(\d+)/i);
const port = portMatch?.[1] || '1';
const isTrunk = cmd.includes('trunk');
const isAccess = cmd.includes('access') || !isTrunk;
const desc = cmd.includes('上联') || cmd.includes('uplink') ? 'Uplink_Port' : 'Access_Port';
const vlanId = '10';
config +=
`interface GigabitEthernet0/${port}\n` +
` description ${desc}\n` +
` switchport mode ${isTrunk ? 'trunk' : 'access'}\n` +
` ${isTrunk ? 'switchport trunk allowed vlan all' : `switchport access vlan ${vlanId}`}\n` +
` no shutdown\n` +
` exit\n`;
}
// ACL
if (cmd.includes('acl') || cmd.includes('访问控制') || cmd.includes('防火墙')) {
let targetIP = '192.168.10.10';
if (devices.length > 0) {
const randomDevice = devices[Math.floor(Math.random() * devices.length)];
targetIP = randomDevice.ip;
}
config +=
`ip access-list extended PROTECT_SERVERS\n` +
` permit tcp any host ${targetIP} eq 22\n` +
` permit tcp any host ${targetIP} eq 80\n` +
` deny ip any any\n` +
` exit\n` +
`interface Vlan10\n` +
` ip access-group PROTECT_SERVERS in\n` +
` exit\n`;
}
if (config.trim() === `! 配置生成于 ${timestamp}`) {
config += '! 当前命令未识别到任何可配置项目\n';
}
return { config };
};
const handleParse = async () => {
if (!selectedDevice || !inputText.trim()) {
Notification.error({
@ -58,7 +127,7 @@ const ConfigPage = () => {
const performParse = async () => {
if (testMode) {
await Common.sleep(800 + Math.random() * 700);
return await configEffect.generateRealisticConfig(inputText, devices);
return generateRealisticConfig(inputText);
}
return await api.parseCommand(inputText);
};
@ -67,7 +136,7 @@ const ConfigPage = () => {
promise: performParse(),
loading: {
title: '正在解析配置',
description: '正在分析您的指令..',
description: '正在分析您的指令...',
},
success: {
title: '解析完成',

View File

@ -43,14 +43,13 @@ const DevicesPage = () => {
const handleSave = (idx) => {
const updated = [...devices];
updated[idx].name = editingName;
Common.sleep(200).then(() => {
setDevices(updated);
ConfigTool.save({ ...ConfigTool.load(), devices: updated });
Notification.success({
title: '设备重命名成功!',
});
setEditingIndex(null);
Common.sleep(200);
setDevices(updated);
ConfigTool.save({ ...ConfigTool.load(), devices: updated });
Notification.success({
title: '设备重命名成功!',
});
setEditingIndex(null);
};
return (

View File

@ -9,8 +9,8 @@ import {
Spinner,
Badge,
HStack,
Table,
} from '@chakra-ui/react';
import { Table } from '@chakra-ui/react';
import DocumentTitle from '@/components/system/pages/DocumentTitle';
import PageContainer from '@/components/system/PageContainer';
import DashboardBackground from '@/components/system/pages/DashboardBackground';
@ -19,7 +19,6 @@ import { api } from '@/services/api/api';
import ConfigTool from '@/libs/config/ConfigTool';
import Common from '@/libs/common';
import Notification from '@/libs/system/Notification';
import scanEffect from '@/libs/script/scanPage/scanEffect';
/**
* 网络扫描页面
@ -36,22 +35,36 @@ const ScanPage = () => {
const testMode = config.testMode;
useEffect(() => {
scanEffect
.fetchLocalInfo({
setLocalIp: setLocalIp,
setSubnet: setSubnet,
subnet: subnet,
})
.then();
const fetchLocalInfo = async () => {
setLocalIp('192.168.1.100');
if (!subnet) {
const ipParts = '192.168.1.0'.split('.');
setSubnet(`${ipParts[0]}.${ipParts[1]}.${ipParts[2]}.0/24`);
}
};
fetchLocalInfo();
}, [subnet, Notification]);
const getTestDevices = () => {
return [
{ ip: '192.168.1.1', mac: '00:1A:2B:3C:4D:5E', ports: [22, 23, 161] },
{ ip: '192.168.1.2', mac: '00:1D:7D:AA:1B:01', ports: [22, 23, 161] },
{ ip: '192.168.1.3', mac: '00:0C:29:5A:3B:11', ports: [22, 23, 161, 443] },
{ ip: '192.168.1.4', mac: '00:23:CD:FF:10:02', ports: [22, 23] },
{ ip: '192.168.1.5', mac: '00:04:4B:AA:BB:CC', ports: [22, 23, 80, 443] },
{ ip: '192.168.1.6', mac: '00:11:22:33:44:55', ports: [22, 23, 161] },
{ ip: '192.168.1.7', mac: '00:1F:45:67:89:AB', ports: [23] },
{ ip: '192.168.1.8', mac: '00:50:56:11:22:33', ports: [22, 443, 8443] },
];
};
const handleScan = async () => {
setLoading(true);
try {
let scanDevices;
if (testMode) {
await Common.sleep(2000);
scanDevices = scanEffect.getTestDevices();
scanDevices = getTestDevices();
Notification.success({
title: '扫描子网设备成功!',
});
@ -91,7 +104,7 @@ const ScanPage = () => {
let scanDevices;
if (testMode) {
await Common.sleep(500);
scanDevices = scanEffect.getTestDevices();
scanDevices = getTestDevices();
Notification.success({
title: '获取上一次扫描记录成功!',
});
@ -160,7 +173,7 @@ const ScanPage = () => {
{loading && (
<HStack>
<Spinner />
<Text>{'正在加载,请稍候..'}</Text>
<Text>{'正在加载,请稍候...'}</Text>
</HStack>
)}
@ -212,7 +225,7 @@ const ScanPage = () => {
)}
{!loading && devices.length === 0 && (
<Text color={'gray.400'}>{'暂无扫描结果,请执行扫描..'}</Text>
<Text color={'gray.400'}>{'暂无扫描结果,请执行扫描'}</Text>
)}
</Box>
</VStack>