mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-12-29 05:25:30 -05:00
fix: Refactor Recipe Zip File Flow (#6170)
This commit is contained in:
@@ -1,24 +1,11 @@
|
||||
from shutil import rmtree
|
||||
from zipfile import ZipFile
|
||||
|
||||
from fastapi import (
|
||||
HTTPException,
|
||||
)
|
||||
from starlette.background import BackgroundTask
|
||||
from starlette.responses import FileResponse
|
||||
|
||||
from mealie.core.dependencies import (
|
||||
get_temporary_path,
|
||||
get_temporary_zip_path,
|
||||
validate_recipe_token,
|
||||
)
|
||||
from mealie.core.security import create_recipe_slug_token
|
||||
from mealie.core.dependencies import get_temporary_path
|
||||
from mealie.routes._base import controller
|
||||
from mealie.routes._base.routers import UserAPIRouter
|
||||
from mealie.schema.recipe import Recipe, RecipeImageTypes
|
||||
from mealie.schema.recipe.request_helpers import (
|
||||
RecipeZipTokenResponse,
|
||||
)
|
||||
from mealie.services.recipe.template_service import TemplateService
|
||||
|
||||
from ._base import BaseRecipeController, FormatResponse
|
||||
@@ -35,11 +22,6 @@ class RecipeExportController(BaseRecipeController):
|
||||
def get_recipe_formats_and_templates(self):
|
||||
return TemplateService().templates
|
||||
|
||||
@router.post("/{slug}/exports", response_model=RecipeZipTokenResponse)
|
||||
def get_recipe_zip_token(self, slug: str):
|
||||
"""Generates a recipe zip token to be used to download a recipe as a zip file"""
|
||||
return RecipeZipTokenResponse(token=create_recipe_slug_token(slug))
|
||||
|
||||
@router.get("/{slug}/exports", response_class=FileResponse)
|
||||
def get_recipe_as_format(self, slug: str, template_name: str):
|
||||
"""
|
||||
@@ -53,24 +35,3 @@ class RecipeExportController(BaseRecipeController):
|
||||
recipe = self.mixins.get_one(slug)
|
||||
file = self.service.render_template(recipe, temp_path, template_name)
|
||||
return FileResponse(file, background=BackgroundTask(rmtree, temp_path))
|
||||
|
||||
@router.get("/{slug}/exports/zip")
|
||||
def get_recipe_as_zip(self, slug: str, token: str):
|
||||
"""Get a Recipe and Its Original Image as a Zip File"""
|
||||
with get_temporary_zip_path(auto_unlink=False) as temp_path:
|
||||
validated_slug = validate_recipe_token(token)
|
||||
|
||||
if validated_slug != slug:
|
||||
raise HTTPException(status_code=400, detail="Invalid Slug")
|
||||
|
||||
recipe: Recipe = self.mixins.get_one(validated_slug)
|
||||
image_asset = recipe.image_dir.joinpath(RecipeImageTypes.original.value)
|
||||
with ZipFile(temp_path, "w") as myzip:
|
||||
myzip.writestr(f"{slug}.json", recipe.model_dump_json())
|
||||
|
||||
if image_asset.is_file():
|
||||
myzip.write(image_asset, arcname=image_asset.name)
|
||||
|
||||
return FileResponse(
|
||||
temp_path, filename=f"{recipe.slug}.zip", background=BackgroundTask(temp_path.unlink, missing_ok=True)
|
||||
)
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
from zipfile import ZipFile
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from pydantic import UUID4
|
||||
from sqlalchemy.orm.session import Session
|
||||
from starlette.background import BackgroundTask
|
||||
from starlette.responses import FileResponse
|
||||
|
||||
from mealie.core.dependencies import get_temporary_zip_path
|
||||
from mealie.core.root_logger import get_logger
|
||||
from mealie.db.db_setup import generate_session
|
||||
from mealie.repos.all_repositories import get_repositories
|
||||
from mealie.schema.recipe import Recipe
|
||||
from mealie.schema.recipe.recipe_image_types import RecipeImageTypes
|
||||
from mealie.schema.response import ErrorResponse
|
||||
|
||||
router = APIRouter()
|
||||
@@ -14,7 +20,7 @@ logger = get_logger()
|
||||
|
||||
|
||||
@router.get("/shared/{token_id}", response_model=Recipe)
|
||||
def get_shared_recipe(token_id: UUID4, session: Session = Depends(generate_session)):
|
||||
def get_shared_recipe(token_id: UUID4, session: Session = Depends(generate_session)) -> Recipe:
|
||||
db = get_repositories(session, group_id=None, household_id=None)
|
||||
|
||||
token_summary = db.recipe_share_tokens.get_one(token_id)
|
||||
@@ -31,3 +37,22 @@ def get_shared_recipe(token_id: UUID4, session: Session = Depends(generate_sessi
|
||||
raise HTTPException(status_code=404, detail=ErrorResponse.respond("Token Not Found"))
|
||||
|
||||
return token_summary.recipe
|
||||
|
||||
|
||||
@router.get("/shared/{token_id}/zip", response_class=FileResponse)
|
||||
def get_shared_recipe_as_zip(token_id: UUID4, session: Session = Depends(generate_session)) -> FileResponse:
|
||||
"""Get a recipe and its original image as a Zip file"""
|
||||
|
||||
recipe = get_shared_recipe(token_id=token_id, session=session)
|
||||
image_asset = recipe.image_dir.joinpath(RecipeImageTypes.original.value)
|
||||
|
||||
with get_temporary_zip_path(auto_unlink=False) as temp_path:
|
||||
with ZipFile(temp_path, "w") as myzip:
|
||||
myzip.writestr(f"{recipe.slug}.json", recipe.model_dump_json())
|
||||
|
||||
if image_asset.is_file():
|
||||
myzip.write(image_asset, arcname=image_asset.name)
|
||||
|
||||
return FileResponse(
|
||||
temp_path, filename=f"{recipe.slug}.zip", background=BackgroundTask(temp_path.unlink, missing_ok=True)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user