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:
parent
5dafe8fbb5
commit
1dc051f562
38 changed files with 179 additions and 224 deletions
2
.flake8
2
.flake8
|
@ -1,6 +1,6 @@
|
|||
[flake8]
|
||||
ignore = [
|
||||
E501 # Line Length - See Black Config in pyproject.toml
|
||||
E722 # Bare Exception | Temporary
|
||||
E402 # Import Not at Top of File
|
||||
]
|
||||
exclude = _all_models.py
|
||||
|
|
23
.gitignore
vendored
23
.gitignore
vendored
|
@ -3,13 +3,11 @@
|
|||
*__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# frontend/.env.development
|
||||
docs/site/
|
||||
mealie/temp/*
|
||||
mealie/temp/api.html
|
||||
.temp/
|
||||
*temp*
|
||||
.secret
|
||||
!*/components/Recipe/Parts
|
||||
|
||||
dev/data/backups/*
|
||||
dev/data/debug/*
|
||||
|
@ -17,14 +15,6 @@ dev/data/img/*
|
|||
dev/data/migration/*
|
||||
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
|
||||
node_modules
|
||||
|
||||
|
@ -148,16 +138,11 @@ ENV/
|
|||
# mypy
|
||||
.mypy_cache/
|
||||
|
||||
# IDE settings
|
||||
# .vscode/
|
||||
|
||||
# Node Modules
|
||||
node_modules/
|
||||
mealie/data/debug/last_recipe.json
|
||||
*.db
|
||||
*.sqlite
|
||||
dev/data/db/test.db
|
||||
scratch.py
|
||||
dev/data/backups/dev_sample_data*.zip
|
||||
dev/data/backups/dev_sample_data*.zip
|
||||
!dev/data/backups/test*.zip
|
||||
dev/data/recipes/*
|
||||
dev/data/recipes/*
|
|
@ -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>
|
|
@ -8,6 +8,11 @@
|
|||
:slug="value.slug"
|
||||
@refresh="$emit('upload')"
|
||||
/>
|
||||
<SettingsMenu
|
||||
class="my-1 mx-1"
|
||||
@upload="uploadImage"
|
||||
:value="value.settings"
|
||||
/>
|
||||
</v-row>
|
||||
<v-row dense>
|
||||
<v-col>
|
||||
|
@ -122,6 +127,7 @@ import Instructions from "@/components/Recipe/Parts/Instructions";
|
|||
import Ingredients from "@/components/Recipe/Parts/Ingredients";
|
||||
import Assets from "@/components/Recipe/Parts/Assets.vue";
|
||||
import Notes from "@/components/Recipe/Parts/Notes.vue";
|
||||
import SettingsMenu from "@/components/Recipe/Parts/Helpers/SettingsMenu.vue";
|
||||
export default {
|
||||
components: {
|
||||
BulkAdd,
|
||||
|
@ -133,6 +139,7 @@ export default {
|
|||
Ingredients,
|
||||
Assets,
|
||||
Notes,
|
||||
SettingsMenu,
|
||||
},
|
||||
props: {
|
||||
value: Object,
|
||||
|
|
|
@ -3,13 +3,10 @@ from fastapi import FastAPI
|
|||
|
||||
from mealie.core import root_logger
|
||||
from mealie.core.config import APP_VERSION, settings
|
||||
from mealie.routes import (backup_routes, debug_routes, migration_routes,
|
||||
theme_routes, utility_routes)
|
||||
from mealie.routes import backup_routes, debug_routes, migration_routes, theme_routes, utility_routes
|
||||
from mealie.routes.groups import groups
|
||||
from mealie.routes.mealplans import mealplans
|
||||
from mealie.routes.recipe import (all_recipe_routes, category_routes,
|
||||
recipe_assets, recipe_crud_routes,
|
||||
tag_routes)
|
||||
from mealie.routes.recipe import router as recipe_router
|
||||
from mealie.routes.site_settings import all_settings
|
||||
from mealie.routes.users import users
|
||||
|
||||
|
@ -30,15 +27,10 @@ def start_scheduler():
|
|||
|
||||
def api_routers():
|
||||
# Authentication
|
||||
app.include_router(utility_routes.router)
|
||||
app.include_router(users.router)
|
||||
app.include_router(groups.router)
|
||||
# Recipes
|
||||
app.include_router(all_recipe_routes.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)
|
||||
app.include_router(recipe_router)
|
||||
# Meal Routes
|
||||
app.include_router(mealplans.router)
|
||||
# Settings Routes
|
||||
|
@ -49,6 +41,7 @@ def api_routers():
|
|||
# Migration Routes
|
||||
app.include_router(migration_routes.router)
|
||||
app.include_router(debug_routes.router)
|
||||
app.include_router(utility_routes.router)
|
||||
|
||||
|
||||
api_routers()
|
||||
|
|
|
@ -120,7 +120,7 @@ class AppSettings(BaseSettings):
|
|||
DEFAULT_EMAIL: str = "changeme@email.com"
|
||||
DEFAULT_PASSWORD: str = "MyPassword"
|
||||
|
||||
TOKEN_TIME: int = 2 # Time in Hours
|
||||
TOKEN_TIME: int = 2 # Time in Hours
|
||||
|
||||
# Not Used!
|
||||
SFTP_USERNAME: Optional[str]
|
||||
|
|
|
@ -14,7 +14,7 @@ ALGORITHM = "HS256"
|
|||
def create_access_token(data: dict(), expires_delta: timedelta = None) -> str:
|
||||
to_encode = data.copy()
|
||||
expires_delta = expires_delta or timedelta(hours=settings.TOKEN_TIME)
|
||||
|
||||
|
||||
expire = datetime.utcnow() + expires_delta
|
||||
|
||||
to_encode.update({"exp": expire})
|
||||
|
|
|
@ -137,4 +137,3 @@ class BaseDocument:
|
|||
|
||||
session.delete(result)
|
||||
session.commit()
|
||||
|
||||
|
|
|
@ -1,17 +1,15 @@
|
|||
import operator
|
||||
import shutil
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status
|
||||
from mealie.core.config import app_dirs
|
||||
from mealie.core.security import create_file_token
|
||||
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.services.backups import imports
|
||||
from mealie.services.backups.exports import backup_all
|
||||
from sqlalchemy.orm.session import Session
|
||||
from starlette.responses import FileResponse
|
||||
|
||||
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,
|
||||
)
|
||||
return {"export_path": export_path}
|
||||
except:
|
||||
raise HTTPException( status.HTTP_500_INTERNAL_SERVER_ERROR )
|
||||
except Exception:
|
||||
raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
|
||||
@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)
|
||||
|
||||
if not dest.is_file:
|
||||
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
||||
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
@router.get("/{file_name}/download")
|
||||
|
@ -95,8 +92,8 @@ def delete_backup(file_name: str):
|
|||
file_path = app_dirs.BACKUP_DIR.joinpath(file_name)
|
||||
|
||||
if not file_path.is_file():
|
||||
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||
try:
|
||||
file_path.unlink()
|
||||
except:
|
||||
raise HTTPException( status.HTTP_500_INTERNAL_SERVER_ERROR )
|
||||
except Exception:
|
||||
raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import json
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from mealie.core.config import APP_VERSION, app_dirs, settings
|
||||
from mealie.core.root_logger import LOGGER_FILE
|
||||
|
|
|
@ -39,8 +39,8 @@ async def create_group(
|
|||
|
||||
try:
|
||||
db.groups.create(session, group_data.dict())
|
||||
except:
|
||||
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
||||
except Exception:
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
@router.put("/{id}")
|
||||
|
@ -61,23 +61,14 @@ async def delete_user_group(
|
|||
""" Removes a user group from the database """
|
||||
|
||||
if id == 1:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail='DEFAULT_GROUP'
|
||||
)
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="DEFAULT_GROUP")
|
||||
|
||||
group: GroupInDB = db.groups.get(session, id)
|
||||
|
||||
if not group:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail='GROUP_NOT_FOUND'
|
||||
)
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="GROUP_NOT_FOUND")
|
||||
|
||||
if not group.users == []:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail='GROUP_WITH_USERS'
|
||||
)
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="GROUP_WITH_USERS")
|
||||
|
||||
db.groups.delete(session, id)
|
||||
|
|
|
@ -43,8 +43,8 @@ def update_meal_plan(
|
|||
processed_plan = MealPlanInDB(uid=plan_id, **processed_plan.dict())
|
||||
try:
|
||||
db.meals.update(session, plan_id, processed_plan.dict())
|
||||
except:
|
||||
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
||||
except Exception:
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
@router.delete("/{plan_id}")
|
||||
|
@ -53,8 +53,8 @@ def delete_meal_plan(plan_id, session: Session = Depends(generate_session), curr
|
|||
|
||||
try:
|
||||
db.meals.delete(session, plan_id)
|
||||
except:
|
||||
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
||||
except Exception:
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
@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:
|
||||
recipe_image = image.read_image(recipe.slug, image_type=image.IMG_OPTIONS.ORIGINAL_IMAGE)
|
||||
else:
|
||||
raise HTTPException( status.HTTP_404_NOT_FOUND )
|
||||
raise HTTPException(status.HTTP_404_NOT_FOUND)
|
||||
if recipe_image:
|
||||
return FileResponse(recipe_image)
|
||||
else:
|
||||
raise HTTPException( status.HTTP_404_NOT_FOUND )
|
||||
raise HTTPException(status.HTTP_404_NOT_FOUND)
|
||||
|
|
|
@ -9,6 +9,7 @@ from mealie.routes.deps import get_current_user
|
|||
from mealie.schema.migration import MigrationFile, Migrations
|
||||
from mealie.services.migrations import migration
|
||||
from sqlalchemy.orm.session import Session
|
||||
from fastapi import HTTPException
|
||||
|
||||
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():
|
||||
shutil.rmtree(remove_path)
|
||||
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)
|
||||
|
@ -67,4 +67,4 @@ def upload_nextcloud_zipfile(import_type: migration.Migration, archive: UploadFi
|
|||
shutil.copyfileobj(archive.file, buffer)
|
||||
|
||||
if not dest.is_file:
|
||||
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||
|
|
|
@ -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)
|
|
@ -1,9 +1,7 @@
|
|||
from typing import List, Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
from fastapi import APIRouter, Depends
|
||||
from mealie.db.database import db
|
||||
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 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)
|
||||
|
||||
|
||||
@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")
|
||||
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 """
|
||||
|
|
|
@ -37,8 +37,8 @@ async def create_recipe_category(
|
|||
|
||||
try:
|
||||
return db.categories.create(session, category.dict())
|
||||
except:
|
||||
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
||||
except Exception:
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
@router.put("/{category}", response_model=RecipeCategoryResponse)
|
||||
|
@ -52,8 +52,8 @@ async def update_recipe_category(
|
|||
|
||||
try:
|
||||
return db.categories.update(session, category, new_category.dict())
|
||||
except:
|
||||
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
||||
except Exception:
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
@router.delete("/{category}")
|
||||
|
@ -66,5 +66,5 @@ async def delete_recipe_category(
|
|||
|
||||
try:
|
||||
db.categories.delete(session, category)
|
||||
except:
|
||||
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
||||
except Exception:
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
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 mealie.core.config import app_dirs
|
||||
from mealie.db.database import db
|
||||
|
@ -11,7 +12,24 @@ from slugify import slugify
|
|||
from sqlalchemy.orm.session import Session
|
||||
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")
|
||||
|
@ -41,9 +59,9 @@ def upload_recipe_asset(
|
|||
shutil.copyfileobj(file.file, buffer)
|
||||
|
||||
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.assets.append(asset_in)
|
||||
db.recipes.update(session, recipe_slug, recipe.dict())
|
||||
return asset_in
|
||||
return asset_in
|
||||
|
|
|
@ -1,14 +1,10 @@
|
|||
from enum import Enum
|
||||
|
||||
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.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, 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 sqlalchemy.orm.session import Session
|
||||
|
||||
|
@ -101,25 +97,8 @@ def delete_recipe(
|
|||
try:
|
||||
db.recipes.delete(session, recipe_slug)
|
||||
delete_image(recipe_slug)
|
||||
except:
|
||||
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 )
|
||||
except Exception:
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
@router.put("/{recipe_slug}/image")
|
||||
|
|
|
@ -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.db_setup import generate_session
|
||||
from mealie.routes.deps import get_current_user
|
||||
|
@ -7,10 +7,7 @@ from sqlalchemy.orm.session import Session
|
|||
|
||||
router = APIRouter(tags=["Recipes"])
|
||||
|
||||
router = APIRouter(
|
||||
prefix="/api/tags",
|
||||
tags=["Recipe Tags"],
|
||||
)
|
||||
router = APIRouter(prefix="/api/tags", tags=["Recipe Tags"])
|
||||
|
||||
|
||||
@router.get("")
|
||||
|
@ -59,5 +56,5 @@ async def delete_recipe_tag(
|
|||
|
||||
try:
|
||||
db.tags.delete(session, tag)
|
||||
except:
|
||||
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
||||
except Exception:
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||
|
|
|
@ -21,7 +21,6 @@ def create_theme(data: SiteTheme, session: Session = Depends(generate_session),
|
|||
db.themes.create(session, data.dict())
|
||||
|
||||
|
||||
|
||||
@router.get("/themes/{theme_name}")
|
||||
def get_single_theme(theme_name: str, session: Session = Depends(generate_session)):
|
||||
""" Returns a named theme """
|
||||
|
@ -44,5 +43,5 @@ def delete_theme(theme_name: str, session: Session = Depends(generate_session),
|
|||
""" Deletes theme from the database """
|
||||
try:
|
||||
db.themes.delete(session, theme_name)
|
||||
except:
|
||||
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
||||
except Exception:
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
from datetime import timedelta
|
||||
|
||||
from fastapi import APIRouter, Depends, status
|
||||
from fastapi.exceptions import HTTPException
|
||||
from fastapi.security import OAuth2PasswordRequestForm
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import shutil
|
||||
from datetime import timedelta
|
||||
|
||||
from fastapi import APIRouter, Depends, File, UploadFile, status, HTTPException
|
||||
from fastapi.responses import FileResponse
|
||||
|
@ -34,8 +33,8 @@ async def get_all_users(
|
|||
):
|
||||
|
||||
if not current_user.admin:
|
||||
raise HTTPException( status.HTTP_403_FORBIDDEN )
|
||||
|
||||
raise HTTPException(status.HTTP_403_FORBIDDEN)
|
||||
|
||||
return db.users.get_all(session)
|
||||
|
||||
|
||||
|
@ -67,7 +66,6 @@ async def reset_user_password(
|
|||
db.users.update_password(session, id, new_password)
|
||||
|
||||
|
||||
|
||||
@router.put("/{id}")
|
||||
async def update_user(
|
||||
id: int,
|
||||
|
@ -109,7 +107,7 @@ async def update_user_image(
|
|||
|
||||
try:
|
||||
[x.unlink() for x in app_dirs.USER_DIR.join(id).glob("profile_image.*")]
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
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)
|
||||
|
||||
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")
|
||||
|
@ -133,12 +131,11 @@ async def update_password(
|
|||
match_passwords = verify_password(password_change.current_password, current_user.password)
|
||||
match_id = current_user.id == id
|
||||
|
||||
if not ( match_passwords and match_id ):
|
||||
raise HTTPException( status.HTTP_401_UNAUTHORIZED )
|
||||
if not (match_passwords and match_id):
|
||||
raise HTTPException(status.HTTP_401_UNAUTHORIZED)
|
||||
|
||||
new_password = get_password_hash(password_change.new_password)
|
||||
db.users.update_password(session, id, new_password)
|
||||
|
||||
|
||||
|
||||
@router.delete("/{id}")
|
||||
|
@ -150,13 +147,10 @@ async def delete_user(
|
|||
""" Removes a user from the database. Must be the current user or a super user"""
|
||||
|
||||
if id == 1:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail='SUPER_USER'
|
||||
)
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="SUPER_USER")
|
||||
|
||||
if current_user.id == id or current_user.admin:
|
||||
try:
|
||||
db.users.delete(session, id)
|
||||
except:
|
||||
raise HTTPException( status.HTTP_400_BAD_REQUEST )
|
||||
except Exception:
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||
|
|
|
@ -8,6 +8,7 @@ from mealie.routes.deps import get_current_user
|
|||
from mealie.schema.sign_up import SignUpIn, SignUpOut, SignUpToken
|
||||
from mealie.schema.user import UserIn, UserInDB
|
||||
from sqlalchemy.orm.session import Session
|
||||
from fastapi import HTTPException, status
|
||||
|
||||
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 """
|
||||
|
||||
if not current_user.admin:
|
||||
raise HTTPException( status.HTTP_403_FORBIDDEN )
|
||||
raise HTTPException(status.HTTP_403_FORBIDDEN)
|
||||
|
||||
sign_up = {
|
||||
"token": str(uuid.uuid1().hex),
|
||||
|
@ -43,7 +44,6 @@ async def create_user_sign_up_key(
|
|||
return db.sign_ups.create(session, sign_up)
|
||||
|
||||
|
||||
|
||||
@router.post("/{token}")
|
||||
async def create_user_with_token(
|
||||
token: str,
|
||||
|
@ -55,12 +55,12 @@ async def create_user_with_token(
|
|||
# Validate Token
|
||||
db_entry: SignUpOut = db.sign_ups.get(session, token, limit=1)
|
||||
if not db_entry:
|
||||
raise HTTPException( status.HTTP_401_UNAUTHORIZED )
|
||||
raise HTTPException(status.HTTP_401_UNAUTHORIZED)
|
||||
|
||||
# Create User
|
||||
new_user.admin = db_entry.admin
|
||||
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
|
||||
db.sign_ups.delete(session, token)
|
||||
|
@ -74,6 +74,6 @@ async def delete_token(
|
|||
):
|
||||
""" Removed a token from the database """
|
||||
if not current_user.admin:
|
||||
raise HTTPException( status.HTTP_403_FORBIDDEN )
|
||||
|
||||
raise HTTPException(status.HTTP_403_FORBIDDEN)
|
||||
|
||||
db.sign_ups.delete(session, token)
|
||||
|
|
|
@ -4,16 +4,17 @@ from typing import Optional
|
|||
from fastapi import APIRouter, Depends
|
||||
from mealie.routes.deps import validate_file_token
|
||||
from starlette.responses import FileResponse
|
||||
from fastapi import HTTPException, status
|
||||
|
||||
router = APIRouter(prefix="/api/utils", tags=["Utils"], include_in_schema=True)
|
||||
|
||||
|
||||
@router.get("/download")
|
||||
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
|
||||
system. """
|
||||
"""Uses a file token obtained by an active user to retrieve a file from the operating
|
||||
system."""
|
||||
print("File Name:", file_path)
|
||||
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)
|
||||
|
|
|
@ -37,7 +37,7 @@ class ExportDatabase:
|
|||
|
||||
try:
|
||||
self.templates = [app_dirs.TEMPLATE_DIR.joinpath(x) for x in templates]
|
||||
except:
|
||||
except Exception:
|
||||
self.templates = False
|
||||
logger.info("No Jinja2 Templates Registered for Export")
|
||||
|
||||
|
|
|
@ -84,20 +84,20 @@ class ImportDatabase:
|
|||
try:
|
||||
del recipe_dict["_id"]
|
||||
del recipe_dict["date_added"]
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
# Migration from list to Object Type Data
|
||||
try:
|
||||
if "" in recipe_dict["tags"]:
|
||||
recipe_dict["tags"] = [tag for tag in recipe_dict["tags"] if tag != ""]
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
if "" in recipe_dict["categories"]:
|
||||
recipe_dict["categories"] = [cat for cat in recipe_dict["categories"] if cat != ""]
|
||||
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if type(recipe_dict["extras"]) == list:
|
||||
|
|
|
@ -54,7 +54,7 @@ def rename_image(original_slug, new_slug) -> Path:
|
|||
def write_image(recipe_slug: str, file_data: bytes, extension: str) -> Path:
|
||||
try:
|
||||
delete_image(recipe_slug)
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
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:
|
||||
r = requests.get(image_url, stream=True)
|
||||
except:
|
||||
except Exception:
|
||||
logger.exception("Fatal Image Request Exception")
|
||||
return None
|
||||
|
||||
|
|
|
@ -35,14 +35,15 @@ def minify_image(image_file: Path) -> ImageSizes:
|
|||
min_dest (Path): FULL Destination File Path
|
||||
tiny_dest (Path): FULL Destination File Path
|
||||
"""
|
||||
|
||||
def cleanup(dir: Path) -> None:
|
||||
for file in dir.glob("*.*"):
|
||||
if file.suffix != ".webp":
|
||||
file.unlink()
|
||||
|
||||
org_dest = image_file.parent.joinpath(f"original.webp")
|
||||
min_dest = image_file.parent.joinpath(f"min-original.webp")
|
||||
tiny_dest = image_file.parent.joinpath(f"tiny-original.webp")
|
||||
org_dest = image_file.parent.joinpath("original.webp")
|
||||
min_dest = image_file.parent.joinpath("min-original.webp")
|
||||
tiny_dest = image_file.parent.joinpath("tiny-original.webp")
|
||||
|
||||
if min_dest.exists() and tiny_dest.exists() and org_dest.exists():
|
||||
return
|
||||
|
|
|
@ -24,7 +24,7 @@ def process_meals(session: Session, meal_plan_base: MealPlanIn) -> MealPlanProce
|
|||
description=recipe.description,
|
||||
)
|
||||
|
||||
except:
|
||||
except Exception:
|
||||
|
||||
meal_data = MealOut(
|
||||
date=meal_plan_base.startDate + timedelta(days=x),
|
||||
|
|
|
@ -8,7 +8,7 @@ from mealie.core import root_logger
|
|||
from mealie.db.database import db
|
||||
from mealie.schema.migration import MigrationImport
|
||||
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.utils.unzip import unpack_zip
|
||||
from pydantic import BaseModel
|
||||
|
|
|
@ -115,7 +115,7 @@ class Cleaner:
|
|||
for step in instructions
|
||||
if step["type"].find("HowToStep") > -1
|
||||
]
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
else:
|
||||
|
|
|
@ -21,7 +21,7 @@ def basic_recipe_from_opengraph(html: str, url: str) -> dict:
|
|||
data = extruct.extract(html, base_url=base_url)
|
||||
try:
|
||||
properties = data["opengraph"][0]["properties"]
|
||||
except:
|
||||
except Exception:
|
||||
return
|
||||
|
||||
return {
|
||||
|
|
|
@ -67,7 +67,7 @@ def download_image_for_recipe(recipe: dict) -> dict:
|
|||
try:
|
||||
img_path = scrape_image(recipe.get("image"), recipe.get("slug"))
|
||||
recipe["image"] = img_path.name
|
||||
except:
|
||||
except Exception:
|
||||
recipe["image"] = "no image"
|
||||
|
||||
return recipe
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
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.unlink(missing_ok=True)
|
||||
|
||||
|
|
BIN
tests/data/migrations/chowdown/chowdown-gh-pages.zip
Normal file
BIN
tests/data/migrations/chowdown/chowdown-gh-pages.zip
Normal file
Binary file not shown.
BIN
tests/data/migrations/nextcloud/new_nextcloud.zip
Normal file
BIN
tests/data/migrations/nextcloud/new_nextcloud.zip
Normal file
Binary file not shown.
|
@ -37,7 +37,7 @@ def test_update_group(api_client: TestClient, api_routes: AppRoutes, token):
|
|||
# Test Update
|
||||
response = api_client.put(api_routes.groups_id(2), json=new_data, headers=token)
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
# Validate Changes
|
||||
response = api_client.get(api_routes.groups, headers=token)
|
||||
all_groups = json.loads(response.text)
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
||||
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)
|
||||
def test_read_update(api_client: TestClient, api_routes: AppRoutes, recipe_data, token):
|
||||
recipe_url = api_routes.recipes_recipe_slug(recipe_data.expected_slug)
|
||||
|
|
Loading…
Reference in a new issue