mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-08-07 06:25:21 +02:00
feature: proper multi-tenant-support (#969)(WIP)
* update naming * refactor tests to use shared structure * shorten names * add tools test case * refactor to support multi-tenant * set group_id on creation * initial refactor for multitenant tags/cats * spelling * additional test case for same valued resources * fix recipe update tests * apply indexes to foreign keys * fix performance regressions * handle unknown exception * utility decorator for function debugging * migrate recipe_id to UUID * GUID for recipes * remove unused import * move image functions into package * move utilities to packages dir * update import * linter * image image and asset routes * update assets and images to use UUIDs * fix migration base * image asset test coverage * use ids for categories and tag crud functions * refactor recipe organizer test suite to reduce duplication * add uuid serlization utility * organizer base router * slug routes testing and fixes * fix postgres error * adopt UUIDs * move tags, categories, and tools under "organizers" umbrella * update composite label * generate ts types * fix import error * update frontend types * fix type errors * fix postgres errors * fix #978 * add null check for title validation * add note in docs on multi-tenancy
This commit is contained in:
parent
9a82a172cb
commit
c617251f4c
157 changed files with 1866 additions and 1578 deletions
|
@ -9,7 +9,7 @@ from mealie.schema.recipe.recipe_ingredient import IngredientFood, IngredientUni
|
|||
|
||||
|
||||
class ShoppingListItemRecipeRef(CamelModel):
|
||||
recipe_id: int
|
||||
recipe_id: UUID4
|
||||
recipe_quantity: float
|
||||
|
||||
|
||||
|
@ -30,9 +30,9 @@ class ShoppingListItemCreate(CamelModel):
|
|||
|
||||
note: Optional[str] = ""
|
||||
quantity: float = 1
|
||||
unit_id: int = None
|
||||
unit_id: UUID4 = None
|
||||
unit: Optional[IngredientUnit]
|
||||
food_id: int = None
|
||||
food_id: UUID4 = None
|
||||
food: Optional[IngredientFood]
|
||||
|
||||
label_id: Optional[UUID4] = None
|
||||
|
@ -58,7 +58,7 @@ class ShoppingListCreate(CamelModel):
|
|||
class ShoppingListRecipeRefOut(CamelModel):
|
||||
id: UUID4
|
||||
shopping_list_id: UUID4
|
||||
recipe_id: int
|
||||
recipe_id: UUID4
|
||||
recipe_quantity: float
|
||||
recipe: RecipeSummary
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ class CreatePlanEntry(CamelModel):
|
|||
entry_type: PlanEntryType = PlanEntryType.breakfast
|
||||
title: str = ""
|
||||
text: str = ""
|
||||
recipe_id: Optional[int]
|
||||
recipe_id: Optional[UUID]
|
||||
|
||||
@validator("recipe_id", always=True)
|
||||
@classmethod
|
||||
|
|
|
@ -6,7 +6,7 @@ from pydantic import UUID4
|
|||
|
||||
|
||||
class Category(CamelModel):
|
||||
id: int
|
||||
id: UUID4
|
||||
name: str
|
||||
slug: str
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ app_dirs = get_app_dirs()
|
|||
|
||||
|
||||
class RecipeTag(CamelModel):
|
||||
id: int = 0
|
||||
id: UUID4 = None
|
||||
name: str
|
||||
slug: str
|
||||
|
||||
|
@ -37,7 +37,7 @@ class RecipeCategory(RecipeTag):
|
|||
|
||||
|
||||
class RecipeTool(RecipeTag):
|
||||
id: int = 0
|
||||
id: UUID4
|
||||
on_hand: bool = False
|
||||
|
||||
|
||||
|
@ -63,7 +63,7 @@ class CreateRecipe(CamelModel):
|
|||
|
||||
|
||||
class RecipeSummary(CamelModel):
|
||||
id: Optional[int]
|
||||
id: Optional[UUID4]
|
||||
|
||||
user_id: UUID4 = Field(default_factory=uuid4)
|
||||
group_id: UUID4 = Field(default_factory=uuid4)
|
||||
|
@ -96,13 +96,13 @@ class RecipeSummary(CamelModel):
|
|||
@validator("tags", always=True, pre=True, allow_reuse=True)
|
||||
def validate_tags(cats: list[Any]): # type: ignore
|
||||
if isinstance(cats, list) and cats and isinstance(cats[0], str):
|
||||
return [RecipeTag(name=c, slug=slugify(c)) for c in cats]
|
||||
return [RecipeTag(id=uuid4(), name=c, slug=slugify(c)) for c in cats]
|
||||
return cats
|
||||
|
||||
@validator("recipe_category", always=True, pre=True, allow_reuse=True)
|
||||
def validate_categories(cats: list[Any]): # type: ignore
|
||||
if isinstance(cats, list) and cats and isinstance(cats[0], str):
|
||||
return [RecipeCategory(name=c, slug=slugify(c)) for c in cats]
|
||||
return [RecipeCategory(id=uuid4(), name=c, slug=slugify(c)) for c in cats]
|
||||
return cats
|
||||
|
||||
@validator("group_id", always=True, pre=True, allow_reuse=True)
|
||||
|
@ -132,12 +132,15 @@ class Recipe(RecipeSummary):
|
|||
comments: Optional[list[RecipeCommentOut]] = []
|
||||
|
||||
@staticmethod
|
||||
def directory_from_slug(slug) -> Path:
|
||||
return app_dirs.RECIPE_DATA_DIR.joinpath(slug)
|
||||
def directory_from_id(recipe_id: UUID4 | str) -> Path:
|
||||
return app_dirs.RECIPE_DATA_DIR.joinpath(str(recipe_id))
|
||||
|
||||
@property
|
||||
def directory(self) -> Path:
|
||||
dir = app_dirs.RECIPE_DATA_DIR.joinpath(self.slug)
|
||||
if not self.id:
|
||||
raise ValueError("Recipe has no ID")
|
||||
|
||||
dir = app_dirs.RECIPE_DATA_DIR.joinpath(str(self.id))
|
||||
dir.mkdir(exist_ok=True, parents=True)
|
||||
return dir
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from fastapi_camelcase import CamelModel
|
||||
from pydantic import UUID4
|
||||
from pydantic.utils import GetterDict
|
||||
|
||||
|
||||
|
@ -6,8 +7,12 @@ class CategoryIn(CamelModel):
|
|||
name: str
|
||||
|
||||
|
||||
class CategorySave(CategoryIn):
|
||||
group_id: UUID4
|
||||
|
||||
|
||||
class CategoryBase(CategoryIn):
|
||||
id: int
|
||||
id: UUID4
|
||||
slug: str
|
||||
|
||||
class Config:
|
||||
|
@ -20,27 +25,45 @@ class CategoryBase(CategoryIn):
|
|||
}
|
||||
|
||||
|
||||
class RecipeCategoryResponse(CategoryBase):
|
||||
recipes: "list[Recipe]" = []
|
||||
class CategoryOut(CategoryBase):
|
||||
slug: str
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
||||
|
||||
class RecipeCategoryResponse(CategoryBase):
|
||||
recipes: "list[RecipeSummary]" = []
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
schema_extra = {"example": {"id": 1, "name": "dinner", "recipes": [{}]}}
|
||||
|
||||
|
||||
class TagIn(CategoryIn):
|
||||
pass
|
||||
|
||||
|
||||
class TagSave(TagIn):
|
||||
group_id: UUID4
|
||||
|
||||
|
||||
class TagBase(CategoryBase):
|
||||
pass
|
||||
|
||||
|
||||
class TagOut(TagSave):
|
||||
id: UUID4
|
||||
slug: str
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
||||
|
||||
class RecipeTagResponse(RecipeCategoryResponse):
|
||||
pass
|
||||
|
||||
|
||||
from mealie.schema.recipe.recipe import Recipe
|
||||
from mealie.schema.recipe.recipe import RecipeSummary
|
||||
|
||||
RecipeCategoryResponse.update_forward_refs()
|
||||
RecipeTagResponse.update_forward_refs()
|
||||
|
|
|
@ -16,7 +16,7 @@ class UserBase(CamelModel):
|
|||
|
||||
|
||||
class RecipeCommentCreate(CamelModel):
|
||||
recipe_id: int
|
||||
recipe_id: UUID4
|
||||
text: str
|
||||
|
||||
|
||||
|
@ -31,7 +31,7 @@ class RecipeCommentUpdate(CamelModel):
|
|||
|
||||
class RecipeCommentOut(RecipeCommentCreate):
|
||||
id: UUID
|
||||
recipe_id: int
|
||||
recipe_id: UUID4
|
||||
created_at: datetime
|
||||
update_at: datetime
|
||||
user_id: UUID4
|
||||
|
|
|
@ -14,7 +14,7 @@ class UnitFoodBase(CamelModel):
|
|||
|
||||
|
||||
class CreateIngredientFood(UnitFoodBase):
|
||||
label_id: UUID4 = None
|
||||
label_id: Optional[UUID4] = None
|
||||
|
||||
|
||||
class SaveIngredientFood(CreateIngredientFood):
|
||||
|
@ -22,7 +22,7 @@ class SaveIngredientFood(CreateIngredientFood):
|
|||
|
||||
|
||||
class IngredientFood(CreateIngredientFood):
|
||||
id: int
|
||||
id: UUID4
|
||||
label: MultiPurposeLabelSummary = None
|
||||
|
||||
class Config:
|
||||
|
@ -39,7 +39,7 @@ class SaveIngredientUnit(CreateIngredientUnit):
|
|||
|
||||
|
||||
class IngredientUnit(CreateIngredientUnit):
|
||||
id: int
|
||||
id: UUID4
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
|
|
@ -11,7 +11,7 @@ def defaut_expires_at_time() -> datetime:
|
|||
|
||||
|
||||
class RecipeShareTokenCreate(CamelModel):
|
||||
recipe_id: int
|
||||
recipe_id: UUID4
|
||||
expires_at: datetime = Field(default_factory=defaut_expires_at_time)
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from typing import List
|
||||
|
||||
from fastapi_camelcase import CamelModel
|
||||
from pydantic import UUID4
|
||||
|
||||
|
||||
class RecipeToolCreate(CamelModel):
|
||||
|
@ -8,8 +9,12 @@ class RecipeToolCreate(CamelModel):
|
|||
on_hand: bool = False
|
||||
|
||||
|
||||
class RecipeToolSave(RecipeToolCreate):
|
||||
group_id: UUID4
|
||||
|
||||
|
||||
class RecipeTool(RecipeToolCreate):
|
||||
id: int
|
||||
id: UUID4
|
||||
slug: str
|
||||
|
||||
class Config:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue