feat: Ipad app features and Dream Weaver for Velocity WebOS
This commit is contained in:
123
backend/auth/service.py
Normal file
123
backend/auth/service.py
Normal file
@@ -0,0 +1,123 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from fastapi import HTTPException, status
|
||||
|
||||
from backend.auth.dependencies import (
|
||||
UserPrincipal,
|
||||
create_access_token,
|
||||
default_tenant_id,
|
||||
verify_password,
|
||||
)
|
||||
from backend.auth.user_directory import ensure_user_directory_schema
|
||||
|
||||
|
||||
async def _get_pool(app: Any):
|
||||
pool = getattr(app.state, "db_pool", None)
|
||||
if pool is None:
|
||||
raise HTTPException(status_code=503, detail="Database unavailable.")
|
||||
return pool
|
||||
|
||||
|
||||
async def login_with_directory(*, app: Any, email: str, password: str) -> dict[str, Any]:
|
||||
await ensure_user_directory_schema(app)
|
||||
pool = await _get_pool(app)
|
||||
tenant_fallback = default_tenant_id()
|
||||
|
||||
async with pool.acquire() as conn:
|
||||
row = await conn.fetchrow(
|
||||
"""
|
||||
SELECT
|
||||
id::text,
|
||||
role,
|
||||
password_hash,
|
||||
COALESCE(NULLIF(tenant_id, ''), $2) AS tenant_id
|
||||
FROM users_and_roles
|
||||
WHERE email = $1 AND is_active = TRUE
|
||||
""",
|
||||
email.strip(),
|
||||
tenant_fallback,
|
||||
)
|
||||
|
||||
if not row or not verify_password(password, row["password_hash"]):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Invalid email or password.",
|
||||
)
|
||||
|
||||
token = create_access_token(
|
||||
user_id=row["id"],
|
||||
role=row["role"],
|
||||
tenant_id=row["tenant_id"],
|
||||
)
|
||||
return {"access_token": token, "token_type": "bearer", "expires_in": 28800}
|
||||
|
||||
|
||||
async def read_authenticated_user_profile(*, app: Any, user: UserPrincipal) -> dict[str, Any]:
|
||||
await ensure_user_directory_schema(app)
|
||||
pool = await _get_pool(app)
|
||||
tenant_scope = user.tenant_id or default_tenant_id()
|
||||
|
||||
async with pool.acquire() as conn:
|
||||
row = await conn.fetchrow(
|
||||
"""
|
||||
SELECT
|
||||
full_name,
|
||||
email,
|
||||
avatar_url,
|
||||
COALESCE(NULLIF(tenant_id, ''), $2) AS tenant_id
|
||||
FROM users_and_roles
|
||||
WHERE id = $1::uuid
|
||||
AND COALESCE(NULLIF(tenant_id, ''), $2) = $2
|
||||
""",
|
||||
user.user_id,
|
||||
tenant_scope,
|
||||
)
|
||||
|
||||
return {
|
||||
"user_id": user.user_id,
|
||||
"role": user.role,
|
||||
"tenant_id": row["tenant_id"] if row else tenant_scope,
|
||||
"full_name": row["full_name"] if row else None,
|
||||
"email": row["email"] if row else None,
|
||||
"avatar_url": row["avatar_url"] if row else None,
|
||||
}
|
||||
|
||||
|
||||
async def list_tenant_users(*, app: Any, user: UserPrincipal) -> list[dict[str, Any]]:
|
||||
await ensure_user_directory_schema(app)
|
||||
pool = await _get_pool(app)
|
||||
tenant_scope = user.tenant_id or default_tenant_id()
|
||||
|
||||
async with pool.acquire() as conn:
|
||||
rows = await conn.fetch(
|
||||
"""
|
||||
SELECT
|
||||
id::text AS user_id,
|
||||
role,
|
||||
COALESCE(NULLIF(tenant_id, ''), $1) AS tenant_id,
|
||||
full_name,
|
||||
email,
|
||||
avatar_url
|
||||
FROM users_and_roles
|
||||
WHERE is_active = TRUE
|
||||
AND COALESCE(NULLIF(tenant_id, ''), $1) = $2
|
||||
ORDER BY
|
||||
COALESCE(NULLIF(full_name, ''), email, id::text) ASC
|
||||
""",
|
||||
default_tenant_id(),
|
||||
tenant_scope,
|
||||
)
|
||||
|
||||
return [
|
||||
{
|
||||
"user_id": row["user_id"],
|
||||
"role": row["role"],
|
||||
"tenant_id": row["tenant_id"],
|
||||
"full_name": row["full_name"],
|
||||
"email": row["email"],
|
||||
"avatar_url": row["avatar_url"],
|
||||
}
|
||||
for row in rows
|
||||
]
|
||||
Reference in New Issue
Block a user