add users

This commit is contained in:
debu-2 2026-10-04 20:42:40 +05:30
parent c01914736b
commit ee23b71f0f
12 changed files with 506 additions and 182 deletions

View file

@ -4,6 +4,7 @@ from __future__ import annotations
import secrets
from datetime import date
from pwdlib import PasswordHash
from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession
@ -11,6 +12,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
from app.models import Account
from app.schemas import OrgMemberCreate, OrgMemberRead
from app.services.domain_provisioning import account_is_domain_owner
from app.services.org_invitations import send_employee_invitation
password_hasher = PasswordHash.recommended()
PLACEHOLDER_BIRTHDATE = date(1990, 1, 1)
@ -46,10 +48,12 @@ async def member_read(
return OrgMemberRead(
id=member.id,
email=member.email,
invitation_email=member.invitation_email,
first_name=member.first_name,
last_name=member.last_name,
org_role=role,
member_status=member.member_status,
must_change_password=member.must_change_password,
is_domain_owner=is_owner,
groups=[],
created_at=member.created_at,
@ -84,10 +88,6 @@ def _placeholder_phone() -> str:
return f"+1{digits}"
def _email_local_part(email: str) -> str:
return email.split("@", 1)[0].lower()
async def create_org_member(
session: AsyncSession,
*,
@ -102,13 +102,18 @@ async def create_org_member(
if not email.endswith(f"@{company_domain}"):
raise ValueError(f"Email must use your company domain @{company_domain}.")
invitation_email = (
str(payload.invitation_email).lower() if payload.invitation_email else None
)
if payload.send_invitation:
raw_password = secrets.token_urlsafe(24)
if not invitation_email:
raise ValueError("A personal invitation email is required.")
if invitation_email.endswith(f"@{company_domain}"):
raise ValueError(
"Use a personal email address for the invitation, not your company domain."
)
member_status = "invited"
else:
if not payload.password:
raise ValueError("Password is required when invitation email is disabled.")
raw_password = payload.password
member_status = "active"
existing = await session.scalar(
@ -135,11 +140,23 @@ async def create_org_member(
company_domain=company_domain,
org_role=payload.org_role,
member_status=member_status,
invitation_email=invitation_email,
must_change_password=payload.force_password_change,
created_by_id=manager.id,
password_hash=password_hasher.hash(raw_password),
password_hash=password_hasher.hash(payload.password),
)
session.add(member)
await session.flush()
if payload.send_invitation:
await send_employee_invitation(
session,
member=member,
manager=manager,
initial_password=payload.password,
force_password_change=payload.force_password_change,
)
return await member_read(session, member)