from django.db import models from django.contrib.auth import get_user_model User = get_user_model() class UserProfile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile') display_name = models.CharField(max_length=100, blank=True, default='') contact_phone = models.CharField(max_length=30, blank=True, default='') default_free_traffic_gb_per_domain_override = models.PositiveIntegerField(null=True, blank=True) class Meta: verbose_name = '用户资料' verbose_name_plural = '用户资料' def __str__(self): return self.display_name or self.user.get_username() # Create your models here. class LoginRecord(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='login_records') ip_address = models.GenericIPAddressField(null=True, blank=True) user_agent = models.CharField(max_length=255, blank=True, default='') created_at = models.DateTimeField(auto_now_add=True) class Meta: verbose_name = '登录历史' verbose_name_plural = '登录历史' ordering = ['-created_at'] def __str__(self): return f"{self.user.get_username()} @ {self.created_at}" class LoginThrottle(models.Model): user = models.ForeignKey(User, null=True, blank=True, on_delete=models.SET_NULL, related_name='login_throttles') ip_address = models.GenericIPAddressField(null=True, blank=True) fail_count = models.PositiveIntegerField(default=0) total_fail_count = models.PositiveIntegerField(default=0) last_failed_at = models.DateTimeField(null=True, blank=True) banned_until = models.DateTimeField(null=True, blank=True) note = models.CharField(max_length=255, blank=True, default='') class Meta: verbose_name = '登录节流' verbose_name_plural = '登录节流' ordering = ['-last_failed_at'] indexes = [ models.Index(fields=['ip_address']), models.Index(fields=['user']), models.Index(fields=['banned_until']), ] def is_banned(self): from django.utils import timezone return bool(self.banned_until and self.banned_until > timezone.now()) def register_failure(self, ban_threshold=5, ban_minutes=15): from django.utils import timezone from datetime import timedelta now = timezone.now() self.fail_count += 1 self.total_fail_count += 1 self.last_failed_at = now if self.fail_count >= ban_threshold: self.banned_until = now + timedelta(minutes=int(ban_minutes)) self.note = f'自动封禁 {int(ban_minutes)} 分钟(超过失败阈值 {int(ban_threshold)})' self.fail_count = 0 self.save(update_fields=['fail_count', 'total_fail_count', 'last_failed_at', 'banned_until', 'note']) def register_success(self): if self.fail_count or self.banned_until: self.fail_count = 0 self.banned_until = None self.note = '' self.save(update_fields=['fail_count', 'banned_until', 'note'])