mirror of
https://github.com/hay-kot/mealie.git
synced 2025-08-22 14:33:33 -07:00
update router
This commit is contained in:
parent
e13d203524
commit
ed89540fec
19 changed files with 155 additions and 117 deletions
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"python.formatting.provider": "black",
|
"python.formatting.provider": "black",
|
||||||
"python.pythonPath": ".venv/bin/python3.9",
|
"python.pythonPath": ".venv/bin/python3.9",
|
||||||
"python.linting.pylintEnabled": true,
|
"python.linting.pylintEnabled": false,
|
||||||
"python.linting.enabled": true,
|
"python.linting.enabled": true,
|
||||||
"python.testing.unittestEnabled": false,
|
"python.testing.unittestEnabled": false,
|
||||||
"python.testing.nosetestsEnabled": false,
|
"python.testing.nosetestsEnabled": false,
|
||||||
|
@ -24,5 +24,6 @@
|
||||||
"postgres",
|
"postgres",
|
||||||
"webp"
|
"webp"
|
||||||
],
|
],
|
||||||
"search.mode": "reuseEditor"
|
"search.mode": "reuseEditor",
|
||||||
|
"python.linting.flake8Enabled": true
|
||||||
}
|
}
|
||||||
|
|
|
@ -135,14 +135,18 @@ export const recipeAPI = {
|
||||||
},
|
},
|
||||||
|
|
||||||
recipeImage(recipeSlug) {
|
recipeImage(recipeSlug) {
|
||||||
return `/api/recipes/media/${recipeSlug}/images/original.webp`;
|
return `/api/media/recipes/${recipeSlug}/images/original.webp`;
|
||||||
},
|
},
|
||||||
|
|
||||||
recipeSmallImage(recipeSlug) {
|
recipeSmallImage(recipeSlug) {
|
||||||
return `/api/recipes/media/${recipeSlug}/images/min-original.webp`;
|
return `/api/media/recipes/${recipeSlug}/images/min-original.webp`;
|
||||||
},
|
},
|
||||||
|
|
||||||
recipeTinyImage(recipeSlug) {
|
recipeTinyImage(recipeSlug) {
|
||||||
return `/api/recipes/media/${recipeSlug}/images/tiny-original.webp`;
|
return `/api/media/recipes/${recipeSlug}/images/tiny-original.webp`;
|
||||||
|
},
|
||||||
|
|
||||||
|
recipeAssetPath(recipeSlug, assetName) {
|
||||||
|
return `api/media/recipes/${recipeSlug}/assets/${assetName}`;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,14 +14,7 @@
|
||||||
<v-list-item-title class="pl-2" v-text="item.name"></v-list-item-title>
|
<v-list-item-title class="pl-2" v-text="item.name"></v-list-item-title>
|
||||||
</v-list-item-content>
|
</v-list-item-content>
|
||||||
<v-list-item-action>
|
<v-list-item-action>
|
||||||
<v-btn
|
<v-btn v-if="!edit" color="primary" icon :href="assetURL(item.fileName)" target="_blank" top>
|
||||||
v-if="!edit"
|
|
||||||
color="primary"
|
|
||||||
icon
|
|
||||||
:href="`/api/recipes/media/${slug}/assets/${item.fileName}`"
|
|
||||||
target="_blank"
|
|
||||||
top
|
|
||||||
>
|
|
||||||
<v-icon> mdi-download</v-icon>
|
<v-icon> mdi-download</v-icon>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
|
@ -118,6 +111,9 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
assetURL(assetName) {
|
||||||
|
return api.recipes.recipeAssetPath(this.slug, assetName);
|
||||||
|
},
|
||||||
setFileObject(obj) {
|
setFileObject(obj) {
|
||||||
this.fileObject = obj;
|
this.fileObject = obj;
|
||||||
},
|
},
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
color="secondary darken-1"
|
color="secondary darken-1"
|
||||||
class="rounded-sm static"
|
class="rounded-sm static"
|
||||||
>
|
>
|
||||||
{{ yields }}
|
{{ recipe.yields }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</v-col>
|
</v-col>
|
||||||
<Rating :value="recipe.rating" :name="recipe.name" :slug="recipe.slug" />
|
<Rating :value="recipe.rating" :name="recipe.name" :slug="recipe.slug" />
|
||||||
|
|
|
@ -71,7 +71,7 @@ export default {
|
||||||
components: { StatCard, ImportDialog, TheUploadBtn, ImportSummaryDialog },
|
components: { StatCard, ImportDialog, TheUploadBtn, ImportSummaryDialog },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
color: "secondary",
|
color: "accent",
|
||||||
selectedName: "",
|
selectedName: "",
|
||||||
selectedDate: "",
|
selectedDate: "",
|
||||||
loading: false,
|
loading: false,
|
||||||
|
|
|
@ -54,7 +54,7 @@ export default {
|
||||||
components: { StatCard },
|
components: { StatCard },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
color: "secondary",
|
color: "accent",
|
||||||
total: 0,
|
total: 0,
|
||||||
events: [],
|
events: [],
|
||||||
icons: {
|
icons: {
|
||||||
|
|
|
@ -1,18 +1,20 @@
|
||||||
import uvicorn
|
import uvicorn
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
|
|
||||||
from mealie.core import root_logger
|
|
||||||
from mealie.core.config import APP_VERSION, settings
|
from mealie.core.config import APP_VERSION, settings
|
||||||
from mealie.routes import backup_routes, debug_routes, migration_routes, theme_routes, utility_routes
|
from mealie.core.root_logger import get_logger
|
||||||
|
from mealie.routes import (backup_routes, debug_routes, migration_routes,
|
||||||
|
theme_routes, utility_routes)
|
||||||
from mealie.routes.about import about_router
|
from mealie.routes.about import about_router
|
||||||
from mealie.routes.groups import groups
|
from mealie.routes.groups import groups_router
|
||||||
from mealie.routes.mealplans import mealplans
|
from mealie.routes.mealplans import meal_plan_router
|
||||||
from mealie.routes.recipe import router as recipe_router
|
from mealie.routes.media import media_router
|
||||||
from mealie.routes.site_settings import all_settings
|
from mealie.routes.recipe import recipe_router
|
||||||
from mealie.routes.users import users
|
from mealie.routes.site_settings import settings_router
|
||||||
|
from mealie.routes.users import user_router
|
||||||
from mealie.services.events import create_general_event
|
from mealie.services.events import create_general_event
|
||||||
|
|
||||||
logger = root_logger.get_logger()
|
logger = get_logger()
|
||||||
|
|
||||||
app = FastAPI(
|
app = FastAPI(
|
||||||
title="Mealie",
|
title="Mealie",
|
||||||
|
@ -29,15 +31,16 @@ def start_scheduler():
|
||||||
|
|
||||||
def api_routers():
|
def api_routers():
|
||||||
# Authentication
|
# Authentication
|
||||||
app.include_router(users.router)
|
app.include_router(user_router)
|
||||||
app.include_router(groups.router)
|
app.include_router(groups_router)
|
||||||
# Recipes
|
# Recipes
|
||||||
app.include_router(recipe_router)
|
app.include_router(recipe_router)
|
||||||
|
app.include_router(media_router)
|
||||||
app.include_router(about_router)
|
app.include_router(about_router)
|
||||||
# Meal Routes
|
# Meal Routes
|
||||||
app.include_router(mealplans.router)
|
app.include_router(meal_plan_router)
|
||||||
# Settings Routes
|
# Settings Routes
|
||||||
app.include_router(all_settings.router)
|
app.include_router(settings_router)
|
||||||
app.include_router(theme_routes.router)
|
app.include_router(theme_routes.router)
|
||||||
# Backups/Imports Routes
|
# Backups/Imports Routes
|
||||||
app.include_router(backup_routes.router)
|
app.include_router(backup_routes.router)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
|
|
||||||
from .events import router as events_router
|
from . import events
|
||||||
|
|
||||||
about_router = APIRouter(prefix="/api/about")
|
about_router = APIRouter(prefix="/api/about")
|
||||||
|
|
||||||
about_router.include_router(events_router)
|
about_router.include_router(events.router)
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
from . import crud, groups
|
||||||
|
|
||||||
|
groups_router = APIRouter()
|
||||||
|
|
||||||
|
groups_router.include_router(crud.router)
|
||||||
|
groups_router.include_router(groups.router)
|
|
@ -0,0 +1,9 @@
|
||||||
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
from . import crud, helpers, mealplans
|
||||||
|
|
||||||
|
meal_plan_router = APIRouter()
|
||||||
|
|
||||||
|
meal_plan_router.include_router(crud.router)
|
||||||
|
meal_plan_router.include_router(helpers.router)
|
||||||
|
meal_plan_router.include_router(mealplans.router)
|
7
mealie/routes/media/__init__.py
Normal file
7
mealie/routes/media/__init__.py
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
from . import recipe
|
||||||
|
|
||||||
|
media_router = APIRouter(prefix="/api/media", tags=["Site Media"])
|
||||||
|
|
||||||
|
media_router.include_router(recipe.router)
|
41
mealie/routes/media/recipe.py
Normal file
41
mealie/routes/media/recipe.py
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
from fastapi import APIRouter, HTTPException, status
|
||||||
|
from mealie.schema.recipe import Recipe
|
||||||
|
from starlette.responses import FileResponse
|
||||||
|
|
||||||
|
"""
|
||||||
|
These routes are for development only! These assets are served by Caddy when not
|
||||||
|
in development mode. If you make changes, be sure to test the production container.
|
||||||
|
"""
|
||||||
|
|
||||||
|
router = APIRouter(prefix="/recipes")
|
||||||
|
|
||||||
|
|
||||||
|
class ImageType(str, Enum):
|
||||||
|
original = "original.webp"
|
||||||
|
small = "min-original.webp"
|
||||||
|
tiny = "tiny-original.webp"
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/{recipe_slug}/images/{file_name}")
|
||||||
|
async def get_recipe_img(recipe_slug: str, file_name: ImageType = ImageType.original):
|
||||||
|
"""Takes in a recipe slug, returns the static image. This route is proxied in the docker image
|
||||||
|
and should not hit the API in production"""
|
||||||
|
recipe_image = Recipe(slug=recipe_slug).image_dir.joinpath(file_name.value)
|
||||||
|
|
||||||
|
if recipe_image:
|
||||||
|
return FileResponse(recipe_image)
|
||||||
|
else:
|
||||||
|
raise HTTPException(status.HTTP_404_NOT_FOUND)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/{recipe_slug}/assets/{file_name}")
|
||||||
|
async def get_recipe_asset(recipe_slug: str, file_name: str):
|
||||||
|
""" Returns a recipe asset """
|
||||||
|
file = Recipe(slug=recipe_slug).asset_dir.joinpath(file_name)
|
||||||
|
|
||||||
|
try:
|
||||||
|
return FileResponse(file)
|
||||||
|
except Exception:
|
||||||
|
raise HTTPException(status.HTTP_404_NOT_FOUND)
|
0
mealie/routes/media/user.py
Normal file
0
mealie/routes/media/user.py
Normal file
|
@ -1,10 +1,9 @@
|
||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
from mealie.routes.recipe import all_recipe_routes, category_routes, recipe_crud_routes, recipe_media, tag_routes
|
from mealie.routes.recipe import all_recipe_routes, category_routes, recipe_crud_routes, tag_routes
|
||||||
|
|
||||||
router = APIRouter()
|
recipe_router = APIRouter()
|
||||||
|
|
||||||
router.include_router(all_recipe_routes.router)
|
recipe_router.include_router(all_recipe_routes.router)
|
||||||
router.include_router(recipe_crud_routes.router)
|
recipe_router.include_router(recipe_crud_routes.router)
|
||||||
router.include_router(recipe_media.router)
|
recipe_router.include_router(category_routes.router)
|
||||||
router.include_router(category_routes.router)
|
recipe_router.include_router(tag_routes.router)
|
||||||
router.include_router(tag_routes.router)
|
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
|
from shutil import copyfileobj
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, File, Form, HTTPException, status
|
from fastapi import APIRouter, Depends, File, Form, HTTPException, status
|
||||||
|
from fastapi.datastructures import UploadFile
|
||||||
from mealie.core.root_logger import get_logger
|
from mealie.core.root_logger import get_logger
|
||||||
from mealie.db.database import db
|
from mealie.db.database import db
|
||||||
from mealie.db.db_setup import generate_session
|
from mealie.db.db_setup import generate_session
|
||||||
from mealie.routes.deps import get_current_user
|
from mealie.routes.deps import get_current_user
|
||||||
from mealie.schema.recipe import Recipe, RecipeURLIn
|
from mealie.schema.recipe import Recipe, RecipeAsset, RecipeURLIn
|
||||||
from mealie.services.events import create_recipe_event
|
from mealie.services.events import create_recipe_event
|
||||||
from mealie.services.image.image import scrape_image, write_image
|
from mealie.services.image.image import scrape_image, write_image
|
||||||
from mealie.services.recipe.media import check_assets, delete_assets
|
from mealie.services.recipe.media import check_assets, delete_assets
|
||||||
from mealie.services.scraper.scraper import create_from_url
|
from mealie.services.scraper.scraper import create_from_url
|
||||||
|
from slugify import slugify
|
||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
|
|
||||||
router = APIRouter(prefix="/api/recipes", tags=["Recipe CRUD"])
|
router = APIRouter(prefix="/api/recipes", tags=["Recipe CRUD"])
|
||||||
|
@ -126,3 +130,30 @@ def scrape_image_url(
|
||||||
""" Removes an existing image and replaces it with the incoming file. """
|
""" Removes an existing image and replaces it with the incoming file. """
|
||||||
|
|
||||||
scrape_image(url.url, recipe_slug)
|
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
|
||||||
|
|
|
@ -1,70 +0,0 @@
|
||||||
import shutil
|
|
||||||
from enum import Enum
|
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, File, Form, HTTPException, status
|
|
||||||
from fastapi.datastructures import UploadFile
|
|
||||||
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
|
|
||||||
from slugify import slugify
|
|
||||||
from sqlalchemy.orm.session import Session
|
|
||||||
from starlette.responses import FileResponse
|
|
||||||
|
|
||||||
router = APIRouter(prefix="/api/recipes/media", tags=["Recipe Media"])
|
|
||||||
|
|
||||||
|
|
||||||
class ImageType(str, Enum):
|
|
||||||
original = "original.webp"
|
|
||||||
small = "min-original.webp"
|
|
||||||
tiny = "tiny-original.webp"
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("/{recipe_slug}/images/{file_name}")
|
|
||||||
async def get_recipe_img(recipe_slug: str, file_name: ImageType = ImageType.original):
|
|
||||||
"""Takes in a recipe slug, returns the static image. This route is proxied in the docker image
|
|
||||||
and should not hit the API in production"""
|
|
||||||
recipe_image = Recipe(slug=recipe_slug).image_dir.joinpath(file_name.value)
|
|
||||||
|
|
||||||
if recipe_image:
|
|
||||||
return FileResponse(recipe_image)
|
|
||||||
else:
|
|
||||||
raise HTTPException(status.HTTP_404_NOT_FOUND)
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("/{recipe_slug}/assets/{file_name}")
|
|
||||||
async def get_recipe_asset(recipe_slug: str, file_name: str):
|
|
||||||
""" Returns a recipe asset """
|
|
||||||
file = Recipe(slug=recipe_slug).asset_dir.joinpath(file_name)
|
|
||||||
|
|
||||||
try:
|
|
||||||
return FileResponse(file)
|
|
||||||
except Exception:
|
|
||||||
raise HTTPException(status.HTTP_404_NOT_FOUND)
|
|
||||||
|
|
||||||
|
|
||||||
@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:
|
|
||||||
shutil.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
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
from . import all_settings, custom_pages, site_settings
|
||||||
|
|
||||||
|
settings_router = APIRouter()
|
||||||
|
|
||||||
|
settings_router.include_router(all_settings.router)
|
||||||
|
settings_router.include_router(custom_pages.router)
|
||||||
|
settings_router.include_router(site_settings.router)
|
|
@ -0,0 +1,9 @@
|
||||||
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
from . import auth, crud, sign_up
|
||||||
|
|
||||||
|
user_router = APIRouter()
|
||||||
|
|
||||||
|
user_router.include_router(auth.router)
|
||||||
|
user_router.include_router(crud.router)
|
||||||
|
user_router.include_router(sign_up.router)
|
|
@ -1,9 +0,0 @@
|
||||||
from fastapi import APIRouter
|
|
||||||
from mealie.routes.users import auth, crud, sign_up
|
|
||||||
|
|
||||||
router = APIRouter()
|
|
||||||
|
|
||||||
router.include_router(sign_up.router)
|
|
||||||
router.include_router(auth.router)
|
|
||||||
router.include_router(sign_up.router)
|
|
||||||
router.include_router(crud.router)
|
|
Loading…
Add table
Add a link
Reference in a new issue