Refactor/recipe routes (#370)

* format with black

* black format

* flake8

* remove bar exceptions

* remove test for depreciated route

* recipe settings editr

* add sqlite

Co-authored-by: hay-kot <hay-kot@pm.me>
This commit is contained in:
Hayden 2021-04-29 21:46:27 -08:00 committed by GitHub
parent 5dafe8fbb5
commit 1dc051f562
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 179 additions and 224 deletions

View File

@ -1,6 +1,6 @@
[flake8] [flake8]
ignore = [ ignore = [
E501 # Line Length - See Black Config in pyproject.toml E501 # Line Length - See Black Config in pyproject.toml
E722 # Bare Exception | Temporary E402 # Import Not at Top of File
] ]
exclude = _all_models.py exclude = _all_models.py

21
.gitignore vendored
View File

@ -3,13 +3,11 @@
*__pycache__/ *__pycache__/
*.py[cod] *.py[cod]
*$py.class *$py.class
# frontend/.env.development # frontend/.env.development
docs/site/ docs/site/
mealie/temp/* *temp*
mealie/temp/api.html
.temp/
.secret .secret
!*/components/Recipe/Parts
dev/data/backups/* dev/data/backups/*
dev/data/debug/* dev/data/debug/*
@ -17,14 +15,6 @@ dev/data/img/*
dev/data/migration/* dev/data/migration/*
dev/data/users/* dev/data/users/*
#Exception to keep folders
!mealie/dist/.gitkeep
!dev/data/backups/.gitkeep
!dev/data/backups/dev_sample_data*
!dev/data/debug/.gitkeep
!dev/data/migration/.gitkeep
!dev/data/img/.gitkeep
.DS_Store .DS_Store
node_modules node_modules
@ -148,16 +138,11 @@ ENV/
# mypy # mypy
.mypy_cache/ .mypy_cache/
# IDE settings
# .vscode/
# Node Modules # Node Modules
node_modules/ node_modules/
mealie/data/debug/last_recipe.json *.db
*.sqlite *.sqlite
dev/data/db/test.db
scratch.py scratch.py
dev/data/backups/dev_sample_data*.zip dev/data/backups/dev_sample_data*.zip
dev/data/backups/dev_sample_data*.zip
!dev/data/backups/test*.zip !dev/data/backups/test*.zip
dev/data/recipes/* dev/data/recipes/*

View File

@ -0,0 +1,55 @@
<template>
<div class="text-center">
<v-menu offset-y top nudge-top="6" :close-on-content-click="false">
<template v-slot:activator="{ on, attrs }">
<v-btn color="accent" dark v-bind="attrs" v-on="on">
<v-icon left>
mdi-cog
</v-icon>
{{ $t("general.settings") }}
</v-btn>
</template>
<v-card>
<v-card-title class="py-2">
<div>
Recipe Settings
</div>
</v-card-title>
<v-divider class="mx-2"></v-divider>
<v-card-text class="mt-n5">
<v-switch
dense
v-for="(itemValue, key) in value"
:key="key"
v-model="value[key]"
flat
inset
:label="labels[key]"
hide-details
></v-switch>
</v-card-text>
</v-card>
</v-menu>
</div>
</template>
<script>
export default {
components: {},
props: {
value: Object,
},
data: () => ({
labels: {
public: "Public Recipe",
showNutrition: "Show Nutrition Values",
showAssets: "Show Assets",
landscapeView: "Landscape View (Coming Soon)",
},
}),
methods: {},
};
</script>
<style lang="scss" scoped>
</style>

View File

@ -8,6 +8,11 @@
:slug="value.slug" :slug="value.slug"
@refresh="$emit('upload')" @refresh="$emit('upload')"
/> />
<SettingsMenu
class="my-1 mx-1"
@upload="uploadImage"
:value="value.settings"
/>
</v-row> </v-row>
<v-row dense> <v-row dense>
<v-col> <v-col>
@ -122,6 +127,7 @@ import Instructions from "@/components/Recipe/Parts/Instructions";
import Ingredients from "@/components/Recipe/Parts/Ingredients"; import Ingredients from "@/components/Recipe/Parts/Ingredients";
import Assets from "@/components/Recipe/Parts/Assets.vue"; import Assets from "@/components/Recipe/Parts/Assets.vue";
import Notes from "@/components/Recipe/Parts/Notes.vue"; import Notes from "@/components/Recipe/Parts/Notes.vue";
import SettingsMenu from "@/components/Recipe/Parts/Helpers/SettingsMenu.vue";
export default { export default {
components: { components: {
BulkAdd, BulkAdd,
@ -133,6 +139,7 @@ export default {
Ingredients, Ingredients,
Assets, Assets,
Notes, Notes,
SettingsMenu,
}, },
props: { props: {
value: Object, value: Object,

View File

@ -3,13 +3,10 @@ from fastapi import FastAPI
from mealie.core import root_logger 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, from mealie.routes import backup_routes, debug_routes, migration_routes, theme_routes, utility_routes
theme_routes, utility_routes)
from mealie.routes.groups import groups from mealie.routes.groups import groups
from mealie.routes.mealplans import mealplans from mealie.routes.mealplans import mealplans
from mealie.routes.recipe import (all_recipe_routes, category_routes, from mealie.routes.recipe import router as recipe_router
recipe_assets, recipe_crud_routes,
tag_routes)
from mealie.routes.site_settings import all_settings from mealie.routes.site_settings import all_settings
from mealie.routes.users import users from mealie.routes.users import users
@ -30,15 +27,10 @@ def start_scheduler():
def api_routers(): def api_routers():
# Authentication # Authentication
app.include_router(utility_routes.router)
app.include_router(users.router) app.include_router(users.router)
app.include_router(groups.router) app.include_router(groups.router)
# Recipes # Recipes
app.include_router(all_recipe_routes.router) app.include_router(recipe_router)
app.include_router(category_routes.router)
app.include_router(tag_routes.router)
app.include_router(recipe_crud_routes.router)
app.include_router(recipe_assets.router)
# Meal Routes # Meal Routes
app.include_router(mealplans.router) app.include_router(mealplans.router)
# Settings Routes # Settings Routes
@ -49,6 +41,7 @@ def api_routers():
# Migration Routes # Migration Routes
app.include_router(migration_routes.router) app.include_router(migration_routes.router)
app.include_router(debug_routes.router) app.include_router(debug_routes.router)
app.include_router(utility_routes.router)
api_routers() api_routers()

View File

@ -137,4 +137,3 @@ class BaseDocument:
session.delete(result) session.delete(result)
session.commit() session.commit()

View File

@ -1,17 +1,15 @@
import operator import operator
import shutil import shutil
from typing import Optional
from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status
from mealie.core.config import app_dirs from mealie.core.config import app_dirs
from mealie.core.security import create_file_token from mealie.core.security import create_file_token
from mealie.db.db_setup import generate_session from mealie.db.db_setup import generate_session
from mealie.routes.deps import get_current_user, validate_file_token from mealie.routes.deps import get_current_user
from mealie.schema.backup import BackupJob, ImportJob, Imports, LocalBackup from mealie.schema.backup import BackupJob, ImportJob, Imports, LocalBackup
from mealie.services.backups import imports from mealie.services.backups import imports
from mealie.services.backups.exports import backup_all from mealie.services.backups.exports import backup_all
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
from starlette.responses import FileResponse
router = APIRouter(prefix="/api/backups", tags=["Backups"], dependencies=[Depends(get_current_user)]) router = APIRouter(prefix="/api/backups", tags=["Backups"], dependencies=[Depends(get_current_user)])
@ -46,8 +44,8 @@ def export_database(data: BackupJob, session: Session = Depends(generate_session
export_groups=data.options.groups, export_groups=data.options.groups,
) )
return {"export_path": export_path} return {"export_path": export_path}
except: except Exception:
raise HTTPException( status.HTTP_500_INTERNAL_SERVER_ERROR ) raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR)
@router.post("/upload", status_code=status.HTTP_200_OK) @router.post("/upload", status_code=status.HTTP_200_OK)
@ -59,8 +57,7 @@ def upload_backup_file(archive: UploadFile = File(...)):
shutil.copyfileobj(archive.file, buffer) shutil.copyfileobj(archive.file, buffer)
if not dest.is_file: if not dest.is_file:
raise HTTPException( status.HTTP_400_BAD_REQUEST ) raise HTTPException(status.HTTP_400_BAD_REQUEST)
@router.get("/{file_name}/download") @router.get("/{file_name}/download")
@ -95,8 +92,8 @@ def delete_backup(file_name: str):
file_path = app_dirs.BACKUP_DIR.joinpath(file_name) file_path = app_dirs.BACKUP_DIR.joinpath(file_name)
if not file_path.is_file(): if not file_path.is_file():
raise HTTPException( status.HTTP_400_BAD_REQUEST ) raise HTTPException(status.HTTP_400_BAD_REQUEST)
try: try:
file_path.unlink() file_path.unlink()
except: except Exception:
raise HTTPException( status.HTTP_500_INTERNAL_SERVER_ERROR ) raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR)

View File

@ -1,5 +1,3 @@
import json
from fastapi import APIRouter, Depends from fastapi import APIRouter, Depends
from mealie.core.config import APP_VERSION, app_dirs, settings from mealie.core.config import APP_VERSION, app_dirs, settings
from mealie.core.root_logger import LOGGER_FILE from mealie.core.root_logger import LOGGER_FILE

View File

@ -39,8 +39,8 @@ async def create_group(
try: try:
db.groups.create(session, group_data.dict()) db.groups.create(session, group_data.dict())
except: except Exception:
raise HTTPException( status.HTTP_400_BAD_REQUEST ) raise HTTPException(status.HTTP_400_BAD_REQUEST)
@router.put("/{id}") @router.put("/{id}")
@ -61,23 +61,14 @@ async def delete_user_group(
""" Removes a user group from the database """ """ Removes a user group from the database """
if id == 1: if id == 1:
raise HTTPException( raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="DEFAULT_GROUP")
status_code=status.HTTP_400_BAD_REQUEST,
detail='DEFAULT_GROUP'
)
group: GroupInDB = db.groups.get(session, id) group: GroupInDB = db.groups.get(session, id)
if not group: if not group:
raise HTTPException( raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="GROUP_NOT_FOUND")
status_code=status.HTTP_400_BAD_REQUEST,
detail='GROUP_NOT_FOUND'
)
if not group.users == []: if not group.users == []:
raise HTTPException( raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="GROUP_WITH_USERS")
status_code=status.HTTP_400_BAD_REQUEST,
detail='GROUP_WITH_USERS'
)
db.groups.delete(session, id) db.groups.delete(session, id)

View File

@ -43,8 +43,8 @@ def update_meal_plan(
processed_plan = MealPlanInDB(uid=plan_id, **processed_plan.dict()) processed_plan = MealPlanInDB(uid=plan_id, **processed_plan.dict())
try: try:
db.meals.update(session, plan_id, processed_plan.dict()) db.meals.update(session, plan_id, processed_plan.dict())
except: except Exception:
raise HTTPException( status.HTTP_400_BAD_REQUEST ) raise HTTPException(status.HTTP_400_BAD_REQUEST)
@router.delete("/{plan_id}") @router.delete("/{plan_id}")
@ -53,8 +53,8 @@ def delete_meal_plan(plan_id, session: Session = Depends(generate_session), curr
try: try:
db.meals.delete(session, plan_id) db.meals.delete(session, plan_id)
except: except Exception:
raise HTTPException( status.HTTP_400_BAD_REQUEST ) raise HTTPException(status.HTTP_400_BAD_REQUEST)
@router.get("/this-week", response_model=MealPlanInDB) @router.get("/this-week", response_model=MealPlanInDB)
@ -90,8 +90,8 @@ def get_todays_image(session: Session = Depends(generate_session), group_name: s
if recipe: if recipe:
recipe_image = image.read_image(recipe.slug, image_type=image.IMG_OPTIONS.ORIGINAL_IMAGE) recipe_image = image.read_image(recipe.slug, image_type=image.IMG_OPTIONS.ORIGINAL_IMAGE)
else: else:
raise HTTPException( status.HTTP_404_NOT_FOUND ) raise HTTPException(status.HTTP_404_NOT_FOUND)
if recipe_image: if recipe_image:
return FileResponse(recipe_image) return FileResponse(recipe_image)
else: else:
raise HTTPException( status.HTTP_404_NOT_FOUND ) raise HTTPException(status.HTTP_404_NOT_FOUND)

View File

@ -9,6 +9,7 @@ from mealie.routes.deps import get_current_user
from mealie.schema.migration import MigrationFile, Migrations from mealie.schema.migration import MigrationFile, Migrations
from mealie.services.migrations import migration from mealie.services.migrations import migration
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
from fastapi import HTTPException
router = APIRouter(prefix="/api/migrations", tags=["Migration"], dependencies=[Depends(get_current_user)]) router = APIRouter(prefix="/api/migrations", tags=["Migration"], dependencies=[Depends(get_current_user)])
@ -52,8 +53,7 @@ def delete_migration_data(import_type: migration.Migration, file_name: str):
elif remove_path.is_dir(): elif remove_path.is_dir():
shutil.rmtree(remove_path) shutil.rmtree(remove_path)
else: else:
raise HTTPException( status.HTTP_400_BAD_REQUEST ) raise HTTPException(status.HTTP_400_BAD_REQUEST)
@router.post("/{import_type}/upload", status_code=status.HTTP_200_OK) @router.post("/{import_type}/upload", status_code=status.HTTP_200_OK)
@ -67,4 +67,4 @@ def upload_nextcloud_zipfile(import_type: migration.Migration, archive: UploadFi
shutil.copyfileobj(archive.file, buffer) shutil.copyfileobj(archive.file, buffer)
if not dest.is_file: if not dest.is_file:
raise HTTPException( status.HTTP_400_BAD_REQUEST ) raise HTTPException(status.HTTP_400_BAD_REQUEST)

View File

@ -0,0 +1,10 @@
from fastapi import APIRouter
from mealie.routes.recipe import all_recipe_routes, category_routes, recipe_assets, recipe_crud_routes, tag_routes
router = APIRouter()
router.include_router(all_recipe_routes.router)
router.include_router(recipe_crud_routes.router)
router.include_router(recipe_assets.router)
router.include_router(category_routes.router)
router.include_router(tag_routes.router)

View File

@ -1,9 +1,7 @@
from typing import List, Optional from fastapi import APIRouter, Depends
from fastapi import APIRouter, Depends, Query
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.schema.recipe import AllRecipeRequest, RecipeSummary from mealie.schema.recipe import RecipeSummary
from slugify import slugify from slugify import slugify
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
@ -31,66 +29,6 @@ async def get_recipe_summary(
return db.recipes.get_all(session, limit=limit, start=start, override_schema=RecipeSummary) return db.recipes.get_all(session, limit=limit, start=start, override_schema=RecipeSummary)
@router.get("/api/recipes", deprecated=True)
def get_all_recipes(
keys: Optional[List[str]] = Query(...),
num: Optional[int] = 100,
session: Session = Depends(generate_session),
):
"""
Returns key data for all recipes based off the query paramters provided.
For example, if slug, image, and name are provided you will recieve a list of
recipes containing the slug, image, and name property. By default, responses
are limited to 100.
At this time you can only query top level values:
- slug
- name
- description
- image
- recipeYield
- total_time
- prep_time
- perform_time
- rating
- org_url
**Note:** You may experience problems with with query parameters. As an alternative
you may also use the post method and provide a body.
See the *Post* method for more details.
"""
return db.recipes.get_all_limit_columns(session, keys, limit=num)
@router.post("/api/recipes", deprecated=True)
def get_all_recipes_post(body: AllRecipeRequest, session: Session = Depends(generate_session)):
"""
Returns key data for all recipes based off the body data provided.
For example, if slug, image, and name are provided you will recieve a list of
recipes containing the slug, image, and name property.
At this time you can only query top level values:
- slug
- name
- description
- image
- recipeYield
- total_time
- prep_time
- perform_time
- rating
- org_url
Refer to the body example for data formats.
"""
return db.recipes.get_all_limit_columns(session, body.properties, body.limit)
@router.post("/api/recipes/category") @router.post("/api/recipes/category")
def filter_by_category(categories: list, session: Session = Depends(generate_session)): def filter_by_category(categories: list, session: Session = Depends(generate_session)):
""" pass a list of categories and get a list of recipes associated with those categories """ """ pass a list of categories and get a list of recipes associated with those categories """

View File

@ -37,8 +37,8 @@ async def create_recipe_category(
try: try:
return db.categories.create(session, category.dict()) return db.categories.create(session, category.dict())
except: except Exception:
raise HTTPException( status.HTTP_400_BAD_REQUEST ) raise HTTPException(status.HTTP_400_BAD_REQUEST)
@router.put("/{category}", response_model=RecipeCategoryResponse) @router.put("/{category}", response_model=RecipeCategoryResponse)
@ -52,8 +52,8 @@ async def update_recipe_category(
try: try:
return db.categories.update(session, category, new_category.dict()) return db.categories.update(session, category, new_category.dict())
except: except Exception:
raise HTTPException( status.HTTP_400_BAD_REQUEST ) raise HTTPException(status.HTTP_400_BAD_REQUEST)
@router.delete("/{category}") @router.delete("/{category}")
@ -66,5 +66,5 @@ async def delete_recipe_category(
try: try:
db.categories.delete(session, category) db.categories.delete(session, category)
except: except Exception:
raise HTTPException( status.HTTP_400_BAD_REQUEST ) raise HTTPException(status.HTTP_400_BAD_REQUEST)

View File

@ -1,6 +1,7 @@
import shutil import shutil
from enum import Enum
from fastapi import APIRouter, Depends, File, Form, status, HTTPException from fastapi import APIRouter, Depends, File, Form, HTTPException, status
from fastapi.datastructures import UploadFile from fastapi.datastructures import UploadFile
from mealie.core.config import app_dirs from mealie.core.config import app_dirs
from mealie.db.database import db from mealie.db.database import db
@ -11,7 +12,24 @@ from slugify import slugify
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
from starlette.responses import FileResponse from starlette.responses import FileResponse
router = APIRouter(prefix="/api/recipes", tags=["Recipe Assets"]) router = APIRouter(prefix="/api/recipes", tags=["Recipe Media"])
class ImageType(str, Enum):
original = "original.webp"
small = "min-original.webp"
tiny = "tiny-original.webp"
@router.get("/image/{recipe_slug}/{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 = app_dirs.IMG_DIR.joinpath(recipe_slug, file_name.value)
if recipe_image:
return FileResponse(recipe_image)
else:
raise HTTPException(status.HTTP_404_NOT_FOUND)
@router.get("/{recipe_slug}/asset") @router.get("/{recipe_slug}/asset")
@ -41,7 +59,7 @@ def upload_recipe_asset(
shutil.copyfileobj(file.file, buffer) shutil.copyfileobj(file.file, buffer)
if not dest.is_file(): if not dest.is_file():
raise HTTPException( status.HTTP_500_INTERNAL_SERVER_ERROR ) raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR)
recipe: Recipe = db.recipes.get(session, recipe_slug) recipe: Recipe = db.recipes.get(session, recipe_slug)
recipe.assets.append(asset_in) recipe.assets.append(asset_in)

View File

@ -1,14 +1,10 @@
from enum import Enum
from fastapi import APIRouter, Depends, File, Form, HTTPException, status from fastapi import APIRouter, Depends, File, Form, HTTPException, status
from fastapi.responses import FileResponse
from mealie.core.config import app_dirs
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, RecipeURLIn
from mealie.services.image.image import IMG_OPTIONS, delete_image, read_image, rename_image, scrape_image, write_image from mealie.services.image.image import delete_image, rename_image, scrape_image, write_image
from mealie.services.scraper.scraper import create_from_url from mealie.services.scraper.scraper import create_from_url
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
@ -101,25 +97,8 @@ def delete_recipe(
try: try:
db.recipes.delete(session, recipe_slug) db.recipes.delete(session, recipe_slug)
delete_image(recipe_slug) delete_image(recipe_slug)
except: except Exception:
raise HTTPException( status.HTTP_400_BAD_REQUEST ) raise HTTPException(status.HTTP_400_BAD_REQUEST)
class ImageType(str, Enum):
original = "original.webp"
small = "min-original.webp"
tiny = "tiny-original.webp"
@router.get("/image/{recipe_slug}/{file_name}")
async def get_recipe_img(recipe_slug: str, file_name: ImageType = ImageType.original):
""" Takes in a recipe slug, returns the static image """
recipe_image = app_dirs.IMG_DIR.joinpath(recipe_slug, file_name.value)
if recipe_image:
return FileResponse(recipe_image)
else:
raise HTTPException( status.HTTP_404_NOT_FOUND )
@router.put("/{recipe_slug}/image") @router.put("/{recipe_slug}/image")

View File

@ -1,4 +1,4 @@
from fastapi import APIRouter, Depends from fastapi import APIRouter, Depends, HTTPException, status
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
@ -7,10 +7,7 @@ from sqlalchemy.orm.session import Session
router = APIRouter(tags=["Recipes"]) router = APIRouter(tags=["Recipes"])
router = APIRouter( router = APIRouter(prefix="/api/tags", tags=["Recipe Tags"])
prefix="/api/tags",
tags=["Recipe Tags"],
)
@router.get("") @router.get("")
@ -59,5 +56,5 @@ async def delete_recipe_tag(
try: try:
db.tags.delete(session, tag) db.tags.delete(session, tag)
except: except Exception:
raise HTTPException( status.HTTP_400_BAD_REQUEST ) raise HTTPException(status.HTTP_400_BAD_REQUEST)

View File

@ -21,7 +21,6 @@ def create_theme(data: SiteTheme, session: Session = Depends(generate_session),
db.themes.create(session, data.dict()) db.themes.create(session, data.dict())
@router.get("/themes/{theme_name}") @router.get("/themes/{theme_name}")
def get_single_theme(theme_name: str, session: Session = Depends(generate_session)): def get_single_theme(theme_name: str, session: Session = Depends(generate_session)):
""" Returns a named theme """ """ Returns a named theme """
@ -44,5 +43,5 @@ def delete_theme(theme_name: str, session: Session = Depends(generate_session),
""" Deletes theme from the database """ """ Deletes theme from the database """
try: try:
db.themes.delete(session, theme_name) db.themes.delete(session, theme_name)
except: except Exception:
raise HTTPException( status.HTTP_400_BAD_REQUEST ) raise HTTPException(status.HTTP_400_BAD_REQUEST)

View File

@ -1,5 +1,3 @@
from datetime import timedelta
from fastapi import APIRouter, Depends, status from fastapi import APIRouter, Depends, status
from fastapi.exceptions import HTTPException from fastapi.exceptions import HTTPException
from fastapi.security import OAuth2PasswordRequestForm from fastapi.security import OAuth2PasswordRequestForm

View File

@ -1,5 +1,4 @@
import shutil import shutil
from datetime import timedelta
from fastapi import APIRouter, Depends, File, UploadFile, status, HTTPException from fastapi import APIRouter, Depends, File, UploadFile, status, HTTPException
from fastapi.responses import FileResponse from fastapi.responses import FileResponse
@ -34,7 +33,7 @@ async def get_all_users(
): ):
if not current_user.admin: if not current_user.admin:
raise HTTPException( status.HTTP_403_FORBIDDEN ) raise HTTPException(status.HTTP_403_FORBIDDEN)
return db.users.get_all(session) return db.users.get_all(session)
@ -67,7 +66,6 @@ async def reset_user_password(
db.users.update_password(session, id, new_password) db.users.update_password(session, id, new_password)
@router.put("/{id}") @router.put("/{id}")
async def update_user( async def update_user(
id: int, id: int,
@ -109,7 +107,7 @@ async def update_user_image(
try: try:
[x.unlink() for x in app_dirs.USER_DIR.join(id).glob("profile_image.*")] [x.unlink() for x in app_dirs.USER_DIR.join(id).glob("profile_image.*")]
except: except Exception:
pass pass
dest = app_dirs.USER_DIR.joinpath(id, f"profile_image.{extension}") dest = app_dirs.USER_DIR.joinpath(id, f"profile_image.{extension}")
@ -118,7 +116,7 @@ async def update_user_image(
shutil.copyfileobj(profile_image.file, buffer) shutil.copyfileobj(profile_image.file, buffer)
if not dest.is_file: if not dest.is_file:
raise HTTPException( status.HTTP_500_INTERNAL_SERVER_ERROR ) raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR)
@router.put("/{id}/password") @router.put("/{id}/password")
@ -133,14 +131,13 @@ async def update_password(
match_passwords = verify_password(password_change.current_password, current_user.password) match_passwords = verify_password(password_change.current_password, current_user.password)
match_id = current_user.id == id match_id = current_user.id == id
if not ( match_passwords and match_id ): if not (match_passwords and match_id):
raise HTTPException( status.HTTP_401_UNAUTHORIZED ) raise HTTPException(status.HTTP_401_UNAUTHORIZED)
new_password = get_password_hash(password_change.new_password) new_password = get_password_hash(password_change.new_password)
db.users.update_password(session, id, new_password) db.users.update_password(session, id, new_password)
@router.delete("/{id}") @router.delete("/{id}")
async def delete_user( async def delete_user(
id: int, id: int,
@ -150,13 +147,10 @@ async def delete_user(
""" Removes a user from the database. Must be the current user or a super user""" """ Removes a user from the database. Must be the current user or a super user"""
if id == 1: if id == 1:
raise HTTPException( raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="SUPER_USER")
status_code=status.HTTP_403_FORBIDDEN,
detail='SUPER_USER'
)
if current_user.id == id or current_user.admin: if current_user.id == id or current_user.admin:
try: try:
db.users.delete(session, id) db.users.delete(session, id)
except: except Exception:
raise HTTPException( status.HTTP_400_BAD_REQUEST ) raise HTTPException(status.HTTP_400_BAD_REQUEST)

View File

@ -8,6 +8,7 @@ from mealie.routes.deps import get_current_user
from mealie.schema.sign_up import SignUpIn, SignUpOut, SignUpToken from mealie.schema.sign_up import SignUpIn, SignUpOut, SignUpToken
from mealie.schema.user import UserIn, UserInDB from mealie.schema.user import UserIn, UserInDB
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
from fastapi import HTTPException, status
router = APIRouter(prefix="/api/users/sign-ups", tags=["User Signup"]) router = APIRouter(prefix="/api/users/sign-ups", tags=["User Signup"])
@ -33,7 +34,7 @@ async def create_user_sign_up_key(
""" Generates a Random Token that a new user can sign up with """ """ Generates a Random Token that a new user can sign up with """
if not current_user.admin: if not current_user.admin:
raise HTTPException( status.HTTP_403_FORBIDDEN ) raise HTTPException(status.HTTP_403_FORBIDDEN)
sign_up = { sign_up = {
"token": str(uuid.uuid1().hex), "token": str(uuid.uuid1().hex),
@ -43,7 +44,6 @@ async def create_user_sign_up_key(
return db.sign_ups.create(session, sign_up) return db.sign_ups.create(session, sign_up)
@router.post("/{token}") @router.post("/{token}")
async def create_user_with_token( async def create_user_with_token(
token: str, token: str,
@ -55,12 +55,12 @@ async def create_user_with_token(
# Validate Token # Validate Token
db_entry: SignUpOut = db.sign_ups.get(session, token, limit=1) db_entry: SignUpOut = db.sign_ups.get(session, token, limit=1)
if not db_entry: if not db_entry:
raise HTTPException( status.HTTP_401_UNAUTHORIZED ) raise HTTPException(status.HTTP_401_UNAUTHORIZED)
# Create User # Create User
new_user.admin = db_entry.admin new_user.admin = db_entry.admin
new_user.password = get_password_hash(new_user.password) new_user.password = get_password_hash(new_user.password)
data = db.users.create(session, new_user.dict()) db.users.create(session, new_user.dict())
# DeleteToken # DeleteToken
db.sign_ups.delete(session, token) db.sign_ups.delete(session, token)
@ -74,6 +74,6 @@ async def delete_token(
): ):
""" Removed a token from the database """ """ Removed a token from the database """
if not current_user.admin: if not current_user.admin:
raise HTTPException( status.HTTP_403_FORBIDDEN ) raise HTTPException(status.HTTP_403_FORBIDDEN)
db.sign_ups.delete(session, token) db.sign_ups.delete(session, token)

View File

@ -4,16 +4,17 @@ from typing import Optional
from fastapi import APIRouter, Depends from fastapi import APIRouter, Depends
from mealie.routes.deps import validate_file_token from mealie.routes.deps import validate_file_token
from starlette.responses import FileResponse from starlette.responses import FileResponse
from fastapi import HTTPException, status
router = APIRouter(prefix="/api/utils", tags=["Utils"], include_in_schema=True) router = APIRouter(prefix="/api/utils", tags=["Utils"], include_in_schema=True)
@router.get("/download") @router.get("/download")
async def download_file(file_path: Optional[Path] = Depends(validate_file_token)): async def download_file(file_path: Optional[Path] = Depends(validate_file_token)):
""" Uses a file token obtained by an active user to retrieve a file from the operating """Uses a file token obtained by an active user to retrieve a file from the operating
system. """ system."""
print("File Name:", file_path) print("File Name:", file_path)
if not file_path.is_file(): if not file_path.is_file():
raise HTTPException( status.HTTP_400_BAD_REQUEST ) raise HTTPException(status.HTTP_400_BAD_REQUEST)
return FileResponse(file_path, media_type="application/octet-stream", filename=file_path.name) return FileResponse(file_path, media_type="application/octet-stream", filename=file_path.name)

View File

@ -37,7 +37,7 @@ class ExportDatabase:
try: try:
self.templates = [app_dirs.TEMPLATE_DIR.joinpath(x) for x in templates] self.templates = [app_dirs.TEMPLATE_DIR.joinpath(x) for x in templates]
except: except Exception:
self.templates = False self.templates = False
logger.info("No Jinja2 Templates Registered for Export") logger.info("No Jinja2 Templates Registered for Export")

View File

@ -84,20 +84,20 @@ class ImportDatabase:
try: try:
del recipe_dict["_id"] del recipe_dict["_id"]
del recipe_dict["date_added"] del recipe_dict["date_added"]
except: except Exception:
pass pass
# Migration from list to Object Type Data # Migration from list to Object Type Data
try: try:
if "" in recipe_dict["tags"]: if "" in recipe_dict["tags"]:
recipe_dict["tags"] = [tag for tag in recipe_dict["tags"] if tag != ""] recipe_dict["tags"] = [tag for tag in recipe_dict["tags"] if tag != ""]
except: except Exception:
pass pass
try: try:
if "" in recipe_dict["categories"]: if "" in recipe_dict["categories"]:
recipe_dict["categories"] = [cat for cat in recipe_dict["categories"] if cat != ""] recipe_dict["categories"] = [cat for cat in recipe_dict["categories"] if cat != ""]
except: except Exception:
pass pass
if type(recipe_dict["extras"]) == list: if type(recipe_dict["extras"]) == list:

View File

@ -54,7 +54,7 @@ def rename_image(original_slug, new_slug) -> Path:
def write_image(recipe_slug: str, file_data: bytes, extension: str) -> Path: def write_image(recipe_slug: str, file_data: bytes, extension: str) -> Path:
try: try:
delete_image(recipe_slug) delete_image(recipe_slug)
except: except Exception:
pass pass
image_dir = Path(app_dirs.IMG_DIR.joinpath(f"{recipe_slug}")) image_dir = Path(app_dirs.IMG_DIR.joinpath(f"{recipe_slug}"))
@ -100,7 +100,7 @@ def scrape_image(image_url: str, slug: str) -> Path:
try: try:
r = requests.get(image_url, stream=True) r = requests.get(image_url, stream=True)
except: except Exception:
logger.exception("Fatal Image Request Exception") logger.exception("Fatal Image Request Exception")
return None return None

View File

@ -35,14 +35,15 @@ def minify_image(image_file: Path) -> ImageSizes:
min_dest (Path): FULL Destination File Path min_dest (Path): FULL Destination File Path
tiny_dest (Path): FULL Destination File Path tiny_dest (Path): FULL Destination File Path
""" """
def cleanup(dir: Path) -> None: def cleanup(dir: Path) -> None:
for file in dir.glob("*.*"): for file in dir.glob("*.*"):
if file.suffix != ".webp": if file.suffix != ".webp":
file.unlink() file.unlink()
org_dest = image_file.parent.joinpath(f"original.webp") org_dest = image_file.parent.joinpath("original.webp")
min_dest = image_file.parent.joinpath(f"min-original.webp") min_dest = image_file.parent.joinpath("min-original.webp")
tiny_dest = image_file.parent.joinpath(f"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():
return return

View File

@ -24,7 +24,7 @@ def process_meals(session: Session, meal_plan_base: MealPlanIn) -> MealPlanProce
description=recipe.description, description=recipe.description,
) )
except: except Exception:
meal_data = MealOut( meal_data = MealOut(
date=meal_plan_base.startDate + timedelta(days=x), date=meal_plan_base.startDate + timedelta(days=x),

View File

@ -8,7 +8,7 @@ from mealie.core import root_logger
from mealie.db.database import db from mealie.db.database import db
from mealie.schema.migration import MigrationImport from mealie.schema.migration import MigrationImport
from mealie.schema.recipe import Recipe from mealie.schema.recipe import Recipe
from mealie.services.image import image, minify from mealie.services.image import image
from mealie.services.scraper.cleaner import Cleaner from mealie.services.scraper.cleaner import Cleaner
from mealie.utils.unzip import unpack_zip from mealie.utils.unzip import unpack_zip
from pydantic import BaseModel from pydantic import BaseModel

View File

@ -115,7 +115,7 @@ class Cleaner:
for step in instructions for step in instructions
if step["type"].find("HowToStep") > -1 if step["type"].find("HowToStep") > -1
] ]
except: except Exception:
pass pass
else: else:

View File

@ -21,7 +21,7 @@ def basic_recipe_from_opengraph(html: str, url: str) -> dict:
data = extruct.extract(html, base_url=base_url) data = extruct.extract(html, base_url=base_url)
try: try:
properties = data["opengraph"][0]["properties"] properties = data["opengraph"][0]["properties"]
except: except Exception:
return return
return { return {

View File

@ -67,7 +67,7 @@ def download_image_for_recipe(recipe: dict) -> dict:
try: try:
img_path = scrape_image(recipe.get("image"), recipe.get("slug")) img_path = scrape_image(recipe.get("image"), recipe.get("slug"))
recipe["image"] = img_path.name recipe["image"] = img_path.name
except: except Exception:
recipe["image"] = "no image" recipe["image"] = "no image"
return recipe return recipe

View File

@ -1,6 +1,6 @@
from mealie.core.config import app_dirs, settings from mealie.core.config import app_dirs, settings
#! I don't like it either! # ! I don't like it either!
SQLITE_FILE = app_dirs.SQLITE_DIR.joinpath("test.db") SQLITE_FILE = app_dirs.SQLITE_DIR.joinpath("test.db")
SQLITE_FILE.unlink(missing_ok=True) SQLITE_FILE.unlink(missing_ok=True)

Binary file not shown.

Binary file not shown.

View File

@ -34,11 +34,6 @@ def test_create_no_image(api_client: TestClient, api_routes: AppRoutes, token, r
assert json.loads(response.text) == "banana-bread-no-image" assert json.loads(response.text) == "banana-bread-no-image"
def test_read_all_post(api_client: TestClient, api_routes: AppRoutes):
response = api_client.post(api_routes.recipes, json={"properties": ["slug", "description", "rating"]})
assert response.status_code == 200
@pytest.mark.parametrize("recipe_data", recipe_test_data) @pytest.mark.parametrize("recipe_data", recipe_test_data)
def test_read_update(api_client: TestClient, api_routes: AppRoutes, recipe_data, token): def test_read_update(api_client: TestClient, api_routes: AppRoutes, recipe_data, token):
recipe_url = api_routes.recipes_recipe_slug(recipe_data.expected_slug) recipe_url = api_routes.recipes_recipe_slug(recipe_data.expected_slug)