""" Scam and fraud report models. """ from django.db import models from django.contrib.auth import get_user_model from django.urls import reverse from django.utils.text import slugify from django.core.cache import cache from accounts.security import DataEncryption User = get_user_model() class SiteSettings(models.Model): """ Site-wide settings that can be managed from admin. Uses singleton pattern - only one instance should exist. """ contact_email = models.EmailField( default='support@fraudplatform.bg', help_text='Основен имейл за контакти и поддръжка' ) contact_phone = models.CharField( max_length=50, blank=True, default='', help_text='Телефонен номер за контакти (незадължително)' ) contact_address = models.CharField( max_length=200, blank=True, default='', help_text='Адрес за контакти (незадължително)' ) # Email Server Settings email_backend = models.CharField( max_length=100, default='django.core.mail.backends.smtp.EmailBackend', choices=[ ('django.core.mail.backends.smtp.EmailBackend', 'SMTP'), ('django.core.mail.backends.console.EmailBackend', 'Console (Development)'), ('django.core.mail.backends.filebased.EmailBackend', 'File Based'), ], help_text='Тип на имейл сървъра' ) email_host = models.CharField( max_length=255, blank=True, default='', help_text='SMTP сървър (напр. smtp.gmail.com)' ) email_port = models.IntegerField( default=587, help_text='SMTP порт (обикновено 587 за TLS или 465 за SSL)' ) email_use_tls = models.BooleanField( default=True, help_text='Използване на TLS (за порт 587)' ) email_use_ssl = models.BooleanField( default=False, help_text='Използване на SSL (за порт 465)' ) email_host_user = models.CharField( max_length=255, blank=True, default='', help_text='SMTP потребителско име / имейл' ) email_host_password = models.CharField( max_length=255, blank=True, default='', help_text='SMTP парола (ще бъде криптирана)' ) default_from_email = models.EmailField( default='noreply@fraudplatform.bg', help_text='Имейл адрес по подразбиране за изпращане' ) email_timeout = models.IntegerField( default=10, help_text='Таймаут за имейл връзка (секунди)' ) updated_at = models.DateTimeField(auto_now=True) class Meta: verbose_name = 'Настройки на Сайта' verbose_name_plural = 'Настройки на Сайта' db_table = 'reports_sitesettings' def __str__(self): return 'Настройки на Сайта' def save(self, *args, **kwargs): # Ensure only one instance exists self.pk = 1 # Encrypt email password if it's provided and not already encrypted if self.email_host_password: # Check if it's already encrypted by trying to decrypt it # If decryption succeeds, it's already encrypted, so keep original # If decryption fails, it's plain text, so encrypt it is_encrypted = False try: # Try to decrypt - if it succeeds, it's already encrypted DataEncryption.decrypt(self.email_host_password) is_encrypted = True except (Exception, ValueError, TypeError): # Decryption failed, so it's plain text is_encrypted = False # Only encrypt if it's not already encrypted if not is_encrypted: self.email_host_password = DataEncryption.encrypt(self.email_host_password) super().save(*args, **kwargs) # Clear cache when settings are updated cache.delete('site_settings') def get_email_password(self): """Get decrypted email password.""" if not self.email_host_password: return '' try: return DataEncryption.decrypt(self.email_host_password) except: # If decryption fails, return as-is (might be plain text from migration) return self.email_host_password def delete(self, *args, **kwargs): # Prevent deletion - settings should always exist pass @classmethod def get_settings(cls): """Get site settings with caching.""" settings = cache.get('site_settings') if settings is None: settings, created = cls.objects.get_or_create(pk=1) cache.set('site_settings', settings, 3600) # Cache for 1 hour return settings class ScamTag(models.Model): """ Tags for categorizing scam reports. """ name = models.CharField(max_length=100, unique=True) slug = models.SlugField(max_length=100, unique=True, blank=True) description = models.TextField(blank=True) color = models.CharField( max_length=7, default='#007bff', help_text='Hex color code for display' ) created_at = models.DateTimeField(auto_now_add=True) class Meta: db_table = 'reports_scamtag' verbose_name = 'Scam Tag' verbose_name_plural = 'Scam Tags' ordering = ['name'] def save(self, *args, **kwargs): if not self.slug: self.slug = slugify(self.name) super().save(*args, **kwargs) def __str__(self): return self.name class ScamReport(models.Model): """ Main scam/fraud report model. """ SCAM_TYPE_CHOICES = [ ('phishing', 'Phishing'), ('fake_website', 'Fake Website'), ('romance_scam', 'Romance Scam'), ('investment_scam', 'Investment Scam'), ('tech_support_scam', 'Tech Support Scam'), ('identity_theft', 'Identity Theft'), ('fake_product', 'Fake Product'), ('advance_fee', 'Advance Fee Fraud'), ('other', 'Other'), ] STATUS_CHOICES = [ ('pending', 'Pending Review'), ('under_review', 'Under Review'), ('verified', 'Verified'), ('rejected', 'Rejected'), ('archived', 'Archived'), ] # Reporter information reporter = models.ForeignKey( User, on_delete=models.SET_NULL, null=True, blank=True, related_name='reports' ) is_anonymous = models.BooleanField( default=False, help_text='Report submitted anonymously' ) # Report details title = models.CharField(max_length=200) description = models.TextField() scam_type = models.CharField( max_length=50, choices=SCAM_TYPE_CHOICES, default='other' ) # Reported entities reported_url = models.URLField(blank=True, null=True, max_length=500) reported_email = models.EmailField(blank=True, null=True) reported_phone = models.CharField(max_length=20, blank=True, null=True) reported_company = models.CharField(max_length=200, blank=True, null=True) # Evidence evidence_files = models.JSONField( default=list, blank=True, help_text='List of file paths for evidence' ) # Status and verification status = models.CharField( max_length=20, choices=STATUS_CHOICES, default='pending' ) verification_score = models.IntegerField( default=0, help_text='OSINT verification confidence score (0-100)' ) # Visibility is_public = models.BooleanField( default=True, help_text='Visible in public database' ) is_auto_discovered = models.BooleanField( default=False, help_text='Automatically discovered by OSINT system' ) # Metadata tags = models.ManyToManyField(ScamTag, blank=True, related_name='reports') created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) verified_at = models.DateTimeField(null=True, blank=True) # IP tracking for anonymous reports reporter_ip = models.GenericIPAddressField(null=True, blank=True) class Meta: db_table = 'reports_scamreport' verbose_name = 'Scam Report' verbose_name_plural = 'Scam Reports' ordering = ['-created_at'] indexes = [ models.Index(fields=['status', 'created_at']), models.Index(fields=['scam_type', 'status']), models.Index(fields=['reported_url']), models.Index(fields=['reported_email']), models.Index(fields=['reported_phone']), ] def __str__(self): return f"{self.title} - {self.get_status_display()}" def get_absolute_url(self): return reverse('reports:detail', kwargs={'pk': self.pk}) def get_reporter_display(self): if self.is_anonymous: return "Anonymous" return self.reporter.username if self.reporter else "Unknown" class ScamVerification(models.Model): """ OSINT verification data for scam reports. """ VERIFICATION_METHOD_CHOICES = [ ('whois', 'WHOIS Lookup'), ('dns', 'DNS Records'), ('ssl', 'SSL Certificate'), ('archive', 'Wayback Machine'), ('email_check', 'Email Validation'), ('phone_check', 'Phone Validation'), ('business_registry', 'Business Registry'), ('social_media', 'Social Media'), ('manual', 'Manual Review'), ] report = models.ForeignKey( ScamReport, on_delete=models.CASCADE, related_name='verifications' ) verification_method = models.CharField( max_length=50, choices=VERIFICATION_METHOD_CHOICES ) verification_data = models.JSONField( default=dict, help_text='Raw verification data' ) confidence_score = models.IntegerField( default=0, help_text='Confidence score for this verification (0-100)' ) verified_by = models.ForeignKey( User, on_delete=models.SET_NULL, null=True, blank=True, related_name='verifications' ) notes = models.TextField(blank=True) created_at = models.DateTimeField(auto_now_add=True) class Meta: db_table = 'reports_scamverification' verbose_name = 'Scam Verification' verbose_name_plural = 'Scam Verifications' ordering = ['-created_at'] def __str__(self): return f"Verification for {self.report.title} via {self.get_verification_method_display()}" class TakedownRequest(models.Model): """ Request to take down a scam report by the accused party. """ STATUS_CHOICES = [ ('pending', 'Pending Review'), ('under_review', 'Under Review'), ('approved', 'Approved'), ('rejected', 'Rejected'), ] report = models.ForeignKey( ScamReport, on_delete=models.CASCADE, related_name='takedown_requests' ) requester_name = models.CharField( max_length=200, help_text='Име на заявителя' ) requester_email = models.EmailField( help_text='Имейл на заявителя' ) requester_phone = models.CharField( max_length=50, blank=True, help_text='Телефон на заявителя (незадължително)' ) reason = models.TextField( help_text='Причина за заявката за премахване' ) evidence = models.TextField( blank=True, help_text='Доказателства или допълнителна информация' ) status = models.CharField( max_length=20, choices=STATUS_CHOICES, default='pending' ) reviewed_by = models.ForeignKey( User, on_delete=models.SET_NULL, null=True, blank=True, related_name='reviewed_takedown_requests', limit_choices_to={'role__in': ['moderator', 'admin']} ) review_notes = models.TextField( blank=True, help_text='Бележки от модератора' ) ip_address = models.GenericIPAddressField(null=True, blank=True) user_agent = models.TextField(blank=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) reviewed_at = models.DateTimeField(null=True, blank=True) class Meta: db_table = 'reports_takedownrequest' verbose_name = 'Заявка за Премахване' verbose_name_plural = 'Заявки за Премахване' ordering = ['-created_at'] indexes = [ models.Index(fields=['report', 'status']), models.Index(fields=['status', 'created_at']), ] def __str__(self): return f"Takedown request for {self.report.title} by {self.requester_name}"