diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index d9c3087d7..151e21fef 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -12,7 +12,7 @@ repos:
exclude: ^tests/data/
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
- rev: v0.12.0
+ rev: v0.12.1
hooks:
- id: ruff
- id: ruff-format
diff --git a/frontend/components/Domain/Recipe/RecipeCard.vue b/frontend/components/Domain/Recipe/RecipeCard.vue
index 50adc5e5b..ac93a8472 100644
--- a/frontend/components/Domain/Recipe/RecipeCard.vue
+++ b/frontend/components/Domain/Recipe/RecipeCard.vue
@@ -1,101 +1,104 @@
+
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/components/Domain/Recipe/RecipeTimeline.vue b/frontend/components/Domain/Recipe/RecipeTimeline.vue
index 9c13700f7..49ed1694d 100644
--- a/frontend/components/Domain/Recipe/RecipeTimeline.vue
+++ b/frontend/components/Domain/Recipe/RecipeTimeline.vue
@@ -69,7 +69,8 @@
:style="maxHeight ? `max-height: ${maxHeight}; overflow-y: auto;` : ''"
>
diff --git a/frontend/composables/use-user.ts b/frontend/composables/use-user.ts
index b4c2485a3..35341c99d 100644
--- a/frontend/composables/use-user.ts
+++ b/frontend/composables/use-user.ts
@@ -1,4 +1,4 @@
-import { useUserApi } from "~/composables/api";
+import { useAdminApi } from "~/composables/api";
import type { UserIn, UserOut } from "~/lib/api/types/user";
/*
@@ -8,7 +8,7 @@ to control whether the object is substantiated... but some of the others rely on
*/
export const useAllUsers = function () {
- const api = useUserApi();
+ const api = useAdminApi();
const asyncKey = String(Date.now());
const { data: users, refresh: refreshAllUsers } = useLazyAsyncData(asyncKey, async () => {
const { data } = await api.users.getAll();
@@ -24,7 +24,7 @@ export const useAllUsers = function () {
};
export const useUser = function (refreshFunc: CallableFunction | null = null) {
- const api = useUserApi();
+ const api = useAdminApi();
const loading = ref(false);
function getUser(id: string) {
diff --git a/mealie/routes/admin/admin_management_users.py b/mealie/routes/admin/admin_management_users.py
index 68dd5d8ce..d87209df5 100644
--- a/mealie/routes/admin/admin_management_users.py
+++ b/mealie/routes/admin/admin_management_users.py
@@ -1,6 +1,6 @@
from functools import cached_property
-from fastapi import APIRouter, Depends, HTTPException
+from fastapi import APIRouter, Depends, HTTPException, status
from pydantic import UUID4
from mealie.core import security
@@ -42,6 +42,11 @@ class AdminUserManagementRoutes(BaseAdminController):
@router.post("", response_model=UserOut, status_code=201)
def create_one(self, data: UserIn):
+ if self.repos.users.get_by_username(data.username):
+ raise HTTPException(status.HTTP_409_CONFLICT, {"message": self.t("exceptions.username-conflict-error")})
+ elif self.repos.users.get_one(data.email, "email"):
+ raise HTTPException(status.HTTP_409_CONFLICT, {"message": self.t("exceptions.email-conflict-error")})
+
data.password = security.hash_password(data.password)
return self.mixins.create_one(data)
diff --git a/mealie/routes/users/__init__.py b/mealie/routes/users/__init__.py
index f46c8a54a..962a68b12 100644
--- a/mealie/routes/users/__init__.py
+++ b/mealie/routes/users/__init__.py
@@ -9,7 +9,6 @@ router = APIRouter()
router.include_router(registration.router, prefix=user_prefix, tags=["Users: Registration"])
router.include_router(crud.user_router)
-router.include_router(crud.admin_router)
router.include_router(forgot_password.router, prefix=user_prefix, tags=["Users: Passwords"])
router.include_router(images.router, prefix=user_prefix, tags=["Users: Images"])
router.include_router(api_tokens.router)
diff --git a/mealie/routes/users/crud.py b/mealie/routes/users/crud.py
index a246b6fa3..45a571df5 100644
--- a/mealie/routes/users/crud.py
+++ b/mealie/routes/users/crud.py
@@ -1,52 +1,17 @@
-from fastapi import Depends, HTTPException, status
+from fastapi import HTTPException, status
from pydantic import UUID4
from mealie.core.security import hash_password
from mealie.core.security.providers.credentials_provider import CredentialsProvider
from mealie.db.models.users.users import AuthMethod
-from mealie.routes._base import BaseAdminController, BaseUserController, controller
-from mealie.routes._base.mixins import HttpRepo
-from mealie.routes._base.routers import AdminAPIRouter, UserAPIRouter
+from mealie.routes._base import BaseUserController, controller
+from mealie.routes._base.routers import UserAPIRouter
from mealie.routes.users._helpers import assert_user_change_allowed
from mealie.schema.response import ErrorResponse, SuccessResponse
-from mealie.schema.response.pagination import PaginationQuery
-from mealie.schema.user import ChangePassword, UserBase, UserIn, UserOut
-from mealie.schema.user.user import UserPagination, UserRatings, UserRatingSummary
+from mealie.schema.user import ChangePassword, UserBase, UserOut
+from mealie.schema.user.user import UserRatings, UserRatingSummary
user_router = UserAPIRouter(prefix="/users", tags=["Users: CRUD"])
-admin_router = AdminAPIRouter(prefix="/users", tags=["Users: Admin CRUD"])
-
-
-@controller(admin_router)
-class AdminUserController(BaseAdminController):
- @property
- def mixins(self) -> HttpRepo:
- return HttpRepo[UserIn, UserOut, UserBase](self.repos.users, self.logger)
-
- @admin_router.get("", response_model=UserPagination)
- def get_all(self, q: PaginationQuery = Depends(PaginationQuery)):
- """Returns all users from all groups"""
-
- response = self.repos.users.page_all(
- pagination=q,
- override=UserOut,
- )
-
- response.set_pagination_guides(admin_router.url_path_for("get_all"), q.model_dump())
- return response
-
- @admin_router.post("", response_model=UserOut, status_code=201)
- def create_user(self, new_user: UserIn):
- new_user.password = hash_password(new_user.password)
- return self.mixins.create_one(new_user)
-
- @admin_router.get("/{item_id}", response_model=UserOut)
- def get_user(self, item_id: UUID4):
- return self.mixins.get_one(item_id)
-
- @admin_router.delete("/{item_id}")
- def delete_user(self, item_id: UUID4):
- self.mixins.delete_one(item_id)
@controller(user_router)
diff --git a/poetry.lock b/poetry.lock
index 729697eeb..3aa27fca5 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1855,14 +1855,14 @@ signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"]
[[package]]
name = "openai"
-version = "1.92.2"
+version = "1.93.0"
description = "The official Python library for the openai API"
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
- {file = "openai-1.92.2-py3-none-any.whl", hash = "sha256:abb64bee7f2571709edf9a856f598ffe871730129a7d807a8a4d8d2958f5c842"},
- {file = "openai-1.92.2.tar.gz", hash = "sha256:b571a79fc7e165e7d00e6963a8a95eb5f42b60ac89fd316f1dc0a2dac5c6fae1"},
+ {file = "openai-1.93.0-py3-none-any.whl", hash = "sha256:3d746fe5498f0dd72e0d9ab706f26c91c0f646bf7459e5629af8ba7c9dbdf090"},
+ {file = "openai-1.93.0.tar.gz", hash = "sha256:988f31ade95e1ff0585af11cc5a64510225e4f5cd392698c675d0a9265b8e337"},
]
[package.dependencies]
diff --git a/tests/fixtures/fixture_users.py b/tests/fixtures/fixture_users.py
index 79283c5c6..0c621acb8 100644
--- a/tests/fixtures/fixture_users.py
+++ b/tests/fixtures/fixture_users.py
@@ -76,7 +76,7 @@ def h2_user(session: Session, admin_token, api_client: TestClient, unique_user:
"admin": False,
"tokens": [],
}
- response = api_client.post(api_routes.users, json=user_data, headers=admin_token)
+ response = api_client.post(api_routes.admin_users, json=user_data, headers=admin_token)
assert response.status_code == 201
# Log in as this user
@@ -135,7 +135,7 @@ def g2_user(session: Session, admin_token, api_client: TestClient):
}
api_client.post(api_routes.admin_groups, json={"name": group}, headers=admin_token)
- response = api_client.post(api_routes.users, json=create_data, headers=admin_token)
+ response = api_client.post(api_routes.admin_users, json=create_data, headers=admin_token)
assert response.status_code == 201
@@ -258,7 +258,7 @@ def user_tuple(session: Session, admin_token, api_client: TestClient) -> Generat
users_out = []
for usr in [create_data_1, create_data_2]:
- response = api_client.post(api_routes.users, json=usr, headers=admin_token)
+ response = api_client.post(api_routes.admin_users, json=usr, headers=admin_token)
assert response.status_code == 201
# Log in as this user
@@ -312,7 +312,7 @@ def user_token(admin_token, api_client: TestClient):
"tokens": [],
}
- response = api_client.post(api_routes.users, json=create_data, headers=admin_token)
+ response = api_client.post(api_routes.admin_users, json=create_data, headers=admin_token)
assert response.status_code == 201
diff --git a/tests/integration_tests/user_tests/test_user_api_token.py b/tests/integration_tests/user_tests/test_user_api_token.py
index 44635addb..56833ed0e 100644
--- a/tests/integration_tests/user_tests/test_user_api_token.py
+++ b/tests/integration_tests/user_tests/test_user_api_token.py
@@ -24,7 +24,7 @@ def test_api_token_private(api_client: TestClient, admin_token):
response = api_client.post(api_routes.users_api_tokens, json={"name": "Test API Token"}, headers=admin_token)
assert response.status_code == 201
- response = api_client.get(api_routes.users, headers=admin_token, params={"perPage": -1})
+ response = api_client.get(api_routes.admin_users, headers=admin_token, params={"perPage": -1})
assert response.status_code == 200
for user in response.json()["items"]:
for user_token in user["tokens"] or []:
@@ -39,7 +39,7 @@ def test_api_token_private(api_client: TestClient, admin_token):
def test_use_token(api_client: TestClient, long_live_token):
- response = api_client.get(api_routes.users, headers=long_live_token)
+ response = api_client.get(api_routes.admin_users, headers=long_live_token)
assert response.status_code == 200
diff --git a/tests/integration_tests/user_tests/test_user_crud.py b/tests/integration_tests/user_tests/test_user_crud.py
index 9d4a60e28..81a68bcc9 100644
--- a/tests/integration_tests/user_tests/test_user_crud.py
+++ b/tests/integration_tests/user_tests/test_user_crud.py
@@ -88,9 +88,11 @@ def test_user_update(api_client: TestClient, unique_user: TestUser, admin_user:
def test_admin_updates(api_client: TestClient, admin_user: TestUser, unique_user: TestUser):
- response = api_client.get(api_routes.users_item_id(unique_user.user_id), headers=admin_user.token)
+ response = api_client.get(api_routes.admin_users_item_id(unique_user.user_id), headers=admin_user.token)
+ assert response.status_code == 200
user = response.json()
- response = api_client.get(api_routes.users_item_id(admin_user.user_id), headers=admin_user.token)
+
+ response = api_client.get(api_routes.admin_users_item_id(admin_user.user_id), headers=admin_user.token)
admin = response.json()
# admin updating themselves