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 记录
120 lines
3.9 KiB
Python
120 lines
3.9 KiB
Python
from sqlalchemy import Column, Integer, String, Text, DateTime, Enum, ForeignKey, JSON, Boolean
|
|
from sqlalchemy.orm import relationship
|
|
from sqlalchemy.sql import func
|
|
from app.core.database import Base
|
|
import enum
|
|
|
|
|
|
class NetworkGroup(str, enum.Enum):
|
|
"""网段分组枚举"""
|
|
PRODUCTION = "production"
|
|
OFFICE = "office"
|
|
TEST = "test"
|
|
MANAGEMENT = "management"
|
|
OTHER = "other"
|
|
|
|
|
|
class IPStatus(str, enum.Enum):
|
|
"""IP状态枚举"""
|
|
AVAILABLE = "available"
|
|
ONLINE = "online"
|
|
OFFLINE = "offline"
|
|
RESERVED = "reserved"
|
|
|
|
|
|
class TaskStatus(str, enum.Enum):
|
|
"""任务状态枚举"""
|
|
PENDING = "pending"
|
|
RUNNING = "running"
|
|
COMPLETED = "completed"
|
|
FAILED = "failed"
|
|
|
|
|
|
class TaskType(str, enum.Enum):
|
|
"""任务类型枚举"""
|
|
PING = "ping"
|
|
ARP = "arp"
|
|
SNMP = "snmp"
|
|
|
|
|
|
class Network(Base):
|
|
"""网段模型"""
|
|
__tablename__ = "networks"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
cidr = Column(String(50), unique=True, index=True, nullable=False) # 如: 192.168.1.0/24
|
|
name = Column(String(100), index=True)
|
|
description = Column(Text, nullable=True)
|
|
group_name = Column(String(100), index=True, nullable=True) # 分组名称
|
|
gateway = Column(String(50), nullable=True)
|
|
vlan_id = Column(Integer, nullable=True)
|
|
total_ips = Column(Integer, default=0)
|
|
used_ips = Column(Integer, default=0)
|
|
reserved_ips = Column(Integer, default=0)
|
|
|
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
|
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
|
|
|
# 关系
|
|
ip_addresses = relationship("IPAddress", back_populates="network", cascade="all, delete-orphan")
|
|
scan_tasks = relationship("ScanTask", back_populates="network")
|
|
|
|
|
|
class IPAddress(Base):
|
|
"""IP地址模型"""
|
|
__tablename__ = "ip_addresses"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
network_id = Column(Integer, ForeignKey("networks.id"), nullable=False)
|
|
ip_address = Column(String(50), unique=True, index=True, nullable=False)
|
|
mac_address = Column(String(50), nullable=True, index=True)
|
|
hostname = Column(String(255), nullable=True)
|
|
status = Column(Enum(IPStatus), default=IPStatus.AVAILABLE)
|
|
|
|
# 业务属性
|
|
owner = Column(String(100), nullable=True)
|
|
business_type = Column(String(50), nullable=True)
|
|
notes = Column(Text, nullable=True)
|
|
custom_fields = Column(JSON, default=dict)
|
|
|
|
# 扫描信息
|
|
last_seen = Column(DateTime(timezone=True), nullable=True)
|
|
first_seen = Column(DateTime(timezone=True), server_default=func.now())
|
|
|
|
# 物理位置信息
|
|
switch_name = Column(String(100), nullable=True)
|
|
switch_port = Column(String(50), nullable=True)
|
|
vendor = Column(String(100), nullable=True) # MAC OUI 解析的厂商
|
|
|
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
|
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
|
|
|
# 关系
|
|
network = relationship("Network", back_populates="ip_addresses")
|
|
|
|
|
|
class ScanTask(Base):
|
|
"""扫描任务模型"""
|
|
__tablename__ = "scan_tasks"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
network_id = Column(Integer, ForeignKey("networks.id"), nullable=True)
|
|
task_type = Column(Enum(TaskType), nullable=False)
|
|
status = Column(Enum(TaskStatus), default=TaskStatus.PENDING)
|
|
progress = Column(Integer, default=0) # 0-100
|
|
celery_task_id = Column(String(100), nullable=True)
|
|
|
|
# 统计信息
|
|
total_count = Column(Integer, default=0)
|
|
success_count = Column(Integer, default=0)
|
|
failed_count = Column(Integer, default=0)
|
|
|
|
started_at = Column(DateTime(timezone=True), nullable=True)
|
|
completed_at = Column(DateTime(timezone=True), nullable=True)
|
|
error_message = Column(Text, nullable=True)
|
|
|
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
|
|
|
# 关系
|
|
network = relationship("Network", back_populates="scan_tasks")
|