From 4b9eb5077a4523246983a790a98f875ad4f28dc9 Mon Sep 17 00:00:00 2001 From: Wim de Groot <34519486+wim-de-groot@users.noreply.github.com> Date: Mon, 4 Nov 2024 17:17:08 +0100 Subject: [PATCH 01/10] feat: implement the possibility to add tls (#4456) Signed-off-by: Wim de Groot <34519486+wim-de-groot@users.noreply.github.com> --- .../getting-started/installation/backend-config.md | 9 +++++++++ mealie/core/settings/settings.py | 9 +++++++++ mealie/main.py | 2 ++ 3 files changed, 20 insertions(+) diff --git a/docs/docs/documentation/getting-started/installation/backend-config.md b/docs/docs/documentation/getting-started/installation/backend-config.md index fdad87700..bd82a09a4 100644 --- a/docs/docs/documentation/getting-started/installation/backend-config.md +++ b/docs/docs/documentation/getting-started/installation/backend-config.md @@ -61,6 +61,15 @@ Changing the webworker settings may cause unforeseen memory leak issues with Mea | --------------- | :-----: | ----------------------------------------------------------------------------- | | UVICORN_WORKERS | 1 | Sets the number of workers for the web server. [More info here][unicorn_workers] | +### TLS + +Use this only when mealie is run without a webserver or reverse proxy. + +| Variables | Default | Description | +| -------------------- | :-----: | ------------------------ | +| TLS_CERTIFICATE_PATH | None | File path to Certificate | +| TLS_PRIVATE_KEY_PATH | None | File path to private key | + ### LDAP | Variables | Default | Description | diff --git a/mealie/core/settings/settings.py b/mealie/core/settings/settings.py index 5f97557ee..dd9686a9a 100644 --- a/mealie/core/settings/settings.py +++ b/mealie/core/settings/settings.py @@ -353,6 +353,15 @@ class AppSettings(AppLoggingSettings): model_config = SettingsConfigDict(arbitrary_types_allowed=True, extra="allow") + # =============================================== + # TLS + + TLS_CERTIFICATE_PATH: str | os.PathLike[str] | None = None + """Path where the certificate resides.""" + + TLS_PRIVATE_KEY_PATH: str | os.PathLike[str] | None = None + """Path where the private key resides.""" + def app_settings_constructor(data_dir: Path, production: bool, env_file: Path, env_encoding="utf-8") -> AppSettings: """ diff --git a/mealie/main.py b/mealie/main.py index d1810bd22..d85d2de9f 100644 --- a/mealie/main.py +++ b/mealie/main.py @@ -13,6 +13,8 @@ def main(): log_config=log_config(), workers=settings.WORKERS, forwarded_allow_ips=settings.HOST_IP, + ssl_keyfile=settings.TLS_PRIVATE_KEY_PATH, + ssl_certfile=settings.TLS_CERTIFICATE_PATH, ) From dbbbe06a23260be753d4cca663d1e320efbd5dea Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 17:08:17 +0000 Subject: [PATCH 02/10] chore(auto): Update pre-commit hooks (#4506) Co-authored-by: boc-the-git <3479092+boc-the-git@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0f55202b5..6bab6e363 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.7.1 + rev: v0.7.2 hooks: - id: ruff - id: ruff-format From f798fafb3e87ddf100aea998151ed8f32bb07792 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 5 Nov 2024 16:32:54 +0100 Subject: [PATCH 03/10] chore(deps): update dependency rich to v13.9.4 (#4491) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index bcf7fee9c..602c44b3c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2796,13 +2796,13 @@ rsa = ["oauthlib[signedtoken] (>=3.0.0)"] [[package]] name = "rich" -version = "13.9.3" +version = "13.9.4" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.8.0" files = [ - {file = "rich-13.9.3-py3-none-any.whl", hash = "sha256:9836f5096eb2172c9e77df411c1b009bace4193d6a481d534fea75ebba758283"}, - {file = "rich-13.9.3.tar.gz", hash = "sha256:bc1e01b899537598cf02579d2b9f4a415104d3fc439313a7a2c165d76557a08e"}, + {file = "rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90"}, + {file = "rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098"}, ] [package.dependencies] From b81b97d934452ee42028ad4268541ecd80696c35 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 5 Nov 2024 19:31:14 +0100 Subject: [PATCH 04/10] fix(deps): update dependency openai to v1.54.0 (#4510) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- poetry.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index 602c44b3c..558dc6d21 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1598,13 +1598,13 @@ signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] [[package]] name = "openai" -version = "1.53.0" +version = "1.54.0" description = "The official Python library for the openai API" optional = false -python-versions = ">=3.7.1" +python-versions = ">=3.8" files = [ - {file = "openai-1.53.0-py3-none-any.whl", hash = "sha256:20f408c32fc5cb66e60c6882c994cdca580a5648e10045cd840734194f033418"}, - {file = "openai-1.53.0.tar.gz", hash = "sha256:be2c4e77721b166cce8130e544178b7d579f751b4b074ffbaade3854b6f85ec5"}, + {file = "openai-1.54.0-py3-none-any.whl", hash = "sha256:24ed8874b56e919f0fbb80b7136c3fb022dc82ce9f5f21579b7b280ea4bba249"}, + {file = "openai-1.54.0.tar.gz", hash = "sha256:df2a84384314165b706722a7ac8988dc33eba20dd7fc3b939d138110e608b1ce"}, ] [package.dependencies] From 8872fd52cd1e0eb1913b08e0bf5295a392d9883c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 5 Nov 2024 19:48:34 +0100 Subject: [PATCH 05/10] fix(deps): update dependency openai to v1.54.1 (#4515) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index 558dc6d21..a5f80e176 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1598,13 +1598,13 @@ signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] [[package]] name = "openai" -version = "1.54.0" +version = "1.54.1" description = "The official Python library for the openai API" optional = false python-versions = ">=3.8" files = [ - {file = "openai-1.54.0-py3-none-any.whl", hash = "sha256:24ed8874b56e919f0fbb80b7136c3fb022dc82ce9f5f21579b7b280ea4bba249"}, - {file = "openai-1.54.0.tar.gz", hash = "sha256:df2a84384314165b706722a7ac8988dc33eba20dd7fc3b939d138110e608b1ce"}, + {file = "openai-1.54.1-py3-none-any.whl", hash = "sha256:3cb49ccb6bfdc724ad01cc397d323ef8314fc7d45e19e9de2afdd6484a533324"}, + {file = "openai-1.54.1.tar.gz", hash = "sha256:5b832bf82002ba8c4f6e5e25c1c0f5d468c22f043711544c716eaffdb30dd6f1"}, ] [package.dependencies] From 898374510603851e60b7571dfbe95a5723a0eeee Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 5 Nov 2024 20:17:57 +0100 Subject: [PATCH 06/10] chore(deps): update dependency mkdocs-material to v9.5.44 (#4516) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index a5f80e176..761589d14 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1464,13 +1464,13 @@ pyyaml = ">=5.1" [[package]] name = "mkdocs-material" -version = "9.5.43" +version = "9.5.44" description = "Documentation that simply works" optional = false python-versions = ">=3.8" files = [ - {file = "mkdocs_material-9.5.43-py3-none-any.whl", hash = "sha256:4aae0664c456fd12837a3192e0225c17960ba8bf55d7f0a7daef7e4b0b914a34"}, - {file = "mkdocs_material-9.5.43.tar.gz", hash = "sha256:83be7ff30b65a1e4930dfa4ab911e75780a3afc9583d162692e434581cb46979"}, + {file = "mkdocs_material-9.5.44-py3-none-any.whl", hash = "sha256:47015f9c167d58a5ff5e682da37441fc4d66a1c79334bfc08d774763cacf69ca"}, + {file = "mkdocs_material-9.5.44.tar.gz", hash = "sha256:f3a6c968e524166b3f3ed1fb97d3ed3e0091183b0545cedf7156a2a6804c56c0"}, ] [package.dependencies] From 87f4b2371166982fa7d81d2bc8404cd83e0a3be7 Mon Sep 17 00:00:00 2001 From: Michael Genson <71845777+michael-genson@users.noreply.github.com> Date: Tue, 5 Nov 2024 13:57:30 -0600 Subject: [PATCH 07/10] feat: Show Cookbooks from Other Households (#4452) --- .../Domain/Cookbook/CookbookPage.vue | 15 +++- frontend/components/Layout/DefaultLayout.vue | 90 ++++++++++++++----- .../Layout/LayoutParts/AppSidebar.vue | 20 ++++- frontend/composables/use-group-cookbooks.ts | 12 +-- frontend/composables/use-users/preferences.ts | 18 ++++ frontend/lang/messages/en-US.json | 2 + .../pages/g/_groupSlug/cookbooks/index.vue | 45 ++++++++-- frontend/types/application-types.ts | 1 + mealie/routes/_base/mixins.py | 15 +++- .../routes/households/controller_cookbooks.py | 15 +++- mealie/routes/recipe/recipe_crud_routes.py | 6 +- .../test_group_cookbooks.py | 67 +++++++++++++- .../test_recipe_cross_household.py | 13 +++ 13 files changed, 264 insertions(+), 55 deletions(-) diff --git a/frontend/components/Domain/Cookbook/CookbookPage.vue b/frontend/components/Domain/Cookbook/CookbookPage.vue index 44486e70d..5d771ad52 100644 --- a/frontend/components/Domain/Cookbook/CookbookPage.vue +++ b/frontend/components/Domain/Cookbook/CookbookPage.vue @@ -7,7 +7,7 @@ width="100%" max-width="1100px" :icon="$globals.icons.pages" - :title="$t('general.edit')" + :title="$tc('general.edit')" :submit-icon="$globals.icons.save" :submit-text="$tc('general.save')" :submit-disabled="!editTarget.queryFilterString" @@ -25,7 +25,7 @@ {{ book.name }} { + if (!($auth.user && book.value?.householdId)) { + return false; + } + + return $auth.user.householdId === book.value.householdId; + }) + const canEdit = computed(() => isOwnGroup.value && isOwnHousehold.value); + const dialogStates = reactive({ edit: false, }); @@ -118,7 +127,7 @@ recipes, removeRecipe, replaceRecipes, - isOwnGroup, + canEdit, dialogStates, editTarget, handleEditCookbook, diff --git a/frontend/components/Layout/DefaultLayout.vue b/frontend/components/Layout/DefaultLayout.vue index 062a70ed3..5e1ea6eca 100644 --- a/frontend/components/Layout/DefaultLayout.vue +++ b/frontend/components/Layout/DefaultLayout.vue @@ -82,12 +82,17 @@ import { computed, defineComponent, onMounted, ref, useContext, useRoute } from import { useLoggedInState } from "~/composables/use-logged-in-state"; import AppHeader from "@/components/Layout/LayoutParts/AppHeader.vue"; import AppSidebar from "@/components/Layout/LayoutParts/AppSidebar.vue"; -import { SidebarLinks } from "~/types/application-types"; +import { SideBarLink } from "~/types/application-types"; import LanguageDialog from "~/components/global/LanguageDialog.vue"; import TheSnackbar from "@/components/Layout/LayoutParts/TheSnackbar.vue"; import { useAppInfo } from "~/composables/api"; import { useCookbooks, usePublicCookbooks } from "~/composables/use-group-cookbooks"; +import { useCookbookPreferences } from "~/composables/use-users/preferences"; +import { useHouseholdStore, usePublicHouseholdStore } from "~/composables/store/use-household-store"; import { useToggleDarkMode } from "~/composables/use-utils"; +import { ReadCookBook } from "~/lib/api/types/cookbook"; +import { HouseholdSummary } from "~/lib/api/types/household"; + export default defineComponent({ components: { AppHeader, AppSidebar, LanguageDialog, TheSnackbar }, @@ -99,6 +104,15 @@ export default defineComponent({ const route = useRoute(); const groupSlug = computed(() => route.value.params.groupSlug || $auth.user?.groupSlug || ""); const { cookbooks } = isOwnGroup.value ? useCookbooks() : usePublicCookbooks(groupSlug.value || ""); + const cookbookPreferences = useCookbookPreferences(); + const { store: households } = isOwnGroup.value ? useHouseholdStore() : usePublicHouseholdStore(groupSlug.value || ""); + + const householdsById = computed(() => { + return households.value.reduce((acc, household) => { + acc[household.id] = household; + return acc; + }, {} as { [key: string]: HouseholdSummary }); + }); const appInfo = useAppInfo(); const showImageImport = computed(() => appInfo.value?.enableOpenaiImageServices); @@ -113,29 +127,57 @@ export default defineComponent({ sidebar.value = !$vuetify.breakpoint.md; }); - const cookbookLinks = computed(() => { - if (!cookbooks.value) return []; - return cookbooks.value.map((cookbook) => { - return { - key: cookbook.slug, - icon: $globals.icons.pages, - title: cookbook.name, - to: `/g/${groupSlug.value}/cookbooks/${cookbook.slug as string}`, - }; - }); - }); - - interface Link { - insertDivider: boolean; - icon: string; - title: string; - subtitle: string | null; - to: string; - restricted: boolean; - hide: boolean; + function cookbookAsLink(cookbook: ReadCookBook): SideBarLink { + return { + key: cookbook.slug || "", + icon: $globals.icons.pages, + title: cookbook.name, + to: `/g/${groupSlug.value}/cookbooks/${cookbook.slug || ""}`, + restricted: false, + }; } - const createLinks = computed(() => [ + const currentUserHouseholdId = computed(() => $auth.user?.householdId); + const cookbookLinks = computed(() => { + if (!cookbooks.value) { + return []; + } + cookbooks.value.sort((a, b) => (a.position || 0) - (b.position || 0)); + + const ownLinks: SideBarLink[] = []; + const links: SideBarLink[] = []; + const cookbooksByHousehold = cookbooks.value.reduce((acc, cookbook) => { + const householdName = householdsById.value[cookbook.householdId]?.name || ""; + if (!acc[householdName]) { + acc[householdName] = []; + } + acc[householdName].push(cookbook); + return acc; + }, {} as Record); + + Object.entries(cookbooksByHousehold).forEach(([householdName, cookbooks]) => { + if (cookbooks[0].householdId === currentUserHouseholdId.value) { + ownLinks.push(...cookbooks.map(cookbookAsLink)); + } else { + links.push({ + key: householdName, + icon: $globals.icons.book, + title: householdName, + children: cookbooks.map(cookbookAsLink), + restricted: false, + }); + } + }); + + links.sort((a, b) => a.title.localeCompare(b.title)); + if ($auth.user && cookbookPreferences.value.hideOtherHouseholds) { + return ownLinks; + } else { + return [...ownLinks, ...links]; + } + }); + + const createLinks = computed(() => [ { insertDivider: false, icon: $globals.icons.link, @@ -165,7 +207,7 @@ export default defineComponent({ }, ]); - const bottomLinks = computed(() => [ + const bottomLinks = computed(() => [ { icon: $globals.icons.cog, title: i18n.tc("general.settings"), @@ -174,7 +216,7 @@ export default defineComponent({ }, ]); - const topLinks = computed(() => [ + const topLinks = computed(() => [ { icon: $globals.icons.silverwareForkKnife, to: `/g/${groupSlug.value}`, diff --git a/frontend/components/Layout/LayoutParts/AppSidebar.vue b/frontend/components/Layout/LayoutParts/AppSidebar.vue index d6c5597f9..ab3e3fb83 100644 --- a/frontend/components/Layout/LayoutParts/AppSidebar.vue +++ b/frontend/components/Layout/LayoutParts/AppSidebar.vue @@ -135,7 +135,7 @@