moonstream/backend/moonstreamapi/routes/users.py

295 wiersze
10 KiB
Python

"""
The Moonstream users HTTP API
"""
import logging
import uuid
from typing import Any, Dict, Optional
from bugout.data import BugoutResource, BugoutToken, BugoutUser, BugoutUserTokens
from bugout.exceptions import BugoutResponseException
from fastapi import APIRouter, Body, Form, Request
from .. import data
from ..actions import create_onboarding_resource
from ..middleware import MoonstreamHTTPException
from ..settings import BUGOUT_REQUEST_TIMEOUT_SECONDS, MOONSTREAM_APPLICATION_ID
from ..settings import bugout_client as bc
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/users")
@router.post("/", tags=["users"], response_model=BugoutUser)
async def create_user_handler(
username: str = Form(...), email: str = Form(...), password: str = Form(...)
) -> BugoutUser:
try:
user: BugoutUser = bc.create_user(
username=username,
email=email,
password=password,
application_id=MOONSTREAM_APPLICATION_ID,
)
except BugoutResponseException as e:
raise MoonstreamHTTPException(status_code=e.status_code, detail=e.detail)
except Exception as e:
raise MoonstreamHTTPException(status_code=500, internal_error=e)
return user
@router.get("/", tags=["users"], response_model=BugoutUser)
async def get_user_handler(request: Request) -> BugoutUser:
user: BugoutUser = request.state.user
return user
@router.post("/password/reset_initiate", tags=["users"], response_model=Dict[str, Any])
async def restore_password_handler(email: str = Form(...)) -> Dict[str, Any]:
try:
response = bc.restore_password(email=email)
except BugoutResponseException as e:
raise MoonstreamHTTPException(status_code=e.status_code, detail=e.detail)
except Exception as e:
raise MoonstreamHTTPException(status_code=500, internal_error=e)
return response
@router.post("/password/reset_complete", tags=["users"], response_model=BugoutUser)
async def reset_password_handler(
reset_id: str = Form(...), new_password: str = Form(...)
) -> BugoutUser:
try:
response = bc.reset_password(reset_id=reset_id, new_password=new_password)
except BugoutResponseException as e:
raise MoonstreamHTTPException(status_code=e.status_code, detail=e.detail)
except Exception as e:
raise MoonstreamHTTPException(status_code=500, internal_error=e)
return response
@router.post("/password/change", tags=["users"], response_model=BugoutUser)
async def change_password_handler(
request: Request, current_password: str = Form(...), new_password: str = Form(...)
) -> BugoutUser:
token = request.state.token
try:
user = bc.change_password(
token=token, current_password=current_password, new_password=new_password
)
except BugoutResponseException as e:
raise MoonstreamHTTPException(status_code=e.status_code, detail=e.detail)
except Exception as e:
raise MoonstreamHTTPException(status_code=500, internal_error=e)
return user
@router.delete("/", tags=["users"], response_model=BugoutUser)
async def delete_user_handler(
request: Request, password: str = Form(...)
) -> BugoutUser:
user = request.state.user
token = request.state.token
try:
user = bc.delete_user(token=token, user_id=user.id, password=password)
except BugoutResponseException as e:
raise MoonstreamHTTPException(status_code=e.status_code, detail=e.detail)
except Exception as e:
raise MoonstreamHTTPException(status_code=500, internal_error=e)
return user
@router.post("/token", tags=["tokens"], response_model=BugoutToken)
async def login_handler(
username: str = Form(...),
password: str = Form(...),
token_note: Optional[str] = Form(None),
) -> BugoutToken:
try:
token: BugoutToken = bc.create_token(
username=username,
password=password,
application_id=MOONSTREAM_APPLICATION_ID,
token_note=token_note,
)
except BugoutResponseException as e:
raise MoonstreamHTTPException(status_code=e.status_code, detail=e.detail)
except Exception as e:
raise MoonstreamHTTPException(status_code=500, internal_error=e)
return token
@router.get("/tokens", tags=["tokens"], response_model=BugoutUserTokens)
async def tokens_handler(request: Request) -> BugoutUserTokens:
token = request.state.token
try:
response = bc.get_user_tokens(
token, timeout=BUGOUT_REQUEST_TIMEOUT_SECONDS, active=True
)
except BugoutResponseException as e:
raise MoonstreamHTTPException(status_code=e.status_code, detail=e.detail)
except Exception as e:
raise MoonstreamHTTPException(status_code=500, internal_error=e)
return response
@router.put("/token", tags=["tokens"], response_model=BugoutToken)
async def token_update_handler(
token_note: str = Form(...), access_token: str = Form(...)
) -> BugoutToken:
try:
response = bc.update_token(token=access_token, token_note=token_note)
except BugoutResponseException as e:
raise MoonstreamHTTPException(status_code=e.status_code, detail=e.detail)
except Exception as e:
raise MoonstreamHTTPException(status_code=500, internal_error=e)
return response
@router.post("/revoke/{access_token}", tags=["tokens"], response_model=uuid.UUID)
async def delete_token_by_id_handler(
request: Request, access_token: uuid.UUID
) -> uuid.UUID:
token = request.state.token
try:
response = bc.revoke_token(
token=token,
target_token=access_token,
timeout=BUGOUT_REQUEST_TIMEOUT_SECONDS,
)
except BugoutResponseException as e:
raise MoonstreamHTTPException(status_code=e.status_code, detail=e.detail)
except Exception as e:
raise MoonstreamHTTPException(status_code=500, internal_error=e)
return response
@router.delete("/token", tags=["tokens"], response_model=uuid.UUID)
async def logout_handler(request: Request) -> uuid.UUID:
token = request.state.token
try:
token_id: uuid.UUID = bc.revoke_token(token=token)
except BugoutResponseException as e:
raise MoonstreamHTTPException(status_code=e.status_code, detail=e.detail)
except Exception as e:
raise MoonstreamHTTPException(status_code=500, internal_error=e)
return token_id
@router.post("/onboarding", tags=["users"], response_model=data.OnboardingState)
async def set_onboarding_state(
request: Request,
onboarding_data: data.OnboardingState = Body(...),
) -> data.OnboardingState:
token = request.state.token
try:
response = bc.list_resources(
token=token,
params={"type": data.USER_ONBOARDING_STATE},
timeout=BUGOUT_REQUEST_TIMEOUT_SECONDS,
)
resource_data = {"type": data.USER_ONBOARDING_STATE, **onboarding_data.dict()}
if response.resources:
resource = bc.update_resource(
token=token,
resource_id=str(response.resources[0].id),
resource_data={"update": resource_data, "drop_keys": []},
)
else:
resource = create_onboarding_resource(
token=token, resource_data=resource_data
)
except BugoutResponseException as e:
raise MoonstreamHTTPException(status_code=e.status_code, detail=e.detail)
except Exception as e:
raise MoonstreamHTTPException(status_code=500)
if (
resource.resource_data.get("is_complete") is None
or resource.resource_data.get("steps") is None
):
logger.error(
f"Resources did not return correct onboarding object. Resource id:{resource.id}"
)
raise MoonstreamHTTPException(status_code=500)
result = data.OnboardingState(
is_complete=resource.resource_data.get("is_complete", False),
steps=resource.resource_data.get("steps", {}),
)
return result
@router.get("/onboarding", tags=["users"], response_model=data.OnboardingState)
async def get_onboarding_state(request: Request) -> data.OnboardingState:
token = request.state.token
try:
response = bc.list_resources(
token=token,
params={"type": data.USER_ONBOARDING_STATE},
timeout=BUGOUT_REQUEST_TIMEOUT_SECONDS,
)
if response.resources:
resource = response.resources[0]
else:
resource = create_onboarding_resource(token=token)
except BugoutResponseException as e:
raise MoonstreamHTTPException(status_code=e.status_code, detail=e.detail)
except Exception as e:
raise MoonstreamHTTPException(status_code=500)
if (
resource.resource_data.get("is_complete") is None
or resource.resource_data.get("steps") is None
):
logger.error(
f"Resources did not return correct onboarding object. Resource id:{resource.id}"
)
raise MoonstreamHTTPException(status_code=500)
result = data.OnboardingState(
is_complete=resource.resource_data.get("is_complete", False),
steps=resource.resource_data.get("steps", {}),
)
return result
@router.delete("/onboarding", tags=["users"], response_model=data.OnboardingState)
async def delete_onboarding_state(request: Request) -> data.OnboardingState:
token = request.state.token
try:
response = bc.list_resources(
token=token,
params={"type": data.USER_ONBOARDING_STATE},
timeout=BUGOUT_REQUEST_TIMEOUT_SECONDS,
)
if not response.resources:
raise MoonstreamHTTPException(status_code=404, detail="not found")
if response.resources:
resource: BugoutResource = bc.delete_resource(
token=token,
resource_id=response.resources[0].id,
timeout=BUGOUT_REQUEST_TIMEOUT_SECONDS,
)
except BugoutResponseException as e:
raise MoonstreamHTTPException(status_code=e.status_code, detail=e.detail)
except Exception as e:
raise MoonstreamHTTPException(status_code=500)
if (
resource.resource_data.get("is_complete") is None
or resource.resource_data.get("steps") is None
):
logger.error(
f"Resources did not return correct onboarding object. Resource id:{resource.id}"
)
raise MoonstreamHTTPException(status_code=500)
result = data.OnboardingState(
is_complete=resource.resource_data.get("is_complete", False),
steps=resource.resource_data.get("steps", {}),
)
return result