bug/bug-fixes (#424)
* fix image write/caching * Caddyfile Caching header * more aggressive caching Co-authored-by: hay-kot <hay-kot@pm.me>
This commit is contained in:
parent
eb3d56936e
commit
503fe5cb2e
11 changed files with 44 additions and 34 deletions
|
@ -5,12 +5,18 @@
|
||||||
|
|
||||||
:80 {
|
:80 {
|
||||||
@proxied path /api/* /docs /openapi.json
|
@proxied path /api/* /docs /openapi.json
|
||||||
|
|
||||||
|
@static {
|
||||||
|
file
|
||||||
|
path *.ico *.css *.js *.gif *.jpg *.jpeg *.png *.svg *.woff *.woff2 *.webp
|
||||||
|
}
|
||||||
|
|
||||||
encode gzip zstd
|
encode gzip zstd
|
||||||
uri strip_suffix /
|
uri strip_suffix /
|
||||||
|
|
||||||
# Handles Recipe Images / Assets
|
# Handles Recipe Images / Assets
|
||||||
handle_path /api/media/recipes/* {
|
handle_path /api/media/recipes/* {
|
||||||
|
header @static Cache-Control max-age=31536000
|
||||||
root * /app/data/recipes/
|
root * /app/data/recipes/
|
||||||
file_server
|
file_server
|
||||||
}
|
}
|
||||||
|
@ -20,6 +26,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
handle {
|
handle {
|
||||||
|
header @static Cache-Control max-age=31536000
|
||||||
root * /app/dist
|
root * /app/dist
|
||||||
try_files {path}.html {path} /index.html
|
try_files {path}.html {path} /index.html
|
||||||
file_server
|
file_server
|
||||||
|
|
|
@ -146,16 +146,16 @@ export const recipeAPI = {
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
recipeImage(recipeSlug) {
|
recipeImage(recipeSlug, version = null, key = null) {
|
||||||
return `/api/media/recipes/${recipeSlug}/images/original.webp`;
|
return `/api/media/recipes/${recipeSlug}/images/original.webp?&rnd=${key}&version=${version}`;
|
||||||
},
|
},
|
||||||
|
|
||||||
recipeSmallImage(recipeSlug) {
|
recipeSmallImage(recipeSlug, version = null, key = null) {
|
||||||
return `/api/media/recipes/${recipeSlug}/images/min-original.webp`;
|
return `/api/media/recipes/${recipeSlug}/images/min-original.webp?&rnd=${key}&version=${version}`;
|
||||||
},
|
},
|
||||||
|
|
||||||
recipeTinyImage(recipeSlug) {
|
recipeTinyImage(recipeSlug, version = null, key = null) {
|
||||||
return `/api/media/recipes/${recipeSlug}/images/tiny-original.webp`;
|
return `/api/media/recipes/${recipeSlug}/images/tiny-original.webp?&rnd=${key}&version=${version}`;
|
||||||
},
|
},
|
||||||
|
|
||||||
recipeAssetPath(recipeSlug, assetName) {
|
recipeAssetPath(recipeSlug, assetName) {
|
||||||
|
|
|
@ -209,10 +209,6 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
getImage(image) {
|
|
||||||
return api.recipes.recipeSmallImage(image);
|
|
||||||
},
|
|
||||||
|
|
||||||
formatDate(date) {
|
formatDate(date) {
|
||||||
if (!date) return null;
|
if (!date) return null;
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,9 @@ export default {
|
||||||
slug: {
|
slug: {
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
|
imageVersion: {
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
height: {
|
height: {
|
||||||
default: 200,
|
default: 200,
|
||||||
},
|
},
|
||||||
|
@ -65,14 +68,14 @@ export default {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getImage(image) {
|
getImage(slug) {
|
||||||
switch (this.imageSize) {
|
switch (this.imageSize) {
|
||||||
case "tiny":
|
case "tiny":
|
||||||
return api.recipes.recipeTinyImage(image);
|
return api.recipes.recipeTinyImage(slug, this.imageVersion);
|
||||||
case "small":
|
case "small":
|
||||||
return api.recipes.recipeSmallImage(image);
|
return api.recipes.recipeSmallImage(slug, this.imageVersion);
|
||||||
case "large":
|
case "large":
|
||||||
return api.recipes.recipeImage(image);
|
return api.recipes.recipeImage(slug, this.imageVersion);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -59,8 +59,8 @@ export default {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getImage(image) {
|
getImage(slug) {
|
||||||
return api.recipes.recipeSmallImage(image);
|
return api.recipes.recipeSmallImage(slug, this.image);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
@click="$emit('click')"
|
@click="$emit('click')"
|
||||||
min-height="275"
|
min-height="275"
|
||||||
>
|
>
|
||||||
<CardImage icon-size="200" :slug="slug">
|
<CardImage icon-size="200" :slug="slug" :image-version="image">
|
||||||
<v-expand-transition v-if="description">
|
<v-expand-transition v-if="description">
|
||||||
<div v-if="hover" class="d-flex transition-fast-in-fast-out secondary v-card--reveal " style="height: 100%;">
|
<div v-if="hover" class="d-flex transition-fast-in-fast-out secondary v-card--reveal " style="height: 100%;">
|
||||||
<v-card-text class="v-card--text-show white--text">
|
<v-card-text class="v-card--text-show white--text">
|
||||||
|
@ -65,8 +65,8 @@ export default {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getImage(image) {
|
getImage(slug) {
|
||||||
return api.recipes.recipeSmallImage(image);
|
return api.recipes.recipeSmallImage(slug, this.image);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -155,9 +155,9 @@ export default {
|
||||||
this.recipeDetails = await api.recipes.requestDetails(this.currentRecipe);
|
this.recipeDetails = await api.recipes.requestDetails(this.currentRecipe);
|
||||||
this.skeleton = false;
|
this.skeleton = false;
|
||||||
},
|
},
|
||||||
getImage(image) {
|
getImage(slug) {
|
||||||
if (image) {
|
if (slug) {
|
||||||
return api.recipes.recipeImage(image) + "?&rnd=" + this.imageKey;
|
return api.recipes.recipeImage(slug, this.imageKey, this.recipeDetails.image);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async deleteRecipe() {
|
async deleteRecipe() {
|
||||||
|
@ -175,7 +175,9 @@ export default {
|
||||||
},
|
},
|
||||||
async saveImage(overrideSuccessMsg = false) {
|
async saveImage(overrideSuccessMsg = false) {
|
||||||
if (this.fileObject) {
|
if (this.fileObject) {
|
||||||
if (api.recipes.updateImage(this.recipeDetails.slug, this.fileObject, overrideSuccessMsg)) {
|
const newVersion = await api.recipes.updateImage(this.recipeDetails.slug, this.fileObject, overrideSuccessMsg);
|
||||||
|
if (newVersion) {
|
||||||
|
this.recipeDetails.image = newVersion.data.version;
|
||||||
this.imageKey += 1;
|
this.imageKey += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -192,6 +194,7 @@ export default {
|
||||||
if (slug != this.recipeDetails.slug) {
|
if (slug != this.recipeDetails.slug) {
|
||||||
this.$router.push(`/recipe/${slug}`);
|
this.$router.push(`/recipe/${slug}`);
|
||||||
}
|
}
|
||||||
|
window.URL.revokeObjectURL(this.getImage(this.recipeDetails.slug));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
printPage() {
|
printPage() {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
|
from random import randint
|
||||||
|
|
||||||
from mealie.db.db_base import BaseDocument
|
from mealie.db.db_base import BaseDocument
|
||||||
from mealie.db.models.event import Event, EventNotification
|
from mealie.db.models.event import Event, EventNotification
|
||||||
|
@ -34,10 +35,10 @@ class _Recipes(BaseDocument):
|
||||||
|
|
||||||
def update_image(self, session: Session, slug: str, extension: str = None) -> str:
|
def update_image(self, session: Session, slug: str, extension: str = None) -> str:
|
||||||
entry: RecipeModel = self._query_one(session, match_value=slug)
|
entry: RecipeModel = self._query_one(session, match_value=slug)
|
||||||
entry.image = f"{slug}.{extension}"
|
entry.image = randint(0, 255)
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
return f"{slug}.{extension}"
|
return entry.image
|
||||||
|
|
||||||
def count_uncategorized(self, session: Session, count=True, override_schema=None) -> int:
|
def count_uncategorized(self, session: Session, count=True, override_schema=None) -> int:
|
||||||
return self._count_attribute(
|
return self._count_attribute(
|
||||||
|
|
|
@ -128,19 +128,18 @@ def delete_recipe(
|
||||||
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
@router.put("/{recipe_slug}/image")
|
@router.put("/{recipe_slug}/image", dependencies=[Depends(get_current_user)])
|
||||||
def update_recipe_image(
|
def update_recipe_image(
|
||||||
recipe_slug: str,
|
recipe_slug: str,
|
||||||
image: bytes = File(...),
|
image: bytes = File(...),
|
||||||
extension: str = Form(...),
|
extension: str = Form(...),
|
||||||
session: Session = Depends(generate_session),
|
session: Session = Depends(generate_session),
|
||||||
current_user=Depends(get_current_user),
|
|
||||||
):
|
):
|
||||||
""" Removes an existing image and replaces it with the incoming file. """
|
""" Removes an existing image and replaces it with the incoming file. """
|
||||||
response = write_image(recipe_slug, image, extension)
|
write_image(recipe_slug, image, extension)
|
||||||
db.recipes.update_image(session, recipe_slug, extension)
|
new_version = db.recipes.update_image(session, recipe_slug, extension)
|
||||||
|
|
||||||
return response
|
return {"image": new_version}
|
||||||
|
|
||||||
|
|
||||||
@router.post("/{recipe_slug}/image")
|
@router.post("/{recipe_slug}/image")
|
||||||
|
|
|
@ -36,7 +36,7 @@ def write_image(recipe_slug: str, file_data: bytes, extension: str) -> Path:
|
||||||
shutil.copyfileobj(file_data, f)
|
shutil.copyfileobj(file_data, f)
|
||||||
|
|
||||||
print(image_path)
|
print(image_path)
|
||||||
minify.minify_image(image_path)
|
minify.minify_image(image_path, force=True)
|
||||||
|
|
||||||
return image_path
|
return image_path
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ def get_image_sizes(org_img: Path, min_img: Path, tiny_img: Path) -> ImageSizes:
|
||||||
return ImageSizes(org=sizeof_fmt(org_img), min=sizeof_fmt(min_img), tiny=sizeof_fmt(tiny_img))
|
return ImageSizes(org=sizeof_fmt(org_img), min=sizeof_fmt(min_img), tiny=sizeof_fmt(tiny_img))
|
||||||
|
|
||||||
|
|
||||||
def minify_image(image_file: Path) -> ImageSizes:
|
def minify_image(image_file: Path, force=False) -> ImageSizes:
|
||||||
"""Minifies an image in it's original file format. Quality is lost
|
"""Minifies an image in it's original file format. Quality is lost
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
@ -39,7 +39,7 @@ def minify_image(image_file: Path) -> ImageSizes:
|
||||||
min_dest = image_file.parent.joinpath("min-original.webp")
|
min_dest = image_file.parent.joinpath("min-original.webp")
|
||||||
tiny_dest = image_file.parent.joinpath("tiny-original.webp")
|
tiny_dest = image_file.parent.joinpath("tiny-original.webp")
|
||||||
|
|
||||||
if min_dest.exists() and tiny_dest.exists() and org_dest.exists():
|
if min_dest.exists() and tiny_dest.exists() and org_dest.exists() and not force:
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
img = Image.open(image_file)
|
img = Image.open(image_file)
|
||||||
|
@ -56,7 +56,8 @@ def minify_image(image_file: Path) -> ImageSizes:
|
||||||
|
|
||||||
cleanup_images = True
|
cleanup_images = True
|
||||||
|
|
||||||
except Exception:
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
shutil.copy(image_file, min_dest)
|
shutil.copy(image_file, min_dest)
|
||||||
shutil.copy(image_file, tiny_dest)
|
shutil.copy(image_file, tiny_dest)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue