Compare commits

..

3 Commits

Author SHA1 Message Date
0b6b9624a6 乱七八糟的 2025-08-12 00:03:16 +08:00
0f8e7fe4d6 Merge remote-tracking branch 'origin/main' 2025-08-09 19:58:10 +08:00
bccdde74ad feat:telnet发送配置命令 2025-07-18 21:10:40 +08:00
17 changed files with 3870 additions and 4745 deletions

View File

@ -82,7 +82,7 @@ async def test_endpoint():
@router.get("/scan_network", summary="扫描网络中的交换机")
async def scan_network(subnet: str = "192.168.1.0/24"):
try:
devices = scanner.scan_subnet(subnet)
devices = await scanner.scan_subnet(subnet)
return {
"success": True,
"devices": devices,
@ -94,7 +94,7 @@ async def scan_network(subnet: str = "192.168.1.0/24"):
@router.get("/list_devices", summary="列出已发现的交换机")
async def list_devices():
return {
"devices": scanner.load_cached_devices()
"devices": await scanner.load_cached_devices()
}
class CommandRequest(BaseModel):
@ -125,7 +125,7 @@ async def parse_command(request: CommandRequest):
@router.post("/apply_config", response_model=dict)
async def apply_config(request: ConfigRequest):
"""
应用配置到交换机
应用配置到交换机弃用
"""
try:
configurator = SwitchConfigurator(

View File

@ -110,6 +110,9 @@ class SwitchConfigurator:
output = ""
for cmd in commands:
if cmd.startswith("!"):
logger.debug(f"跳过特殊命令: {cmd}")
continue
logger.info(f"发送命令: {cmd}")
writer.write(f"{cmd}\n")
await writer.drain()

View File

@ -42,7 +42,7 @@ class AIService:
]
try:
response = self.client.chat.completions.create(
response = await self.client.chat.completions.create(
model="deepseek-ai/DeepSeek-V3",
messages=messages,
temperature=0.3,

View File

@ -9,7 +9,7 @@ class NetworkScanner:
self.cache_path = Path(cache_path)
self.nm = nmap.PortScanner()
def scan_subnet(self, subnet: str = "192.168.1.0/24", ports: List[int] = [22, 23, 80]) -> List[Dict]:
async def scan_subnet(self, subnet: str = "192.168.1.0/24", ports: List[int] = [22, 23, 80]) -> List[Dict]:
"""扫描指定子网的设备,获取设备信息和开放端口"""
logger.info(f"Scanning subnet: {subnet}")
@ -33,16 +33,16 @@ class NetworkScanner:
except Exception as e:
logger.error(f"Error while scanning subnet: {e}")
self._save_to_cache(devices)
await self._save_to_cache(devices)
return devices
def _save_to_cache(self, devices: List[Dict]):
async def _save_to_cache(self, devices: List[Dict]):
"""保存扫描结果到本地文件"""
with open(self.cache_path, "w") as f:
json.dump(devices, f, indent=2)
logger.info(f"Saved {len(devices)} devices to cache")
def load_cached_devices(self) -> List[Dict]:
async def load_cached_devices(self) -> List[Dict]:
"""从缓存加载设备列表"""
if not self.cache_path.exists():
return []

View File

@ -21,6 +21,7 @@
"@nestjs/platform-express": "^11.0.1",
"@nestjs/swagger": "^11.2.0",
"axios": "^1.10.0",
"ip": "^2.0.1",
"reflect-metadata": "^0.2.2",
"rxjs": "^7.8.1",
"ssh2": "^1.16.0"
@ -34,6 +35,7 @@
"@swc/cli": "^0.6.0",
"@swc/core": "^1.10.7",
"@types/express": "^5.0.0",
"@types/ip": "^1.1.3",
"@types/jest": "^29.5.14",
"@types/node": "^22.16.4",
"@types/supertest": "^6.0.2",

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,13 @@
import { Module } from '@nestjs/common';
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { RootModule } from './root/root.module';
import { HttpMiddleware } from './common/middleware/http.middleware';
import { NetworkModule } from './modules/network/network.module';
@Module({
imports: [RootModule],
imports: [RootModule, NetworkModule],
})
export class AppModule {}
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer): any {
consumer.apply(HttpMiddleware).forRoutes('api');
}
}

View File

@ -1,28 +0,0 @@
import {
ArgumentsHost,
Catch,
ExceptionFilter,
HttpException,
HttpStatus,
} from '@nestjs/common';
@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const status =
exception instanceof HttpException
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
const message =
exception instanceof HttpException ? exception.message : '服务器内部错误';
response.status(status).json({
success: false,
data: null,
message,
});
}
}

View File

@ -19,7 +19,6 @@ export class ResponseInterceptor<T>
map((data) => ({
success: true,
data,
message: '操作成功',
})),
);
}

View File

@ -0,0 +1,25 @@
import { Injectable, Logger, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Injectable()
export class HttpMiddleware implements NestMiddleware {
private readonly logger = new Logger('HTTP');
use(req: Request, res: Response, next: NextFunction): void {
const { method, originalUrl } = req;
this.logger.log(`HTTP request for ${method} ${originalUrl}`);
const startTime = Date.now();
res.on('finish', () => {
const statusCode = res.statusCode;
const contentLength = res.get('content-length') || 0;
const duration = Date.now() - startTime;
this.logger.log(
`${method} ${originalUrl} ${statusCode} ${contentLength}B - ${duration}ms`,
);
});
next();
}
}

View File

@ -1,5 +1,4 @@
export interface ApiResponse<T = any> {
success: boolean;
data: T;
message: string;
}

View File

@ -3,13 +3,11 @@ import { AppModule } from './app.module';
import { Logger } from '@nestjs/common';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { ResponseInterceptor } from './common/interceptors/response.interceptor';
import { AllExceptionsFilter } from './common/filters/all-exception.filter';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.setGlobalPrefix('api');
app.useGlobalInterceptors(new ResponseInterceptor());
app.useGlobalFilters(new AllExceptionsFilter());
const config = new DocumentBuilder()
.setTitle('交换机API平台')
.setDescription('自动化交换机配置和流量监控接口')

View File

@ -0,0 +1,18 @@
import { Controller, Get, Inject } from '@nestjs/common';
import { NetworkService } from './network.service';
import { ApiTags, ApiOperation } from '@nestjs/swagger';
@ApiTags('Network')
@Controller('network')
export class NetworkController {
constructor(
@Inject(NetworkService)
private readonly networkService: NetworkService,
) {}
@Get('adapters')
@ApiOperation({ summary: '获取网络适配器网段' })
async getNetworkAdapters() {
return this.networkService.getNetworkAdapters();
}
}

View File

@ -0,0 +1,10 @@
import { Module } from '@nestjs/common';
import { NetworkService } from './network.service';
import { NetworkController } from './network.controller';
@Module({
controllers: [NetworkController],
providers: [NetworkService],
exports: [NetworkService],
})
export class NetworkModule {}

View File

@ -0,0 +1,44 @@
import { Injectable, Logger } from '@nestjs/common';
import os from 'os';
import ip from 'ip';
@Injectable()
export class NetworkService {
private readonly logger = new Logger(NetworkService.name);
/**
*
*/
async getNetworkAdapters(): Promise<any> {
try {
this.logger.log('Getting network adapters');
const interfaces = os.networkInterfaces();
const networks: any[] = [];
for (const [adapter, addrs] of Object.entries(interfaces)) {
if (!addrs) continue;
for (const addr of addrs) {
if (addr.family === 'IPv4' && !addr.internal) {
const ipAddress = addr.address;
const subnetMask = addr.netmask;
const cidr = ip.subnet(ipAddress, subnetMask);
const networkCidr = `${cidr.networkAddress}/${cidr.subnetMaskLength}`;
networks.push({
adapter,
network: networkCidr,
ip: ipAddress,
subnet_mask: subnetMask,
});
}
}
}
return { networks };
} catch (error) {
this.logger.error('获取网络适配器信息失败', error);
return { error: `获取网络适配器信息失败: ${error.message}` };
}
}
}

View File

@ -3,7 +3,7 @@ import { Controller, Get } from '@nestjs/common';
@Controller()
export class RootController {
@Get()
getWelcome() {
async getWelcome() {
return {
message: '欢迎使用交换机管理平台 API',
};

View File

@ -1,5 +1,6 @@
import { Module } from '@nestjs/common';
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { RootController } from './root.controller';
import { HttpMiddleware } from '../common/middleware/http.middleware';
@Module({
controllers: [RootController],