Initial commit
This commit is contained in:
80
accounts/models.py
Normal file
80
accounts/models.py
Normal file
@@ -0,0 +1,80 @@
|
||||
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'])
|
||||
Reference in New Issue
Block a user