SentinelAI / app /db /models /user.py
sajith-0701's picture
initial deployment for HF Spaces
71c1ad2
# app/db/models/user.py
# Beanie User document β€” mirrors the Mongoose User model from Backend/
from __future__ import annotations
from datetime import datetime
from enum import Enum
from typing import Optional
from beanie import Document, Indexed
from pydantic import EmailStr, Field
class UserRole(str, Enum):
PARENT = "parent"
CHILD = "child"
ADMIN = "admin"
class UserDocument(Document):
"""
User document stored in MongoDB.
Supports Parent β†’ Child relationship for monitoring.
"""
email: Optional[EmailStr] = None
username: Indexed(str, unique=True) # type: ignore[valid-type]
password_hash: str # bcrypt hash β€” never returned in API responses
role: UserRole = UserRole.PARENT
first_name: str
last_name: str
phone: Optional[str] = None
date_of_birth: Optional[datetime] = None
# Parent-Child relationship
parent_id: Optional[str] = None # set for child accounts
children: list[str] = Field(default_factory=list) # set for parent accounts
# Consent & Privacy
consent_given: bool = False
consent_date: Optional[datetime] = None
parental_consent: Optional[bool] = None # for child accounts
# Account status
is_active: bool = True
is_verified: bool = False
last_login_at: Optional[datetime] = None
# Security β€” refresh tokens stored for rotation/revocation
refresh_tokens: list[str] = Field(default_factory=list)
password_changed_at: Optional[datetime] = None
created_at: datetime = Field(default_factory=datetime.utcnow)
updated_at: datetime = Field(default_factory=datetime.utcnow)
class Settings:
name = "users"
# Atlas already has the correct indexes from the Mongoose model.
# Beanie will use the field-level Indexed() annotations and won't
# try to re-create conflicting ones when allow_index_dropping=False.
def to_public(self) -> dict:
"""Return safe user dict without sensitive fields."""
return {
"id": str(self.id),
"email": self.email,
"username": self.username,
"role": self.role.value,
"first_name": self.first_name,
"last_name": self.last_name,
"is_active": self.is_active,
"is_verified": self.is_verified,
"parent_id": self.parent_id,
"children": self.children,
"created_at": self.created_at.isoformat(),
}