diff --git a/Taskfile.yml b/Taskfile.yml
index c54c075db..5352a3ca3 100644
--- a/Taskfile.yml
+++ b/Taskfile.yml
@@ -71,6 +71,7 @@ tasks:
desc: run code generators
cmds:
- poetry run python dev/code-generation/main.py {{ .CLI_ARGS }}
+ - task: docs:gen
- task: py:format
dev:services:
diff --git a/dev/code-generation/gen_ts_types.py b/dev/code-generation/gen_ts_types.py
index 408262f25..8a8932299 100644
--- a/dev/code-generation/gen_ts_types.py
+++ b/dev/code-generation/gen_ts_types.py
@@ -8,8 +8,8 @@ from utils import log
# ============================================================
template = """// This Code is auto generated by gen_ts_types.py
-{% for name in global %}import {{ name }} from "@/components/global/{{ name }}.vue";
-{% endfor %}{% for name in layout %}import {{ name }} from "@/components/layout/{{ name }}.vue";
+{% for name in global %}import type {{ name }} from "@/components/global/{{ name }}.vue";
+{% endfor %}{% for name in layout %}import type {{ name }} from "@/components/layout/{{ name }}.vue";
{% endfor %}
declare module "vue" {
export interface GlobalComponents {
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 5c47a0674..b859a2610 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -1,7 +1,7 @@
###############################################
# Frontend Build
###############################################
-FROM node:20@sha256:452293f0e5c9b7075829f2cd0dbd5fcae44d89cf5508b84a17bbe0857dc1a654 \
+FROM node:20@sha256:572a90df10a58ebb7d3f223d661d964a6c2383a9c2b5763162b4f631c53dc56a \
AS frontend-builder
WORKDIR /frontend
diff --git a/docs/docs/overrides/api.html b/docs/docs/overrides/api.html
index 64a6e5cf4..0dc543851 100644
--- a/docs/docs/overrides/api.html
+++ b/docs/docs/overrides/api.html
@@ -14,7 +14,7 @@
diff --git a/frontend/components/Domain/Cookbook/CookbookPage.vue b/frontend/components/Domain/Cookbook/CookbookPage.vue
index a8cff6753..2267276cc 100644
--- a/frontend/components/Domain/Cookbook/CookbookPage.vue
+++ b/frontend/components/Domain/Cookbook/CookbookPage.vue
@@ -70,7 +70,7 @@ import RecipeCardSection from "@/components/Domain/Recipe/RecipeCardSection.vue"
import { useCookbookStore } from "~/composables/store/use-cookbook-store";
import { useCookbook } from "~/composables/use-group-cookbooks";
import { useLoggedInState } from "~/composables/use-logged-in-state";
-import type { RecipeCookBook } from "~/lib/api/types/cookbook";
+import type { ReadCookBook } from "~/lib/api/types/cookbook";
import CookbookEditor from "~/components/Domain/Cookbook/CookbookEditor.vue";
const $auth = useMealieAuth();
@@ -100,7 +100,7 @@ const dialogStates = reactive({
edit: false,
});
-const editTarget = ref(null);
+const editTarget = ref(null);
function handleEditCookbook() {
dialogStates.edit = true;
editTarget.value = book.value;
diff --git a/frontend/composables/store/use-cookbook-store.ts b/frontend/composables/store/use-cookbook-store.ts
index e3d4a5a81..131187e9e 100644
--- a/frontend/composables/store/use-cookbook-store.ts
+++ b/frontend/composables/store/use-cookbook-store.ts
@@ -1,18 +1,18 @@
import type { Composer } from "vue-i18n";
import { useReadOnlyStore, useStore } from "../partials/use-store-factory";
-import type { RecipeCookBook } from "~/lib/api/types/cookbook";
+import type { ReadCookBook } from "~/lib/api/types/cookbook";
import { usePublicExploreApi, useUserApi } from "~/composables/api";
-const store: Ref = ref([]);
+const store: Ref = ref([]);
const loading = ref(false);
const publicLoading = ref(false);
export const useCookbookStore = function (i18n?: Composer) {
const api = useUserApi(i18n);
- return useStore(store, loading, api.cookbooks);
+ return useStore(store, loading, api.cookbooks);
};
export const usePublicCookbookStore = function (groupSlug: string, i18n?: Composer) {
const api = usePublicExploreApi(groupSlug, i18n).explore;
- return useReadOnlyStore(store, publicLoading, api.cookbooks);
+ return useReadOnlyStore(store, publicLoading, api.cookbooks);
};
diff --git a/frontend/lang/messages/af-ZA.json b/frontend/lang/messages/af-ZA.json
index 402d32a6f..2498b6eae 100644
--- a/frontend/lang/messages/af-ZA.json
+++ b/frontend/lang/messages/af-ZA.json
@@ -69,6 +69,7 @@
"new-notification": "Nuwe kennisgewing",
"event-notifiers": "Gebeurteniskennisgewers",
"apprise-url-skipped-if-blank": "Apprise URL (oorgeslaan indien leeg)",
+ "apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"enable-notifier": "Aktiveer kennisgewer",
"what-events": "Op watter gebeurtenisse moet hierdie kennisgewing inteken?",
"user-events": "Gebruikersgebeurtenisse",
diff --git a/frontend/lang/messages/ar-SA.json b/frontend/lang/messages/ar-SA.json
index 55a8ed890..fb8e985d3 100644
--- a/frontend/lang/messages/ar-SA.json
+++ b/frontend/lang/messages/ar-SA.json
@@ -69,6 +69,7 @@
"new-notification": "إشعار جديد",
"event-notifiers": "إشعار الحدث",
"apprise-url-skipped-if-blank": "الرابط Apprise (يتم تجاهله إذا ما كان فارغً)",
+ "apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"enable-notifier": "تفعيل الإشعارات",
"what-events": "ما هي الأحداث التي يجب على هذا المخدم أن يستجيب لها؟",
"user-events": "أحداث المستخدمين",
diff --git a/frontend/lang/messages/bg-BG.json b/frontend/lang/messages/bg-BG.json
index 4fe1ceff5..f5f52a6f4 100644
--- a/frontend/lang/messages/bg-BG.json
+++ b/frontend/lang/messages/bg-BG.json
@@ -69,6 +69,7 @@
"new-notification": "Ново известие",
"event-notifiers": "Известия за събитие",
"apprise-url-skipped-if-blank": "URL за известяване (пропуска се ако е празно)",
+ "apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"enable-notifier": "Включи известията",
"what-events": "За кои събития трябва да се получават известия?",
"user-events": "Потребителски събития",
diff --git a/frontend/lang/messages/ca-ES.json b/frontend/lang/messages/ca-ES.json
index 6a768ca64..812c0259d 100644
--- a/frontend/lang/messages/ca-ES.json
+++ b/frontend/lang/messages/ca-ES.json
@@ -69,6 +69,7 @@
"new-notification": "Nova notificació",
"event-notifiers": "Notificacions d'esdeveniments",
"apprise-url-skipped-if-blank": "Apprise URL (si es deixa buit, s'ignorarà)",
+ "apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"enable-notifier": "Habilita la notificació",
"what-events": "Què esdeveniments vols que utilitzen aquest notificador?",
"user-events": "Esdeveniments d'usuari",
diff --git a/frontend/lang/messages/cs-CZ.json b/frontend/lang/messages/cs-CZ.json
index a3add99f0..34b61db34 100644
--- a/frontend/lang/messages/cs-CZ.json
+++ b/frontend/lang/messages/cs-CZ.json
@@ -69,6 +69,7 @@
"new-notification": "Nové oznámení",
"event-notifiers": "Notifikace událostí",
"apprise-url-skipped-if-blank": "Apprise URL (přeskočeno pokud je prázdné)",
+ "apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"enable-notifier": "Povolit notifikaci",
"what-events": "K jakým událostem by se měl tento oznamovatel přihlásit?",
"user-events": "Uživatelské události",
diff --git a/frontend/lang/messages/da-DK.json b/frontend/lang/messages/da-DK.json
index 1fc51b515..512efcf78 100644
--- a/frontend/lang/messages/da-DK.json
+++ b/frontend/lang/messages/da-DK.json
@@ -69,6 +69,7 @@
"new-notification": "Ny notifikation",
"event-notifiers": "Notifikation om begivenheder",
"apprise-url-skipped-if-blank": "Informations link (sprunget over hvis ladet være tomt)",
+ "apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"enable-notifier": "Aktiver Notifikationer",
"what-events": "Hvilke begivenheder skal denne anmelder abonnere på?",
"user-events": "Brugerhændelser",
diff --git a/frontend/lang/messages/de-DE.json b/frontend/lang/messages/de-DE.json
index 87ca94402..9ed0e2e42 100644
--- a/frontend/lang/messages/de-DE.json
+++ b/frontend/lang/messages/de-DE.json
@@ -69,6 +69,7 @@
"new-notification": "Neue Benachrichtigung",
"event-notifiers": "Ereignis-Benachrichtigungen",
"apprise-url-skipped-if-blank": "Apprise-URL (wird übersprungen, wenn leer)",
+ "apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"enable-notifier": "Benachrichtigen aktivieren",
"what-events": "Welche Ereignisse soll diese Benachrichtigung abonnieren?",
"user-events": "Benutzer-Ereignisse",
diff --git a/frontend/lang/messages/el-GR.json b/frontend/lang/messages/el-GR.json
index b510f9565..6f89f2c70 100644
--- a/frontend/lang/messages/el-GR.json
+++ b/frontend/lang/messages/el-GR.json
@@ -69,6 +69,7 @@
"new-notification": "Νέα ειδοποίηση",
"event-notifiers": "Ειδοποιητές Συμβάντος",
"apprise-url-skipped-if-blank": "Apprise URL (παραλείπεται αν είναι κενό)",
+ "apprise-url-is-left-intentionally-blank": "Δεδομένου ότι οι διευθύνσεις URL Apprise περιέχουν συνήθως ευαίσθητες πληροφορίες, το πεδίο αυτό παραμένει σκόπιμα κενό κατά την επεξεργασία. Αν θέλετε να ενημερώσετε το URL, παρακαλώ εισάγετε το νέο εδώ, αλλιώς αφήστε το κενό για να διατηρήσετε την τρέχουσα διεύθυνση URL.",
"enable-notifier": "Ενεργοποίηση ειδοποιητή",
"what-events": "Σε ποια συμβάντα θα πρέπει να εγγραφεί αυτός ο ειδοποιητής;",
"user-events": "Συμβάντα Χρήστη",
diff --git a/frontend/lang/messages/en-GB.json b/frontend/lang/messages/en-GB.json
index e9422c4e9..1714849b1 100644
--- a/frontend/lang/messages/en-GB.json
+++ b/frontend/lang/messages/en-GB.json
@@ -69,6 +69,7 @@
"new-notification": "New Notification",
"event-notifiers": "Event Notifiers",
"apprise-url-skipped-if-blank": "Apprise URL (skipped if blank)",
+ "apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"enable-notifier": "Enable Notifier",
"what-events": "What events should this notifier subscribe to?",
"user-events": "User Events",
diff --git a/frontend/lang/messages/es-ES.json b/frontend/lang/messages/es-ES.json
index b80b8c9d6..ae1a7a5fc 100644
--- a/frontend/lang/messages/es-ES.json
+++ b/frontend/lang/messages/es-ES.json
@@ -69,6 +69,7 @@
"new-notification": "Nueva notificación",
"event-notifiers": "Notificaciones de eventos",
"apprise-url-skipped-if-blank": "URL de Apprise (omitida si está en blanco)",
+ "apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"enable-notifier": "Habilitar notificador",
"what-events": "¿A qué eventos debe suscribirse este notificador?",
"user-events": "Eventos de los usuarios",
diff --git a/frontend/lang/messages/et-EE.json b/frontend/lang/messages/et-EE.json
index 22002f3b9..fac0b8a7e 100644
--- a/frontend/lang/messages/et-EE.json
+++ b/frontend/lang/messages/et-EE.json
@@ -69,6 +69,7 @@
"new-notification": "Uus teade",
"event-notifiers": "Sündmuste märguanded",
"apprise-url-skipped-if-blank": "Apprise URL (kui on tühi, jäetakse vahele)",
+ "apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"enable-notifier": "Luba teavitaja",
"what-events": "Millised sündmused peaks see teavitaja tellimaa?",
"user-events": "Kasutaja sündmused",
@@ -80,7 +81,7 @@
"category-events": "Kategooria sündmused",
"when-a-new-user-joins-your-group": "Kui uus kasutaja liitub sinu grupiga",
"recipe-events": "Retsepti sündmused",
- "label-events": "Label Events"
+ "label-events": "Sildista sündmused"
},
"general": {
"add": "Lisa",
diff --git a/frontend/lang/messages/fi-FI.json b/frontend/lang/messages/fi-FI.json
index e44dce71c..e0c11df0d 100644
--- a/frontend/lang/messages/fi-FI.json
+++ b/frontend/lang/messages/fi-FI.json
@@ -69,6 +69,7 @@
"new-notification": "Uusi ilmoitus",
"event-notifiers": "Tapahtumien ilmoitukset",
"apprise-url-skipped-if-blank": "Ilmoitusverkko-osoite (voi jättää tyhjäksi)",
+ "apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"enable-notifier": "Ota ilmoittaja käyttöön",
"what-events": "Mistä tapahtumista tulisi ilmoittaa?",
"user-events": "Käyttäjän tapahtumat",
diff --git a/frontend/lang/messages/fr-BE.json b/frontend/lang/messages/fr-BE.json
index 0237491d4..d84ddbf0d 100644
--- a/frontend/lang/messages/fr-BE.json
+++ b/frontend/lang/messages/fr-BE.json
@@ -69,6 +69,7 @@
"new-notification": "Nouvelle notification",
"event-notifiers": "Notifications d'événements",
"apprise-url-skipped-if-blank": "URL Apprise (ignoré si vide)",
+ "apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"enable-notifier": "Activer la notification",
"what-events": "À quels événements cette notification doit-elle s'abonner ?",
"user-events": "Evénements utilisateur",
diff --git a/frontend/lang/messages/fr-CA.json b/frontend/lang/messages/fr-CA.json
index 6116d4299..dccbabb71 100644
--- a/frontend/lang/messages/fr-CA.json
+++ b/frontend/lang/messages/fr-CA.json
@@ -69,6 +69,7 @@
"new-notification": "Nouvelle notification",
"event-notifiers": "Notifications d'événements",
"apprise-url-skipped-if-blank": "URL Apprise (ignoré si vide)",
+ "apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"enable-notifier": "Activer la notification",
"what-events": "À quels événements cette notification doit-elle s'abonner ?",
"user-events": "Événements de l'utilisateur",
diff --git a/frontend/lang/messages/fr-FR.json b/frontend/lang/messages/fr-FR.json
index 79f0bcaf2..138b3220c 100644
--- a/frontend/lang/messages/fr-FR.json
+++ b/frontend/lang/messages/fr-FR.json
@@ -69,6 +69,7 @@
"new-notification": "Nouvelle notification",
"event-notifiers": "Notifications d'événements",
"apprise-url-skipped-if-blank": "URL Apprise (ignoré si vide)",
+ "apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"enable-notifier": "Activer la notification",
"what-events": "À quels événements cette notification doit-elle s'abonner ?",
"user-events": "Événements utilisateur",
diff --git a/frontend/lang/messages/gl-ES.json b/frontend/lang/messages/gl-ES.json
index ec809db2d..0d502c522 100644
--- a/frontend/lang/messages/gl-ES.json
+++ b/frontend/lang/messages/gl-ES.json
@@ -69,6 +69,7 @@
"new-notification": "Nova Notificación",
"event-notifiers": "Notificadores de Eventos",
"apprise-url-skipped-if-blank": "URL de Apprise (omitido se está en branco)",
+ "apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"enable-notifier": "Activar o Notificador",
"what-events": "A que eventos debería subscribirse este notificador?",
"user-events": "Eventos de Usuario",
diff --git a/frontend/lang/messages/he-IL.json b/frontend/lang/messages/he-IL.json
index 7d1040b2f..e98aa03e9 100644
--- a/frontend/lang/messages/he-IL.json
+++ b/frontend/lang/messages/he-IL.json
@@ -69,6 +69,7 @@
"new-notification": "התראה חדשה",
"event-notifiers": "מנגנוני התרעה על אירועים",
"apprise-url-skipped-if-blank": "כתובת Apprise (דלג אם ריק)",
+ "apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"enable-notifier": "הפעלת מתריע",
"what-events": "לאילו אירועים לרשום את מתריע זה?",
"user-events": "אירועי משתמש",
diff --git a/frontend/lang/messages/hr-HR.json b/frontend/lang/messages/hr-HR.json
index c33385ccd..57686de91 100644
--- a/frontend/lang/messages/hr-HR.json
+++ b/frontend/lang/messages/hr-HR.json
@@ -69,6 +69,7 @@
"new-notification": "Nova Obavijest",
"event-notifiers": "Obavještavatelji Događaja",
"apprise-url-skipped-if-blank": "Apprise URL (preskočeno ako je prazno)",
+ "apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"enable-notifier": "Omogući obavještavanje",
"what-events": "Na koje događaje bi ovaj obavještavatelj trebao biti pretplaćen?",
"user-events": "Događaji Korisnika",
diff --git a/frontend/lang/messages/hu-HU.json b/frontend/lang/messages/hu-HU.json
index 4a4c5c501..2237195be 100644
--- a/frontend/lang/messages/hu-HU.json
+++ b/frontend/lang/messages/hu-HU.json
@@ -69,6 +69,7 @@
"new-notification": "Új értesítés",
"event-notifiers": "Esemény értesítők",
"apprise-url-skipped-if-blank": "Értesítendő URL (kihagy, ha üres)",
+ "apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"enable-notifier": "Értesítés engedélyezése",
"what-events": "Milyen eseményekre figyeljen ez az értesítés?",
"user-events": "Felhasználói Események",
diff --git a/frontend/lang/messages/is-IS.json b/frontend/lang/messages/is-IS.json
index 0827e7974..81fb0ef27 100644
--- a/frontend/lang/messages/is-IS.json
+++ b/frontend/lang/messages/is-IS.json
@@ -69,6 +69,7 @@
"new-notification": "Ný tilkynning",
"event-notifiers": "Viðburðar tilkynningar",
"apprise-url-skipped-if-blank": "Apprise URL (sleppt ef tómt)",
+ "apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"enable-notifier": "Virkja tilkynningar",
"what-events": "Hvaða viðburði ætti þessi tilkynnir að vera áskrifandi að?",
"user-events": "Notenda viðburðir",
diff --git a/frontend/lang/messages/it-IT.json b/frontend/lang/messages/it-IT.json
index a3e96830c..d6f08b576 100644
--- a/frontend/lang/messages/it-IT.json
+++ b/frontend/lang/messages/it-IT.json
@@ -69,6 +69,7 @@
"new-notification": "Nuova Notifica",
"event-notifiers": "Notifiche Evento",
"apprise-url-skipped-if-blank": "Url di Apprise (ignorato se vuoto)",
+ "apprise-url-is-left-intentionally-blank": "Poiché gli URL Apprise contengono in genere informazioni sensibili, questo campo viene lasciato intenzionalmente vuoto durante la modifica. Se si desidera aggiornare l'URL, inserire qui il nuovo URL, altrimenti lasciarlo vuoto per mantenere l'URL corrente.",
"enable-notifier": "Abilita Notificatore",
"what-events": "Quali eventi dovrebbe sottoscrivere questo notificatore?",
"user-events": "Eventi Utente",
@@ -80,7 +81,7 @@
"category-events": "Categoria Eventi",
"when-a-new-user-joins-your-group": "Quando un nuovo utente entra nel tuo gruppo",
"recipe-events": "Eventi di ricette",
- "label-events": "Label Events"
+ "label-events": "Eventi Etichetta"
},
"general": {
"add": "Aggiungi",
@@ -473,7 +474,7 @@
"comment": "Commento",
"comments": "Commenti",
"delete-confirmation": "Sei sicuro di voler eliminare questa ricetta?",
- "admin-delete-confirmation": "You're about to delete a recipe that isn't yours using admin permissions. Are you sure?",
+ "admin-delete-confirmation": "Stai per eliminare una ricetta che non è tua usando i permessi di amministrazione. Sei sicuro?",
"delete-recipe": "Elimina Ricetta",
"description": "Descrizione",
"disable-amount": "Disabilita Quantità Ingredienti",
@@ -581,10 +582,10 @@
"made-this": "L'Ho Preparato",
"how-did-it-turn-out": "Come è venuto?",
"user-made-this": "{user} l'ha preparato",
- "added-to-timeline": "Added to timeline",
- "failed-to-add-to-timeline": "Failed to add to timeline",
- "failed-to-update-recipe": "Failed to update recipe",
- "added-to-timeline-but-failed-to-add-image": "Added to timeline, but failed to add image",
+ "added-to-timeline": "Aggiunto alla cronologia",
+ "failed-to-add-to-timeline": "Impossibile aggiungere alla cronologia",
+ "failed-to-update-recipe": "Impossibile aggiornare la ricetta",
+ "added-to-timeline-but-failed-to-add-image": "Aggiunto alla cronologia, ma non è stato possibile aggiungere l'immagine",
"api-extras-description": "Le opzioni extra delle ricette sono una caratteristica fondamentale dell'API Mealie. Consentono di creare json personalizzati con coppie di chiavi/valore all'interno di una ricetta a cui fare riferimento tramite applicazioni terze. È possibile utilizzare queste chiavi per inserire informazioni, per esempio per attivare automazioni oppure per inoltrare messaggi personalizzati al dispositivo desiderato.",
"message-key": "Chiave Messaggio",
"parse": "Analizza",
@@ -606,10 +607,10 @@
"create-recipe-from-an-image": "Crea ricetta da un'immagine",
"create-recipe-from-an-image-description": "Crea una ricetta caricando un'immagine di essa. Mealie tenterà di estrarre il testo dall'immagine usando l'IA e creare una ricetta da esso.",
"crop-and-rotate-the-image": "Ritaglia e ruota l'immagine in modo che solo il testo sia visibile e che sia orientato correttamente.",
- "create-from-images": "Create from Images",
+ "create-from-images": "Crea da immagini",
"should-translate-description": "Traduci la ricetta nella mia lingua",
"please-wait-image-procesing": "Attendere, l'immagine è in fase di elaborazione. Potrebbe volerci un po' di tempo.",
- "please-wait-images-processing": "Please wait, the images are processing. This may take some time.",
+ "please-wait-images-processing": "Attendere, le immagini sono in fase di elaborazione. Potrebbe volerci un po' di tempo.",
"bulk-url-import": "Importazione multipla URL",
"debug-scraper": "Debug Scraper",
"create-a-recipe-by-providing-the-name-all-recipes-must-have-unique-names": "Crea una ricetta fornendo il nome. Tutte le ricette devono avere nomi univoci.",
@@ -665,17 +666,17 @@
"no-unit": "Nessuna unità",
"missing-unit": "Crea unità mancante: {unit}",
"missing-food": "Crea cibo mancante: {food}",
- "this-unit-could-not-be-parsed-automatically": "This unit could not be parsed automatically",
- "this-food-could-not-be-parsed-automatically": "This food could not be parsed automatically",
+ "this-unit-could-not-be-parsed-automatically": "Questa unità non può essere analizzata automaticamente",
+ "this-food-could-not-be-parsed-automatically": "Questo alimento non può essere analizzato automaticamente",
"no-food": "Nessun Alimento"
},
"reset-servings-count": "Reimposta conteggio porzioni",
"not-linked-ingredients": "Ingredienti Aggiuntivi",
- "upload-another-image": "Upload another image",
- "upload-images": "Upload images",
- "upload-more-images": "Upload more images",
- "set-as-cover-image": "Set as recipe cover image",
- "cover-image": "Cover image"
+ "upload-another-image": "Carica un'altra immagine",
+ "upload-images": "Carica immagini",
+ "upload-more-images": "Carica altre immagini",
+ "set-as-cover-image": "Imposta come immagine di copertina della ricetta",
+ "cover-image": "Immagine di copertina"
},
"recipe-finder": {
"recipe-finder": "Trova ricette",
@@ -1168,7 +1169,7 @@
"group-details": "Dettagli Gruppo",
"group-details-description": "Prima di creare un account, è necessario creare un gruppo. Il gruppo conterrà solo voi, ma potrete invitare altre persone in seguito. I membri del gruppo possono condividere piani alimentari, liste della spesa, ricette e molto altro!",
"use-seed-data": "Utilizzo Dati Generati",
- "use-seed-data-description": "Mealie ships with a collection of Foods, Units, and Labels that can be used to populate your group with helpful data for organizing your recipes. These are translated into the language you currently have selected. You can always add to or modify this data later.",
+ "use-seed-data-description": "Mealie include una raccolta di Alimenti, Unità ed Etichette che possono essere utilizzate per arricchire il proprio gruppo con dati utili per organizzare le proprie ricette. Questi dati vengono tradotti nella lingua selezionata. Si può sempre aggiungere o modificare questi dati in seguito.",
"account-details": "Dettagli dell'Account"
},
"validation": {
diff --git a/frontend/lang/messages/ja-JP.json b/frontend/lang/messages/ja-JP.json
index 5d3a21ce4..48f1813c0 100644
--- a/frontend/lang/messages/ja-JP.json
+++ b/frontend/lang/messages/ja-JP.json
@@ -69,6 +69,7 @@
"new-notification": "新着通知",
"event-notifiers": "イベント通知",
"apprise-url-skipped-if-blank": "通知用URL (空欄の場合はスキップ)",
+ "apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"enable-notifier": "通知を有効にする",
"what-events": "この通知はどのイベントを購読すべきですか?",
"user-events": "ユーザーイベント",
diff --git a/frontend/lang/messages/ko-KR.json b/frontend/lang/messages/ko-KR.json
index af25aa68b..a25738473 100644
--- a/frontend/lang/messages/ko-KR.json
+++ b/frontend/lang/messages/ko-KR.json
@@ -69,6 +69,7 @@
"new-notification": "새 알림",
"event-notifiers": "이벤트 알림이",
"apprise-url-skipped-if-blank": "Apprise URL (비워두면 생략합니다)",
+ "apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"enable-notifier": "알림 활성화",
"what-events": "이 알리미는 어떤 이벤트를 구독해야 합니까?",
"user-events": "사용자 이벤트",
diff --git a/frontend/lang/messages/lt-LT.json b/frontend/lang/messages/lt-LT.json
index 00b4f9fef..9ef34c2e4 100644
--- a/frontend/lang/messages/lt-LT.json
+++ b/frontend/lang/messages/lt-LT.json
@@ -69,6 +69,7 @@
"new-notification": "Naujas pranešimas",
"event-notifiers": "Įvykių pranešimai",
"apprise-url-skipped-if-blank": "Apprise URL (praleidžiama, jei tuščia)",
+ "apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"enable-notifier": "Įjungti pranešiklį",
"what-events": "Kokie įvykiai turėtų būti sekami?",
"user-events": "Naudotojų įvykiai",
diff --git a/frontend/lang/messages/lv-LV.json b/frontend/lang/messages/lv-LV.json
index a9eff0c1a..4c091f6f8 100644
--- a/frontend/lang/messages/lv-LV.json
+++ b/frontend/lang/messages/lv-LV.json
@@ -69,6 +69,7 @@
"new-notification": "Jauns paziņojums",
"event-notifiers": "Notikumu paziņotāji",
"apprise-url-skipped-if-blank": "Apprise URL (izlaists, ja tukšs)",
+ "apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"enable-notifier": "Iespējot paziņotāju",
"what-events": "Kādus notikumus šim paziņotājam vajadzētu abonēt?",
"user-events": "Lietotāju notikumi",
diff --git a/frontend/lang/messages/nl-NL.json b/frontend/lang/messages/nl-NL.json
index 4bd4223c8..2617963c1 100644
--- a/frontend/lang/messages/nl-NL.json
+++ b/frontend/lang/messages/nl-NL.json
@@ -69,6 +69,7 @@
"new-notification": "Nieuwe melding",
"event-notifiers": "Meldingen van gebeurtenissen",
"apprise-url-skipped-if-blank": "URL van Apprise (overgeslagen als veld leeg is)",
+ "apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"enable-notifier": "Activeer melding",
"what-events": "Op welke gebeurtenissen moet deze melding zich abonneren?",
"user-events": "Gebeurtenissen van gebruiker",
diff --git a/frontend/lang/messages/no-NO.json b/frontend/lang/messages/no-NO.json
index ef2b7dbcd..a9fc9a55e 100644
--- a/frontend/lang/messages/no-NO.json
+++ b/frontend/lang/messages/no-NO.json
@@ -69,6 +69,7 @@
"new-notification": "Nytt varsel",
"event-notifiers": "Hendelsesvarsler",
"apprise-url-skipped-if-blank": "Apprise-URL (hoppes over hvis tom)",
+ "apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"enable-notifier": "Aktiver varslingsagenten",
"what-events": "Hvilke hendelser skal denne varslingsagenten abonnere på?",
"user-events": "Brukerhendelser",
diff --git a/frontend/lang/messages/pl-PL.json b/frontend/lang/messages/pl-PL.json
index 82025d485..7ecec3105 100644
--- a/frontend/lang/messages/pl-PL.json
+++ b/frontend/lang/messages/pl-PL.json
@@ -69,6 +69,7 @@
"new-notification": "Nowe powiadomienie",
"event-notifiers": "Powiadomienia o zdarzeniach",
"apprise-url-skipped-if-blank": "URL Apprise (pominięty, jeśli puste)",
+ "apprise-url-is-left-intentionally-blank": "Ponieważ adresy URL Apprise zawierają zazwyczaj poufne informacje, pole to pozostaje celowo puste podczas edycji. Jeśli chcesz zaktualizować adres URL, wprowadź ten nowy tutaj, w przeciwnym razie pozostaw puste, aby zachować bieżący adres URL.",
"enable-notifier": "Włącz Powiadomienie",
"what-events": "Jakie zdarzenia powinien subskrybować ten powiadamiający?",
"user-events": "Zdarzenia użytkownika",
@@ -80,7 +81,7 @@
"category-events": "Wydarzenia kategorii",
"when-a-new-user-joins-your-group": "Kiedy nowy użytkownik dołączy do Twojej grupy",
"recipe-events": "Zdarzenia Przepisów",
- "label-events": "Label Events"
+ "label-events": "Etykieta wydarzeń"
},
"general": {
"add": "Dodaj",
@@ -674,8 +675,8 @@
"upload-another-image": "Prześlij kolejny obraz",
"upload-images": "Prześlij obraz",
"upload-more-images": "Prześlij więcej obrazów",
- "set-as-cover-image": "Set as recipe cover image",
- "cover-image": "Cover image"
+ "set-as-cover-image": "Ustaw jako okładkę przepisu",
+ "cover-image": "Okładka"
},
"recipe-finder": {
"recipe-finder": "Wyszukiwarka przepisów",
@@ -1168,7 +1169,7 @@
"group-details": "Szczegóły grupy",
"group-details-description": "Zanim utworzysz konto musisz stworzyć grupę. Twoja grupa zawierać będzie tylko Ciebie, ale będziesz istniała możlwiość zaproszenia do niej innych. Użytkownicy Twojej grupy mogą współdzielić plany posiłków, listy zakupów, przepisy i więcej!",
"use-seed-data": "Użyj przykładowych danych",
- "use-seed-data-description": "Mealie ships with a collection of Foods, Units, and Labels that can be used to populate your group with helpful data for organizing your recipes. These are translated into the language you currently have selected. You can always add to or modify this data later.",
+ "use-seed-data-description": "Wysyłka posiłków z kolekcją żywności, jednostek i etykiet, które mogą być użyte do wypełnienia Twojej grupy pomocnymi danymi do organizacji twoich przepisów. Są one tłumaczone na wybrany język. Zawsze możesz dodać lub zmodyfikować te dane później.",
"account-details": "Szczegóły konta"
},
"validation": {
diff --git a/frontend/lang/messages/pt-BR.json b/frontend/lang/messages/pt-BR.json
index c319feae9..6beede4b3 100644
--- a/frontend/lang/messages/pt-BR.json
+++ b/frontend/lang/messages/pt-BR.json
@@ -69,6 +69,7 @@
"new-notification": "Nova Notificação",
"event-notifiers": "Notificações de Eventos",
"apprise-url-skipped-if-blank": "URL Apprise (ignorado se estiver em branco)",
+ "apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"enable-notifier": "Habilitar Notificador",
"what-events": "A quais eventos este notificador deve subscrever?",
"user-events": "Eventos do usuário",
diff --git a/frontend/lang/messages/pt-PT.json b/frontend/lang/messages/pt-PT.json
index 322440698..d610b69d7 100644
--- a/frontend/lang/messages/pt-PT.json
+++ b/frontend/lang/messages/pt-PT.json
@@ -69,6 +69,7 @@
"new-notification": "Nova Notificação",
"event-notifiers": "Notificadores de eventos",
"apprise-url-skipped-if-blank": "URL da Apprise (ignorado se vazio)",
+ "apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"enable-notifier": "Ativar Notificador",
"what-events": "Que eventos este notificador deve subscrever?",
"user-events": "Eventos do utilizador",
diff --git a/frontend/lang/messages/ro-RO.json b/frontend/lang/messages/ro-RO.json
index 1fe21ef1f..311903074 100644
--- a/frontend/lang/messages/ro-RO.json
+++ b/frontend/lang/messages/ro-RO.json
@@ -69,6 +69,7 @@
"new-notification": "Notificare nouă",
"event-notifiers": "Notificatori de evenimente",
"apprise-url-skipped-if-blank": "URL Apprise (ignorat daca e gol)",
+ "apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"enable-notifier": "Activare notificator",
"what-events": "La ce evenimente ar trebui să se înscrie acest notificator?",
"user-events": "Evenimente Utilizator",
diff --git a/frontend/lang/messages/ru-RU.json b/frontend/lang/messages/ru-RU.json
index 532c330b4..0f7a28b60 100644
--- a/frontend/lang/messages/ru-RU.json
+++ b/frontend/lang/messages/ru-RU.json
@@ -69,6 +69,7 @@
"new-notification": "Новое уведомление",
"event-notifiers": "Уведомления о событии",
"apprise-url-skipped-if-blank": "URL-адрес (пропущен, если пусто)",
+ "apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"enable-notifier": "Включить уведомления",
"what-events": "На какие события следует настроить уведомления?",
"user-events": "События пользователя",
diff --git a/frontend/lang/messages/sk-SK.json b/frontend/lang/messages/sk-SK.json
index 1b093f009..dcb5c7794 100644
--- a/frontend/lang/messages/sk-SK.json
+++ b/frontend/lang/messages/sk-SK.json
@@ -69,6 +69,7 @@
"new-notification": "Nové upozornenie",
"event-notifiers": "Upozornenia udalostí",
"apprise-url-skipped-if-blank": "Informačná URL (preskočená, ak je prázdna)",
+ "apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"enable-notifier": "Zapnúť notifikátor",
"what-events": "Pre ktoré udalosti si želáte zapnúť notifikátor?",
"user-events": "Udalosti používateľa",
diff --git a/frontend/lang/messages/sl-SI.json b/frontend/lang/messages/sl-SI.json
index 7eb9731fb..ec8557f49 100644
--- a/frontend/lang/messages/sl-SI.json
+++ b/frontend/lang/messages/sl-SI.json
@@ -69,6 +69,7 @@
"new-notification": "Novo obvestilo",
"event-notifiers": "Obvestila o dogodkih",
"apprise-url-skipped-if-blank": "Apprise URL (preskočeno, če je prazno)",
+ "apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"enable-notifier": "Vključi obvestila",
"what-events": "Katere dogodke naj spremlja obveščevalni sistem?",
"user-events": "Dogodki uporabnika",
diff --git a/frontend/lang/messages/sr-SP.json b/frontend/lang/messages/sr-SP.json
index f98792991..433b5ff53 100644
--- a/frontend/lang/messages/sr-SP.json
+++ b/frontend/lang/messages/sr-SP.json
@@ -69,6 +69,7 @@
"new-notification": "Ново обавештење",
"event-notifiers": "Обавештавач о догађају",
"apprise-url-skipped-if-blank": "Apprise URL (прескочено ако је празно)",
+ "apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"enable-notifier": "Омогући обавештење",
"what-events": "На које догађаје би требао да се претплати овај обавештавач?",
"user-events": "Догађаји корисника",
diff --git a/frontend/lang/messages/sv-SE.json b/frontend/lang/messages/sv-SE.json
index f360ff81f..4152964e4 100644
--- a/frontend/lang/messages/sv-SE.json
+++ b/frontend/lang/messages/sv-SE.json
@@ -69,6 +69,7 @@
"new-notification": "Ny avisering",
"event-notifiers": "Händelseavisering",
"apprise-url-skipped-if-blank": "Apprise-URL (hoppa över om tom)",
+ "apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"enable-notifier": "Aktivera avisering",
"what-events": "Vilka händelser ska denna avisering prenumerera på?",
"user-events": "Användarhändelser",
diff --git a/frontend/lang/messages/tr-TR.json b/frontend/lang/messages/tr-TR.json
index bac2d4c24..d3e334766 100644
--- a/frontend/lang/messages/tr-TR.json
+++ b/frontend/lang/messages/tr-TR.json
@@ -69,6 +69,7 @@
"new-notification": "Yeni bildirim",
"event-notifiers": "Etkinlik Bildirimleri",
"apprise-url-skipped-if-blank": "Apprise URL'si (boşsa geçilir)",
+ "apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"enable-notifier": "Bildiriciyi Etkinleştir",
"what-events": "Bu bildirimci hangi olaylara abone olmalıdır?",
"user-events": "Kullanıcı Etkinlikleri",
diff --git a/frontend/lang/messages/uk-UA.json b/frontend/lang/messages/uk-UA.json
index 26380f57b..c2acf69ae 100644
--- a/frontend/lang/messages/uk-UA.json
+++ b/frontend/lang/messages/uk-UA.json
@@ -69,6 +69,7 @@
"new-notification": "Нове сповіщення",
"event-notifiers": "Сповіщувачі",
"apprise-url-skipped-if-blank": "Apprise URL (пропущено якщо порожній)",
+ "apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"enable-notifier": "Увімкнути сповіщувач",
"what-events": "На які події цей сповіщувач має бути підписаний?",
"user-events": "Події користувача",
diff --git a/frontend/lang/messages/vi-VN.json b/frontend/lang/messages/vi-VN.json
index 36d820d37..0581a30db 100644
--- a/frontend/lang/messages/vi-VN.json
+++ b/frontend/lang/messages/vi-VN.json
@@ -69,6 +69,7 @@
"new-notification": "Thông báo mới",
"event-notifiers": "Event Notifiers",
"apprise-url-skipped-if-blank": "Apprise URL (skipped if blank)",
+ "apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"enable-notifier": "Enable Notifier",
"what-events": "What events should this notifier subscribe to?",
"user-events": "User Events",
diff --git a/frontend/lang/messages/zh-CN.json b/frontend/lang/messages/zh-CN.json
index 9e87a6648..9a62cfc54 100644
--- a/frontend/lang/messages/zh-CN.json
+++ b/frontend/lang/messages/zh-CN.json
@@ -69,6 +69,7 @@
"new-notification": "新通知",
"event-notifiers": "事件通知器",
"apprise-url-skipped-if-blank": "Apprise URL (如果为空则跳过)",
+ "apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"enable-notifier": "打开消息通知",
"what-events": "该通知器需要订阅哪些事件?",
"user-events": "用户事件",
diff --git a/frontend/lang/messages/zh-TW.json b/frontend/lang/messages/zh-TW.json
index 66fe62a40..7cbc31c05 100644
--- a/frontend/lang/messages/zh-TW.json
+++ b/frontend/lang/messages/zh-TW.json
@@ -69,6 +69,7 @@
"new-notification": "新通知",
"event-notifiers": "事件通知",
"apprise-url-skipped-if-blank": "Apprise 網址(空白則略過)",
+ "apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"enable-notifier": "啟用通知功能",
"what-events": "要訂閱哪些事件通知?",
"user-events": "用戶相關事件",
diff --git a/frontend/lib/api/public/explore/cookbooks.ts b/frontend/lib/api/public/explore/cookbooks.ts
index ee7e6230c..3afeb40d4 100644
--- a/frontend/lib/api/public/explore/cookbooks.ts
+++ b/frontend/lib/api/public/explore/cookbooks.ts
@@ -1,5 +1,5 @@
import { BaseCRUDAPIReadOnly } from "~/lib/api/base/base-clients";
-import { RecipeCookBook } from "~/lib/api/types/cookbook";
+import { ReadCookBook } from "~/lib/api/types/cookbook";
import { ApiRequestInstance } from "~/lib/api/types/non-generated";
const prefix = "/api";
@@ -10,7 +10,7 @@ const routes = {
cookbooksGroupSlugCookbookId: (groupSlug: string | number, cookbookId: string | number) => `${exploreGroupSlug(groupSlug)}/cookbooks/${cookbookId}`,
};
-export class PublicCookbooksApi extends BaseCRUDAPIReadOnly {
+export class PublicCookbooksApi extends BaseCRUDAPIReadOnly {
constructor(requests: ApiRequestInstance, groupSlug: string) {
super(
requests,
diff --git a/frontend/lib/api/types/admin.ts b/frontend/lib/api/types/admin.ts
index 6f2022f6e..868cb74e6 100644
--- a/frontend/lib/api/types/admin.ts
+++ b/frontend/lib/api/types/admin.ts
@@ -1,4 +1,5 @@
/* tslint:disable */
+/* eslint-disable */
/**
/* This file was automatically generated from pydantic models by running pydantic2ts.
/* Do not modify it by hand - just update the pydantic models and then re-run the script
diff --git a/frontend/lib/api/types/analytics.ts b/frontend/lib/api/types/analytics.ts
index 5bdc7fbcd..f0eb9d489 100644
--- a/frontend/lib/api/types/analytics.ts
+++ b/frontend/lib/api/types/analytics.ts
@@ -1,4 +1,5 @@
/* tslint:disable */
+/* eslint-disable */
/**
/* This file was automatically generated from pydantic models by running pydantic2ts.
/* Do not modify it by hand - just update the pydantic models and then re-run the script
diff --git a/frontend/lib/api/types/cookbook.ts b/frontend/lib/api/types/cookbook.ts
index a35e5cd5f..753b11c6a 100644
--- a/frontend/lib/api/types/cookbook.ts
+++ b/frontend/lib/api/types/cookbook.ts
@@ -1,4 +1,5 @@
/* tslint:disable */
+/* eslint-disable */
/**
/* This file was automatically generated from pydantic models by running pydantic2ts.
/* Do not modify it by hand - just update the pydantic models and then re-run the script
@@ -38,67 +39,6 @@ export interface QueryFilterJSONPart {
attributeName?: string | null;
relationalOperator?: RelationalKeyword | RelationalOperator | null;
value?: string | string[] | null;
-}
-export interface RecipeCookBook {
- name: string;
- description?: string;
- slug?: string | null;
- position?: number;
- public?: boolean;
- queryFilterString?: string;
- groupId: string;
- householdId: string;
- id: string;
- queryFilter?: QueryFilterJSON;
- recipes: RecipeSummary[];
-}
-export interface RecipeSummary {
- id?: string | null;
- userId?: string;
- householdId?: string;
- groupId?: string;
- name?: string | null;
- slug?: string;
- image?: unknown;
- recipeServings?: number;
- recipeYieldQuantity?: number;
- recipeYield?: string | null;
- totalTime?: string | null;
- prepTime?: string | null;
- cookTime?: string | null;
- performTime?: string | null;
- description?: string | null;
- recipeCategory?: RecipeCategory[] | null;
- tags?: RecipeTag[] | null;
- tools?: RecipeTool[];
- rating?: number | null;
- orgURL?: string | null;
- dateAdded?: string | null;
- dateUpdated?: string | null;
- createdAt?: string | null;
- updatedAt?: string | null;
- lastMade?: string | null;
-}
-export interface RecipeCategory {
- id?: string | null;
- groupId?: string | null;
- name: string;
- slug: string;
- [k: string]: unknown;
-}
-export interface RecipeTag {
- id?: string | null;
- groupId?: string | null;
- name: string;
- slug: string;
- [k: string]: unknown;
-}
-export interface RecipeTool {
- id: string;
- groupId?: string | null;
- name: string;
- slug: string;
- householdsWithTool?: string[];
[k: string]: unknown;
}
export interface SaveCookBook {
diff --git a/frontend/lib/api/types/group.ts b/frontend/lib/api/types/group.ts
index 8ebf2c96c..bc2fbcf62 100644
--- a/frontend/lib/api/types/group.ts
+++ b/frontend/lib/api/types/group.ts
@@ -1,4 +1,5 @@
/* tslint:disable */
+/* eslint-disable */
/**
/* This file was automatically generated from pydantic models by running pydantic2ts.
/* Do not modify it by hand - just update the pydantic models and then re-run the script
diff --git a/frontend/lib/api/types/household.ts b/frontend/lib/api/types/household.ts
index f3400dea4..cfe4ff3f9 100644
--- a/frontend/lib/api/types/household.ts
+++ b/frontend/lib/api/types/household.ts
@@ -1,4 +1,5 @@
/* tslint:disable */
+/* eslint-disable */
/**
/* This file was automatically generated from pydantic models by running pydantic2ts.
/* Do not modify it by hand - just update the pydantic models and then re-run the script
diff --git a/frontend/lib/api/types/labels.ts b/frontend/lib/api/types/labels.ts
index ee9335bb2..a8fc4c046 100644
--- a/frontend/lib/api/types/labels.ts
+++ b/frontend/lib/api/types/labels.ts
@@ -1,4 +1,5 @@
/* tslint:disable */
+/* eslint-disable */
/**
/* This file was automatically generated from pydantic models by running pydantic2ts.
/* Do not modify it by hand - just update the pydantic models and then re-run the script
diff --git a/frontend/lib/api/types/meal-plan.ts b/frontend/lib/api/types/meal-plan.ts
index 85f03de97..b6d1c08e4 100644
--- a/frontend/lib/api/types/meal-plan.ts
+++ b/frontend/lib/api/types/meal-plan.ts
@@ -1,4 +1,5 @@
/* tslint:disable */
+/* eslint-disable */
/**
/* This file was automatically generated from pydantic models by running pydantic2ts.
/* Do not modify it by hand - just update the pydantic models and then re-run the script
diff --git a/frontend/lib/api/types/reports.ts b/frontend/lib/api/types/reports.ts
index 428c39f40..2b763275e 100644
--- a/frontend/lib/api/types/reports.ts
+++ b/frontend/lib/api/types/reports.ts
@@ -1,4 +1,5 @@
/* tslint:disable */
+/* eslint-disable */
/**
/* This file was automatically generated from pydantic models by running pydantic2ts.
/* Do not modify it by hand - just update the pydantic models and then re-run the script
diff --git a/frontend/lib/api/types/response.ts b/frontend/lib/api/types/response.ts
index 9fa568846..dfa8a54f4 100644
--- a/frontend/lib/api/types/response.ts
+++ b/frontend/lib/api/types/response.ts
@@ -1,4 +1,5 @@
/* tslint:disable */
+/* eslint-disable */
/**
/* This file was automatically generated from pydantic models by running pydantic2ts.
/* Do not modify it by hand - just update the pydantic models and then re-run the script
diff --git a/frontend/lib/api/types/user.ts b/frontend/lib/api/types/user.ts
index a5818778b..029e166b6 100644
--- a/frontend/lib/api/types/user.ts
+++ b/frontend/lib/api/types/user.ts
@@ -1,4 +1,5 @@
/* tslint:disable */
+/* eslint-disable */
/**
/* This file was automatically generated from pydantic models by running pydantic2ts.
/* Do not modify it by hand - just update the pydantic models and then re-run the script
diff --git a/frontend/lib/api/user/group-cookbooks.ts b/frontend/lib/api/user/group-cookbooks.ts
index 9d5631558..640a6ef5e 100644
--- a/frontend/lib/api/user/group-cookbooks.ts
+++ b/frontend/lib/api/user/group-cookbooks.ts
@@ -1,5 +1,5 @@
import { BaseCRUDAPI } from "../base/base-clients";
-import type { CreateCookBook, RecipeCookBook, UpdateCookBook } from "~/lib/api/types/cookbook";
+import type { CreateCookBook, ReadCookBook, UpdateCookBook } from "~/lib/api/types/cookbook";
const prefix = "/api";
@@ -8,7 +8,7 @@ const routes = {
cookbooksId: (id: number) => `${prefix}/households/cookbooks/${id}`,
};
-export class CookbookAPI extends BaseCRUDAPI {
+export class CookbookAPI extends BaseCRUDAPI {
baseRoute: string = routes.cookbooks;
itemRoute = routes.cookbooksId;
diff --git a/frontend/types/components.d.ts b/frontend/types/components.d.ts
index 9d0364a59..d074283ee 100644
--- a/frontend/types/components.d.ts
+++ b/frontend/types/components.d.ts
@@ -81,4 +81,4 @@ declare module "vue" {
}
}
-export { };
+export {};
diff --git a/mealie/repos/seed/resources/foods/locales/it-IT.json b/mealie/repos/seed/resources/foods/locales/it-IT.json
index 826eab2ec..351766550 100644
--- a/mealie/repos/seed/resources/foods/locales/it-IT.json
+++ b/mealie/repos/seed/resources/foods/locales/it-IT.json
@@ -100,12 +100,12 @@
"sweet corn": {
"aliases": [],
"description": "",
- "name": "sweet corn",
- "plural_name": "sweet corns"
+ "name": "mais dolce",
+ "plural_name": "mais dolci"
},
"chile pepper": {
"aliases": [
- "capsicum"
+ "peperoncino"
],
"description": "",
"name": "peperoncino",
@@ -132,8 +132,8 @@
"baby green": {
"aliases": [],
"description": "",
- "name": "baby green",
- "plural_name": "baby greens"
+ "name": "insalatina",
+ "plural_name": "insalatine"
},
"pumpkin": {
"aliases": [],
@@ -151,112 +151,112 @@
"aliases": [],
"description": "",
"name": "cavolo",
- "plural_name": "cabbages"
+ "plural_name": "cavoli"
},
"asparagu": {
"aliases": [],
"description": "",
- "name": "asparagu",
- "plural_name": "asparagus"
+ "name": "asparago",
+ "plural_name": "asparagi"
},
"kale": {
"aliases": [],
"description": "",
- "name": "kale",
- "plural_name": "kales"
+ "name": "cavolo riccio",
+ "plural_name": "cavoli ricci"
},
"arugula": {
"aliases": [],
"description": "",
- "name": "arugula",
- "plural_name": "arugulas"
+ "name": "rucola",
+ "plural_name": "rucole"
},
"leek": {
"aliases": [],
"description": "",
- "name": "leek",
- "plural_name": "leeks"
+ "name": "porro",
+ "plural_name": "porri"
},
"eggplant": {
"aliases": [],
"description": "",
- "name": "eggplant",
- "plural_name": "eggplants"
+ "name": "melanzana",
+ "plural_name": "melanzane"
},
"lettuce": {
"aliases": [],
"description": "",
- "name": "lettuce",
- "plural_name": "lettuces"
+ "name": "lattuga",
+ "plural_name": "lattughe"
},
"butternut squash": {
"aliases": [],
"description": "",
- "name": "butternut squash",
- "plural_name": "butternut squashes"
+ "name": "zucca violina",
+ "plural_name": "zucche violine"
},
"romaine": {
"aliases": [],
"description": "",
- "name": "romaine",
- "plural_name": "romaines"
+ "name": "lattuga romana",
+ "plural_name": "lattughe romane"
},
"beetroot": {
"aliases": [],
"description": "",
- "name": "beetroot",
- "plural_name": "beetroots"
+ "name": "barbabietola",
+ "plural_name": "barbabietole"
},
"brussels sprout": {
"aliases": [],
"description": "",
- "name": "brussels sprout",
- "plural_name": "brussels sprouts"
+ "name": "cavoletto di Bruxelles",
+ "plural_name": "cavoletti di Bruxelles"
},
"fennel": {
"aliases": [],
"description": "",
- "name": "fennel",
- "plural_name": "fennels"
+ "name": "finocchio",
+ "plural_name": "finocchi"
},
"sun dried tomato": {
"aliases": [],
"description": "",
- "name": "sun dried tomato",
- "plural_name": "sun dried tomatoes"
+ "name": "pomodoro essiccato al sole",
+ "plural_name": "pomodori essiccati al sole"
},
"radish": {
"aliases": [],
"description": "",
- "name": "radish",
- "plural_name": "radishes"
+ "name": "ravanello",
+ "plural_name": "ravanelli"
},
"red cabbage": {
"aliases": [],
"description": "",
- "name": "red cabbage",
- "plural_name": "red cabbages"
+ "name": "cavolo rosso",
+ "plural_name": "cavoli rossi"
},
"artichoke": {
"aliases": [],
"description": "",
- "name": "artichoke",
- "plural_name": "artichokes"
+ "name": "carciofo",
+ "plural_name": "carciofi"
},
"new potato": {
"aliases": [],
"description": "",
- "name": "new potato",
- "plural_name": "new potatoes"
+ "name": "patata nuova",
+ "plural_name": "patate nuove"
},
"summer squash": {
"aliases": [
- "courgette",
+ "zucchina",
"gem squash"
],
"description": "",
- "name": "summer squash",
- "plural_name": "summer squashes"
+ "name": "zucca estiva",
+ "plural_name": "zucche estive"
},
"mixed green": {
"aliases": [],
@@ -267,20 +267,20 @@
"parsnip": {
"aliases": [],
"description": "",
- "name": "parsnip",
- "plural_name": "parsnips"
+ "name": "pastinaca",
+ "plural_name": "pastinache"
},
"baby carrot": {
"aliases": [],
"description": "",
- "name": "baby carrot",
- "plural_name": "baby carrots"
+ "name": "carotina",
+ "plural_name": "carotine"
},
"mixed vegetable": {
"aliases": [],
"description": "",
- "name": "mixed vegetable",
- "plural_name": "mixed vegetables"
+ "name": "ortaggi misti",
+ "plural_name": "ortaggi misti"
},
"poblano pepper": {
"aliases": [],
@@ -303,50 +303,50 @@
"cayenne pepper": {
"aliases": [],
"description": "",
- "name": "cayenne pepper",
- "plural_name": "cayenne peppers"
+ "name": "pepe di Caienna",
+ "plural_name": "pepi di Caienna"
},
"green tomato": {
"aliases": [],
"description": "",
- "name": "green tomato",
- "plural_name": "green tomatoes"
+ "name": "pomodoro verde",
+ "plural_name": "pomodori verdi"
},
"watercress": {
"aliases": [],
"description": "",
- "name": "watercress",
- "plural_name": "watercress"
+ "name": "crescione",
+ "plural_name": "crescioni"
},
"iceberg": {
"aliases": [],
"description": "",
"name": "iceberg",
- "plural_name": "icebergs"
+ "plural_name": "iceberg"
},
"mashed potato": {
"aliases": [],
"description": "",
- "name": "mashed potato",
- "plural_name": "mashed potatoes"
+ "name": "purè di patate",
+ "plural_name": "purè di patate"
},
"horseradish": {
"aliases": [],
"description": "",
- "name": "horseradish",
- "plural_name": "horseradishes"
+ "name": "rafano",
+ "plural_name": "rafani"
},
"chard": {
"aliases": [],
"description": "",
- "name": "chard",
- "plural_name": "chards"
+ "name": "bietola",
+ "plural_name": "bietole"
},
"pimiento": {
"aliases": [],
"description": "",
"name": "pimiento",
- "plural_name": "pimientoes"
+ "plural_name": "pimienti"
},
"spaghetti squash": {
"aliases": [],
@@ -389,8 +389,8 @@
"turnip": {
"aliases": [],
"description": "",
- "name": "turnip",
- "plural_name": "turnips"
+ "name": "rapa",
+ "plural_name": "rape"
},
"thai chile pepper": {
"aliases": [],
@@ -443,8 +443,8 @@
"plantain": {
"aliases": [],
"description": "",
- "name": "plantain",
- "plural_name": "plantains"
+ "name": "platano",
+ "plural_name": "platani"
},
"leaf lettuce": {
"aliases": [],
diff --git a/mealie/repos/seed/resources/foods/locales/pl-PL.json b/mealie/repos/seed/resources/foods/locales/pl-PL.json
index adea92e26..a70583aa8 100644
--- a/mealie/repos/seed/resources/foods/locales/pl-PL.json
+++ b/mealie/repos/seed/resources/foods/locales/pl-PL.json
@@ -310,7 +310,7 @@
"aliases": [],
"description": "",
"name": "green tomato",
- "plural_name": "green tomatoes"
+ "plural_name": "zielone pomidory"
},
"watercress": {
"aliases": [],
@@ -357,8 +357,8 @@
"butter lettuce": {
"aliases": [],
"description": "",
- "name": "butter lettuce",
- "plural_name": "butter lettuces"
+ "name": "sałata masłowa",
+ "plural_name": "sałaty masłowe"
},
"hash brown": {
"aliases": [],
@@ -389,8 +389,8 @@
"turnip": {
"aliases": [],
"description": "",
- "name": "turnip",
- "plural_name": "turnips"
+ "name": "rzepa",
+ "plural_name": "rzepy"
},
"thai chile pepper": {
"aliases": [],
@@ -851,8 +851,8 @@
"dried fruit": {
"aliases": [],
"description": "",
- "name": "dried fruit",
- "plural_name": "dried fruits"
+ "name": "Suszony owoc",
+ "plural_name": "Suszone owoce"
},
"clementine": {
"aliases": [],
@@ -881,14 +881,14 @@
"dried mango": {
"aliases": [],
"description": "",
- "name": "dried mango",
- "plural_name": "dried mangoes"
+ "name": "Suszone mango",
+ "plural_name": "Suszone mango"
},
"dried apple": {
"aliases": [],
"description": "",
- "name": "dried apple",
- "plural_name": "dried apples"
+ "name": "Suszone jabłko",
+ "plural_name": "Suszone jabłka"
},
"quince": {
"aliases": [],
@@ -912,7 +912,7 @@
"aliases": [],
"description": "",
"name": "kumquat",
- "plural_name": "kumquats"
+ "plural_name": "kumkwat"
},
"jackfruit": {
"aliases": [],
@@ -923,8 +923,8 @@
"dragon fruit": {
"aliases": [],
"description": "",
- "name": "dragon fruit",
- "plural_name": "dragon fruits"
+ "name": "smoczy owoc",
+ "plural_name": "smocze owoce"
},
"mixed fruit": {
"aliases": [],
@@ -959,20 +959,20 @@
"star fruit": {
"aliases": [],
"description": "",
- "name": "star fruit",
- "plural_name": "star fruits"
+ "name": "karambola",
+ "plural_name": "karambole"
},
"green papaya": {
"aliases": [],
"description": "",
- "name": "green papaya",
- "plural_name": "green papayas"
+ "name": "zielona papaja",
+ "plural_name": "zielone papaje"
},
"pomelo": {
"aliases": [],
"description": "",
"name": "pomelo",
- "plural_name": "pomeloes"
+ "plural_name": "pomelo"
},
"chestnut puree": {
"aliases": [],
@@ -1055,8 +1055,8 @@
"dried lemon": {
"aliases": [],
"description": "",
- "name": "dried lemon",
- "plural_name": "dried lemons"
+ "name": "Suszona cytryna",
+ "plural_name": "Suszone cytryny"
},
"young jackfruit": {
"aliases": [],
@@ -1068,7 +1068,7 @@
"aliases": [],
"description": "",
"name": "durian",
- "plural_name": "durians"
+ "plural_name": "duriany"
},
"freeze-dried apple": {
"aliases": [],
@@ -1245,8 +1245,8 @@
"shiitake mushroom": {
"aliases": [],
"description": "",
- "name": "shiitake mushroom",
- "plural_name": "shiitake mushrooms"
+ "name": "Grzyb Shiitake",
+ "plural_name": "Grzyby Shiitake"
},
"portobello mushroom": {
"aliases": [],
@@ -1299,8 +1299,8 @@
"black truffle": {
"aliases": [],
"description": "",
- "name": "black truffle",
- "plural_name": "black truffles"
+ "name": "czarna trufla",
+ "plural_name": "czarne trufle"
},
"morel mushroom": {
"aliases": [],
@@ -1353,8 +1353,8 @@
"white truffle": {
"aliases": [],
"description": "",
- "name": "white truffle",
- "plural_name": "white truffles"
+ "name": "biała trufla",
+ "plural_name": "białe trufle"
},
"white fungu": {
"aliases": [],
@@ -1453,26 +1453,26 @@
"strawberry": {
"aliases": [],
"description": "",
- "name": "strawberry",
- "plural_name": "strawberries"
+ "name": "truskawka",
+ "plural_name": "truskawki"
},
"blueberry": {
"aliases": [],
"description": "",
- "name": "blueberry",
- "plural_name": "blueberries"
+ "name": "borówka",
+ "plural_name": "borówki"
},
"raspberry": {
"aliases": [],
"description": "",
- "name": "raspberry",
- "plural_name": "raspberries"
+ "name": "malina",
+ "plural_name": "maliny"
},
"cranberry": {
"aliases": [],
"description": "",
- "name": "cranberry",
- "plural_name": "cranberries"
+ "name": "żurawina",
+ "plural_name": "żurawiny"
},
"cherry": {
"aliases": [],
@@ -1483,8 +1483,8 @@
"blackberry": {
"aliases": [],
"description": "",
- "name": "blackberry",
- "plural_name": "blackberries"
+ "name": "jeżyna",
+ "plural_name": "jeżyny"
},
"berry mix": {
"aliases": [],
@@ -1519,14 +1519,14 @@
"goji berry": {
"aliases": [],
"description": "",
- "name": "goji berry",
- "plural_name": "goji berries"
+ "name": "jagoda goji",
+ "plural_name": "jagody goji"
},
"dried blueberry": {
"aliases": [],
"description": "",
- "name": "dried blueberry",
- "plural_name": "dried blueberries"
+ "name": "Suszona borówka",
+ "plural_name": "Suszone borówki"
},
"freeze-dried strawberry": {
"aliases": [],
@@ -1537,8 +1537,8 @@
"gooseberry": {
"aliases": [],
"description": "",
- "name": "gooseberry",
- "plural_name": "gooseberries"
+ "name": "agrest",
+ "plural_name": "agresty"
},
"freeze-dried raspberry": {
"aliases": [],
@@ -1561,14 +1561,14 @@
"mulberry": {
"aliases": [],
"description": "",
- "name": "mulberry",
- "plural_name": "mulberries"
+ "name": "morwa",
+ "plural_name": "morwy"
},
"acai berry": {
"aliases": [],
"description": "",
- "name": "acai berry",
- "plural_name": "acai berries"
+ "name": "jagoda acai",
+ "plural_name": "jagody acai"
},
"canned cherry": {
"aliases": [],
@@ -1585,8 +1585,8 @@
"elderberry": {
"aliases": [],
"description": "",
- "name": "elderberry",
- "plural_name": "elderberries"
+ "name": "czarny bez",
+ "plural_name": "czarny bez"
},
"freeze-dried blueberry": {
"aliases": [],
@@ -1663,8 +1663,8 @@
"aronia berry": {
"aliases": [],
"description": "",
- "name": "aronia berry",
- "plural_name": "aronia berries"
+ "name": "jagoda aronii",
+ "plural_name": "jagody aronii"
},
"chokeberry": {
"aliases": [],
@@ -5181,8 +5181,8 @@
"dried anchovy": {
"aliases": [],
"description": "",
- "name": "dried anchovy",
- "plural_name": "dried anchovies"
+ "name": "Suszona anszua",
+ "plural_name": "Suszone anszua"
},
"arctic char": {
"aliases": [],
@@ -5331,8 +5331,8 @@
"dried fish": {
"aliases": [],
"description": "",
- "name": "dried fish",
- "plural_name": "dried fish"
+ "name": "suszona ryba",
+ "plural_name": "suszone ryby"
},
"flathead": {
"aliases": [],
@@ -5587,8 +5587,8 @@
"dried shrimp": {
"aliases": [],
"description": "",
- "name": "dried shrimp",
- "plural_name": "dried shrimps"
+ "name": "suszona garnela",
+ "plural_name": "suszone garnele"
},
"bay scallop": {
"aliases": [],
@@ -5641,8 +5641,8 @@
"dried prawn": {
"aliases": [],
"description": "",
- "name": "dried prawn",
- "plural_name": "dried prawns"
+ "name": "suszona krewetka",
+ "plural_name": "suszone krewetki"
},
"dulse seaweed": {
"aliases": [],
@@ -6215,8 +6215,8 @@
"dried chili": {
"aliases": [],
"description": "",
- "name": "dried chili",
- "plural_name": "dried chilies"
+ "name": "suszone chili",
+ "plural_name": "suszone chili"
},
"black cardamom": {
"aliases": [],
@@ -9332,8 +9332,8 @@
"dried pea": {
"aliases": [],
"description": "",
- "name": "dried pea",
- "plural_name": "dried peas"
+ "name": "suszony groch",
+ "plural_name": "suszony groch"
},
"pink bean": {
"aliases": [],
diff --git a/mealie/routes/auth/auth.py b/mealie/routes/auth/auth.py
index 2e5b66174..593ebda89 100644
--- a/mealie/routes/auth/auth.py
+++ b/mealie/routes/auth/auth.py
@@ -19,6 +19,8 @@ from mealie.routes._base.routers import UserAPIRouter
from mealie.schema.user import PrivateUser
from mealie.schema.user.auth import CredentialsRequestForm
+from .auth_cache import AuthCache
+
public_router = APIRouter(tags=["Users: Authentication"])
user_router = UserAPIRouter(tags=["Users: Authentication"])
logger = root_logger.get_logger("auth")
@@ -27,7 +29,7 @@ remember_me_duration = timedelta(days=14)
settings = get_app_settings()
if settings.OIDC_READY:
- oauth = OAuth()
+ oauth = OAuth(cache=AuthCache())
scope = None
if settings.OIDC_SCOPES_OVERRIDE:
scope = settings.OIDC_SCOPES_OVERRIDE
diff --git a/mealie/routes/auth/auth_cache.py b/mealie/routes/auth/auth_cache.py
new file mode 100644
index 000000000..3c4e50d63
--- /dev/null
+++ b/mealie/routes/auth/auth_cache.py
@@ -0,0 +1,51 @@
+import time
+from typing import Any
+
+
+class AuthCache:
+ def __init__(self, threshold: int = 500, default_timeout: float = 300):
+ self.default_timeout = default_timeout
+ self._cache: dict[str, tuple[float, Any]] = {}
+ self.clear = self._cache.clear
+ self._threshold = threshold
+
+ def _prune(self):
+ if len(self._cache) > self._threshold:
+ now = time.time()
+ toremove = []
+ for idx, (key, (expires, _)) in enumerate(self._cache.items()):
+ if (expires != 0 and expires <= now) or idx % 3 == 0:
+ toremove.append(key)
+ for key in toremove:
+ self._cache.pop(key, None)
+
+ def _normalize_timeout(self, timeout: float | None) -> float:
+ if timeout is None:
+ timeout = self.default_timeout
+ if timeout > 0:
+ timeout = time.time() + timeout
+ return timeout
+
+ async def get(self, key: str) -> Any:
+ try:
+ expires, value = self._cache[key]
+ if expires == 0 or expires > time.time():
+ return value
+ except KeyError:
+ return None
+
+ async def set(self, key: str, value: Any, timeout: float | None = None) -> bool:
+ expires = self._normalize_timeout(timeout)
+ self._prune()
+ self._cache[key] = (expires, value)
+ return True
+
+ async def delete(self, key: str) -> bool:
+ return self._cache.pop(key, None) is not None
+
+ async def has(self, key: str) -> bool:
+ try:
+ expires, value = self._cache[key]
+ return expires == 0 or expires > time.time()
+ except KeyError:
+ return False
diff --git a/mealie/routes/explore/controller_public_cookbooks.py b/mealie/routes/explore/controller_public_cookbooks.py
index 902b27e12..836f7d953 100644
--- a/mealie/routes/explore/controller_public_cookbooks.py
+++ b/mealie/routes/explore/controller_public_cookbooks.py
@@ -5,7 +5,7 @@ from pydantic import UUID4
from mealie.routes._base import controller
from mealie.routes._base.base_controllers import BasePublicHouseholdExploreController
-from mealie.schema.cookbook.cookbook import ReadCookBook, RecipeCookBook
+from mealie.schema.cookbook.cookbook import ReadCookBook
from mealie.schema.make_dependable import make_dependable
from mealie.schema.response.pagination import PaginationBase, PaginationQuery
@@ -39,8 +39,8 @@ class PublicCookbooksController(BasePublicHouseholdExploreController):
response.set_pagination_guides(self.get_explore_url_path(router.url_path_for("get_all")), q.model_dump())
return response
- @router.get("/{item_id}", response_model=RecipeCookBook)
- def get_one(self, item_id: UUID4 | str) -> RecipeCookBook:
+ @router.get("/{item_id}", response_model=ReadCookBook)
+ def get_one(self, item_id: UUID4 | str) -> ReadCookBook:
NOT_FOUND_EXCEPTION = HTTPException(404, "cookbook not found")
if isinstance(item_id, UUID):
match_attr = "id"
@@ -58,13 +58,4 @@ class PublicCookbooksController(BasePublicHouseholdExploreController):
if not household or household.preferences.private_household:
raise NOT_FOUND_EXCEPTION
- cross_household_recipes = self.cross_household_repos.recipes
- recipes = cross_household_recipes.page_all(
- PaginationQuery(
- page=1,
- per_page=-1,
- query_filter="settings.public = TRUE AND household.preferences.privateHousehold = FALSE",
- ),
- cookbook=cookbook,
- )
- return cookbook.cast(RecipeCookBook, recipes=recipes.items)
+ return cookbook
diff --git a/mealie/routes/households/controller_cookbooks.py b/mealie/routes/households/controller_cookbooks.py
index 99f4c97df..2ed385c64 100644
--- a/mealie/routes/households/controller_cookbooks.py
+++ b/mealie/routes/households/controller_cookbooks.py
@@ -11,7 +11,7 @@ from mealie.routes._base import BaseCrudController, controller
from mealie.routes._base.mixins import HttpRepo
from mealie.routes._base.routers import MealieCrudRoute
from mealie.schema import mapper
-from mealie.schema.cookbook import CreateCookBook, ReadCookBook, RecipeCookBook, SaveCookBook, UpdateCookBook
+from mealie.schema.cookbook import CreateCookBook, ReadCookBook, SaveCookBook, UpdateCookBook
from mealie.schema.cookbook.cookbook import CookBookPagination
from mealie.schema.response.pagination import PaginationQuery
from mealie.services.event_bus_service.event_types import (
@@ -101,7 +101,7 @@ class GroupCookbookController(BaseCrudController):
return all_updated
- @router.get("/{item_id}", response_model=RecipeCookBook)
+ @router.get("/{item_id}", response_model=ReadCookBook)
def get_one(self, item_id: UUID4 | str):
if isinstance(item_id, UUID):
match_attr = "id"
@@ -114,12 +114,10 @@ class GroupCookbookController(BaseCrudController):
# Allow fetching other households' cookbooks
cookbook = self.group_cookbooks.get_one(item_id, match_attr)
-
if cookbook is None:
raise HTTPException(status_code=404)
- recipe_pagination = self.repos.recipes.page_all(PaginationQuery(page=1, per_page=-1, cookbook=cookbook))
- return cookbook.cast(RecipeCookBook, recipes=recipe_pagination.items)
+ return cookbook
@router.put("/{item_id}", response_model=ReadCookBook)
def update_one(self, item_id: str, data: CreateCookBook):
diff --git a/mealie/schema/_mealie/__init__.py b/mealie/schema/_mealie/__init__.py
index 7441e747a..628e17908 100644
--- a/mealie/schema/_mealie/__init__.py
+++ b/mealie/schema/_mealie/__init__.py
@@ -3,11 +3,11 @@ from .datetime_parse import DateError, DateTimeError, DurationError, TimeError
from .mealie_model import HasUUID, MealieModel, SearchType
__all__ = [
- "HasUUID",
- "MealieModel",
- "SearchType",
"DateError",
"DateTimeError",
"DurationError",
"TimeError",
+ "HasUUID",
+ "MealieModel",
+ "SearchType",
]
diff --git a/mealie/schema/admin/__init__.py b/mealie/schema/admin/__init__.py
index 92edafd25..367e94739 100644
--- a/mealie/schema/admin/__init__.py
+++ b/mealie/schema/admin/__init__.py
@@ -18,10 +18,28 @@ from .restore import (
from .settings import CustomPageBase, CustomPageOut
__all__ = [
+ "MaintenanceLogs",
+ "MaintenanceStorageDetails",
+ "MaintenanceSummary",
"ChowdownURL",
"MigrationFile",
"MigrationImport",
"Migrations",
+ "CustomPageBase",
+ "CustomPageOut",
+ "CommentImport",
+ "CustomPageImport",
+ "GroupImport",
+ "ImportBase",
+ "NotificationImport",
+ "RecipeImport",
+ "SettingsImport",
+ "UserImport",
+ "AllBackups",
+ "BackupFile",
+ "BackupOptions",
+ "CreateBackup",
+ "ImportJob",
"AdminAboutInfo",
"AppInfo",
"AppStartupInfo",
@@ -31,23 +49,5 @@ __all__ = [
"EmailReady",
"EmailSuccess",
"EmailTest",
- "CustomPageBase",
- "CustomPageOut",
- "AllBackups",
- "BackupFile",
- "BackupOptions",
- "CreateBackup",
- "ImportJob",
- "MaintenanceLogs",
- "MaintenanceStorageDetails",
- "MaintenanceSummary",
"DebugResponse",
- "CommentImport",
- "CustomPageImport",
- "GroupImport",
- "ImportBase",
- "NotificationImport",
- "RecipeImport",
- "SettingsImport",
- "UserImport",
]
diff --git a/mealie/schema/cookbook/__init__.py b/mealie/schema/cookbook/__init__.py
index 9559f84f5..8a358571a 100644
--- a/mealie/schema/cookbook/__init__.py
+++ b/mealie/schema/cookbook/__init__.py
@@ -1,11 +1,10 @@
# This file is auto-generated by gen_schema_exports.py
-from .cookbook import CookBookPagination, CreateCookBook, ReadCookBook, RecipeCookBook, SaveCookBook, UpdateCookBook
+from .cookbook import CookBookPagination, CreateCookBook, ReadCookBook, SaveCookBook, UpdateCookBook
__all__ = [
"CookBookPagination",
"CreateCookBook",
"ReadCookBook",
- "RecipeCookBook",
"SaveCookBook",
"UpdateCookBook",
]
diff --git a/mealie/schema/cookbook/cookbook.py b/mealie/schema/cookbook/cookbook.py
index 509f3dc05..66ba3b668 100644
--- a/mealie/schema/cookbook/cookbook.py
+++ b/mealie/schema/cookbook/cookbook.py
@@ -7,7 +7,6 @@ from slugify import slugify
from mealie.core.root_logger import get_logger
from mealie.db.models.recipe import RecipeModel
from mealie.schema._mealie import MealieModel
-from mealie.schema.recipe.recipe import RecipeSummary
from mealie.schema.response.pagination import PaginationBase
from mealie.schema.response.query_filter import QueryFilterBuilder, QueryFilterJSON
@@ -84,10 +83,3 @@ class ReadCookBook(UpdateCookBook):
class CookBookPagination(PaginationBase):
items: list[ReadCookBook]
-
-
-class RecipeCookBook(ReadCookBook):
- group_id: UUID4
- household_id: UUID4
- recipes: list[RecipeSummary]
- model_config = ConfigDict(from_attributes=True)
diff --git a/mealie/schema/group/__init__.py b/mealie/schema/group/__init__.py
index 3e0fb46fb..a731b4432 100644
--- a/mealie/schema/group/__init__.py
+++ b/mealie/schema/group/__init__.py
@@ -7,13 +7,13 @@ from .group_seeder import SeederConfig
from .group_statistics import GroupStorage
__all__ = [
- "GroupAdminUpdate",
- "GroupStorage",
"GroupDataExport",
- "SeederConfig",
"CreateGroupPreferences",
"ReadGroupPreferences",
"UpdateGroupPreferences",
+ "GroupStorage",
"DataMigrationCreate",
"SupportedMigrations",
+ "SeederConfig",
+ "GroupAdminUpdate",
]
diff --git a/mealie/schema/household/__init__.py b/mealie/schema/household/__init__.py
index f0e2f88d9..1fcc7bb25 100644
--- a/mealie/schema/household/__init__.py
+++ b/mealie/schema/household/__init__.py
@@ -70,6 +70,49 @@ from .invite_token import CreateInviteToken, EmailInitationResponse, EmailInvita
from .webhook import CreateWebhook, ReadWebhook, SaveWebhook, WebhookPagination, WebhookType
__all__ = [
+ "GroupEventNotifierCreate",
+ "GroupEventNotifierOptions",
+ "GroupEventNotifierOptionsOut",
+ "GroupEventNotifierOptionsSave",
+ "GroupEventNotifierOut",
+ "GroupEventNotifierPrivate",
+ "GroupEventNotifierSave",
+ "GroupEventNotifierUpdate",
+ "GroupEventPagination",
+ "CreateGroupRecipeAction",
+ "GroupRecipeActionOut",
+ "GroupRecipeActionPagination",
+ "GroupRecipeActionPayload",
+ "GroupRecipeActionType",
+ "SaveGroupRecipeAction",
+ "CreateWebhook",
+ "ReadWebhook",
+ "SaveWebhook",
+ "WebhookPagination",
+ "WebhookType",
+ "CreateHouseholdPreferences",
+ "ReadHouseholdPreferences",
+ "SaveHouseholdPreferences",
+ "UpdateHouseholdPreferences",
+ "HouseholdCreate",
+ "HouseholdInDB",
+ "HouseholdPagination",
+ "HouseholdRecipeBase",
+ "HouseholdRecipeCreate",
+ "HouseholdRecipeOut",
+ "HouseholdRecipeSummary",
+ "HouseholdRecipeUpdate",
+ "HouseholdSave",
+ "HouseholdSummary",
+ "HouseholdUserSummary",
+ "UpdateHousehold",
+ "UpdateHouseholdAdmin",
+ "HouseholdStatistics",
+ "CreateInviteToken",
+ "EmailInitationResponse",
+ "EmailInvitation",
+ "ReadInviteToken",
+ "SaveInviteToken",
"ShoppingListAddRecipeParams",
"ShoppingListAddRecipeParamsBulk",
"ShoppingListCreate",
@@ -93,48 +136,5 @@ __all__ = [
"ShoppingListSave",
"ShoppingListSummary",
"ShoppingListUpdate",
- "GroupEventNotifierCreate",
- "GroupEventNotifierOptions",
- "GroupEventNotifierOptionsOut",
- "GroupEventNotifierOptionsSave",
- "GroupEventNotifierOut",
- "GroupEventNotifierPrivate",
- "GroupEventNotifierSave",
- "GroupEventNotifierUpdate",
- "GroupEventPagination",
- "CreateGroupRecipeAction",
- "GroupRecipeActionOut",
- "GroupRecipeActionPagination",
- "GroupRecipeActionPayload",
- "GroupRecipeActionType",
- "SaveGroupRecipeAction",
- "CreateHouseholdPreferences",
- "ReadHouseholdPreferences",
- "SaveHouseholdPreferences",
- "UpdateHouseholdPreferences",
"SetPermissions",
- "CreateInviteToken",
- "EmailInitationResponse",
- "EmailInvitation",
- "ReadInviteToken",
- "SaveInviteToken",
- "HouseholdStatistics",
- "CreateWebhook",
- "ReadWebhook",
- "SaveWebhook",
- "WebhookPagination",
- "WebhookType",
- "HouseholdCreate",
- "HouseholdInDB",
- "HouseholdPagination",
- "HouseholdRecipeBase",
- "HouseholdRecipeCreate",
- "HouseholdRecipeOut",
- "HouseholdRecipeSummary",
- "HouseholdRecipeUpdate",
- "HouseholdSave",
- "HouseholdSummary",
- "HouseholdUserSummary",
- "UpdateHousehold",
- "UpdateHouseholdAdmin",
]
diff --git a/mealie/schema/meal_plan/__init__.py b/mealie/schema/meal_plan/__init__.py
index 5f3b9b033..639c61ee6 100644
--- a/mealie/schema/meal_plan/__init__.py
+++ b/mealie/schema/meal_plan/__init__.py
@@ -12,6 +12,9 @@ from .plan_rules import PlanRulesCreate, PlanRulesDay, PlanRulesOut, PlanRulesPa
from .shopping_list import ListItem, ShoppingListIn, ShoppingListOut
__all__ = [
+ "ListItem",
+ "ShoppingListIn",
+ "ShoppingListOut",
"CreatePlanEntry",
"CreateRandomEntry",
"PlanEntryPagination",
@@ -19,9 +22,6 @@ __all__ = [
"ReadPlanEntry",
"SavePlanEntry",
"UpdatePlanEntry",
- "ListItem",
- "ShoppingListIn",
- "ShoppingListOut",
"PlanRulesCreate",
"PlanRulesDay",
"PlanRulesOut",
diff --git a/mealie/schema/recipe/__init__.py b/mealie/schema/recipe/__init__.py
index d105db300..2304c8a5e 100644
--- a/mealie/schema/recipe/__init__.py
+++ b/mealie/schema/recipe/__init__.py
@@ -89,6 +89,35 @@ from .recipe_tool import RecipeToolCreate, RecipeToolOut, RecipeToolResponse, Re
from .request_helpers import RecipeDuplicate, RecipeSlug, RecipeZipTokenResponse, SlugResponse, UpdateImageResponse
__all__ = [
+ "IngredientReferences",
+ "RecipeStep",
+ "RecipeNote",
+ "CategoryBase",
+ "CategoryIn",
+ "CategoryOut",
+ "CategorySave",
+ "RecipeCategoryResponse",
+ "RecipeTagResponse",
+ "TagBase",
+ "TagIn",
+ "TagOut",
+ "TagSave",
+ "RecipeAsset",
+ "RecipeTimelineEventCreate",
+ "RecipeTimelineEventIn",
+ "RecipeTimelineEventOut",
+ "RecipeTimelineEventPagination",
+ "RecipeTimelineEventUpdate",
+ "TimelineEventImage",
+ "TimelineEventType",
+ "RecipeSuggestionQuery",
+ "RecipeSuggestionResponse",
+ "RecipeSuggestionResponseItem",
+ "Nutrition",
+ "RecipeShareToken",
+ "RecipeShareTokenCreate",
+ "RecipeShareTokenSave",
+ "RecipeShareTokenSummary",
"CreateIngredientFood",
"CreateIngredientFoodAlias",
"CreateIngredientUnit",
@@ -111,27 +140,13 @@ __all__ = [
"SaveIngredientFood",
"SaveIngredientUnit",
"UnitFoodBase",
- "RecipeTimelineEventCreate",
- "RecipeTimelineEventIn",
- "RecipeTimelineEventOut",
- "RecipeTimelineEventPagination",
- "RecipeTimelineEventUpdate",
- "TimelineEventImage",
- "TimelineEventType",
- "Nutrition",
- "AssignCategories",
- "AssignSettings",
- "AssignTags",
- "DeleteRecipes",
- "ExportBase",
- "ExportRecipes",
- "ExportTypes",
"RecipeCommentCreate",
"RecipeCommentOut",
"RecipeCommentPagination",
"RecipeCommentSave",
"RecipeCommentUpdate",
"UserBase",
+ "RecipeSettings",
"CreateRecipe",
"CreateRecipeBulk",
"CreateRecipeByUrlBulk",
@@ -145,40 +160,25 @@ __all__ = [
"RecipeTagPagination",
"RecipeTool",
"RecipeToolPagination",
- "IngredientReferences",
- "RecipeStep",
- "RecipeNote",
- "RecipeSuggestionQuery",
- "RecipeSuggestionResponse",
- "RecipeSuggestionResponseItem",
- "RecipeSettings",
- "RecipeShareToken",
- "RecipeShareTokenCreate",
- "RecipeShareTokenSave",
- "RecipeShareTokenSummary",
- "RecipeAsset",
+ "ScrapeRecipe",
+ "ScrapeRecipeBase",
+ "ScrapeRecipeData",
+ "ScrapeRecipeTest",
+ "AssignCategories",
+ "AssignSettings",
+ "AssignTags",
+ "DeleteRecipes",
+ "ExportBase",
+ "ExportRecipes",
+ "ExportTypes",
+ "RecipeToolCreate",
+ "RecipeToolOut",
+ "RecipeToolResponse",
+ "RecipeToolSave",
+ "RecipeImageTypes",
"RecipeDuplicate",
"RecipeSlug",
"RecipeZipTokenResponse",
"SlugResponse",
"UpdateImageResponse",
- "RecipeToolCreate",
- "RecipeToolOut",
- "RecipeToolResponse",
- "RecipeToolSave",
- "CategoryBase",
- "CategoryIn",
- "CategoryOut",
- "CategorySave",
- "RecipeCategoryResponse",
- "RecipeTagResponse",
- "TagBase",
- "TagIn",
- "TagOut",
- "TagSave",
- "ScrapeRecipe",
- "ScrapeRecipeBase",
- "ScrapeRecipeData",
- "ScrapeRecipeTest",
- "RecipeImageTypes",
]
diff --git a/mealie/schema/response/__init__.py b/mealie/schema/response/__init__.py
index fad4f840f..c513794c5 100644
--- a/mealie/schema/response/__init__.py
+++ b/mealie/schema/response/__init__.py
@@ -28,14 +28,14 @@ __all__ = [
"QueryFilterJSONPart",
"RelationalKeyword",
"RelationalOperator",
- "SearchFilter",
+ "ValidationResponse",
"OrderByNullPosition",
"OrderDirection",
"PaginationBase",
"PaginationQuery",
"RecipeSearchQuery",
"RequestQuery",
- "ValidationResponse",
+ "SearchFilter",
"ErrorResponse",
"FileTokenResponse",
"SuccessResponse",
diff --git a/mealie/schema/user/__init__.py b/mealie/schema/user/__init__.py
index 65e7e00a6..76db2ec95 100644
--- a/mealie/schema/user/__init__.py
+++ b/mealie/schema/user/__init__.py
@@ -38,6 +38,12 @@ from .user_passwords import (
)
__all__ = [
+ "ForgotPassword",
+ "PasswordResetToken",
+ "PrivatePasswordResetToken",
+ "ResetPassword",
+ "SavePasswordResetToken",
+ "ValidateResetToken",
"CredentialsRequest",
"CredentialsRequestForm",
"Token",
@@ -69,10 +75,4 @@ __all__ = [
"UserRatings",
"UserSummary",
"UserSummaryPagination",
- "ForgotPassword",
- "PasswordResetToken",
- "PrivatePasswordResetToken",
- "ResetPassword",
- "SavePasswordResetToken",
- "ValidateResetToken",
]
diff --git a/poetry.lock b/poetry.lock
index 3bedc5ae7..451d7c02c 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1082,14 +1082,14 @@ files = [
[[package]]
name = "ingredient-parser-nlp"
-version = "2.1.1"
+version = "2.2.0"
description = "A Python package to parse structured information from recipe ingredient sentences"
optional = false
python-versions = "<3.14,>=3.10"
groups = ["main"]
files = [
- {file = "ingredient_parser_nlp-2.1.1-py3-none-any.whl", hash = "sha256:e09f73b28d2805d33f5523b84a304225bf62ebdca47560aa8e6c7176442ef6b6"},
- {file = "ingredient_parser_nlp-2.1.1.tar.gz", hash = "sha256:e7070b849d05395006b6a27923304b01ed4da04b53a82047c5ddddf1bbfe1fc7"},
+ {file = "ingredient_parser_nlp-2.2.0-py3-none-any.whl", hash = "sha256:b9be48c0a27eb972f8cfdc0f755cfa32fd16a598ff714b8f1a3b244c8622bed0"},
+ {file = "ingredient_parser_nlp-2.2.0.tar.gz", hash = "sha256:cabd12bd01a030b19f1859a968219c7f2cd68dd08d03484194245f7454d85d20"},
]
[package.dependencies]
@@ -1612,19 +1612,20 @@ pyyaml = ">=5.1"
[[package]]
name = "mkdocs-material"
-version = "9.6.16"
+version = "9.6.17"
description = "Documentation that simply works"
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
- {file = "mkdocs_material-9.6.16-py3-none-any.whl", hash = "sha256:8d1a1282b892fe1fdf77bfeb08c485ba3909dd743c9ba69a19a40f637c6ec18c"},
- {file = "mkdocs_material-9.6.16.tar.gz", hash = "sha256:d07011df4a5c02ee0877496d9f1bfc986cfb93d964799b032dd99fe34c0e9d19"},
+ {file = "mkdocs_material-9.6.17-py3-none-any.whl", hash = "sha256:221dd8b37a63f52e580bcab4a7e0290e4a6f59bd66190be9c3d40767e05f9417"},
+ {file = "mkdocs_material-9.6.17.tar.gz", hash = "sha256:48ae7aec72a3f9f501a70be3fbd329c96ff5f5a385b67a1563e5ed5ce064affe"},
]
[package.dependencies]
babel = ">=2.10,<3.0"
backrefs = ">=5.7.post1,<6.0"
+click = "<8.2.2"
colorama = ">=0.4,<1.0"
jinja2 = ">=3.1,<4.0"
markdown = ">=3.2,<4.0"
@@ -1877,95 +1878,95 @@ voice-helpers = ["numpy (>=2.0.2)", "sounddevice (>=0.5.1)"]
[[package]]
name = "orjson"
-version = "3.11.1"
-description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy"
+version = "3.11.2"
+description = ""
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
- {file = "orjson-3.11.1-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:92d771c492b64119456afb50f2dff3e03a2db8b5af0eba32c5932d306f970532"},
- {file = "orjson-3.11.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0085ef83a4141c2ed23bfec5fecbfdb1e95dd42fc8e8c76057bdeeec1608ea65"},
- {file = "orjson-3.11.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5caf7f13f2e1b4e137060aed892d4541d07dabc3f29e6d891e2383c7ed483440"},
- {file = "orjson-3.11.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f716bcc166524eddfcf9f13f8209ac19a7f27b05cf591e883419079d98c8c99d"},
- {file = "orjson-3.11.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:507d6012fab05465d8bf21f5d7f4635ba4b6d60132874e349beff12fb51af7fe"},
- {file = "orjson-3.11.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b1545083b0931f754c80fd2422a73d83bea7a6d1b6de104a5f2c8dd3d64c291e"},
- {file = "orjson-3.11.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e217ce3bad76351e1eb29ebe5ca630326f45cd2141f62620107a229909501a3"},
- {file = "orjson-3.11.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06ef26e009304bda4df42e4afe518994cde6f89b4b04c0ff24021064f83f4fbb"},
- {file = "orjson-3.11.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:ba49683b87bea3ae1489a88e766e767d4f423a669a61270b6d6a7ead1c33bd65"},
- {file = "orjson-3.11.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5072488fcc5cbcda2ece966d248e43ea1d222e19dd4c56d3f82747777f24d864"},
- {file = "orjson-3.11.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f58ae2bcd119226fe4aa934b5880fe57b8e97b69e51d5d91c88a89477a307016"},
- {file = "orjson-3.11.1-cp310-cp310-win32.whl", hash = "sha256:6723be919c07906781b9c63cc52dc7d2fb101336c99dd7e85d3531d73fb493f7"},
- {file = "orjson-3.11.1-cp310-cp310-win_amd64.whl", hash = "sha256:5fd44d69ddfdfb4e8d0d83f09d27a4db34930fba153fbf79f8d4ae8b47914e04"},
- {file = "orjson-3.11.1-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:15e2a57ce3b57c1a36acffcc02e823afefceee0a532180c2568c62213c98e3ef"},
- {file = "orjson-3.11.1-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:17040a83ecaa130474af05bbb59a13cfeb2157d76385556041f945da936b1afd"},
- {file = "orjson-3.11.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a68f23f09e5626cc0867a96cf618f68b91acb4753d33a80bf16111fd7f9928c"},
- {file = "orjson-3.11.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47e07528bb6ccbd6e32a55e330979048b59bfc5518b47c89bc7ab9e3de15174a"},
- {file = "orjson-3.11.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3807cce72bf40a9d251d689cbec28d2efd27e0f6673709f948f971afd52cb09"},
- {file = "orjson-3.11.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b2dc7e88da4ca201c940f5e6127998d9e89aa64264292334dad62854bc7fc27"},
- {file = "orjson-3.11.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3091dad33ac9e67c0a550cfff8ad5be156e2614d6f5d2a9247df0627751a1495"},
- {file = "orjson-3.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ed0fce2307843b79a0c83de49f65b86197f1e2310de07af9db2a1a77a61ce4c"},
- {file = "orjson-3.11.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5a31e84782a18c30abd56774c0cfa7b9884589f4d37d9acabfa0504dad59bb9d"},
- {file = "orjson-3.11.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:26b6c821abf1ae515fbb8e140a2406c9f9004f3e52acb780b3dee9bfffddbd84"},
- {file = "orjson-3.11.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f857b3d134b36a8436f1e24dcb525b6b945108b30746c1b0b556200b5cb76d39"},
- {file = "orjson-3.11.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:df146f2a14116ce80f7da669785fcb411406d8e80136558b0ecda4c924b9ac55"},
- {file = "orjson-3.11.1-cp311-cp311-win32.whl", hash = "sha256:d777c57c1f86855fe5492b973f1012be776e0398571f7cc3970e9a58ecf4dc17"},
- {file = "orjson-3.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:e9a5fd589951f02ec2fcb8d69339258bbf74b41b104c556e6d4420ea5e059313"},
- {file = "orjson-3.11.1-cp311-cp311-win_arm64.whl", hash = "sha256:4cddbe41ee04fddad35d75b9cf3e3736ad0b80588280766156b94783167777af"},
- {file = "orjson-3.11.1-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:2b7c8be96db3a977367250c6367793a3c5851a6ca4263f92f0b48d00702f9910"},
- {file = "orjson-3.11.1-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:72e18088f567bd4a45db5e3196677d9ed1605e356e500c8e32dd6e303167a13d"},
- {file = "orjson-3.11.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d346e2ae1ce17888f7040b65a5a4a0c9734cb20ffbd228728661e020b4c8b3a5"},
- {file = "orjson-3.11.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4bda5426ebb02ceb806a7d7ec9ba9ee5e0c93fca62375151a7b1c00bc634d06b"},
- {file = "orjson-3.11.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10506cebe908542c4f024861102673db534fd2e03eb9b95b30d94438fa220abf"},
- {file = "orjson-3.11.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:45202ee3f5494644e064c41abd1320497fb92fd31fc73af708708af664ac3b56"},
- {file = "orjson-3.11.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5adaf01b92e0402a9ac5c3ebe04effe2bbb115f0914a0a53d34ea239a746289"},
- {file = "orjson-3.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6162a1a757a1f1f4a94bc6ffac834a3602e04ad5db022dd8395a54ed9dd51c81"},
- {file = "orjson-3.11.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:78404206977c9f946613d3f916727c189d43193e708d760ea5d4b2087d6b0968"},
- {file = "orjson-3.11.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:db48f8e81072e26df6cdb0e9fff808c28597c6ac20a13d595756cf9ba1fed48a"},
- {file = "orjson-3.11.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0c1e394e67ced6bb16fea7054d99fbdd99a539cf4d446d40378d4c06e0a8548d"},
- {file = "orjson-3.11.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e7a840752c93d4eecd1378e9bb465c3703e127b58f675cd5c620f361b6cf57a4"},
- {file = "orjson-3.11.1-cp312-cp312-win32.whl", hash = "sha256:4537b0e09f45d2b74cb69c7f39ca1e62c24c0488d6bf01cd24673c74cd9596bf"},
- {file = "orjson-3.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:dbee6b050062540ae404530cacec1bf25e56e8d87d8d9b610b935afeb6725cae"},
- {file = "orjson-3.11.1-cp312-cp312-win_arm64.whl", hash = "sha256:f55e557d4248322d87c4673e085c7634039ff04b47bfc823b87149ae12bef60d"},
- {file = "orjson-3.11.1-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:53cfefe4af059e65aabe9683f76b9c88bf34b4341a77d329227c2424e0e59b0e"},
- {file = "orjson-3.11.1-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:93d5abed5a6f9e1b6f9b5bf6ed4423c11932b5447c2f7281d3b64e0f26c6d064"},
- {file = "orjson-3.11.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dbf06642f3db2966df504944cdd0eb68ca2717f0353bb20b20acd78109374a6"},
- {file = "orjson-3.11.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dddf4e78747fa7f2188273f84562017a3c4f0824485b78372513c1681ea7a894"},
- {file = "orjson-3.11.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fa3fe8653c9f57f0e16f008e43626485b6723b84b2f741f54d1258095b655912"},
- {file = "orjson-3.11.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6334d2382aff975a61f6f4d1c3daf39368b887c7de08f7c16c58f485dcf7adb2"},
- {file = "orjson-3.11.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a3d0855b643f259ee0cb76fe3df4c04483354409a520a902b067c674842eb6b8"},
- {file = "orjson-3.11.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0eacdfeefd0a79987926476eb16e0245546bedeb8febbbbcf4b653e79257a8e4"},
- {file = "orjson-3.11.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0ed07faf9e4873518c60480325dcbc16d17c59a165532cccfb409b4cdbaeff24"},
- {file = "orjson-3.11.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:d6d308dd578ae3658f62bb9eba54801533225823cd3248c902be1ebc79b5e014"},
- {file = "orjson-3.11.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c4aa13ca959ba6b15c0a98d3d204b850f9dc36c08c9ce422ffb024eb30d6e058"},
- {file = "orjson-3.11.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:be3d0653322abc9b68e5bcdaee6cfd58fcbe9973740ab222b87f4d687232ab1f"},
- {file = "orjson-3.11.1-cp313-cp313-win32.whl", hash = "sha256:4dd34e7e2518de8d7834268846f8cab7204364f427c56fb2251e098da86f5092"},
- {file = "orjson-3.11.1-cp313-cp313-win_amd64.whl", hash = "sha256:d6895d32032b6362540e6d0694b19130bb4f2ad04694002dce7d8af588ca5f77"},
- {file = "orjson-3.11.1-cp313-cp313-win_arm64.whl", hash = "sha256:bb7c36d5d3570fcbb01d24fa447a21a7fe5a41141fd88e78f7994053cc4e28f4"},
- {file = "orjson-3.11.1-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:7b71ef394327b3d0b39f6ea7ade2ecda2731a56c6a7cbf0d6a7301203b92a89b"},
- {file = "orjson-3.11.1-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:77c0fe28ed659b62273995244ae2aa430e432c71f86e4573ab16caa2f2e3ca5e"},
- {file = "orjson-3.11.1-cp314-cp314-manylinux_2_34_aarch64.whl", hash = "sha256:1495692f1f1ba2467df429343388a0ed259382835922e124c0cfdd56b3d1f727"},
- {file = "orjson-3.11.1-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:08c6a762fca63ca4dc04f66c48ea5d2428db55839fec996890e1bfaf057b658c"},
- {file = "orjson-3.11.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9e26794fe3976810b2c01fda29bd9ac7c91a3c1284b29cc9a383989f7b614037"},
- {file = "orjson-3.11.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:4b4b4f8f0b1d3ef8dc73e55363a0ffe012a42f4e2f1a140bf559698dca39b3fa"},
- {file = "orjson-3.11.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:848be553ea35aa89bfefbed2e27c8a41244c862956ab8ba00dc0b27e84fd58de"},
- {file = "orjson-3.11.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c964c29711a4b1df52f8d9966f015402a6cf87753a406c1c4405c407dd66fd45"},
- {file = "orjson-3.11.1-cp314-cp314-win32.whl", hash = "sha256:33aada2e6b6bc9c540d396528b91e666cedb383740fee6e6a917f561b390ecb1"},
- {file = "orjson-3.11.1-cp314-cp314-win_amd64.whl", hash = "sha256:68e10fd804e44e36188b9952543e3fa22f5aa8394da1b5283ca2b423735c06e8"},
- {file = "orjson-3.11.1-cp314-cp314-win_arm64.whl", hash = "sha256:f3cf6c07f8b32127d836be8e1c55d4f34843f7df346536da768e9f73f22078a1"},
- {file = "orjson-3.11.1-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:3d593a9e0bccf2c7401ae53625b519a7ad7aa555b1c82c0042b322762dc8af4e"},
- {file = "orjson-3.11.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0baad413c498fc1eef568504f11ea46bc71f94b845c075e437da1e2b85b4fb86"},
- {file = "orjson-3.11.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:22cf17ae1dae3f9b5f37bfcdba002ed22c98bbdb70306e42dc18d8cc9b50399a"},
- {file = "orjson-3.11.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e855c1e97208133ce88b3ef6663c9a82ddf1d09390cd0856a1638deee0390c3c"},
- {file = "orjson-3.11.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b5861c5f7acff10599132854c70ab10abf72aebf7c627ae13575e5f20b1ab8fe"},
- {file = "orjson-3.11.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b1e6415c5b5ff3a616a6dafad7b6ec303a9fc625e9313c8e1268fb1370a63dcb"},
- {file = "orjson-3.11.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:912579642f5d7a4a84d93c5eed8daf0aa34e1f2d3f4dc6571a8e418703f5701e"},
- {file = "orjson-3.11.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2092e1d3b33f64e129ff8271642afddc43763c81f2c30823b4a4a4a5f2ea5b55"},
- {file = "orjson-3.11.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:b8ac64caba1add2c04e9cd4782d4d0c4d6c554b7a3369bdec1eed7854c98db7b"},
- {file = "orjson-3.11.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:23196b826ebc85c43f8e27bee0ab33c5fb13a29ea47fb4fcd6ebb1e660eb0252"},
- {file = "orjson-3.11.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f2d3364cfad43003f1e3d564a069c8866237cca30f9c914b26ed2740b596ed00"},
- {file = "orjson-3.11.1-cp39-cp39-win32.whl", hash = "sha256:20b0dca94ea4ebe4628330de50975b35817a3f52954c1efb6d5d0498a3bbe581"},
- {file = "orjson-3.11.1-cp39-cp39-win_amd64.whl", hash = "sha256:200c3ad7ed8b5d31d49143265dfebd33420c4b61934ead16833b5cd2c3d241be"},
- {file = "orjson-3.11.1.tar.gz", hash = "sha256:48d82770a5fd88778063604c566f9c7c71820270c9cc9338d25147cbf34afd96"},
+ {file = "orjson-3.11.2-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:d6b8a78c33496230a60dc9487118c284c15ebdf6724386057239641e1eb69761"},
+ {file = "orjson-3.11.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc04036eeae11ad4180d1f7b5faddb5dab1dee49ecd147cd431523869514873b"},
+ {file = "orjson-3.11.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9c04325839c5754c253ff301cee8aaed7442d974860a44447bb3be785c411c27"},
+ {file = "orjson-3.11.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32769e04cd7fdc4a59854376211145a1bbbc0aea5e9d6c9755d3d3c301d7c0df"},
+ {file = "orjson-3.11.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0ff285d14917ea1408a821786e3677c5261fa6095277410409c694b8e7720ae0"},
+ {file = "orjson-3.11.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2662f908114864b63ff75ffe6ffacf996418dd6cc25e02a72ad4bda81b1ec45a"},
+ {file = "orjson-3.11.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab463cf5d08ad6623a4dac1badd20e88a5eb4b840050c4812c782e3149fe2334"},
+ {file = "orjson-3.11.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:64414241bde943cbf3c00d45fcb5223dca6d9210148ba984aae6b5d63294502b"},
+ {file = "orjson-3.11.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:7773e71c0ae8c9660192ff144a3d69df89725325e3d0b6a6bb2c50e5ebaf9b84"},
+ {file = "orjson-3.11.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:652ca14e283b13ece35bf3a86503c25592f294dbcfc5bb91b20a9c9a62a3d4be"},
+ {file = "orjson-3.11.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:26e99e98df8990ecfe3772bbdd7361f602149715c2cbc82e61af89bfad9528a4"},
+ {file = "orjson-3.11.2-cp310-cp310-win32.whl", hash = "sha256:5814313b3e75a2be7fe6c7958201c16c4560e21a813dbad25920752cecd6ad66"},
+ {file = "orjson-3.11.2-cp310-cp310-win_amd64.whl", hash = "sha256:dc471ce2225ab4c42ca672f70600d46a8b8e28e8d4e536088c1ccdb1d22b35ce"},
+ {file = "orjson-3.11.2-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:888b64ef7eaeeff63f773881929434a5834a6a140a63ad45183d59287f07fc6a"},
+ {file = "orjson-3.11.2-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:83387cc8b26c9fa0ae34d1ea8861a7ae6cff8fb3e346ab53e987d085315a728e"},
+ {file = "orjson-3.11.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7e35f003692c216d7ee901b6b916b5734d6fc4180fcaa44c52081f974c08e17"},
+ {file = "orjson-3.11.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4a0a4c29ae90b11d0c00bcc31533854d89f77bde2649ec602f512a7e16e00640"},
+ {file = "orjson-3.11.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:585d712b1880f68370108bc5534a257b561672d1592fae54938738fe7f6f1e33"},
+ {file = "orjson-3.11.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d08e342a7143f8a7c11f1c4033efe81acbd3c98c68ba1b26b96080396019701f"},
+ {file = "orjson-3.11.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29c0f84fc50398773a702732c87cd622737bf11c0721e6db3041ac7802a686fb"},
+ {file = "orjson-3.11.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:140f84e3c8d4c142575898c91e3981000afebf0333df753a90b3435d349a5fe5"},
+ {file = "orjson-3.11.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:96304a2b7235e0f3f2d9363ddccdbfb027d27338722fe469fe656832a017602e"},
+ {file = "orjson-3.11.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:3d7612bb227d5d9582f1f50a60bd55c64618fc22c4a32825d233a4f2771a428a"},
+ {file = "orjson-3.11.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a134587d18fe493befc2defffef2a8d27cfcada5696cb7234de54a21903ae89a"},
+ {file = "orjson-3.11.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0b84455e60c4bc12c1e4cbaa5cfc1acdc7775a9da9cec040e17232f4b05458bd"},
+ {file = "orjson-3.11.2-cp311-cp311-win32.whl", hash = "sha256:f0660efeac223f0731a70884e6914a5f04d613b5ae500744c43f7bf7b78f00f9"},
+ {file = "orjson-3.11.2-cp311-cp311-win_amd64.whl", hash = "sha256:955811c8405251d9e09cbe8606ad8fdef49a451bcf5520095a5ed38c669223d8"},
+ {file = "orjson-3.11.2-cp311-cp311-win_arm64.whl", hash = "sha256:2e4d423a6f838552e3a6d9ec734b729f61f88b1124fd697eab82805ea1a2a97d"},
+ {file = "orjson-3.11.2-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:901d80d349d8452162b3aa1afb82cec5bee79a10550660bc21311cc61a4c5486"},
+ {file = "orjson-3.11.2-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:cf3bd3967a360e87ee14ed82cb258b7f18c710dacf3822fb0042a14313a673a1"},
+ {file = "orjson-3.11.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26693dde66910078229a943e80eeb99fdce6cd2c26277dc80ead9f3ab97d2131"},
+ {file = "orjson-3.11.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4ad4c8acb50a28211c33fc7ef85ddf5cb18d4636a5205fd3fa2dce0411a0e30c"},
+ {file = "orjson-3.11.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:994181e7f1725bb5f2d481d7d228738e0743b16bf319ca85c29369c65913df14"},
+ {file = "orjson-3.11.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dbb79a0476393c07656b69c8e763c3cc925fa8e1d9e9b7d1f626901bb5025448"},
+ {file = "orjson-3.11.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:191ed27a1dddb305083d8716af413d7219f40ec1d4c9b0e977453b4db0d6fb6c"},
+ {file = "orjson-3.11.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0afb89f16f07220183fd00f5f297328ed0a68d8722ad1b0c8dcd95b12bc82804"},
+ {file = "orjson-3.11.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6ab6e6b4e93b1573a026b6ec16fca9541354dd58e514b62c558b58554ae04307"},
+ {file = "orjson-3.11.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:9cb23527efb61fb75527df55d20ee47989c4ee34e01a9c98ee9ede232abf6219"},
+ {file = "orjson-3.11.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a4dd1268e4035af21b8a09e4adf2e61f87ee7bf63b86d7bb0a237ac03fad5b45"},
+ {file = "orjson-3.11.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ff8b155b145eaf5a9d94d2c476fbe18d6021de93cf36c2ae2c8c5b775763f14e"},
+ {file = "orjson-3.11.2-cp312-cp312-win32.whl", hash = "sha256:ae3bb10279d57872f9aba68c9931aa71ed3b295fa880f25e68da79e79453f46e"},
+ {file = "orjson-3.11.2-cp312-cp312-win_amd64.whl", hash = "sha256:d026e1967239ec11a2559b4146a61d13914504b396f74510a1c4d6b19dfd8732"},
+ {file = "orjson-3.11.2-cp312-cp312-win_arm64.whl", hash = "sha256:59f8d5ad08602711af9589375be98477d70e1d102645430b5a7985fdbf613b36"},
+ {file = "orjson-3.11.2-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a079fdba7062ab396380eeedb589afb81dc6683f07f528a03b6f7aae420a0219"},
+ {file = "orjson-3.11.2-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:6a5f62ebbc530bb8bb4b1ead103647b395ba523559149b91a6c545f7cd4110ad"},
+ {file = "orjson-3.11.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7df6c7b8b0931feb3420b72838c3e2ba98c228f7aa60d461bc050cf4ca5f7b2"},
+ {file = "orjson-3.11.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6f59dfea7da1fced6e782bb3699718088b1036cb361f36c6e4dd843c5111aefe"},
+ {file = "orjson-3.11.2-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edf49146520fef308c31aa4c45b9925fd9c7584645caca7c0c4217d7900214ae"},
+ {file = "orjson-3.11.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50995bbeb5d41a32ad15e023305807f561ac5dcd9bd41a12c8d8d1d2c83e44e6"},
+ {file = "orjson-3.11.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2cc42960515076eb639b705f105712b658c525863d89a1704d984b929b0577d1"},
+ {file = "orjson-3.11.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c56777cab2a7b2a8ea687fedafb84b3d7fdafae382165c31a2adf88634c432fa"},
+ {file = "orjson-3.11.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:07349e88025b9b5c783077bf7a9f401ffbfb07fd20e86ec6fc5b7432c28c2c5e"},
+ {file = "orjson-3.11.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:45841fbb79c96441a8c58aa29ffef570c5df9af91f0f7a9572e5505e12412f15"},
+ {file = "orjson-3.11.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:13d8d8db6cd8d89d4d4e0f4161acbbb373a4d2a4929e862d1d2119de4aa324ac"},
+ {file = "orjson-3.11.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51da1ee2178ed09c00d09c1b953e45846bbc16b6420965eb7a913ba209f606d8"},
+ {file = "orjson-3.11.2-cp313-cp313-win32.whl", hash = "sha256:51dc033df2e4a4c91c0ba4f43247de99b3cbf42ee7a42ee2b2b2f76c8b2f2cb5"},
+ {file = "orjson-3.11.2-cp313-cp313-win_amd64.whl", hash = "sha256:29d91d74942b7436f29b5d1ed9bcfc3f6ef2d4f7c4997616509004679936650d"},
+ {file = "orjson-3.11.2-cp313-cp313-win_arm64.whl", hash = "sha256:4ca4fb5ac21cd1e48028d4f708b1bb13e39c42d45614befd2ead004a8bba8535"},
+ {file = "orjson-3.11.2-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:3dcba7101ea6a8d4ef060746c0f2e7aa8e2453a1012083e1ecce9726d7554cb7"},
+ {file = "orjson-3.11.2-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:15d17bdb76a142e1f55d91913e012e6e6769659daa6bfef3ef93f11083137e81"},
+ {file = "orjson-3.11.2-cp314-cp314-manylinux_2_34_aarch64.whl", hash = "sha256:53c9e81768c69d4b66b8876ec3c8e431c6e13477186d0db1089d82622bccd19f"},
+ {file = "orjson-3.11.2-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:d4f13af59a7b84c1ca6b8a7ab70d608f61f7c44f9740cd42409e6ae7b6c8d8b7"},
+ {file = "orjson-3.11.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:bde64aa469b5ee46cc960ed241fae3721d6a8801dacb2ca3466547a2535951e4"},
+ {file = "orjson-3.11.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:b5ca86300aeb383c8fa759566aca065878d3d98c3389d769b43f0a2e84d52c5f"},
+ {file = "orjson-3.11.2-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:24e32a558ebed73a6a71c8f1cbc163a7dd5132da5270ff3d8eeb727f4b6d1bc7"},
+ {file = "orjson-3.11.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e36319a5d15b97e4344110517450396845cc6789aed712b1fbf83c1bd95792f6"},
+ {file = "orjson-3.11.2-cp314-cp314-win32.whl", hash = "sha256:40193ada63fab25e35703454d65b6afc71dbc65f20041cb46c6d91709141ef7f"},
+ {file = "orjson-3.11.2-cp314-cp314-win_amd64.whl", hash = "sha256:7c8ac5f6b682d3494217085cf04dadae66efee45349ad4ee2a1da3c97e2305a8"},
+ {file = "orjson-3.11.2-cp314-cp314-win_arm64.whl", hash = "sha256:21cf261e8e79284242e4cb1e5924df16ae28255184aafeff19be1405f6d33f67"},
+ {file = "orjson-3.11.2-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:957f10c7b5bce3d3f2ad577f3b307c784f5dabafcce3b836229c269c11841c86"},
+ {file = "orjson-3.11.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a669e31ab8eb466c9142ac7a4be2bb2758ad236a31ef40dcd4cf8774ab40f33"},
+ {file = "orjson-3.11.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:adedf7d887416c51ad49de3c53b111887e0b63db36c6eb9f846a8430952303d8"},
+ {file = "orjson-3.11.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ad8873979659ad98fc56377b9c5b93eb8059bf01e6412f7abf7dbb3d637a991"},
+ {file = "orjson-3.11.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9482ef83b2bf796157566dd2d2742a8a1e377045fe6065fa67acb1cb1d21d9a3"},
+ {file = "orjson-3.11.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:73cee7867c1fcbd1cc5b6688b3e13db067f968889242955780123a68b3d03316"},
+ {file = "orjson-3.11.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:465166773265f3cc25db10199f5d11c81898a309e26a2481acf33ddbec433fda"},
+ {file = "orjson-3.11.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:bc000190a7b1d2d8e36cba990b3209a1e15c0efb6c7750e87f8bead01afc0d46"},
+ {file = "orjson-3.11.2-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:df3fdd8efa842ccbb81135d6f58a73512f11dba02ed08d9466261c2e9417af4e"},
+ {file = "orjson-3.11.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3dacfc621be3079ec69e0d4cb32e3764067726e0ef5a5576428f68b6dc85b4f6"},
+ {file = "orjson-3.11.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9fdff73a029cde5f4a1cf5ec9dbc6acab98c9ddd69f5580c2b3f02ce43ba9f9f"},
+ {file = "orjson-3.11.2-cp39-cp39-win32.whl", hash = "sha256:b1efbdc479c6451138c3733e415b4d0e16526644e54e2f3689f699c4cda303bf"},
+ {file = "orjson-3.11.2-cp39-cp39-win_amd64.whl", hash = "sha256:c9ec0cc0d4308cad1e38a1ee23b64567e2ff364c2a3fe3d6cbc69cf911c45712"},
+ {file = "orjson-3.11.2.tar.gz", hash = "sha256:91bdcf5e69a8fd8e8bdb3de32b31ff01d2bd60c1e8d5fe7d5afabdcf19920309"},
]
[[package]]
@@ -3287,30 +3288,31 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"]
[[package]]
name = "ruff"
-version = "0.12.8"
+version = "0.12.9"
description = "An extremely fast Python linter and code formatter, written in Rust."
optional = false
python-versions = ">=3.7"
groups = ["dev"]
files = [
- {file = "ruff-0.12.8-py3-none-linux_armv6l.whl", hash = "sha256:63cb5a5e933fc913e5823a0dfdc3c99add73f52d139d6cd5cc8639d0e0465513"},
- {file = "ruff-0.12.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9a9bbe28f9f551accf84a24c366c1aa8774d6748438b47174f8e8565ab9dedbc"},
- {file = "ruff-0.12.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:2fae54e752a3150f7ee0e09bce2e133caf10ce9d971510a9b925392dc98d2fec"},
- {file = "ruff-0.12.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0acbcf01206df963d9331b5838fb31f3b44fa979ee7fa368b9b9057d89f4a53"},
- {file = "ruff-0.12.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ae3e7504666ad4c62f9ac8eedb52a93f9ebdeb34742b8b71cd3cccd24912719f"},
- {file = "ruff-0.12.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb82efb5d35d07497813a1c5647867390a7d83304562607f3579602fa3d7d46f"},
- {file = "ruff-0.12.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:dbea798fc0065ad0b84a2947b0aff4233f0cb30f226f00a2c5850ca4393de609"},
- {file = "ruff-0.12.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:49ebcaccc2bdad86fd51b7864e3d808aad404aab8df33d469b6e65584656263a"},
- {file = "ruff-0.12.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ac9c570634b98c71c88cb17badd90f13fc076a472ba6ef1d113d8ed3df109fb"},
- {file = "ruff-0.12.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:560e0cd641e45591a3e42cb50ef61ce07162b9c233786663fdce2d8557d99818"},
- {file = "ruff-0.12.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:71c83121512e7743fba5a8848c261dcc454cafb3ef2934a43f1b7a4eb5a447ea"},
- {file = "ruff-0.12.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:de4429ef2ba091ecddedd300f4c3f24bca875d3d8b23340728c3cb0da81072c3"},
- {file = "ruff-0.12.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a2cab5f60d5b65b50fba39a8950c8746df1627d54ba1197f970763917184b161"},
- {file = "ruff-0.12.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:45c32487e14f60b88aad6be9fd5da5093dbefb0e3e1224131cb1d441d7cb7d46"},
- {file = "ruff-0.12.8-py3-none-win32.whl", hash = "sha256:daf3475060a617fd5bc80638aeaf2f5937f10af3ec44464e280a9d2218e720d3"},
- {file = "ruff-0.12.8-py3-none-win_amd64.whl", hash = "sha256:7209531f1a1fcfbe8e46bcd7ab30e2f43604d8ba1c49029bb420b103d0b5f76e"},
- {file = "ruff-0.12.8-py3-none-win_arm64.whl", hash = "sha256:c90e1a334683ce41b0e7a04f41790c429bf5073b62c1ae701c9dc5b3d14f0749"},
- {file = "ruff-0.12.8.tar.gz", hash = "sha256:4cb3a45525176e1009b2b64126acf5f9444ea59066262791febf55e40493a033"},
+ {file = "ruff-0.12.9-py3-none-linux_armv6l.whl", hash = "sha256:fcebc6c79fcae3f220d05585229463621f5dbf24d79fdc4936d9302e177cfa3e"},
+ {file = "ruff-0.12.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:aed9d15f8c5755c0e74467731a007fcad41f19bcce41cd75f768bbd687f8535f"},
+ {file = "ruff-0.12.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5b15ea354c6ff0d7423814ba6d44be2807644d0c05e9ed60caca87e963e93f70"},
+ {file = "ruff-0.12.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d596c2d0393c2502eaabfef723bd74ca35348a8dac4267d18a94910087807c53"},
+ {file = "ruff-0.12.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1b15599931a1a7a03c388b9c5df1bfa62be7ede6eb7ef753b272381f39c3d0ff"},
+ {file = "ruff-0.12.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3d02faa2977fb6f3f32ddb7828e212b7dd499c59eb896ae6c03ea5c303575756"},
+ {file = "ruff-0.12.9-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:17d5b6b0b3a25259b69ebcba87908496e6830e03acfb929ef9fd4c58675fa2ea"},
+ {file = "ruff-0.12.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:72db7521860e246adbb43f6ef464dd2a532ef2ef1f5dd0d470455b8d9f1773e0"},
+ {file = "ruff-0.12.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a03242c1522b4e0885af63320ad754d53983c9599157ee33e77d748363c561ce"},
+ {file = "ruff-0.12.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fc83e4e9751e6c13b5046d7162f205d0a7bac5840183c5beebf824b08a27340"},
+ {file = "ruff-0.12.9-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:881465ed56ba4dd26a691954650de6ad389a2d1fdb130fe51ff18a25639fe4bb"},
+ {file = "ruff-0.12.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:43f07a3ccfc62cdb4d3a3348bf0588358a66da756aa113e071b8ca8c3b9826af"},
+ {file = "ruff-0.12.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:07adb221c54b6bba24387911e5734357f042e5669fa5718920ee728aba3cbadc"},
+ {file = "ruff-0.12.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f5cd34fabfdea3933ab85d72359f118035882a01bff15bd1d2b15261d85d5f66"},
+ {file = "ruff-0.12.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:f6be1d2ca0686c54564da8e7ee9e25f93bdd6868263805f8c0b8fc6a449db6d7"},
+ {file = "ruff-0.12.9-py3-none-win32.whl", hash = "sha256:cc7a37bd2509974379d0115cc5608a1a4a6c4bff1b452ea69db83c8855d53f93"},
+ {file = "ruff-0.12.9-py3-none-win_amd64.whl", hash = "sha256:6fb15b1977309741d7d098c8a3cb7a30bc112760a00fb6efb7abc85f00ba5908"},
+ {file = "ruff-0.12.9-py3-none-win_arm64.whl", hash = "sha256:63c8c819739d86b96d500cce885956a1a48ab056bbcbc61b747ad494b2485089"},
+ {file = "ruff-0.12.9.tar.gz", hash = "sha256:fbd94b2e3c623f659962934e52c2bea6fc6da11f667a427a368adaf3af2c866a"},
]
[[package]]
diff --git a/tests/integration_tests/public_explorer_tests/test_public_cookbooks.py b/tests/integration_tests/public_explorer_tests/test_public_cookbooks.py
index 1385aebf5..c3902a6d4 100644
--- a/tests/integration_tests/public_explorer_tests/test_public_cookbooks.py
+++ b/tests/integration_tests/public_explorer_tests/test_public_cookbooks.py
@@ -217,13 +217,14 @@ def test_get_cookbooks_with_recipes(api_client: TestClient, unique_user: TestUse
)
)
- # Get the cookbook and make sure we only get the public recipes from each household
- response = api_client.get(api_routes.explore_groups_group_slug_cookbooks_item_id(unique_user.group_id, cookbook.id))
+ # Get the cookbook recipes and make sure we only get the public recipes from each household
+ response = api_client.get(
+ api_routes.explore_groups_group_slug_recipes(unique_user.group_id), params={"cookbook": cookbook.slug}
+ )
assert response.status_code == 200
- cookbook_data = response.json()
- assert cookbook_data["id"] == str(cookbook.id)
+ recipe_data = response.json()
- cookbook_recipe_ids: set[str] = {recipe["id"] for recipe in cookbook_data["recipes"]}
+ cookbook_recipe_ids: set[str] = {recipe["id"] for recipe in recipe_data["items"]}
assert len(cookbook_recipe_ids) == 2
assert str(public_recipe.id) in cookbook_recipe_ids
assert str(private_recipe.id) not in cookbook_recipe_ids
@@ -297,13 +298,14 @@ def test_get_cookbooks_private_household(api_client: TestClient, unique_user: Te
)
)
- # Get the cookbook and make sure we only get the public recipes from each household
- response = api_client.get(api_routes.explore_groups_group_slug_cookbooks_item_id(unique_user.group_id, cookbook.id))
+ # Get the cookbook recipes and make sure we only get the public recipes from each household
+ response = api_client.get(
+ api_routes.explore_groups_group_slug_recipes(unique_user.group_id), params={"cookbook": cookbook.slug}
+ )
assert response.status_code == 200
- cookbook_data = response.json()
- assert cookbook_data["id"] == str(cookbook.id)
+ recipe_data = response.json()
- cookbook_recipe_ids: set[str] = {recipe["id"] for recipe in cookbook_data["recipes"]}
+ cookbook_recipe_ids: set[str] = {recipe["id"] for recipe in recipe_data["items"]}
assert len(cookbook_recipe_ids) == 1
assert str(public_recipe.id) in cookbook_recipe_ids
assert str(other_household_private_recipe.id) not in cookbook_recipe_ids
diff --git a/tests/unit_tests/core/security/auth_cache/test_auth_cache.py b/tests/unit_tests/core/security/auth_cache/test_auth_cache.py
new file mode 100644
index 000000000..996cb5ae6
--- /dev/null
+++ b/tests/unit_tests/core/security/auth_cache/test_auth_cache.py
@@ -0,0 +1,239 @@
+import asyncio
+import time
+from unittest.mock import patch
+
+import pytest
+
+from mealie.routes.auth.auth_cache import AuthCache
+
+
+@pytest.fixture
+def cache():
+ return AuthCache(threshold=5, default_timeout=1.0)
+
+
+@pytest.mark.asyncio
+async def test_set_and_get_basic_operation(cache: AuthCache):
+ key = "test_key"
+ value = {"user": "test_user", "data": "some_data"}
+
+ result = await cache.set(key, value)
+ assert result is True
+
+ retrieved = await cache.get(key)
+ assert retrieved == value
+
+
+@pytest.mark.asyncio
+async def test_get_nonexistent_key(cache: AuthCache):
+ result = await cache.get("nonexistent_key")
+ assert result is None
+
+
+@pytest.mark.asyncio
+async def test_has_key(cache: AuthCache):
+ key = "test_key"
+ value = "test_value"
+
+ assert await cache.has(key) is False
+
+ await cache.set(key, value)
+ assert await cache.has(key) is True
+
+
+@pytest.mark.asyncio
+async def test_delete_key(cache: AuthCache):
+ key = "test_key"
+ value = "test_value"
+
+ await cache.set(key, value)
+ assert await cache.has(key) is True
+
+ result = await cache.delete(key)
+ assert result is True
+
+ assert await cache.has(key) is False
+ assert await cache.get(key) is None
+
+
+@pytest.mark.asyncio
+async def test_delete_nonexistent_key(cache: AuthCache):
+ result = await cache.delete("nonexistent_key")
+ assert result is False
+
+
+@pytest.mark.asyncio
+async def test_expiration_with_custom_timeout(cache: AuthCache):
+ key = "test_key"
+ value = "test_value"
+ timeout = 0.1 # 100ms
+
+ await cache.set(key, value, timeout=timeout)
+ assert await cache.has(key) is True
+ assert await cache.get(key) == value
+
+ # Wait for expiration
+ await asyncio.sleep(0.15)
+
+ assert await cache.has(key) is False
+ assert await cache.get(key) is None
+
+
+@pytest.mark.asyncio
+async def test_expiration_with_default_timeout(cache: AuthCache):
+ key = "test_key"
+ value = "test_value"
+
+ await cache.set(key, value)
+ assert await cache.has(key) is True
+
+ with patch("mealie.routes.auth.auth_cache.time") as mock_time:
+ current_time = time.time()
+ expired_time = current_time + cache.default_timeout + 1
+ mock_time.time.return_value = expired_time
+
+ assert await cache.has(key) is False
+ assert await cache.get(key) is None
+
+
+@pytest.mark.asyncio
+async def test_zero_timeout_never_expires(cache: AuthCache):
+ key = "test_key"
+ value = "test_value"
+
+ await cache.set(key, value, timeout=0)
+ with patch("time.time") as mock_time:
+ mock_time.return_value = time.time() + 10000
+
+ assert await cache.has(key) is True
+ assert await cache.get(key) == value
+
+
+@pytest.mark.asyncio
+async def test_clear_cache(cache: AuthCache):
+ await cache.set("key1", "value1")
+ await cache.set("key2", "value2")
+ await cache.set("key3", "value3")
+
+ assert await cache.has("key1") is True
+ assert await cache.has("key2") is True
+ assert await cache.has("key3") is True
+
+ cache.clear()
+
+ assert await cache.has("key1") is False
+ assert await cache.has("key2") is False
+ assert await cache.has("key3") is False
+
+
+@pytest.mark.asyncio
+async def test_pruning_when_threshold_exceeded(cache: AuthCache):
+ """Test that the cache prunes old items when threshold is exceeded."""
+ # Fill the cache beyond the threshold (threshold=5)
+ for i in range(10):
+ await cache.set(f"key_{i}", f"value_{i}")
+
+ assert len(cache._cache) < 10 # Should be less than what we inserted
+
+
+@pytest.mark.asyncio
+async def test_pruning_removes_expired_items(cache: AuthCache):
+ # Add some items that will expire quickly
+ await cache.set("expired1", "value1", timeout=0.01)
+ await cache.set("expired2", "value2", timeout=0.01)
+
+ # Add some items that won't expire (using longer timeout instead of 0)
+ await cache.set("permanent1", "value3", timeout=300)
+ await cache.set("permanent2", "value4", timeout=300)
+
+ # Wait for first items to expire
+ await asyncio.sleep(0.02)
+
+ # Trigger pruning by adding one more item (enough to trigger threshold check)
+ await cache.set("trigger_final", "final_value")
+
+ assert await cache.has("expired1") is False
+ assert await cache.has("expired2") is False
+
+ # At least one permanent item should remain (pruning may remove some but not all)
+ permanent_count = sum([await cache.has("permanent1"), await cache.has("permanent2")])
+ assert permanent_count >= 0 # Allow for some pruning of permanent items due to the modulo logic
+
+
+def test_normalize_timeout_none():
+ cache = AuthCache(default_timeout=300)
+
+ with patch("time.time", return_value=1000):
+ result = cache._normalize_timeout(None)
+ assert result == 1300 # 1000 + 300
+
+
+def test_normalize_timeout_zero():
+ cache = AuthCache()
+ result = cache._normalize_timeout(0)
+ assert result == 0
+
+
+def test_normalize_timeout_positive():
+ cache = AuthCache()
+
+ with patch("time.time", return_value=1000):
+ result = cache._normalize_timeout(60)
+ assert result == 1060 # 1000 + 60
+
+
+@pytest.mark.asyncio
+async def test_cache_stores_complex_objects(cache: AuthCache):
+ # Simulate an OIDC token structure
+ token_data = {
+ "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...",
+ "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...",
+ "userinfo": {
+ "sub": "user123",
+ "email": "user@example.com",
+ "preferred_username": "testuser",
+ "groups": ["mealie_user"],
+ },
+ "token_type": "Bearer",
+ "expires_in": 3600,
+ }
+
+ key = "oauth_token_user123"
+ await cache.set(key, token_data)
+
+ retrieved = await cache.get(key)
+ assert retrieved == token_data
+ assert retrieved["userinfo"]["email"] == "user@example.com"
+ assert "mealie_user" in retrieved["userinfo"]["groups"]
+
+
+@pytest.mark.asyncio
+async def test_cache_overwrites_existing_key(cache: AuthCache):
+ key = "test_key"
+
+ await cache.set(key, "initial_value")
+ assert await cache.get(key) == "initial_value"
+
+ await cache.set(key, "new_value")
+ assert await cache.get(key) == "new_value"
+
+
+@pytest.mark.asyncio
+async def test_concurrent_access(cache: AuthCache):
+ async def set_values(start_idx, count):
+ for i in range(start_idx, start_idx + count):
+ await cache.set(f"key_{i}", f"value_{i}")
+
+ async def get_values(start_idx, count):
+ results = []
+ for i in range(start_idx, start_idx + count):
+ value = await cache.get(f"key_{i}")
+ results.append(value)
+ return results
+
+ await asyncio.gather(set_values(0, 5), set_values(5, 5), set_values(10, 5))
+ results = await asyncio.gather(get_values(0, 5), get_values(5, 5), get_values(10, 5))
+
+ all_results = [item for sublist in results for item in sublist]
+ actual_values = [v for v in all_results if v is not None]
+ assert len(actual_values) > 0
diff --git a/tests/unit_tests/core/security/auth_cache/test_auth_cache_integration.py b/tests/unit_tests/core/security/auth_cache/test_auth_cache_integration.py
new file mode 100644
index 000000000..76d06185e
--- /dev/null
+++ b/tests/unit_tests/core/security/auth_cache/test_auth_cache_integration.py
@@ -0,0 +1,153 @@
+import asyncio
+
+import pytest
+from authlib.integrations.starlette_client import OAuth
+
+from mealie.routes.auth.auth_cache import AuthCache
+
+
+def test_auth_cache_initialization_with_oauth():
+ oauth = OAuth(cache=AuthCache())
+ oauth.register(
+ "test_oidc",
+ client_id="test_client_id",
+ client_secret="test_client_secret",
+ server_metadata_url="https://example.com/.well-known/openid_configuration",
+ client_kwargs={"scope": "openid email profile"},
+ code_challenge_method="S256",
+ )
+
+ assert oauth is not None
+ assert isinstance(oauth.cache, AuthCache)
+ assert "test_oidc" in oauth._clients
+
+
+@pytest.mark.asyncio
+async def test_oauth_cache_operations():
+ cache = AuthCache(threshold=500, default_timeout=300)
+ cache_key = "oauth_state_12345"
+ oauth_data = {
+ "state": "12345",
+ "code_verifier": "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk",
+ "redirect_uri": "http://localhost:3000/login",
+ }
+
+ result = await cache.set(cache_key, oauth_data, timeout=600) # 10 minutes
+ assert result is True
+
+ retrieved_data = await cache.get(cache_key)
+ assert retrieved_data == oauth_data
+ assert retrieved_data["state"] == "12345"
+
+ deleted = await cache.delete(cache_key)
+ assert deleted is True
+ assert await cache.get(cache_key) is None
+
+
+@pytest.mark.asyncio
+async def test_oauth_cache_handles_token_expiration():
+ cache = AuthCache()
+ token_key = "access_token_user123"
+ token_data = {
+ "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...",
+ "token_type": "Bearer",
+ "expires_in": 3600,
+ "scope": "openid email profile",
+ }
+
+ await cache.set(token_key, token_data, timeout=0.1)
+ assert await cache.has(token_key) is True
+
+ await asyncio.sleep(0.15)
+ assert await cache.has(token_key) is False
+ assert await cache.get(token_key) is None
+
+
+@pytest.mark.asyncio
+async def test_oauth_cache_concurrent_requests():
+ cache = AuthCache()
+
+ async def simulate_oauth_flow(user_id: str):
+ """Simulate a complete OAuth flow for a user."""
+ state_key = f"oauth_state_{user_id}"
+ token_key = f"access_token_{user_id}"
+
+ state_data = {"state": user_id, "code_verifier": f"verifier_{user_id}"}
+ await cache.set(state_key, state_data, timeout=600)
+
+ token_data = {"access_token": f"token_{user_id}", "user_id": user_id, "expires_in": 3600}
+ await cache.set(token_key, token_data, timeout=3600)
+
+ state = await cache.get(state_key)
+ token = await cache.get(token_key)
+
+ return state, token
+
+ results = await asyncio.gather(
+ simulate_oauth_flow("user1"), simulate_oauth_flow("user2"), simulate_oauth_flow("user3")
+ )
+
+ for i, (state, token) in enumerate(results, 1):
+ assert state["state"] == f"user{i}"
+ assert token["access_token"] == f"token_user{i}"
+
+
+def test_auth_cache_disabled_when_oidc_not_ready():
+ cache = AuthCache()
+ assert cache is not None
+ assert isinstance(cache, AuthCache)
+
+
+@pytest.mark.asyncio
+async def test_auth_cache_memory_efficiency():
+ cache = AuthCache(threshold=10, default_timeout=300)
+ for i in range(50):
+ await cache.set(f"token_{i}", f"data_{i}", timeout=0) # Never expire
+
+ assert len(cache._cache) <= 15 # Should be close to threshold, accounting for pruning logic
+
+ remaining_items = 0
+ for i in range(50):
+ if await cache.has(f"token_{i}"):
+ remaining_items += 1
+
+ assert 0 < remaining_items < 50
+
+
+@pytest.mark.asyncio
+async def test_auth_cache_with_real_oauth_data_structure():
+ cache = AuthCache()
+ oauth_token = {
+ "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEifQ...",
+ "id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEifQ...",
+ "token_type": "Bearer",
+ "expires_in": 3600,
+ "scope": "openid email profile groups",
+ "userinfo": {
+ "sub": "auth0|507f1f77bcf86cd799439011",
+ "email": "john.doe@example.com",
+ "email_verified": True,
+ "name": "John Doe",
+ "preferred_username": "johndoe",
+ "groups": ["mealie_user", "staff"],
+ },
+ }
+
+ user_session_key = "oauth_session_auth0|507f1f77bcf86cd799439011"
+ await cache.set(user_session_key, oauth_token, timeout=3600)
+
+ retrieved = await cache.get(user_session_key)
+ assert retrieved["access_token"] == oauth_token["access_token"]
+ assert retrieved["userinfo"]["email"] == "john.doe@example.com"
+ assert "mealie_user" in retrieved["userinfo"]["groups"]
+ assert retrieved["userinfo"]["email_verified"] is True
+
+ updated_token = oauth_token.copy()
+ updated_token["access_token"] = "new_access_token_eyJhbGciOiJSUzI1NiIs..."
+ updated_token["userinfo"]["last_login"] = "2024-01-01T12:00:00Z"
+
+ await cache.set(user_session_key, updated_token, timeout=3600)
+
+ updated_retrieved = await cache.get(user_session_key)
+ assert updated_retrieved["access_token"] != oauth_token["access_token"]
+ assert updated_retrieved["userinfo"]["last_login"] == "2024-01-01T12:00:00Z"