diff --git a/server/app/commands/create_superuser.py b/server/app/commands/create_superuser.py index 0d18b30..43c6be7 100644 --- a/server/app/commands/create_superuser.py +++ b/server/app/commands/create_superuser.py @@ -104,6 +104,22 @@ def _find_resource(db: Session, parent_id: int | None, name: str) -> AdminResour return db.scalars(stmt.order_by(AdminResource.id.asc())).first() +# Keep system management after all business top-level menus. +SYSTEM_MENU_SORT = 99 + + +def ensure_system_menu_order(db: Session) -> None: + """Move existing System directory to the end of the sidebar without recreating children.""" + directory = db.scalar( + select(AdminResource).where(AdminResource.parent_id.is_(None), AdminResource.name == "System") + ) + if directory is None: + return + if directory.sort != SYSTEM_MENU_SORT: + directory.sort = SYSTEM_MENU_SORT + db.flush() + + def _ensure_resource( db: Session, *, @@ -861,7 +877,7 @@ def seed_superuser(db: Session, username: str, password: str) -> None: component="", icon="ri:settings-3-line", permission="", - sort=1, + sort=SYSTEM_MENU_SORT, visible=True, ) resource_ids = [directory.id] diff --git a/server/app/main.py b/server/app/main.py index f55b8aa..7207186 100644 --- a/server/app/main.py +++ b/server/app/main.py @@ -57,6 +57,7 @@ def prepare_runtime() -> None: ensure_report_menu, ensure_discipline_menu, ensure_edu_menu, + ensure_system_menu_order, ensure_tech_menu, ) @@ -70,6 +71,7 @@ def prepare_runtime() -> None: ensure_discipline_menu(db) ensure_tech_menu(db) ensure_edu_menu(db) + ensure_system_menu_order(db) db.commit() finally: db.close() diff --git a/server/tests/test_rbac.py b/server/tests/test_rbac.py index 38508b8..8658976 100644 --- a/server/tests/test_rbac.py +++ b/server/tests/test_rbac.py @@ -704,6 +704,16 @@ def test_seed_superuser_is_idempotent(client: TestClient): assert directory is not None assert directory.path == "/system" assert directory.icon == "ri:settings-3-line" + assert directory.sort == 99 + other_top = db.scalars( + select(AdminResource).where( + AdminResource.parent_id.is_(None), + AdminResource.name != "System", + AdminResource.status == "active", + ) + ).all() + assert other_top + assert directory.sort > max(item.sort for item in other_top) page = db.scalar(select(AdminResource).where(AdminResource.parent_id == directory.id, AdminResource.name == "User")) assert page is not None assert page.component == "/system/user"