Files
2026-07-13 16:52:00 -04:00

69 lines
2.5 KiB
Python

"""User model. Argon2 password hashing, role/trust fields, Flask-Login mixin."""
from datetime import datetime
from app.utils.time import utcnow
from flask_login import UserMixin
from app.extensions import db
from app.models.enums import Role, UserStatus, TrustTier
from app.utils.security import hash_password, verify_password
class User(UserMixin, db.Model):
__tablename__ = "users"
id = db.Column(db.BigInteger().with_variant(db.Integer, "sqlite"), primary_key=True, autoincrement=True)
email = db.Column(db.String(255), unique=True, nullable=False, index=True)
password_hash = db.Column(db.String(255), nullable=False)
display_name = db.Column(db.String(80), nullable=False)
role = db.Column(db.Enum(Role), nullable=False, default=Role.free)
status = db.Column(db.Enum(UserStatus), nullable=False, default=UserStatus.active)
locale = db.Column(db.String(5), nullable=False, default="en")
tier_id = db.Column(db.BigInteger, db.ForeignKey("plans.id"), nullable=True)
tier = db.relationship("Plan", back_populates="users")
trust_score = db.Column(db.Integer, nullable=False, default=0)
trust_tier = db.Column(db.Enum(TrustTier), nullable=False, default=TrustTier.new)
verified = db.Column(db.Boolean, nullable=False, default=False) # identity/badge
email_verified = db.Column(db.Boolean, nullable=False, default=False)
last_login_at = db.Column(db.DateTime, nullable=True)
created_at = db.Column(db.DateTime, default=utcnow, nullable=False)
updated_at = db.Column(db.DateTime, default=utcnow,
onupdate=utcnow, nullable=False)
trust_events = db.relationship("TrustEvent", back_populates="user",
lazy="dynamic", cascade="all, delete-orphan")
# --- password ---
def set_password(self, raw):
self.password_hash = hash_password(raw)
def check_password(self, raw):
return verify_password(self.password_hash, raw)
# --- RBAC helpers ---
@property
def is_admin(self):
return self.role == Role.admin
@property
def is_moderator(self):
return self.role in (Role.moderator, Role.admin)
@property
def is_subscriber(self):
return self.role == Role.subscriber
def has_role(self, *roles):
return self.role in roles
@property
def is_active(self):
# Flask-Login uses this to permit login.
return self.status == UserStatus.active
def __repr__(self):
return f"<User {self.email}>"