Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions mindtrace/apps/mindtrace/apps/poseidon/poseidon/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@
from . import state
from .pages import (
index,
login_page, register_page, register_admin_page, register_super_admin_page,
login_page, register_page,
admin_page, super_admin_dashboard_page,
user_management_page, organization_management_page,
profile_page,
)

__all__ = [
"state", "index",
"login_page", "register_page", "register_admin_page", "register_super_admin_page",
"login_page", "register_page",
"admin_page", "super_admin_dashboard_page",
"user_management_page", "organization_management_page",
"profile_page",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,63 +1,77 @@
from mindtrace.database import MindtraceDocument
from typing import List, TYPE_CHECKING
from typing import List, TYPE_CHECKING, Union
from datetime import datetime, UTC
from .enums import OrgRole

from beanie import Link, before_event, Insert, Replace, SaveChanges, after_event, Delete
from pydantic import Field
from mindtrace.database import MindtraceDocument
from .enums import OrgRole

if TYPE_CHECKING:
from .organization import Organization
from .project import Project

class User(MindtraceDocument):
username: str
email: str
password_hash: str
# --- identity (no username) ---
first_name: str
last_name: str
email: str # store lowercased
password_hash: str # hashed password
organization: Link["Organization"]

# Single organization role
org_role: OrgRole = OrgRole.USER
# e.g. "user" | "admin" | "super_admin"
org_role: OrgRole = Field(default=OrgRole.USER.value)

# Reference to projects
# --- relationships ---
projects: List[Link["Project"]] = Field(default_factory=list)

# --- status & timestamps ---
is_active: bool = True
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
updated_at: datetime = Field(default_factory=lambda: datetime.now(UTC))

# --------------------- lifecycle hooks ---------------------
@before_event(Insert)
async def validate_and_set_creation_timestamps(self):
# Fetch the linked organization
# Normalize email
if self.email:
self.email = self.email.strip().lower()

# Link must be loaded for validations below
await self.fetch_link(User.organization)

# Enforce organization max_users limit
if not self.organization.is_within_user_limit():
raise ValueError(f"Organization '{self.organization.id}' has reached its user limit.")

# Set timestamps
now = datetime.now(UTC)
self.created_at = now
self.updated_at = now

@before_event([Replace, SaveChanges])
def update_timestamp(self):
# Normalize email on updates as well
if self.email:
self.email = self.email.strip().lower()
self.updated_at = datetime.now(UTC)

def has_org_role(self, role: OrgRole) -> bool:
"""Check if user has the specified org-level role"""
return self.org_role == role
# ---------------------- convenience methods ----------------------

def has_org_role(self, role: Union[str, OrgRole]) -> bool:
"""Check if user has the specified org-level role (string or enum)."""
target = getattr(role, "value", role)
return str(self.org_role) == str(target)

def add_project(self, project: "Project"):
"""Add a project to the user's list if not already present"""
"""Add a project to the user's list if not already present."""
if project not in self.projects:
self.projects.append(project)

def remove_project(self, project: "Project"):
"""Remove a project from the user's list"""
"""Remove a project from the user's list."""
self.projects = [p for p in self.projects if p.id != project.id]

def is_assigned_to_project(self, project: "Project") -> bool:
"""Check if user is assigned to a specific project"""
"""Check if user is assigned to a specific project."""
return any(p.id == project.id for p in self.projects)

@after_event(Insert)
Expand Down
Loading