mirror of
				https://github.com/mealie-recipes/mealie.git
				synced 2025-10-31 10:13:32 -04:00 
			
		
		
		
	feat: add initial notification support
* Add updated recipe notification * Add recipe deleted notification * Add notifications translations * Shopping lists full c/u/d notifications * Add categories c/u/d notifications * Deal with None values in translation provider * Add tag c/u/d notifications * Add cookbook c/u/d notifications * use single key pairs for consistency with frontend * change dependency injection strategy * use generic update messages * use service to manage url generation server-side * use new strategies for messages * fix translator Co-authored-by: Miroito <alban.vachette@gmail.com>
This commit is contained in:
		| @@ -11,5 +11,12 @@ | ||||
|         "integrity-error": "Database integrity error", | ||||
|         "username-conflict-error": "This username is already taken", | ||||
|         "email-conflict-error": "This email is already in use" | ||||
|     }, | ||||
|     "notifications": { | ||||
|         "generic-created": "{name} was created", | ||||
|         "generic-updated": "{name} was updated", | ||||
|         "generic-created-with-url": "{name} has been created, {url}", | ||||
|         "generic-updated-with-url": "{name} has been updated, {url}", | ||||
|         "generic-deleted": "{name} has been created" | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -29,6 +29,10 @@ class JsonProvider: | ||||
|                 break | ||||
|  | ||||
|             if i == last: | ||||
|                 for key, value in kwargs.items(): | ||||
|                     if not value: | ||||
|                         value = "" | ||||
|                     translation_value = translation_value.replace("{" + key + "}", value) | ||||
|                 return translation_value | ||||
|  | ||||
|         return default or key | ||||
|   | ||||
| @@ -5,6 +5,8 @@ from fastapi import Depends | ||||
| from pydantic import UUID4 | ||||
|  | ||||
| from mealie.core.exceptions import mealie_registered_exceptions | ||||
| from mealie.lang import local_provider | ||||
| from mealie.lang.providers import Translator | ||||
| from mealie.repos.all_repositories import AllRepositories | ||||
| from mealie.routes._base.checks import OperationChecks | ||||
| from mealie.routes._base.dependencies import SharedDependencies | ||||
| @@ -19,6 +21,10 @@ class BasePublicController(ABC): | ||||
|     """ | ||||
|  | ||||
|     deps: SharedDependencies = Depends(SharedDependencies.public) | ||||
|     translator: Translator = Depends(local_provider) | ||||
|  | ||||
|     def __init__(self): | ||||
|         self.t = self.translator.t if self.translator else local_provider().t | ||||
|  | ||||
|  | ||||
| class BaseUserController(ABC): | ||||
| @@ -29,6 +35,10 @@ class BaseUserController(ABC): | ||||
|     """ | ||||
|  | ||||
|     deps: SharedDependencies = Depends(SharedDependencies.user) | ||||
|     translator: Translator = Depends(local_provider) | ||||
|  | ||||
|     def __init__(self): | ||||
|         self.t = self.translator.t if self.translator else local_provider().t | ||||
|  | ||||
|     def registered_exceptions(self, ex: type[Exception]) -> str: | ||||
|         registered = { | ||||
| @@ -65,3 +75,7 @@ class BaseAdminController(BaseUserController): | ||||
|     """ | ||||
|  | ||||
|     deps: SharedDependencies = Depends(SharedDependencies.admin) | ||||
|     translator: Translator = Depends(local_provider) | ||||
|  | ||||
|     def __init__(self): | ||||
|         self.t = self.translator.t if self.translator else local_provider().t | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| from functools import cached_property | ||||
|  | ||||
| from fastapi import APIRouter, HTTPException | ||||
| from fastapi import APIRouter, Depends, HTTPException | ||||
| from pydantic import UUID4 | ||||
|  | ||||
| from mealie.core.exceptions import mealie_registered_exceptions | ||||
| @@ -8,12 +8,17 @@ from mealie.routes._base import BaseUserController, controller | ||||
| from mealie.routes._base.mixins import HttpRepo | ||||
| from mealie.schema import mapper | ||||
| from mealie.schema.cookbook import CreateCookBook, ReadCookBook, RecipeCookBook, SaveCookBook, UpdateCookBook | ||||
| from mealie.services.event_bus_service.event_bus_service import EventBusService | ||||
| from mealie.services.event_bus_service.message_types import EventTypes | ||||
|  | ||||
| router = APIRouter(prefix="/groups/cookbooks", tags=["Groups: Cookbooks"]) | ||||
|  | ||||
|  | ||||
| @controller(router) | ||||
| class GroupCookbookController(BaseUserController): | ||||
|  | ||||
|     event_bus: EventBusService = Depends(EventBusService) | ||||
|  | ||||
|     @cached_property | ||||
|     def repo(self): | ||||
|         return self.deps.repos.cookbooks.by_group(self.group_id) | ||||
| @@ -41,7 +46,15 @@ class GroupCookbookController(BaseUserController): | ||||
|     @router.post("", response_model=ReadCookBook, status_code=201) | ||||
|     def create_one(self, data: CreateCookBook): | ||||
|         data = mapper.cast(data, SaveCookBook, group_id=self.group_id) | ||||
|         return self.mixins.create_one(data) | ||||
|         val = self.mixins.create_one(data) | ||||
|  | ||||
|         if val: | ||||
|             self.event_bus.dispatch( | ||||
|                 self.deps.acting_user.group_id, | ||||
|                 EventTypes.cookbook_created, | ||||
|                 msg=self.t("notifications.generic-created", name=val.name), | ||||
|             ) | ||||
|         return val | ||||
|  | ||||
|     @router.put("", response_model=list[ReadCookBook]) | ||||
|     def update_many(self, data: list[UpdateCookBook]): | ||||
| @@ -75,8 +88,23 @@ class GroupCookbookController(BaseUserController): | ||||
|  | ||||
|     @router.put("/{item_id}", response_model=ReadCookBook) | ||||
|     def update_one(self, item_id: str, data: CreateCookBook): | ||||
|         return self.mixins.update_one(data, item_id)  # type: ignore | ||||
|         val = self.mixins.update_one(data, item_id)  # type: ignore | ||||
|         if val: | ||||
|             self.event_bus.dispatch( | ||||
|                 self.deps.acting_user.group_id, | ||||
|                 EventTypes.cookbook_updated, | ||||
|                 msg=self.t("notifications.generic-updated", name=val.name), | ||||
|             ) | ||||
|  | ||||
|         return val | ||||
|  | ||||
|     @router.delete("/{item_id}", response_model=ReadCookBook) | ||||
|     def delete_one(self, item_id: str): | ||||
|         return self.mixins.delete_one(item_id) | ||||
|         val = self.mixins.delete_one(item_id) | ||||
|         if val: | ||||
|             self.event_bus.dispatch( | ||||
|                 self.deps.acting_user.group_id, | ||||
|                 EventTypes.cookbook_deleted, | ||||
|                 msg=self.t("notifications.generic-deleted", name=val.name), | ||||
|             ) | ||||
|         return val | ||||
|   | ||||
| @@ -28,6 +28,9 @@ item_router = APIRouter(prefix="/groups/shopping/items", tags=["Group: Shopping | ||||
|  | ||||
| @controller(item_router) | ||||
| class ShoppingListItemController(BaseUserController): | ||||
|  | ||||
|     event_bus: EventBusService = Depends(EventBusService) | ||||
|  | ||||
|     @cached_property | ||||
|     def service(self): | ||||
|         return ShoppingListService(self.repos) | ||||
| @@ -106,7 +109,7 @@ class ShoppingListController(BaseUserController): | ||||
|     # CRUD Operations | ||||
|  | ||||
|     @cached_property | ||||
|     def mixins(self) -> HttpRepo: | ||||
|     def mixins(self) -> HttpRepo[ShoppingListCreate, ShoppingListOut, ShoppingListSave]: | ||||
|         return HttpRepo(self.repo, self.deps.logger, self.registered_exceptions, "An unexpected error occurred.") | ||||
|  | ||||
|     @router.get("", response_model=list[ShoppingListSummary]) | ||||
| @@ -122,7 +125,7 @@ class ShoppingListController(BaseUserController): | ||||
|             self.event_bus.dispatch( | ||||
|                 self.deps.acting_user.group_id, | ||||
|                 EventTypes.shopping_list_created, | ||||
|                 msg="A new shopping list has been created.", | ||||
|                 msg=self.t("notifications.generic-created", name=val.name), | ||||
|             ) | ||||
|  | ||||
|         return val | ||||
| @@ -133,11 +136,25 @@ class ShoppingListController(BaseUserController): | ||||
|  | ||||
|     @router.put("/{item_id}", response_model=ShoppingListOut) | ||||
|     def update_one(self, item_id: UUID4, data: ShoppingListUpdate): | ||||
|         return self.mixins.update_one(data, item_id) | ||||
|         data = self.mixins.update_one(data, item_id)  # type: ignore | ||||
|         if data: | ||||
|             self.event_bus.dispatch( | ||||
|                 self.deps.acting_user.group_id, | ||||
|                 EventTypes.shopping_list_updated, | ||||
|                 msg=self.t("notifications.generic-updated", name=data.name), | ||||
|             ) | ||||
|         return data | ||||
|  | ||||
|     @router.delete("/{item_id}", response_model=ShoppingListOut) | ||||
|     def delete_one(self, item_id: UUID4): | ||||
|         return self.mixins.delete_one(item_id)  # type: ignore | ||||
|         data = self.mixins.delete_one(item_id)  # type: ignore | ||||
|         if data: | ||||
|             self.event_bus.dispatch( | ||||
|                 self.deps.acting_user.group_id, | ||||
|                 EventTypes.shopping_list_updated, | ||||
|                 msg=self.t("notifications.generic-deleted", name=data.name), | ||||
|             ) | ||||
|         return data | ||||
|  | ||||
|     # ======================================================================= | ||||
|     # Other Operations | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| from functools import cached_property | ||||
|  | ||||
| from fastapi import APIRouter | ||||
| from fastapi import APIRouter, Depends | ||||
| from pydantic import UUID4, BaseModel | ||||
|  | ||||
| from mealie.routes._base import BaseUserController, controller | ||||
| @@ -9,6 +9,9 @@ from mealie.schema import mapper | ||||
| from mealie.schema.recipe import CategoryIn, RecipeCategoryResponse | ||||
| from mealie.schema.recipe.recipe import RecipeCategory | ||||
| from mealie.schema.recipe.recipe_category import CategoryBase, CategorySave | ||||
| from mealie.services import urls | ||||
| from mealie.services.event_bus_service.event_bus_service import EventBusService | ||||
| from mealie.services.event_bus_service.message_types import EventTypes | ||||
|  | ||||
| router = APIRouter(prefix="/categories", tags=["Organizer: Categories"]) | ||||
|  | ||||
| @@ -24,6 +27,9 @@ class CategorySummary(BaseModel): | ||||
|  | ||||
| @controller(router) | ||||
| class RecipeCategoryController(BaseUserController): | ||||
|  | ||||
|     event_bus: EventBusService = Depends(EventBusService) | ||||
|  | ||||
|     # ========================================================================= | ||||
|     # CRUD Operations | ||||
|     @cached_property | ||||
| @@ -43,7 +49,18 @@ class RecipeCategoryController(BaseUserController): | ||||
|     def create_one(self, category: CategoryIn): | ||||
|         """Creates a Category in the database""" | ||||
|         save_data = mapper.cast(category, CategorySave, group_id=self.group_id) | ||||
|         return self.mixins.create_one(save_data) | ||||
|         data = self.mixins.create_one(save_data) | ||||
|         if data: | ||||
|             self.event_bus.dispatch( | ||||
|                 self.deps.acting_user.group_id, | ||||
|                 EventTypes.category_created, | ||||
|                 msg=self.t( | ||||
|                     "notifications.generic-created-with-url", | ||||
|                     name=data.name, | ||||
|                     url=urls.category_url(data.slug, self.deps.settings.BASE_URL), | ||||
|                 ), | ||||
|             ) | ||||
|         return data | ||||
|  | ||||
|     @router.get("/{item_id}", response_model=CategorySummary) | ||||
|     def get_one(self, item_id: UUID4): | ||||
| @@ -56,7 +73,19 @@ class RecipeCategoryController(BaseUserController): | ||||
|     def update_one(self, item_id: UUID4, update_data: CategoryIn): | ||||
|         """Updates an existing Tag in the database""" | ||||
|         save_data = mapper.cast(update_data, CategorySave, group_id=self.group_id) | ||||
|         return self.mixins.update_one(save_data, item_id) | ||||
|         data = self.mixins.update_one(save_data, item_id) | ||||
|  | ||||
|         if data: | ||||
|             self.event_bus.dispatch( | ||||
|                 self.deps.acting_user.group_id, | ||||
|                 EventTypes.category_updated, | ||||
|                 msg=self.t( | ||||
|                     "notifications.generic-updated-with-url", | ||||
|                     name=data.name, | ||||
|                     url=urls.category_url(data.slug, self.deps.settings.BASE_URL), | ||||
|                 ), | ||||
|             ) | ||||
|         return data | ||||
|  | ||||
|     @router.delete("/{item_id}") | ||||
|     def delete_one(self, item_id: UUID4): | ||||
| @@ -65,7 +94,12 @@ class RecipeCategoryController(BaseUserController): | ||||
|         category does not impact a recipe. The category will be removed | ||||
|         from any recipes that contain it | ||||
|         """ | ||||
|         self.mixins.delete_one(item_id) | ||||
|         if data := self.mixins.delete_one(item_id): | ||||
|             self.event_bus.dispatch( | ||||
|                 self.deps.acting_user.group_id, | ||||
|                 EventTypes.category_deleted, | ||||
|                 msg=self.t("notifications.generic-deleted", name=data.name), | ||||
|             ) | ||||
|  | ||||
|     # ========================================================================= | ||||
|     # Read All Operations | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| from functools import cached_property | ||||
|  | ||||
| from fastapi import APIRouter, HTTPException, status | ||||
| from fastapi import APIRouter, Depends, HTTPException, status | ||||
| from pydantic import UUID4 | ||||
|  | ||||
| from mealie.routes._base import BaseUserController, controller | ||||
| @@ -9,12 +9,18 @@ from mealie.schema import mapper | ||||
| from mealie.schema.recipe import RecipeTagResponse, TagIn | ||||
| from mealie.schema.recipe.recipe import RecipeTag | ||||
| from mealie.schema.recipe.recipe_category import TagSave | ||||
| from mealie.services import urls | ||||
| from mealie.services.event_bus_service.event_bus_service import EventBusService | ||||
| from mealie.services.event_bus_service.message_types import EventTypes | ||||
|  | ||||
| router = APIRouter(prefix="/tags", tags=["Organizer: Tags"]) | ||||
|  | ||||
|  | ||||
| @controller(router) | ||||
| class TagController(BaseUserController): | ||||
|  | ||||
|     event_bus: EventBusService = Depends(EventBusService) | ||||
|  | ||||
|     @cached_property | ||||
|     def repo(self): | ||||
|         return self.repos.tags.by_group(self.group_id) | ||||
| @@ -42,13 +48,35 @@ class TagController(BaseUserController): | ||||
|     def create_one(self, tag: TagIn): | ||||
|         """Creates a Tag in the database""" | ||||
|         save_data = mapper.cast(tag, TagSave, group_id=self.group_id) | ||||
|         return self.repo.create(save_data) | ||||
|         data = self.repo.create(save_data) | ||||
|         if data: | ||||
|             self.event_bus.dispatch( | ||||
|                 self.deps.acting_user.group_id, | ||||
|                 EventTypes.tag_created, | ||||
|                 msg=self.t( | ||||
|                     "notifications.generic-created-with-url", | ||||
|                     name=data.name, | ||||
|                     url=urls.tag_url(data.slug, self.deps.settings.BASE_URL), | ||||
|                 ), | ||||
|             ) | ||||
|         return data | ||||
|  | ||||
|     @router.put("/{item_id}", response_model=RecipeTagResponse) | ||||
|     def update_one(self, item_id: UUID4, new_tag: TagIn): | ||||
|         """Updates an existing Tag in the database""" | ||||
|         save_data = mapper.cast(new_tag, TagSave, group_id=self.group_id) | ||||
|         return self.repo.update(item_id, save_data) | ||||
|         data = self.repo.update(item_id, save_data) | ||||
|         if data: | ||||
|             self.event_bus.dispatch( | ||||
|                 self.deps.acting_user.group_id, | ||||
|                 EventTypes.tag_updated, | ||||
|                 msg=self.t( | ||||
|                     "notifications.generic-updated-with-url", | ||||
|                     name=data.name, | ||||
|                     url=urls.tag_url(data.slug, self.deps.settings.BASE_URL), | ||||
|                 ), | ||||
|             ) | ||||
|         return data | ||||
|  | ||||
|     @router.delete("/{item_id}") | ||||
|     def delete_recipe_tag(self, item_id: UUID4): | ||||
| @@ -57,10 +85,17 @@ class TagController(BaseUserController): | ||||
|         from any recipes that contain it""" | ||||
|  | ||||
|         try: | ||||
|             self.repo.delete(item_id) | ||||
|             data = self.repo.delete(item_id) | ||||
|         except Exception as e: | ||||
|             raise HTTPException(status.HTTP_400_BAD_REQUEST) from e | ||||
|  | ||||
|         if data: | ||||
|             self.event_bus.dispatch( | ||||
|                 self.deps.acting_user.group_id, | ||||
|                 EventTypes.tag_deleted, | ||||
|                 msg=self.t("notifications.generic-deleted", name=data.name), | ||||
|             ) | ||||
|  | ||||
|     @router.get("/slug/{tag_slug}", response_model=RecipeTagResponse) | ||||
|     async def get_one_by_slug(self, tag_slug: str): | ||||
|         return self.repo.get_one(tag_slug, "slug", override_schema=RecipeTagResponse) | ||||
|   | ||||
| @@ -29,6 +29,9 @@ from mealie.schema.recipe.recipe_asset import RecipeAsset | ||||
| from mealie.schema.recipe.recipe_scraper import ScrapeRecipeTest | ||||
| from mealie.schema.response.responses import ErrorResponse | ||||
| from mealie.schema.server.tasks import ServerTaskNames | ||||
| from mealie.services import urls | ||||
| from mealie.services.event_bus_service.event_bus_service import EventBusService | ||||
| from mealie.services.event_bus_service.message_types import EventTypes | ||||
| from mealie.services.recipe.recipe_data_service import RecipeDataService | ||||
| from mealie.services.recipe.recipe_service import RecipeService | ||||
| from mealie.services.recipe.template_service import TemplateService | ||||
| @@ -120,6 +123,8 @@ router = UserAPIRouter(prefix="/recipes", tags=["Recipe: CRUD"]) | ||||
|  | ||||
| @controller(router) | ||||
| class RecipeController(BaseRecipeController): | ||||
|     event_bus: EventBusService = Depends(EventBusService) | ||||
|  | ||||
|     def handle_exceptions(self, ex: Exception) -> None: | ||||
|         match type(ex): | ||||
|             case exceptions.PermissionDenied: | ||||
| @@ -152,7 +157,20 @@ class RecipeController(BaseRecipeController): | ||||
|  | ||||
|             recipe.tags = extras.use_tags(ctx)  # type: ignore | ||||
|  | ||||
|         return self.service.create_one(recipe).slug | ||||
|         new_recipe = self.service.create_one(recipe) | ||||
|  | ||||
|         if new_recipe: | ||||
|             self.event_bus.dispatch( | ||||
|                 self.deps.acting_user.group_id, | ||||
|                 EventTypes.recipe_created, | ||||
|                 msg=self.t( | ||||
|                     "notifications.generic-created-with-url", | ||||
|                     name=new_recipe.name, | ||||
|                     url=urls.recipe_url(new_recipe.slug, self.deps.settings.BASE_URL), | ||||
|                 ), | ||||
|             ) | ||||
|  | ||||
|         return new_recipe.slug | ||||
|  | ||||
|     @router.post("/create-url/bulk", status_code=202) | ||||
|     def parse_recipe_url_bulk(self, bulk: CreateRecipeByUrlBulk, bg_tasks: BackgroundTasks): | ||||
| @@ -249,6 +267,17 @@ class RecipeController(BaseRecipeController): | ||||
|         except Exception as e: | ||||
|             self.handle_exceptions(e) | ||||
|  | ||||
|         if data: | ||||
|             self.event_bus.dispatch( | ||||
|                 self.deps.acting_user.group_id, | ||||
|                 EventTypes.recipe_updated, | ||||
|                 msg=self.t( | ||||
|                     "notifications.generic-updated-with-url", | ||||
|                     name=data.name, | ||||
|                     url=urls.recipe_url(data.slug, self.deps.settings.BASE_URL), | ||||
|                 ), | ||||
|             ) | ||||
|  | ||||
|         return data | ||||
|  | ||||
|     @router.patch("/{slug}") | ||||
| @@ -264,10 +293,19 @@ class RecipeController(BaseRecipeController): | ||||
|     def delete_one(self, slug: str): | ||||
|         """Deletes a recipe by slug""" | ||||
|         try: | ||||
|             return self.service.delete_one(slug) | ||||
|             data = self.service.delete_one(slug) | ||||
|         except Exception as e: | ||||
|             self.handle_exceptions(e) | ||||
|  | ||||
|         if data: | ||||
|             self.event_bus.dispatch( | ||||
|                 self.deps.acting_user.group_id, | ||||
|                 EventTypes.recipe_deleted, | ||||
|                 msg=self.t("notifications.generic-deleted", name=data.name), | ||||
|             ) | ||||
|  | ||||
|         return data | ||||
|  | ||||
|     # ================================================================================================================== | ||||
|     # Image and Assets | ||||
|  | ||||
|   | ||||
| @@ -19,8 +19,14 @@ class RegistrationController(BasePublicController): | ||||
|  | ||||
|         if not settings.ALLOW_SIGNUP and data.group_token is None or data.group_token == "": | ||||
|             raise HTTPException( | ||||
|                 status_code=status.HTTP_403_FORBIDDEN, detail=ErrorResponse.respond("User Registration is Disabled") | ||||
|                 status_code=status.HTTP_403_FORBIDDEN, | ||||
|                 detail=ErrorResponse.respond("User Registration is Disabled"), | ||||
|             ) | ||||
|  | ||||
|         registration_service = RegistrationService( | ||||
|             self.deps.logger, | ||||
|             get_repositories(self.deps.session), | ||||
|             self.translator, | ||||
|         ) | ||||
|  | ||||
|         registration_service = RegistrationService(self.deps.logger, get_repositories(self.deps.session), self.deps.t) | ||||
|         return registration_service.register_user(data) | ||||
|   | ||||
							
								
								
									
										1
									
								
								mealie/services/urls/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								mealie/services/urls/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| from .url_constructors import * | ||||
							
								
								
									
										36
									
								
								mealie/services/urls/url_constructors.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								mealie/services/urls/url_constructors.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| from pydantic import UUID4 | ||||
|  | ||||
| from mealie.core.config import get_app_settings | ||||
|  | ||||
|  | ||||
| def _base_or(base_url: str | None) -> str: | ||||
|     if base_url is None: | ||||
|         settings = get_app_settings() | ||||
|         return settings.BASE_URL | ||||
|  | ||||
|     return base_url | ||||
|  | ||||
|  | ||||
| def recipe_url(recipe_slug: str, base_url: str | None) -> str: | ||||
|     base = _base_or(base_url) | ||||
|     return f"{base}/recipe/{recipe_slug}" | ||||
|  | ||||
|  | ||||
| def shopping_list_url(shopping_list_id: UUID4 | str, base_url: str | None) -> str: | ||||
|     base = _base_or(base_url) | ||||
|     return f"{base}/shopping-list/{shopping_list_id}" | ||||
|  | ||||
|  | ||||
| def tag_url(tag_slug: str, base_url: str | None) -> str: | ||||
|     base = _base_or(base_url) | ||||
|     return f"{base}/recipes/tags/{tag_slug}" | ||||
|  | ||||
|  | ||||
| def category_url(category_slug: str, base_url: str | None) -> str: | ||||
|     base = _base_or(base_url) | ||||
|     return f"{base}/recipes/categories/{category_slug}" | ||||
|  | ||||
|  | ||||
| def tool_url(tool_slug: str, base_url: str | None) -> str: | ||||
|     base = _base_or(base_url) | ||||
|     return f"{base}/recipes/tool/{tool_slug}" | ||||
| @@ -17,10 +17,10 @@ class RegistrationService: | ||||
|     logger: Logger | ||||
|     repos: AllRepositories | ||||
|  | ||||
|     def __init__(self, logger: Logger, db: AllRepositories, t: Translator): | ||||
|     def __init__(self, logger: Logger, db: AllRepositories, translator: Translator): | ||||
|         self.logger = logger | ||||
|         self.repos = db | ||||
|         self.t = t | ||||
|         self.t = translator.t | ||||
|  | ||||
|     def _create_new_user(self, group: GroupInDB, new_group: bool) -> PrivateUser: | ||||
|         new_user = UserIn( | ||||
| @@ -58,9 +58,9 @@ class RegistrationService: | ||||
|         self.registration = registration | ||||
|  | ||||
|         if self.repos.users.get_by_username(registration.username): | ||||
|             raise HTTPException(status.HTTP_409_CONFLICT, {"message": self.t.t("exceptions.username-conflict-error")}) | ||||
|             raise HTTPException(status.HTTP_409_CONFLICT, {"message": self.t("exceptions.username-conflict-error")}) | ||||
|         elif self.repos.users.get(registration.email, "email"): | ||||
|             raise HTTPException(status.HTTP_409_CONFLICT, {"message": self.t.t("exceptions.email-conflict-error")}) | ||||
|             raise HTTPException(status.HTTP_409_CONFLICT, {"message": self.t("exceptions.email-conflict-error")}) | ||||
|  | ||||
|         self.logger.info(f"Registering user {registration.username}") | ||||
|         token_entry = None | ||||
|   | ||||
		Reference in New Issue
	
	Block a user