Files
xingrin/backend/apps/engine/models.py

129 lines
5.2 KiB
Python
Raw Normal View History

2025-12-12 18:04:57 +08:00
from django.db import models
class WorkerNode(models.Model):
"""Worker 节点模型 - 分布式扫描执行器"""
# 状态选项(前后端统一)
STATUS_CHOICES = [
('pending', '待部署'),
('deploying', '部署中'),
('online', '在线'),
('offline', '离线'),
2025-12-19 20:07:55 +08:00
('updating', '更新中'),
('outdated', '版本过低'),
2025-12-12 18:04:57 +08:00
]
name = models.CharField(max_length=100, help_text='节点名称')
# 本地节点会自动填入 127.0.0.1 或容器 IP
ip_address = models.GenericIPAddressField(help_text='IP 地址(本地节点为 127.0.0.1')
ssh_port = models.IntegerField(default=22, help_text='SSH 端口')
username = models.CharField(max_length=50, default='root', help_text='SSH 用户名')
password = models.CharField(max_length=200, blank=True, default='', help_text='SSH 密码')
# 本地节点标记Docker 容器内的 Worker
is_local = models.BooleanField(default=False, help_text='是否为本地节点Docker 容器内)')
# 状态(前后端统一)
status = models.CharField(
max_length=20,
choices=STATUS_CHOICES,
default='pending',
help_text='状态: pending/deploying/online/offline'
)
# 心跳数据存储在 Redisworker:load:{id}),不再使用数据库字段
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'worker_node'
verbose_name = 'Worker 节点'
ordering = ['-created_at']
constraints = [
# 远程节点 IP 唯一(本地节点不限制,因为都是 127.0.0.1
models.UniqueConstraint(
fields=['ip_address'],
condition=models.Q(is_local=False),
name='unique_remote_worker_ip'
),
# 名称全局唯一
models.UniqueConstraint(
fields=['name'],
name='unique_worker_name'
),
]
def __str__(self):
if self.is_local:
return f"{self.name} (本地)"
return f"{self.name} ({self.ip_address or '未知'})"
class ScanEngine(models.Model):
"""扫描引擎模型"""
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=200, unique=True, help_text='引擎名称')
configuration = models.CharField(max_length=10000, blank=True, default='', help_text='引擎配置yaml 格式')
created_at = models.DateTimeField(auto_now_add=True, help_text='创建时间')
updated_at = models.DateTimeField(auto_now=True, help_text='更新时间')
class Meta:
db_table = 'scan_engine'
verbose_name = '扫描引擎'
verbose_name_plural = '扫描引擎'
ordering = ['-created_at']
indexes = [
models.Index(fields=['-created_at']),
]
def __str__(self):
return str(self.name or f'ScanEngine {self.id}')
class Wordlist(models.Model):
"""字典文件模型"""
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=200, unique=True, help_text='字典名称,唯一')
description = models.CharField(max_length=200, blank=True, default='', help_text='字典描述')
file_path = models.CharField(max_length=500, help_text='后端保存的字典文件绝对路径')
file_size = models.BigIntegerField(default=0, help_text='文件大小(字节)')
line_count = models.IntegerField(default=0, help_text='字典行数')
file_hash = models.CharField(max_length=64, blank=True, default='', help_text='文件 SHA-256 哈希,用于缓存校验')
created_at = models.DateTimeField(auto_now_add=True, help_text='创建时间')
updated_at = models.DateTimeField(auto_now=True, help_text='更新时间')
class Meta:
db_table = 'wordlist'
verbose_name = '字典文件'
verbose_name_plural = '字典文件'
ordering = ['-created_at']
indexes = [
models.Index(fields=['-created_at']),
]
def __str__(self) -> str:
return self.name
class NucleiTemplateRepo(models.Model):
"""Nuclei 模板 Git 仓库模型(多仓库)"""
name = models.CharField(max_length=200, unique=True, help_text="仓库名称,用于前端展示和配置引用")
repo_url = models.CharField(max_length=500, help_text="Git 仓库地址")
local_path = models.CharField(max_length=500, blank=True, default='', help_text="本地工作目录绝对路径")
commit_hash = models.CharField(max_length=40, blank=True, default='', help_text="最后同步的 Git commit hash用于 Worker 版本校验")
last_synced_at = models.DateTimeField(null=True, blank=True, help_text="最后一次成功同步时间")
created_at = models.DateTimeField(auto_now_add=True, help_text="创建时间")
updated_at = models.DateTimeField(auto_now=True, help_text="更新时间")
class Meta:
db_table = "nuclei_template_repo"
verbose_name = "Nuclei 模板仓库"
verbose_name_plural = "Nuclei 模板仓库"
def __str__(self) -> str: # pragma: no cover - 简单表示
return f"NucleiTemplateRepo({self.id}, {self.name})"