from typing import Annotated from fastapi import APIRouter, File, Form, HTTPException, Request from sqlalchemy import select from sqlalchemy.exc import IntegrityError from starlette import status from starlette.responses import RedirectResponse from allmende_payment_system import types from allmende_payment_system.api.dependencies import SessionDep, UserDep from allmende_payment_system.models import ( Account, Area, Permission, Product, Transaction, User, UserGroup, ) from allmende_payment_system.tools import get_jinja_renderer admin_router = APIRouter(prefix="/admin") # USERS @admin_router.get("/users") async def user_list(request: Request, session: SessionDep, user: UserDep): if not user.has_permission("user", "edit"): raise HTTPException(status_code=403, detail="Insufficient permissions") users = session.scalars(select(User)).all() groups = session.scalars(select(UserGroup)).all() templates = get_jinja_renderer() return templates.TemplateResponse( "users.html.jinja", context={"request": request, "users": users, "all_groups": groups}, ) @admin_router.post("/users/{user_id}/add_group") async def user_add_group( request: Request, session: SessionDep, loggend_in_user: UserDep, user_id: int, group_id: Annotated[int, Form()], ): if not loggend_in_user.has_permission("user", "edit"): raise HTTPException(status_code=403, detail="Insufficient permissions") group = session.execute( select(UserGroup).where(UserGroup.id == group_id) ).scalar_one() user = session.execute(select(User).where(User.id == user_id)).scalar_one() user.user_groups.append(group) return RedirectResponse(url="/admin/users", status_code=status.HTTP_303_SEE_OTHER) @admin_router.get("/users/{user_id}/remove_group/{group_id}") async def user_remove_group( request: Request, session: SessionDep, loggend_in_user: UserDep, user_id: int, group_id: int, ): if not loggend_in_user.has_permission("user", "edit"): raise HTTPException(status_code=403, detail="Insufficient permissions") group = session.execute( select(UserGroup).where(UserGroup.id == group_id) ).scalar_one() user = session.execute(select(User).where(User.id == user_id)).scalar_one() print(user) user.user_groups.remove(group) return RedirectResponse(url="/admin/users", status_code=status.HTTP_303_SEE_OTHER) # GROUPS @admin_router.get("/groups") async def group_list(request: Request, session: SessionDep, user: UserDep): if not user.has_permission("user", "edit"): raise HTTPException(status_code=403, detail="Insufficient permissions") groups = session.scalars(select(UserGroup)).all() templates = get_jinja_renderer() return templates.TemplateResponse( "groups.html.jinja", context={"request": request, "groups": groups}, ) @admin_router.post("/groups/{group_id}/add_permission") async def group_add_permission( request: Request, session: SessionDep, user: UserDep, group_id: int, permission: Annotated[str, Form()], ): if not user.has_permission("user", "edit"): raise HTTPException(status_code=403, detail="Insufficient permissions") scope_action = permission.split(":") if len(scope_action) != 2: raise HTTPException( status_code=400, detail="Permission must be in the format 'scope:action'" ) permission = Permission(scope=scope_action[0], action=scope_action[1]) group = session.execute( select(UserGroup).where(UserGroup.id == group_id) ).scalar_one() session.add(permission) group.permissions.append(permission) return RedirectResponse(url="/admin/groups", status_code=status.HTTP_303_SEE_OTHER) @admin_router.get("/groups/{group_id}/remove_permission/{permission_id}") async def group_remove_permission( request: Request, session: SessionDep, user: UserDep, group_id: int, permission_id: int, ): if not user.has_permission("user", "edit"): raise HTTPException(status_code=403, detail="Insufficient permissions") permission = session.execute( select(Permission).where(Permission.id == permission_id) ).scalar_one() group = session.execute( select(UserGroup).where(UserGroup.id == group_id) ).scalar_one() group.permissions.remove(permission) session.delete(permission) return RedirectResponse(url="/admin/groups", status_code=status.HTTP_303_SEE_OTHER) @admin_router.post("/groups/create") async def create_group( request: Request, session: SessionDep, user: UserDep, group_data: Annotated[types.UserGroup, Form()], ): if not user.has_permission("user", "edit"): raise HTTPException(status_code=403, detail="Insufficient permissions") group = UserGroup(name=group_data.name, description=group_data.description) session.add(group) return RedirectResponse(url="/admin/groups", status_code=status.HTTP_303_SEE_OTHER) @admin_router.get("/groups/{group_id}/delete") async def delete_group( request: Request, session: SessionDep, user: UserDep, group_id: int, ): if not user.has_permission("user", "edit"): raise HTTPException(status_code=403, detail="Insufficient permissions") group = session.execute( select(UserGroup).where(UserGroup.id == group_id) ).scalar_one() session.delete(group) return RedirectResponse(url="/admin/groups", status_code=status.HTTP_303_SEE_OTHER) # PRODUCTS @admin_router.get("/products") async def get_products( request: Request, session: SessionDep, user: UserDep, ): products = session.scalars( select(Product).order_by(Product.area_id, Product.name) ).all() templates = get_jinja_renderer() return templates.TemplateResponse( "products.html.jinja", context={"request": request, "products": products}, ) @admin_router.get("/products/edit/{product_id}") async def edit_product_get( request: Request, session: SessionDep, user: UserDep, product_id: int ): product = session.execute(select(Product).where(Product.id == product_id)).scalar() areas = session.scalars(select(Area)).all() templates = get_jinja_renderer() return templates.TemplateResponse( "product_edit.html.jinja", context={ "request": request, "product": product, "edit_mode": True, "areas": areas, }, ) @admin_router.post("/products/edit/{product_id}") async def edit_product_post( request: Request, session: SessionDep, user: UserDep, product_id: int, product_data: Annotated[types.Product, Form()], ): if not user.has_permission("product", "edit"): raise HTTPException(status_code=403, detail="Insufficient permissions") product = session.execute( select(Product).where(Product.id == product_id) ).scalar_one() for field_name, data in product_data.model_dump().items(): setattr(product, field_name, data) session.flush() return RedirectResponse( url="/admin/products", status_code=status.HTTP_303_SEE_OTHER ) @admin_router.get("/products/new") async def new_product_get( request: Request, session: SessionDep, user: UserDep, ): if not user.has_permission("product", "edit"): raise HTTPException(status_code=403, detail="Insufficient permissions") areas = session.scalars(select(Area)).all() templates = get_jinja_renderer() return templates.TemplateResponse( "product_edit.html.jinja", context={"request": request, "edit_mode": False, "areas": areas}, ) @admin_router.post("/products/new") async def new_product_post( request: Request, session: SessionDep, user: UserDep, product_data: Annotated[types.Product, Form()], # product_image: Annotated[bytes, File()] ): if not user.has_permission("product", "edit"): raise HTTPException(status_code=403, detail="Insufficient permissions") # print(len(product_image)) product = Product() for field_name, data in product_data.model_dump().items(): setattr(product, field_name, data) session.add(product) return RedirectResponse( url="/admin/products", status_code=status.HTTP_303_SEE_OTHER ) @admin_router.get("/accounts") async def get_accounts( request: Request, session: SessionDep, user: UserDep, ): if not user.has_permission("account", "edit"): raise HTTPException(status_code=403, detail="Insufficient permissions") templates = get_jinja_renderer() accounts = session.scalars(select(Account)).all() users = session.scalars(select(User)).all() return templates.TemplateResponse( "accounts.html.jinja", context={"request": request, "accounts": accounts, "users": users}, ) @admin_router.post("/accounts/new") async def create_account( request: Request, session: SessionDep, user: UserDep, account_name: Annotated[str, Form()], ): if not user.has_permission("account", "edit"): raise HTTPException(status_code=403, detail="Insufficient permissions") account = Account(name=account_name) session.add(account) try: session.flush() except IntegrityError as e: session.rollback() raise HTTPException( status_code=400, detail="Account with this name already exists" ) from e return RedirectResponse( url="/admin/accounts", status_code=status.HTTP_303_SEE_OTHER ) @admin_router.post("/accounts/{account_id}/add_user") async def add_user_to_account( request: Request, session: SessionDep, user: UserDep, account_id: int, user_id: Annotated[int, Form()], ): if not user.has_permission("account", "edit"): raise HTTPException(status_code=403, detail="Insufficient permissions") account = session.execute( select(Account).where(Account.id == account_id) ).scalar_one() user_to_add = session.execute(select(User).where(User.id == user_id)).scalar_one() account.users.append(user_to_add) return RedirectResponse( url="/admin/accounts", status_code=status.HTTP_303_SEE_OTHER ) @admin_router.post("/accounts/{account_id}/add_balance") async def add_balance_to_account( request: Request, session: SessionDep, user: UserDep, account_id: int, amount: Annotated[float, Form()], ): if not user.has_permission("account", "edit"): raise HTTPException(status_code=403, detail="Insufficient permissions") account = session.execute( select(Account).where(Account.id == account_id) ).scalar_one() account.transactions.append(Transaction(type="deposit", total_amount=amount)) return RedirectResponse( url="/admin/accounts", status_code=status.HTTP_303_SEE_OTHER )