diff --git a/.gitignore b/.gitignore index 0f4444b6e..da82f89e4 100644 --- a/.gitignore +++ b/.gitignore @@ -145,4 +145,26 @@ node_modules/ scratch.py dev/data/backups/dev_sample_data*.zip !dev/data/backups/test*.zip -dev/data/recipes/* \ No newline at end of file +dev/data/recipes/* +dev/scripts/output/app_routes.py +dev/scripts/output/javascriptAPI/apiRoutes.js +dev/scripts/output/javascriptAPI/appEvents.js +dev/scripts/output/javascriptAPI/authentication.js +dev/scripts/output/javascriptAPI/backups.js +dev/scripts/output/javascriptAPI/debug.js +dev/scripts/output/javascriptAPI/groups.js +dev/scripts/output/javascriptAPI/index.js +dev/scripts/output/javascriptAPI/mealPlan.js +dev/scripts/output/javascriptAPI/migration.js +dev/scripts/output/javascriptAPI/queryAllRecipes.js +dev/scripts/output/javascriptAPI/recipeCategories.js +dev/scripts/output/javascriptAPI/recipeCRUD.js +dev/scripts/output/javascriptAPI/recipeTags.js +dev/scripts/output/javascriptAPI/settings.js +dev/scripts/output/javascriptAPI/shoppingLists.js +dev/scripts/output/javascriptAPI/siteMedia.js +dev/scripts/output/javascriptAPI/themes.js +dev/scripts/output/javascriptAPI/userAPITokens.js +dev/scripts/output/javascriptAPI/users.js +dev/scripts/output/javascriptAPI/userSignup.js +dev/scripts/output/javascriptAPI/utils.js diff --git a/dev/scripts/app_routes_gen copy.py b/dev/scripts/app_routes_gen copy.py new file mode 100644 index 000000000..3e42f0574 --- /dev/null +++ b/dev/scripts/app_routes_gen copy.py @@ -0,0 +1,144 @@ +import json +import re +from enum import Enum +from itertools import groupby +from pathlib import Path + +import slugify +from fastapi import FastAPI +from humps import camelize +from jinja2 import Template +from mealie.app import app +from pydantic import BaseModel + +CWD = Path(__file__).parent +OUT_DIR = CWD / "output" +OUT_FILE = OUT_DIR / "app_routes.py" + +JS_DIR = OUT_DIR / "javascriptAPI" +JS_OUT_FILE = JS_DIR / "apiRoutes.js" +TEMPLATES_DIR = CWD / "templates" + +PYTEST_TEMPLATE = TEMPLATES_DIR / "pytest_routes.j2" +JS_REQUESTS = TEMPLATES_DIR / "js_requests.j2" +JS_ROUTES = TEMPLATES_DIR / "js_routes.j2" +JS_INDEX = TEMPLATES_DIR / "js_index.j2" + +JS_DIR.mkdir(exist_ok=True, parents=True) + + +class RouteObject: + def __init__(self, route_string) -> None: + self.prefix = "/" + route_string.split("/")[1] + self.route = route_string.replace(self.prefix, "") + self.js_route = self.route.replace("{", "${") + self.parts = route_string.split("/")[1:] + self.var = re.findall(r"\{(.*?)\}", route_string) + self.is_function = "{" in self.route + self.router_slug = slugify.slugify("_".join(self.parts[1:]), separator="_") + self.router_camel = camelize(self.router_slug) + + def __repr__(self) -> str: + return f"""Route: {self.route} +Parts: {self.parts} +Function: {self.is_function} +Var: {self.var} +Slug: {self.router_slug} +""" + + +class RequestType(str, Enum): + get = "get" + put = "put" + post = "post" + patch = "patch" + delete = "delete" + + +class HTTPRequest(BaseModel): + request_type: RequestType + description: str = "" + summary: str + tags: list[str] + + @property + def summary_camel(self): + return camelize(self.summary) + + @property + def js_docs(self): + return self.description.replace("\n", " \n * ") + + +class PathObject(BaseModel): + route_object: RouteObject + http_verbs: list[HTTPRequest] + + class Config: + arbitrary_types_allowed = True + + +def get_path_objects(app: FastAPI): + paths = [] + + with open("scratch.json", "w") as f: + f.write(json.dumps(app.openapi())) + for key, value in app.openapi().items(): + if key == "paths": + for key, value in value.items(): + + paths.append( + PathObject( + route_object=RouteObject(key), + http_verbs=[HTTPRequest(request_type=k, **v) for k, v in value.items()], + ) + ) + + return paths + + +def read_template(file: Path): + with open(file, "r") as f: + return f.read() + + +def generate_template(app): + paths = get_path_objects(app) + + static_paths = [x.route_object for x in paths if not x.route_object.is_function] + function_paths = [x.route_object for x in paths if x.route_object.is_function] + + static_paths.sort(key=lambda x: x.router_slug) + function_paths.sort(key=lambda x: x.router_slug) + + template = Template(read_template(PYTEST_TEMPLATE)) + content = template.render(paths={"prefix": "/api", "static_paths": static_paths, "function_paths": function_paths}) + with open(OUT_FILE, "w") as f: + f.write(content) + + template = Template(read_template(JS_ROUTES)) + content = template.render( + paths={"prefix": "/api", "static_paths": static_paths, "function_paths": function_paths, "all_paths": paths} + ) + with open(JS_OUT_FILE, "w") as f: + f.write(content) + + all_tags = [] + for k, g in groupby(paths, lambda x: x.http_verbs[0].tags[0]): + template = Template(read_template(JS_REQUESTS)) + content = template.render(paths={"all_paths": list(g), "export_name": camelize(k)}) + + all_tags.append(camelize(k)) + + with open(JS_DIR.joinpath(camelize(k) + ".js"), "w") as f: + f.write(content) + + template = Template(read_template(JS_INDEX)) + content = template.render(files={"files": all_tags}) + + with open(JS_DIR.joinpath("index.js"), "w") as f: + f.write(content) + + +if __name__ == "__main__": + generate_template(app) diff --git a/dev/scripts/app_routes_gen.py b/dev/scripts/app_routes_gen.py deleted file mode 100644 index aa52c1da9..000000000 --- a/dev/scripts/app_routes_gen.py +++ /dev/null @@ -1,81 +0,0 @@ -import json -import re -from pathlib import Path - -import slugify -from jinja2 import Template -from mealie.app import app - -CWD = Path(__file__).parent -OUT_FILE = CWD.joinpath("output", "app_routes.py") - -code_template = """ -class AppRoutes: - def __init__(self) -> None: - self.prefix = '{{paths.prefix}}' -{% for path in paths.static_paths %} - self.{{ path.router_slug }} = "{{path.prefix}}{{ path.route }}"{% endfor %} -{% for path in paths.function_paths %} - def {{path.router_slug}}(self, {{path.var|join(", ")}}): - return f"{self.prefix}{{ path.route }}" -{% endfor %} -""" - - -def get_variables(path): - path = path.replace("/", " ") - print(path) - var = re.findall(r" \{.*\}", path) - print(var) - if var: - return [v.replace("{", "").replace("}", "") for v in var] - else: - return None - - -class RouteObject: - def __init__(self, route_string) -> None: - self.prefix = "/" + route_string.split("/")[1] - self.route = route_string.replace(self.prefix, "") - self.parts = route_string.split("/")[1:] - self.var = re.findall(r"\{(.*?)\}", route_string) - self.is_function = "{" in self.route - self.router_slug = slugify.slugify("_".join(self.parts[1:]), separator="_") - - def __repr__(self) -> str: - return f"""Route: {self.route} -Parts: {self.parts} -Function: {self.is_function} -Var: {self.var} -Slug: {self.router_slug} -""" - - -def get_paths(app): - paths = [] - print(json.dumps(app.openapi())) - for key, value in app.openapi().items(): - if key == "paths": - for key, value in value.items(): - paths.append(key) - - return paths - - -def generate_template(app): - paths = get_paths(app) - new_paths = [RouteObject(path) for path in paths] - - static_paths = [p for p in new_paths if not p.is_function] - function_paths = [p for p in new_paths if p.is_function] - - template = Template(code_template) - - content = template.render(paths={"prefix": "/api", "static_paths": static_paths, "function_paths": function_paths}) - - with open(OUT_FILE, "w") as f: - f.write(content) - - -if __name__ == "__main__": - generate_template(app) diff --git a/dev/scripts/output/app_routes.py b/dev/scripts/output/app_routes.py index 40e4b2c16..f46ec7102 100644 --- a/dev/scripts/output/app_routes.py +++ b/dev/scripts/output/app_routes.py @@ -1,105 +1,63 @@ +# This Content is Auto Generated for Pytest + + class AppRoutes: def __init__(self) -> None: - self.prefix = "/api" + self.prefix = '/api' + self.about_events = "/api/about/events" + self.about_events_notifications = "/api/about/events/notifications" + self.about_events_notifications_test = "/api/about/events/notifications/test" + self.auth_refresh = "/api/auth/refresh" self.auth_token = "/api/auth/token" self.auth_token_long = "/api/auth/token/long" - self.auth_refresh = "/api/auth/refresh" - self.users_sign_ups = "/api/users/sign-ups" - self.users = "/api/users" - self.users_self = "/api/users/self" - self.users_api_tokens = "/api/users-tokens" - self.groups = "/api/groups" - self.groups_self = "/api/groups/self" - self.recipes_summary = "/api/recipes/summary" - self.recipes_summary_untagged = "/api/recipes/summary/untagged" - self.recipes_summary_uncategorized = "/api/recipes/summary/uncategorized" - self.recipes_category = "/api/recipes/category" - self.recipes_tag = "/api/recipes/tag" - self.recipes_create = "/api/recipes/create" - self.recipes_create_url = "/api/recipes/create-url" + self.backups_available = "/api/backups/available" + self.backups_export_database = "/api/backups/export/database" + self.backups_upload = "/api/backups/upload" self.categories = "/api/categories" self.categories_empty = "/api/categories/empty" - self.tags = "/api/tags" - self.tags_empty = "/api/tags/empty" - self.about_events = "/api/about/events" + self.debug = "/api/debug" + self.debug_last_recipe_json = "/api/debug/last-recipe-json" + self.debug_log = "/api/debug/log" + self.debug_statistics = "/api/debug/statistics" + self.debug_version = "/api/debug/version" + self.groups = "/api/groups" + self.groups_self = "/api/groups/self" self.meal_plans_all = "/api/meal-plans/all" self.meal_plans_create = "/api/meal-plans/create" self.meal_plans_this_week = "/api/meal-plans/this-week" self.meal_plans_today = "/api/meal-plans/today" self.meal_plans_today_image = "/api/meal-plans/today/image" - self.site_settings_custom_pages = "/api/site-settings/custom-pages" + self.migrations = "/api/migrations" + self.recipes_category = "/api/recipes/category" + self.recipes_create = "/api/recipes/create" + self.recipes_create_url = "/api/recipes/create-url" + self.recipes_summary = "/api/recipes/summary" + self.recipes_summary_uncategorized = "/api/recipes/summary/uncategorized" + self.recipes_summary_untagged = "/api/recipes/summary/untagged" + self.recipes_tag = "/api/recipes/tag" + self.shopping_lists = "/api/shopping-lists" self.site_settings = "/api/site-settings" + self.site_settings_custom_pages = "/api/site-settings/custom-pages" self.site_settings_webhooks_test = "/api/site-settings/webhooks/test" + self.tags = "/api/tags" + self.tags_empty = "/api/tags/empty" self.themes = "/api/themes" self.themes_create = "/api/themes/create" - self.backups_available = "/api/backups/available" - self.backups_export_database = "/api/backups/export/database" - self.backups_upload = "/api/backups/upload" - self.migrations = "/api/migrations" - self.debug = "/api/debug" - self.debug_statistics = "/api/debug/statistics" - self.debug_version = "/api/debug/version" - self.debug_last_recipe_json = "/api/debug/last-recipe-json" - self.debug_log = "/api/debug/log" + self.users = "/api/users" + self.users_api_tokens = "/api/users-tokens" + self.users_self = "/api/users/self" + self.users_sign_ups = "/api/users/sign-ups" self.utils_download = "/api/utils/download" - def users_sign_ups_token(self, token): - return f"{self.prefix}/users/sign-ups/{token}" - - def users_id(self, id): - return f"{self.prefix}/users/{id}" - - def users_id_reset_password(self, id): - return f"{self.prefix}/users/{id}/reset-password" - - def users_id_image(self, id): - return f"{self.prefix}/users/{id}/image" - - def users_id_password(self, id): - return f"{self.prefix}/users/{id}/password" - - def users_api_tokens_token_id(self, token_id): - return f"{self.prefix}/users-tokens/{token_id}" - - def groups_id(self, id): - return f"{self.prefix}/groups/{id}" - - def recipes_recipe_slug(self, recipe_slug): - return f"{self.prefix}/recipes/{recipe_slug}" - - def recipes_recipe_slug_image(self, recipe_slug): - return f"{self.prefix}/recipes/{recipe_slug}/image" - - def recipes_recipe_slug_assets(self, recipe_slug): - return f"{self.prefix}/recipes/{recipe_slug}/assets" - - def categories_category(self, category): - return f"{self.prefix}/categories/{category}" - - def tags_tag(self, tag): - return f"{self.prefix}/tags/{tag}" - - def media_recipes_recipe_slug_images_file_name(self, recipe_slug, file_name): - return f"{self.prefix}/media/recipes/{recipe_slug}/images/{file_name}" - - def media_recipes_recipe_slug_assets_file_name(self, recipe_slug, file_name): - return f"{self.prefix}/media/recipes/{recipe_slug}/assets/{file_name}" - def about_events_id(self, id): return f"{self.prefix}/about/events/{id}" - def meal_plans_plan_id(self, plan_id): - return f"{self.prefix}/meal-plans/{plan_id}" + def about_events_notifications_id(self, id): + return f"{self.prefix}/about/events/notifications/{id}" - def meal_plans_id_shopping_list(self, id): - return f"{self.prefix}/meal-plans/{id}/shopping-list" - - def site_settings_custom_pages_id(self, id): - return f"{self.prefix}/site-settings/custom-pages/{id}" - - def themes_id(self, id): - return f"{self.prefix}/themes/{id}" + def backups_file_name_delete(self, file_name): + return f"{self.prefix}/backups/{file_name}/delete" def backups_file_name_download(self, file_name): return f"{self.prefix}/backups/{file_name}/download" @@ -107,17 +65,71 @@ class AppRoutes: def backups_file_name_import(self, file_name): return f"{self.prefix}/backups/{file_name}/import" - def backups_file_name_delete(self, file_name): - return f"{self.prefix}/backups/{file_name}/delete" + def categories_category(self, category): + return f"{self.prefix}/categories/{category}" - def migrations_import_type_file_name_import(self, import_type, file_name): - return f"{self.prefix}/migrations/{import_type}/{file_name}/import" + def debug_log_num(self, num): + return f"{self.prefix}/debug/log/{num}" + + def groups_id(self, id): + return f"{self.prefix}/groups/{id}" + + def meal_plans_id_shopping_list(self, id): + return f"{self.prefix}/meal-plans/{id}/shopping-list" + + def meal_plans_plan_id(self, plan_id): + return f"{self.prefix}/meal-plans/{plan_id}" + + def media_recipes_recipe_slug_assets_file_name(self, recipe_slug, file_name): + return f"{self.prefix}/media/recipes/{recipe_slug}/assets/{file_name}" + + def media_recipes_recipe_slug_images_file_name(self, recipe_slug, file_name): + return f"{self.prefix}/media/recipes/{recipe_slug}/images/{file_name}" def migrations_import_type_file_name_delete(self, import_type, file_name): return f"{self.prefix}/migrations/{import_type}/{file_name}/delete" + def migrations_import_type_file_name_import(self, import_type, file_name): + return f"{self.prefix}/migrations/{import_type}/{file_name}/import" + def migrations_import_type_upload(self, import_type): return f"{self.prefix}/migrations/{import_type}/upload" - def debug_log_num(self, num): - return f"{self.prefix}/debug/log/{num}" + def recipes_recipe_slug(self, recipe_slug): + return f"{self.prefix}/recipes/{recipe_slug}" + + def recipes_recipe_slug_assets(self, recipe_slug): + return f"{self.prefix}/recipes/{recipe_slug}/assets" + + def recipes_recipe_slug_image(self, recipe_slug): + return f"{self.prefix}/recipes/{recipe_slug}/image" + + def shopping_lists_id(self, id): + return f"{self.prefix}/shopping-lists/{id}" + + def site_settings_custom_pages_id(self, id): + return f"{self.prefix}/site-settings/custom-pages/{id}" + + def tags_tag(self, tag): + return f"{self.prefix}/tags/{tag}" + + def themes_id(self, id): + return f"{self.prefix}/themes/{id}" + + def users_api_tokens_token_id(self, token_id): + return f"{self.prefix}/users-tokens/{token_id}" + + def users_id(self, id): + return f"{self.prefix}/users/{id}" + + def users_id_image(self, id): + return f"{self.prefix}/users/{id}/image" + + def users_id_password(self, id): + return f"{self.prefix}/users/{id}/password" + + def users_id_reset_password(self, id): + return f"{self.prefix}/users/{id}/reset-password" + + def users_sign_ups_token(self, token): + return f"{self.prefix}/users/sign-ups/{token}" diff --git a/frontend/src/api/apiRoutes.js b/frontend/src/api/apiRoutes.js new file mode 100644 index 000000000..a978ccd3b --- /dev/null +++ b/frontend/src/api/apiRoutes.js @@ -0,0 +1,77 @@ +// This Content is Auto Generated +const prefix = '/api' +export const API_ROUTES = { + aboutEvents: "/api/about/events", + aboutEventsNotifications: "/api/about/events/notifications", + aboutEventsNotificationsTest: "/api/about/events/notifications/test", + authRefresh: "/api/auth/refresh", + authToken: "/api/auth/token", + authTokenLong: "/api/auth/token/long", + backupsAvailable: "/api/backups/available", + backupsExportDatabase: "/api/backups/export/database", + backupsUpload: "/api/backups/upload", + categories: "/api/categories", + categoriesEmpty: "/api/categories/empty", + debug: "/api/debug", + debugLastRecipeJson: "/api/debug/last-recipe-json", + debugLog: "/api/debug/log", + debugStatistics: "/api/debug/statistics", + debugVersion: "/api/debug/version", + groups: "/api/groups", + groupsSelf: "/api/groups/self", + mealPlansAll: "/api/meal-plans/all", + mealPlansCreate: "/api/meal-plans/create", + mealPlansThisWeek: "/api/meal-plans/this-week", + mealPlansToday: "/api/meal-plans/today", + mealPlansTodayImage: "/api/meal-plans/today/image", + migrations: "/api/migrations", + recipesCategory: "/api/recipes/category", + recipesCreate: "/api/recipes/create", + recipesCreateUrl: "/api/recipes/create-url", + recipesSummary: "/api/recipes/summary", + recipesSummaryUncategorized: "/api/recipes/summary/uncategorized", + recipesSummaryUntagged: "/api/recipes/summary/untagged", + recipesTag: "/api/recipes/tag", + shoppingLists: "/api/shopping-lists", + siteSettings: "/api/site-settings", + siteSettingsCustomPages: "/api/site-settings/custom-pages", + siteSettingsWebhooksTest: "/api/site-settings/webhooks/test", + tags: "/api/tags", + tagsEmpty: "/api/tags/empty", + themes: "/api/themes", + themesCreate: "/api/themes/create", + users: "/api/users", + usersApiTokens: "/api/users-tokens", + usersSelf: "/api/users/self", + usersSignUps: "/api/users/sign-ups", + utilsDownload: "/api/utils/download", + + aboutEventsId: (id) => `${prefix}/about/events/${id}`, + aboutEventsNotificationsId: (id) => `${prefix}/about/events/notifications/${id}`, + backupsFileNameDelete: (file_name) => `${prefix}/backups/${file_name}/delete`, + backupsFileNameDownload: (file_name) => `${prefix}/backups/${file_name}/download`, + backupsFileNameImport: (file_name) => `${prefix}/backups/${file_name}/import`, + categoriesCategory: (category) => `${prefix}/categories/${category}`, + debugLogNum: (num) => `${prefix}/debug/log/${num}`, + groupsId: (id) => `${prefix}/groups/${id}`, + mealPlansIdShoppingList: (id) => `${prefix}/meal-plans/${id}/shopping-list`, + mealPlansPlanId: (plan_id) => `${prefix}/meal-plans/${plan_id}`, + mediaRecipesRecipeSlugAssetsFileName: (recipe_slug, file_name) => `${prefix}/media/recipes/${recipe_slug}/assets/${file_name}`, + mediaRecipesRecipeSlugImagesFileName: (recipe_slug, file_name) => `${prefix}/media/recipes/${recipe_slug}/images/${file_name}`, + migrationsImportTypeFileNameDelete: (import_type, file_name) => `${prefix}/migrations/${import_type}/${file_name}/delete`, + migrationsImportTypeFileNameImport: (import_type, file_name) => `${prefix}/migrations/${import_type}/${file_name}/import`, + migrationsImportTypeUpload: (import_type) => `${prefix}/migrations/${import_type}/upload`, + recipesRecipeSlug: (recipe_slug) => `${prefix}/recipes/${recipe_slug}`, + recipesRecipeSlugAssets: (recipe_slug) => `${prefix}/recipes/${recipe_slug}/assets`, + recipesRecipeSlugImage: (recipe_slug) => `${prefix}/recipes/${recipe_slug}/image`, + shoppingListsId: (id) => `${prefix}/shopping-lists/${id}`, + siteSettingsCustomPagesId: (id) => `${prefix}/site-settings/custom-pages/${id}`, + tagsTag: (tag) => `${prefix}/tags/${tag}`, + themesId: (id) => `${prefix}/themes/${id}`, + usersApiTokensTokenId: (token_id) => `${prefix}/users-tokens/${token_id}`, + usersId: (id) => `${prefix}/users/${id}`, + usersIdImage: (id) => `${prefix}/users/${id}/image`, + usersIdPassword: (id) => `${prefix}/users/${id}/password`, + usersIdResetPassword: (id) => `${prefix}/users/${id}/reset-password`, + usersSignUpsToken: (token) => `${prefix}/users/sign-ups/${token}`, +} \ No newline at end of file diff --git a/frontend/src/api/index.js b/frontend/src/api/index.js index 53d22e8ac..dbc633212 100644 --- a/frontend/src/api/index.js +++ b/frontend/src/api/index.js @@ -12,6 +12,7 @@ import { signupAPI } from "./signUps"; import { groupAPI } from "./groups"; import { siteSettingsAPI } from "./siteSettings"; import { aboutAPI } from "./about"; +import { shoppingListsAPI } from "./shoppingLists"; /** * The main object namespace for interacting with the backend database @@ -32,4 +33,5 @@ export const api = { signUps: signupAPI, groups: groupAPI, about: aboutAPI, + shoppingLists: shoppingListsAPI, }; diff --git a/frontend/src/api/shoppingLists.js b/frontend/src/api/shoppingLists.js new file mode 100644 index 000000000..74a7c9e2c --- /dev/null +++ b/frontend/src/api/shoppingLists.js @@ -0,0 +1,33 @@ +// This Content is Auto Generated +import { API_ROUTES } from "./apiRoutes"; +import { apiReq } from "./api-utils"; + +export const shoppingListsAPI = { + /** Create Shopping List in the Database + */ + async createShoppingList(data) { + const response = await apiReq.post(API_ROUTES.shoppingLists, data); + return response.data; + }, + /** Get Shopping List from the Database + * @param id + */ + async getShoppingList(id) { + const response = await apiReq.get(API_ROUTES.shoppingListsId(id)); + return response.data; + }, + /** Update Shopping List in the Database + * @param id + */ + async updateShoppingList(id, data) { + const response = await apiReq.put(API_ROUTES.shoppingListsId(id), data); + return response.data; + }, + /** Delete Shopping List from the Database + * @param id + */ + async deleteShoppingList(id) { + const response = await apiReq.delete(API_ROUTES.shoppingListsId(id)); + return response.data; + }, +}; diff --git a/frontend/src/components/Fallbacks/NoRecipe.vue b/frontend/src/components/Fallbacks/NoRecipe.vue new file mode 100644 index 000000000..d93b92401 --- /dev/null +++ b/frontend/src/components/Fallbacks/NoRecipe.vue @@ -0,0 +1,17 @@ + + + + No Recipe Found + + + + + + + + diff --git a/frontend/src/components/Fallbacks/The404.vue b/frontend/src/components/Fallbacks/The404.vue new file mode 100644 index 000000000..01e57e372 --- /dev/null +++ b/frontend/src/components/Fallbacks/The404.vue @@ -0,0 +1,51 @@ + + + + + {{ $t("404.page-not-found") }} + + + + + 4 + + mdi-silverware-variant + + 4 + + + + + + + {{ button.icon }} + {{ button.text }} + + + + + + + + + + \ No newline at end of file diff --git a/frontend/src/components/MealPlan/MealPlanCard.vue b/frontend/src/components/MealPlan/MealPlanCard.vue index c13cf35de..da95e286f 100644 --- a/frontend/src/components/MealPlan/MealPlanCard.vue +++ b/frontend/src/components/MealPlan/MealPlanCard.vue @@ -1,29 +1,60 @@ + + + + + + + + + + mdi-square-edit-outline + + No Recipe + + {{ $d(new Date(planDay.date.split("-")), "short") }} {{ planDay.meals[0].name }} - - - - - mdi-plus - - Side - - + + + + + + + mdi-square-edit-outline + + No Recipe + + + + + mdi-plus + + Side + + + - + @@ -48,12 +79,14 @@ @@ -116,4 +172,8 @@ export default { .relative-card { position: relative; } + +.custom-button { + z-index: -1; +} diff --git a/frontend/src/components/Recipe/CardImage.vue b/frontend/src/components/Recipe/CardImage.vue index d329c149a..d37f16619 100644 --- a/frontend/src/components/Recipe/CardImage.vue +++ b/frontend/src/components/Recipe/CardImage.vue @@ -7,11 +7,16 @@ @load="fallBackImage = false" @error="fallBackImage = true" > - + - - mdi-silverware-variant - + + + + + + mdi-silverware-variant + + @@ -75,6 +80,15 @@ export default { diff --git a/frontend/src/components/UI/Buttons/TheCopyButton.vue b/frontend/src/components/UI/Buttons/TheCopyButton.vue index 087773100..6633eda71 100644 --- a/frontend/src/components/UI/Buttons/TheCopyButton.vue +++ b/frontend/src/components/UI/Buttons/TheCopyButton.vue @@ -3,7 +3,7 @@ ref="copyToolTip" v-model="show" color="success lighten-1" - right + top :open-on-hover="false" :open-on-click="true" close-delay="500" @@ -12,7 +12,7 @@ mdi-clipboard-check - {{ $t('general.coppied')}}! - + {{ $t("general.coppied") }}! @@ -39,6 +38,9 @@ export default { copyText: { default: "Default Copy Text", }, + color: { + default: "primary", + }, }, data() { return { diff --git a/frontend/src/components/UI/Dialogs/BaseDialog.vue b/frontend/src/components/UI/Dialogs/BaseDialog.vue index 0a83ce0df..7eae2236c 100644 --- a/frontend/src/components/UI/Dialogs/BaseDialog.vue +++ b/frontend/src/components/UI/Dialogs/BaseDialog.vue @@ -64,7 +64,7 @@ export default { default: false, }, top: { - default: false, + default: null, }, submitText: { default: () => i18n.t("general.create"), diff --git a/frontend/src/components/UI/Search/SearchDialog.vue b/frontend/src/components/UI/Search/SearchDialog.vue index efbeafa93..688d58374 100644 --- a/frontend/src/components/UI/Search/SearchDialog.vue +++ b/frontend/src/components/UI/Search/SearchDialog.vue @@ -74,8 +74,6 @@ export default { }, open() { this.dialog = true; - this.$refs.mealSearchBar.resetSearch(); - this.$router.push("#search"); }, toggleDialog(open) { if (open) { diff --git a/frontend/src/components/UI/TheSiteMenu.vue b/frontend/src/components/UI/TheSiteMenu.vue index 7c03cc174..1b4d41bc8 100644 --- a/frontend/src/components/UI/TheSiteMenu.vue +++ b/frontend/src/components/UI/TheSiteMenu.vue @@ -63,6 +63,12 @@ export default { nav: "/meal-plan/planner", restricted: true, }, + { + icon: "mdi-format-list-checks", + title: "Shopping Lists", + nav: "/shopping-list", + restricted: true, + }, { icon: "mdi-logout", title: this.$t("user.logout"), diff --git a/frontend/src/pages/404Page.vue b/frontend/src/pages/404Page.vue index bf4f195db..e98fab51c 100644 --- a/frontend/src/pages/404Page.vue +++ b/frontend/src/pages/404Page.vue @@ -1,22 +1,13 @@ - - - - - - {{ $t("404.page-not-found") }} - - {{ $t("404.take-me-home") }} - - - - + - diff --git a/frontend/src/pages/MealPlan/Planner.vue b/frontend/src/pages/MealPlan/Planner.vue index 5cf1e3465..7e7d25a93 100644 --- a/frontend/src/pages/MealPlan/Planner.vue +++ b/frontend/src/pages/MealPlan/Planner.vue @@ -2,7 +2,6 @@ - @@ -13,12 +12,34 @@ - + {{ $d(new Date(mealplan.startDate.split("-")), "short") }} - {{ $d(new Date(mealplan.endDate.split("-")), "short") }} + + + + + mdi-cart-check + + Create Shopping List + + + + mdi-cart-check + + Shopping List + + - + @@ -47,17 +68,14 @@ - - - {{ $t("meal-plan.shopping-list") }} + + + {{ $t("general.delete") }} - + {{ $t("general.edit") }} - - {{ $t("general.delete") }} - @@ -70,13 +88,11 @@ import { api } from "@/api"; import { utils } from "@/utils"; import NewMeal from "@/components/MealPlan/MealPlanNew"; import EditPlan from "@/components/MealPlan/MealPlanEditor"; -import ShoppingListDialog from "@/components/MealPlan/ShoppingListDialog"; export default { components: { NewMeal, EditPlan, - ShoppingListDialog, }, data: () => ({ plannedMeals: [], @@ -114,8 +130,13 @@ export default { this.requestMeals(); } }, - openShoppingList(id) { - this.$refs.shoppingList.openDialog(id); + async createShoppingList(id) { + await api.mealPlans.shoppingList(id); + this.requestMeals(); + this.$store.dispatch("requestCurrentGroup"); + }, + redirectToList(id) { + this.$router.push(id); }, }, }; diff --git a/frontend/src/pages/MealPlan/ThisWeek.vue b/frontend/src/pages/MealPlan/ThisWeek.vue index db0e41ca8..46de8676b 100644 --- a/frontend/src/pages/MealPlan/ThisWeek.vue +++ b/frontend/src/pages/MealPlan/ThisWeek.vue @@ -1,43 +1,44 @@ - - - - - - - - - {{ meal.name }} - - {{ $d(new Date(meal.date), "short") }} - - {{ meal.description }} - - - - - {{ $t("recipe.view-recipe") }} - - - - - - - - - - - - - - + + + + {{ $d(new Date(planDay.date), "short") }} + + + + + Main + + + + Sides + + + + + + \ No newline at end of file diff --git a/frontend/src/routes/general.js b/frontend/src/routes/general.js index 5a6ffe515..897ba9061 100644 --- a/frontend/src/routes/general.js +++ b/frontend/src/routes/general.js @@ -1,9 +1,11 @@ import SearchPage from "@/pages/SearchPage"; import HomePage from "@/pages/HomePage"; +import ShoppingList from "@/pages/ShoppingList"; export const generalRoutes = [ { path: "/", name: "home", component: HomePage }, { path: "/mealie", component: HomePage }, + { path: "/shopping-list", component: ShoppingList }, { path: "/search", component: SearchPage, diff --git a/frontend/src/utils/index.js b/frontend/src/utils/index.js index d62b1ffe9..8d8139366 100644 --- a/frontend/src/utils/index.js +++ b/frontend/src/utils/index.js @@ -5,18 +5,14 @@ import { store } from "@/store"; export const utils = { recipe: recipe, - getImageURL(image) { - return `/api/recipes/${image}/image?image_type=small`; - }, generateUniqueKey(item, index) { const uniqueKey = `${item}-${index}`; return uniqueKey; }, getDateAsPythonDate(dateObject) { - const month = dateObject.getUTCMonth() + 1; - const day = dateObject.getUTCDate(); + const month = dateObject.getMonth() + 1; + const day = dateObject.getDate(); const year = dateObject.getFullYear(); - return `${year}-${month}-${day}`; }, notify: { diff --git a/mealie/app.py b/mealie/app.py index 4427c7fda..1f90d916d 100644 --- a/mealie/app.py +++ b/mealie/app.py @@ -9,6 +9,7 @@ from mealie.routes.groups import groups_router from mealie.routes.mealplans import meal_plan_router from mealie.routes.media import media_router from mealie.routes.recipe import recipe_router +from mealie.routes.shopping_list import shopping_list_router from mealie.routes.site_settings import settings_router from mealie.routes.users import user_router from mealie.services.events import create_general_event @@ -32,6 +33,7 @@ def api_routers(): # Authentication app.include_router(user_router) app.include_router(groups_router) + app.include_router(shopping_list_router) # Recipes app.include_router(recipe_router) app.include_router(media_router) diff --git a/mealie/db/database.py b/mealie/db/database.py index 45e461ecf..e8bf5be6b 100644 --- a/mealie/db/database.py +++ b/mealie/db/database.py @@ -6,6 +6,7 @@ from mealie.db.models.group import Group from mealie.db.models.mealplan import MealPlan from mealie.db.models.recipe.recipe import Category, RecipeModel, Tag from mealie.db.models.settings import CustomPage, SiteSettings +from mealie.db.models.shopping_list import ShoppingList from mealie.db.models.sign_up import SignUp from mealie.db.models.theme import SiteThemeModel from mealie.db.models.users import LongLiveToken, User @@ -16,6 +17,7 @@ from mealie.schema.meal import MealPlanOut from mealie.schema.recipe import Recipe from mealie.schema.settings import CustomPageOut from mealie.schema.settings import SiteSettings as SiteSettingsSchema +from mealie.schema.shopping_list import ShoppingListOut from mealie.schema.sign_up import SignUpOut from mealie.schema.theme import SiteTheme from mealie.schema.user import GroupInDB, LongLiveTokenInDB, UserInDB @@ -136,6 +138,13 @@ class _Groups(BaseDocument): return group.mealplans +class _ShoppingList(BaseDocument): + def __init__(self) -> None: + self.primary_key = "id" + self.sql_model = ShoppingList + self.schema = ShoppingListOut + + class _SignUps(BaseDocument): def __init__(self) -> None: self.primary_key = "token" @@ -179,6 +188,7 @@ class Database: self.custom_pages = _CustomPages() self.events = _Events() self.event_notifications = _EventNotification() + self.shopping_lists = _ShoppingList() db = Database() diff --git a/mealie/db/models/_all_models.py b/mealie/db/models/_all_models.py index 0e5daca17..c38ae9e4a 100644 --- a/mealie/db/models/_all_models.py +++ b/mealie/db/models/_all_models.py @@ -3,6 +3,7 @@ from mealie.db.models.group import * from mealie.db.models.mealplan import * from mealie.db.models.recipe.recipe import * from mealie.db.models.settings import * +from mealie.db.models.shopping_list import * from mealie.db.models.sign_up import * from mealie.db.models.theme import * from mealie.db.models.users import * diff --git a/mealie/db/models/group.py b/mealie/db/models/group.py index 80806a108..661038f66 100644 --- a/mealie/db/models/group.py +++ b/mealie/db/models/group.py @@ -24,6 +24,13 @@ class Group(SqlAlchemyBase, BaseMixins): single_parent=True, order_by="MealPlan.start_date", ) + + shopping_lists = orm.relationship( + "ShoppingList", + back_populates="group", + single_parent=True, + ) + categories = orm.relationship("Category", secondary=group2categories, single_parent=True) # Webhook Settings @@ -32,16 +39,7 @@ class Group(SqlAlchemyBase, BaseMixins): webhook_urls = orm.relationship("WebhookURLModel", uselist=True, cascade="all, delete-orphan") def __init__( - self, - name, - id=None, - users=None, - mealplans=None, - categories=[], - session=None, - webhook_enable=False, - webhook_time="00:00", - webhook_urls=[], + self, name, categories=[], session=None, webhook_enable=False, webhook_time="00:00", webhook_urls=[], **_ ) -> None: self.name = name self.categories = [Category.get_ref(session=session, slug=cat.get("slug")) for cat in categories] diff --git a/mealie/db/models/mealplan.py b/mealie/db/models/mealplan.py index ae7cc3030..629b3f02a 100644 --- a/mealie/db/models/mealplan.py +++ b/mealie/db/models/mealplan.py @@ -2,6 +2,7 @@ import sqlalchemy.orm as orm from mealie.db.models.group import Group from mealie.db.models.model_base import BaseMixins, SqlAlchemyBase from mealie.db.models.recipe.recipe import RecipeModel +from mealie.db.models.shopping_list import ShoppingList from sqlalchemy import Column, Date, ForeignKey, Integer, String from sqlalchemy.ext.orderinglist import ordering_list @@ -56,8 +57,24 @@ class MealPlan(SqlAlchemyBase, BaseMixins): group_id = Column(Integer, ForeignKey("groups.id")) group = orm.relationship("Group", back_populates="mealplans") - def __init__(self, start_date, end_date, plan_days, group: str, uid=None, session=None) -> None: + shopping_list_id = Column(Integer, ForeignKey("shopping_lists.id")) + shopping_list: ShoppingList = orm.relationship("ShoppingList", single_parent=True) + + def __init__( + self, + start_date, + end_date, + plan_days, + group: str, + shopping_list: int = None, + session=None, + **_, + ) -> None: self.start_date = start_date self.end_date = end_date self.group = Group.get_ref(session, group) + + if shopping_list: + self.shopping_list = ShoppingList.get_ref(session, shopping_list) + self.plan_days = [MealDay(**day, session=session) for day in plan_days] diff --git a/mealie/db/models/shopping_list.py b/mealie/db/models/shopping_list.py new file mode 100644 index 000000000..41d3e6256 --- /dev/null +++ b/mealie/db/models/shopping_list.py @@ -0,0 +1,49 @@ +import sqlalchemy.orm as orm +from mealie.db.models.group import Group +from mealie.db.models.model_base import BaseMixins, SqlAlchemyBase +from requests import Session +from sqlalchemy import Boolean, Column, ForeignKey, Integer, String +from sqlalchemy.ext.orderinglist import ordering_list + + +class ShoppingListItem(SqlAlchemyBase, BaseMixins): + __tablename__ = "shopping_list_items" + id = Column(Integer, primary_key=True) + parent_id = Column(Integer, ForeignKey("shopping_lists.id")) + position = Column(Integer, nullable=False) + + title = Column(String) + text = Column(String) + quantity = Column(Integer) + checked = Column(Boolean) + + def __init__(self, title, text, quantity, checked, **_) -> None: + self.title = title + self.text = text + self.quantity = quantity + self.checked = checked + + +class ShoppingList(SqlAlchemyBase, BaseMixins): + __tablename__ = "shopping_lists" + id = Column(Integer, primary_key=True) + + group_id = Column(Integer, ForeignKey("groups.id")) + group = orm.relationship("Group", back_populates="shopping_lists") + + name = Column(String) + items: list[ShoppingListItem] = orm.relationship( + ShoppingListItem, + cascade="all, delete, delete-orphan", + order_by="ShoppingListItem.position", + collection_class=ordering_list("position"), + ) + + def __init__(self, name, group, items, session=None, **_) -> None: + self.name = name + self.group = Group.get_ref(session, group) + self.items = [ShoppingListItem(**i) for i in items] + + @staticmethod + def get_ref(session: Session, id: int): + return session.query(ShoppingList).filter(ShoppingList.id == id).one_or_none() diff --git a/mealie/routes/mealplans/helpers.py b/mealie/routes/mealplans/helpers.py index 2afe00336..62bc980cf 100644 --- a/mealie/routes/mealplans/helpers.py +++ b/mealie/routes/mealplans/helpers.py @@ -1,11 +1,16 @@ from fastapi import APIRouter, Depends +from mealie.core.root_logger import get_logger from mealie.db.database import db from mealie.db.db_setup import generate_session from mealie.routes.deps import get_current_user from mealie.schema.meal import MealPlanOut from mealie.schema.recipe import Recipe +from mealie.schema.shopping_list import ListItem, ShoppingListIn, ShoppingListOut +from mealie.schema.user import UserInDB from sqlalchemy.orm.session import Session +logger = get_logger() + router = APIRouter(prefix="/api/meal-plans", tags=["Meal Plan"]) @@ -13,12 +18,32 @@ router = APIRouter(prefix="/api/meal-plans", tags=["Meal Plan"]) def get_shopping_list( id: str, session: Session = Depends(generate_session), - current_user=Depends(get_current_user), + current_user: UserInDB = Depends(get_current_user), ): - # ! Refactor into Single Database Call - mealplan = db.meals.get(session, id) - mealplan: MealPlanOut - slugs = [x.slug for x in mealplan.meals] - recipes: list[Recipe] = [db.recipes.get(session, x) for x in slugs] - return [{"name": x.name, "recipe_ingredient": x.recipe_ingredient} for x in recipes if x] + mealplan: MealPlanOut = db.meals.get(session, id) + + all_ingredients = [] + + for plan_day in mealplan.plan_days: + for meal in plan_day.meals: + if not meal.slug: + continue + + try: + recipe: Recipe = db.recipes.get(session, meal.slug) + all_ingredients += recipe.recipe_ingredient + except Exception: + logger.error("Recipe Not Found") + + new_list = ShoppingListIn( + name="MealPlan Shopping List", group=current_user.group, items=[ListItem(text=t) for t in all_ingredients] + ) + + created_list: ShoppingListOut = db.shopping_lists.create(session, new_list) + + mealplan.shopping_list = created_list.id + + db.meals.update(session, mealplan.uid, mealplan) + + return created_list diff --git a/mealie/routes/recipe/category_routes.py b/mealie/routes/recipe/category_routes.py index c358a69af..431dbb5b4 100644 --- a/mealie/routes/recipe/category_routes.py +++ b/mealie/routes/recipe/category_routes.py @@ -5,10 +5,7 @@ from mealie.routes.deps import get_current_user from mealie.schema.category import CategoryIn, RecipeCategoryResponse from sqlalchemy.orm.session import Session -router = APIRouter( - prefix="/api/categories", - tags=["Recipe Categories"], -) +router = APIRouter(prefix="/api/categories", tags=["Recipe Categories"]) @router.get("") diff --git a/mealie/routes/shopping_list.py b/mealie/routes/shopping_list.py new file mode 100644 index 000000000..83dfc8774 --- /dev/null +++ b/mealie/routes/shopping_list.py @@ -0,0 +1,40 @@ +from fastapi import APIRouter, Depends +from mealie.db.database import db +from mealie.db.db_setup import generate_session +from mealie.routes.deps import get_current_user +from mealie.schema.shopping_list import ShoppingListIn, ShoppingListOut +from mealie.schema.user import UserInDB +from sqlalchemy.orm.session import Session + +shopping_list_router = APIRouter(prefix="/api/shopping-lists", tags=["Shopping Lists"]) + + +@shopping_list_router.post("", response_model=ShoppingListOut) +async def create_shopping_list( + list_in: ShoppingListIn, + current_user: UserInDB = Depends(get_current_user), + session: Session = Depends(generate_session), +): + """ Create Shopping List in the Database """ + + list_in.group = current_user.group + + return db.shopping_lists.create(session, list_in) + + +@shopping_list_router.get("/{id}", response_model=ShoppingListOut) +async def get_shopping_list(id: int, session: Session = Depends(generate_session)): + """ Get Shopping List from the Database """ + return db.shopping_lists.get(session, id) + + +@shopping_list_router.put("/{id}", dependencies=[Depends(get_current_user)], response_model=ShoppingListOut) +async def update_shopping_list(id: int, new_data: ShoppingListIn, session: Session = Depends(generate_session)): + """ Update Shopping List in the Database """ + return db.shopping_lists.update(session, id, new_data) + + +@shopping_list_router.delete("/{id}", dependencies=[Depends(get_current_user)]) +async def delete_shopping_list(id: int, session: Session = Depends(generate_session)): + """ Delete Shopping List from the Database """ + return db.shopping_lists.delete(session, id) diff --git a/mealie/schema/meal.py b/mealie/schema/meal.py index 93aecc5cd..fccb0cc56 100644 --- a/mealie/schema/meal.py +++ b/mealie/schema/meal.py @@ -49,13 +49,22 @@ class MealPlanIn(CamelModel): class MealPlanOut(MealPlanIn): uid: int + shopping_list: Optional[int] class Config: orm_mode = True @classmethod def getter_dict(_cls, name_orm: MealPlan): - return { - **GetterDict(name_orm), - "group": name_orm.group.name, - } + try: + return { + **GetterDict(name_orm), + "group": name_orm.group.name, + "shopping_list": name_orm.shopping_list.id, + } + except Exception: + return { + **GetterDict(name_orm), + "group": name_orm.group.name, + "shopping_list": None, + } diff --git a/mealie/schema/shopping_list.py b/mealie/schema/shopping_list.py new file mode 100644 index 000000000..14fa0631e --- /dev/null +++ b/mealie/schema/shopping_list.py @@ -0,0 +1,35 @@ +from typing import Optional + +from fastapi_camelcase import CamelModel +from mealie.db.models.shopping_list import ShoppingList +from pydantic.utils import GetterDict + + +class ListItem(CamelModel): + title: Optional[str] + text: str = "" + quantity: int = 1 + checked: bool = False + + class Config: + orm_mode = True + + +class ShoppingListIn(CamelModel): + name: str + group: Optional[str] + items: list[ListItem] + + +class ShoppingListOut(ShoppingListIn): + id: int + + class Config: + orm_mode = True + + @classmethod + def getter_dict(cls, ormModel: ShoppingList): + return { + **GetterDict(ormModel), + "group": ormModel.group.name, + } diff --git a/mealie/schema/user.py b/mealie/schema/user.py index c8f6fb4fc..4bba3e556 100644 --- a/mealie/schema/user.py +++ b/mealie/schema/user.py @@ -6,6 +6,7 @@ from mealie.db.models.group import Group from mealie.db.models.users import User from mealie.schema.category import CategoryBase from mealie.schema.meal import MealPlanOut +from mealie.schema.shopping_list import ShoppingListOut from pydantic.types import constr from pydantic.utils import GetterDict @@ -106,6 +107,7 @@ class UpdateGroup(GroupBase): class GroupInDB(UpdateGroup): users: Optional[list[UserOut]] mealplans: Optional[list[MealPlanOut]] + shopping_lists: Optional[list[ShoppingListOut]] class Config: orm_mode = True diff --git a/mealie/services/meal_services.py b/mealie/services/meal_services.py index 52f8951e4..b5a6fafa7 100644 --- a/mealie/services/meal_services.py +++ b/mealie/services/meal_services.py @@ -12,7 +12,6 @@ from sqlalchemy.orm.session import Session def set_mealplan_dates(meal_plan_base: MealPlanIn) -> MealPlanIn: for x, plan_days in enumerate(meal_plan_base.plan_days): plan_days: MealDayIn - plan_days.date = meal_plan_base.start_date + timedelta(days=x) @@ -29,22 +28,22 @@ def get_todays_meal(session: Session, group: Union[int, GroupInDB]) -> Recipe: Recipe: Pydantic Recipe Object """ - return + session = session or create_session() - # session = session or create_session() + if isinstance(group, int): + group: GroupInDB = db.groups.get(session, group) - # if isinstance(group, int): - # group: GroupInDB = db.groups.get(session, group) + today_slug = None - # today_slug = None + for mealplan in group.mealplans: + for plan_day in mealplan.plan_days: + if plan_day.date == date.today(): + if plan_day.meals[0].slug and plan_day.meals[0].slug != "": + today_slug = plan_day.meals[0].slug + else: + return plan_day.meals[0] - # for mealplan in group.mealplans: - # for meal in mealplan.meals: - # if meal.date == date.today(): - # today_slug = meal.slug - # break - - # if today_slug: - # return db.recipes.get(session, today_slug) - # else: - # return None + if today_slug: + return db.recipes.get(session, today_slug) + else: + return None diff --git a/scratch.json b/scratch.json new file mode 100644 index 000000000..b7cdad46a --- /dev/null +++ b/scratch.json @@ -0,0 +1 @@ +{"openapi": "3.0.2", "info": {"title": "Mealie", "description": "A place for all your recipes", "version": "v0.5.0beta"}, "paths": {"/api/auth/token": {"post": {"tags": ["Authentication"], "summary": "Get Token", "operationId": "get_token_api_auth_token_post", "requestBody": {"content": {"application/x-www-form-urlencoded": {"schema": {"$ref": "#/components/schemas/Body_get_token_api_auth_token_post"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/api/auth/token/long": {"post": {"tags": ["Authentication"], "summary": "Get Token", "operationId": "get_token_api_auth_token_long_post", "requestBody": {"content": {"application/x-www-form-urlencoded": {"schema": {"$ref": "#/components/schemas/Body_get_token_api_auth_token_long_post"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/api/auth/refresh": {"get": {"tags": ["Authentication"], "summary": "Refresh Token", "description": "Use a valid token to get another token", "operationId": "refresh_token_api_auth_refresh_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/users/sign-ups": {"get": {"tags": ["User Signup"], "summary": "Get All Open Sign Ups", "description": "Returns a list of open sign up links ", "operationId": "get_all_open_sign_ups_api_users_sign_ups_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"title": "Response Get All Open Sign Ups Api Users Sign Ups Get", "type": "array", "items": {"$ref": "#/components/schemas/SignUpOut"}}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "post": {"tags": ["User Signup"], "summary": "Create User Sign Up Key", "description": "Generates a Random Token that a new user can sign up with ", "operationId": "create_user_sign_up_key_api_users_sign_ups_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/SignUpIn"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SignUpToken"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/users/sign-ups/{token}": {"post": {"tags": ["User Signup"], "summary": "Create User With Token", "description": "Creates a user with a valid sign up token ", "operationId": "create_user_with_token_api_users_sign_ups__token__post", "parameters": [{"required": true, "schema": {"title": "Token", "type": "string"}, "name": "token", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserIn"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "delete": {"tags": ["User Signup"], "summary": "Delete Token", "description": "Removed a token from the database ", "operationId": "delete_token_api_users_sign_ups__token__delete", "parameters": [{"required": true, "schema": {"title": "Token", "type": "string"}, "name": "token", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/users": {"get": {"tags": ["Users"], "summary": "Get All Users", "operationId": "get_all_users_api_users_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"title": "Response Get All Users Api Users Get", "type": "array", "items": {"$ref": "#/components/schemas/UserOut"}}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "post": {"tags": ["Users"], "summary": "Create User", "operationId": "create_user_api_users_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserIn"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserOut"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/users/self": {"get": {"tags": ["Users"], "summary": "Get Logged In User", "operationId": "get_logged_in_user_api_users_self_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserOut"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/users/{id}": {"get": {"tags": ["Users"], "summary": "Get User By Id", "operationId": "get_user_by_id_api_users__id__get", "parameters": [{"required": true, "schema": {"title": "Id", "type": "integer"}, "name": "id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserOut"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "put": {"tags": ["Users"], "summary": "Update User", "operationId": "update_user_api_users__id__put", "parameters": [{"required": true, "schema": {"title": "Id", "type": "integer"}, "name": "id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserBase"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["Users"], "summary": "Delete User", "description": "Removes a user from the database. Must be the current user or a super user", "operationId": "delete_user_api_users__id__delete", "parameters": [{"required": true, "schema": {"title": "Id", "type": "integer"}, "name": "id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/users/{id}/reset-password": {"put": {"tags": ["Users"], "summary": "Reset User Password", "operationId": "reset_user_password_api_users__id__reset_password_put", "parameters": [{"required": true, "schema": {"title": "Id", "type": "integer"}, "name": "id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/users/{id}/image": {"get": {"tags": ["Users"], "summary": "Get User Image", "description": "Returns a users profile picture ", "operationId": "get_user_image_api_users__id__image_get", "parameters": [{"required": true, "schema": {"title": "Id", "type": "string"}, "name": "id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "post": {"tags": ["Users"], "summary": "Update User Image", "description": "Updates a User Image ", "operationId": "update_user_image_api_users__id__image_post", "parameters": [{"required": true, "schema": {"title": "Id", "type": "string"}, "name": "id", "in": "path"}], "requestBody": {"content": {"multipart/form-data": {"schema": {"$ref": "#/components/schemas/Body_update_user_image_api_users__id__image_post"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/users/{id}/password": {"put": {"tags": ["Users"], "summary": "Update Password", "description": "Resets the User Password", "operationId": "update_password_api_users__id__password_put", "parameters": [{"required": true, "schema": {"title": "Id", "type": "integer"}, "name": "id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ChangePassword"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/users/api-tokens": {"post": {"tags": ["User API Tokens"], "summary": "Create Api Token", "description": "Create api_token in the Database ", "operationId": "create_api_token_api_users_api_tokens_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/LoingLiveTokenIn"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/users/api-tokens/{token_id}": {"delete": {"tags": ["User API Tokens"], "summary": "Delete Api Token", "description": "Delete api_token from the Database ", "operationId": "delete_api_token_api_users_api_tokens__token_id__delete", "parameters": [{"required": true, "schema": {"title": "Token Id", "type": "integer"}, "name": "token_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/groups": {"get": {"tags": ["Groups"], "summary": "Get All Groups", "description": "Returns a list of all groups in the database ", "operationId": "get_all_groups_api_groups_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"title": "Response Get All Groups Api Groups Get", "type": "array", "items": {"$ref": "#/components/schemas/GroupInDB"}}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "post": {"tags": ["Groups"], "summary": "Create Group", "description": "Creates a Group in the Database ", "operationId": "create_group_api_groups_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/GroupBase"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/groups/self": {"get": {"tags": ["Groups"], "summary": "Get Current User Group", "description": "Returns the Group Data for the Current User ", "operationId": "get_current_user_group_api_groups_self_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/GroupInDB"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/groups/{id}": {"put": {"tags": ["Groups"], "summary": "Update Group Data", "description": "Updates a User Group ", "operationId": "update_group_data_api_groups__id__put", "parameters": [{"required": true, "schema": {"title": "Id", "type": "integer"}, "name": "id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/UpdateGroup"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["Groups"], "summary": "Delete User Group", "description": "Removes a user group from the database ", "operationId": "delete_user_group_api_groups__id__delete", "parameters": [{"required": true, "schema": {"title": "Id", "type": "integer"}, "name": "id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/shopping-lists": {"post": {"tags": ["Shopping Lists"], "summary": "Create Shopping List", "description": "Create Shopping List in the Database ", "operationId": "create_shopping_list_api_shopping_lists_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ShoppingListIn"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ShoppingListOut"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/shopping-lists/{id}": {"get": {"tags": ["Shopping Lists"], "summary": "Get Shopping List", "description": "Get Shopping List from the Database ", "operationId": "get_shopping_list_api_shopping_lists__id__get", "parameters": [{"required": true, "schema": {"title": "Id", "type": "integer"}, "name": "id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ShoppingListOut"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["Shopping Lists"], "summary": "Update Shopping List", "description": "Update Shopping List in the Database ", "operationId": "update_shopping_list_api_shopping_lists__id__put", "parameters": [{"required": true, "schema": {"title": "Id", "type": "integer"}, "name": "id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ShoppingListOut"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["Shopping Lists"], "summary": "Delete Shopping List", "description": "Delete Shopping List from the Database ", "operationId": "delete_shopping_list_api_shopping_lists__id__delete", "parameters": [{"required": true, "schema": {"title": "Id", "type": "integer"}, "name": "id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/recipes/summary": {"get": {"tags": ["Query All Recipes"], "summary": "Get Recipe Summary", "description": "Returns key the recipe summary data for recipes in the database. You can perform\nslice operations to set the skip/end amounts for recipes. All recipes are sorted by the added date.\n\n**Query Parameters**\n- skip: The database entry to start at. (0 Indexed)\n- end: The number of entries to return.\n\nskip=2, end=10 will return entries", "operationId": "get_recipe_summary_api_recipes_summary_get", "parameters": [{"required": false, "schema": {"title": "Start", "default": 0}, "name": "start", "in": "query"}, {"required": false, "schema": {"title": "Limit", "default": 9999}, "name": "limit", "in": "query"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"title": "Response Get Recipe Summary Api Recipes Summary Get", "type": "array", "items": {"$ref": "#/components/schemas/RecipeSummary"}}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/api/recipes/summary/untagged": {"get": {"tags": ["Query All Recipes"], "summary": "Get Untagged Recipes", "operationId": "get_untagged_recipes_api_recipes_summary_untagged_get", "parameters": [{"required": false, "schema": {"title": "Count", "type": "boolean", "default": false}, "name": "count", "in": "query"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"title": "Response Get Untagged Recipes Api Recipes Summary Untagged Get", "type": "array", "items": {"$ref": "#/components/schemas/RecipeSummary"}}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/api/recipes/summary/uncategorized": {"get": {"tags": ["Query All Recipes"], "summary": "Get Uncategorized Recipes", "operationId": "get_uncategorized_recipes_api_recipes_summary_uncategorized_get", "parameters": [{"required": false, "schema": {"title": "Count", "type": "boolean", "default": false}, "name": "count", "in": "query"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"title": "Response Get Uncategorized Recipes Api Recipes Summary Uncategorized Get", "type": "array", "items": {"$ref": "#/components/schemas/RecipeSummary"}}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/api/recipes/category": {"post": {"tags": ["Query All Recipes"], "summary": "Filter By Category", "description": "pass a list of categories and get a list of recipes associated with those categories ", "operationId": "filter_by_category_api_recipes_category_post", "requestBody": {"content": {"application/json": {"schema": {"title": "Categories", "type": "array", "items": {}}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/api/recipes/tag": {"post": {"tags": ["Query All Recipes"], "summary": "Filter By Tags", "description": "pass a list of tags and get a list of recipes associated with those tags", "operationId": "filter_by_tags_api_recipes_tag_post", "requestBody": {"content": {"application/json": {"schema": {"title": "Tags", "type": "array", "items": {}}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/api/recipes/create": {"post": {"tags": ["Recipe CRUD"], "summary": "Create From Json", "description": "Takes in a JSON string and loads data into the database as a new entry", "operationId": "create_from_json_api_recipes_create_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Recipe"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"title": "Response Create From Json Api Recipes Create Post", "type": "string"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/recipes/create-url": {"post": {"tags": ["Recipe CRUD"], "summary": "Parse Recipe Url", "description": "Takes in a URL and attempts to scrape data and load it into the database ", "operationId": "parse_recipe_url_api_recipes_create_url_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/RecipeURLIn"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"title": "Response Parse Recipe Url Api Recipes Create Url Post", "type": "string"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/recipes/{recipe_slug}": {"get": {"tags": ["Recipe CRUD"], "summary": "Get Recipe", "description": "Takes in a recipe slug, returns all data for a recipe ", "operationId": "get_recipe_api_recipes__recipe_slug__get", "parameters": [{"required": true, "schema": {"title": "Recipe Slug", "type": "string"}, "name": "recipe_slug", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Recipe"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["Recipe CRUD"], "summary": "Update Recipe", "description": "Updates a recipe by existing slug and data. ", "operationId": "update_recipe_api_recipes__recipe_slug__put", "parameters": [{"required": true, "schema": {"title": "Recipe Slug", "type": "string"}, "name": "recipe_slug", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Recipe"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["Recipe CRUD"], "summary": "Delete Recipe", "description": "Deletes a recipe by slug ", "operationId": "delete_recipe_api_recipes__recipe_slug__delete", "parameters": [{"required": true, "schema": {"title": "Recipe Slug", "type": "string"}, "name": "recipe_slug", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "patch": {"tags": ["Recipe CRUD"], "summary": "Patch Recipe", "description": "Updates a recipe by existing slug and data. ", "operationId": "patch_recipe_api_recipes__recipe_slug__patch", "parameters": [{"required": true, "schema": {"title": "Recipe Slug", "type": "string"}, "name": "recipe_slug", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Recipe"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/recipes/{recipe_slug}/image": {"put": {"tags": ["Recipe CRUD"], "summary": "Update Recipe Image", "description": "Removes an existing image and replaces it with the incoming file. ", "operationId": "update_recipe_image_api_recipes__recipe_slug__image_put", "parameters": [{"required": true, "schema": {"title": "Recipe Slug", "type": "string"}, "name": "recipe_slug", "in": "path"}], "requestBody": {"content": {"multipart/form-data": {"schema": {"$ref": "#/components/schemas/Body_update_recipe_image_api_recipes__recipe_slug__image_put"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "post": {"tags": ["Recipe CRUD"], "summary": "Scrape Image Url", "description": "Removes an existing image and replaces it with the incoming file. ", "operationId": "scrape_image_url_api_recipes__recipe_slug__image_post", "parameters": [{"required": true, "schema": {"title": "Recipe Slug", "type": "string"}, "name": "recipe_slug", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/RecipeURLIn"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/recipes/{recipe_slug}/assets": {"post": {"tags": ["Recipe CRUD"], "summary": "Upload Recipe Asset", "description": "Upload a file to store as a recipe asset ", "operationId": "upload_recipe_asset_api_recipes__recipe_slug__assets_post", "parameters": [{"required": true, "schema": {"title": "Recipe Slug", "type": "string"}, "name": "recipe_slug", "in": "path"}], "requestBody": {"content": {"multipart/form-data": {"schema": {"$ref": "#/components/schemas/Body_upload_recipe_asset_api_recipes__recipe_slug__assets_post"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/RecipeAsset"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/categories": {"get": {"tags": ["Recipe Categories"], "summary": "Get All Recipe Categories", "description": "Returns a list of available categories in the database ", "operationId": "get_all_recipe_categories_api_categories_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}}, "post": {"tags": ["Recipe Categories"], "summary": "Create Recipe Category", "description": "Creates a Category in the database ", "operationId": "create_recipe_category_api_categories_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/CategoryIn"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/categories/empty": {"get": {"tags": ["Recipe Categories"], "summary": "Get Empty Categories", "description": "Returns a list of categories that do not contain any recipes", "operationId": "get_empty_categories_api_categories_empty_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}}}, "/api/categories/{category}": {"get": {"tags": ["Recipe Categories"], "summary": "Get All Recipes By Category", "description": "Returns a list of recipes associated with the provided category. ", "operationId": "get_all_recipes_by_category_api_categories__category__get", "parameters": [{"required": true, "schema": {"title": "Category", "type": "string"}, "name": "category", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/RecipeCategoryResponse"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["Recipe Categories"], "summary": "Update Recipe Category", "description": "Updates an existing Tag in the database ", "operationId": "update_recipe_category_api_categories__category__put", "parameters": [{"required": true, "schema": {"title": "Category", "type": "string"}, "name": "category", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/CategoryIn"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/RecipeCategoryResponse"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["Recipe Categories"], "summary": "Delete Recipe Category", "description": "Removes a recipe category from the database. Deleting a\ncategory does not impact a recipe. The category will be removed\nfrom any recipes that contain it", "operationId": "delete_recipe_category_api_categories__category__delete", "parameters": [{"required": true, "schema": {"title": "Category", "type": "string"}, "name": "category", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/tags": {"get": {"tags": ["Recipe Tags"], "summary": "Get All Recipe Tags", "description": "Returns a list of available tags in the database ", "operationId": "get_all_recipe_tags_api_tags_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}}, "post": {"tags": ["Recipe Tags"], "summary": "Create Recipe Tag", "description": "Creates a Tag in the database ", "operationId": "create_recipe_tag_api_tags_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/TagIn"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/tags/empty": {"get": {"tags": ["Recipe Tags"], "summary": "Get Empty Tags", "description": "Returns a list of tags that do not contain any recipes", "operationId": "get_empty_tags_api_tags_empty_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}}}, "/api/tags/{tag}": {"get": {"tags": ["Recipe Tags"], "summary": "Get All Recipes By Tag", "description": "Returns a list of recipes associated with the provided tag. ", "operationId": "get_all_recipes_by_tag_api_tags__tag__get", "parameters": [{"required": true, "schema": {"title": "Tag", "type": "string"}, "name": "tag", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/RecipeTagResponse"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["Recipe Tags"], "summary": "Update Recipe Tag", "description": "Updates an existing Tag in the database ", "operationId": "update_recipe_tag_api_tags__tag__put", "parameters": [{"required": true, "schema": {"title": "Tag", "type": "string"}, "name": "tag", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/TagIn"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/RecipeTagResponse"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["Recipe Tags"], "summary": "Delete Recipe Tag", "description": "Removes a recipe tag from the database. Deleting a\ntag does not impact a recipe. The tag will be removed\nfrom any recipes that contain it", "operationId": "delete_recipe_tag_api_tags__tag__delete", "parameters": [{"required": true, "schema": {"title": "Tag", "type": "string"}, "name": "tag", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/media/recipes/{recipe_slug}/images/{file_name}": {"get": {"tags": ["Site Media"], "summary": "Get Recipe Img", "description": "Takes in a recipe slug, returns the static image. This route is proxied in the docker image\nand should not hit the API in production", "operationId": "get_recipe_img_api_media_recipes__recipe_slug__images__file_name__get", "parameters": [{"required": true, "schema": {"title": "Recipe Slug", "type": "string"}, "name": "recipe_slug", "in": "path"}, {"required": true, "schema": {"$ref": "#/components/schemas/ImageType"}, "name": "file_name", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/api/media/recipes/{recipe_slug}/assets/{file_name}": {"get": {"tags": ["Site Media"], "summary": "Get Recipe Asset", "description": "Returns a recipe asset ", "operationId": "get_recipe_asset_api_media_recipes__recipe_slug__assets__file_name__get", "parameters": [{"required": true, "schema": {"title": "Recipe Slug", "type": "string"}, "name": "recipe_slug", "in": "path"}, {"required": true, "schema": {"title": "File Name", "type": "string"}, "name": "file_name", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/api/about/events": {"get": {"tags": ["App Events"], "summary": "Get Events", "description": "Get event from the Database ", "operationId": "get_events_api_about_events_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/EventsOut"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["App Events"], "summary": "Delete Events", "description": "Get event from the Database ", "operationId": "delete_events_api_about_events_delete", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/about/events/{id}": {"delete": {"tags": ["App Events"], "summary": "Delete Event", "description": "Delete event from the Database ", "operationId": "delete_event_api_about_events__id__delete", "parameters": [{"required": true, "schema": {"title": "Id", "type": "integer"}, "name": "id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/about/events/notifications": {"get": {"tags": ["App Events"], "summary": "Get All Event Notification", "description": "Get all event_notification from the Database ", "operationId": "get_all_event_notification_api_about_events_notifications_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"title": "Response Get All Event Notification Api About Events Notifications Get", "type": "array", "items": {"$ref": "#/components/schemas/EventNotificationOut"}}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "post": {"tags": ["App Events"], "summary": "Create Event Notification", "description": "Create event_notification in the Database ", "operationId": "create_event_notification_api_about_events_notifications_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/EventNotificationIn"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/about/events/notifications/test": {"post": {"tags": ["App Events"], "summary": "Test Notification Route", "description": "Create event_notification in the Database ", "operationId": "test_notification_route_api_about_events_notifications_test_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/TestEvent"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/about/events/notifications/{id}": {"put": {"tags": ["App Events"], "summary": "Update Event Notification", "description": "Update event_notification in the Database ", "operationId": "update_event_notification_api_about_events_notifications__id__put", "parameters": [{"required": true, "schema": {"title": "Id", "type": "integer"}, "name": "id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["App Events"], "summary": "Delete Event Notification", "description": "Delete event_notification from the Database ", "operationId": "delete_event_notification_api_about_events_notifications__id__delete", "parameters": [{"required": true, "schema": {"title": "Id", "type": "integer"}, "name": "id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/meal-plans/all": {"get": {"tags": ["Meal Plan"], "summary": "Get All Meals", "description": "Returns a list of all available Meal Plan ", "operationId": "get_all_meals_api_meal_plans_all_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"title": "Response Get All Meals Api Meal Plans All Get", "type": "array", "items": {"$ref": "#/components/schemas/MealPlanOut"}}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/meal-plans/create": {"post": {"tags": ["Meal Plan"], "summary": "Create Meal Plan", "description": "Creates a meal plan database entry ", "operationId": "create_meal_plan_api_meal_plans_create_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/MealPlanIn"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/meal-plans/{plan_id}": {"put": {"tags": ["Meal Plan"], "summary": "Update Meal Plan", "description": "Updates a meal plan based off ID ", "operationId": "update_meal_plan_api_meal_plans__plan_id__put", "parameters": [{"required": true, "schema": {"title": "Plan Id", "type": "string"}, "name": "plan_id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/MealPlanIn"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["Meal Plan"], "summary": "Delete Meal Plan", "description": "Removes a meal plan from the database ", "operationId": "delete_meal_plan_api_meal_plans__plan_id__delete", "parameters": [{"required": true, "schema": {"title": "Plan Id"}, "name": "plan_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/meal-plans/this-week": {"get": {"tags": ["Meal Plan"], "summary": "Get This Week", "description": "Returns the meal plan data for this week ", "operationId": "get_this_week_api_meal_plans_this_week_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MealPlanOut"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/meal-plans/today": {"get": {"tags": ["Meal Plan", "Meal Plan"], "summary": "Get Today", "description": "Returns the recipe slug for the meal scheduled for today.\nIf no meal is scheduled nothing is returned", "operationId": "get_today_api_meal_plans_today_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/meal-plans/today/image": {"get": {"tags": ["Meal Plan", "Meal Plan"], "summary": "Get Todays Image", "description": "Returns the image for todays meal-plan.", "operationId": "get_todays_image_api_meal_plans_today_image_get", "parameters": [{"required": false, "schema": {"title": "Group Name", "type": "string", "default": "Home"}, "name": "group_name", "in": "query"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/api/meal-plans/{id}/shopping-list": {"get": {"tags": ["Meal Plan"], "summary": "Get Shopping List", "operationId": "get_shopping_list_api_meal_plans__id__shopping_list_get", "parameters": [{"required": true, "schema": {"title": "Id", "type": "string"}, "name": "id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/site-settings/custom-pages": {"get": {"tags": ["Settings"], "summary": "Get Custom Pages", "description": "Returns the sites custom pages ", "operationId": "get_custom_pages_api_site_settings_custom_pages_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}}, "put": {"tags": ["Settings"], "summary": "Update Multiple Pages", "description": "Update multiple custom pages ", "operationId": "update_multiple_pages_api_site_settings_custom_pages_put", "requestBody": {"content": {"application/json": {"schema": {"title": "Pages", "type": "array", "items": {"$ref": "#/components/schemas/CustomPageOut"}}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "post": {"tags": ["Settings"], "summary": "Create New Page", "description": "Creates a new Custom Page ", "operationId": "create_new_page_api_site_settings_custom_pages_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/CustomPageBase"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/site-settings/custom-pages/{id}": {"get": {"tags": ["Settings"], "summary": "Get Single Page", "description": "Removes a custom page from the database ", "operationId": "get_single_page_api_site_settings_custom_pages__id__get", "parameters": [{"required": true, "schema": {"title": "Id", "anyOf": [{"type": "integer"}, {"type": "string"}]}, "name": "id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["Settings"], "summary": "Update Single Page", "description": "Removes a custom page from the database ", "operationId": "update_single_page_api_site_settings_custom_pages__id__put", "parameters": [{"required": true, "schema": {"title": "Id", "type": "integer"}, "name": "id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/CustomPageOut"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["Settings"], "summary": "Delete Custom Page", "description": "Removes a custom page from the database ", "operationId": "delete_custom_page_api_site_settings_custom_pages__id__delete", "parameters": [{"required": true, "schema": {"title": "Id", "type": "integer"}, "name": "id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/site-settings": {"get": {"tags": ["Settings"], "summary": "Get Main Settings", "description": "Returns basic site settings ", "operationId": "get_main_settings_api_site_settings_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}}, "put": {"tags": ["Settings"], "summary": "Update Settings", "description": "Returns Site Settings ", "operationId": "update_settings_api_site_settings_put", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/SiteSettings"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/site-settings/webhooks/test": {"post": {"tags": ["Settings"], "summary": "Test Webhooks", "description": "Run the function to test your webhooks ", "operationId": "test_webhooks_api_site_settings_webhooks_test_post", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/themes": {"get": {"tags": ["Themes"], "summary": "Get All Themes", "description": "Returns all site themes ", "operationId": "get_all_themes_api_themes_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}}}, "/api/themes/create": {"post": {"tags": ["Themes"], "summary": "Create Theme", "description": "Creates a site color theme database entry ", "operationId": "create_theme_api_themes_create_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/SiteTheme"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/themes/{id}": {"get": {"tags": ["Themes"], "summary": "Get Single Theme", "description": "Returns a named theme ", "operationId": "get_single_theme_api_themes__id__get", "parameters": [{"required": true, "schema": {"title": "Id", "type": "integer"}, "name": "id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}, "put": {"tags": ["Themes"], "summary": "Update Theme", "description": "Update a theme database entry ", "operationId": "update_theme_api_themes__id__put", "parameters": [{"required": true, "schema": {"title": "Id", "type": "integer"}, "name": "id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/SiteTheme"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["Themes"], "summary": "Delete Theme", "description": "Deletes theme from the database ", "operationId": "delete_theme_api_themes__id__delete", "parameters": [{"required": true, "schema": {"title": "Id", "type": "integer"}, "name": "id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/backups/available": {"get": {"tags": ["Backups"], "summary": "Available Imports", "description": "Returns a list of avaiable .zip files for import into Mealie.", "operationId": "available_imports_api_backups_available_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Imports"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/backups/export/database": {"post": {"tags": ["Backups"], "summary": "Export Database", "description": "Generates a backup of the recipe database in json format.", "operationId": "export_database_api_backups_export_database_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/BackupJob"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/backups/upload": {"post": {"tags": ["Backups"], "summary": "Upload Backup File", "description": "Upload a .zip File to later be imported into Mealie ", "operationId": "upload_backup_file_api_backups_upload_post", "requestBody": {"content": {"multipart/form-data": {"schema": {"$ref": "#/components/schemas/Body_upload_backup_file_api_backups_upload_post"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/backups/{file_name}/download": {"get": {"tags": ["Backups"], "summary": "Download Backup File", "description": "Returns a token to download a file ", "operationId": "download_backup_file_api_backups__file_name__download_get", "parameters": [{"required": true, "schema": {"title": "File Name", "type": "string"}, "name": "file_name", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/backups/{file_name}/import": {"post": {"tags": ["Backups"], "summary": "Import Database", "description": "Import a database backup file generated from Mealie. ", "operationId": "import_database_api_backups__file_name__import_post", "parameters": [{"required": true, "schema": {"title": "File Name", "type": "string"}, "name": "file_name", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ImportJob"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/backups/{file_name}/delete": {"delete": {"tags": ["Backups"], "summary": "Delete Backup", "description": "Removes a database backup from the file system ", "operationId": "delete_backup_api_backups__file_name__delete_delete", "parameters": [{"required": true, "schema": {"title": "File Name", "type": "string"}, "name": "file_name", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/migrations": {"get": {"tags": ["Migration"], "summary": "Get All Migration Options", "description": "Returns a list of avaiable directories that can be imported into Mealie ", "operationId": "get_all_migration_options_api_migrations_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"title": "Response Get All Migration Options Api Migrations Get", "type": "array", "items": {"$ref": "#/components/schemas/Migrations"}}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/migrations/{import_type}/{file_name}/import": {"post": {"tags": ["Migration"], "summary": "Import Migration", "description": "Imports all the recipes in a given directory ", "operationId": "import_migration_api_migrations__import_type___file_name__import_post", "parameters": [{"required": true, "schema": {"$ref": "#/components/schemas/Migration"}, "name": "import_type", "in": "path"}, {"required": true, "schema": {"title": "File Name", "type": "string"}, "name": "file_name", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/migrations/{import_type}/{file_name}/delete": {"delete": {"tags": ["Migration"], "summary": "Delete Migration Data", "description": "Removes migration data from the file system ", "operationId": "delete_migration_data_api_migrations__import_type___file_name__delete_delete", "parameters": [{"required": true, "schema": {"$ref": "#/components/schemas/Migration"}, "name": "import_type", "in": "path"}, {"required": true, "schema": {"title": "File Name", "type": "string"}, "name": "file_name", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/migrations/{import_type}/upload": {"post": {"tags": ["Migration"], "summary": "Upload Nextcloud Zipfile", "description": "Upload a .zip File to later be imported into Mealie ", "operationId": "upload_nextcloud_zipfile_api_migrations__import_type__upload_post", "parameters": [{"required": true, "schema": {"$ref": "#/components/schemas/Migration"}, "name": "import_type", "in": "path"}], "requestBody": {"content": {"multipart/form-data": {"schema": {"$ref": "#/components/schemas/Body_upload_nextcloud_zipfile_api_migrations__import_type__upload_post"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/debug": {"get": {"tags": ["Debug"], "summary": "Get Debug Info", "description": "Returns general information about the application for debugging ", "operationId": "get_debug_info_api_debug_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/debug/statistics": {"get": {"tags": ["Debug"], "summary": "Get App Statistics", "operationId": "get_app_statistics_api_debug_statistics_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}}}, "/api/debug/version": {"get": {"tags": ["Debug"], "summary": "Get Mealie Version", "description": "Returns the current version of mealie", "operationId": "get_mealie_version_api_debug_version_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}}}, "/api/debug/last-recipe-json": {"get": {"tags": ["Debug"], "summary": "Get Last Recipe Json", "description": "Returns a token to download a file ", "operationId": "get_last_recipe_json_api_debug_last_recipe_json_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/debug/log/{num}": {"get": {"tags": ["Debug"], "summary": "Get Log", "description": "Doc Str ", "operationId": "get_log_api_debug_log__num__get", "parameters": [{"required": true, "schema": {"title": "Num", "type": "integer"}, "name": "num", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"OAuth2PasswordBearer": []}]}}, "/api/debug/log": {"get": {"tags": ["Debug"], "summary": "Get Log File", "description": "Returns a token to download a file ", "operationId": "get_log_file_api_debug_log_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}}}, "/api/utils/download": {"get": {"tags": ["Utils"], "summary": "Download File", "description": "Uses a file token obtained by an active user to retrieve a file from the operating\nsystem.", "operationId": "download_file_api_utils_download_get", "parameters": [{"required": false, "schema": {"title": "Token", "type": "string"}, "name": "token", "in": "query"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}}, "components": {"schemas": {"BackupJob": {"title": "BackupJob", "required": ["options"], "type": "object", "properties": {"tag": {"title": "Tag", "type": "string"}, "options": {"$ref": "#/components/schemas/BackupOptions"}, "templates": {"title": "Templates", "type": "array", "items": {"type": "string"}}}, "example": {"tag": "July 23rd 2021", "options": {"recipes": true, "settings": true, "pages": true, "themes": true, "groups": true, "users": true, "notifications": true}, "template": ["recipes.md"]}}, "BackupOptions": {"title": "BackupOptions", "type": "object", "properties": {"recipes": {"title": "Recipes", "type": "boolean", "default": true}, "settings": {"title": "Settings", "type": "boolean", "default": true}, "pages": {"title": "Pages", "type": "boolean", "default": true}, "themes": {"title": "Themes", "type": "boolean", "default": true}, "groups": {"title": "Groups", "type": "boolean", "default": true}, "users": {"title": "Users", "type": "boolean", "default": true}, "notifications": {"title": "Notifications", "type": "boolean", "default": true}}, "example": {"recipes": true, "settings": true, "themes": true, "groups": true, "users": true}}, "Body_get_token_api_auth_token_long_post": {"title": "Body_get_token_api_auth_token_long_post", "required": ["username", "password"], "type": "object", "properties": {"grant_type": {"title": "Grant Type", "pattern": "password", "type": "string"}, "username": {"title": "Username", "type": "string"}, "password": {"title": "Password", "type": "string"}, "scope": {"title": "Scope", "type": "string", "default": ""}, "client_id": {"title": "Client Id", "type": "string"}, "client_secret": {"title": "Client Secret", "type": "string"}}}, "Body_get_token_api_auth_token_post": {"title": "Body_get_token_api_auth_token_post", "required": ["username", "password"], "type": "object", "properties": {"grant_type": {"title": "Grant Type", "pattern": "password", "type": "string"}, "username": {"title": "Username", "type": "string"}, "password": {"title": "Password", "type": "string"}, "scope": {"title": "Scope", "type": "string", "default": ""}, "client_id": {"title": "Client Id", "type": "string"}, "client_secret": {"title": "Client Secret", "type": "string"}}}, "Body_update_recipe_image_api_recipes__recipe_slug__image_put": {"title": "Body_update_recipe_image_api_recipes__recipe_slug__image_put", "required": ["image", "extension"], "type": "object", "properties": {"image": {"title": "Image", "type": "string", "format": "binary"}, "extension": {"title": "Extension", "type": "string"}}}, "Body_update_user_image_api_users__id__image_post": {"title": "Body_update_user_image_api_users__id__image_post", "required": ["profile_image"], "type": "object", "properties": {"profile_image": {"title": "Profile Image", "type": "string", "format": "binary"}}}, "Body_upload_backup_file_api_backups_upload_post": {"title": "Body_upload_backup_file_api_backups_upload_post", "required": ["archive"], "type": "object", "properties": {"archive": {"title": "Archive", "type": "string", "format": "binary"}}}, "Body_upload_nextcloud_zipfile_api_migrations__import_type__upload_post": {"title": "Body_upload_nextcloud_zipfile_api_migrations__import_type__upload_post", "required": ["archive"], "type": "object", "properties": {"archive": {"title": "Archive", "type": "string", "format": "binary"}}}, "Body_upload_recipe_asset_api_recipes__recipe_slug__assets_post": {"title": "Body_upload_recipe_asset_api_recipes__recipe_slug__assets_post", "required": ["name", "icon", "extension", "file"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "icon": {"title": "Icon", "type": "string"}, "extension": {"title": "Extension", "type": "string"}, "file": {"title": "File", "type": "string", "format": "binary"}}}, "CategoryBase": {"title": "CategoryBase", "required": ["name", "id", "slug"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "integer"}, "slug": {"title": "Slug", "type": "string"}}}, "CategoryIn": {"title": "CategoryIn", "required": ["name"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}}}, "ChangePassword": {"title": "ChangePassword", "required": ["currentPassword", "newPassword"], "type": "object", "properties": {"currentPassword": {"title": "Currentpassword", "type": "string"}, "newPassword": {"title": "Newpassword", "type": "string"}}}, "Colors": {"title": "Colors", "type": "object", "properties": {"primary": {"title": "Primary", "type": "string", "default": "#E58325"}, "accent": {"title": "Accent", "type": "string", "default": "#00457A"}, "secondary": {"title": "Secondary", "type": "string", "default": "#973542"}, "success": {"title": "Success", "type": "string", "default": "#43A047"}, "info": {"title": "Info", "type": "string", "default": "#1976D2"}, "warning": {"title": "Warning", "type": "string", "default": "#FF6F00"}, "error": {"title": "Error", "type": "string", "default": "#EF5350"}}}, "CustomPageBase": {"title": "CustomPageBase", "required": ["name", "position"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "slug": {"title": "Slug", "type": "string"}, "position": {"title": "Position", "type": "integer"}, "categories": {"title": "Categories", "type": "array", "items": {"$ref": "#/components/schemas/RecipeCategoryResponse"}, "default": []}}}, "CustomPageOut": {"title": "CustomPageOut", "required": ["name", "position", "id"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "slug": {"title": "Slug", "type": "string"}, "position": {"title": "Position", "type": "integer"}, "categories": {"title": "Categories", "type": "array", "items": {"$ref": "#/components/schemas/RecipeCategoryResponse"}, "default": []}, "id": {"title": "Id", "type": "integer"}}}, "DeclaredTypes": {"title": "DeclaredTypes", "enum": ["General", "Discord", "Gotify", "Pushover", "Home Assistant"], "type": "string", "description": "An enumeration."}, "Event": {"title": "Event", "required": ["title", "text"], "type": "object", "properties": {"id": {"title": "Id", "type": "integer"}, "title": {"title": "Title", "type": "string"}, "text": {"title": "Text", "type": "string"}, "timeStamp": {"title": "Timestamp", "type": "string", "format": "date-time"}, "category": {"allOf": [{"$ref": "#/components/schemas/EventCategory"}], "default": "general"}}}, "EventCategory": {"title": "EventCategory", "enum": ["general", "recipe", "backup", "scheduled", "migration", "group", "user"], "type": "string", "description": "An enumeration."}, "EventNotificationIn": {"title": "EventNotificationIn", "type": "object", "properties": {"id": {"title": "Id", "type": "integer"}, "name": {"title": "Name", "type": "string", "default": ""}, "type": {"allOf": [{"$ref": "#/components/schemas/DeclaredTypes"}], "default": "General"}, "general": {"title": "General", "type": "boolean", "default": true}, "recipe": {"title": "Recipe", "type": "boolean", "default": true}, "backup": {"title": "Backup", "type": "boolean", "default": true}, "scheduled": {"title": "Scheduled", "type": "boolean", "default": true}, "migration": {"title": "Migration", "type": "boolean", "default": true}, "group": {"title": "Group", "type": "boolean", "default": true}, "user": {"title": "User", "type": "boolean", "default": true}, "notificationUrl": {"title": "Notificationurl", "type": "string", "default": ""}}}, "EventNotificationOut": {"title": "EventNotificationOut", "type": "object", "properties": {"id": {"title": "Id", "type": "integer"}, "name": {"title": "Name", "type": "string", "default": ""}, "type": {"allOf": [{"$ref": "#/components/schemas/DeclaredTypes"}], "default": "General"}, "general": {"title": "General", "type": "boolean", "default": true}, "recipe": {"title": "Recipe", "type": "boolean", "default": true}, "backup": {"title": "Backup", "type": "boolean", "default": true}, "scheduled": {"title": "Scheduled", "type": "boolean", "default": true}, "migration": {"title": "Migration", "type": "boolean", "default": true}, "group": {"title": "Group", "type": "boolean", "default": true}, "user": {"title": "User", "type": "boolean", "default": true}}}, "EventsOut": {"title": "EventsOut", "required": ["total", "events"], "type": "object", "properties": {"total": {"title": "Total", "type": "integer"}, "events": {"title": "Events", "type": "array", "items": {"$ref": "#/components/schemas/Event"}}}}, "GroupBase": {"title": "GroupBase", "required": ["name"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}}}, "GroupInDB": {"title": "GroupInDB", "required": ["name", "id", "webhookEnable"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "integer"}, "categories": {"title": "Categories", "type": "array", "items": {"$ref": "#/components/schemas/CategoryBase"}, "default": []}, "webhookUrls": {"title": "Webhookurls", "type": "array", "items": {"type": "string"}, "default": []}, "webhookTime": {"title": "Webhooktime", "type": "string", "default": "00:00"}, "webhookEnable": {"title": "Webhookenable", "type": "boolean"}, "users": {"title": "Users", "type": "array", "items": {"$ref": "#/components/schemas/UserOut"}}, "mealplans": {"title": "Mealplans", "type": "array", "items": {"$ref": "#/components/schemas/MealPlanOut"}}, "shoppingLists": {"title": "Shoppinglists", "type": "array", "items": {"$ref": "#/components/schemas/ShoppingListOut"}}}}, "HTTPValidationError": {"title": "HTTPValidationError", "type": "object", "properties": {"detail": {"title": "Detail", "type": "array", "items": {"$ref": "#/components/schemas/ValidationError"}}}}, "ImageType": {"title": "ImageType", "enum": ["original.webp", "min-original.webp", "tiny-original.webp"], "type": "string", "description": "An enumeration."}, "ImportJob": {"title": "ImportJob", "required": ["name"], "type": "object", "properties": {"recipes": {"title": "Recipes", "type": "boolean", "default": true}, "settings": {"title": "Settings", "type": "boolean", "default": true}, "pages": {"title": "Pages", "type": "boolean", "default": true}, "themes": {"title": "Themes", "type": "boolean", "default": true}, "groups": {"title": "Groups", "type": "boolean", "default": true}, "users": {"title": "Users", "type": "boolean", "default": true}, "notifications": {"title": "Notifications", "type": "boolean", "default": true}, "name": {"title": "Name", "type": "string"}, "force": {"title": "Force", "type": "boolean", "default": false}, "rebase": {"title": "Rebase", "type": "boolean", "default": false}}, "example": {"name": "my_local_backup.zip", "recipes": true, "settings": true, "themes": true, "groups": true, "users": true}}, "Imports": {"title": "Imports", "required": ["imports", "templates"], "type": "object", "properties": {"imports": {"title": "Imports", "type": "array", "items": {"$ref": "#/components/schemas/LocalBackup"}}, "templates": {"title": "Templates", "type": "array", "items": {"type": "string"}}}, "example": {"imports": [{"name": "AutoBackup_12-1-2020.zip", "date": "2021-05-22T14:40:41.656959"}], "templates": ["recipes.md", "custom_template.md"]}}, "ListItem": {"title": "ListItem", "type": "object", "properties": {"title": {"title": "Title", "type": "string"}, "text": {"title": "Text", "type": "string", "default": ""}, "quantity": {"title": "Quantity", "type": "integer", "default": 1}, "checked": {"title": "Checked", "type": "boolean", "default": false}}}, "LocalBackup": {"title": "LocalBackup", "required": ["name", "date"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "date": {"title": "Date", "type": "string", "format": "date-time"}}}, "LoingLiveTokenIn": {"title": "LoingLiveTokenIn", "required": ["name"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}}}, "LongLiveTokenOut": {"title": "LongLiveTokenOut", "required": ["name", "id"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "integer"}}}, "MealDayIn": {"title": "MealDayIn", "required": ["meals"], "type": "object", "properties": {"date": {"title": "Date", "type": "string", "format": "date"}, "meals": {"title": "Meals", "type": "array", "items": {"$ref": "#/components/schemas/MealIn"}}}}, "MealIn": {"title": "MealIn", "type": "object", "properties": {"slug": {"title": "Slug", "type": "string"}, "name": {"title": "Name", "type": "string"}, "description": {"title": "Description", "type": "string"}}}, "MealPlanIn": {"title": "MealPlanIn", "required": ["group", "startDate", "endDate", "planDays"], "type": "object", "properties": {"group": {"title": "Group", "type": "string"}, "startDate": {"title": "Startdate", "type": "string", "format": "date"}, "endDate": {"title": "Enddate", "type": "string", "format": "date"}, "planDays": {"title": "Plandays", "type": "array", "items": {"$ref": "#/components/schemas/MealDayIn"}}}}, "MealPlanOut": {"title": "MealPlanOut", "required": ["group", "startDate", "endDate", "planDays", "uid"], "type": "object", "properties": {"group": {"title": "Group", "type": "string"}, "startDate": {"title": "Startdate", "type": "string", "format": "date"}, "endDate": {"title": "Enddate", "type": "string", "format": "date"}, "planDays": {"title": "Plandays", "type": "array", "items": {"$ref": "#/components/schemas/MealDayIn"}}, "uid": {"title": "Uid", "type": "integer"}}}, "Migration": {"title": "Migration", "enum": ["nextcloud", "chowdown"], "type": "string", "description": "The class defining the supported types of migrations for Mealie. Pass the\nclass attribute of the class instead of the string when using."}, "MigrationFile": {"title": "MigrationFile", "required": ["name", "date"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "date": {"title": "Date", "type": "string", "format": "date-time"}}}, "Migrations": {"title": "Migrations", "required": ["type"], "type": "object", "properties": {"type": {"title": "Type", "type": "string"}, "files": {"title": "Files", "type": "array", "items": {"$ref": "#/components/schemas/MigrationFile"}, "default": []}}}, "Nutrition": {"title": "Nutrition", "type": "object", "properties": {"calories": {"title": "Calories", "type": "string"}, "fatContent": {"title": "Fatcontent", "type": "string"}, "proteinContent": {"title": "Proteincontent", "type": "string"}, "carbohydrateContent": {"title": "Carbohydratecontent", "type": "string"}, "fiberContent": {"title": "Fibercontent", "type": "string"}, "sodiumContent": {"title": "Sodiumcontent", "type": "string"}, "sugarContent": {"title": "Sugarcontent", "type": "string"}}}, "Recipe": {"title": "Recipe", "type": "object", "properties": {"id": {"title": "Id", "type": "integer"}, "name": {"title": "Name", "type": "string"}, "slug": {"title": "Slug", "type": "string", "default": ""}, "image": {"title": "Image"}, "description": {"title": "Description", "type": "string"}, "recipeCategory": {"title": "Recipecategory", "type": "array", "items": {"type": "string"}, "default": []}, "tags": {"title": "Tags", "type": "array", "items": {"type": "string"}, "default": []}, "rating": {"title": "Rating", "type": "integer"}, "dateAdded": {"title": "Dateadded", "type": "string", "format": "date"}, "dateUpdated": {"title": "Dateupdated", "type": "string", "format": "date-time"}, "recipeYield": {"title": "Recipeyield", "type": "string"}, "recipeIngredient": {"title": "Recipeingredient", "type": "array", "items": {"type": "string"}}, "recipeInstructions": {"title": "Recipeinstructions", "type": "array", "items": {"$ref": "#/components/schemas/RecipeStep"}}, "nutrition": {"$ref": "#/components/schemas/Nutrition"}, "tools": {"title": "Tools", "type": "array", "items": {"type": "string"}, "default": []}, "totalTime": {"title": "Totaltime", "type": "string"}, "prepTime": {"title": "Preptime", "type": "string"}, "performTime": {"title": "Performtime", "type": "string"}, "settings": {"$ref": "#/components/schemas/RecipeSettings"}, "assets": {"title": "Assets", "type": "array", "items": {"$ref": "#/components/schemas/RecipeAsset"}, "default": []}, "notes": {"title": "Notes", "type": "array", "items": {"$ref": "#/components/schemas/RecipeNote"}, "default": []}, "orgURL": {"title": "Orgurl", "type": "string"}, "extras": {"title": "Extras", "type": "object", "default": {}}}, "example": {"name": "Chicken and Rice With Leeks and Salsa Verde", "description": "This one-skillet dinner gets deep oniony flavor from lots of leeks cooked down to jammy tenderness.", "image": "chicken-and-rice-with-leeks-and-salsa-verde.jpg", "recipe_yield": "4 Servings", "recipe_ingredient": ["1 1/2 lb. skinless, boneless chicken thighs (4-8 depending on size)", "Kosher salt, freshly ground pepper", "3 Tbsp. unsalted butter, divided"], "recipe_instructions": [{"text": "Season chicken with salt and pepper."}], "slug": "chicken-and-rice-with-leeks-and-salsa-verde", "tags": ["favorite", "yummy!"], "recipe_category": ["Dinner", "Pasta"], "notes": [{"title": "Watch Out!", "text": "Prep the day before!"}], "org_url": "https://www.bonappetit.com/recipe/chicken-and-rice-with-leeks-and-salsa-verde", "rating": 3, "extras": {"message": "Don't forget to defrost the chicken!"}}}, "RecipeAsset": {"title": "RecipeAsset", "required": ["name", "icon"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "icon": {"title": "Icon", "type": "string"}, "fileName": {"title": "Filename", "type": "string"}}}, "RecipeCategoryResponse": {"title": "RecipeCategoryResponse", "required": ["name", "id", "slug"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "integer"}, "slug": {"title": "Slug", "type": "string"}, "recipes": {"title": "Recipes", "type": "array", "items": {"$ref": "#/components/schemas/Recipe"}}}, "example": {"id": 1, "name": "dinner", "recipes": [{}]}}, "RecipeNote": {"title": "RecipeNote", "required": ["title", "text"], "type": "object", "properties": {"title": {"title": "Title", "type": "string"}, "text": {"title": "Text", "type": "string"}}}, "RecipeSettings": {"title": "RecipeSettings", "type": "object", "properties": {"public": {"title": "Public", "type": "boolean", "default": true}, "showNutrition": {"title": "Shownutrition", "type": "boolean", "default": true}, "showAssets": {"title": "Showassets", "type": "boolean", "default": true}, "landscapeView": {"title": "Landscapeview", "type": "boolean", "default": true}}}, "RecipeStep": {"title": "RecipeStep", "required": ["text"], "type": "object", "properties": {"title": {"title": "Title", "type": "string", "default": ""}, "text": {"title": "Text", "type": "string"}}}, "RecipeSummary": {"title": "RecipeSummary", "type": "object", "properties": {"id": {"title": "Id", "type": "integer"}, "name": {"title": "Name", "type": "string"}, "slug": {"title": "Slug", "type": "string", "default": ""}, "image": {"title": "Image"}, "description": {"title": "Description", "type": "string"}, "recipeCategory": {"title": "Recipecategory", "type": "array", "items": {"type": "string"}, "default": []}, "tags": {"title": "Tags", "type": "array", "items": {"type": "string"}, "default": []}, "rating": {"title": "Rating", "type": "integer"}, "dateAdded": {"title": "Dateadded", "type": "string", "format": "date"}, "dateUpdated": {"title": "Dateupdated", "type": "string", "format": "date-time"}}}, "RecipeTagResponse": {"title": "RecipeTagResponse", "required": ["name", "id", "slug"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "integer"}, "slug": {"title": "Slug", "type": "string"}, "recipes": {"title": "Recipes", "type": "array", "items": {"$ref": "#/components/schemas/Recipe"}}}, "example": {"id": 1, "name": "dinner", "recipes": [{}]}}, "RecipeURLIn": {"title": "RecipeURLIn", "required": ["url"], "type": "object", "properties": {"url": {"title": "Url", "type": "string"}}, "example": {"url": "https://myfavoriterecipes.com/recipes"}}, "ShoppingListIn": {"title": "ShoppingListIn", "required": ["name", "items"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "group": {"title": "Group", "type": "string"}, "items": {"title": "Items", "type": "array", "items": {"$ref": "#/components/schemas/ListItem"}}}}, "ShoppingListOut": {"title": "ShoppingListOut", "required": ["name", "items", "id"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "group": {"title": "Group", "type": "string"}, "items": {"title": "Items", "type": "array", "items": {"$ref": "#/components/schemas/ListItem"}}, "id": {"title": "Id", "type": "integer"}}}, "SignUpIn": {"title": "SignUpIn", "required": ["name", "admin"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "admin": {"title": "Admin", "type": "boolean"}}}, "SignUpOut": {"title": "SignUpOut", "required": ["name", "admin", "token", "id"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "admin": {"title": "Admin", "type": "boolean"}, "token": {"title": "Token", "type": "string"}, "id": {"title": "Id", "type": "integer"}}}, "SignUpToken": {"title": "SignUpToken", "required": ["name", "admin", "token"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "admin": {"title": "Admin", "type": "boolean"}, "token": {"title": "Token", "type": "string"}}}, "SiteSettings": {"title": "SiteSettings", "type": "object", "properties": {"language": {"title": "Language", "type": "string", "default": "en-US"}, "firstDayOfWeek": {"title": "Firstdayofweek", "type": "integer", "default": 0}, "showRecent": {"title": "Showrecent", "type": "boolean", "default": true}, "cardsPerSection": {"title": "Cardspersection", "type": "integer", "default": 9}, "categories": {"title": "Categories", "type": "array", "items": {"$ref": "#/components/schemas/CategoryBase"}, "default": []}}, "example": {"language": "en", "firstDayOfWeek": 0, "showRecent": true, "categories": [{"id": 1, "name": "thanksgiving", "slug": "thanksgiving"}, {"id": 2, "name": "homechef", "slug": "homechef"}, {"id": 3, "name": "potatoes", "slug": "potatoes"}]}}, "SiteTheme": {"title": "SiteTheme", "type": "object", "properties": {"id": {"title": "Id", "type": "integer"}, "name": {"title": "Name", "type": "string", "default": "default"}, "colors": {"title": "Colors", "allOf": [{"$ref": "#/components/schemas/Colors"}], "default": {"primary": "#E58325", "accent": "#00457A", "secondary": "#973542", "success": "#43A047", "info": "#1976D2", "warning": "#FF6F00", "error": "#EF5350"}}}, "example": {"name": "default", "colors": {"primary": "#E58325", "accent": "#00457A", "secondary": "#973542", "success": "#5AB1BB", "info": "#4990BA", "warning": "#FF4081", "error": "#EF5350"}}}, "TagIn": {"title": "TagIn", "required": ["name"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}}}, "TestEvent": {"title": "TestEvent", "type": "object", "properties": {"id": {"title": "Id", "type": "integer"}, "testUrl": {"title": "Testurl", "type": "string"}}}, "UpdateGroup": {"title": "UpdateGroup", "required": ["name", "id", "webhookEnable"], "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "integer"}, "categories": {"title": "Categories", "type": "array", "items": {"$ref": "#/components/schemas/CategoryBase"}, "default": []}, "webhookUrls": {"title": "Webhookurls", "type": "array", "items": {"type": "string"}, "default": []}, "webhookTime": {"title": "Webhooktime", "type": "string", "default": "00:00"}, "webhookEnable": {"title": "Webhookenable", "type": "boolean"}}}, "UserBase": {"title": "UserBase", "required": ["email", "admin"], "type": "object", "properties": {"fullName": {"title": "Fullname", "type": "string"}, "email": {"title": "Email", "type": "string"}, "admin": {"title": "Admin", "type": "boolean"}, "group": {"title": "Group", "type": "string"}}}, "UserIn": {"title": "UserIn", "required": ["email", "admin", "password"], "type": "object", "properties": {"fullName": {"title": "Fullname", "type": "string"}, "email": {"title": "Email", "type": "string"}, "admin": {"title": "Admin", "type": "boolean"}, "group": {"title": "Group", "type": "string"}, "password": {"title": "Password", "type": "string"}}}, "UserOut": {"title": "UserOut", "required": ["email", "admin", "group", "id"], "type": "object", "properties": {"fullName": {"title": "Fullname", "type": "string"}, "email": {"title": "Email", "type": "string"}, "admin": {"title": "Admin", "type": "boolean"}, "group": {"title": "Group", "type": "string"}, "id": {"title": "Id", "type": "integer"}, "tokens": {"title": "Tokens", "type": "array", "items": {"$ref": "#/components/schemas/LongLiveTokenOut"}}}}, "ValidationError": {"title": "ValidationError", "required": ["loc", "msg", "type"], "type": "object", "properties": {"loc": {"title": "Location", "type": "array", "items": {"type": "string"}}, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}}}}, "securitySchemes": {"OAuth2PasswordBearer": {"type": "oauth2", "flows": {"password": {"scopes": {}, "tokenUrl": "/api/auth/token"}}}}}} \ No newline at end of file
4