from sqlalchemy import func, select from sqlalchemy.ext.asyncio import AsyncSession from app.core.security import hash_password, verify_password from app.models.role import Role from app.models.user import User async def get(db: AsyncSession, user_id: int) -> User | None: return await db.get(User, user_id) async def get_by_email(db: AsyncSession, email: str) -> User | None: result = await db.execute(select(User).where(User.email == email)) return result.scalar_one_or_none() async def list_users(db: AsyncSession, skip: int = 0, limit: int = 100) -> list[User]: result = await db.execute(select(User).order_by(User.id).offset(skip).limit(limit)) return list(result.scalars().all()) async def count(db: AsyncSession) -> int: result = await db.execute(select(func.count()).select_from(User)) return result.scalar_one() async def _resolve_roles(db: AsyncSession, role_ids: list[int]) -> list[Role]: if not role_ids: return [] result = await db.execute(select(Role).where(Role.id.in_(role_ids))) return list(result.scalars().all()) async def create( db: AsyncSession, *, email: str, password: str, full_name: str | None = None, is_superuser: bool = False, role_ids: list[int] | None = None, ) -> User: user = User( email=email, hashed_password=hash_password(password), full_name=full_name, is_superuser=is_superuser, roles=await _resolve_roles(db, role_ids or []), ) db.add(user) await db.commit() await db.refresh(user) return user async def update(db: AsyncSession, user: User, data: dict) -> User: if "password" in data and data["password"]: user.hashed_password = hash_password(data.pop("password")) else: data.pop("password", None) if "role_ids" in data and data["role_ids"] is not None: user.roles = await _resolve_roles(db, data.pop("role_ids")) else: data.pop("role_ids", None) for field, value in data.items(): if value is not None: setattr(user, field, value) await db.commit() await db.refresh(user) return user async def delete(db: AsyncSession, user: User) -> None: await db.delete(user) await db.commit() async def authenticate(db: AsyncSession, email: str, password: str) -> User | None: user = await get_by_email(db, email) if not user or not verify_password(password, user.hashed_password): return None return user