mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-10-30 20:05:42 +08:00
154 lines
4.9 KiB
Python
154 lines
4.9 KiB
Python
"""
|
|
用户账号、认证信息与权限的 ORM 模型与会话内数据操作。
|
|
|
|
会话及事务由调用方管理,本模块不负责提交或回滚。
|
|
"""
|
|
|
|
from typing import Any, Optional, Self
|
|
from uuid import uuid4
|
|
|
|
from sqlalchemy import JSON, Boolean, Index, String, select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from sqlalchemy.orm import Mapped, Session, mapped_column
|
|
|
|
from app.db.base import Base, get_id_column
|
|
|
|
|
|
class User(Base):
|
|
"""
|
|
用户表
|
|
"""
|
|
|
|
# 用户记录的数据库主键。
|
|
id = get_id_column()
|
|
# 用户名,唯一值
|
|
name: Mapped[str] = mapped_column(String, nullable=False)
|
|
# 邮箱
|
|
email: Mapped[Optional[str]] = mapped_column(String)
|
|
# 加密后密码
|
|
hashed_password: Mapped[Optional[str]] = mapped_column(String)
|
|
# 是否启用
|
|
is_active: Mapped[Optional[bool]] = mapped_column(Boolean(), default=True)
|
|
# 是否管理员
|
|
is_superuser: Mapped[Optional[bool]] = mapped_column(Boolean(), default=False)
|
|
# 头像
|
|
avatar: Mapped[Optional[str]] = mapped_column(String)
|
|
# 是否启用otp二次验证
|
|
is_otp: Mapped[Optional[bool]] = mapped_column(Boolean(), default=False)
|
|
# otp秘钥
|
|
otp_secret: Mapped[Optional[str]] = mapped_column(String, default=None)
|
|
# 用户权限 json
|
|
permissions: Mapped[Optional[Any]] = mapped_column(JSON, default=dict)
|
|
# 用户个性化设置 json
|
|
settings: Mapped[Optional[Any]] = mapped_column(JSON, default=dict)
|
|
# 通知使用不可变账号身份;主键复用、改名或删除重建不能继承旧设备。
|
|
notification_identity: Mapped[str] = mapped_column(String(32), default=lambda: uuid4().hex, nullable=False)
|
|
|
|
__table_args__ = (Index("ux_user_name", "name", unique=True),)
|
|
|
|
@classmethod
|
|
def get_by_name(
|
|
cls,
|
|
db: Session,
|
|
name: str,
|
|
) -> Optional[Self]:
|
|
"""
|
|
在调用方同步会话中按用户名查询用户。
|
|
"""
|
|
user: Optional[Self] = db.execute(select(cls).where(cls.name == name)).scalars().first()
|
|
return user
|
|
|
|
@classmethod
|
|
async def async_get_by_name(
|
|
cls,
|
|
db: AsyncSession,
|
|
name: str,
|
|
) -> Optional[Self]:
|
|
"""
|
|
在调用方异步会话中按用户名查询用户。
|
|
"""
|
|
result = await db.execute(select(cls).filter(cls.name == name))
|
|
user: Optional[Self] = result.scalars().first()
|
|
return user
|
|
|
|
@classmethod
|
|
def get_by_id(cls, db: Session, user_id: int) -> Optional[Self]:
|
|
"""
|
|
在调用方同步会话中按用户 ID 查询用户。
|
|
"""
|
|
user: Optional[Self] = db.execute(select(cls).where(cls.id == user_id)).scalars().first()
|
|
return user
|
|
|
|
@classmethod
|
|
async def async_get_by_id(
|
|
cls,
|
|
db: AsyncSession,
|
|
user_id: int,
|
|
) -> Optional[Self]:
|
|
"""
|
|
在调用方异步会话中按用户 ID 查询用户。
|
|
"""
|
|
result = await db.execute(select(cls).filter(cls.id == user_id))
|
|
user: Optional[Self] = result.scalars().first()
|
|
return user
|
|
|
|
def delete_by_name(self, db: Session, name: str) -> bool:
|
|
"""
|
|
在调用方同步会话中按用户名暂存删除。
|
|
"""
|
|
user = self.get_by_name(db, name)
|
|
if user:
|
|
db.delete(user)
|
|
return True
|
|
|
|
async def async_delete_by_name(self, db: AsyncSession, name: str) -> bool:
|
|
"""
|
|
在调用方异步会话中按用户名暂存删除。
|
|
"""
|
|
user = await self.async_get_by_name(db, name)
|
|
if user:
|
|
await db.delete(user)
|
|
return True
|
|
|
|
def delete_by_id(self, db: Session, user_id: int) -> bool:
|
|
"""
|
|
在调用方同步会话中按用户 ID 暂存删除。
|
|
"""
|
|
user = self.get_by_id(db, user_id)
|
|
if user:
|
|
db.delete(user)
|
|
return True
|
|
|
|
@classmethod
|
|
async def async_delete_by_id(cls, db: AsyncSession, user_id: int) -> bool:
|
|
"""
|
|
异步按用户 ID 删除用户,供 UserOper 通过类方法调用。
|
|
"""
|
|
user = await cls.async_get_by_id(db, user_id)
|
|
if user:
|
|
await db.delete(user)
|
|
return True
|
|
|
|
def update_otp_by_name(self, db: Session, name: str, otp: bool, secret: str) -> bool:
|
|
"""
|
|
在调用方同步会话中更新指定用户的 OTP 状态。
|
|
"""
|
|
user = self.get_by_name(db, name)
|
|
if user:
|
|
user.is_otp = otp
|
|
user.otp_secret = secret
|
|
return True
|
|
return False
|
|
|
|
@classmethod
|
|
async def async_update_otp_by_name(cls, db: AsyncSession, name: str, otp: bool, secret: str) -> bool:
|
|
"""
|
|
异步按用户名更新 OTP 状态,供 UserOper 通过类方法调用。
|
|
"""
|
|
user = await cls.async_get_by_name(db, name)
|
|
if user:
|
|
user.is_otp = otp
|
|
user.otp_secret = secret
|
|
return True
|
|
return False
|