Files
allmende-payment-system/src/allmende_payment_system/api/admin.py

376 lines
11 KiB
Python

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
)