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 记录
109 lines
3.6 KiB
Python
109 lines
3.6 KiB
Python
from fastapi import APIRouter, Depends, HTTPException
|
|
from sqlalchemy.orm import Session
|
|
from typing import List, Dict, Any
|
|
|
|
from app.core.database import get_db
|
|
from app.core.security import get_current_user
|
|
from app.schemas.network import ScanTask, ScanTaskListResponse, ScanResult
|
|
from app.models.network import TaskType, TaskStatus
|
|
from app.services.scan_service import ScanService
|
|
from app.services.network_service import NetworkService
|
|
|
|
router = APIRouter(
|
|
prefix="/scan",
|
|
tags=["扫描管理"],
|
|
dependencies=[Depends(get_current_user)],
|
|
)
|
|
|
|
|
|
@router.get("/tasks", response_model=ScanTaskListResponse, summary="获取扫描任务列表")
|
|
def get_scan_tasks(
|
|
skip: int = 0,
|
|
limit: int = 50,
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""获取所有扫描任务"""
|
|
total, items = ScanService.get_task_list(db, skip=skip, limit=limit)
|
|
return {"total": total, "items": items}
|
|
|
|
|
|
@router.get("/tasks/{task_id}", response_model=ScanTask, summary="获取扫描任务状态")
|
|
def get_scan_task(task_id: int, db: Session = Depends(get_db)):
|
|
"""获取指定扫描任务的状态"""
|
|
task = ScanService.get_task_by_id(db, task_id)
|
|
if not task:
|
|
raise HTTPException(status_code=404, detail="任务不存在")
|
|
return task
|
|
|
|
|
|
@router.post("/ping/{network_id}", response_model=ScanTask, status_code=201, summary="触发网段Ping扫描")
|
|
def trigger_ping_scan(network_id: int, db: Session = Depends(get_db)):
|
|
"""对指定网段触发ICMP Ping扫描"""
|
|
network = NetworkService.get_by_id(db, network_id)
|
|
if not network:
|
|
raise HTTPException(status_code=404, detail="网段不存在")
|
|
|
|
# 创建扫描任务
|
|
task = ScanService.create_scan_task(db, TaskType.PING, network_id=network_id)
|
|
|
|
# 立即执行扫描(简单版,后续用Celery)
|
|
try:
|
|
ScanService.update_task_status(db, task.id, TaskStatus.RUNNING, progress=0)
|
|
|
|
# 执行扫描
|
|
results = ScanService.ping_network(network.cidr)
|
|
|
|
# 更新IP状态
|
|
online_count, offline_count = ScanService.update_ip_status_from_scan_results(db, results)
|
|
|
|
# 刷新网段统计字段(used_ips/reserved_ips),保证网段列表利用率能即时反映
|
|
NetworkService.get_stats(db, network_id)
|
|
|
|
# 更新任务状态
|
|
ScanService.update_task_status(
|
|
db,
|
|
task.id,
|
|
TaskStatus.COMPLETED,
|
|
progress=100,
|
|
total_count=len(results),
|
|
success_count=online_count,
|
|
failed_count=offline_count
|
|
)
|
|
|
|
except Exception as e:
|
|
ScanService.update_task_status(
|
|
db,
|
|
task.id,
|
|
TaskStatus.FAILED,
|
|
error_message=str(e)
|
|
)
|
|
raise HTTPException(status_code=500, detail=f"扫描失败: {str(e)}")
|
|
|
|
# 重新获取任务状态
|
|
task = ScanService.get_task_by_id(db, task.id)
|
|
return task
|
|
|
|
|
|
@router.post("/ping/single/{ip_address}", response_model=ScanResult, summary="单个IP Ping测试")
|
|
def ping_single_ip(ip_address: str):
|
|
"""对单个IP执行Ping测试"""
|
|
result = ScanService.ping_single_ip(ip_address)
|
|
return {
|
|
"ip_address": result["ip_address"],
|
|
"status": result["status"],
|
|
"mac_address": None,
|
|
"hostname": None,
|
|
"response_time": result.get("response_time")
|
|
}
|
|
|
|
|
|
@router.post("/refresh/stats", summary="刷新所有网段统计")
|
|
def refresh_all_stats(db: Session = Depends(get_db)):
|
|
"""刷新所有网段的统计数据"""
|
|
stats = NetworkService.get_all_stats(db)
|
|
return {
|
|
"message": "统计更新完成",
|
|
"networks_updated": len(stats),
|
|
"stats": stats
|
|
}
|