1e1e5957e8
- Ips.vue: el-select 添加 width: 280px,修复网段下拉框过窄 - Ips.vue: query 参数 networkId → network_id(与 FastAPI snake_case 一致) - Scan.vue: el-select 用 network.id 代替整个 network 对象作为 value,修复切换网段不生效 - enhanced_scan_service.py: 替换失效的 scapy ARP 扫描为 arp-scan 命令;扫描结果自动创建缺失 IP 记录
174 lines
4.1 KiB
Python
174 lines
4.1 KiB
Python
from pydantic import BaseModel, Field, field_validator
|
|
from typing import Optional, List, Dict, Any
|
|
from datetime import datetime
|
|
import ipaddress
|
|
from app.models.network import IPStatus, TaskStatus, TaskType
|
|
|
|
|
|
# ========== 网段相关 Schemas ==========
|
|
|
|
class NetworkBase(BaseModel):
|
|
cidr: str = Field(..., description="网段CIDR,如: 192.168.1.0/24")
|
|
name: Optional[str] = Field(None, max_length=100, description="网段名称")
|
|
description: Optional[str] = Field(None, description="描述")
|
|
group_name: Optional[str] = Field(None, max_length=100, description="分组名称")
|
|
gateway: Optional[str] = Field(None, max_length=50, description="网关地址")
|
|
vlan_id: Optional[int] = Field(None, description="VLAN ID")
|
|
|
|
@field_validator('cidr')
|
|
@classmethod
|
|
def validate_cidr(cls, v):
|
|
try:
|
|
network = ipaddress.ip_network(v, strict=False)
|
|
return str(network)
|
|
except ValueError:
|
|
raise ValueError(f"无效的CIDR格式: {v}")
|
|
|
|
@field_validator('gateway')
|
|
@classmethod
|
|
def validate_gateway(cls, v):
|
|
if v:
|
|
try:
|
|
ipaddress.ip_address(v)
|
|
except ValueError:
|
|
raise ValueError(f"无效的IP地址: {v}")
|
|
return v
|
|
|
|
|
|
class NetworkCreate(NetworkBase):
|
|
pass
|
|
|
|
|
|
class NetworkUpdate(BaseModel):
|
|
name: Optional[str] = None
|
|
description: Optional[str] = None
|
|
group_name: Optional[str] = None
|
|
gateway: Optional[str] = None
|
|
vlan_id: Optional[int] = None
|
|
|
|
|
|
class Network(NetworkBase):
|
|
id: int
|
|
total_ips: int
|
|
used_ips: int
|
|
reserved_ips: int
|
|
created_at: datetime
|
|
updated_at: Optional[datetime]
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class NetworkStats(BaseModel):
|
|
"""网段统计信息"""
|
|
id: int
|
|
cidr: str
|
|
name: Optional[str]
|
|
total_ips: int
|
|
used_ips: int
|
|
reserved_ips: int
|
|
available_ips: int
|
|
usage_percent: float
|
|
group_name: Optional[str]
|
|
vlan_id: Optional[int]
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class NetworkListResponse(BaseModel):
|
|
total: int
|
|
items: List[Network]
|
|
|
|
|
|
# ========== IP地址相关 Schemas ==========
|
|
|
|
class IPAddressBase(BaseModel):
|
|
ip_address: str
|
|
mac_address: Optional[str] = None
|
|
hostname: Optional[str] = None
|
|
owner: Optional[str] = None
|
|
business_type: Optional[str] = None
|
|
notes: Optional[str] = None
|
|
custom_fields: Dict[str, Any] = Field(default_factory=dict)
|
|
switch_name: Optional[str] = None
|
|
switch_port: Optional[str] = None
|
|
vendor: Optional[str] = None
|
|
|
|
|
|
class IPAddressCreate(IPAddressBase):
|
|
network_id: int
|
|
status: IPStatus = IPStatus.AVAILABLE
|
|
|
|
|
|
class IPAddressUpdate(BaseModel):
|
|
mac_address: Optional[str] = None
|
|
hostname: Optional[str] = None
|
|
status: Optional[IPStatus] = None
|
|
owner: Optional[str] = None
|
|
business_type: Optional[str] = None
|
|
notes: Optional[str] = None
|
|
custom_fields: Optional[Dict[str, Any]] = None
|
|
|
|
|
|
class IPAddress(IPAddressBase):
|
|
id: int
|
|
network_id: int
|
|
status: IPStatus
|
|
last_seen: Optional[datetime]
|
|
first_seen: datetime
|
|
created_at: datetime
|
|
updated_at: Optional[datetime]
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class IPAddressListResponse(BaseModel):
|
|
total: int
|
|
items: List[IPAddress]
|
|
|
|
|
|
# ========== 扫描任务相关 Schemas ==========
|
|
|
|
class ScanTaskBase(BaseModel):
|
|
task_type: TaskType
|
|
network_id: Optional[int] = None
|
|
|
|
|
|
class ScanTaskCreate(ScanTaskBase):
|
|
pass
|
|
|
|
|
|
class ScanTask(BaseModel):
|
|
id: int
|
|
network_id: Optional[int]
|
|
task_type: TaskType
|
|
status: TaskStatus
|
|
progress: int
|
|
celery_task_id: Optional[str]
|
|
total_count: int
|
|
success_count: int
|
|
failed_count: int
|
|
started_at: Optional[datetime]
|
|
completed_at: Optional[datetime]
|
|
error_message: Optional[str]
|
|
created_at: datetime
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class ScanTaskListResponse(BaseModel):
|
|
total: int
|
|
items: List[ScanTask]
|
|
|
|
|
|
class ScanResult(BaseModel):
|
|
"""单次扫描结果"""
|
|
ip_address: str
|
|
status: str
|
|
mac_address: Optional[str] = None
|
|
hostname: Optional[str] = None
|
|
response_time: Optional[float] = None
|