From 8311db7e6039c0e4e528fcf7d31a39d61818ebd5 Mon Sep 17 00:00:00 2001 From: Michael Genson <71845777+michael-genson@users.noreply.github.com> Date: Mon, 11 Dec 2023 12:20:57 -0600 Subject: [PATCH] fix: Prevent Creating Groups With No Name (#2803) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * prevent creating groups with no name * add db fix fro groups with no name * moved non-actionable fix logs to debug level * 🧹 * use id as default name to avoid collisions * simplified group name constraint * removed redundant import --- mealie/db/fixes/fix_group_with_no_name.py | 50 +++++++++++++++++++++++ mealie/db/fixes/fix_slug_foods.py | 2 +- mealie/db/init_db.py | 2 + mealie/schema/user/user.py | 2 +- 4 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 mealie/db/fixes/fix_group_with_no_name.py diff --git a/mealie/db/fixes/fix_group_with_no_name.py b/mealie/db/fixes/fix_group_with_no_name.py new file mode 100644 index 000000000000..992c5483be20 --- /dev/null +++ b/mealie/db/fixes/fix_group_with_no_name.py @@ -0,0 +1,50 @@ +from slugify import slugify +from sqlalchemy.exc import IntegrityError +from sqlalchemy.orm import Session + +from mealie.core import root_logger +from mealie.db.models.group import Group + +logger = root_logger.get_logger("init_db") + + +def _do_fix(session: Session, group: Group, counter: int): + if counter: + new_name = f"{group.id} ({counter})" + else: + new_name = str(group.id) + + group.name = new_name + group.slug = slugify(group.name) + session.commit() + + +def fix_group_with_no_name(session: Session): + groups = session.query(Group).filter(Group.name == "").all() + if not groups: + logger.debug("No group found with an empty name; skipping fix") + return + + logger.info( + f'{len(groups)} {"group" if len(groups) == 1 else "groups"} found with a missing name; ' + f"applying default name" + ) + + offset = 0 + for i, group in enumerate(groups): + attempts = 0 + while True: + if attempts >= 3: + raise Exception( + f'Unable to fix empty group name for group_id "{group.id}": too many attempts ({attempts})' + ) + + counter = i + offset + try: + _do_fix(session, group, counter) + break + except IntegrityError: + session.rollback() + attempts += 1 + offset += 1 + continue diff --git a/mealie/db/fixes/fix_slug_foods.py b/mealie/db/fixes/fix_slug_foods.py index 95d60b05c3be..74edec6c8e7a 100644 --- a/mealie/db/fixes/fix_slug_foods.py +++ b/mealie/db/fixes/fix_slug_foods.py @@ -13,7 +13,7 @@ def fix_slug_food_names(db: AllRepositories): logger = root_logger.get_logger("init_db") if not food: - logger.info(f"No food found with slug: '{check_for_food}' skipping fix") + logger.debug(f"No food found with slug: '{check_for_food}' skipping fix") return all_foods = db.ingredient_foods.get_all() diff --git a/mealie/db/init_db.py b/mealie/db/init_db.py index 90dd91a8a2a1..3ac888c18110 100644 --- a/mealie/db/init_db.py +++ b/mealie/db/init_db.py @@ -10,6 +10,7 @@ from alembic.runtime import migration from mealie.core import root_logger from mealie.core.config import get_app_settings from mealie.db.db_setup import session_context +from mealie.db.fixes.fix_group_with_no_name import fix_group_with_no_name from mealie.db.fixes.fix_slug_foods import fix_slug_food_names from mealie.repos.all_repositories import get_repositories from mealie.repos.repository_factory import AllRepositories @@ -104,6 +105,7 @@ def main(): init_db(db) safe_try(lambda: fix_slug_food_names(db)) + safe_try(lambda: fix_group_with_no_name(session)) if __name__ == "__main__": diff --git a/mealie/schema/user/user.py b/mealie/schema/user/user.py index d30890802a0f..21ce7a593539 100644 --- a/mealie/schema/user/user.py +++ b/mealie/schema/user/user.py @@ -61,7 +61,7 @@ class ChangePassword(MealieModel): class GroupBase(MealieModel): - name: str + name: constr(strip_whitespace=True, min_length=1) # type: ignore class Config: orm_mode = True