mirror of
https://github.com/yyhuni/xingrin.git
synced 2026-01-31 11:46:16 +08:00
- Split monolithic models.py into separate model files (scan_models.py, scan_log_model.py, scheduled_scan_model.py, subfinder_provider_settings_model.py) - Split monolithic serializers.py into separate serializer files with dedicated modules for each domain - Add SubfinderProviderSettings model to store API key configurations for subfinder data sources - Create SubfinderProviderConfigService to generate provider configuration files dynamically - Add subfinder_provider_settings views and serializers for API key management - Update subdomain_discovery_flow to support provider configuration file generation and passing to subfinder - Update command templates to use provider config file and remove recursive flag for better source coverage - Add frontend settings page for managing API keys at /settings/api-keys - Add frontend hooks and services for API key settings management - Update sidebar navigation to include API keys settings link - Add internationalization support for new API keys settings UI (English and Chinese) - Improves code maintainability by organizing related models and serializers into logical modules
107 lines
4.2 KiB
Python
107 lines
4.2 KiB
Python
"""扫描相关模型"""
|
||
|
||
from django.db import models
|
||
from django.contrib.postgres.fields import ArrayField
|
||
|
||
from apps.common.definitions import ScanStatus
|
||
|
||
|
||
class SoftDeleteManager(models.Manager):
|
||
"""软删除管理器:默认只返回未删除的记录"""
|
||
|
||
def get_queryset(self):
|
||
return super().get_queryset().filter(deleted_at__isnull=True)
|
||
|
||
|
||
class Scan(models.Model):
|
||
"""扫描任务模型"""
|
||
|
||
id = models.AutoField(primary_key=True)
|
||
|
||
target = models.ForeignKey('targets.Target', on_delete=models.CASCADE, related_name='scans', help_text='扫描目标')
|
||
|
||
# 多引擎支持字段
|
||
engine_ids = ArrayField(
|
||
models.IntegerField(),
|
||
default=list,
|
||
help_text='引擎 ID 列表'
|
||
)
|
||
engine_names = models.JSONField(
|
||
default=list,
|
||
help_text='引擎名称列表,如 ["引擎A", "引擎B"]'
|
||
)
|
||
yaml_configuration = models.TextField(
|
||
default='',
|
||
help_text='YAML 格式的扫描配置'
|
||
)
|
||
|
||
created_at = models.DateTimeField(auto_now_add=True, help_text='任务创建时间')
|
||
stopped_at = models.DateTimeField(null=True, blank=True, help_text='扫描结束时间')
|
||
|
||
status = models.CharField(
|
||
max_length=20,
|
||
choices=ScanStatus.choices,
|
||
default=ScanStatus.INITIATED,
|
||
db_index=True,
|
||
help_text='任务状态'
|
||
)
|
||
|
||
results_dir = models.CharField(max_length=100, blank=True, default='', help_text='结果存储目录')
|
||
|
||
container_ids = ArrayField(
|
||
models.CharField(max_length=100),
|
||
blank=True,
|
||
default=list,
|
||
help_text='容器 ID 列表(Docker Container ID)'
|
||
)
|
||
|
||
worker = models.ForeignKey(
|
||
'engine.WorkerNode',
|
||
on_delete=models.SET_NULL,
|
||
related_name='scans',
|
||
null=True,
|
||
blank=True,
|
||
help_text='执行扫描的 Worker 节点'
|
||
)
|
||
|
||
error_message = models.CharField(max_length=2000, blank=True, default='', help_text='错误信息')
|
||
|
||
# ==================== 软删除字段 ====================
|
||
deleted_at = models.DateTimeField(null=True, blank=True, db_index=True, help_text='删除时间(NULL表示未删除)')
|
||
|
||
# ==================== 管理器 ====================
|
||
objects = SoftDeleteManager() # 默认管理器:只返回未删除的记录
|
||
all_objects = models.Manager() # 全量管理器:包括已删除的记录(用于硬删除)
|
||
|
||
# ==================== 进度跟踪字段 ====================
|
||
progress = models.IntegerField(default=0, help_text='扫描进度 0-100')
|
||
current_stage = models.CharField(max_length=50, blank=True, default='', help_text='当前扫描阶段')
|
||
stage_progress = models.JSONField(default=dict, help_text='各阶段进度详情')
|
||
|
||
# ==================== 缓存统计字段 ====================
|
||
cached_subdomains_count = models.IntegerField(default=0, help_text='缓存的子域名数量')
|
||
cached_websites_count = models.IntegerField(default=0, help_text='缓存的网站数量')
|
||
cached_endpoints_count = models.IntegerField(default=0, help_text='缓存的端点数量')
|
||
cached_ips_count = models.IntegerField(default=0, help_text='缓存的IP地址数量')
|
||
cached_directories_count = models.IntegerField(default=0, help_text='缓存的目录数量')
|
||
cached_vulns_total = models.IntegerField(default=0, help_text='缓存的漏洞总数')
|
||
cached_vulns_critical = models.IntegerField(default=0, help_text='缓存的严重漏洞数量')
|
||
cached_vulns_high = models.IntegerField(default=0, help_text='缓存的高危漏洞数量')
|
||
cached_vulns_medium = models.IntegerField(default=0, help_text='缓存的中危漏洞数量')
|
||
cached_vulns_low = models.IntegerField(default=0, help_text='缓存的低危漏洞数量')
|
||
stats_updated_at = models.DateTimeField(null=True, blank=True, help_text='统计数据最后更新时间')
|
||
|
||
class Meta:
|
||
db_table = 'scan'
|
||
verbose_name = '扫描任务'
|
||
verbose_name_plural = '扫描任务'
|
||
ordering = ['-created_at']
|
||
indexes = [
|
||
models.Index(fields=['-created_at']),
|
||
models.Index(fields=['target']),
|
||
models.Index(fields=['deleted_at', '-created_at']),
|
||
]
|
||
|
||
def __str__(self):
|
||
return f"Scan #{self.id} - {self.target.name}"
|