diff --git a/mealie/routes/backup_routes.py b/mealie/routes/backup_routes.py index 1e0917ecf..4fef48f8d 100644 --- a/mealie/routes/backup_routes.py +++ b/mealie/routes/backup_routes.py @@ -2,7 +2,7 @@ import operator import shutil from pathlib import Path -from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status +from fastapi import APIRouter, BackgroundTasks, Depends, File, HTTPException, UploadFile, status from mealie.core.config import app_dirs from mealie.core.root_logger import get_logger from mealie.core.security import create_file_token @@ -33,7 +33,7 @@ def available_imports(): @router.post("/export/database", status_code=status.HTTP_201_CREATED) -def export_database(data: BackupJob, session: Session = Depends(generate_session)): +def export_database(background_tasks: BackgroundTasks, data: BackupJob, session: Session = Depends(generate_session)): """Generates a backup of the recipe database in json format.""" try: export_path = backup_all( @@ -47,7 +47,9 @@ def export_database(data: BackupJob, session: Session = Depends(generate_session export_users=data.options.users, export_groups=data.options.groups, ) - create_backup_event("Database Backup", f"Manual Backup Created '{Path(export_path).name}'", session) + background_tasks.add_task( + create_backup_event, "Database Backup", f"Manual Backup Created '{Path(export_path).name}'", session + ) return {"export_path": export_path} except Exception as e: logger.error(e) @@ -75,7 +77,12 @@ async def download_backup_file(file_name: str): @router.post("/{file_name}/import", status_code=status.HTTP_200_OK) -def import_database(file_name: str, import_data: ImportJob, session: Session = Depends(generate_session)): +def import_database( + background_tasks: BackgroundTasks, + file_name: str, + import_data: ImportJob, + session: Session = Depends(generate_session), +): """ Import a database backup file generated from Mealie. """ db_import = imports.import_database( @@ -90,7 +97,7 @@ def import_database(file_name: str, import_data: ImportJob, session: Session = D force_import=import_data.force, rebase=import_data.rebase, ) - create_backup_event("Database Restore", f"Restore File: {file_name}", session) + background_tasks.add_task(create_backup_event, "Database Restore", f"Restore File: {file_name}", session) return db_import diff --git a/mealie/routes/groups/crud.py b/mealie/routes/groups/crud.py index b4a43ed51..0a19c1aec 100644 --- a/mealie/routes/groups/crud.py +++ b/mealie/routes/groups/crud.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Depends, HTTPException, status +from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, status from mealie.db.database import db from mealie.db.db_setup import generate_session from mealie.routes.deps import get_current_user @@ -32,6 +32,7 @@ async def get_current_user_group( @router.post("", status_code=status.HTTP_201_CREATED) async def create_group( + background_tasks: BackgroundTasks, group_data: GroupBase, current_user=Depends(get_current_user), session: Session = Depends(generate_session), @@ -40,7 +41,7 @@ async def create_group( try: db.groups.create(session, group_data.dict()) - create_group_event("Group Created", f"'{group_data.name}' created") + background_tasks.add_task(create_group_event, "Group Created", f"'{group_data.name}' created", session) except Exception: raise HTTPException(status.HTTP_400_BAD_REQUEST) @@ -58,7 +59,10 @@ async def update_group_data( @router.delete("/{id}") async def delete_user_group( - id: int, current_user=Depends(get_current_user), session: Session = Depends(generate_session) + background_tasks: BackgroundTasks, + id: int, + current_user: UserInDB = Depends(get_current_user), + session: Session = Depends(generate_session), ): """ Removes a user group from the database """ @@ -73,5 +77,8 @@ async def delete_user_group( if group.users != []: raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="GROUP_WITH_USERS") - create_group_event("Group Deleted", f"'{group.name}' Deleted") + background_tasks.add_task( + create_group_event, "Group Deleted", f"'{group.name}' deleted by {current_user.full_name}", session + ) + db.groups.delete(session, id) diff --git a/mealie/routes/mealplans/crud.py b/mealie/routes/mealplans/crud.py index b3fe00f5a..2fc703912 100644 --- a/mealie/routes/mealplans/crud.py +++ b/mealie/routes/mealplans/crud.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Depends, HTTPException, status +from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, status from mealie.db.database import db from mealie.db.db_setup import generate_session from mealie.routes.deps import get_current_user @@ -25,16 +25,22 @@ def get_all_meals( @router.post("/create", status_code=status.HTTP_201_CREATED) def create_meal_plan( - data: MealPlanIn, session: Session = Depends(generate_session), current_user: UserInDB = Depends(get_current_user) + background_tasks: BackgroundTasks, + data: MealPlanIn, + session: Session = Depends(generate_session), + current_user: UserInDB = Depends(get_current_user), ): """ Creates a meal plan database entry """ processed_plan = process_meals(session, data) - create_group_event("Meal Plan Created", f"Mealplan Created for '{current_user.group}'") + background_tasks.add_task( + create_group_event, "Meal Plan Created", f"Mealplan Created for '{current_user.group}'", session=session + ) return db.meals.create(session, processed_plan.dict()) @router.put("/{plan_id}") def update_meal_plan( + background_tasks: BackgroundTasks, plan_id: str, meal_plan: MealPlanIn, session: Session = Depends(generate_session), @@ -45,13 +51,16 @@ def update_meal_plan( processed_plan = MealPlanInDB(uid=plan_id, **processed_plan.dict()) try: db.meals.update(session, plan_id, processed_plan.dict()) - create_group_event("Meal Plan Updated", f"Mealplan Updated for '{current_user.group}'") + background_tasks.add_task( + create_group_event, "Meal Plan Updated", f"Mealplan Updated for '{current_user.group}'", session=session + ) except Exception: raise HTTPException(status.HTTP_400_BAD_REQUEST) @router.delete("/{plan_id}") def delete_meal_plan( + background_tasks: BackgroundTasks, plan_id, session: Session = Depends(generate_session), current_user: UserInDB = Depends(get_current_user), @@ -60,7 +69,9 @@ def delete_meal_plan( try: db.meals.delete(session, plan_id) - create_group_event("Meal Plan Deleted", f"Mealplan Deleted for '{current_user.group}'") + background_tasks.add_task( + create_group_event, "Meal Plan Deleted", f"Mealplan Deleted for '{current_user.group}'", session=session + ) except Exception: raise HTTPException(status.HTTP_400_BAD_REQUEST) diff --git a/mealie/routes/recipe/recipe_crud_routes.py b/mealie/routes/recipe/recipe_crud_routes.py index 110393050..aa7918431 100644 --- a/mealie/routes/recipe/recipe_crud_routes.py +++ b/mealie/routes/recipe/recipe_crud_routes.py @@ -1,6 +1,6 @@ from shutil import copyfileobj -from fastapi import APIRouter, Depends, File, Form, HTTPException, status +from fastapi import APIRouter, BackgroundTasks, Depends, File, Form, HTTPException, status from fastapi.datastructures import UploadFile from mealie.core.root_logger import get_logger from mealie.db.database import db @@ -20,6 +20,7 @@ logger = get_logger() @router.post("/create", status_code=201, response_model=str) def create_from_json( + background_tasks: BackgroundTasks, data: Recipe, session: Session = Depends(generate_session), current_user=Depends(get_current_user), @@ -27,13 +28,20 @@ def create_from_json( """ Takes in a JSON string and loads data into the database as a new entry""" recipe: Recipe = db.recipes.create(session, data.dict()) - create_recipe_event("Recipe Created", f"Recipe '{recipe.name}' created", session=session) + background_tasks.add_task( + create_recipe_event, + "Recipe Created (URL)", + f"'{recipe.name}' by {current_user.full_name}", + session=session, + attachment=recipe.image_dir.joinpath("min-original.webp"), + ) return recipe.slug @router.post("/create-url", status_code=201, response_model=str) def parse_recipe_url( + background_tasks: BackgroundTasks, url: RecipeURLIn, session: Session = Depends(generate_session), current_user=Depends(get_current_user), @@ -42,7 +50,14 @@ def parse_recipe_url( recipe = create_from_url(url.url) recipe: Recipe = db.recipes.create(session, recipe.dict()) - create_recipe_event("Recipe Created (URL)", f"'{recipe.name}' by {current_user.full_name}", session=session) + + background_tasks.add_task( + create_recipe_event, + "Recipe Created (URL)", + f"'{recipe.name}' by {current_user.full_name}", + session=session, + attachment=recipe.image_dir.joinpath("min-original.webp"), + ) return recipe.slug @@ -64,7 +79,6 @@ def update_recipe( """ Updates a recipe by existing slug and data. """ recipe: Recipe = db.recipes.update(session, recipe_slug, data.dict()) - print(recipe.assets) check_assets(original_slug=recipe_slug, recipe=recipe) @@ -91,6 +105,7 @@ def patch_recipe( @router.delete("/{recipe_slug}") def delete_recipe( + background_tasks: BackgroundTasks, recipe_slug: str, session: Session = Depends(generate_session), current_user=Depends(get_current_user), @@ -100,7 +115,12 @@ def delete_recipe( try: recipe: Recipe = db.recipes.delete(session, recipe_slug) delete_assets(recipe_slug=recipe_slug) - create_recipe_event("Recipe Deleted", f"'{recipe.name}' deleted by {current_user.full_name}", session=session) + background_tasks.add_task( + create_recipe_event, + "Recipe Deleted", + f"'{recipe.name}' deleted by {current_user.full_name}", + session=session, + ) return recipe except Exception: raise HTTPException(status.HTTP_400_BAD_REQUEST) diff --git a/mealie/routes/users/auth.py b/mealie/routes/users/auth.py index 0780556f7..bfcb02982 100644 --- a/mealie/routes/users/auth.py +++ b/mealie/routes/users/auth.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Depends, Request, status +from fastapi import APIRouter, BackgroundTasks, Depends, Request, status from fastapi.exceptions import HTTPException from fastapi.security import OAuth2PasswordRequestForm from mealie.core import security @@ -15,6 +15,7 @@ router = APIRouter(prefix="/api/auth", tags=["Authentication"]) @router.post("/token/long") @router.post("/token") def get_token( + background_tasks: BackgroundTasks, request: Request, data: OAuth2PasswordRequestForm = Depends(), session: Session = Depends(generate_session), @@ -25,7 +26,9 @@ def get_token( user = authenticate_user(session, email, password) if not user: - create_user_event("Failed Login", f"Username: {email}, Source IP: '{request.client.host}'") + background_tasks.add_task( + create_user_event, "Failed Login", f"Username: {email}, Source IP: '{request.client.host}'" + ) raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, headers={"WWW-Authenticate": "Bearer"}, diff --git a/mealie/routes/users/crud.py b/mealie/routes/users/crud.py index ab0a2ea3b..8fe60bac5 100644 --- a/mealie/routes/users/crud.py +++ b/mealie/routes/users/crud.py @@ -1,6 +1,6 @@ import shutil -from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status +from fastapi import APIRouter, BackgroundTasks, Depends, File, HTTPException, UploadFile, status from fastapi.responses import FileResponse from mealie.core import security from mealie.core.config import app_dirs, settings @@ -17,13 +17,16 @@ router = APIRouter(prefix="/api/users", tags=["Users"]) @router.post("", response_model=UserOut, status_code=201) async def create_user( + background_tasks: BackgroundTasks, new_user: UserIn, current_user=Depends(get_current_user), session: Session = Depends(generate_session), ): new_user.password = get_password_hash(new_user.password) - create_user_event("User Created", f"Created by {current_user.full_name}", session=session) + background_tasks.add_task( + create_user_event, "User Created", f"Created by {current_user.full_name}", session=session + ) return db.users.create(session, new_user.dict()) @@ -138,6 +141,7 @@ async def update_password( @router.delete("/{id}") async def delete_user( + background_tasks: BackgroundTasks, id: int, current_user: UserInDB = Depends(get_current_user), session: Session = Depends(generate_session), @@ -150,6 +154,6 @@ async def delete_user( if current_user.id == id or current_user.admin: try: db.users.delete(session, id) - create_user_event("User Deleted", f"User ID: {id}", session=session) + background_tasks.add_task(create_user_event, "User Deleted", f"User ID: {id}", session=session) except Exception: raise HTTPException(status.HTTP_400_BAD_REQUEST) diff --git a/mealie/routes/users/sign_up.py b/mealie/routes/users/sign_up.py index c0cb386b6..ef81b4df0 100644 --- a/mealie/routes/users/sign_up.py +++ b/mealie/routes/users/sign_up.py @@ -1,6 +1,6 @@ import uuid -from fastapi import APIRouter, Depends, HTTPException, status +from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, status from mealie.core.security import get_password_hash from mealie.db.database import db from mealie.db.db_setup import generate_session @@ -25,6 +25,7 @@ async def get_all_open_sign_ups( @router.post("", response_model=SignUpToken) async def create_user_sign_up_key( + background_tasks: BackgroundTasks, key_data: SignUpIn, current_user: UserInDB = Depends(get_current_user), session: Session = Depends(generate_session), @@ -39,12 +40,16 @@ async def create_user_sign_up_key( "name": key_data.name, "admin": key_data.admin, } - create_user_event("Sign-up Token Created", f"Created by {current_user.full_name}", session=session) + + background_tasks.add_task( + create_user_event, "Sign-up Token Created", f"Created by {current_user.full_name}", session=session + ) return db.sign_ups.create(session, sign_up) @router.post("/{token}") async def create_user_with_token( + background_tasks: BackgroundTasks, token: str, new_user: UserIn, session: Session = Depends(generate_session), @@ -62,7 +67,9 @@ async def create_user_with_token( db.users.create(session, new_user.dict()) # DeleteToken - create_user_event("Sign-up Token Used", f"New User {new_user.full_name}", session=session) + background_tasks.add_task( + create_user_event, "Sign-up Token Used", f"New User {new_user.full_name}", session=session + ) db.sign_ups.delete(session, token) diff --git a/mealie/services/events.py b/mealie/services/events.py index 3ac042ecd..7dd18b72a 100644 --- a/mealie/services/events.py +++ b/mealie/services/events.py @@ -17,7 +17,7 @@ def test_notification(notification_url, event=None) -> bool: post_notifications(event, [notification_url], hard_fail=True) -def post_notifications(event: Event, notification_urls=list[str], hard_fail=False): +def post_notifications(event: Event, notification_urls=list[str], hard_fail=False, attachment=None): asset = apprise.AppriseAsset(async_mode=False) apobj = apprise.Apprise(asset=asset) @@ -27,20 +27,23 @@ def post_notifications(event: Event, notification_urls=list[str], hard_fail=Fals if not status and hard_fail: raise Exception("Apprise URL Add Failed") + print(attachment) + apobj.notify( body=event.text, title=event.title, + attach=str(attachment), ) -def save_event(title, text, category, session: Session): +def save_event(title, text, category, session: Session, attachment=None): event = Event(title=title, text=text, category=category) session = session or create_session() db.events.create(session, event.dict()) notification_objects = db.event_notifications.get(session=session, match_value=True, match_key=category, limit=9999) notification_urls = [x.notification_url for x in notification_objects] - post_notifications(event, notification_urls) + post_notifications(event, notification_urls, attachment=attachment) def create_general_event(title, text, session=None): @@ -48,10 +51,10 @@ def create_general_event(title, text, session=None): save_event(title=title, text=text, category=category, session=session) -def create_recipe_event(title, text, session=None): +def create_recipe_event(title, text, session=None, attachment=None): category = EventCategory.recipe - save_event(title=title, text=text, category=category, session=session) + save_event(title=title, text=text, category=category, session=session, attachment=attachment) def create_backup_event(title, text, session=None):