from django.db import models from .fields import EncryptedCharField class ScanProfile(models.Model): INTERVAL_CHOICES = [ (1, 'Every 1 day'), (3, 'Every 3 days'), (7, 'Every 7 days'), (30, 'Every 30 days'), ] SEVERITY_CHOICES = [ ('warning', 'Warning and above'), ('critical', 'Critical only'), ] name = models.CharField(max_length=200) enabled = models.BooleanField(default=True) schedule_interval = models.IntegerField(choices=INTERVAL_CHOICES, default=7) gateway_ip = models.GenericIPAddressField(help_text='e.g. 192.168.1.1') public_ip = models.GenericIPAddressField(help_text='Your public/WAN IP address') network_cidr = models.CharField(max_length=50, blank=True, help_text='e.g. 192.168.1.0/24') auth_provider_host = models.CharField(max_length=255, blank=True, help_text='e.g. pass.junv.cc') domains = models.JSONField(default=list, blank=True, help_text='List of public hostnames to check') cameras = models.JSONField(default=list, blank=True, help_text='List of camera IPs to probe') telegram_bot_token = EncryptedCharField(blank=True) telegram_chat_id = EncryptedCharField(blank=True) notify_on_severity = models.CharField(max_length=20, choices=SEVERITY_CHOICES, default='critical') last_run_at = models.DateTimeField(null=True, blank=True) created_at = models.DateTimeField(auto_now_add=True) def __str__(self): return self.name class ScanRun(models.Model): STATUS_CHOICES = [ ('pending', 'Pending'), ('running', 'Running'), ('success', 'Success'), ('failed', 'Failed'), ] profile = models.ForeignKey(ScanProfile, on_delete=models.CASCADE, related_name='runs') started_at = models.DateTimeField(auto_now_add=True) finished_at = models.DateTimeField(null=True, blank=True) status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending') summary = models.JSONField(default=dict) triggered_by = models.CharField(max_length=20, default='manual') class Meta: ordering = ['-started_at'] def __str__(self): return f'{self.profile.name} run #{self.pk} ({self.status})' @property def duration_seconds(self): if self.finished_at and self.started_at: return int((self.finished_at - self.started_at).total_seconds()) return None class ScanFinding(models.Model): SEVERITY_CHOICES = [ ('ok', 'OK'), ('info', 'Info'), ('warning', 'Warning'), ('critical', 'Critical'), ] run = models.ForeignKey(ScanRun, on_delete=models.CASCADE, related_name='findings') check_name = models.CharField(max_length=100) severity = models.CharField(max_length=20, choices=SEVERITY_CHOICES) title = models.CharField(max_length=255) detail = models.TextField() raw = models.JSONField(default=dict) class Meta: ordering = ['severity', 'check_name'] def __str__(self): return f'[{self.severity.upper()}] {self.title}'