mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-12-28 05:05:12 -05:00
feat: ✨ add user recipe export functionality (#845)
* feat(frontend): ✨ add user recipe export functionality * remove depreciated folders * change/remove depreciated folders * add testing variable in config * add GUID support for group_id * improve testing feedback on 422 errors * remove/cleanup files/folders * initial user export support * delete unused css * update backup page UI * remove depreciated settings * feat: ✨ export download links * fix #813 * remove top level statements * show footer * add export purger to scheduler * update purge glob * fix meal-planner lockout * feat: ✨ add bulk delete/purge exports * style(frontend): 💄 update UI for site settings * feat: ✨ add version checker * update documentation Co-authored-by: hay-kot <hay-kot@pm.me>
This commit is contained in:
@@ -2,6 +2,7 @@ from fastapi import APIRouter, Depends
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from mealie.core.config import get_app_settings
|
||||
from mealie.core.release_checker import get_latest_version
|
||||
from mealie.core.settings.static import APP_VERSION
|
||||
from mealie.db.database import get_database
|
||||
from mealie.db.db_setup import generate_session
|
||||
@@ -18,6 +19,7 @@ async def get_app_info():
|
||||
return AdminAboutInfo(
|
||||
production=settings.PRODUCTION,
|
||||
version=APP_VERSION,
|
||||
versionLatest=get_latest_version(),
|
||||
demo_status=settings.IS_DEMO,
|
||||
api_port=settings.API_PORT,
|
||||
api_docs=settings.API_DOCS,
|
||||
@@ -49,4 +51,5 @@ async def check_app_config():
|
||||
email_ready=settings.SMTP_ENABLE,
|
||||
ldap_ready=settings.LDAP_ENABLED,
|
||||
base_url_set=url_set,
|
||||
is_up_to_date=get_latest_version() == APP_VERSION,
|
||||
)
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
from fastapi import BackgroundTasks, Depends, HTTPException, status
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from mealie.core.dependencies import get_current_user
|
||||
from mealie.db.database import get_database
|
||||
from mealie.db.db_setup import generate_session
|
||||
from mealie.routes.routers import AdminAPIRouter
|
||||
from mealie.schema.user import GroupBase, GroupInDB, PrivateUser, UpdateGroup
|
||||
from mealie.services.events import create_group_event
|
||||
|
||||
router = AdminAPIRouter(prefix="/groups")
|
||||
|
||||
|
||||
@router.get("", response_model=list[GroupInDB])
|
||||
async def get_all_groups(session: Session = Depends(generate_session)):
|
||||
"""Returns a list of all groups in the database"""
|
||||
db = get_database(session)
|
||||
return db.groups.get_all()
|
||||
|
||||
|
||||
@router.post("", status_code=status.HTTP_201_CREATED, response_model=GroupInDB)
|
||||
async def create_group(
|
||||
background_tasks: BackgroundTasks,
|
||||
group_data: GroupBase,
|
||||
session: Session = Depends(generate_session),
|
||||
):
|
||||
"""Creates a Group in the Database"""
|
||||
db = get_database(session)
|
||||
|
||||
try:
|
||||
new_group = db.groups.create(group_data.dict())
|
||||
background_tasks.add_task(create_group_event, "Group Created", f"'{group_data.name}' created", session)
|
||||
return new_group
|
||||
except Exception:
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
@router.put("/{id}")
|
||||
async def update_group_data(id: int, group_data: UpdateGroup, session: Session = Depends(generate_session)):
|
||||
"""Updates a User Group"""
|
||||
db = get_database(session)
|
||||
db.groups.update(id, group_data.dict())
|
||||
|
||||
|
||||
@router.delete("/{id}")
|
||||
async def delete_user_group(
|
||||
background_tasks: BackgroundTasks,
|
||||
id: int,
|
||||
current_user: PrivateUser = Depends(get_current_user),
|
||||
session: Session = Depends(generate_session),
|
||||
):
|
||||
"""Removes a user group from the database"""
|
||||
db = get_database(session)
|
||||
|
||||
if id == 1:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="DEFAULT_GROUP")
|
||||
|
||||
group: GroupInDB = db.groups.get(id)
|
||||
|
||||
if not group:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="GROUP_NOT_FOUND")
|
||||
|
||||
if group.users != []:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="GROUP_WITH_USERS")
|
||||
|
||||
background_tasks.add_task(
|
||||
create_group_event, "Group Deleted", f"'{group.name}' deleted by {current_user.full_name}", session
|
||||
)
|
||||
|
||||
db.groups.delete(id)
|
||||
@@ -18,7 +18,7 @@ def log_wrapper(request: Request, e):
|
||||
def register_debug_handler(app: FastAPI):
|
||||
settings = get_app_settings()
|
||||
|
||||
if settings.PRODUCTION:
|
||||
if settings.PRODUCTION and not settings.TESTING:
|
||||
return
|
||||
|
||||
@app.exception_handler(RequestValidationError)
|
||||
|
||||
@@ -13,3 +13,4 @@ router.include_router(recipe_crud_routes.user_router, prefix=prefix, tags=["Reci
|
||||
router.include_router(image_and_assets.user_router, prefix=prefix, tags=["Recipe: Images and Assets"])
|
||||
router.include_router(comments.router, prefix=prefix, tags=["Recipe: Comments"])
|
||||
router.include_router(bulk_actions.router, prefix=prefix, tags=["Recipe: Bulk Actions"])
|
||||
router.include_router(bulk_actions.export_router, prefix=prefix, tags=["Recipe: Bulk Exports"])
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from fastapi.responses import FileResponse
|
||||
|
||||
from mealie.core.dependencies.dependencies import temporary_zip_path
|
||||
from mealie.core.security import create_file_token
|
||||
from mealie.schema.group.group_exports import GroupDataExport
|
||||
from mealie.schema.recipe.recipe_bulk_actions import (
|
||||
AssignCategories,
|
||||
AssignTags,
|
||||
@@ -38,7 +41,10 @@ def bulk_delete_recipes(
|
||||
bulk_service.delete_recipes(delete_recipes.recipes)
|
||||
|
||||
|
||||
@router.post("/export", response_class=FileResponse)
|
||||
export_router = APIRouter(prefix="/bulk-actions")
|
||||
|
||||
|
||||
@export_router.post("/export")
|
||||
def bulk_export_recipes(
|
||||
export_recipes: ExportRecipes,
|
||||
temp_path=Depends(temporary_zip_path),
|
||||
@@ -46,4 +52,26 @@ def bulk_export_recipes(
|
||||
):
|
||||
bulk_service.export_recipes(temp_path, export_recipes.recipes)
|
||||
|
||||
return FileResponse(temp_path, filename="recipes.zip")
|
||||
# return FileResponse(temp_path, filename="recipes.zip")
|
||||
|
||||
|
||||
@export_router.get("/export/download")
|
||||
def get_exported_data_token(path: Path, _: RecipeBulkActions = Depends(RecipeBulkActions.private)):
|
||||
# return FileResponse(temp_path, filename="recipes.zip")
|
||||
"""Returns a token to download a file"""
|
||||
|
||||
return {"fileToken": create_file_token(path)}
|
||||
|
||||
|
||||
@export_router.get("/export", response_model=list[GroupDataExport])
|
||||
def get_exported_data(bulk_service: RecipeBulkActions = Depends(RecipeBulkActions.private)):
|
||||
return bulk_service.get_exports()
|
||||
|
||||
# return FileResponse(temp_path, filename="recipes.zip")
|
||||
|
||||
|
||||
@export_router.delete("/export/purge")
|
||||
def purge_export_data(bulk_service: RecipeBulkActions = Depends(RecipeBulkActions.private)):
|
||||
"""Remove all exports data, including items on disk without database entry"""
|
||||
amountDelete = bulk_service.purge_exports()
|
||||
return {"message": f"{amountDelete} exports deleted"}
|
||||
|
||||
@@ -21,8 +21,13 @@ logger = get_logger()
|
||||
|
||||
|
||||
@user_router.get("", response_model=list[RecipeSummary])
|
||||
async def get_all(start=0, limit=None, service: RecipeService = Depends(RecipeService.private)):
|
||||
json_compatible_item_data = jsonable_encoder(service.get_all(start, limit))
|
||||
async def get_all(
|
||||
start: int = 0,
|
||||
limit: int = None,
|
||||
load_foods: bool = False,
|
||||
service: RecipeService = Depends(RecipeService.private),
|
||||
):
|
||||
json_compatible_item_data = jsonable_encoder(service.get_all(start, limit, load_foods))
|
||||
return JSONResponse(content=json_compatible_item_data)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user