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 @@