improve registration flows
Build and Push Docker Images / Build and Push Docker Images (push) Failing after 5s
Build and Push Docker Images / Build and Push Docker Images (push) Failing after 5s
This commit is contained in:
@@ -2,16 +2,31 @@
|
||||
This is the models module for api.
|
||||
"""
|
||||
|
||||
import secrets
|
||||
from datetime import timedelta
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
from django.db import models
|
||||
from .managers import CustomUserManager
|
||||
from .storages import idcard_storage
|
||||
from django.utils import timezone
|
||||
import pyotp
|
||||
from billing.models import WalletTransaction
|
||||
|
||||
|
||||
class User(AbstractUser):
|
||||
# Registration workflow states. `verified` (below) stays the login gate and
|
||||
# is kept in sync: verified is True iff status == STATUS_VERIFIED.
|
||||
STATUS_PENDING = "pending" # created, awaiting/undergoing auto-verify
|
||||
STATUS_VERIFIED = "verified" # approved, may log in
|
||||
STATUS_ID_REQUIRED = "id_required" # must (re)upload an ID/passport photo
|
||||
STATUS_ID_SUBMITTED = "id_submitted" # photo uploaded, awaiting admin review
|
||||
STATUS_CHOICES = [
|
||||
(STATUS_PENDING, "Pending"),
|
||||
(STATUS_VERIFIED, "Verified"),
|
||||
(STATUS_ID_REQUIRED, "ID required"),
|
||||
(STATUS_ID_SUBMITTED, "ID submitted"),
|
||||
]
|
||||
|
||||
address = models.CharField(max_length=255, blank=True)
|
||||
email = models.EmailField(blank=True, null=True, unique=True)
|
||||
mobile = models.CharField(
|
||||
@@ -23,6 +38,16 @@ class User(AbstractUser):
|
||||
max_length=255, blank=True, unique=True, null=True, db_index=True
|
||||
)
|
||||
verified = models.BooleanField(default=False)
|
||||
status = models.CharField(
|
||||
max_length=20, choices=STATUS_CHOICES, default=STATUS_PENDING, db_index=True
|
||||
)
|
||||
id_card_photo = models.FileField(
|
||||
upload_to="",
|
||||
storage=idcard_storage,
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text="ID card / passport (JPG, PNG or PDF) uploaded by the user for manual review.",
|
||||
)
|
||||
is_admin = models.BooleanField(default=False)
|
||||
dob = models.DateField(blank=True, null=True)
|
||||
terms_accepted = models.BooleanField(default=False)
|
||||
@@ -79,6 +104,46 @@ class User(AbstractUser):
|
||||
# by createsuperuser.
|
||||
REQUIRED_FIELDS = []
|
||||
|
||||
def set_status(self, status, *, save=True):
|
||||
"""Set workflow status and keep the `verified` login gate in sync."""
|
||||
self.status = status
|
||||
self.verified = status == self.STATUS_VERIFIED
|
||||
if save:
|
||||
self.save(update_fields=["status", "verified", "updated_at"])
|
||||
|
||||
|
||||
class IdUploadToken(models.Model):
|
||||
"""
|
||||
One-time-ish magic-link token letting an unverified user reach the public
|
||||
ID-upload page without logging in. Issuing a fresh token supersedes any
|
||||
outstanding ones for the same user.
|
||||
"""
|
||||
|
||||
key = models.CharField(max_length=64, unique=True, db_index=True)
|
||||
user = models.ForeignKey(
|
||||
"User", on_delete=models.CASCADE, related_name="id_upload_tokens"
|
||||
)
|
||||
created_at = models.DateTimeField(default=timezone.now)
|
||||
expires_at = models.DateTimeField()
|
||||
used_at = models.DateTimeField(null=True, blank=True)
|
||||
|
||||
@classmethod
|
||||
def issue(cls, user, ttl_hours=48):
|
||||
cls.objects.filter(user=user, used_at__isnull=True).update(
|
||||
used_at=timezone.now()
|
||||
)
|
||||
return cls.objects.create(
|
||||
key=secrets.token_urlsafe(32),
|
||||
user=user,
|
||||
expires_at=timezone.now() + timedelta(hours=ttl_hours),
|
||||
)
|
||||
|
||||
def is_valid(self):
|
||||
return self.used_at is None and self.expires_at > timezone.now()
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"IdUploadToken(user={self.user_id}, valid={self.is_valid()})"
|
||||
|
||||
|
||||
class TemporaryUser(models.Model):
|
||||
t_id = models.AutoField(primary_key=True)
|
||||
|
||||
Reference in New Issue
Block a user