import components.users from components.utils import batch, ensure_list from components.database import IN_MEMORY_DB from components.web.utils import * blueprint = Blueprint("users", __name__, url_prefix="/system/users") @blueprint.context_processor def load_context(): from components.models.users import UserProfile context = {"schemas": {"user_profile": UserProfile.model_json_schema()}} return context @blueprint.route("/") @acl("system") async def get_user(user_id: str): try: user = await components.users.get(user_id=user_id) except ValidationError as e: return validation_error(e.errors()) except ValueError as e: name, message = e.args return validation_error([{"loc": [name], "msg": message}]) return await render_or_json( "system/includes/users/row.html", request.headers, user=user ) @blueprint.route("/") @blueprint.route("/search", methods=["POST"]) @acl("system") @formoptions(["users"]) async def get_users(): try: ( search_model, page, page_size, sort_attr, sort_reverse, filters, ) = table_search_helper(request.form_parsed, "users", default_sort_attr="login") except ValidationError as e: return validation_error(e.errors()) if request.method == "POST": matched_users = [m for m in await components.users.search(name=search_model.q)] user_pages = [ m for m in batch( sorted( matched_users, key=lambda x: getattr(x, sort_attr, "id"), reverse=sort_reverse, ), page_size, ) ] try: user_pages[page - 1] except IndexError: page = len(user_pages) users = user_pages[page - 1] if page else user_pages return await render_template( "system/includes/users/table_body.html", data={ "users": users, "page_size": page_size, "page": page, "pages": len(user_pages), "elements": len(matched_users), }, ) else: return await render_template( "system/users.html", data={"tokens": IN_MEMORY_DB["TOKENS"]} ) @blueprint.route("/delete", methods=["POST"]) @blueprint.route("/", methods=["DELETE"]) @acl("system") async def delete_user(user_id: str | None = None): if request.method == "POST": user_ids = request.form_parsed.get("id") try: async with ClusterLock("users", current_app): for user_id in ensure_list(user_ids): await components.users.delete(user_id=user_id) except ValidationError as e: return validation_error(e.errors()) except ValueError as e: name, message = e.args return validation_error([{"loc": [name], "msg": message}]) return trigger_notification( level="success", response_code=204, title="User removed", message=f"{len(ensure_list(user_ids))} user{'s' if len(ensure_list(user_ids)) > 1 else ''} removed", ) @blueprint.route("//credential/", methods=["PATCH"]) @acl("system") async def patch_user_credential(user_id: str, hex_id: str): try: async with ClusterLock("users", current_app): await components.users.patch_credential( user_id=user_id, hex_id=hex_id, data=request.form_parsed, ) except ValidationError as e: return validation_error(e.errors()) except ValueError as e: name, message = e.args return validation_error([{"loc": [name], "msg": message}]) return trigger_notification( level="success", response_code=204, title="Credential modified", message="Credential was modified", ) @blueprint.route("//credential/", methods=["DELETE"]) @acl("system") async def delete_user_credential(user_id: str, hex_id: str): try: async with ClusterLock("users", current_app): await components.users.delete_credential( user_id=user_id, hex_id=hex_id, ) except ValidationError as e: return validation_error(e.errors()) return trigger_notification( level="success", response_code=204, title="Credential deleted", message="Credential was removed", ) @blueprint.route("/patch", methods=["POST"]) @blueprint.route("/", methods=["PATCH"]) @acl("system") async def patch_user(user_id: str | None = None): try: if not user_id: user_id = request.form_parsed.get("id") async with ClusterLock("users", current_app): await components.users.patch(user_id=user_id, data=request.form_parsed) await components.users.patch_profile( user_id=user_id, data=request.form_parsed.get("profile", {}) ) except ValidationError as e: return validation_error(e.errors()) except ValueError as e: name, message = e.args return validation_error([{"loc": [name], "msg": message}]) IN_MEMORY_DB["SESSION_VALIDATED"].pop(user_id, None) return trigger_notification( level="success", response_code=204, title="User modified", message=f"User was updated", )