mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-10-27 08:14:30 -04:00
* update router * update caddy file * setup depends in docker-fole * make changes for serving on subpath * set dev config * fix router signups * consolidate links * backup-functionality to dashboard * new user card * consolidate theme into profile * fix theme tests * fix pg tests * fix pg tests * remove unused import * mobile margin Co-authored-by: hay-kot <hay-kot@pm.me>
160 lines
5.0 KiB
Python
160 lines
5.0 KiB
Python
from shutil import copyfileobj
|
|
|
|
from fastapi import APIRouter, Depends, File, Form, HTTPException, status
|
|
from fastapi.datastructures import UploadFile
|
|
from mealie.core.root_logger import get_logger
|
|
from mealie.db.database import db
|
|
from mealie.db.db_setup import generate_session
|
|
from mealie.routes.deps import get_current_user
|
|
from mealie.schema.recipe import Recipe, RecipeAsset, RecipeURLIn
|
|
from mealie.services.events import create_recipe_event
|
|
from mealie.services.image.image import scrape_image, write_image
|
|
from mealie.services.recipe.media import check_assets, delete_assets
|
|
from mealie.services.scraper.scraper import create_from_url
|
|
from slugify import slugify
|
|
from sqlalchemy.orm.session import Session
|
|
|
|
router = APIRouter(prefix="/api/recipes", tags=["Recipe CRUD"])
|
|
logger = get_logger()
|
|
|
|
|
|
@router.post("/create", status_code=201, response_model=str)
|
|
def create_from_json(
|
|
data: Recipe,
|
|
session: Session = Depends(generate_session),
|
|
current_user=Depends(get_current_user),
|
|
) -> str:
|
|
""" 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)
|
|
|
|
return recipe.slug
|
|
|
|
|
|
@router.post("/create-url", status_code=201, response_model=str)
|
|
def parse_recipe_url(
|
|
url: RecipeURLIn,
|
|
session: Session = Depends(generate_session),
|
|
current_user=Depends(get_current_user),
|
|
):
|
|
""" Takes in a URL and attempts to scrape data and load it into the database """
|
|
|
|
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)
|
|
|
|
return recipe.slug
|
|
|
|
|
|
@router.get("/{recipe_slug}", response_model=Recipe)
|
|
def get_recipe(recipe_slug: str, session: Session = Depends(generate_session)):
|
|
""" Takes in a recipe slug, returns all data for a recipe """
|
|
|
|
return db.recipes.get(session, recipe_slug)
|
|
|
|
|
|
@router.put("/{recipe_slug}")
|
|
def update_recipe(
|
|
recipe_slug: str,
|
|
data: Recipe,
|
|
session: Session = Depends(generate_session),
|
|
current_user=Depends(get_current_user),
|
|
):
|
|
""" 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)
|
|
|
|
return recipe
|
|
|
|
|
|
@router.patch("/{recipe_slug}")
|
|
def patch_recipe(
|
|
recipe_slug: str,
|
|
data: Recipe,
|
|
session: Session = Depends(generate_session),
|
|
current_user=Depends(get_current_user),
|
|
):
|
|
""" Updates a recipe by existing slug and data. """
|
|
|
|
recipe: Recipe = db.recipes.patch(
|
|
session, recipe_slug, new_data=data.dict(exclude_unset=True, exclude_defaults=True)
|
|
)
|
|
|
|
check_assets(original_slug=recipe_slug, recipe=recipe)
|
|
|
|
return recipe
|
|
|
|
|
|
@router.delete("/{recipe_slug}")
|
|
def delete_recipe(
|
|
recipe_slug: str,
|
|
session: Session = Depends(generate_session),
|
|
current_user=Depends(get_current_user),
|
|
):
|
|
""" Deletes a recipe by slug """
|
|
|
|
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)
|
|
return recipe
|
|
except Exception:
|
|
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
|
|
|
|
|
@router.put("/{recipe_slug}/image")
|
|
def update_recipe_image(
|
|
recipe_slug: str,
|
|
image: bytes = File(...),
|
|
extension: str = Form(...),
|
|
session: Session = Depends(generate_session),
|
|
current_user=Depends(get_current_user),
|
|
):
|
|
""" Removes an existing image and replaces it with the incoming file. """
|
|
response = write_image(recipe_slug, image, extension)
|
|
db.recipes.update_image(session, recipe_slug, extension)
|
|
|
|
return response
|
|
|
|
|
|
@router.post("/{recipe_slug}/image")
|
|
def scrape_image_url(
|
|
recipe_slug: str,
|
|
url: RecipeURLIn,
|
|
current_user=Depends(get_current_user),
|
|
):
|
|
""" Removes an existing image and replaces it with the incoming file. """
|
|
|
|
scrape_image(url.url, recipe_slug)
|
|
|
|
|
|
@router.post("/{recipe_slug}/assets", response_model=RecipeAsset)
|
|
def upload_recipe_asset(
|
|
recipe_slug: str,
|
|
name: str = Form(...),
|
|
icon: str = Form(...),
|
|
extension: str = Form(...),
|
|
file: UploadFile = File(...),
|
|
session: Session = Depends(generate_session),
|
|
current_user=Depends(get_current_user),
|
|
):
|
|
""" Upload a file to store as a recipe asset """
|
|
file_name = slugify(name) + "." + extension
|
|
asset_in = RecipeAsset(name=name, icon=icon, file_name=file_name)
|
|
dest = Recipe(slug=recipe_slug).asset_dir.joinpath(file_name)
|
|
|
|
with dest.open("wb") as buffer:
|
|
copyfileobj(file.file, buffer)
|
|
|
|
if not dest.is_file():
|
|
raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
|
|
recipe: Recipe = db.recipes.get(session, recipe_slug)
|
|
recipe.assets.append(asset_in)
|
|
db.recipes.update(session, recipe_slug, recipe.dict())
|
|
return asset_in
|