1
0
Fork 0
mirror of https://github.com/mealie-recipes/mealie.git synced 2025-08-05 13:35:23 +02:00

fix: Handle Data With Invalid User (#4325)

Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com>
This commit is contained in:
Michael Genson 2024-10-14 10:10:55 -05:00 committed by GitHub
parent 02791e294d
commit cba381cb67
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 66 additions and 0 deletions

View file

@ -1,18 +1,73 @@
from uuid import uuid4 from uuid import uuid4
from slugify import slugify from slugify import slugify
from sqlalchemy import and_, update
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from mealie.core import root_logger from mealie.core import root_logger
from mealie.db.models._model_base import SqlAlchemyBase
from mealie.db.models.group.group import Group from mealie.db.models.group.group import Group
from mealie.db.models.household.shopping_list import ShoppingList, ShoppingListMultiPurposeLabel from mealie.db.models.household.shopping_list import ShoppingList, ShoppingListMultiPurposeLabel
from mealie.db.models.labels import MultiPurposeLabel from mealie.db.models.labels import MultiPurposeLabel
from mealie.db.models.recipe.ingredient import IngredientFoodModel, IngredientUnitModel from mealie.db.models.recipe.ingredient import IngredientFoodModel, IngredientUnitModel
from mealie.db.models.recipe.recipe import RecipeModel from mealie.db.models.recipe.recipe import RecipeModel
from mealie.db.models.users.users import User
logger = root_logger.get_logger("init_db") logger = root_logger.get_logger("init_db")
def fix_dangling_refs(session: Session):
REASSIGN_REF_TABLES = ["group_meal_plans", "recipes", "shopping_lists"]
DELETE_REF_TABLES = ["long_live_tokens", "password_reset_tokens", "recipe_comments", "recipe_timeline_events"]
groups = session.query(Group).all()
for group in groups:
# Find an arbitrary admin user in the group
default_user = session.query(User).filter(User.group_id == group.id, User.admin == True).first() # noqa: E712 - required for SQLAlchemy comparison
if not default_user:
# If there is no admin user, just pick the first user
default_user = session.query(User).filter(User.group_id == group.id).first()
# If there are no users in the group, we can't do anything
if not default_user:
continue
valid_user_ids = {user.id for user in group.users}
for table_name in REASSIGN_REF_TABLES:
table = SqlAlchemyBase.metadata.tables[table_name]
update_stmt = (
update(table)
.where(
and_(
table.c.user_id.notin_(valid_user_ids),
table.c.group_id == group.id,
)
)
.values(user_id=default_user.id)
)
result = session.execute(update_stmt)
if result.rowcount:
logger.info(
f'Reassigned {result.rowcount} {"row" if result.rowcount == 1 else "rows"} '
f'in "{table_name}" table to default user ({default_user.id})'
)
for table_name in DELETE_REF_TABLES:
table = SqlAlchemyBase.metadata.tables[table_name]
delete_stmt = table.delete().where(table.c.user_id.notin_(valid_user_ids))
result = session.execute(delete_stmt)
if result.rowcount:
logger.info(
f'Deleted {result.rowcount} {"row" if result.rowcount == 1 else "rows"} '
f'in "{table_name}" table with invalid user ids'
)
session.commit()
def fix_recipe_normalized_search_properties(session: Session): def fix_recipe_normalized_search_properties(session: Session):
recipes = session.query(RecipeModel).all() recipes = session.query(RecipeModel).all()
recipes_fixed = False recipes_fixed = False
@ -144,6 +199,8 @@ def fix_normalized_unit_and_food_names(session: Session):
def fix_migration_data(session: Session): def fix_migration_data(session: Session):
logger.info("Checking for migration data fixes") logger.info("Checking for migration data fixes")
fix_dangling_refs(session)
fix_recipe_normalized_search_properties(session) fix_recipe_normalized_search_properties(session)
fix_shopping_list_label_settings(session) fix_shopping_list_label_settings(session)
fix_group_slugs(session) fix_group_slugs(session)

View file

@ -14,6 +14,7 @@ from sqlalchemy.orm import sessionmaker
from alembic import command from alembic import command
from alembic.config import Config from alembic.config import Config
from mealie.db import init_db from mealie.db import init_db
from mealie.db.fixes.fix_migration_data import fix_migration_data
from mealie.db.models._model_utils.guid import GUID from mealie.db.models._model_utils.guid import GUID
from mealie.services._base_service import BaseService from mealie.services._base_service import BaseService
@ -137,6 +138,14 @@ class AlchemyExporter(BaseService):
Returns the entire SQLAlchemy database as a python dictionary. This dictionary is wrapped by Returns the entire SQLAlchemy database as a python dictionary. This dictionary is wrapped by
jsonable_encoder to ensure that the object can be converted to a json string. jsonable_encoder to ensure that the object can be converted to a json string.
""" """
# run database fixes first so we aren't backing up bad data
with self.session_maker() as session:
try:
fix_migration_data(session)
except Exception:
self.logger.error("Error fixing migration data during export; continuing anyway")
with self.engine.connect() as connection: with self.engine.connect() as connection:
self.meta.reflect(bind=self.engine) # http://docs.sqlalchemy.org/en/rel_0_9/core/reflection.html self.meta.reflect(bind=self.engine) # http://docs.sqlalchemy.org/en/rel_0_9/core/reflection.html